Add changeExtension()

Closes #32
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b29d2ab..b5fbeb9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.5.0
+
+* Add a `setExtension()` top-level function and `Context` method.
+
 ## 1.4.2
 
 * Treat `package:` URLs as absolute.
diff --git a/lib/path.dart b/lib/path.dart
index fda0c51..d3c7512 100644
--- a/lib/path.dart
+++ b/lib/path.dart
@@ -363,6 +363,18 @@
 ///     p.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
 String withoutExtension(String path) => context.withoutExtension(path);
 
+/// Returns [path] with the trailing extension set to [extension].
+///
+/// If [path] doesn't have a trailing extension, this just adds [extension] to
+/// the end.
+///
+///     p.setExtension('path/to/foo.dart', '.js') // -> 'path/to/foo.js'
+///     p.setExtension('path/to/foo.dart.js', '.map')
+///       // -> 'path/to/foo.dart.map'
+///     p.setExtension('path/to/foo', '.js') // -> 'path/to/foo.js'
+String setExtension(String path, String extension) =>
+    context.setExtension(path, extension);
+
 /// Returns the path represented by [uri], which may be a [String] or a [Uri].
 ///
 /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL
diff --git a/lib/src/context.dart b/lib/src/context.dart
index 108955b..8d022e0 100644
--- a/lib/src/context.dart
+++ b/lib/src/context.dart
@@ -938,6 +938,20 @@
     return parsed.toString();
   }
 
+  /// Returns [path] with the trailing extension set to [extension].
+  ///
+  /// If [path] doesn't have a trailing extension, this just adds [extension] to
+  /// the end.
+  ///
+  ///     context.setExtension('path/to/foo.dart', '.js')
+  ///       // -> 'path/to/foo.js'
+  ///     context.setExtension('path/to/foo.dart.js', '.map')
+  ///       // -> 'path/to/foo.dart.map'
+  ///     context.setExtension('path/to/foo', '.js')
+  ///       // -> 'path/to/foo.js'
+  String setExtension(String path, String extension) =>
+      withoutExtension(path) + extension;
+
   /// Returns the path represented by [uri], which may be a [String] or a [Uri].
   ///
   /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL
diff --git a/pubspec.yaml b/pubspec.yaml
index 2c90e1c..5e94e09 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: path
-version: 1.4.2
+version: 1.5.0
 author: Dart Team <misc@dartlang.org>
 description: >
  A string-based path manipulation library. All of the path operations you know
diff --git a/test/posix_test.dart b/test/posix_test.dart
index 4549ebd..df3e6c5 100644
--- a/test/posix_test.dart
+++ b/test/posix_test.dart
@@ -534,6 +534,25 @@
     expect(context.withoutExtension('a/b.c//'), 'a/b//');
   });
 
+  test('setExtension', () {
+    expect(context.setExtension('', '.x'), '.x');
+    expect(context.setExtension('a', '.x'), 'a.x');
+    expect(context.setExtension('.a', '.x'), '.a.x');
+    expect(context.setExtension('a.b', '.x'), 'a.x');
+    expect(context.setExtension('a/b.c', '.x'), 'a/b.x');
+    expect(context.setExtension('a/b.c.d', '.x'), 'a/b.c.x');
+    expect(context.setExtension('a/', '.x'), 'a/.x');
+    expect(context.setExtension('a/b/', '.x'), 'a/b/.x');
+    expect(context.setExtension('a/.', '.x'), 'a/..x');
+    expect(context.setExtension('a/.b', '.x'), 'a/.b.x');
+    expect(context.setExtension('a.b/c', '.x'), 'a.b/c.x');
+    expect(context.setExtension(r'a.b\c', '.x'), r'a.x');
+    expect(context.setExtension(r'a/b\c', '.x'), r'a/b\c.x');
+    expect(context.setExtension(r'a/b\c.d', '.x'), r'a/b\c.x');
+    expect(context.setExtension('a/b.c/', '.x'), 'a/b/.x');
+    expect(context.setExtension('a/b.c//', '.x'), 'a/b//.x');
+  });
+
   group('fromUri', () {
     test('with a URI', () {
       expect(context.fromUri(Uri.parse('file:///path/to/foo')), '/path/to/foo');
diff --git a/test/url_test.dart b/test/url_test.dart
index b8f4e0f..8f912b7 100644
--- a/test/url_test.dart
+++ b/test/url_test.dart
@@ -819,6 +819,25 @@
     expect(context.withoutExtension('a/b.c//'), 'a/b//');
   });
 
+  test('withoutExtension', () {
+    expect(context.setExtension('', '.x'), '.x');
+    expect(context.setExtension('a', '.x'), 'a.x');
+    expect(context.setExtension('.a', '.x'), '.a.x');
+    expect(context.setExtension('a.b', '.x'), 'a.x');
+    expect(context.setExtension('a/b.c', '.x'), 'a/b.x');
+    expect(context.setExtension('a/b.c.d', '.x'), 'a/b.c.x');
+    expect(context.setExtension('a/', '.x'), 'a/.x');
+    expect(context.setExtension('a/b/', '.x'), 'a/b/.x');
+    expect(context.setExtension('a/.', '.x'), 'a/..x');
+    expect(context.setExtension('a/.b', '.x'), 'a/.b.x');
+    expect(context.setExtension('a.b/c', '.x'), 'a.b/c.x');
+    expect(context.setExtension(r'a.b\c', '.x'), r'a.x');
+    expect(context.setExtension(r'a/b\c', '.x'), r'a/b\c.x');
+    expect(context.setExtension(r'a/b\c.d', '.x'), r'a/b\c.x');
+    expect(context.setExtension('a/b.c/', '.x'), 'a/b/.x');
+    expect(context.setExtension('a/b.c//', '.x'), 'a/b//.x');
+  });
+
   group('fromUri', () {
     test('with a URI', () {
       expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo')),
diff --git a/test/windows_test.dart b/test/windows_test.dart
index 364db34..e24a3fc 100644
--- a/test/windows_test.dart
+++ b/test/windows_test.dart
@@ -684,6 +684,25 @@
     expect(context.withoutExtension(r'a\b.c\'), r'a\b\');
   });
 
+  test('withoutExtension', () {
+    expect(context.setExtension('', '.x'), '.x');
+    expect(context.setExtension('a', '.x'), 'a.x');
+    expect(context.setExtension('.a', '.x'), '.a.x');
+    expect(context.setExtension('a.b', '.x'), 'a.x');
+    expect(context.setExtension(r'a\b.c', '.x'), r'a\b.x');
+    expect(context.setExtension(r'a\b.c.d', '.x'), r'a\b.c.x');
+    expect(context.setExtension(r'a\', '.x'), r'a\.x');
+    expect(context.setExtension(r'a\b\', '.x'), r'a\b\.x');
+    expect(context.setExtension(r'a\.', '.x'), r'a\..x');
+    expect(context.setExtension(r'a\.b', '.x'), r'a\.b.x');
+    expect(context.setExtension(r'a.b\c', '.x'), r'a.b\c.x');
+    expect(context.setExtension(r'a/b.c/d', '.x'), r'a/b.c/d.x');
+    expect(context.setExtension(r'a\b/c', '.x'), r'a\b/c.x');
+    expect(context.setExtension(r'a\b/c.d', '.x'), r'a\b/c.x');
+    expect(context.setExtension(r'a.b/c', '.x'), r'a.b/c.x');
+    expect(context.setExtension(r'a\b.c\', '.x'), r'a\b\.x');
+  });
+
   group('fromUri', () {
     test('with a URI', () {
       expect(context.fromUri(Uri.parse('file:///C:/path/to/foo')),