[Pkg-javascript-commits] [node-source-map] 01/02: Imported Upstream version 0.1.33

Leo Iannacone l3on-guest at moszumanska.debian.org
Tue May 6 09:44:42 UTC 2014


This is an automated email from the git hooks/post-receive script.

l3on-guest pushed a commit to branch master
in repository node-source-map.

commit fa57402866f1fedc6df352baf030b7d1bc9ca994
Author: Leo Iannacone <l3on at ubuntu.com>
Date:   Wed Apr 30 11:51:24 2014 +0200

    Imported Upstream version 0.1.33
---
 .travis.yml                                  |   4 +
 CHANGELOG.md                                 | 130 +++++++
 LICENSE                                      |  28 ++
 Makefile.dryice.js                           | 166 ++++++++
 README.md                                    | 446 ++++++++++++++++++++++
 build/assert-shim.js                         |  56 +++
 build/mini-require.js                        | 152 ++++++++
 build/prefix-source-map.jsm                  |  20 +
 build/prefix-utils.jsm                       |  18 +
 build/suffix-browser.js                      |   8 +
 build/suffix-source-map.jsm                  |   6 +
 build/suffix-utils.jsm                       |  21 ++
 build/test-prefix.js                         |   8 +
 build/test-suffix.js                         |   3 +
 lib/source-map.js                            |   8 +
 lib/source-map/array-set.js                  |  97 +++++
 lib/source-map/base64-vlq.js                 | 144 +++++++
 lib/source-map/base64.js                     |  42 +++
 lib/source-map/binary-search.js              |  81 ++++
 lib/source-map/source-map-consumer.js        | 478 ++++++++++++++++++++++++
 lib/source-map/source-map-generator.js       | 397 ++++++++++++++++++++
 lib/source-map/source-node.js                | 387 +++++++++++++++++++
 lib/source-map/util.js                       | 302 +++++++++++++++
 package.json                                 |  56 +++
 test/run-tests.js                            |  62 +++
 test/source-map/test-api.js                  |  26 ++
 test/source-map/test-array-set.js            | 104 ++++++
 test/source-map/test-base64-vlq.js           |  24 ++
 test/source-map/test-base64.js               |  35 ++
 test/source-map/test-binary-search.js        |  54 +++
 test/source-map/test-dog-fooding.js          |  84 +++++
 test/source-map/test-source-map-consumer.js  | 475 +++++++++++++++++++++++
 test/source-map/test-source-map-generator.js | 540 +++++++++++++++++++++++++++
 test/source-map/test-source-node.js          | 439 ++++++++++++++++++++++
 test/source-map/test-util.js                 | 127 +++++++
 test/source-map/util.js                      | 161 ++++++++
 36 files changed, 5189 insertions(+)

diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..ddc9c4f
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+  - 0.8
+  - "0.10"
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..a0e1b9c
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,130 @@
+# Change Log
+
+## 0.1.33
+
+* Fix some edge cases surrounding path joining and URL resolution.
+
+* Add a third parameter for relative path to
+  `SourceMapGenerator.prototype.applySourceMap`.
+
+* Fix issues with mappings and EOLs.
+
+## 0.1.32
+
+* Fixed a bug where SourceMapConsumer couldn't handle negative relative columns
+  (issue 92).
+
+* Fixed test runner to actually report number of failed tests as its process
+  exit code.
+
+* Fixed a typo when reporting bad mappings (issue 87).
+
+## 0.1.31
+
+* Delay parsing the mappings in SourceMapConsumer until queried for a source
+  location.
+
+* Support Sass source maps (which at the time of writing deviate from the spec
+  in small ways) in SourceMapConsumer.
+
+## 0.1.30
+
+* Do not join source root with a source, when the source is a data URI.
+
+* Extend the test runner to allow running single specific test files at a time.
+
+* Performance improvements in `SourceNode.prototype.walk` and
+  `SourceMapConsumer.prototype.eachMapping`.
+
+* Source map browser builds will now work inside Workers.
+
+* Better error messages when attempting to add an invalid mapping to a
+  `SourceMapGenerator`.
+
+## 0.1.29
+
+* Allow duplicate entries in the `names` and `sources` arrays of source maps
+  (usually from TypeScript) we are parsing. Fixes github isse 72.
+
+## 0.1.28
+
+* Skip duplicate mappings when creating source maps from SourceNode; github
+  issue 75.
+
+## 0.1.27
+
+* Don't throw an error when the `file` property is missing in SourceMapConsumer,
+  we don't use it anyway.
+
+## 0.1.26
+
+* Fix SourceNode.fromStringWithSourceMap for empty maps. Fixes github issue 70.
+
+## 0.1.25
+
+* Make compatible with browserify
+
+## 0.1.24
+
+* Fix issue with absolute paths and `file://` URIs. See
+  https://bugzilla.mozilla.org/show_bug.cgi?id=885597
+
+## 0.1.23
+
+* Fix issue with absolute paths and sourcesContent, github issue 64.
+
+## 0.1.22
+
+* Ignore duplicate mappings in SourceMapGenerator. Fixes github issue 21.
+
+## 0.1.21
+
+* Fixed handling of sources that start with a slash so that they are relative to
+  the source root's host.
+
+## 0.1.20
+
+* Fixed github issue #43: absolute URLs aren't joined with the source root
+  anymore.
+
+## 0.1.19
+
+* Using Travis CI to run tests.
+
+## 0.1.18
+
+* Fixed a bug in the handling of sourceRoot.
+
+## 0.1.17
+
+* Added SourceNode.fromStringWithSourceMap.
+
+## 0.1.16
+
+* Added missing documentation.
+
+* Fixed the generating of empty mappings in SourceNode.
+
+## 0.1.15
+
+* Added SourceMapGenerator.applySourceMap.
+
+## 0.1.14
+
+* The sourceRoot is now handled consistently.
+
+## 0.1.13
+
+* Added SourceMapGenerator.fromSourceMap.
+
+## 0.1.12
+
+* SourceNode now generates empty mappings too.
+
+## 0.1.11
+
+* Added name support to SourceNode.
+
+## 0.1.10
+
+* Added sourcesContent support to the customer and generator.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ed1b7cf
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+
+Copyright (c) 2009-2011, Mozilla Foundation and contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the names of the Mozilla Foundation nor the names of project
+  contributors may be used to endorse or promote products derived from this
+  software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile.dryice.js b/Makefile.dryice.js
new file mode 100644
index 0000000..d6fc26a
--- /dev/null
+++ b/Makefile.dryice.js
@@ -0,0 +1,166 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+var path = require('path');
+var fs = require('fs');
+var copy = require('dryice').copy;
+
+function removeAmdefine(src) {
+  src = String(src).replace(
+    /if\s*\(typeof\s*define\s*!==\s*'function'\)\s*{\s*var\s*define\s*=\s*require\('amdefine'\)\(module,\s*require\);\s*}\s*/g,
+    '');
+  src = src.replace(
+    /\b(define\(.*)('amdefine',?)/gm,
+    '$1');
+  return src;
+}
+removeAmdefine.onRead = true;
+
+function makeNonRelative(src) {
+  return src
+    .replace(/require\('.\//g, 'require(\'source-map/')
+    .replace(/\.\.\/\.\.\/lib\//g, '');
+}
+makeNonRelative.onRead = true;
+
+function buildBrowser() {
+  console.log('\nCreating dist/source-map.js');
+
+  var project = copy.createCommonJsProject({
+    roots: [ path.join(__dirname, 'lib') ]
+  });
+
+  copy({
+    source: [
+      'build/mini-require.js',
+      {
+        project: project,
+        require: [ 'source-map/source-map-generator',
+                   'source-map/source-map-consumer',
+                   'source-map/source-node']
+      },
+      'build/suffix-browser.js'
+    ],
+    filter: [
+      copy.filter.moduleDefines,
+      removeAmdefine
+    ],
+    dest: 'dist/source-map.js'
+  });
+}
+
+function buildBrowserMin() {
+  console.log('\nCreating dist/source-map.min.js');
+
+  copy({
+    source: 'dist/source-map.js',
+    filter: copy.filter.uglifyjs,
+    dest: 'dist/source-map.min.js'
+  });
+}
+
+function buildFirefox() {
+  console.log('\nCreating dist/SourceMap.jsm');
+
+  var project = copy.createCommonJsProject({
+    roots: [ path.join(__dirname, 'lib') ]
+  });
+
+  copy({
+    source: [
+      'build/prefix-source-map.jsm',
+      {
+        project: project,
+        require: [ 'source-map/source-map-consumer',
+                   'source-map/source-map-generator',
+                   'source-map/source-node' ]
+      },
+      'build/suffix-source-map.jsm'
+    ],
+    filter: [
+      copy.filter.moduleDefines,
+      removeAmdefine,
+      makeNonRelative
+    ],
+    dest: 'dist/SourceMap.jsm'
+  });
+
+  // Create dist/test/Utils.jsm
+  console.log('\nCreating dist/test/Utils.jsm');
+
+  project = copy.createCommonJsProject({
+    roots: [ __dirname, path.join(__dirname, 'lib') ]
+  });
+
+  copy({
+    source: [
+      'build/prefix-utils.jsm',
+      'build/assert-shim.js',
+      {
+        project: project,
+        require: [ 'test/source-map/util' ]
+      },
+      'build/suffix-utils.jsm'
+    ],
+    filter: [
+      copy.filter.moduleDefines,
+      removeAmdefine,
+      makeNonRelative
+    ],
+    dest: 'dist/test/Utils.jsm'
+  });
+
+  function isTestFile(f) {
+    return /^test\-.*?\.js/.test(f);
+  }
+
+  var testFiles = fs.readdirSync(path.join(__dirname, 'test', 'source-map')).filter(isTestFile);
+
+  testFiles.forEach(function (testFile) {
+    console.log('\nCreating', path.join('dist', 'test', testFile.replace(/\-/g, '_')));
+
+    copy({
+      source: [
+        'build/test-prefix.js',
+        path.join('test', 'source-map', testFile),
+        'build/test-suffix.js'
+      ],
+      filter: [
+        removeAmdefine,
+        makeNonRelative,
+        function (input, source) {
+          return input.replace('define(',
+                               'define("'
+                               + path.join('test', 'source-map', testFile.replace(/\.js$/, ''))
+                               + '", ["require", "exports", "module"], ');
+        },
+        function (input, source) {
+          return input.replace('{THIS_MODULE}', function () {
+            return "test/source-map/" + testFile.replace(/\.js$/, '');
+          });
+        }
+      ],
+      dest: path.join('dist', 'test', testFile.replace(/\-/g, '_'))
+    });
+  });
+}
+
+function ensureDir(name) {
+  var dirExists = false;
+  try {
+    dirExists = fs.statSync(name).isDirectory();
+  } catch (err) {}
+
+  if (!dirExists) {
+    fs.mkdirSync(name, 0777);
+  }
+}
+
+ensureDir("dist");
+ensureDir("dist/test");
+buildFirefox();
+buildBrowser();
+buildBrowserMin();
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b00e970
--- /dev/null
+++ b/README.md
@@ -0,0 +1,446 @@
+# Source Map
+
+This is a library to generate and consume the source map format
+[described here][format].
+
+This library is written in the Asynchronous Module Definition format, and works
+in the following environments:
+
+* Modern Browsers supporting ECMAScript 5 (either after the build, or with an
+  AMD loader such as RequireJS)
+
+* Inside Firefox (as a JSM file, after the build)
+
+* With NodeJS versions 0.8.X and higher
+
+## Node
+
+    $ npm install source-map
+
+## Building from Source (for everywhere else)
+
+Install Node and then run
+
+    $ git clone https://fitzgen@github.com/mozilla/source-map.git
+    $ cd source-map
+    $ npm link .
+
+Next, run
+
+    $ node Makefile.dryice.js
+
+This should spew a bunch of stuff to stdout, and create the following files:
+
+* `dist/source-map.js` - The unminified browser version.
+
+* `dist/source-map.min.js` - The minified browser version.
+
+* `dist/SourceMap.jsm` - The JavaScript Module for inclusion in Firefox source.
+
+## Examples
+
+### Consuming a source map
+
+    var rawSourceMap = {
+      version: 3,
+      file: 'min.js',
+      names: ['bar', 'baz', 'n'],
+      sources: ['one.js', 'two.js'],
+      sourceRoot: 'http://example.com/www/js/',
+      mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+    };
+
+    var smc = new SourceMapConsumer(rawSourceMap);
+
+    console.log(smc.sources);
+    // [ 'http://example.com/www/js/one.js',
+    //   'http://example.com/www/js/two.js' ]
+
+    console.log(smc.originalPositionFor({
+      line: 2,
+      column: 28
+    }));
+    // { source: 'http://example.com/www/js/two.js',
+    //   line: 2,
+    //   column: 10,
+    //   name: 'n' }
+
+    console.log(smc.generatedPositionFor({
+      source: 'http://example.com/www/js/two.js',
+      line: 2,
+      column: 10
+    }));
+    // { line: 2, column: 28 }
+
+    smc.eachMapping(function (m) {
+      // ...
+    });
+
+### Generating a source map
+
+In depth guide:
+[**Compiling to JavaScript, and Debugging with Source Maps**](https://hacks.mozilla.org/2013/05/compiling-to-javascript-and-debugging-with-source-maps/)
+
+#### With SourceNode (high level API)
+
+    function compile(ast) {
+      switch (ast.type) {
+      case 'BinaryExpression':
+        return new SourceNode(
+          ast.location.line,
+          ast.location.column,
+          ast.location.source,
+          [compile(ast.left), " + ", compile(ast.right)]
+        );
+      case 'Literal':
+        return new SourceNode(
+          ast.location.line,
+          ast.location.column,
+          ast.location.source,
+          String(ast.value)
+        );
+      // ...
+      default:
+        throw new Error("Bad AST");
+      }
+    }
+
+    var ast = parse("40 + 2", "add.js");
+    console.log(compile(ast).toStringWithSourceMap({
+      file: 'add.js'
+    }));
+    // { code: '40 + 2',
+    //   map: [object SourceMapGenerator] }
+
+#### With SourceMapGenerator (low level API)
+
+    var map = new SourceMapGenerator({
+      file: "source-mapped.js"
+    });
+
+    map.addMapping({
+      generated: {
+        line: 10,
+        column: 35
+      },
+      source: "foo.js",
+      original: {
+        line: 33,
+        column: 2
+      },
+      name: "christopher"
+    });
+
+    console.log(map.toString());
+    // '{"version":3,"file":"source-mapped.js","sources":["foo.js"],"names":["christopher"],"mappings":";;;;;;;;;mCAgCEA"}'
+
+## API
+
+Get a reference to the module:
+
+    // NodeJS
+    var sourceMap = require('source-map');
+
+    // Browser builds
+    var sourceMap = window.sourceMap;
+
+    // Inside Firefox
+    let sourceMap = {};
+    Components.utils.import('resource:///modules/devtools/SourceMap.jsm', sourceMap);
+
+### SourceMapConsumer
+
+A SourceMapConsumer instance represents a parsed source map which we can query
+for information about the original file positions by giving it a file position
+in the generated source.
+
+#### new SourceMapConsumer(rawSourceMap)
+
+The only parameter is the raw source map (either as a string which can be
+`JSON.parse`'d, or an object). According to the spec, source maps have the
+following attributes:
+
+* `version`: Which version of the source map spec this map is following.
+
+* `sources`: An array of URLs to the original source files.
+
+* `names`: An array of identifiers which can be referrenced by individual
+  mappings.
+
+* `sourceRoot`: Optional. The URL root from which all sources are relative.
+
+* `sourcesContent`: Optional. An array of contents of the original source files.
+
+* `mappings`: A string of base64 VLQs which contain the actual mappings.
+
+* `file`: Optional. The generated filename this source map is associated with.
+
+#### SourceMapConsumer.prototype.originalPositionFor(generatedPosition)
+
+Returns the original source, line, and column information for the generated
+source's line and column positions provided. The only argument is an object with
+the following properties:
+
+* `line`: The line number in the generated source.
+
+* `column`: The column number in the generated source.
+
+and an object is returned with the following properties:
+
+* `source`: The original source file, or null if this information is not
+  available.
+
+* `line`: The line number in the original source, or null if this information is
+  not available.
+
+* `column`: The column number in the original source, or null or null if this
+  information is not available.
+
+* `name`: The original identifier, or null if this information is not available.
+
+#### SourceMapConsumer.prototype.generatedPositionFor(originalPosition)
+
+Returns the generated line and column information for the original source,
+line, and column positions provided. The only argument is an object with
+the following properties:
+
+* `source`: The filename of the original source.
+
+* `line`: The line number in the original source.
+
+* `column`: The column number in the original source.
+
+and an object is returned with the following properties:
+
+* `line`: The line number in the generated source, or null.
+
+* `column`: The column number in the generated source, or null.
+
+#### SourceMapConsumer.prototype.sourceContentFor(source)
+
+Returns the original source content for the source provided. The only
+argument is the URL of the original source file.
+
+#### SourceMapConsumer.prototype.eachMapping(callback, context, order)
+
+Iterate over each mapping between an original source/line/column and a
+generated line/column in this source map.
+
+* `callback`: The function that is called with each mapping. Mappings have the
+  form `{ source, generatedLine, generatedColumn, originalLine, originalColumn,
+  name }`
+
+* `context`: Optional. If specified, this object will be the value of `this`
+  every time that `callback` is called.
+
+* `order`: Either `SourceMapConsumer.GENERATED_ORDER` or
+  `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to iterate over
+  the mappings sorted by the generated file's line/column order or the
+  original's source/line/column order, respectively. Defaults to
+  `SourceMapConsumer.GENERATED_ORDER`.
+
+### SourceMapGenerator
+
+An instance of the SourceMapGenerator represents a source map which is being
+built incrementally.
+
+#### new SourceMapGenerator([startOfSourceMap])
+
+You may pass an object with the following properties:
+
+* `file`: The filename of the generated source that this source map is
+  associated with.
+
+* `sourceRoot`: A root for all relative URLs in this source map.
+
+#### SourceMapGenerator.fromSourceMap(sourceMapConsumer)
+
+Creates a new SourceMapGenerator based on a SourceMapConsumer
+
+* `sourceMapConsumer` The SourceMap.
+
+#### SourceMapGenerator.prototype.addMapping(mapping)
+
+Add a single mapping from original source line and column to the generated
+source's line and column for this source map being created. The mapping object
+should have the following properties:
+
+* `generated`: An object with the generated line and column positions.
+
+* `original`: An object with the original line and column positions.
+
+* `source`: The original source file (relative to the sourceRoot).
+
+* `name`: An optional original token name for this mapping.
+
+#### SourceMapGenerator.prototype.setSourceContent(sourceFile, sourceContent)
+
+Set the source content for an original source file.
+
+* `sourceFile` the URL of the original source file.
+
+* `sourceContent` the content of the source file.
+
+#### SourceMapGenerator.prototype.applySourceMap(sourceMapConsumer[, sourceFile[, sourceMapPath]])
+
+Applies a SourceMap for a source file to the SourceMap.
+Each mapping to the supplied source file is rewritten using the
+supplied SourceMap. Note: The resolution for the resulting mappings
+is the minimium of this map and the supplied map.
+
+* `sourceMapConsumer`: The SourceMap to be applied.
+
+* `sourceFile`: Optional. The filename of the source file.
+  If omitted, sourceMapConsumer.file will be used, if it exists.
+  Otherwise an error will be thrown.
+
+* `sourceMapPath`: Optional. The dirname of the path to the SourceMap
+  to be applied. If relative, it is relative to the SourceMap.
+
+  This parameter is needed when the two SourceMaps aren't in the same
+  directory, and the SourceMap to be applied contains relative source
+  paths. If so, those relative source paths need to be rewritten
+  relative to the SourceMap.
+
+  If omitted, it is assumed that both SourceMaps are in the same directory,
+  thus not needing any rewriting. (Supplying `'.'` has the same effect.)
+
+#### SourceMapGenerator.prototype.toString()
+
+Renders the source map being generated to a string.
+
+### SourceNode
+
+SourceNodes provide a way to abstract over interpolating and/or concatenating
+snippets of generated JavaScript source code, while maintaining the line and
+column information associated between those snippets and the original source
+code. This is useful as the final intermediate representation a compiler might
+use before outputting the generated JS and source map.
+
+#### new SourceNode([line, column, source[, chunk[, name]]])
+
+* `line`: The original line number associated with this source node, or null if
+  it isn't associated with an original line.
+
+* `column`: The original column number associated with this source node, or null
+  if it isn't associated with an original column.
+
+* `source`: The original source's filename; null if no filename is provided.
+
+* `chunk`: Optional. Is immediately passed to `SourceNode.prototype.add`, see
+  below.
+
+* `name`: Optional. The original identifier.
+
+#### SourceNode.fromStringWithSourceMap(code, sourceMapConsumer)
+
+Creates a SourceNode from generated code and a SourceMapConsumer.
+
+* `code`: The generated code
+
+* `sourceMapConsumer` The SourceMap for the generated code
+
+#### SourceNode.prototype.add(chunk)
+
+Add a chunk of generated JS to this source node.
+
+* `chunk`: A string snippet of generated JS code, another instance of
+   `SourceNode`, or an array where each member is one of those things.
+
+#### SourceNode.prototype.prepend(chunk)
+
+Prepend a chunk of generated JS to this source node.
+
+* `chunk`: A string snippet of generated JS code, another instance of
+   `SourceNode`, or an array where each member is one of those things.
+
+#### SourceNode.prototype.setSourceContent(sourceFile, sourceContent)
+
+Set the source content for a source file. This will be added to the
+`SourceMap` in the `sourcesContent` field.
+
+* `sourceFile`: The filename of the source file
+
+* `sourceContent`: The content of the source file
+
+#### SourceNode.prototype.walk(fn)
+
+Walk over the tree of JS snippets in this node and its children. The walking
+function is called once for each snippet of JS and is passed that snippet and
+the its original associated source's line/column location.
+
+* `fn`: The traversal function.
+
+#### SourceNode.prototype.walkSourceContents(fn)
+
+Walk over the tree of SourceNodes. The walking function is called for each
+source file content and is passed the filename and source content.
+
+* `fn`: The traversal function.
+
+#### SourceNode.prototype.join(sep)
+
+Like `Array.prototype.join` except for SourceNodes. Inserts the separator
+between each of this source node's children.
+
+* `sep`: The separator.
+
+#### SourceNode.prototype.replaceRight(pattern, replacement)
+
+Call `String.prototype.replace` on the very right-most source snippet. Useful
+for trimming whitespace from the end of a source node, etc.
+
+* `pattern`: The pattern to replace.
+
+* `replacement`: The thing to replace the pattern with.
+
+#### SourceNode.prototype.toString()
+
+Return the string representation of this source node. Walks over the tree and
+concatenates all the various snippets together to one string.
+
+### SourceNode.prototype.toStringWithSourceMap([startOfSourceMap])
+
+Returns the string representation of this tree of source nodes, plus a
+SourceMapGenerator which contains all the mappings between the generated and
+original sources.
+
+The arguments are the same as those to `new SourceMapGenerator`.
+
+## Tests
+
+[![Build Status](https://travis-ci.org/mozilla/source-map.png?branch=master)](https://travis-ci.org/mozilla/source-map)
+
+Install NodeJS version 0.8.0 or greater, then run `node test/run-tests.js`.
+
+To add new tests, create a new file named `test/test-<your new test name>.js`
+and export your test functions with names that start with "test", for example
+
+    exports["test doing the foo bar"] = function (assert, util) {
+      ...
+    };
+
+The new test will be located automatically when you run the suite.
+
+The `util` argument is the test utility module located at `test/source-map/util`.
+
+The `assert` argument is a cut down version of node's assert module. You have
+access to the following assertion functions:
+
+* `doesNotThrow`
+
+* `equal`
+
+* `ok`
+
+* `strictEqual`
+
+* `throws`
+
+(The reason for the restricted set of test functions is because we need the
+tests to run inside Firefox's test suite as well and so the assert module is
+shimmed in that environment. See `build/assert-shim.js`.)
+
+[format]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
+[feature]: https://wiki.mozilla.org/DevTools/Features/SourceMap
+[Dryice]: https://github.com/mozilla/dryice
diff --git a/build/assert-shim.js b/build/assert-shim.js
new file mode 100644
index 0000000..daa1a62
--- /dev/null
+++ b/build/assert-shim.js
@@ -0,0 +1,56 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+define('test/source-map/assert', ['exports'], function (exports) {
+
+  let do_throw = function (msg) {
+    throw new Error(msg);
+  };
+
+  exports.init = function (throw_fn) {
+    do_throw = throw_fn;
+  };
+
+  exports.doesNotThrow = function (fn) {
+    try {
+      fn();
+    }
+    catch (e) {
+      do_throw(e.message);
+    }
+  };
+
+  exports.equal = function (actual, expected, msg) {
+    msg = msg || String(actual) + ' != ' + String(expected);
+    if (actual != expected) {
+      do_throw(msg);
+    }
+  };
+
+  exports.ok = function (val, msg) {
+    msg = msg || String(val) + ' is falsey';
+    if (!Boolean(val)) {
+      do_throw(msg);
+    }
+  };
+
+  exports.strictEqual = function (actual, expected, msg) {
+    msg = msg || String(actual) + ' !== ' + String(expected);
+    if (actual !== expected) {
+      do_throw(msg);
+    }
+  };
+
+  exports.throws = function (fn) {
+    try {
+      fn();
+      do_throw('Expected an error to be thrown, but it wasn\'t.');
+    }
+    catch (e) {
+    }
+  };
+
+});
diff --git a/build/mini-require.js b/build/mini-require.js
new file mode 100644
index 0000000..0daf453
--- /dev/null
+++ b/build/mini-require.js
@@ -0,0 +1,152 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/**
+ * Define a module along with a payload.
+ * @param {string} moduleName Name for the payload
+ * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec
+ * @param {function} payload Function with (require, exports, module) params
+ */
+function define(moduleName, deps, payload) {
+  if (typeof moduleName != "string") {
+    throw new TypeError('Expected string, got: ' + moduleName);
+  }
+
+  if (arguments.length == 2) {
+    payload = deps;
+  }
+
+  if (moduleName in define.modules) {
+    throw new Error("Module already defined: " + moduleName);
+  }
+  define.modules[moduleName] = payload;
+};
+
+/**
+ * The global store of un-instantiated modules
+ */
+define.modules = {};
+
+
+/**
+ * We invoke require() in the context of a Domain so we can have multiple
+ * sets of modules running separate from each other.
+ * This contrasts with JSMs which are singletons, Domains allows us to
+ * optionally load a CommonJS module twice with separate data each time.
+ * Perhaps you want 2 command lines with a different set of commands in each,
+ * for example.
+ */
+function Domain() {
+  this.modules = {};
+  this._currentModule = null;
+}
+
+(function () {
+
+  /**
+   * Lookup module names and resolve them by calling the definition function if
+   * needed.
+   * There are 2 ways to call this, either with an array of dependencies and a
+   * callback to call when the dependencies are found (which can happen
+   * asynchronously in an in-page context) or with a single string an no callback
+   * where the dependency is resolved synchronously and returned.
+   * The API is designed to be compatible with the CommonJS AMD spec and
+   * RequireJS.
+   * @param {string[]|string} deps A name, or names for the payload
+   * @param {function|undefined} callback Function to call when the dependencies
+   * are resolved
+   * @return {undefined|object} The module required or undefined for
+   * array/callback method
+   */
+  Domain.prototype.require = function(deps, callback) {
+    if (Array.isArray(deps)) {
+      var params = deps.map(function(dep) {
+        return this.lookup(dep);
+      }, this);
+      if (callback) {
+        callback.apply(null, params);
+      }
+      return undefined;
+    }
+    else {
+      return this.lookup(deps);
+    }
+  };
+
+  function normalize(path) {
+    var bits = path.split('/');
+    var i = 1;
+    while (i < bits.length) {
+      if (bits[i] === '..') {
+        bits.splice(i-1, 1);
+      } else if (bits[i] === '.') {
+        bits.splice(i, 1);
+      } else {
+        i++;
+      }
+    }
+    return bits.join('/');
+  }
+
+  function join(a, b) {
+    a = a.trim();
+    b = b.trim();
+    if (/^\//.test(b)) {
+      return b;
+    } else {
+      return a.replace(/\/*$/, '/') + b;
+    }
+  }
+
+  function dirname(path) {
+    var bits = path.split('/');
+    bits.pop();
+    return bits.join('/');
+  }
+
+  /**
+   * Lookup module names and resolve them by calling the definition function if
+   * needed.
+   * @param {string} moduleName A name for the payload to lookup
+   * @return {object} The module specified by aModuleName or null if not found.
+   */
+  Domain.prototype.lookup = function(moduleName) {
+    if (/^\./.test(moduleName)) {
+      moduleName = normalize(join(dirname(this._currentModule), moduleName));
+    }
+
+    if (moduleName in this.modules) {
+      var module = this.modules[moduleName];
+      return module;
+    }
+
+    if (!(moduleName in define.modules)) {
+      throw new Error("Module not defined: " + moduleName);
+    }
+
+    var module = define.modules[moduleName];
+
+    if (typeof module == "function") {
+      var exports = {};
+      var previousModule = this._currentModule;
+      this._currentModule = moduleName;
+      module(this.require.bind(this), exports, { id: moduleName, uri: "" });
+      this._currentModule = previousModule;
+      module = exports;
+    }
+
+    // cache the resulting module object for next time
+    this.modules[moduleName] = module;
+
+    return module;
+  };
+
+}());
+
+define.Domain = Domain;
+define.globalDomain = new Domain();
+var require = define.globalDomain.require.bind(define.globalDomain);
diff --git a/build/prefix-source-map.jsm b/build/prefix-source-map.jsm
new file mode 100644
index 0000000..ee2539d
--- /dev/null
+++ b/build/prefix-source-map.jsm
@@ -0,0 +1,20 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+this.EXPORTED_SYMBOLS = [ "SourceMapConsumer", "SourceMapGenerator", "SourceNode" ];
+
+Components.utils.import('resource://gre/modules/devtools/Require.jsm');
diff --git a/build/prefix-utils.jsm b/build/prefix-utils.jsm
new file mode 100644
index 0000000..80341d4
--- /dev/null
+++ b/build/prefix-utils.jsm
@@ -0,0 +1,18 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+Components.utils.import('resource://gre/modules/devtools/Require.jsm');
+Components.utils.import('resource://gre/modules/devtools/SourceMap.jsm');
+
+this.EXPORTED_SYMBOLS = [ "define", "runSourceMapTests" ];
diff --git a/build/suffix-browser.js b/build/suffix-browser.js
new file mode 100644
index 0000000..fb29ff5
--- /dev/null
+++ b/build/suffix-browser.js
@@ -0,0 +1,8 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+///////////////////////////////////////////////////////////////////////////////
+
+this.sourceMap = {
+  SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer,
+  SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator,
+  SourceNode: require('source-map/source-node').SourceNode
+};
diff --git a/build/suffix-source-map.jsm b/build/suffix-source-map.jsm
new file mode 100644
index 0000000..cf3c2d8
--- /dev/null
+++ b/build/suffix-source-map.jsm
@@ -0,0 +1,6 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+///////////////////////////////////////////////////////////////////////////////
+
+this.SourceMapConsumer = require('source-map/source-map-consumer').SourceMapConsumer;
+this.SourceMapGenerator = require('source-map/source-map-generator').SourceMapGenerator;
+this.SourceNode = require('source-map/source-node').SourceNode;
diff --git a/build/suffix-utils.jsm b/build/suffix-utils.jsm
new file mode 100644
index 0000000..b31b84c
--- /dev/null
+++ b/build/suffix-utils.jsm
@@ -0,0 +1,21 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+function runSourceMapTests(modName, do_throw) {
+  let mod = require(modName);
+  let assert = require('test/source-map/assert');
+  let util = require('test/source-map/util');
+
+  assert.init(do_throw);
+
+  for (let k in mod) {
+    if (/^test/.test(k)) {
+      mod[k](assert, util);
+    }
+  }
+
+}
+this.runSourceMapTests = runSourceMapTests;
diff --git a/build/test-prefix.js b/build/test-prefix.js
new file mode 100644
index 0000000..1b13f30
--- /dev/null
+++ b/build/test-prefix.js
@@ -0,0 +1,8 @@
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+Components.utils.import('resource://test/Utils.jsm');
diff --git a/build/test-suffix.js b/build/test-suffix.js
new file mode 100644
index 0000000..bec2de3
--- /dev/null
+++ b/build/test-suffix.js
@@ -0,0 +1,3 @@
+function run_test() {
+  runSourceMapTests('{THIS_MODULE}', do_throw);
+}
diff --git a/lib/source-map.js b/lib/source-map.js
new file mode 100644
index 0000000..121ad24
--- /dev/null
+++ b/lib/source-map.js
@@ -0,0 +1,8 @@
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+exports.SourceMapGenerator = require('./source-map/source-map-generator').SourceMapGenerator;
+exports.SourceMapConsumer = require('./source-map/source-map-consumer').SourceMapConsumer;
+exports.SourceNode = require('./source-map/source-node').SourceNode;
diff --git a/lib/source-map/array-set.js b/lib/source-map/array-set.js
new file mode 100644
index 0000000..40f9a18
--- /dev/null
+++ b/lib/source-map/array-set.js
@@ -0,0 +1,97 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var util = require('./util');
+
+  /**
+   * A data structure which is a combination of an array and a set. Adding a new
+   * member is O(1), testing for membership is O(1), and finding the index of an
+   * element is O(1). Removing elements from the set is not supported. Only
+   * strings are supported for membership.
+   */
+  function ArraySet() {
+    this._array = [];
+    this._set = {};
+  }
+
+  /**
+   * Static method for creating ArraySet instances from an existing array.
+   */
+  ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) {
+    var set = new ArraySet();
+    for (var i = 0, len = aArray.length; i < len; i++) {
+      set.add(aArray[i], aAllowDuplicates);
+    }
+    return set;
+  };
+
+  /**
+   * Add the given string to this set.
+   *
+   * @param String aStr
+   */
+  ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
+    var isDuplicate = this.has(aStr);
+    var idx = this._array.length;
+    if (!isDuplicate || aAllowDuplicates) {
+      this._array.push(aStr);
+    }
+    if (!isDuplicate) {
+      this._set[util.toSetString(aStr)] = idx;
+    }
+  };
+
+  /**
+   * Is the given string a member of this set?
+   *
+   * @param String aStr
+   */
+  ArraySet.prototype.has = function ArraySet_has(aStr) {
+    return Object.prototype.hasOwnProperty.call(this._set,
+                                                util.toSetString(aStr));
+  };
+
+  /**
+   * What is the index of the given string in the array?
+   *
+   * @param String aStr
+   */
+  ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
+    if (this.has(aStr)) {
+      return this._set[util.toSetString(aStr)];
+    }
+    throw new Error('"' + aStr + '" is not in the set.');
+  };
+
+  /**
+   * What is the element at the given index?
+   *
+   * @param Number aIdx
+   */
+  ArraySet.prototype.at = function ArraySet_at(aIdx) {
+    if (aIdx >= 0 && aIdx < this._array.length) {
+      return this._array[aIdx];
+    }
+    throw new Error('No element indexed by ' + aIdx);
+  };
+
+  /**
+   * Returns the array representation of this set (which has the proper indices
+   * indicated by indexOf). Note that this is a copy of the internal array used
+   * for storing the members so that no one can mess with internal state.
+   */
+  ArraySet.prototype.toArray = function ArraySet_toArray() {
+    return this._array.slice();
+  };
+
+  exports.ArraySet = ArraySet;
+
+});
diff --git a/lib/source-map/base64-vlq.js b/lib/source-map/base64-vlq.js
new file mode 100644
index 0000000..1b67bb3
--- /dev/null
+++ b/lib/source-map/base64-vlq.js
@@ -0,0 +1,144 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ *
+ * Based on the Base 64 VLQ implementation in Closure Compiler:
+ * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
+ *
+ * Copyright 2011 The Closure Compiler Authors. All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution.
+ *  * Neither the name of Google Inc. nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var base64 = require('./base64');
+
+  // A single base 64 digit can contain 6 bits of data. For the base 64 variable
+  // length quantities we use in the source map spec, the first bit is the sign,
+  // the next four bits are the actual value, and the 6th bit is the
+  // continuation bit. The continuation bit tells us whether there are more
+  // digits in this value following this digit.
+  //
+  //   Continuation
+  //   |    Sign
+  //   |    |
+  //   V    V
+  //   101011
+
+  var VLQ_BASE_SHIFT = 5;
+
+  // binary: 100000
+  var VLQ_BASE = 1 << VLQ_BASE_SHIFT;
+
+  // binary: 011111
+  var VLQ_BASE_MASK = VLQ_BASE - 1;
+
+  // binary: 100000
+  var VLQ_CONTINUATION_BIT = VLQ_BASE;
+
+  /**
+   * Converts from a two-complement value to a value where the sign bit is
+   * is placed in the least significant bit.  For example, as decimals:
+   *   1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
+   *   2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
+   */
+  function toVLQSigned(aValue) {
+    return aValue < 0
+      ? ((-aValue) << 1) + 1
+      : (aValue << 1) + 0;
+  }
+
+  /**
+   * Converts to a two-complement value from a value where the sign bit is
+   * is placed in the least significant bit.  For example, as decimals:
+   *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
+   *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
+   */
+  function fromVLQSigned(aValue) {
+    var isNegative = (aValue & 1) === 1;
+    var shifted = aValue >> 1;
+    return isNegative
+      ? -shifted
+      : shifted;
+  }
+
+  /**
+   * Returns the base 64 VLQ encoded value.
+   */
+  exports.encode = function base64VLQ_encode(aValue) {
+    var encoded = "";
+    var digit;
+
+    var vlq = toVLQSigned(aValue);
+
+    do {
+      digit = vlq & VLQ_BASE_MASK;
+      vlq >>>= VLQ_BASE_SHIFT;
+      if (vlq > 0) {
+        // There are still more digits in this value, so we must make sure the
+        // continuation bit is marked.
+        digit |= VLQ_CONTINUATION_BIT;
+      }
+      encoded += base64.encode(digit);
+    } while (vlq > 0);
+
+    return encoded;
+  };
+
+  /**
+   * Decodes the next base 64 VLQ value from the given string and returns the
+   * value and the rest of the string.
+   */
+  exports.decode = function base64VLQ_decode(aStr) {
+    var i = 0;
+    var strLen = aStr.length;
+    var result = 0;
+    var shift = 0;
+    var continuation, digit;
+
+    do {
+      if (i >= strLen) {
+        throw new Error("Expected more digits in base 64 VLQ value.");
+      }
+      digit = base64.decode(aStr.charAt(i++));
+      continuation = !!(digit & VLQ_CONTINUATION_BIT);
+      digit &= VLQ_BASE_MASK;
+      result = result + (digit << shift);
+      shift += VLQ_BASE_SHIFT;
+    } while (continuation);
+
+    return {
+      value: fromVLQSigned(result),
+      rest: aStr.slice(i)
+    };
+  };
+
+});
diff --git a/lib/source-map/base64.js b/lib/source-map/base64.js
new file mode 100644
index 0000000..863cc46
--- /dev/null
+++ b/lib/source-map/base64.js
@@ -0,0 +1,42 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var charToIntMap = {};
+  var intToCharMap = {};
+
+  'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+    .split('')
+    .forEach(function (ch, index) {
+      charToIntMap[ch] = index;
+      intToCharMap[index] = ch;
+    });
+
+  /**
+   * Encode an integer in the range of 0 to 63 to a single base 64 digit.
+   */
+  exports.encode = function base64_encode(aNumber) {
+    if (aNumber in intToCharMap) {
+      return intToCharMap[aNumber];
+    }
+    throw new TypeError("Must be between 0 and 63: " + aNumber);
+  };
+
+  /**
+   * Decode a single base 64 digit to an integer.
+   */
+  exports.decode = function base64_decode(aChar) {
+    if (aChar in charToIntMap) {
+      return charToIntMap[aChar];
+    }
+    throw new TypeError("Not a valid base 64 digit: " + aChar);
+  };
+
+});
diff --git a/lib/source-map/binary-search.js b/lib/source-map/binary-search.js
new file mode 100644
index 0000000..ff347c6
--- /dev/null
+++ b/lib/source-map/binary-search.js
@@ -0,0 +1,81 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  /**
+   * Recursive implementation of binary search.
+   *
+   * @param aLow Indices here and lower do not contain the needle.
+   * @param aHigh Indices here and higher do not contain the needle.
+   * @param aNeedle The element being searched for.
+   * @param aHaystack The non-empty array being searched.
+   * @param aCompare Function which takes two elements and returns -1, 0, or 1.
+   */
+  function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) {
+    // This function terminates when one of the following is true:
+    //
+    //   1. We find the exact element we are looking for.
+    //
+    //   2. We did not find the exact element, but we can return the next
+    //      closest element that is less than that element.
+    //
+    //   3. We did not find the exact element, and there is no next-closest
+    //      element which is less than the one we are searching for, so we
+    //      return null.
+    var mid = Math.floor((aHigh - aLow) / 2) + aLow;
+    var cmp = aCompare(aNeedle, aHaystack[mid], true);
+    if (cmp === 0) {
+      // Found the element we are looking for.
+      return aHaystack[mid];
+    }
+    else if (cmp > 0) {
+      // aHaystack[mid] is greater than our needle.
+      if (aHigh - mid > 1) {
+        // The element is in the upper half.
+        return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare);
+      }
+      // We did not find an exact match, return the next closest one
+      // (termination case 2).
+      return aHaystack[mid];
+    }
+    else {
+      // aHaystack[mid] is less than our needle.
+      if (mid - aLow > 1) {
+        // The element is in the lower half.
+        return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare);
+      }
+      // The exact needle element was not found in this haystack. Determine if
+      // we are in termination case (2) or (3) and return the appropriate thing.
+      return aLow < 0
+        ? null
+        : aHaystack[aLow];
+    }
+  }
+
+  /**
+   * This is an implementation of binary search which will always try and return
+   * the next lowest value checked if there is no exact hit. This is because
+   * mappings between original and generated line/col pairs are single points,
+   * and there is an implicit region between each of them, so a miss just means
+   * that you aren't on the very start of a region.
+   *
+   * @param aNeedle The element you are looking for.
+   * @param aHaystack The array that is being searched.
+   * @param aCompare A function which takes the needle and an element in the
+   *     array and returns -1, 0, or 1 depending on whether the needle is less
+   *     than, equal to, or greater than the element, respectively.
+   */
+  exports.search = function search(aNeedle, aHaystack, aCompare) {
+    return aHaystack.length > 0
+      ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare)
+      : null;
+  };
+
+});
diff --git a/lib/source-map/source-map-consumer.js b/lib/source-map/source-map-consumer.js
new file mode 100644
index 0000000..5214d5e
--- /dev/null
+++ b/lib/source-map/source-map-consumer.js
@@ -0,0 +1,478 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var util = require('./util');
+  var binarySearch = require('./binary-search');
+  var ArraySet = require('./array-set').ArraySet;
+  var base64VLQ = require('./base64-vlq');
+
+  /**
+   * A SourceMapConsumer instance represents a parsed source map which we can
+   * query for information about the original file positions by giving it a file
+   * position in the generated source.
+   *
+   * The only parameter is the raw source map (either as a JSON string, or
+   * already parsed to an object). According to the spec, source maps have the
+   * following attributes:
+   *
+   *   - version: Which version of the source map spec this map is following.
+   *   - sources: An array of URLs to the original source files.
+   *   - names: An array of identifiers which can be referrenced by individual mappings.
+   *   - sourceRoot: Optional. The URL root from which all sources are relative.
+   *   - sourcesContent: Optional. An array of contents of the original source files.
+   *   - mappings: A string of base64 VLQs which contain the actual mappings.
+   *   - file: Optional. The generated file this source map is associated with.
+   *
+   * Here is an example source map, taken from the source map spec[0]:
+   *
+   *     {
+   *       version : 3,
+   *       file: "out.js",
+   *       sourceRoot : "",
+   *       sources: ["foo.js", "bar.js"],
+   *       names: ["src", "maps", "are", "fun"],
+   *       mappings: "AA,AB;;ABCDE;"
+   *     }
+   *
+   * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
+   */
+  function SourceMapConsumer(aSourceMap) {
+    var sourceMap = aSourceMap;
+    if (typeof aSourceMap === 'string') {
+      sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+    }
+
+    var version = util.getArg(sourceMap, 'version');
+    var sources = util.getArg(sourceMap, 'sources');
+    // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
+    // requires the array) to play nice here.
+    var names = util.getArg(sourceMap, 'names', []);
+    var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
+    var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
+    var mappings = util.getArg(sourceMap, 'mappings');
+    var file = util.getArg(sourceMap, 'file', null);
+
+    // Once again, Sass deviates from the spec and supplies the version as a
+    // string rather than a number, so we use loose equality checking here.
+    if (version != this._version) {
+      throw new Error('Unsupported version: ' + version);
+    }
+
+    // Pass `true` below to allow duplicate names and sources. While source maps
+    // are intended to be compressed and deduplicated, the TypeScript compiler
+    // sometimes generates source maps with duplicates in them. See Github issue
+    // #72 and bugzil.la/889492.
+    this._names = ArraySet.fromArray(names, true);
+    this._sources = ArraySet.fromArray(sources, true);
+
+    this.sourceRoot = sourceRoot;
+    this.sourcesContent = sourcesContent;
+    this._mappings = mappings;
+    this.file = file;
+  }
+
+  /**
+   * Create a SourceMapConsumer from a SourceMapGenerator.
+   *
+   * @param SourceMapGenerator aSourceMap
+   *        The source map that will be consumed.
+   * @returns SourceMapConsumer
+   */
+  SourceMapConsumer.fromSourceMap =
+    function SourceMapConsumer_fromSourceMap(aSourceMap) {
+      var smc = Object.create(SourceMapConsumer.prototype);
+
+      smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
+      smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
+      smc.sourceRoot = aSourceMap._sourceRoot;
+      smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
+                                                              smc.sourceRoot);
+      smc.file = aSourceMap._file;
+
+      smc.__generatedMappings = aSourceMap._mappings.slice()
+        .sort(util.compareByGeneratedPositions);
+      smc.__originalMappings = aSourceMap._mappings.slice()
+        .sort(util.compareByOriginalPositions);
+
+      return smc;
+    };
+
+  /**
+   * The version of the source mapping spec that we are consuming.
+   */
+  SourceMapConsumer.prototype._version = 3;
+
+  /**
+   * The list of original sources.
+   */
+  Object.defineProperty(SourceMapConsumer.prototype, 'sources', {
+    get: function () {
+      return this._sources.toArray().map(function (s) {
+        return this.sourceRoot ? util.join(this.sourceRoot, s) : s;
+      }, this);
+    }
+  });
+
+  // `__generatedMappings` and `__originalMappings` are arrays that hold the
+  // parsed mapping coordinates from the source map's "mappings" attribute. They
+  // are lazily instantiated, accessed via the `_generatedMappings` and
+  // `_originalMappings` getters respectively, and we only parse the mappings
+  // and create these arrays once queried for a source location. We jump through
+  // these hoops because there can be many thousands of mappings, and parsing
+  // them is expensive, so we only want to do it if we must.
+  //
+  // Each object in the arrays is of the form:
+  //
+  //     {
+  //       generatedLine: The line number in the generated code,
+  //       generatedColumn: The column number in the generated code,
+  //       source: The path to the original source file that generated this
+  //               chunk of code,
+  //       originalLine: The line number in the original source that
+  //                     corresponds to this chunk of generated code,
+  //       originalColumn: The column number in the original source that
+  //                       corresponds to this chunk of generated code,
+  //       name: The name of the original symbol which generated this chunk of
+  //             code.
+  //     }
+  //
+  // All properties except for `generatedLine` and `generatedColumn` can be
+  // `null`.
+  //
+  // `_generatedMappings` is ordered by the generated positions.
+  //
+  // `_originalMappings` is ordered by the original positions.
+
+  SourceMapConsumer.prototype.__generatedMappings = null;
+  Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
+    get: function () {
+      if (!this.__generatedMappings) {
+        this.__generatedMappings = [];
+        this.__originalMappings = [];
+        this._parseMappings(this._mappings, this.sourceRoot);
+      }
+
+      return this.__generatedMappings;
+    }
+  });
+
+  SourceMapConsumer.prototype.__originalMappings = null;
+  Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
+    get: function () {
+      if (!this.__originalMappings) {
+        this.__generatedMappings = [];
+        this.__originalMappings = [];
+        this._parseMappings(this._mappings, this.sourceRoot);
+      }
+
+      return this.__originalMappings;
+    }
+  });
+
+  /**
+   * Parse the mappings in a string in to a data structure which we can easily
+   * query (the ordered arrays in the `this.__generatedMappings` and
+   * `this.__originalMappings` properties).
+   */
+  SourceMapConsumer.prototype._parseMappings =
+    function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
+      var generatedLine = 1;
+      var previousGeneratedColumn = 0;
+      var previousOriginalLine = 0;
+      var previousOriginalColumn = 0;
+      var previousSource = 0;
+      var previousName = 0;
+      var mappingSeparator = /^[,;]/;
+      var str = aStr;
+      var mapping;
+      var temp;
+
+      while (str.length > 0) {
+        if (str.charAt(0) === ';') {
+          generatedLine++;
+          str = str.slice(1);
+          previousGeneratedColumn = 0;
+        }
+        else if (str.charAt(0) === ',') {
+          str = str.slice(1);
+        }
+        else {
+          mapping = {};
+          mapping.generatedLine = generatedLine;
+
+          // Generated column.
+          temp = base64VLQ.decode(str);
+          mapping.generatedColumn = previousGeneratedColumn + temp.value;
+          previousGeneratedColumn = mapping.generatedColumn;
+          str = temp.rest;
+
+          if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
+            // Original source.
+            temp = base64VLQ.decode(str);
+            mapping.source = this._sources.at(previousSource + temp.value);
+            previousSource += temp.value;
+            str = temp.rest;
+            if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
+              throw new Error('Found a source, but no line and column');
+            }
+
+            // Original line.
+            temp = base64VLQ.decode(str);
+            mapping.originalLine = previousOriginalLine + temp.value;
+            previousOriginalLine = mapping.originalLine;
+            // Lines are stored 0-based
+            mapping.originalLine += 1;
+            str = temp.rest;
+            if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
+              throw new Error('Found a source and line, but no column');
+            }
+
+            // Original column.
+            temp = base64VLQ.decode(str);
+            mapping.originalColumn = previousOriginalColumn + temp.value;
+            previousOriginalColumn = mapping.originalColumn;
+            str = temp.rest;
+
+            if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
+              // Original name.
+              temp = base64VLQ.decode(str);
+              mapping.name = this._names.at(previousName + temp.value);
+              previousName += temp.value;
+              str = temp.rest;
+            }
+          }
+
+          this.__generatedMappings.push(mapping);
+          if (typeof mapping.originalLine === 'number') {
+            this.__originalMappings.push(mapping);
+          }
+        }
+      }
+
+      this.__generatedMappings.sort(util.compareByGeneratedPositions);
+      this.__originalMappings.sort(util.compareByOriginalPositions);
+    };
+
+  /**
+   * Find the mapping that best matches the hypothetical "needle" mapping that
+   * we are searching for in the given "haystack" of mappings.
+   */
+  SourceMapConsumer.prototype._findMapping =
+    function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,
+                                           aColumnName, aComparator) {
+      // To return the position we are searching for, we must first find the
+      // mapping for the given position and then return the opposite position it
+      // points to. Because the mappings are sorted, we can use binary search to
+      // find the best mapping.
+
+      if (aNeedle[aLineName] <= 0) {
+        throw new TypeError('Line must be greater than or equal to 1, got '
+                            + aNeedle[aLineName]);
+      }
+      if (aNeedle[aColumnName] < 0) {
+        throw new TypeError('Column must be greater than or equal to 0, got '
+                            + aNeedle[aColumnName]);
+      }
+
+      return binarySearch.search(aNeedle, aMappings, aComparator);
+    };
+
+  /**
+   * Returns the original source, line, and column information for the generated
+   * source's line and column positions provided. The only argument is an object
+   * with the following properties:
+   *
+   *   - line: The line number in the generated source.
+   *   - column: The column number in the generated source.
+   *
+   * and an object is returned with the following properties:
+   *
+   *   - source: The original source file, or null.
+   *   - line: The line number in the original source, or null.
+   *   - column: The column number in the original source, or null.
+   *   - name: The original identifier, or null.
+   */
+  SourceMapConsumer.prototype.originalPositionFor =
+    function SourceMapConsumer_originalPositionFor(aArgs) {
+      var needle = {
+        generatedLine: util.getArg(aArgs, 'line'),
+        generatedColumn: util.getArg(aArgs, 'column')
+      };
+
+      var mapping = this._findMapping(needle,
+                                      this._generatedMappings,
+                                      "generatedLine",
+                                      "generatedColumn",
+                                      util.compareByGeneratedPositions);
+
+      if (mapping && mapping.generatedLine === needle.generatedLine) {
+        var source = util.getArg(mapping, 'source', null);
+        if (source && this.sourceRoot) {
+          source = util.join(this.sourceRoot, source);
+        }
+        return {
+          source: source,
+          line: util.getArg(mapping, 'originalLine', null),
+          column: util.getArg(mapping, 'originalColumn', null),
+          name: util.getArg(mapping, 'name', null)
+        };
+      }
+
+      return {
+        source: null,
+        line: null,
+        column: null,
+        name: null
+      };
+    };
+
+  /**
+   * Returns the original source content. The only argument is the url of the
+   * original source file. Returns null if no original source content is
+   * availible.
+   */
+  SourceMapConsumer.prototype.sourceContentFor =
+    function SourceMapConsumer_sourceContentFor(aSource) {
+      if (!this.sourcesContent) {
+        return null;
+      }
+
+      if (this.sourceRoot) {
+        aSource = util.relative(this.sourceRoot, aSource);
+      }
+
+      if (this._sources.has(aSource)) {
+        return this.sourcesContent[this._sources.indexOf(aSource)];
+      }
+
+      var url;
+      if (this.sourceRoot
+          && (url = util.urlParse(this.sourceRoot))) {
+        // XXX: file:// URIs and absolute paths lead to unexpected behavior for
+        // many users. We can help them out when they expect file:// URIs to
+        // behave like it would if they were running a local HTTP server. See
+        // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
+        var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+        if (url.scheme == "file"
+            && this._sources.has(fileUriAbsPath)) {
+          return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
+        }
+
+        if ((!url.path || url.path == "/")
+            && this._sources.has("/" + aSource)) {
+          return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+        }
+      }
+
+      throw new Error('"' + aSource + '" is not in the SourceMap.');
+    };
+
+  /**
+   * Returns the generated line and column information for the original source,
+   * line, and column positions provided. The only argument is an object with
+   * the following properties:
+   *
+   *   - source: The filename of the original source.
+   *   - line: The line number in the original source.
+   *   - column: The column number in the original source.
+   *
+   * and an object is returned with the following properties:
+   *
+   *   - line: The line number in the generated source, or null.
+   *   - column: The column number in the generated source, or null.
+   */
+  SourceMapConsumer.prototype.generatedPositionFor =
+    function SourceMapConsumer_generatedPositionFor(aArgs) {
+      var needle = {
+        source: util.getArg(aArgs, 'source'),
+        originalLine: util.getArg(aArgs, 'line'),
+        originalColumn: util.getArg(aArgs, 'column')
+      };
+
+      if (this.sourceRoot) {
+        needle.source = util.relative(this.sourceRoot, needle.source);
+      }
+
+      var mapping = this._findMapping(needle,
+                                      this._originalMappings,
+                                      "originalLine",
+                                      "originalColumn",
+                                      util.compareByOriginalPositions);
+
+      if (mapping) {
+        return {
+          line: util.getArg(mapping, 'generatedLine', null),
+          column: util.getArg(mapping, 'generatedColumn', null)
+        };
+      }
+
+      return {
+        line: null,
+        column: null
+      };
+    };
+
+  SourceMapConsumer.GENERATED_ORDER = 1;
+  SourceMapConsumer.ORIGINAL_ORDER = 2;
+
+  /**
+   * Iterate over each mapping between an original source/line/column and a
+   * generated line/column in this source map.
+   *
+   * @param Function aCallback
+   *        The function that is called with each mapping.
+   * @param Object aContext
+   *        Optional. If specified, this object will be the value of `this` every
+   *        time that `aCallback` is called.
+   * @param aOrder
+   *        Either `SourceMapConsumer.GENERATED_ORDER` or
+   *        `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
+   *        iterate over the mappings sorted by the generated file's line/column
+   *        order or the original's source/line/column order, respectively. Defaults to
+   *        `SourceMapConsumer.GENERATED_ORDER`.
+   */
+  SourceMapConsumer.prototype.eachMapping =
+    function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
+      var context = aContext || null;
+      var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
+
+      var mappings;
+      switch (order) {
+      case SourceMapConsumer.GENERATED_ORDER:
+        mappings = this._generatedMappings;
+        break;
+      case SourceMapConsumer.ORIGINAL_ORDER:
+        mappings = this._originalMappings;
+        break;
+      default:
+        throw new Error("Unknown order of iteration.");
+      }
+
+      var sourceRoot = this.sourceRoot;
+      mappings.map(function (mapping) {
+        var source = mapping.source;
+        if (source && sourceRoot) {
+          source = util.join(sourceRoot, source);
+        }
+        return {
+          source: source,
+          generatedLine: mapping.generatedLine,
+          generatedColumn: mapping.generatedColumn,
+          originalLine: mapping.originalLine,
+          originalColumn: mapping.originalColumn,
+          name: mapping.name
+        };
+      }).forEach(aCallback, context);
+    };
+
+  exports.SourceMapConsumer = SourceMapConsumer;
+
+});
diff --git a/lib/source-map/source-map-generator.js b/lib/source-map/source-map-generator.js
new file mode 100644
index 0000000..d555f08
--- /dev/null
+++ b/lib/source-map/source-map-generator.js
@@ -0,0 +1,397 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var base64VLQ = require('./base64-vlq');
+  var util = require('./util');
+  var ArraySet = require('./array-set').ArraySet;
+
+  /**
+   * An instance of the SourceMapGenerator represents a source map which is
+   * being built incrementally. You may pass an object with the following
+   * properties:
+   *
+   *   - file: The filename of the generated source.
+   *   - sourceRoot: A root for all relative URLs in this source map.
+   */
+  function SourceMapGenerator(aArgs) {
+    if (!aArgs) {
+      aArgs = {};
+    }
+    this._file = util.getArg(aArgs, 'file', null);
+    this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
+    this._sources = new ArraySet();
+    this._names = new ArraySet();
+    this._mappings = [];
+    this._sourcesContents = null;
+  }
+
+  SourceMapGenerator.prototype._version = 3;
+
+  /**
+   * Creates a new SourceMapGenerator based on a SourceMapConsumer
+   *
+   * @param aSourceMapConsumer The SourceMap.
+   */
+  SourceMapGenerator.fromSourceMap =
+    function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
+      var sourceRoot = aSourceMapConsumer.sourceRoot;
+      var generator = new SourceMapGenerator({
+        file: aSourceMapConsumer.file,
+        sourceRoot: sourceRoot
+      });
+      aSourceMapConsumer.eachMapping(function (mapping) {
+        var newMapping = {
+          generated: {
+            line: mapping.generatedLine,
+            column: mapping.generatedColumn
+          }
+        };
+
+        if (mapping.source) {
+          newMapping.source = mapping.source;
+          if (sourceRoot) {
+            newMapping.source = util.relative(sourceRoot, newMapping.source);
+          }
+
+          newMapping.original = {
+            line: mapping.originalLine,
+            column: mapping.originalColumn
+          };
+
+          if (mapping.name) {
+            newMapping.name = mapping.name;
+          }
+        }
+
+        generator.addMapping(newMapping);
+      });
+      aSourceMapConsumer.sources.forEach(function (sourceFile) {
+        var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+        if (content) {
+          generator.setSourceContent(sourceFile, content);
+        }
+      });
+      return generator;
+    };
+
+  /**
+   * Add a single mapping from original source line and column to the generated
+   * source's line and column for this source map being created. The mapping
+   * object should have the following properties:
+   *
+   *   - generated: An object with the generated line and column positions.
+   *   - original: An object with the original line and column positions.
+   *   - source: The original source file (relative to the sourceRoot).
+   *   - name: An optional original token name for this mapping.
+   */
+  SourceMapGenerator.prototype.addMapping =
+    function SourceMapGenerator_addMapping(aArgs) {
+      var generated = util.getArg(aArgs, 'generated');
+      var original = util.getArg(aArgs, 'original', null);
+      var source = util.getArg(aArgs, 'source', null);
+      var name = util.getArg(aArgs, 'name', null);
+
+      this._validateMapping(generated, original, source, name);
+
+      if (source && !this._sources.has(source)) {
+        this._sources.add(source);
+      }
+
+      if (name && !this._names.has(name)) {
+        this._names.add(name);
+      }
+
+      this._mappings.push({
+        generatedLine: generated.line,
+        generatedColumn: generated.column,
+        originalLine: original != null && original.line,
+        originalColumn: original != null && original.column,
+        source: source,
+        name: name
+      });
+    };
+
+  /**
+   * Set the source content for a source file.
+   */
+  SourceMapGenerator.prototype.setSourceContent =
+    function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
+      var source = aSourceFile;
+      if (this._sourceRoot) {
+        source = util.relative(this._sourceRoot, source);
+      }
+
+      if (aSourceContent !== null) {
+        // Add the source content to the _sourcesContents map.
+        // Create a new _sourcesContents map if the property is null.
+        if (!this._sourcesContents) {
+          this._sourcesContents = {};
+        }
+        this._sourcesContents[util.toSetString(source)] = aSourceContent;
+      } else {
+        // Remove the source file from the _sourcesContents map.
+        // If the _sourcesContents map is empty, set the property to null.
+        delete this._sourcesContents[util.toSetString(source)];
+        if (Object.keys(this._sourcesContents).length === 0) {
+          this._sourcesContents = null;
+        }
+      }
+    };
+
+  /**
+   * Applies the mappings of a sub-source-map for a specific source file to the
+   * source map being generated. Each mapping to the supplied source file is
+   * rewritten using the supplied source map. Note: The resolution for the
+   * resulting mappings is the minimium of this map and the supplied map.
+   *
+   * @param aSourceMapConsumer The source map to be applied.
+   * @param aSourceFile Optional. The filename of the source file.
+   *        If omitted, SourceMapConsumer's file property will be used.
+   * @param aSourceMapPath Optional. The dirname of the path to the source map
+   *        to be applied. If relative, it is relative to the SourceMapConsumer.
+   *        This parameter is needed when the two source maps aren't in the same
+   *        directory, and the source map to be applied contains relative source
+   *        paths. If so, those relative source paths need to be rewritten
+   *        relative to the SourceMapGenerator.
+   */
+  SourceMapGenerator.prototype.applySourceMap =
+    function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
+      // If aSourceFile is omitted, we will use the file property of the SourceMap
+      if (!aSourceFile) {
+        if (!aSourceMapConsumer.file) {
+          throw new Error(
+            'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' +
+            'or the source map\'s "file" property. Both were omitted.'
+          );
+        }
+        aSourceFile = aSourceMapConsumer.file;
+      }
+      var sourceRoot = this._sourceRoot;
+      // Make "aSourceFile" relative if an absolute Url is passed.
+      if (sourceRoot) {
+        aSourceFile = util.relative(sourceRoot, aSourceFile);
+      }
+      // Applying the SourceMap can add and remove items from the sources and
+      // the names array.
+      var newSources = new ArraySet();
+      var newNames = new ArraySet();
+
+      // Find mappings for the "aSourceFile"
+      this._mappings.forEach(function (mapping) {
+        if (mapping.source === aSourceFile && mapping.originalLine) {
+          // Check if it can be mapped by the source map, then update the mapping.
+          var original = aSourceMapConsumer.originalPositionFor({
+            line: mapping.originalLine,
+            column: mapping.originalColumn
+          });
+          if (original.source !== null) {
+            // Copy mapping
+            mapping.source = original.source;
+            if (aSourceMapPath) {
+              mapping.source = util.join(aSourceMapPath, mapping.source)
+            }
+            if (sourceRoot) {
+              mapping.source = util.relative(sourceRoot, mapping.source);
+            }
+            mapping.originalLine = original.line;
+            mapping.originalColumn = original.column;
+            if (original.name !== null && mapping.name !== null) {
+              // Only use the identifier name if it's an identifier
+              // in both SourceMaps
+              mapping.name = original.name;
+            }
+          }
+        }
+
+        var source = mapping.source;
+        if (source && !newSources.has(source)) {
+          newSources.add(source);
+        }
+
+        var name = mapping.name;
+        if (name && !newNames.has(name)) {
+          newNames.add(name);
+        }
+
+      }, this);
+      this._sources = newSources;
+      this._names = newNames;
+
+      // Copy sourcesContents of applied map.
+      aSourceMapConsumer.sources.forEach(function (sourceFile) {
+        var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+        if (content) {
+          if (sourceRoot) {
+            sourceFile = util.relative(sourceRoot, sourceFile);
+          }
+          this.setSourceContent(sourceFile, content);
+        }
+      }, this);
+    };
+
+  /**
+   * A mapping can have one of the three levels of data:
+   *
+   *   1. Just the generated position.
+   *   2. The Generated position, original position, and original source.
+   *   3. Generated and original position, original source, as well as a name
+   *      token.
+   *
+   * To maintain consistency, we validate that any new mapping being added falls
+   * in to one of these categories.
+   */
+  SourceMapGenerator.prototype._validateMapping =
+    function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
+                                                aName) {
+      if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+          && aGenerated.line > 0 && aGenerated.column >= 0
+          && !aOriginal && !aSource && !aName) {
+        // Case 1.
+        return;
+      }
+      else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+               && aOriginal && 'line' in aOriginal && 'column' in aOriginal
+               && aGenerated.line > 0 && aGenerated.column >= 0
+               && aOriginal.line > 0 && aOriginal.column >= 0
+               && aSource) {
+        // Cases 2 and 3.
+        return;
+      }
+      else {
+        throw new Error('Invalid mapping: ' + JSON.stringify({
+          generated: aGenerated,
+          source: aSource,
+          original: aOriginal,
+          name: aName
+        }));
+      }
+    };
+
+  /**
+   * Serialize the accumulated mappings in to the stream of base 64 VLQs
+   * specified by the source map format.
+   */
+  SourceMapGenerator.prototype._serializeMappings =
+    function SourceMapGenerator_serializeMappings() {
+      var previousGeneratedColumn = 0;
+      var previousGeneratedLine = 1;
+      var previousOriginalColumn = 0;
+      var previousOriginalLine = 0;
+      var previousName = 0;
+      var previousSource = 0;
+      var result = '';
+      var mapping;
+
+      // The mappings must be guaranteed to be in sorted order before we start
+      // serializing them or else the generated line numbers (which are defined
+      // via the ';' separators) will be all messed up. Note: it might be more
+      // performant to maintain the sorting as we insert them, rather than as we
+      // serialize them, but the big O is the same either way.
+      this._mappings.sort(util.compareByGeneratedPositions);
+
+      for (var i = 0, len = this._mappings.length; i < len; i++) {
+        mapping = this._mappings[i];
+
+        if (mapping.generatedLine !== previousGeneratedLine) {
+          previousGeneratedColumn = 0;
+          while (mapping.generatedLine !== previousGeneratedLine) {
+            result += ';';
+            previousGeneratedLine++;
+          }
+        }
+        else {
+          if (i > 0) {
+            if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) {
+              continue;
+            }
+            result += ',';
+          }
+        }
+
+        result += base64VLQ.encode(mapping.generatedColumn
+                                   - previousGeneratedColumn);
+        previousGeneratedColumn = mapping.generatedColumn;
+
+        if (mapping.source) {
+          result += base64VLQ.encode(this._sources.indexOf(mapping.source)
+                                     - previousSource);
+          previousSource = this._sources.indexOf(mapping.source);
+
+          // lines are stored 0-based in SourceMap spec version 3
+          result += base64VLQ.encode(mapping.originalLine - 1
+                                     - previousOriginalLine);
+          previousOriginalLine = mapping.originalLine - 1;
+
+          result += base64VLQ.encode(mapping.originalColumn
+                                     - previousOriginalColumn);
+          previousOriginalColumn = mapping.originalColumn;
+
+          if (mapping.name) {
+            result += base64VLQ.encode(this._names.indexOf(mapping.name)
+                                       - previousName);
+            previousName = this._names.indexOf(mapping.name);
+          }
+        }
+      }
+
+      return result;
+    };
+
+  SourceMapGenerator.prototype._generateSourcesContent =
+    function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
+      return aSources.map(function (source) {
+        if (!this._sourcesContents) {
+          return null;
+        }
+        if (aSourceRoot) {
+          source = util.relative(aSourceRoot, source);
+        }
+        var key = util.toSetString(source);
+        return Object.prototype.hasOwnProperty.call(this._sourcesContents,
+                                                    key)
+          ? this._sourcesContents[key]
+          : null;
+      }, this);
+    };
+
+  /**
+   * Externalize the source map.
+   */
+  SourceMapGenerator.prototype.toJSON =
+    function SourceMapGenerator_toJSON() {
+      var map = {
+        version: this._version,
+        file: this._file,
+        sources: this._sources.toArray(),
+        names: this._names.toArray(),
+        mappings: this._serializeMappings()
+      };
+      if (this._sourceRoot) {
+        map.sourceRoot = this._sourceRoot;
+      }
+      if (this._sourcesContents) {
+        map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);
+      }
+
+      return map;
+    };
+
+  /**
+   * Render the source map being generated to a string.
+   */
+  SourceMapGenerator.prototype.toString =
+    function SourceMapGenerator_toString() {
+      return JSON.stringify(this);
+    };
+
+  exports.SourceMapGenerator = SourceMapGenerator;
+
+});
diff --git a/lib/source-map/source-node.js b/lib/source-map/source-node.js
new file mode 100644
index 0000000..1d98123
--- /dev/null
+++ b/lib/source-map/source-node.js
@@ -0,0 +1,387 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator;
+  var util = require('./util');
+
+  /**
+   * SourceNodes provide a way to abstract over interpolating/concatenating
+   * snippets of generated JavaScript source code while maintaining the line and
+   * column information associated with the original source code.
+   *
+   * @param aLine The original line number.
+   * @param aColumn The original column number.
+   * @param aSource The original source's filename.
+   * @param aChunks Optional. An array of strings which are snippets of
+   *        generated JS, or other SourceNodes.
+   * @param aName The original identifier.
+   */
+  function SourceNode(aLine, aColumn, aSource, aChunks, aName) {
+    this.children = [];
+    this.sourceContents = {};
+    this.line = aLine === undefined ? null : aLine;
+    this.column = aColumn === undefined ? null : aColumn;
+    this.source = aSource === undefined ? null : aSource;
+    this.name = aName === undefined ? null : aName;
+    if (aChunks != null) this.add(aChunks);
+  }
+
+  /**
+   * Creates a SourceNode from generated code and a SourceMapConsumer.
+   *
+   * @param aGeneratedCode The generated code
+   * @param aSourceMapConsumer The SourceMap for the generated code
+   */
+  SourceNode.fromStringWithSourceMap =
+    function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) {
+      // The SourceNode we want to fill with the generated code
+      // and the SourceMap
+      var node = new SourceNode();
+
+      // The generated code
+      // Processed fragments are removed from this array.
+      var remainingLines = aGeneratedCode.split('\n');
+
+      // We need to remember the position of "remainingLines"
+      var lastGeneratedLine = 1, lastGeneratedColumn = 0;
+
+      // The generate SourceNodes we need a code range.
+      // To extract it current and last mapping is used.
+      // Here we store the last mapping.
+      var lastMapping = null;
+
+      aSourceMapConsumer.eachMapping(function (mapping) {
+        if (lastMapping !== null) {
+          // We add the code from "lastMapping" to "mapping":
+          // First check if there is a new line in between.
+          if (lastGeneratedLine < mapping.generatedLine) {
+            var code = "";
+            // Associate first line with "lastMapping"
+            addMappingWithCode(lastMapping, remainingLines.shift() + "\n");
+            lastGeneratedLine++;
+            lastGeneratedColumn = 0;
+            // The remaining code is added without mapping
+          } else {
+            // There is no new line in between.
+            // Associate the code between "lastGeneratedColumn" and
+            // "mapping.generatedColumn" with "lastMapping"
+            var nextLine = remainingLines[0];
+            var code = nextLine.substr(0, mapping.generatedColumn -
+                                          lastGeneratedColumn);
+            remainingLines[0] = nextLine.substr(mapping.generatedColumn -
+                                                lastGeneratedColumn);
+            lastGeneratedColumn = mapping.generatedColumn;
+            addMappingWithCode(lastMapping, code);
+            // No more remaining code, continue
+            lastMapping = mapping;
+            return;
+          }
+        }
+        // We add the generated code until the first mapping
+        // to the SourceNode without any mapping.
+        // Each line is added as separate string.
+        while (lastGeneratedLine < mapping.generatedLine) {
+          node.add(remainingLines.shift() + "\n");
+          lastGeneratedLine++;
+        }
+        if (lastGeneratedColumn < mapping.generatedColumn) {
+          var nextLine = remainingLines[0];
+          node.add(nextLine.substr(0, mapping.generatedColumn));
+          remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+          lastGeneratedColumn = mapping.generatedColumn;
+        }
+        lastMapping = mapping;
+      }, this);
+      // We have processed all mappings.
+      if (remainingLines.length > 0) {
+        if (lastMapping) {
+          // Associate the remaining code in the current line with "lastMapping"
+          var lastLine = remainingLines.shift();
+          if (remainingLines.length > 0) lastLine += "\n";
+          addMappingWithCode(lastMapping, lastLine);
+        }
+        // and add the remaining lines without any mapping
+        node.add(remainingLines.join("\n"));
+      }
+
+      // Copy sourcesContent into SourceNode
+      aSourceMapConsumer.sources.forEach(function (sourceFile) {
+        var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+        if (content) {
+          node.setSourceContent(sourceFile, content);
+        }
+      });
+
+      return node;
+
+      function addMappingWithCode(mapping, code) {
+        if (mapping === null || mapping.source === undefined) {
+          node.add(code);
+        } else {
+          node.add(new SourceNode(mapping.originalLine,
+                                  mapping.originalColumn,
+                                  mapping.source,
+                                  code,
+                                  mapping.name));
+        }
+      }
+    };
+
+  /**
+   * Add a chunk of generated JS to this source node.
+   *
+   * @param aChunk A string snippet of generated JS code, another instance of
+   *        SourceNode, or an array where each member is one of those things.
+   */
+  SourceNode.prototype.add = function SourceNode_add(aChunk) {
+    if (Array.isArray(aChunk)) {
+      aChunk.forEach(function (chunk) {
+        this.add(chunk);
+      }, this);
+    }
+    else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
+      if (aChunk) {
+        this.children.push(aChunk);
+      }
+    }
+    else {
+      throw new TypeError(
+        "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+      );
+    }
+    return this;
+  };
+
+  /**
+   * Add a chunk of generated JS to the beginning of this source node.
+   *
+   * @param aChunk A string snippet of generated JS code, another instance of
+   *        SourceNode, or an array where each member is one of those things.
+   */
+  SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
+    if (Array.isArray(aChunk)) {
+      for (var i = aChunk.length-1; i >= 0; i--) {
+        this.prepend(aChunk[i]);
+      }
+    }
+    else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
+      this.children.unshift(aChunk);
+    }
+    else {
+      throw new TypeError(
+        "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+      );
+    }
+    return this;
+  };
+
+  /**
+   * Walk over the tree of JS snippets in this node and its children. The
+   * walking function is called once for each snippet of JS and is passed that
+   * snippet and the its original associated source's line/column location.
+   *
+   * @param aFn The traversal function.
+   */
+  SourceNode.prototype.walk = function SourceNode_walk(aFn) {
+    var chunk;
+    for (var i = 0, len = this.children.length; i < len; i++) {
+      chunk = this.children[i];
+      if (chunk instanceof SourceNode) {
+        chunk.walk(aFn);
+      }
+      else {
+        if (chunk !== '') {
+          aFn(chunk, { source: this.source,
+                       line: this.line,
+                       column: this.column,
+                       name: this.name });
+        }
+      }
+    }
+  };
+
+  /**
+   * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
+   * each of `this.children`.
+   *
+   * @param aSep The separator.
+   */
+  SourceNode.prototype.join = function SourceNode_join(aSep) {
+    var newChildren;
+    var i;
+    var len = this.children.length;
+    if (len > 0) {
+      newChildren = [];
+      for (i = 0; i < len-1; i++) {
+        newChildren.push(this.children[i]);
+        newChildren.push(aSep);
+      }
+      newChildren.push(this.children[i]);
+      this.children = newChildren;
+    }
+    return this;
+  };
+
+  /**
+   * Call String.prototype.replace on the very right-most source snippet. Useful
+   * for trimming whitespace from the end of a source node, etc.
+   *
+   * @param aPattern The pattern to replace.
+   * @param aReplacement The thing to replace the pattern with.
+   */
+  SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
+    var lastChild = this.children[this.children.length - 1];
+    if (lastChild instanceof SourceNode) {
+      lastChild.replaceRight(aPattern, aReplacement);
+    }
+    else if (typeof lastChild === 'string') {
+      this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);
+    }
+    else {
+      this.children.push(''.replace(aPattern, aReplacement));
+    }
+    return this;
+  };
+
+  /**
+   * Set the source content for a source file. This will be added to the SourceMapGenerator
+   * in the sourcesContent field.
+   *
+   * @param aSourceFile The filename of the source file
+   * @param aSourceContent The content of the source file
+   */
+  SourceNode.prototype.setSourceContent =
+    function SourceNode_setSourceContent(aSourceFile, aSourceContent) {
+      this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
+    };
+
+  /**
+   * Walk over the tree of SourceNodes. The walking function is called for each
+   * source file content and is passed the filename and source content.
+   *
+   * @param aFn The traversal function.
+   */
+  SourceNode.prototype.walkSourceContents =
+    function SourceNode_walkSourceContents(aFn) {
+      for (var i = 0, len = this.children.length; i < len; i++) {
+        if (this.children[i] instanceof SourceNode) {
+          this.children[i].walkSourceContents(aFn);
+        }
+      }
+
+      var sources = Object.keys(this.sourceContents);
+      for (var i = 0, len = sources.length; i < len; i++) {
+        aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);
+      }
+    };
+
+  /**
+   * Return the string representation of this source node. Walks over the tree
+   * and concatenates all the various snippets together to one string.
+   */
+  SourceNode.prototype.toString = function SourceNode_toString() {
+    var str = "";
+    this.walk(function (chunk) {
+      str += chunk;
+    });
+    return str;
+  };
+
+  /**
+   * Returns the string representation of this source node along with a source
+   * map.
+   */
+  SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
+    var generated = {
+      code: "",
+      line: 1,
+      column: 0
+    };
+    var map = new SourceMapGenerator(aArgs);
+    var sourceMappingActive = false;
+    var lastOriginalSource = null;
+    var lastOriginalLine = null;
+    var lastOriginalColumn = null;
+    var lastOriginalName = null;
+    this.walk(function (chunk, original) {
+      generated.code += chunk;
+      if (original.source !== null
+          && original.line !== null
+          && original.column !== null) {
+        if(lastOriginalSource !== original.source
+           || lastOriginalLine !== original.line
+           || lastOriginalColumn !== original.column
+           || lastOriginalName !== original.name) {
+          map.addMapping({
+            source: original.source,
+            original: {
+              line: original.line,
+              column: original.column
+            },
+            generated: {
+              line: generated.line,
+              column: generated.column
+            },
+            name: original.name
+          });
+        }
+        lastOriginalSource = original.source;
+        lastOriginalLine = original.line;
+        lastOriginalColumn = original.column;
+        lastOriginalName = original.name;
+        sourceMappingActive = true;
+      } else if (sourceMappingActive) {
+        map.addMapping({
+          generated: {
+            line: generated.line,
+            column: generated.column
+          }
+        });
+        lastOriginalSource = null;
+        sourceMappingActive = false;
+      }
+      chunk.split('').forEach(function (ch, idx, array) {
+        if (ch === '\n') {
+          generated.line++;
+          generated.column = 0;
+          // Mappings end at eol
+          if (idx + 1 === array.length) {
+            lastOriginalSource = null;
+            sourceMappingActive = false;
+          } else if (sourceMappingActive) {
+            map.addMapping({
+              source: original.source,
+              original: {
+                line: original.line,
+                column: original.column
+              },
+              generated: {
+                line: generated.line,
+                column: generated.column
+              },
+              name: original.name
+            });
+          }
+        } else {
+          generated.column++;
+        }
+      });
+    });
+    this.walkSourceContents(function (sourceFile, sourceContent) {
+      map.setSourceContent(sourceFile, sourceContent);
+    });
+
+    return { code: generated.code, map: map };
+  };
+
+  exports.SourceNode = SourceNode;
+
+});
diff --git a/lib/source-map/util.js b/lib/source-map/util.js
new file mode 100644
index 0000000..4316445
--- /dev/null
+++ b/lib/source-map/util.js
@@ -0,0 +1,302 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  /**
+   * This is a helper function for getting values from parameter/options
+   * objects.
+   *
+   * @param args The object we are extracting values from
+   * @param name The name of the property we are getting.
+   * @param defaultValue An optional value to return if the property is missing
+   * from the object. If this is not specified and the property is missing, an
+   * error will be thrown.
+   */
+  function getArg(aArgs, aName, aDefaultValue) {
+    if (aName in aArgs) {
+      return aArgs[aName];
+    } else if (arguments.length === 3) {
+      return aDefaultValue;
+    } else {
+      throw new Error('"' + aName + '" is a required argument.');
+    }
+  }
+  exports.getArg = getArg;
+
+  var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+  var dataUrlRegexp = /^data:.+\,.+$/;
+
+  function urlParse(aUrl) {
+    var match = aUrl.match(urlRegexp);
+    if (!match) {
+      return null;
+    }
+    return {
+      scheme: match[1],
+      auth: match[2],
+      host: match[3],
+      port: match[4],
+      path: match[5]
+    };
+  }
+  exports.urlParse = urlParse;
+
+  function urlGenerate(aParsedUrl) {
+    var url = '';
+    if (aParsedUrl.scheme) {
+      url += aParsedUrl.scheme + ':';
+    }
+    url += '//';
+    if (aParsedUrl.auth) {
+      url += aParsedUrl.auth + '@';
+    }
+    if (aParsedUrl.host) {
+      url += aParsedUrl.host;
+    }
+    if (aParsedUrl.port) {
+      url += ":" + aParsedUrl.port
+    }
+    if (aParsedUrl.path) {
+      url += aParsedUrl.path;
+    }
+    return url;
+  }
+  exports.urlGenerate = urlGenerate;
+
+  /**
+   * Normalizes a path, or the path portion of a URL:
+   *
+   * - Replaces consequtive slashes with one slash.
+   * - Removes unnecessary '.' parts.
+   * - Removes unnecessary '<dir>/..' parts.
+   *
+   * Based on code in the Node.js 'path' core module.
+   *
+   * @param aPath The path or url to normalize.
+   */
+  function normalize(aPath) {
+    var path = aPath;
+    var url = urlParse(aPath);
+    if (url) {
+      if (!url.path) {
+        return aPath;
+      }
+      path = url.path;
+    }
+    var isAbsolute = (path.charAt(0) === '/');
+
+    var parts = path.split(/\/+/);
+    for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
+      part = parts[i];
+      if (part === '.') {
+        parts.splice(i, 1);
+      } else if (part === '..') {
+        up++;
+      } else if (up > 0) {
+        if (part === '') {
+          // The first part is blank if the path is absolute. Trying to go
+          // above the root is a no-op. Therefore we can remove all '..' parts
+          // directly after the root.
+          parts.splice(i + 1, up);
+          up = 0;
+        } else {
+          parts.splice(i, 2);
+          up--;
+        }
+      }
+    }
+    path = parts.join('/');
+
+    if (path === '') {
+      path = isAbsolute ? '/' : '.';
+    }
+
+    if (url) {
+      url.path = path;
+      return urlGenerate(url);
+    }
+    return path;
+  }
+  exports.normalize = normalize;
+
+  /**
+   * Joins two paths/URLs.
+   *
+   * @param aRoot The root path or URL.
+   * @param aPath The path or URL to be joined with the root.
+   *
+   * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
+   *   scheme-relative URL: Then the scheme of aRoot, if any, is prepended
+   *   first.
+   * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
+   *   is updated with the result and aRoot is returned. Otherwise the result
+   *   is returned.
+   *   - If aPath is absolute, the result is aPath.
+   *   - Otherwise the two paths are joined with a slash.
+   * - Joining for example 'http://' and 'www.example.com' is also supported.
+   */
+  function join(aRoot, aPath) {
+    var aPathUrl = urlParse(aPath);
+    var aRootUrl = urlParse(aRoot);
+    if (aRootUrl) {
+      aRoot = aRootUrl.path || '/';
+    }
+
+    // `join(foo, '//www.example.org')`
+    if (aPathUrl && !aPathUrl.scheme) {
+      if (aRootUrl) {
+        aPathUrl.scheme = aRootUrl.scheme;
+      }
+      return urlGenerate(aPathUrl);
+    }
+
+    if (aPathUrl || aPath.match(dataUrlRegexp)) {
+      return aPath;
+    }
+
+    // `join('http://', 'www.example.com')`
+    if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
+      aRootUrl.host = aPath;
+      return urlGenerate(aRootUrl);
+    }
+
+    var joined = aPath.charAt(0) === '/'
+      ? aPath
+      : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);
+
+    if (aRootUrl) {
+      aRootUrl.path = joined;
+      return urlGenerate(aRootUrl);
+    }
+    return joined;
+  }
+  exports.join = join;
+
+  /**
+   * Because behavior goes wacky when you set `__proto__` on objects, we
+   * have to prefix all the strings in our set with an arbitrary character.
+   *
+   * See https://github.com/mozilla/source-map/pull/31 and
+   * https://github.com/mozilla/source-map/issues/30
+   *
+   * @param String aStr
+   */
+  function toSetString(aStr) {
+    return '$' + aStr;
+  }
+  exports.toSetString = toSetString;
+
+  function fromSetString(aStr) {
+    return aStr.substr(1);
+  }
+  exports.fromSetString = fromSetString;
+
+  function relative(aRoot, aPath) {
+    aRoot = aRoot.replace(/\/$/, '');
+
+    var url = urlParse(aRoot);
+    if (aPath.charAt(0) == "/" && url && url.path == "/") {
+      return aPath.slice(1);
+    }
+
+    return aPath.indexOf(aRoot + '/') === 0
+      ? aPath.substr(aRoot.length + 1)
+      : aPath;
+  }
+  exports.relative = relative;
+
+  function strcmp(aStr1, aStr2) {
+    var s1 = aStr1 || "";
+    var s2 = aStr2 || "";
+    return (s1 > s2) - (s1 < s2);
+  }
+
+  /**
+   * Comparator between two mappings where the original positions are compared.
+   *
+   * Optionally pass in `true` as `onlyCompareGenerated` to consider two
+   * mappings with the same original source/line/column, but different generated
+   * line and column the same. Useful when searching for a mapping with a
+   * stubbed out mapping.
+   */
+  function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
+    var cmp;
+
+    cmp = strcmp(mappingA.source, mappingB.source);
+    if (cmp) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalLine - mappingB.originalLine;
+    if (cmp) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalColumn - mappingB.originalColumn;
+    if (cmp || onlyCompareOriginal) {
+      return cmp;
+    }
+
+    cmp = strcmp(mappingA.name, mappingB.name);
+    if (cmp) {
+      return cmp;
+    }
+
+    cmp = mappingA.generatedLine - mappingB.generatedLine;
+    if (cmp) {
+      return cmp;
+    }
+
+    return mappingA.generatedColumn - mappingB.generatedColumn;
+  };
+  exports.compareByOriginalPositions = compareByOriginalPositions;
+
+  /**
+   * Comparator between two mappings where the generated positions are
+   * compared.
+   *
+   * Optionally pass in `true` as `onlyCompareGenerated` to consider two
+   * mappings with the same generated line and column, but different
+   * source/name/original line and column the same. Useful when searching for a
+   * mapping with a stubbed out mapping.
+   */
+  function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) {
+    var cmp;
+
+    cmp = mappingA.generatedLine - mappingB.generatedLine;
+    if (cmp) {
+      return cmp;
+    }
+
+    cmp = mappingA.generatedColumn - mappingB.generatedColumn;
+    if (cmp || onlyCompareGenerated) {
+      return cmp;
+    }
+
+    cmp = strcmp(mappingA.source, mappingB.source);
+    if (cmp) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalLine - mappingB.originalLine;
+    if (cmp) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalColumn - mappingB.originalColumn;
+    if (cmp) {
+      return cmp;
+    }
+
+    return strcmp(mappingA.name, mappingB.name);
+  };
+  exports.compareByGeneratedPositions = compareByGeneratedPositions;
+
+});
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..360678e
--- /dev/null
+++ b/package.json
@@ -0,0 +1,56 @@
+{
+  "name": "source-map",
+  "description": "Generates and consumes source maps",
+  "version": "0.1.33",
+  "homepage": "https://github.com/mozilla/source-map",
+  "author": "Nick Fitzgerald <nfitzgerald at mozilla.com>",
+  "contributors": [
+    "Tobias Koppers <tobias.koppers at googlemail.com>",
+    "Duncan Beevers <duncan at dweebd.com>",
+    "Stephen Crane <scrane at mozilla.com>",
+    "Ryan Seddon <seddon.ryan at gmail.com>",
+    "Miles Elam <miles.elam at deem.com>",
+    "Mihai Bazon <mihai.bazon at gmail.com>",
+    "Michael Ficarra <github.public.email at michael.ficarra.me>",
+    "Todd Wolfson <todd at twolfson.com>",
+    "Alexander Solovyov <alexander at solovyov.net>",
+    "Felix Gnass <fgnass at gmail.com>",
+    "Conrad Irwin <conrad.irwin at gmail.com>",
+    "usrbincc <usrbincc at yahoo.com>",
+    "David Glasser <glasser at davidglasser.net>",
+    "Chase Douglas <chase at newrelic.com>",
+    "Evan Wallace <evan.exe at gmail.com>",
+    "Heather Arthur <fayearthur at gmail.com>",
+    "Hugh Kennedy <hughskennedy at gmail.com>",
+    "David Glasser <glasser at davidglasser.net>",
+    "Simon Lydell <simon.lydell at gmail.com>",
+    "Jmeas Smith <jellyes2 at gmail.com>"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "http://github.com/mozilla/source-map.git"
+  },
+  "directories": {
+    "lib": "./lib"
+  },
+  "main": "./lib/source-map.js",
+  "engines": {
+    "node": ">=0.8.0"
+  },
+  "licenses": [
+    {
+      "type": "BSD",
+      "url": "http://opensource.org/licenses/BSD-3-Clause"
+    }
+  ],
+  "dependencies": {
+    "amdefine": ">=0.0.4"
+  },
+  "devDependencies": {
+    "dryice": ">=0.4.8"
+  },
+  "scripts": {
+    "test": "node test/run-tests.js",
+    "build": "node Makefile.dryice.js"
+  }
+}
diff --git a/test/run-tests.js b/test/run-tests.js
new file mode 100755
index 0000000..64a7c3a
--- /dev/null
+++ b/test/run-tests.js
@@ -0,0 +1,62 @@
+#!/usr/bin/env node
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+var assert = require('assert');
+var fs = require('fs');
+var path = require('path');
+var util = require('./source-map/util');
+
+function run(tests) {
+  var total = 0;
+  var passed = 0;
+
+  for (var i = 0; i < tests.length; i++) {
+    for (var k in tests[i].testCase) {
+      if (/^test/.test(k)) {
+        total++;
+        try {
+          tests[i].testCase[k](assert, util);
+          passed++;
+        }
+        catch (e) {
+          console.log('FAILED ' + tests[i].name + ': ' + k + '!');
+          console.log(e.stack);
+        }
+      }
+    }
+  }
+
+  console.log('');
+  console.log(passed + ' / ' + total + ' tests passed.');
+  console.log('');
+
+  return total - passed;
+}
+
+function isTestFile(f) {
+  var testToRun = process.argv[2];
+  return testToRun
+    ? path.basename(testToRun) === f
+    : /^test\-.*?\.js/.test(f);
+}
+
+function toModule(f) {
+  return './source-map/' + f.replace(/\.js$/, '');
+}
+
+var requires = fs.readdirSync(path.join(__dirname, 'source-map'))
+  .filter(isTestFile)
+  .map(toModule);
+
+var code = run(requires.map(require).map(function (mod, i) {
+  return {
+    name: requires[i],
+    testCase: mod
+  };
+}));
+
+process.exit(code);
diff --git a/test/source-map/test-api.js b/test/source-map/test-api.js
new file mode 100644
index 0000000..3801233
--- /dev/null
+++ b/test/source-map/test-api.js
@@ -0,0 +1,26 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2012 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var sourceMap;
+  try {
+    sourceMap = require('../../lib/source-map');
+  } catch (e) {
+    sourceMap = {};
+    Components.utils.import('resource:///modules/devtools/SourceMap.jsm', sourceMap);
+  }
+
+  exports['test that the api is properly exposed in the top level'] = function (assert, util) {
+    assert.equal(typeof sourceMap.SourceMapGenerator, "function");
+    assert.equal(typeof sourceMap.SourceMapConsumer, "function");
+    assert.equal(typeof sourceMap.SourceNode, "function");
+  };
+
+});
diff --git a/test/source-map/test-array-set.js b/test/source-map/test-array-set.js
new file mode 100644
index 0000000..b5797ed
--- /dev/null
+++ b/test/source-map/test-array-set.js
@@ -0,0 +1,104 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var ArraySet = require('../../lib/source-map/array-set').ArraySet;
+
+  function makeTestSet() {
+    var set = new ArraySet();
+    for (var i = 0; i < 100; i++) {
+      set.add(String(i));
+    }
+    return set;
+  }
+
+  exports['test .has() membership'] = function (assert, util) {
+    var set = makeTestSet();
+    for (var i = 0; i < 100; i++) {
+      assert.ok(set.has(String(i)));
+    }
+  };
+
+  exports['test .indexOf() elements'] = function (assert, util) {
+    var set = makeTestSet();
+    for (var i = 0; i < 100; i++) {
+      assert.strictEqual(set.indexOf(String(i)), i);
+    }
+  };
+
+  exports['test .at() indexing'] = function (assert, util) {
+    var set = makeTestSet();
+    for (var i = 0; i < 100; i++) {
+      assert.strictEqual(set.at(i), String(i));
+    }
+  };
+
+  exports['test creating from an array'] = function (assert, util) {
+    var set = ArraySet.fromArray(['foo', 'bar', 'baz', 'quux', 'hasOwnProperty']);
+
+    assert.ok(set.has('foo'));
+    assert.ok(set.has('bar'));
+    assert.ok(set.has('baz'));
+    assert.ok(set.has('quux'));
+    assert.ok(set.has('hasOwnProperty'));
+
+    assert.strictEqual(set.indexOf('foo'), 0);
+    assert.strictEqual(set.indexOf('bar'), 1);
+    assert.strictEqual(set.indexOf('baz'), 2);
+    assert.strictEqual(set.indexOf('quux'), 3);
+
+    assert.strictEqual(set.at(0), 'foo');
+    assert.strictEqual(set.at(1), 'bar');
+    assert.strictEqual(set.at(2), 'baz');
+    assert.strictEqual(set.at(3), 'quux');
+  };
+
+  exports['test that you can add __proto__; see github issue #30'] = function (assert, util) {
+    var set = new ArraySet();
+    set.add('__proto__');
+    assert.ok(set.has('__proto__'));
+    assert.strictEqual(set.at(0), '__proto__');
+    assert.strictEqual(set.indexOf('__proto__'), 0);
+  };
+
+  exports['test .fromArray() with duplicates'] = function (assert, util) {
+    var set = ArraySet.fromArray(['foo', 'foo']);
+    assert.ok(set.has('foo'));
+    assert.strictEqual(set.at(0), 'foo');
+    assert.strictEqual(set.indexOf('foo'), 0);
+    assert.strictEqual(set.toArray().length, 1);
+
+    set = ArraySet.fromArray(['foo', 'foo'], true);
+    assert.ok(set.has('foo'));
+    assert.strictEqual(set.at(0), 'foo');
+    assert.strictEqual(set.at(1), 'foo');
+    assert.strictEqual(set.indexOf('foo'), 0);
+    assert.strictEqual(set.toArray().length, 2);
+  };
+
+  exports['test .add() with duplicates'] = function (assert, util) {
+    var set = new ArraySet();
+    set.add('foo');
+
+    set.add('foo');
+    assert.ok(set.has('foo'));
+    assert.strictEqual(set.at(0), 'foo');
+    assert.strictEqual(set.indexOf('foo'), 0);
+    assert.strictEqual(set.toArray().length, 1);
+
+    set.add('foo', true);
+    assert.ok(set.has('foo'));
+    assert.strictEqual(set.at(0), 'foo');
+    assert.strictEqual(set.at(1), 'foo');
+    assert.strictEqual(set.indexOf('foo'), 0);
+    assert.strictEqual(set.toArray().length, 2);
+  };
+
+});
diff --git a/test/source-map/test-base64-vlq.js b/test/source-map/test-base64-vlq.js
new file mode 100644
index 0000000..653a874
--- /dev/null
+++ b/test/source-map/test-base64-vlq.js
@@ -0,0 +1,24 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var base64VLQ = require('../../lib/source-map/base64-vlq');
+
+  exports['test normal encoding and decoding'] = function (assert, util) {
+    var result;
+    for (var i = -255; i < 256; i++) {
+      result = base64VLQ.decode(base64VLQ.encode(i));
+      assert.ok(result);
+      assert.equal(result.value, i);
+      assert.equal(result.rest, "");
+    }
+  };
+
+});
diff --git a/test/source-map/test-base64.js b/test/source-map/test-base64.js
new file mode 100644
index 0000000..ff3a244
--- /dev/null
+++ b/test/source-map/test-base64.js
@@ -0,0 +1,35 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var base64 = require('../../lib/source-map/base64');
+
+  exports['test out of range encoding'] = function (assert, util) {
+    assert.throws(function () {
+      base64.encode(-1);
+    });
+    assert.throws(function () {
+      base64.encode(64);
+    });
+  };
+
+  exports['test out of range decoding'] = function (assert, util) {
+    assert.throws(function () {
+      base64.decode('=');
+    });
+  };
+
+  exports['test normal encoding and decoding'] = function (assert, util) {
+    for (var i = 0; i < 64; i++) {
+      assert.equal(base64.decode(base64.encode(i)), i);
+    }
+  };
+
+});
diff --git a/test/source-map/test-binary-search.js b/test/source-map/test-binary-search.js
new file mode 100644
index 0000000..ee30683
--- /dev/null
+++ b/test/source-map/test-binary-search.js
@@ -0,0 +1,54 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var binarySearch = require('../../lib/source-map/binary-search');
+
+  function numberCompare(a, b) {
+    return a - b;
+  }
+
+  exports['test too high'] = function (assert, util) {
+    var needle = 30;
+    var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+    assert.doesNotThrow(function () {
+      binarySearch.search(needle, haystack, numberCompare);
+    });
+
+    assert.equal(binarySearch.search(needle, haystack, numberCompare), 20);
+  };
+
+  exports['test too low'] = function (assert, util) {
+    var needle = 1;
+    var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+    assert.doesNotThrow(function () {
+      binarySearch.search(needle, haystack, numberCompare);
+    });
+
+    assert.equal(binarySearch.search(needle, haystack, numberCompare), null);
+  };
+
+  exports['test exact search'] = function (assert, util) {
+    var needle = 4;
+    var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+    assert.equal(binarySearch.search(needle, haystack, numberCompare), 4);
+  };
+
+  exports['test fuzzy search'] = function (assert, util) {
+    var needle = 19;
+    var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+    assert.equal(binarySearch.search(needle, haystack, numberCompare), 18);
+  };
+
+});
diff --git a/test/source-map/test-dog-fooding.js b/test/source-map/test-dog-fooding.js
new file mode 100644
index 0000000..26757b2
--- /dev/null
+++ b/test/source-map/test-dog-fooding.js
@@ -0,0 +1,84 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var SourceMapConsumer = require('../../lib/source-map/source-map-consumer').SourceMapConsumer;
+  var SourceMapGenerator = require('../../lib/source-map/source-map-generator').SourceMapGenerator;
+
+  exports['test eating our own dog food'] = function (assert, util) {
+    var smg = new SourceMapGenerator({
+      file: 'testing.js',
+      sourceRoot: '/wu/tang'
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 1, column: 0 },
+      generated: { line: 2, column: 2 }
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 2, column: 0 },
+      generated: { line: 3, column: 2 }
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 3, column: 0 },
+      generated: { line: 4, column: 2 }
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 4, column: 0 },
+      generated: { line: 5, column: 2 }
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 5, column: 10 },
+      generated: { line: 6, column: 12 }
+    });
+
+    var smc = new SourceMapConsumer(smg.toString());
+
+    // Exact
+    util.assertMapping(2, 2, '/wu/tang/gza.coffee', 1, 0, null, smc, assert);
+    util.assertMapping(3, 2, '/wu/tang/gza.coffee', 2, 0, null, smc, assert);
+    util.assertMapping(4, 2, '/wu/tang/gza.coffee', 3, 0, null, smc, assert);
+    util.assertMapping(5, 2, '/wu/tang/gza.coffee', 4, 0, null, smc, assert);
+    util.assertMapping(6, 12, '/wu/tang/gza.coffee', 5, 10, null, smc, assert);
+
+    // Fuzzy
+
+    // Generated to original
+    util.assertMapping(2, 0, null, null, null, null, smc, assert, true);
+    util.assertMapping(2, 9, '/wu/tang/gza.coffee', 1, 0, null, smc, assert, true);
+    util.assertMapping(3, 0, null, null, null, null, smc, assert, true);
+    util.assertMapping(3, 9, '/wu/tang/gza.coffee', 2, 0, null, smc, assert, true);
+    util.assertMapping(4, 0, null, null, null, null, smc, assert, true);
+    util.assertMapping(4, 9, '/wu/tang/gza.coffee', 3, 0, null, smc, assert, true);
+    util.assertMapping(5, 0, null, null, null, null, smc, assert, true);
+    util.assertMapping(5, 9, '/wu/tang/gza.coffee', 4, 0, null, smc, assert, true);
+    util.assertMapping(6, 0, null, null, null, null, smc, assert, true);
+    util.assertMapping(6, 9, null, null, null, null, smc, assert, true);
+    util.assertMapping(6, 13, '/wu/tang/gza.coffee', 5, 10, null, smc, assert, true);
+
+    // Original to generated
+    util.assertMapping(2, 2, '/wu/tang/gza.coffee', 1, 1, null, smc, assert, null, true);
+    util.assertMapping(3, 2, '/wu/tang/gza.coffee', 2, 3, null, smc, assert, null, true);
+    util.assertMapping(4, 2, '/wu/tang/gza.coffee', 3, 6, null, smc, assert, null, true);
+    util.assertMapping(5, 2, '/wu/tang/gza.coffee', 4, 9, null, smc, assert, null, true);
+    util.assertMapping(5, 2, '/wu/tang/gza.coffee', 5, 9, null, smc, assert, null, true);
+    util.assertMapping(6, 12, '/wu/tang/gza.coffee', 6, 19, null, smc, assert, null, true);
+  };
+
+});
diff --git a/test/source-map/test-source-map-consumer.js b/test/source-map/test-source-map-consumer.js
new file mode 100644
index 0000000..acd24d5
--- /dev/null
+++ b/test/source-map/test-source-map-consumer.js
@@ -0,0 +1,475 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var SourceMapConsumer = require('../../lib/source-map/source-map-consumer').SourceMapConsumer;
+  var SourceMapGenerator = require('../../lib/source-map/source-map-generator').SourceMapGenerator;
+
+  exports['test that we can instantiate with a string or an object'] = function (assert, util) {
+    assert.doesNotThrow(function () {
+      var map = new SourceMapConsumer(util.testMap);
+    });
+    assert.doesNotThrow(function () {
+      var map = new SourceMapConsumer(JSON.stringify(util.testMap));
+    });
+  };
+
+  exports['test that the `sources` field has the original sources'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var sources = map.sources;
+
+    assert.equal(sources[0], '/the/root/one.js');
+    assert.equal(sources[1], '/the/root/two.js');
+    assert.equal(sources.length, 2);
+  };
+
+  exports['test that the source root is reflected in a mapping\'s source field'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var mapping;
+
+    mapping = map.originalPositionFor({
+      line: 2,
+      column: 1
+    });
+    assert.equal(mapping.source, '/the/root/two.js');
+
+    mapping = map.originalPositionFor({
+      line: 1,
+      column: 1
+    });
+    assert.equal(mapping.source, '/the/root/one.js');
+  };
+
+  exports['test mapping tokens back exactly'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+
+    util.assertMapping(1, 1, '/the/root/one.js', 1, 1, null, map, assert);
+    util.assertMapping(1, 5, '/the/root/one.js', 1, 5, null, map, assert);
+    util.assertMapping(1, 9, '/the/root/one.js', 1, 11, null, map, assert);
+    util.assertMapping(1, 18, '/the/root/one.js', 1, 21, 'bar', map, assert);
+    util.assertMapping(1, 21, '/the/root/one.js', 2, 3, null, map, assert);
+    util.assertMapping(1, 28, '/the/root/one.js', 2, 10, 'baz', map, assert);
+    util.assertMapping(1, 32, '/the/root/one.js', 2, 14, 'bar', map, assert);
+
+    util.assertMapping(2, 1, '/the/root/two.js', 1, 1, null, map, assert);
+    util.assertMapping(2, 5, '/the/root/two.js', 1, 5, null, map, assert);
+    util.assertMapping(2, 9, '/the/root/two.js', 1, 11, null, map, assert);
+    util.assertMapping(2, 18, '/the/root/two.js', 1, 21, 'n', map, assert);
+    util.assertMapping(2, 21, '/the/root/two.js', 2, 3, null, map, assert);
+    util.assertMapping(2, 28, '/the/root/two.js', 2, 10, 'n', map, assert);
+  };
+
+  exports['test mapping tokens fuzzy'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+
+    // Finding original positions
+    util.assertMapping(1, 20, '/the/root/one.js', 1, 21, 'bar', map, assert, true);
+    util.assertMapping(1, 30, '/the/root/one.js', 2, 10, 'baz', map, assert, true);
+    util.assertMapping(2, 12, '/the/root/two.js', 1, 11, null, map, assert, true);
+
+    // Finding generated positions
+    util.assertMapping(1, 18, '/the/root/one.js', 1, 22, 'bar', map, assert, null, true);
+    util.assertMapping(1, 28, '/the/root/one.js', 2, 13, 'baz', map, assert, null, true);
+    util.assertMapping(2, 9, '/the/root/two.js', 1, 16, null, map, assert, null, true);
+  };
+
+  exports['test mappings and end of lines'] = function (assert, util) {
+    var smg = new SourceMapGenerator({
+      file: 'foo.js'
+    });
+    smg.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 1, column: 1 },
+      source: 'bar.js'
+    });
+    smg.addMapping({
+      original: { line: 2, column: 2 },
+      generated: { line: 2, column: 2 },
+      source: 'bar.js'
+    });
+
+    var map = SourceMapConsumer.fromSourceMap(smg);
+
+    // When finding original positions, mappings end at the end of the line.
+    util.assertMapping(2, 1, null, null, null, null, map, assert, true)
+
+    // When finding generated positions, mappings do not end at the end of the line.
+    util.assertMapping(1, 1, 'bar.js', 2, 1, null, map, assert, null, true);
+  };
+
+  exports['test creating source map consumers with )]}\' prefix'] = function (assert, util) {
+    assert.doesNotThrow(function () {
+      var map = new SourceMapConsumer(")]}'" + JSON.stringify(util.testMap));
+    });
+  };
+
+  exports['test eachMapping'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var previousLine = -Infinity;
+    var previousColumn = -Infinity;
+    map.eachMapping(function (mapping) {
+      assert.ok(mapping.generatedLine >= previousLine);
+
+      if (mapping.source) {
+        assert.equal(mapping.source.indexOf(util.testMap.sourceRoot), 0);
+      }
+
+      if (mapping.generatedLine === previousLine) {
+        assert.ok(mapping.generatedColumn >= previousColumn);
+        previousColumn = mapping.generatedColumn;
+      }
+      else {
+        previousLine = mapping.generatedLine;
+        previousColumn = -Infinity;
+      }
+    });
+  };
+
+  exports['test iterating over mappings in a different order'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var previousLine = -Infinity;
+    var previousColumn = -Infinity;
+    var previousSource = "";
+    map.eachMapping(function (mapping) {
+      assert.ok(mapping.source >= previousSource);
+
+      if (mapping.source === previousSource) {
+        assert.ok(mapping.originalLine >= previousLine);
+
+        if (mapping.originalLine === previousLine) {
+          assert.ok(mapping.originalColumn >= previousColumn);
+          previousColumn = mapping.originalColumn;
+        }
+        else {
+          previousLine = mapping.originalLine;
+          previousColumn = -Infinity;
+        }
+      }
+      else {
+        previousSource = mapping.source;
+        previousLine = -Infinity;
+        previousColumn = -Infinity;
+      }
+    }, null, SourceMapConsumer.ORIGINAL_ORDER);
+  };
+
+  exports['test that we can set the context for `this` in eachMapping'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var context = {};
+    map.eachMapping(function () {
+      assert.equal(this, context);
+    }, context);
+  };
+
+  exports['test that the `sourcesContent` field has the original sources'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMapWithSourcesContent);
+    var sourcesContent = map.sourcesContent;
+
+    assert.equal(sourcesContent[0], ' ONE.foo = function (bar) {\n   return baz(bar);\n };');
+    assert.equal(sourcesContent[1], ' TWO.inc = function (n) {\n   return n + 1;\n };');
+    assert.equal(sourcesContent.length, 2);
+  };
+
+  exports['test that we can get the original sources for the sources'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMapWithSourcesContent);
+    var sources = map.sources;
+
+    assert.equal(map.sourceContentFor(sources[0]), ' ONE.foo = function (bar) {\n   return baz(bar);\n };');
+    assert.equal(map.sourceContentFor(sources[1]), ' TWO.inc = function (n) {\n   return n + 1;\n };');
+    assert.equal(map.sourceContentFor("one.js"), ' ONE.foo = function (bar) {\n   return baz(bar);\n };');
+    assert.equal(map.sourceContentFor("two.js"), ' TWO.inc = function (n) {\n   return n + 1;\n };');
+    assert.throws(function () {
+      map.sourceContentFor("");
+    }, Error);
+    assert.throws(function () {
+      map.sourceContentFor("/the/root/three.js");
+    }, Error);
+    assert.throws(function () {
+      map.sourceContentFor("three.js");
+    }, Error);
+  };
+
+  exports['test sourceRoot + generatedPositionFor'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      sourceRoot: 'foo/bar',
+      file: 'baz.js'
+    });
+    map.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 2, column: 2 },
+      source: 'bang.coffee'
+    });
+    map.addMapping({
+      original: { line: 5, column: 5 },
+      generated: { line: 6, column: 6 },
+      source: 'bang.coffee'
+    });
+    map = new SourceMapConsumer(map.toString());
+
+    // Should handle without sourceRoot.
+    var pos = map.generatedPositionFor({
+      line: 1,
+      column: 1,
+      source: 'bang.coffee'
+    });
+
+    assert.equal(pos.line, 2);
+    assert.equal(pos.column, 2);
+
+    // Should handle with sourceRoot.
+    var pos = map.generatedPositionFor({
+      line: 1,
+      column: 1,
+      source: 'foo/bar/bang.coffee'
+    });
+
+    assert.equal(pos.line, 2);
+    assert.equal(pos.column, 2);
+  };
+
+  exports['test sourceRoot + originalPositionFor'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      sourceRoot: 'foo/bar',
+      file: 'baz.js'
+    });
+    map.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 2, column: 2 },
+      source: 'bang.coffee'
+    });
+    map = new SourceMapConsumer(map.toString());
+
+    var pos = map.originalPositionFor({
+      line: 2,
+      column: 2,
+    });
+
+    // Should always have the prepended source root
+    assert.equal(pos.source, 'foo/bar/bang.coffee');
+    assert.equal(pos.line, 1);
+    assert.equal(pos.column, 1);
+  };
+
+  exports['test github issue #56'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      sourceRoot: 'http://',
+      file: 'www.example.com/foo.js'
+    });
+    map.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 2, column: 2 },
+      source: 'www.example.com/original.js'
+    });
+    map = new SourceMapConsumer(map.toString());
+
+    var sources = map.sources;
+    assert.equal(sources.length, 1);
+    assert.equal(sources[0], 'http://www.example.com/original.js');
+  };
+
+  exports['test github issue #43'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      sourceRoot: 'http://example.com',
+      file: 'foo.js'
+    });
+    map.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 2, column: 2 },
+      source: 'http://cdn.example.com/original.js'
+    });
+    map = new SourceMapConsumer(map.toString());
+
+    var sources = map.sources;
+    assert.equal(sources.length, 1,
+                 'Should only be one source.');
+    assert.equal(sources[0], 'http://cdn.example.com/original.js',
+                 'Should not be joined with the sourceRoot.');
+  };
+
+  exports['test absolute path, but same host sources'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      sourceRoot: 'http://example.com/foo/bar',
+      file: 'foo.js'
+    });
+    map.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 2, column: 2 },
+      source: '/original.js'
+    });
+    map = new SourceMapConsumer(map.toString());
+
+    var sources = map.sources;
+    assert.equal(sources.length, 1,
+                 'Should only be one source.');
+    assert.equal(sources[0], 'http://example.com/original.js',
+                 'Source should be relative the host of the source root.');
+  };
+
+  exports['test github issue #64'] = function (assert, util) {
+    var map = new SourceMapConsumer({
+      "version": 3,
+      "file": "foo.js",
+      "sourceRoot": "http://example.com/",
+      "sources": ["/a"],
+      "names": [],
+      "mappings": "AACA",
+      "sourcesContent": ["foo"]
+    });
+
+    assert.equal(map.sourceContentFor("a"), "foo");
+    assert.equal(map.sourceContentFor("/a"), "foo");
+  };
+
+  exports['test bug 885597'] = function (assert, util) {
+    var map = new SourceMapConsumer({
+      "version": 3,
+      "file": "foo.js",
+      "sourceRoot": "file:///Users/AlGore/Invented/The/Internet/",
+      "sources": ["/a"],
+      "names": [],
+      "mappings": "AACA",
+      "sourcesContent": ["foo"]
+    });
+
+    var s = map.sources[0];
+    assert.equal(map.sourceContentFor(s), "foo");
+  };
+
+  exports['test github issue #72, duplicate sources'] = function (assert, util) {
+    var map = new SourceMapConsumer({
+      "version": 3,
+      "file": "foo.js",
+      "sources": ["source1.js", "source1.js", "source3.js"],
+      "names": [],
+      "mappings": ";EAAC;;IAEE;;MEEE",
+      "sourceRoot": "http://example.com"
+    });
+
+    var pos = map.originalPositionFor({
+      line: 2,
+      column: 2
+    });
+    assert.equal(pos.source, 'http://example.com/source1.js');
+    assert.equal(pos.line, 1);
+    assert.equal(pos.column, 1);
+
+    var pos = map.originalPositionFor({
+      line: 4,
+      column: 4
+    });
+    assert.equal(pos.source, 'http://example.com/source1.js');
+    assert.equal(pos.line, 3);
+    assert.equal(pos.column, 3);
+
+    var pos = map.originalPositionFor({
+      line: 6,
+      column: 6
+    });
+    assert.equal(pos.source, 'http://example.com/source3.js');
+    assert.equal(pos.line, 5);
+    assert.equal(pos.column, 5);
+  };
+
+  exports['test github issue #72, duplicate names'] = function (assert, util) {
+    var map = new SourceMapConsumer({
+      "version": 3,
+      "file": "foo.js",
+      "sources": ["source.js"],
+      "names": ["name1", "name1", "name3"],
+      "mappings": ";EAACA;;IAEEA;;MAEEE",
+      "sourceRoot": "http://example.com"
+    });
+
+    var pos = map.originalPositionFor({
+      line: 2,
+      column: 2
+    });
+    assert.equal(pos.name, 'name1');
+    assert.equal(pos.line, 1);
+    assert.equal(pos.column, 1);
+
+    var pos = map.originalPositionFor({
+      line: 4,
+      column: 4
+    });
+    assert.equal(pos.name, 'name1');
+    assert.equal(pos.line, 3);
+    assert.equal(pos.column, 3);
+
+    var pos = map.originalPositionFor({
+      line: 6,
+      column: 6
+    });
+    assert.equal(pos.name, 'name3');
+    assert.equal(pos.line, 5);
+    assert.equal(pos.column, 5);
+  };
+
+  exports['test SourceMapConsumer.fromSourceMap'] = function (assert, util) {
+    var smg = new SourceMapGenerator({
+      sourceRoot: 'http://example.com/',
+      file: 'foo.js'
+    });
+    smg.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 2, column: 2 },
+      source: 'bar.js'
+    });
+    smg.addMapping({
+      original: { line: 2, column: 2 },
+      generated: { line: 4, column: 4 },
+      source: 'baz.js',
+      name: 'dirtMcGirt'
+    });
+    smg.setSourceContent('baz.js', 'baz.js content');
+
+    var smc = SourceMapConsumer.fromSourceMap(smg);
+    assert.equal(smc.file, 'foo.js');
+    assert.equal(smc.sourceRoot, 'http://example.com/');
+    assert.equal(smc.sources.length, 2);
+    assert.equal(smc.sources[0], 'http://example.com/bar.js');
+    assert.equal(smc.sources[1], 'http://example.com/baz.js');
+    assert.equal(smc.sourceContentFor('baz.js'), 'baz.js content');
+
+    var pos = smc.originalPositionFor({
+      line: 2,
+      column: 2
+    });
+    assert.equal(pos.line, 1);
+    assert.equal(pos.column, 1);
+    assert.equal(pos.source, 'http://example.com/bar.js');
+    assert.equal(pos.name, null);
+
+    pos = smc.generatedPositionFor({
+      line: 1,
+      column: 1,
+      source: 'http://example.com/bar.js'
+    });
+    assert.equal(pos.line, 2);
+    assert.equal(pos.column, 2);
+
+    pos = smc.originalPositionFor({
+      line: 4,
+      column: 4
+    });
+    assert.equal(pos.line, 2);
+    assert.equal(pos.column, 2);
+    assert.equal(pos.source, 'http://example.com/baz.js');
+    assert.equal(pos.name, 'dirtMcGirt');
+
+    pos = smc.generatedPositionFor({
+      line: 2,
+      column: 2,
+      source: 'http://example.com/baz.js'
+    });
+    assert.equal(pos.line, 4);
+    assert.equal(pos.column, 4);
+  };
+});
diff --git a/test/source-map/test-source-map-generator.js b/test/source-map/test-source-map-generator.js
new file mode 100644
index 0000000..16241dd
--- /dev/null
+++ b/test/source-map/test-source-map-generator.js
@@ -0,0 +1,540 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var SourceMapGenerator = require('../../lib/source-map/source-map-generator').SourceMapGenerator;
+  var SourceMapConsumer = require('../../lib/source-map/source-map-consumer').SourceMapConsumer;
+  var SourceNode = require('../../lib/source-map/source-node').SourceNode;
+  var util = require('./util');
+
+  exports['test some simple stuff'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'foo.js',
+      sourceRoot: '.'
+    });
+    assert.ok(true);
+
+    var map = new SourceMapGenerator();
+    assert.ok(true);
+  };
+
+  exports['test JSON serialization'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'foo.js',
+      sourceRoot: '.'
+    });
+    assert.equal(map.toString(), JSON.stringify(map));
+  };
+
+  exports['test adding mappings (case 1)'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'generated-foo.js',
+      sourceRoot: '.'
+    });
+
+    assert.doesNotThrow(function () {
+      map.addMapping({
+        generated: { line: 1, column: 1 }
+      });
+    });
+  };
+
+  exports['test adding mappings (case 2)'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'generated-foo.js',
+      sourceRoot: '.'
+    });
+
+    assert.doesNotThrow(function () {
+      map.addMapping({
+        generated: { line: 1, column: 1 },
+        source: 'bar.js',
+        original: { line: 1, column: 1 }
+      });
+    });
+  };
+
+  exports['test adding mappings (case 3)'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'generated-foo.js',
+      sourceRoot: '.'
+    });
+
+    assert.doesNotThrow(function () {
+      map.addMapping({
+        generated: { line: 1, column: 1 },
+        source: 'bar.js',
+        original: { line: 1, column: 1 },
+        name: 'someToken'
+      });
+    });
+  };
+
+  exports['test adding mappings (invalid)'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'generated-foo.js',
+      sourceRoot: '.'
+    });
+
+    // Not enough info.
+    assert.throws(function () {
+      map.addMapping({});
+    });
+
+    // Original file position, but no source.
+    assert.throws(function () {
+      map.addMapping({
+        generated: { line: 1, column: 1 },
+        original: { line: 1, column: 1 }
+      });
+    });
+  };
+
+  exports['test that the correct mappings are being generated'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'min.js',
+      sourceRoot: '/the/root'
+    });
+
+    map.addMapping({
+      generated: { line: 1, column: 1 },
+      original: { line: 1, column: 1 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 5 },
+      original: { line: 1, column: 5 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 9 },
+      original: { line: 1, column: 11 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 18 },
+      original: { line: 1, column: 21 },
+      source: 'one.js',
+      name: 'bar'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 21 },
+      original: { line: 2, column: 3 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 28 },
+      original: { line: 2, column: 10 },
+      source: 'one.js',
+      name: 'baz'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 32 },
+      original: { line: 2, column: 14 },
+      source: 'one.js',
+      name: 'bar'
+    });
+
+    map.addMapping({
+      generated: { line: 2, column: 1 },
+      original: { line: 1, column: 1 },
+      source: 'two.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 5 },
+      original: { line: 1, column: 5 },
+      source: 'two.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 9 },
+      original: { line: 1, column: 11 },
+      source: 'two.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 18 },
+      original: { line: 1, column: 21 },
+      source: 'two.js',
+      name: 'n'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 21 },
+      original: { line: 2, column: 3 },
+      source: 'two.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 28 },
+      original: { line: 2, column: 10 },
+      source: 'two.js',
+      name: 'n'
+    });
+
+    map = JSON.parse(map.toString());
+
+    util.assertEqualMaps(assert, map, util.testMap);
+  };
+
+  exports['test that source content can be set'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'min.js',
+      sourceRoot: '/the/root'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 1 },
+      original: { line: 1, column: 1 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 1 },
+      original: { line: 1, column: 1 },
+      source: 'two.js'
+    });
+    map.setSourceContent('one.js', 'one file content');
+
+    map = JSON.parse(map.toString());
+    assert.equal(map.sources[0], 'one.js');
+    assert.equal(map.sources[1], 'two.js');
+    assert.equal(map.sourcesContent[0], 'one file content');
+    assert.equal(map.sourcesContent[1], null);
+  };
+
+  exports['test .fromSourceMap'] = function (assert, util) {
+    var map = SourceMapGenerator.fromSourceMap(new SourceMapConsumer(util.testMap));
+    util.assertEqualMaps(assert, map.toJSON(), util.testMap);
+  };
+
+  exports['test .fromSourceMap with sourcesContent'] = function (assert, util) {
+    var map = SourceMapGenerator.fromSourceMap(
+      new SourceMapConsumer(util.testMapWithSourcesContent));
+    util.assertEqualMaps(assert, map.toJSON(), util.testMapWithSourcesContent);
+  };
+
+  exports['test applySourceMap'] = function (assert, util) {
+    var node = new SourceNode(null, null, null, [
+      new SourceNode(2, 0, 'fileX', 'lineX2\n'),
+      'genA1\n',
+      new SourceNode(2, 0, 'fileY', 'lineY2\n'),
+      'genA2\n',
+      new SourceNode(1, 0, 'fileX', 'lineX1\n'),
+      'genA3\n',
+      new SourceNode(1, 0, 'fileY', 'lineY1\n')
+    ]);
+    var mapStep1 = node.toStringWithSourceMap({
+      file: 'fileA'
+    }).map;
+    mapStep1.setSourceContent('fileX', 'lineX1\nlineX2\n');
+    mapStep1 = mapStep1.toJSON();
+
+    node = new SourceNode(null, null, null, [
+      'gen1\n',
+      new SourceNode(1, 0, 'fileA', 'lineA1\n'),
+      new SourceNode(2, 0, 'fileA', 'lineA2\n'),
+      new SourceNode(3, 0, 'fileA', 'lineA3\n'),
+      new SourceNode(4, 0, 'fileA', 'lineA4\n'),
+      new SourceNode(1, 0, 'fileB', 'lineB1\n'),
+      new SourceNode(2, 0, 'fileB', 'lineB2\n'),
+      'gen2\n'
+    ]);
+    var mapStep2 = node.toStringWithSourceMap({
+      file: 'fileGen'
+    }).map;
+    mapStep2.setSourceContent('fileB', 'lineB1\nlineB2\n');
+    mapStep2 = mapStep2.toJSON();
+
+    node = new SourceNode(null, null, null, [
+      'gen1\n',
+      new SourceNode(2, 0, 'fileX', 'lineA1\n'),
+      new SourceNode(2, 0, 'fileA', 'lineA2\n'),
+      new SourceNode(2, 0, 'fileY', 'lineA3\n'),
+      new SourceNode(4, 0, 'fileA', 'lineA4\n'),
+      new SourceNode(1, 0, 'fileB', 'lineB1\n'),
+      new SourceNode(2, 0, 'fileB', 'lineB2\n'),
+      'gen2\n'
+    ]);
+    var expectedMap = node.toStringWithSourceMap({
+      file: 'fileGen'
+    }).map;
+    expectedMap.setSourceContent('fileX', 'lineX1\nlineX2\n');
+    expectedMap.setSourceContent('fileB', 'lineB1\nlineB2\n');
+    expectedMap = expectedMap.toJSON();
+
+    // apply source map "mapStep1" to "mapStep2"
+    var generator = SourceMapGenerator.fromSourceMap(new SourceMapConsumer(mapStep2));
+    generator.applySourceMap(new SourceMapConsumer(mapStep1));
+    var actualMap = generator.toJSON();
+
+    util.assertEqualMaps(assert, actualMap, expectedMap);
+  };
+
+  exports['test applySourceMap throws when file is missing'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'test.js'
+    });
+    var map2 = new SourceMapGenerator();
+    assert.throws(function() {
+      map.applySourceMap(new SourceMapConsumer(map2.toJSON()));
+    });
+  };
+
+  exports['test the two additional parameters of applySourceMap'] = function (assert, util) {
+    // Assume the following directory structure:
+    //
+    // http://foo.org/
+    //   bar.coffee
+    //   app/
+    //     coffee/
+    //       foo.coffee
+    //     temp/
+    //       bundle.js
+    //     temp_maps/
+    //       bundle.js.map
+    //     public/
+    //       bundle.min.js
+    //       bundle.min.js.map
+    //
+    // http://www.example.com/
+    //   baz.coffee
+
+    var bundleMap = new SourceMapGenerator({
+      file: 'bundle.js'
+    });
+    bundleMap.addMapping({
+      generated: { line: 3, column: 3 },
+      original: { line: 2, column: 2 },
+      source: '../coffee/foo.coffee'
+    });
+    bundleMap.addMapping({
+      generated: { line: 13, column: 13 },
+      original: { line: 12, column: 12 },
+      source: '/bar.coffee'
+    });
+    bundleMap.addMapping({
+      generated: { line: 23, column: 23 },
+      original: { line: 22, column: 22 },
+      source: 'http://www.example.com/baz.coffee'
+    });
+    bundleMap = new SourceMapConsumer(bundleMap.toJSON());
+
+    var minifiedMap = new SourceMapGenerator({
+      file: 'bundle.min.js',
+      sourceRoot: '..'
+    });
+    minifiedMap.addMapping({
+      generated: { line: 1, column: 1 },
+      original: { line: 3, column: 3 },
+      source: 'temp/bundle.js'
+    });
+    minifiedMap.addMapping({
+      generated: { line: 11, column: 11 },
+      original: { line: 13, column: 13 },
+      source: 'temp/bundle.js'
+    });
+    minifiedMap.addMapping({
+      generated: { line: 21, column: 21 },
+      original: { line: 23, column: 23 },
+      source: 'temp/bundle.js'
+    });
+    minifiedMap = new SourceMapConsumer(minifiedMap.toJSON());
+
+    var expectedMap = function (sources) {
+      var map = new SourceMapGenerator({
+        file: 'bundle.min.js',
+        sourceRoot: '..'
+      });
+      map.addMapping({
+        generated: { line: 1, column: 1 },
+        original: { line: 2, column: 2 },
+        source: sources[0]
+      });
+      map.addMapping({
+        generated: { line: 11, column: 11 },
+        original: { line: 12, column: 12 },
+        source: sources[1]
+      });
+      map.addMapping({
+        generated: { line: 21, column: 21 },
+        original: { line: 22, column: 22 },
+        source: sources[2]
+      });
+      return map.toJSON();
+    }
+
+    var actualMap = function (aSourceMapPath) {
+      var map = SourceMapGenerator.fromSourceMap(minifiedMap);
+      // Note that relying on `bundleMap.file` (which is simply 'bundle.js')
+      // instead of supplying the second parameter wouldn't work here.
+      map.applySourceMap(bundleMap, '../temp/bundle.js', aSourceMapPath);
+      return map.toJSON();
+    }
+
+    util.assertEqualMaps(assert, actualMap('../temp_maps'), expectedMap([
+      'coffee/foo.coffee',
+      '/bar.coffee',
+      'http://www.example.com/baz.coffee'
+    ]));
+
+    util.assertEqualMaps(assert, actualMap('/app/temp_maps'), expectedMap([
+      '/app/coffee/foo.coffee',
+      '/bar.coffee',
+      'http://www.example.com/baz.coffee'
+    ]));
+
+    util.assertEqualMaps(assert, actualMap('http://foo.org/app/temp_maps'), expectedMap([
+      'http://foo.org/app/coffee/foo.coffee',
+      'http://foo.org/bar.coffee',
+      'http://www.example.com/baz.coffee'
+    ]));
+  };
+
+  exports['test sorting with duplicate generated mappings'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'test.js'
+    });
+    map.addMapping({
+      generated: { line: 3, column: 0 },
+      original: { line: 2, column: 0 },
+      source: 'a.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 0 }
+    });
+    map.addMapping({
+      generated: { line: 2, column: 0 }
+    });
+    map.addMapping({
+      generated: { line: 1, column: 0 },
+      original: { line: 1, column: 0 },
+      source: 'a.js'
+    });
+
+    util.assertEqualMaps(assert, map.toJSON(), {
+      version: 3,
+      file: 'test.js',
+      sources: ['a.js'],
+      names: [],
+      mappings: 'AAAA;A;AACA'
+    });
+  };
+
+  exports['test ignore duplicate mappings.'] = function (assert, util) {
+    var init = { file: 'min.js', sourceRoot: '/the/root' };
+    var map1, map2;
+
+    // null original source location
+    var nullMapping1 = {
+      generated: { line: 1, column: 0 }
+    };
+    var nullMapping2 = {
+      generated: { line: 2, column: 2 }
+    };
+
+    map1 = new SourceMapGenerator(init);
+    map2 = new SourceMapGenerator(init);
+
+    map1.addMapping(nullMapping1);
+    map1.addMapping(nullMapping1);
+
+    map2.addMapping(nullMapping1);
+
+    util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+    map1.addMapping(nullMapping2);
+    map1.addMapping(nullMapping1);
+
+    map2.addMapping(nullMapping2);
+
+    util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+    // original source location
+    var srcMapping1 = {
+      generated: { line: 1, column: 0 },
+      original: { line: 11, column: 0 },
+      source: 'srcMapping1.js'
+    };
+    var srcMapping2 = {
+      generated: { line: 2, column: 2 },
+      original: { line: 11, column: 0 },
+      source: 'srcMapping2.js'
+    };
+
+    map1 = new SourceMapGenerator(init);
+    map2 = new SourceMapGenerator(init);
+
+    map1.addMapping(srcMapping1);
+    map1.addMapping(srcMapping1);
+
+    map2.addMapping(srcMapping1);
+
+    util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+    map1.addMapping(srcMapping2);
+    map1.addMapping(srcMapping1);
+
+    map2.addMapping(srcMapping2);
+
+    util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+    // full original source and name information
+    var fullMapping1 = {
+      generated: { line: 1, column: 0 },
+      original: { line: 11, column: 0 },
+      source: 'fullMapping1.js',
+      name: 'fullMapping1'
+    };
+    var fullMapping2 = {
+      generated: { line: 2, column: 2 },
+      original: { line: 11, column: 0 },
+      source: 'fullMapping2.js',
+      name: 'fullMapping2'
+    };
+
+    map1 = new SourceMapGenerator(init);
+    map2 = new SourceMapGenerator(init);
+
+    map1.addMapping(fullMapping1);
+    map1.addMapping(fullMapping1);
+
+    map2.addMapping(fullMapping1);
+
+    util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+    map1.addMapping(fullMapping2);
+    map1.addMapping(fullMapping1);
+
+    map2.addMapping(fullMapping2);
+
+    util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+  };
+
+  exports['test github issue #72, check for duplicate names or sources'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'test.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 1 },
+      original: { line: 2, column: 2 },
+      source: 'a.js',
+      name: 'foo'
+    });
+    map.addMapping({
+      generated: { line: 3, column: 3 },
+      original: { line: 4, column: 4 },
+      source: 'a.js',
+      name: 'foo'
+    });
+    util.assertEqualMaps(assert, map.toJSON(), {
+      version: 3,
+      file: 'test.js',
+      sources: ['a.js'],
+      names: ['foo'],
+      mappings: 'CACEA;;GAEEA'
+    });
+  };
+
+});
diff --git a/test/source-map/test-source-node.js b/test/source-map/test-source-node.js
new file mode 100644
index 0000000..e7afc4e
--- /dev/null
+++ b/test/source-map/test-source-node.js
@@ -0,0 +1,439 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var SourceMapGenerator = require('../../lib/source-map/source-map-generator').SourceMapGenerator;
+  var SourceMapConsumer = require('../../lib/source-map/source-map-consumer').SourceMapConsumer;
+  var SourceNode = require('../../lib/source-map/source-node').SourceNode;
+
+  exports['test .add()'] = function (assert, util) {
+    var node = new SourceNode(null, null, null);
+
+    // Adding a string works.
+    node.add('function noop() {}');
+
+    // Adding another source node works.
+    node.add(new SourceNode(null, null, null));
+
+    // Adding an array works.
+    node.add(['function foo() {',
+              new SourceNode(null, null, null,
+                             'return 10;'),
+              '}']);
+
+    // Adding other stuff doesn't.
+    assert.throws(function () {
+      node.add({});
+    });
+    assert.throws(function () {
+      node.add(function () {});
+    });
+  };
+
+  exports['test .prepend()'] = function (assert, util) {
+    var node = new SourceNode(null, null, null);
+
+    // Prepending a string works.
+    node.prepend('function noop() {}');
+    assert.equal(node.children[0], 'function noop() {}');
+    assert.equal(node.children.length, 1);
+
+    // Prepending another source node works.
+    node.prepend(new SourceNode(null, null, null));
+    assert.equal(node.children[0], '');
+    assert.equal(node.children[1], 'function noop() {}');
+    assert.equal(node.children.length, 2);
+
+    // Prepending an array works.
+    node.prepend(['function foo() {',
+              new SourceNode(null, null, null,
+                             'return 10;'),
+              '}']);
+    assert.equal(node.children[0], 'function foo() {');
+    assert.equal(node.children[1], 'return 10;');
+    assert.equal(node.children[2], '}');
+    assert.equal(node.children[3], '');
+    assert.equal(node.children[4], 'function noop() {}');
+    assert.equal(node.children.length, 5);
+
+    // Prepending other stuff doesn't.
+    assert.throws(function () {
+      node.prepend({});
+    });
+    assert.throws(function () {
+      node.prepend(function () {});
+    });
+  };
+
+  exports['test .toString()'] = function (assert, util) {
+    assert.equal((new SourceNode(null, null, null,
+                                 ['function foo() {',
+                                  new SourceNode(null, null, null, 'return 10;'),
+                                  '}'])).toString(),
+                 'function foo() {return 10;}');
+  };
+
+  exports['test .join()'] = function (assert, util) {
+    assert.equal((new SourceNode(null, null, null,
+                                 ['a', 'b', 'c', 'd'])).join(', ').toString(),
+                 'a, b, c, d');
+  };
+
+  exports['test .walk()'] = function (assert, util) {
+    var node = new SourceNode(null, null, null,
+                              ['(function () {\n',
+                               '  ', new SourceNode(1, 0, 'a.js', ['someCall()']), ';\n',
+                               '  ', new SourceNode(2, 0, 'b.js', ['if (foo) bar()']), ';\n',
+                               '}());']);
+    var expected = [
+      { str: '(function () {\n', source: null,   line: null, column: null },
+      { str: '  ',               source: null,   line: null, column: null },
+      { str: 'someCall()',       source: 'a.js', line: 1,    column: 0    },
+      { str: ';\n',              source: null,   line: null, column: null },
+      { str: '  ',               source: null,   line: null, column: null },
+      { str: 'if (foo) bar()',   source: 'b.js', line: 2,    column: 0    },
+      { str: ';\n',              source: null,   line: null, column: null },
+      { str: '}());',            source: null,   line: null, column: null },
+    ];
+    var i = 0;
+    node.walk(function (chunk, loc) {
+      assert.equal(expected[i].str, chunk);
+      assert.equal(expected[i].source, loc.source);
+      assert.equal(expected[i].line, loc.line);
+      assert.equal(expected[i].column, loc.column);
+      i++;
+    });
+  };
+
+  exports['test .replaceRight'] = function (assert, util) {
+    var node;
+
+    // Not nested
+    node = new SourceNode(null, null, null, 'hello world');
+    node.replaceRight(/world/, 'universe');
+    assert.equal(node.toString(), 'hello universe');
+
+    // Nested
+    node = new SourceNode(null, null, null,
+                          [new SourceNode(null, null, null, 'hey sexy mama, '),
+                           new SourceNode(null, null, null, 'want to kill all humans?')]);
+    node.replaceRight(/kill all humans/, 'watch Futurama');
+    assert.equal(node.toString(), 'hey sexy mama, want to watch Futurama?');
+  };
+
+  exports['test .toStringWithSourceMap()'] = function (assert, util) {
+    var node = new SourceNode(null, null, null,
+                              ['(function () {\n',
+                               '  ',
+                                 new SourceNode(1, 0, 'a.js', 'someCall', 'originalCall'),
+                                 new SourceNode(1, 8, 'a.js', '()'),
+                                 ';\n',
+                               '  ', new SourceNode(2, 0, 'b.js', ['if (foo) bar()']), ';\n',
+                               '}());']);
+    var map = node.toStringWithSourceMap({
+      file: 'foo.js'
+    }).map;
+    var mapWithoutOptions = node.toStringWithSourceMap().map;
+
+    assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+    assert.ok(mapWithoutOptions instanceof SourceMapGenerator, 'mapWithoutOptions instanceof SourceMapGenerator');
+    mapWithoutOptions._file = 'foo.js';
+    util.assertEqualMaps(assert, map.toJSON(), mapWithoutOptions.toJSON());
+
+    map = new SourceMapConsumer(map.toString());
+
+    var actual;
+
+    actual = map.originalPositionFor({
+      line: 1,
+      column: 4
+    });
+    assert.equal(actual.source, null);
+    assert.equal(actual.line, null);
+    assert.equal(actual.column, null);
+
+    actual = map.originalPositionFor({
+      line: 2,
+      column: 2
+    });
+    assert.equal(actual.source, 'a.js');
+    assert.equal(actual.line, 1);
+    assert.equal(actual.column, 0);
+    assert.equal(actual.name, 'originalCall');
+
+    actual = map.originalPositionFor({
+      line: 3,
+      column: 2
+    });
+    assert.equal(actual.source, 'b.js');
+    assert.equal(actual.line, 2);
+    assert.equal(actual.column, 0);
+
+    actual = map.originalPositionFor({
+      line: 3,
+      column: 16
+    });
+    assert.equal(actual.source, null);
+    assert.equal(actual.line, null);
+    assert.equal(actual.column, null);
+
+    actual = map.originalPositionFor({
+      line: 4,
+      column: 2
+    });
+    assert.equal(actual.source, null);
+    assert.equal(actual.line, null);
+    assert.equal(actual.column, null);
+  };
+
+  exports['test .fromStringWithSourceMap()'] = function (assert, util) {
+    var node = SourceNode.fromStringWithSourceMap(
+                              util.testGeneratedCode,
+                              new SourceMapConsumer(util.testMap));
+
+    var result = node.toStringWithSourceMap({
+      file: 'min.js'
+    });
+    var map = result.map;
+    var code = result.code;
+
+    assert.equal(code, util.testGeneratedCode);
+    assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+    map = map.toJSON();
+    assert.equal(map.version, util.testMap.version);
+    assert.equal(map.file, util.testMap.file);
+    assert.equal(map.mappings, util.testMap.mappings);
+  };
+
+  exports['test .fromStringWithSourceMap() empty map'] = function (assert, util) {
+    var node = SourceNode.fromStringWithSourceMap(
+                              util.testGeneratedCode,
+                              new SourceMapConsumer(util.emptyMap));
+    var result = node.toStringWithSourceMap({
+      file: 'min.js'
+    });
+    var map = result.map;
+    var code = result.code;
+
+    assert.equal(code, util.testGeneratedCode);
+    assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+    map = map.toJSON();
+    assert.equal(map.version, util.emptyMap.version);
+    assert.equal(map.file, util.emptyMap.file);
+    assert.equal(map.mappings.length, util.emptyMap.mappings.length);
+    assert.equal(map.mappings, util.emptyMap.mappings);
+  };
+
+  exports['test .fromStringWithSourceMap() complex version'] = function (assert, util) {
+    var input = new SourceNode(null, null, null, [
+      "(function() {\n",
+        "  var Test = {};\n",
+        "  ", new SourceNode(1, 0, "a.js", "Test.A = { value: 1234 };\n"),
+        "  ", new SourceNode(2, 0, "a.js", "Test.A.x = 'xyz';"), "\n",
+        "}());\n",
+        "/* Generated Source */"]);
+    input = input.toStringWithSourceMap({
+      file: 'foo.js'
+    });
+
+    var node = SourceNode.fromStringWithSourceMap(
+                              input.code,
+                              new SourceMapConsumer(input.map.toString()));
+
+    var result = node.toStringWithSourceMap({
+      file: 'foo.js'
+    });
+    var map = result.map;
+    var code = result.code;
+
+    assert.equal(code, input.code);
+    assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+    map = map.toJSON();
+    var inputMap = input.map.toJSON();
+    util.assertEqualMaps(assert, map, inputMap);
+  };
+
+  exports['test .toStringWithSourceMap() merging duplicate mappings'] = function (assert, util) {
+    var input = new SourceNode(null, null, null, [
+      new SourceNode(1, 0, "a.js", "(function"),
+      new SourceNode(1, 0, "a.js", "() {\n"),
+      "  ",
+      new SourceNode(1, 0, "a.js", "var Test = "),
+      new SourceNode(1, 0, "b.js", "{};\n"),
+      new SourceNode(2, 0, "b.js", "Test"),
+      new SourceNode(2, 0, "b.js", ".A", "A"),
+      new SourceNode(2, 20, "b.js", " = { value: ", "A"),
+      "1234",
+      new SourceNode(2, 40, "b.js", " };\n", "A"),
+      "}());\n",
+      "/* Generated Source */"
+    ]);
+    input = input.toStringWithSourceMap({
+      file: 'foo.js'
+    });
+
+    var correctMap = new SourceMapGenerator({
+      file: 'foo.js'
+    });
+    correctMap.addMapping({
+      generated: { line: 1, column: 0 },
+      source: 'a.js',
+      original: { line: 1, column: 0 }
+    });
+    // Here is no need for a empty mapping,
+    // because mappings ends at eol
+    correctMap.addMapping({
+      generated: { line: 2, column: 2 },
+      source: 'a.js',
+      original: { line: 1, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 2, column: 13 },
+      source: 'b.js',
+      original: { line: 1, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 3, column: 0 },
+      source: 'b.js',
+      original: { line: 2, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 3, column: 4 },
+      source: 'b.js',
+      name: 'A',
+      original: { line: 2, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 3, column: 6 },
+      source: 'b.js',
+      name: 'A',
+      original: { line: 2, column: 20 }
+    });
+    // This empty mapping is required,
+    // because there is a hole in the middle of the line
+    correctMap.addMapping({
+      generated: { line: 3, column: 18 }
+    });
+    correctMap.addMapping({
+      generated: { line: 3, column: 22 },
+      source: 'b.js',
+      name: 'A',
+      original: { line: 2, column: 40 }
+    });
+    // Here is no need for a empty mapping,
+    // because mappings ends at eol
+
+    var inputMap = input.map.toJSON();
+    correctMap = correctMap.toJSON();
+    util.assertEqualMaps(assert, inputMap, correctMap);
+  };
+
+  exports['test .toStringWithSourceMap() multi-line SourceNodes'] = function (assert, util) {
+    var input = new SourceNode(null, null, null, [
+      new SourceNode(1, 0, "a.js", "(function() {\nvar nextLine = 1;\nanotherLine();\n"),
+      new SourceNode(2, 2, "b.js", "Test.call(this, 123);\n"),
+      new SourceNode(2, 2, "b.js", "this['stuff'] = 'v';\n"),
+      new SourceNode(2, 2, "b.js", "anotherLine();\n"),
+      "/*\nGenerated\nSource\n*/\n",
+      new SourceNode(3, 4, "c.js", "anotherLine();\n"),
+      "/*\nGenerated\nSource\n*/"
+    ]);
+    input = input.toStringWithSourceMap({
+      file: 'foo.js'
+    });
+
+    var correctMap = new SourceMapGenerator({
+      file: 'foo.js'
+    });
+    correctMap.addMapping({
+      generated: { line: 1, column: 0 },
+      source: 'a.js',
+      original: { line: 1, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 2, column: 0 },
+      source: 'a.js',
+      original: { line: 1, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 3, column: 0 },
+      source: 'a.js',
+      original: { line: 1, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 4, column: 0 },
+      source: 'b.js',
+      original: { line: 2, column: 2 }
+    });
+    correctMap.addMapping({
+      generated: { line: 5, column: 0 },
+      source: 'b.js',
+      original: { line: 2, column: 2 }
+    });
+    correctMap.addMapping({
+      generated: { line: 6, column: 0 },
+      source: 'b.js',
+      original: { line: 2, column: 2 }
+    });
+    correctMap.addMapping({
+      generated: { line: 11, column: 0 },
+      source: 'c.js',
+      original: { line: 3, column: 4 }
+    });
+
+    var inputMap = input.map.toJSON();
+    correctMap = correctMap.toJSON();
+    util.assertEqualMaps(assert, inputMap, correctMap);
+  };
+
+  exports['test setSourceContent with toStringWithSourceMap'] = function (assert, util) {
+    var aNode = new SourceNode(1, 1, 'a.js', 'a');
+    aNode.setSourceContent('a.js', 'someContent');
+    var node = new SourceNode(null, null, null,
+                              ['(function () {\n',
+                               '  ', aNode,
+                               '  ', new SourceNode(1, 1, 'b.js', 'b'),
+                               '}());']);
+    node.setSourceContent('b.js', 'otherContent');
+    var map = node.toStringWithSourceMap({
+      file: 'foo.js'
+    }).map;
+
+    assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+    map = new SourceMapConsumer(map.toString());
+
+    assert.equal(map.sources.length, 2);
+    assert.equal(map.sources[0], 'a.js');
+    assert.equal(map.sources[1], 'b.js');
+    assert.equal(map.sourcesContent.length, 2);
+    assert.equal(map.sourcesContent[0], 'someContent');
+    assert.equal(map.sourcesContent[1], 'otherContent');
+  };
+
+  exports['test walkSourceContents'] = function (assert, util) {
+    var aNode = new SourceNode(1, 1, 'a.js', 'a');
+    aNode.setSourceContent('a.js', 'someContent');
+    var node = new SourceNode(null, null, null,
+                              ['(function () {\n',
+                               '  ', aNode,
+                               '  ', new SourceNode(1, 1, 'b.js', 'b'),
+                               '}());']);
+    node.setSourceContent('b.js', 'otherContent');
+    var results = [];
+    node.walkSourceContents(function (sourceFile, sourceContent) {
+      results.push([sourceFile, sourceContent]);
+    });
+    assert.equal(results.length, 2);
+    assert.equal(results[0][0], 'a.js');
+    assert.equal(results[0][1], 'someContent');
+    assert.equal(results[1][0], 'b.js');
+    assert.equal(results[1][1], 'otherContent');
+  };
+});
diff --git a/test/source-map/test-util.js b/test/source-map/test-util.js
new file mode 100644
index 0000000..22e9a9e
--- /dev/null
+++ b/test/source-map/test-util.js
@@ -0,0 +1,127 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2014 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var libUtil = require('../../lib/source-map/util');
+
+  exports['test urls'] = function (assert, util) {
+    var assertUrl = function (url) {
+      assert.equal(url, libUtil.urlGenerate(libUtil.urlParse(url)));
+    };
+    assertUrl('http://');
+    assertUrl('http://www.example.com');
+    assertUrl('http://user:pass@www.example.com');
+    assertUrl('http://www.example.com:80');
+    assertUrl('http://www.example.com/');
+    assertUrl('http://www.example.com/foo/bar');
+    assertUrl('http://www.example.com/foo/bar/');
+    assertUrl('http://user:pass@www.example.com:80/foo/bar/');
+
+    assertUrl('//');
+    assertUrl('//www.example.com');
+    assertUrl('file:///www.example.com');
+
+    assert.equal(libUtil.urlParse('a//b'), null);
+  };
+
+  exports['test normalize()'] = function (assert, util) {
+    assert.equal(libUtil.normalize('/..'), '/');
+    assert.equal(libUtil.normalize('/../'), '/');
+    assert.equal(libUtil.normalize('/../../../..'), '/');
+    assert.equal(libUtil.normalize('/../../../../a/b/c'), '/a/b/c');
+    assert.equal(libUtil.normalize('/a/b/c/../../../d/../../e'), '/e');
+
+    assert.equal(libUtil.normalize('..'), '..');
+    assert.equal(libUtil.normalize('../'), '../');
+    assert.equal(libUtil.normalize('../../a/'), '../../a/');
+    assert.equal(libUtil.normalize('a/..'), '.');
+    assert.equal(libUtil.normalize('a/../../..'), '../..');
+
+    assert.equal(libUtil.normalize('/.'), '/');
+    assert.equal(libUtil.normalize('/./'), '/');
+    assert.equal(libUtil.normalize('/./././.'), '/');
+    assert.equal(libUtil.normalize('/././././a/b/c'), '/a/b/c');
+    assert.equal(libUtil.normalize('/a/b/c/./././d/././e'), '/a/b/c/d/e');
+
+    assert.equal(libUtil.normalize('.'), '.');
+    assert.equal(libUtil.normalize('./'), '.');
+    assert.equal(libUtil.normalize('././a'), 'a');
+    assert.equal(libUtil.normalize('a/./'), 'a/');
+    assert.equal(libUtil.normalize('a/././.'), 'a');
+
+    assert.equal(libUtil.normalize('/a/b//c////d/////'), '/a/b/c/d/');
+    assert.equal(libUtil.normalize('///a/b//c////d/////'), '///a/b/c/d/');
+    assert.equal(libUtil.normalize('a/b//c////d'), 'a/b/c/d');
+
+    assert.equal(libUtil.normalize('.///.././../a/b//./..'), '../../a')
+
+    assert.equal(libUtil.normalize('http://www.example.com'), 'http://www.example.com');
+    assert.equal(libUtil.normalize('http://www.example.com/'), 'http://www.example.com/');
+    assert.equal(libUtil.normalize('http://www.example.com/./..//a/b/c/.././d//'), 'http://www.example.com/a/b/d/');
+  };
+
+  exports['test join()'] = function (assert, util) {
+    assert.equal(libUtil.join('a', 'b'), 'a/b');
+    assert.equal(libUtil.join('a/', 'b'), 'a/b');
+    assert.equal(libUtil.join('a//', 'b'), 'a/b');
+    assert.equal(libUtil.join('a', 'b/'), 'a/b/');
+    assert.equal(libUtil.join('a', 'b//'), 'a/b/');
+    assert.equal(libUtil.join('a/', '/b'), '/b');
+    assert.equal(libUtil.join('a//', '//b'), '//b');
+
+    assert.equal(libUtil.join('a', '..'), '.');
+    assert.equal(libUtil.join('a', '../b'), 'b');
+    assert.equal(libUtil.join('a/b', '../c'), 'a/c');
+
+    assert.equal(libUtil.join('a', '.'), 'a');
+    assert.equal(libUtil.join('a', './b'), 'a/b');
+    assert.equal(libUtil.join('a/b', './c'), 'a/b/c');
+
+    assert.equal(libUtil.join('a', 'http://www.example.com'), 'http://www.example.com');
+    assert.equal(libUtil.join('a', 'data:foo,bar'), 'data:foo,bar');
+
+
+    assert.equal(libUtil.join('http://foo.org/a', 'b'), 'http://foo.org/a/b');
+    assert.equal(libUtil.join('http://foo.org/a/', 'b'), 'http://foo.org/a/b');
+    assert.equal(libUtil.join('http://foo.org/a//', 'b'), 'http://foo.org/a/b');
+    assert.equal(libUtil.join('http://foo.org/a', 'b/'), 'http://foo.org/a/b/');
+    assert.equal(libUtil.join('http://foo.org/a', 'b//'), 'http://foo.org/a/b/');
+    assert.equal(libUtil.join('http://foo.org/a/', '/b'), 'http://foo.org/b');
+    assert.equal(libUtil.join('http://foo.org/a//', '//b'), 'http://b');
+
+    assert.equal(libUtil.join('http://foo.org/a', '..'), 'http://foo.org/');
+    assert.equal(libUtil.join('http://foo.org/a', '../b'), 'http://foo.org/b');
+    assert.equal(libUtil.join('http://foo.org/a/b', '../c'), 'http://foo.org/a/c');
+
+    assert.equal(libUtil.join('http://foo.org/a', '.'), 'http://foo.org/a');
+    assert.equal(libUtil.join('http://foo.org/a', './b'), 'http://foo.org/a/b');
+    assert.equal(libUtil.join('http://foo.org/a/b', './c'), 'http://foo.org/a/b/c');
+
+    assert.equal(libUtil.join('http://foo.org/a', 'http://www.example.com'), 'http://www.example.com');
+    assert.equal(libUtil.join('http://foo.org/a', 'data:foo,bar'), 'data:foo,bar');
+
+
+    assert.equal(libUtil.join('http://foo.org', 'a'), 'http://foo.org/a');
+    assert.equal(libUtil.join('http://foo.org/', 'a'), 'http://foo.org/a');
+    assert.equal(libUtil.join('http://foo.org//', 'a'), 'http://foo.org/a');
+    assert.equal(libUtil.join('http://foo.org', '/a'), 'http://foo.org/a');
+    assert.equal(libUtil.join('http://foo.org/', '/a'), 'http://foo.org/a');
+    assert.equal(libUtil.join('http://foo.org//', '/a'), 'http://foo.org/a');
+
+
+    assert.equal(libUtil.join('http://', 'www.example.com'), 'http://www.example.com');
+    assert.equal(libUtil.join('file:///', 'www.example.com'), 'file:///www.example.com');
+    assert.equal(libUtil.join('http://', 'ftp://example.com'), 'ftp://example.com');
+
+    assert.equal(libUtil.join('http://www.example.com', '//foo.org/bar'), 'http://foo.org/bar');
+    assert.equal(libUtil.join('//www.example.com', '//foo.org/bar'), '//foo.org/bar');
+  };
+
+});
diff --git a/test/source-map/util.js b/test/source-map/util.js
new file mode 100644
index 0000000..288046b
--- /dev/null
+++ b/test/source-map/util.js
@@ -0,0 +1,161 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var util = require('../../lib/source-map/util');
+
+  // This is a test mapping which maps functions from two different files
+  // (one.js and two.js) to a minified generated source.
+  //
+  // Here is one.js:
+  //
+  //   ONE.foo = function (bar) {
+  //     return baz(bar);
+  //   };
+  //
+  // Here is two.js:
+  //
+  //   TWO.inc = function (n) {
+  //     return n + 1;
+  //   };
+  //
+  // And here is the generated code (min.js):
+  //
+  //   ONE.foo=function(a){return baz(a);};
+  //   TWO.inc=function(a){return a+1;};
+  exports.testGeneratedCode = " ONE.foo=function(a){return baz(a);};\n"+
+                              " TWO.inc=function(a){return a+1;};";
+  exports.testMap = {
+    version: 3,
+    file: 'min.js',
+    names: ['bar', 'baz', 'n'],
+    sources: ['one.js', 'two.js'],
+    sourceRoot: '/the/root',
+    mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+  };
+  exports.testMapWithSourcesContent = {
+    version: 3,
+    file: 'min.js',
+    names: ['bar', 'baz', 'n'],
+    sources: ['one.js', 'two.js'],
+    sourcesContent: [
+      ' ONE.foo = function (bar) {\n' +
+      '   return baz(bar);\n' +
+      ' };',
+      ' TWO.inc = function (n) {\n' +
+      '   return n + 1;\n' +
+      ' };'
+    ],
+    sourceRoot: '/the/root',
+    mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+  };
+  exports.emptyMap = {
+    version: 3,
+    file: 'min.js',
+    names: [],
+    sources: [],
+    mappings: ''
+  };
+
+
+  function assertMapping(generatedLine, generatedColumn, originalSource,
+                         originalLine, originalColumn, name, map, assert,
+                         dontTestGenerated, dontTestOriginal) {
+    if (!dontTestOriginal) {
+      var origMapping = map.originalPositionFor({
+        line: generatedLine,
+        column: generatedColumn
+      });
+      assert.equal(origMapping.name, name,
+                   'Incorrect name, expected ' + JSON.stringify(name)
+                   + ', got ' + JSON.stringify(origMapping.name));
+      assert.equal(origMapping.line, originalLine,
+                   'Incorrect line, expected ' + JSON.stringify(originalLine)
+                   + ', got ' + JSON.stringify(origMapping.line));
+      assert.equal(origMapping.column, originalColumn,
+                   'Incorrect column, expected ' + JSON.stringify(originalColumn)
+                   + ', got ' + JSON.stringify(origMapping.column));
+
+      var expectedSource;
+
+      if (originalSource && map.sourceRoot && originalSource.indexOf(map.sourceRoot) === 0) {
+        expectedSource = originalSource;
+      } else if (originalSource) {
+        expectedSource = map.sourceRoot
+          ? util.join(map.sourceRoot, originalSource)
+          : originalSource;
+      } else {
+        expectedSource = null;
+      }
+
+      assert.equal(origMapping.source, expectedSource,
+                   'Incorrect source, expected ' + JSON.stringify(expectedSource)
+                   + ', got ' + JSON.stringify(origMapping.source));
+    }
+
+    if (!dontTestGenerated) {
+      var genMapping = map.generatedPositionFor({
+        source: originalSource,
+        line: originalLine,
+        column: originalColumn
+      });
+      assert.equal(genMapping.line, generatedLine,
+                   'Incorrect line, expected ' + JSON.stringify(generatedLine)
+                   + ', got ' + JSON.stringify(genMapping.line));
+      assert.equal(genMapping.column, generatedColumn,
+                   'Incorrect column, expected ' + JSON.stringify(generatedColumn)
+                   + ', got ' + JSON.stringify(genMapping.column));
+    }
+  }
+  exports.assertMapping = assertMapping;
+
+  function assertEqualMaps(assert, actualMap, expectedMap) {
+    assert.equal(actualMap.version, expectedMap.version, "version mismatch");
+    assert.equal(actualMap.file, expectedMap.file, "file mismatch");
+    assert.equal(actualMap.names.length,
+                 expectedMap.names.length,
+                 "names length mismatch: " +
+                   actualMap.names.join(", ") + " != " + expectedMap.names.join(", "));
+    for (var i = 0; i < actualMap.names.length; i++) {
+      assert.equal(actualMap.names[i],
+                   expectedMap.names[i],
+                   "names[" + i + "] mismatch: " +
+                     actualMap.names.join(", ") + " != " + expectedMap.names.join(", "));
+    }
+    assert.equal(actualMap.sources.length,
+                 expectedMap.sources.length,
+                 "sources length mismatch: " +
+                   actualMap.sources.join(", ") + " != " + expectedMap.sources.join(", "));
+    for (var i = 0; i < actualMap.sources.length; i++) {
+      assert.equal(actualMap.sources[i],
+                   expectedMap.sources[i],
+                   "sources[" + i + "] length mismatch: " +
+                   actualMap.sources.join(", ") + " != " + expectedMap.sources.join(", "));
+    }
+    assert.equal(actualMap.sourceRoot,
+                 expectedMap.sourceRoot,
+                 "sourceRoot mismatch: " +
+                   actualMap.sourceRoot + " != " + expectedMap.sourceRoot);
+    assert.equal(actualMap.mappings, expectedMap.mappings,
+                 "mappings mismatch:\nActual:   " + actualMap.mappings + "\nExpected: " + expectedMap.mappings);
+    if (actualMap.sourcesContent) {
+      assert.equal(actualMap.sourcesContent.length,
+                   expectedMap.sourcesContent.length,
+                   "sourcesContent length mismatch");
+      for (var i = 0; i < actualMap.sourcesContent.length; i++) {
+        assert.equal(actualMap.sourcesContent[i],
+                     expectedMap.sourcesContent[i],
+                     "sourcesContent[" + i + "] mismatch");
+      }
+    }
+  }
+  exports.assertEqualMaps = assertEqualMaps;
+
+});

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-source-map.git



More information about the Pkg-javascript-commits mailing list