/* -*- 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; function forEachNewline(fn) { return function (assert, util) { ['\n', '\r\n'].forEach(fn.bind(null, assert, util)); } } 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()'] = forEachNewline(function (assert, util, nl) { var node = new SourceNode(null, null, null, ['(function () {' + nl, ' ', new SourceNode(1, 0, 'a.js', 'someCall', 'originalCall'), new SourceNode(1, 8, 'a.js', '()'), ';' + nl, ' ', new SourceNode(2, 0, 'b.js', ['if (foo) bar()']), ';' + nl, '}());']); var result = node.toStringWithSourceMap({ file: 'foo.js' }); assert.equal(result.code, [ '(function () {', ' someCall();', ' if (foo) bar();', '}());' ].join(nl)); var map = result.map; var mapWithoutOptions = node.toStringWithSourceMap().map; assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator'); assert.ok(mapWithoutOptions instanceof SourceMapGenerator, 'mapWithoutOptions instanceof SourceMapGenerator'); assert.ok(!('file' in mapWithoutOptions)); 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()'] = forEachNewline(function (assert, util, nl) { var testCode = util.testGeneratedCode.replace(/\n/g, nl); var node = SourceNode.fromStringWithSourceMap( testCode, new SourceMapConsumer(util.testMap)); var result = node.toStringWithSourceMap({ file: 'min.js' }); var map = result.map; var code = result.code; assert.equal(code, testCode); 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'] = forEachNewline(function (assert, util, nl) { var node = SourceNode.fromStringWithSourceMap( util.testGeneratedCode.replace(/\n/g, nl), new SourceMapConsumer(util.emptyMap)); var result = node.toStringWithSourceMap({ file: 'min.js' }); var map = result.map; var code = result.code; assert.equal(code, util.testGeneratedCode.replace(/\n/g, nl)); 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'] = forEachNewline(function (assert, util, nl) { var input = new SourceNode(null, null, null, [ "(function() {" + nl, " var Test = {};" + nl, " ", new SourceNode(1, 0, "a.js", "Test.A = { value: 1234 };" + nl), " ", new SourceNode(2, 0, "a.js", "Test.A.x = 'xyz';"), nl, "}());" + nl, "/* 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 .fromStringWithSourceMap() third argument'] = function (assert, util) { // Assume the following directory structure: // // http://foo.org/ // bar.coffee // app/ // coffee/ // foo.coffee // coffeeBundle.js # Made from {foo,bar,baz}.coffee // maps/ // coffeeBundle.js.map // js/ // foo.js // public/ // app.js # Made from {foo,coffeeBundle}.js // app.js.map // // http://www.example.com/ // baz.coffee var coffeeBundle = new SourceNode(1, 0, 'foo.coffee', 'foo(coffee);\n'); coffeeBundle.setSourceContent('foo.coffee', 'foo coffee'); coffeeBundle.add(new SourceNode(2, 0, '/bar.coffee', 'bar(coffee);\n')); coffeeBundle.add(new SourceNode(3, 0, 'http://www.example.com/baz.coffee', 'baz(coffee);')); coffeeBundle = coffeeBundle.toStringWithSourceMap({ file: 'foo.js', sourceRoot: '..' }); var foo = new SourceNode(1, 0, 'foo.js', 'foo(js);'); var test = function(relativePath, expectedSources) { var app = new SourceNode(); app.add(SourceNode.fromStringWithSourceMap( coffeeBundle.code, new SourceMapConsumer(coffeeBundle.map.toString()), relativePath)); app.add(foo); var i = 0; app.walk(function (chunk, loc) { assert.equal(loc.source, expectedSources[i]); i++; }); app.walkSourceContents(function (sourceFile, sourceContent) { assert.equal(sourceFile, expectedSources[0]); assert.equal(sourceContent, 'foo coffee'); }) }; test('../coffee/maps', [ '../coffee/foo.coffee', '/bar.coffee', 'http://www.example.com/baz.coffee', 'foo.js' ]); // If the third parameter is omitted or set to the current working // directory we get incorrect source paths: test(undefined, [ '../foo.coffee', '/bar.coffee', 'http://www.example.com/baz.coffee', 'foo.js' ]); test('', [ '../foo.coffee', '/bar.coffee', 'http://www.example.com/baz.coffee', 'foo.js' ]); test('.', [ '../foo.coffee', '/bar.coffee', 'http://www.example.com/baz.coffee', 'foo.js' ]); test('./', [ '../foo.coffee', '/bar.coffee', 'http://www.example.com/baz.coffee', 'foo.js' ]); }; exports['test .toStringWithSourceMap() merging duplicate mappings'] = forEachNewline(function (assert, util, nl) { var input = new SourceNode(null, null, null, [ new SourceNode(1, 0, "a.js", "(function"), new SourceNode(1, 0, "a.js", "() {" + nl), " ", new SourceNode(1, 0, "a.js", "var Test = "), new SourceNode(1, 0, "b.js", "{};" + nl), 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", " };" + nl, "A"), "}());" + nl, "/* Generated Source */" ]); input = input.toStringWithSourceMap({ file: 'foo.js' }); assert.equal(input.code, [ "(function() {", " var Test = {};", "Test.A = { value: 1234 };", "}());", "/* Generated Source */" ].join(nl)) 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'] = forEachNewline(function (assert, util, nl) { var input = new SourceNode(null, null, null, [ new SourceNode(1, 0, "a.js", "(function() {" + nl + "var nextLine = 1;" + nl + "anotherLine();" + nl), new SourceNode(2, 2, "b.js", "Test.call(this, 123);" + nl), new SourceNode(2, 2, "b.js", "this['stuff'] = 'v';" + nl), new SourceNode(2, 2, "b.js", "anotherLine();" + nl), "/*" + nl + "Generated" + nl + "Source" + nl + "*/" + nl, new SourceNode(3, 4, "c.js", "anotherLine();" + nl), "/*" + nl + "Generated" + nl + "Source" + nl + "*/" ]); input = input.toStringWithSourceMap({ file: 'foo.js' }); assert.equal(input.code, [ "(function() {", "var nextLine = 1;", "anotherLine();", "Test.call(this, 123);", "this['stuff'] = 'v';", "anotherLine();", "/*", "Generated", "Source", "*/", "anotherLine();", "/*", "Generated", "Source", "*/" ].join(nl)); 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 .toStringWithSourceMap() with empty string'] = function (assert, util) { var node = new SourceNode(1, 0, 'empty.js', ''); var result = node.toStringWithSourceMap(); assert.equal(result.code, ''); }; exports['test .toStringWithSourceMap() with consecutive newlines'] = forEachNewline(function (assert, util, nl) { var input = new SourceNode(null, null, null, [ "/***/" + nl + nl, new SourceNode(1, 0, "a.js", "'use strict';" + nl), new SourceNode(2, 0, "a.js", "a();"), ]); input = input.toStringWithSourceMap({ file: 'foo.js' }); assert.equal(input.code, [ "/***/", "", "'use strict';", "a();", ].join(nl)); var correctMap = new SourceMapGenerator({ file: 'foo.js' }); correctMap.addMapping({ generated: { line: 3, column: 0 }, source: 'a.js', original: { line: 1, column: 0 } }); correctMap.addMapping({ generated: { line: 4, column: 0 }, source: 'a.js', original: { line: 2, column: 0 } }); 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'); }; });