├── .travis.yml ├── .gitignore ├── lib ├── path-is-absolute.js ├── mappings-from-map.js └── path-is-absolute.license ├── package.json ├── LICENSE ├── example ├── two-files-short.js └── two-files.js ├── README.md ├── index.js └── test └── combine-source-map.js /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "0.10" 5 | - "0.12" 6 | - "iojs-v2.4" 7 | before_install: 8 | - npm install --global npm 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | node_modules 15 | npm-debug.log 16 | tmp 17 | -------------------------------------------------------------------------------- /lib/path-is-absolute.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function posix(path) { 4 | return path.charAt(0) === '/'; 5 | }; 6 | 7 | function win32(path) { 8 | // https://github.com/joyent/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56 9 | var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; 10 | var result = splitDeviceRe.exec(path); 11 | var device = result[1] || ''; 12 | var isUnc = !!device && device.charAt(1) !== ':'; 13 | 14 | // UNC paths are always absolute 15 | return !!result[2] || isUnc; 16 | }; 17 | 18 | module.exports = process.platform === 'win32' ? win32 : posix; 19 | module.exports.posix = posix; 20 | module.exports.win32 = win32; -------------------------------------------------------------------------------- /lib/mappings-from-map.js: -------------------------------------------------------------------------------- 1 | var SMConsumer = require('source-map').SourceMapConsumer; 2 | 3 | /** 4 | * @name mappingsFromMap 5 | * @function 6 | * @param map {Object} the JSON.parse()'ed map 7 | * @return {Array} array of mappings 8 | */ 9 | module.exports = function (map) { 10 | var consumer = new SMConsumer(map); 11 | var mappings = []; 12 | 13 | consumer.eachMapping(function (mapping) { 14 | // only set source if we have original position to handle edgecase (see inline-source-map tests) 15 | mappings.push({ 16 | original: mapping.originalColumn != null ? { 17 | column: mapping.originalColumn 18 | , line: mapping.originalLine 19 | } : undefined 20 | , generated: { 21 | column: mapping.generatedColumn 22 | , line: mapping.generatedLine 23 | } 24 | , source: mapping.originalColumn != null ? mapping.source : undefined 25 | , name: mapping.name 26 | }); 27 | }); 28 | 29 | return mappings; 30 | } 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "combine-source-map", 3 | "version": "0.8.0", 4 | "description": "Add source maps of multiple files, offset them and then combine them into one source map", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "tap test/*.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/thlorenz/combine-source-map.git" 12 | }, 13 | "homepage": "https://github.com/thlorenz/combine-source-map", 14 | "dependencies": { 15 | "convert-source-map": "~1.1.0", 16 | "inline-source-map": "~0.6.0", 17 | "lodash.memoize": "~3.0.3", 18 | "source-map": "~0.5.3" 19 | }, 20 | "devDependencies": { 21 | "tap": "~0.4.3" 22 | }, 23 | "keywords": [ 24 | "source", 25 | "map", 26 | "sourcemap", 27 | "bundle", 28 | "combine", 29 | "cat", 30 | "sourceMappingUrl", 31 | "browserify" 32 | ], 33 | "author": { 34 | "name": "Thorsten Lorenz", 35 | "email": "thlorenz@gmx.de", 36 | "url": "http://thlorenz.com" 37 | }, 38 | "license": "MIT", 39 | "engine": { 40 | "node": ">=0.6" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Thorsten Lorenz. 2 | All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person 5 | obtaining a copy of this software and associated documentation 6 | files (the "Software"), to deal in the Software without 7 | restriction, including without limitation the rights to use, 8 | copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /lib/path-is-absolute.license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Sindre Sorhus (sindresorhus.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /example/two-files-short.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var convert = require('convert-source-map'); 4 | var combine = require('..'); 5 | 6 | var fooComment = '//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9vLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZm9vLmNvZmZlZSJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Q0FBQTtDQUFBLENBQUEsQ0FBQSxJQUFPLEdBQUs7Q0FBWiIsInNvdXJjZXNDb250ZW50IjpbImNvbnNvbGUubG9nKHJlcXVpcmUgJy4vYmFyLmpzJylcbiJdfQ=='; 7 | var barComment = '//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiYmFyLmNvZmZlZSJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Q0FBQTtDQUFBLENBQUEsQ0FBQSxJQUFPLEdBQUs7Q0FBWiIsInNvdXJjZXNDb250ZW50IjpbImNvbnNvbGUubG9nKGFsZXJ0ICdhbGVydHMgc3VjaycpXG4iXX0='; 8 | 9 | var fooFile = { 10 | source: '(function() {\n\n console.log(require(\'./bar.js\'));\n\n}).call(this);\n' + '\n' + fooComment 11 | , sourceFile: 'foo.js' 12 | }; 13 | var barFile = { 14 | source: '(function() {\n\n console.log(alert(\'alerts suck\'));\n\n}).call(this);\n' + '\n' + barComment 15 | , sourceFile: 'bar.js' 16 | }; 17 | 18 | var offset = { line: 2 }; 19 | var base64 = combine 20 | .create('bundle.js') 21 | .addFile(fooFile, offset) 22 | .addFile(barFile, { line: offset.line + 8 }) 23 | .base64(); 24 | 25 | var sm = convert.fromBase64(base64).toObject(); 26 | console.log(sm); 27 | -------------------------------------------------------------------------------- /example/two-files.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var convert = require('convert-source-map'); 4 | var combine = require('..'); 5 | 6 | var foo = { 7 | version : 3, 8 | file : 'foo.js', 9 | sourceRoot : '', 10 | sources : [ 'foo.coffee' ], 11 | names : [], 12 | mappings : ';AAAA;CAAA;CAAA,CAAA,CAAA,IAAO,GAAK;CAAZ', 13 | sourcesContent : [ 'console.log(require \'./bar.js\')\n' ] }; 14 | 15 | var bar = { 16 | version : 3, 17 | file : 'bar.js', 18 | sourceRoot : '', 19 | sources : [ 'bar.coffee' ], 20 | names : [], 21 | mappings : ';AAAA;CAAA;CAAA,CAAA,CAAA,IAAO,GAAK;CAAZ', 22 | sourcesContent : [ 'console.log(alert \'alerts suck\')\n' ] }; 23 | 24 | 25 | var fooComment = convert.fromObject(foo).toComment(); 26 | var barComment = convert.fromObject(bar).toComment(); 27 | 28 | var fooFile = { 29 | source: '(function() {\n\n console.log(require(\'./bar.js\'));\n\n}).call(this);\n' + '\n' + fooComment 30 | , sourceFile: 'foo.js' 31 | }; 32 | var barFile = { 33 | source: '(function() {\n\n console.log(alert(\'alerts suck\'));\n\n}).call(this);\n' + '\n' + barComment 34 | , sourceFile: 'bar.js' 35 | }; 36 | 37 | var offset = { line: 2 }; 38 | var base64 = combine 39 | .create('bundle.js') 40 | .addFile(fooFile, offset) 41 | .addFile(barFile, { line: offset.line + 8 }) 42 | .base64(); 43 | 44 | var sm = convert.fromBase64(base64).toObject(); 45 | console.log('Combined source maps:\n', sm); 46 | console.log('\nMappings:\n', sm.mappings); 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # combine-source-map [![build status](https://secure.travis-ci.org/thlorenz/combine-source-map.png)](http://travis-ci.org/thlorenz/combine-source-map) 2 | 3 | Add source maps of multiple files, offset them and then combine them into one source map. 4 | 5 | ```js 6 | var convert = require('convert-source-map'); 7 | var combine = require('combine-source-map'); 8 | 9 | var fooComment = '//# sourceMappingURL=data:application/json;base64,eyJ2Z [..] pzJylcbiJdfQ=='; 10 | var barComment = '//# sourceMappingURL=data:application/json;base64,eyJ2Z [..] VjaycpXG4iXX0='; 11 | 12 | var fooFile = { 13 | source: '(function() {\n\n console.log(require(\'./bar.js\'));\n\n}).call(this);\n' + '\n' + fooComment 14 | , sourceFile: 'foo.js' 15 | }; 16 | var barFile = { 17 | source: '(function() {\n\n console.log(alert(\'alerts suck\'));\n\n}).call(this);\n' + '\n' + barComment 18 | , sourceFile: 'bar.js' 19 | }; 20 | 21 | var offset = { line: 2 }; 22 | var base64 = combine 23 | .create('bundle.js') 24 | .addFile(fooFile, offset) 25 | .addFile(barFile, { line: offset.line + 8 }) 26 | .base64(); 27 | 28 | var sm = convert.fromBase64(base64).toObject(); 29 | console.log(sm); 30 | ``` 31 | 32 | ``` 33 | { version: 3, 34 | file: 'bundle.js', 35 | sources: [ 'foo.coffee', 'bar.coffee' ], 36 | names: [], 37 | mappings: ';;;AAAA;CAAA;CAAA,CAAA,CAAA,IAAO,GAAK;CAAZ;;;;;ACAA;CAAA;CAAA,CAAA,CAAA,IAAO,GAAK;CAAZ', 38 | sourcesContent: 39 | [ 'console.log(require \'./bar.js\')\n', 40 | 'console.log(alert \'alerts suck\')\n' ] } 41 | ``` 42 | 43 | ## Installation 44 | 45 | npm install combine-source-map 46 | 47 | ## API 48 | 49 | ### create() 50 | 51 | ``` 52 | /** 53 | * @name create 54 | * @function 55 | * @param file {String} optional name of the generated file 56 | * @param sourceRoot { String} optional sourceRoot of the map to be generated 57 | * @return {Object} Combiner instance to which source maps can be added and later combined 58 | */ 59 | ``` 60 | 61 | ### Combiner.prototype.addFile(opts, offset) 62 | 63 | ``` 64 | /** 65 | * Adds map to underlying source map. 66 | * If source contains a source map comment that has the source of the original file inlined it will offset these 67 | * mappings and include them. 68 | * If no source map comment is found or it has no source inlined, mappings for the file will be generated and included 69 | * 70 | * @name addMap 71 | * @function 72 | * @param opts {Object} { sourceFile: {String}, source: {String} } 73 | * @param offset {Object} { line: {Number}, column: {Number} } 74 | */ 75 | ``` 76 | 77 | ### Combiner.prototype.base64() 78 | 79 | ``` 80 | /** 81 | * @name base64 82 | * @function 83 | * @return {String} base64 encoded combined source map 84 | */ 85 | ``` 86 | 87 | ### Combiner.prototype.comment() 88 | 89 | ``` 90 | /** 91 | * @name comment 92 | * @function 93 | * @return {String} base64 encoded sourceMappingUrl comment of the combined source map 94 | */ 95 | ``` 96 | 97 | ### removeComments(src) 98 | 99 | ``` 100 | /** 101 | * @name removeComments 102 | * @function 103 | * @param src 104 | * @return {String} src with all sourceMappingUrl comments removed 105 | */ 106 | ``` 107 | 108 | ## Example 109 | 110 | Read and run the [more elaborate example](https://github.com/thlorenz/combine-source-map/blob/master/example/two-files.js) 111 | in order to get a better idea how things work. 112 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var convert = require('convert-source-map'); 5 | var memoize = require('lodash.memoize'); 6 | var createGenerator = require('inline-source-map'); 7 | var pathIsAbsolute = require('./lib/path-is-absolute'); 8 | var mappingsFromMap = require('./lib/mappings-from-map'); 9 | 10 | var protocolRx = /^[a-z]+:\/\//; 11 | 12 | /** 13 | * Rebases a relative path in 'sourceFile' to be relative 14 | * to the path where 'sourceFile' is located. 15 | * 16 | * This is necessary before adding relative paths to the 17 | * new combined map to ensure all paths are relative to their 18 | * original source. 19 | * 20 | * The 'sourceRoot' from the original source map is joined 21 | * as well to ensure the complete path. 22 | * 23 | * Resulting paths that are absolute are passed along directly. 24 | * 25 | * @param sourceFile {String} path to the original source file that references a map 26 | * @param relativeRoot {String} sourceRoot in sourceFile's map to combine with relativePath 27 | * @param relativePath {String} source path from sourceFile's map 28 | */ 29 | var rebaseRelativePath = memoize(function(sourceFile, relativeRoot, relativePath) { 30 | if (!relativePath) { 31 | return relativePath; 32 | } 33 | 34 | // join relative path to root (e.g. 'src/' + 'file.js') 35 | var relativeRootedPath = relativeRoot ? path.join(relativeRoot, relativePath) : relativePath; 36 | relativeRootedPath = relativeRootedPath.replace(/\\/g, '/'); 37 | sourceFile = sourceFile.replace(/\\/g, '/'); 38 | 39 | if (sourceFile === relativeRootedPath || // same path, 40 | pathIsAbsolute(relativeRootedPath) || // absolute path, nor 41 | protocolRx.test(relativeRootedPath)) { // absolute protocol need rebasing 42 | return relativeRootedPath; 43 | } 44 | 45 | // make relative to source file 46 | return path.join(path.dirname(sourceFile), relativeRootedPath).replace(/\\/g, '/'); 47 | }, function(a, b, c) { 48 | return a + '::' + b + '::' + c; 49 | }); 50 | 51 | function resolveMap(source) { 52 | var gen = convert.fromSource(source); 53 | return gen ? gen.toObject() : null; 54 | } 55 | 56 | function hasInlinedSource(existingMap) { 57 | return existingMap.sourcesContent && !!existingMap.sourcesContent[0]; 58 | } 59 | 60 | function Combiner(file, sourceRoot) { 61 | // since we include the original code in the map sourceRoot actually not needed 62 | this.generator = createGenerator({ file: file || 'generated.js', sourceRoot: sourceRoot }); 63 | } 64 | 65 | Combiner.prototype._addGeneratedMap = function (sourceFile, source, offset) { 66 | this.generator.addGeneratedMappings(sourceFile, source, offset); 67 | this.generator.addSourceContent(sourceFile, source); 68 | return this; 69 | }; 70 | 71 | Combiner.prototype._addExistingMap = function (sourceFile, source, existingMap, offset) { 72 | var mappings = mappingsFromMap(existingMap); 73 | 74 | // add all of the sources from the map 75 | for (var i = 0, len = existingMap.sources.length; i < len; i++) { 76 | if (!existingMap.sourcesContent) continue; 77 | 78 | this.generator.addSourceContent( 79 | rebaseRelativePath(sourceFile, existingMap.sourceRoot, existingMap.sources[i]), 80 | existingMap.sourcesContent[i]); 81 | } 82 | 83 | // add the mappings, preserving the original mapping 'source' 84 | mappings.forEach(function(mapping) { 85 | // Add the mappings one at a time because 'inline-source-map' doesn't handle 86 | // mapping source filenames. The mapping.source already takes sourceRoot into account 87 | // per the SMConsumer.eachMapping function, so pass null for the root here. 88 | this.generator.addMappings( 89 | rebaseRelativePath(sourceFile, null, mapping.source), [mapping], offset); 90 | }, this); 91 | 92 | return this; 93 | }; 94 | 95 | /** 96 | * Adds map to underlying source map. 97 | * If source contains a source map comment that has the source of the original file inlined it will offset these 98 | * mappings and include them. 99 | * If no source map comment is found or it has no source inlined, mappings for the file will be generated and included 100 | * 101 | * @name addMap 102 | * @function 103 | * @param opts {Object} { sourceFile: {String}, source: {String} } 104 | * @param offset {Object} { line: {Number}, column: {Number} } 105 | */ 106 | Combiner.prototype.addFile = function (opts, offset) { 107 | 108 | offset = offset || {}; 109 | if (!offset.hasOwnProperty('line')) offset.line = 0; 110 | if (!offset.hasOwnProperty('column')) offset.column = 0; 111 | 112 | var existingMap = resolveMap(opts.source); 113 | 114 | return existingMap && hasInlinedSource(existingMap) 115 | ? this._addExistingMap(opts.sourceFile, opts.source, existingMap, offset) 116 | : this._addGeneratedMap(opts.sourceFile, opts.source, offset); 117 | }; 118 | 119 | /** 120 | * @name base64 121 | * @function 122 | * @return {String} base64 encoded combined source map 123 | */ 124 | Combiner.prototype.base64 = function () { 125 | return this.generator.base64Encode(); 126 | }; 127 | 128 | /** 129 | * @name comment 130 | * @function 131 | * @return {String} base64 encoded sourceMappingUrl comment of the combined source map 132 | */ 133 | Combiner.prototype.comment = function () { 134 | return this.generator.inlineMappingUrl(); 135 | }; 136 | 137 | /** 138 | * @name create 139 | * @function 140 | * @param file {String} optional name of the generated file 141 | * @param sourceRoot {String} optional sourceRoot of the map to be generated 142 | * @return {Object} Combiner instance to which source maps can be added and later combined 143 | */ 144 | exports.create = function (file, sourceRoot) { return new Combiner(file, sourceRoot); }; 145 | 146 | /** 147 | * @name removeComments 148 | * @function 149 | * @param src 150 | * @return {String} src with all sourceMappingUrl comments removed 151 | */ 152 | exports.removeComments = function (src) { 153 | if (!src.replace) return src; 154 | return src.replace(convert.commentRegex, '').replace(convert.mapFileCommentRegex, ''); 155 | }; 156 | -------------------------------------------------------------------------------- /test/combine-source-map.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /*jshint asi: true */ 3 | 4 | var test = require('tap').test; 5 | var convert = require('convert-source-map'); 6 | var commentRegex = require('convert-source-map').commentRegex; 7 | var combine = require('..'); 8 | var mappingsFromMap = require('../lib/mappings-from-map'); 9 | 10 | function checkMappings(foo, sm, lineOffset) { 11 | function inspect(obj, depth) { 12 | return require('util').inspect(obj, false, depth || 5, true); 13 | } 14 | 15 | var fooMappings = mappingsFromMap(foo); 16 | var mappings = mappingsFromMap(sm); 17 | 18 | var genLinesOffset = true; 19 | var origLinesSame = true; 20 | for (var i = 0; i < mappings.length; i++) { 21 | var fooGen = fooMappings[i].generated; 22 | var fooOrig = fooMappings[i].original; 23 | var gen = mappings[i].generated 24 | var orig = mappings[i].original; 25 | 26 | if (gen.column !== fooGen.column || gen.line !== (fooGen.line + lineOffset)) { 27 | console.error( 28 | 'generated mapping at %s not offset properly:\ninput: [%s]\noutput:[%s]\n\n', 29 | i , 30 | inspect(fooGen), 31 | inspect(gen) 32 | ); 33 | genLinesOffset = false; 34 | } 35 | 36 | if (orig.column !== fooOrig.column || orig.line !== fooOrig.line) { 37 | console.error( 38 | 'original mapping at %s is not the same as the genrated mapping:\ninput: [%s]\noutput:[%s]\n\n', 39 | i , 40 | inspect(fooOrig), 41 | inspect(orig) 42 | ); 43 | origLinesSame = false; 44 | } 45 | } 46 | return { genLinesOffset: genLinesOffset, origLinesSame: origLinesSame }; 47 | } 48 | 49 | var foo = { 50 | version : 3, 51 | file : 'foo.js', 52 | sourceRoot : '', 53 | sources : [ 'foo.coffee' ], 54 | names : [], 55 | mappings : ';AAAA;CAAA;CAAA,CAAA,CAAA,IAAO,GAAK;CAAZ', 56 | sourcesContent : [ 'console.log(require \'./bar.js\')\n' ] }; 57 | 58 | test('add one file with inlined source', function (t) { 59 | 60 | var mapComment = convert.fromObject(foo).toComment(); 61 | var file = { 62 | id: 'xyz' 63 | , source: '(function() {\n\n console.log(require(\'./bar.js\'));\n\n}).call(this);\n' + '\n' + mapComment 64 | , sourceFile: 'foo.js' 65 | }; 66 | 67 | var lineOffset = 3 68 | var base64 = combine.create() 69 | .addFile(file, { line: lineOffset }) 70 | .base64() 71 | 72 | var sm = convert.fromBase64(base64).toObject(); 73 | var res = checkMappings(foo, sm, lineOffset); 74 | 75 | t.ok(res.genLinesOffset, 'all generated lines are offset properly and columns unchanged') 76 | t.ok(res.origLinesSame, 'all original lines and columns are unchanged') 77 | t.deepEqual(sm.sourcesContent, foo.sourcesContent, 'includes the original source') 78 | t.deepEqual(sm.sources, ['foo.coffee'], 'includes original filename') 79 | t.end() 80 | }); 81 | 82 | 83 | test('add one file without inlined source', function (t) { 84 | 85 | var mapComment = convert 86 | .fromObject(foo) 87 | .setProperty('sourcesContent', []) 88 | .toComment(); 89 | 90 | var file = { 91 | id: 'xyz' 92 | , source: '(function() {\n\n console.log(require(\'./bar.js\'));\n\n}).call(this);\n' + '\n' + mapComment 93 | , sourceFile: 'foo.js' 94 | }; 95 | 96 | var lineOffset = 3 97 | var base64 = combine.create() 98 | .addFile(file, { line: lineOffset }) 99 | .base64() 100 | 101 | var sm = convert.fromBase64(base64).toObject(); 102 | var mappings = mappingsFromMap(sm); 103 | 104 | t.deepEqual(sm.sourcesContent, [file.source], 'includes the generated source') 105 | t.deepEqual(sm.sources, ['foo.js'], 'includes generated filename') 106 | 107 | t.deepEqual( 108 | mappings 109 | , [ { generated: { line: 4, column: 0 }, 110 | original: { line: 1, column: 0 }, 111 | source: 'foo.js', name: null }, 112 | { generated: { line: 5, column: 0 }, 113 | original: { line: 2, column: 0 }, 114 | source: 'foo.js', name: null }, 115 | { generated: { line: 6, column: 0 }, 116 | original: { line: 3, column: 0 }, 117 | source: 'foo.js', name: null }, 118 | { generated: { line: 7, column: 0 }, 119 | original: { line: 4, column: 0 }, 120 | source: 'foo.js', name: null }, 121 | { generated: { line: 8, column: 0 }, 122 | original: { line: 5, column: 0 }, 123 | source: 'foo.js', name: null }, 124 | { generated: { line: 9, column: 0 }, 125 | original: { line: 6, column: 0 }, 126 | source: 'foo.js', name: null }, 127 | { generated: { line: 10, column: 0 }, 128 | original: { line: 7, column: 0 }, 129 | source: 'foo.js', name: null } ] 130 | , 'generates mappings offset by the given line' 131 | ) 132 | t.end() 133 | }) 134 | 135 | test('add one file with inlined sources from multiple files', function(t) { 136 | var gen1Map = { 137 | version: 3, 138 | sources: [ 'one.js', 'two.js' ], 139 | names: [], 140 | mappings: 'AAAA;ACAA', 141 | sourcesContent: [ 'console.log(1);', 'console.log(2);' ] 142 | }; 143 | 144 | var gen2Map = { 145 | version: 3, 146 | sources: [ 'three.js', 'four.js' ], 147 | names: [], 148 | mappings: 'AAAA;ACAA', 149 | sourcesContent: [ 'console.log(3);', 'console.log(4);' ] 150 | }; 151 | 152 | var base64 = combine.create() 153 | .addFile({ 154 | source: 'console.log(1);\nconsole.log(2);\n' + convert.fromObject(gen1Map).toComment(), 155 | sourceFile: 'gen1.js' 156 | }) 157 | .addFile({ 158 | source: 'console.log(3);\nconsole.log(4);\n' + convert.fromObject(gen2Map).toComment(), 159 | sourceFile: 'gen2.js' 160 | }, {line: 2}) 161 | .base64() 162 | 163 | var sm = convert.fromBase64(base64).toObject(); 164 | 165 | 166 | t.deepEqual(sm.sources, ['one.js', 'two.js', 'three.js', 'four.js'], 'include the correct source'); 167 | 168 | t.deepEqual(sm.sourcesContent, [ 169 | 'console.log(1);', 170 | 'console.log(2);', 171 | 'console.log(3);', 172 | 'console.log(4);' 173 | ], 'include the correct source file content'); 174 | 175 | t.deepEqual( 176 | mappingsFromMap(sm) 177 | , [ { original: { column: 0, line: 1 }, 178 | generated: { column: 0, line: 1 }, 179 | source: 'one.js', 180 | name: null }, 181 | { original: { column: 0, line: 1 }, 182 | generated: { column: 0, line: 2 }, 183 | source: 'two.js', 184 | name: null }, 185 | { original: { column: 0, line: 1 }, 186 | generated: { column: 0, line: 3 }, 187 | source: 'three.js', 188 | name: null }, 189 | { original: { column: 0, line: 1 }, 190 | generated: { column: 0, line: 4 }, 191 | source: 'four.js', 192 | name: null } ], 'should properly map multiple files'); 193 | t.end() 194 | }); 195 | 196 | test('relative path from multiple files', function(t) { 197 | // Folder structure as follows: 198 | // 199 | // project 200 | // +- src 201 | // +- package1 202 | // +- sub 203 | // -- one.js 204 | // -- two.js 205 | // +- package2 206 | // +- sub 207 | // -- three.js 208 | // -- four.js 209 | // +- gen 210 | // +- gen1.js 211 | // +- gen2.js 212 | // -- combined.js 213 | // 214 | // Where 'one.js', 'two.js' were combined to 'gen1.js' 215 | // and 'three.js', 'four.js' were combined to 'gen2.js'. 216 | // Now 'gen1.js' and 'gen2.js' are being combined from 217 | // the project root folder. 218 | var gen1Map = { 219 | version: 3, 220 | sources: [ 'sub/one.js', 'sub/two.js' ], 221 | names: [], 222 | mappings: 'AAAA;ACAA', 223 | sourcesContent: [ 'console.log(1);', 'console.log(2);' ], 224 | sourceRoot: '../src/package1' 225 | }; 226 | 227 | var gen2Map = { 228 | version: 3, 229 | sources: [ 'sub/three.js', 'sub/four.js' ], 230 | names: [], 231 | mappings: 'AAAA;ACAA', 232 | sourcesContent: [ 'console.log(3);', 'console.log(4);' ], 233 | sourceRoot: '../src/package2' 234 | }; 235 | 236 | var base64 = combine.create() 237 | .addFile({ 238 | source: 'console.log(1);\nconsole.log(2);\n' + convert.fromObject(gen1Map).toComment(), 239 | sourceFile: 'gen/gen1.js' 240 | }) 241 | .addFile({ 242 | source: 'console.log(3);\nconsole.log(4);\n' + convert.fromObject(gen2Map).toComment(), 243 | sourceFile: 'gen/gen2.js' 244 | }, {line: 2}) 245 | .base64() 246 | 247 | var sm = convert.fromBase64(base64).toObject(); 248 | 249 | t.deepEqual(sm.sources, ['src/package1/sub/one.js', 'src/package1/sub/two.js', 250 | 'src/package2/sub/three.js', 'src/package2/sub/four.js'], 251 | 'include the correct source'); 252 | 253 | t.deepEqual(sm.sourcesContent, [ 254 | 'console.log(1);', 255 | 'console.log(2);', 256 | 'console.log(3);', 257 | 'console.log(4);' 258 | ], 'include the correct source file content'); 259 | 260 | t.deepEqual( 261 | mappingsFromMap(sm) 262 | , [ { original: { column: 0, line: 1 }, 263 | generated: { column: 0, line: 1 }, 264 | source: 'src/package1/sub/one.js', 265 | name: null }, 266 | { original: { column: 0, line: 1 }, 267 | generated: { column: 0, line: 2 }, 268 | source: 'src/package1/sub/two.js', 269 | name: null }, 270 | { original: { column: 0, line: 1 }, 271 | generated: { column: 0, line: 3 }, 272 | source: 'src/package2/sub/three.js', 273 | name: null }, 274 | { original: { column: 0, line: 1 }, 275 | generated: { column: 0, line: 4 }, 276 | source: 'src/package2/sub/four.js', 277 | name: null } ], 'should properly map multiple files'); 278 | t.end() 279 | }); 280 | 281 | test('relative path when source and file name are the same', function(t) { 282 | var gen1Map = { 283 | version: 3, 284 | sources: [ 'a/b/one.js' ], 285 | names: [], 286 | mappings: 'AAAA', 287 | file: 'a/b/one.js', 288 | sourcesContent: [ 'console.log(1);\n' ] 289 | }; 290 | 291 | var gen2Map = { 292 | version: 3, 293 | sources: [ 'a/b/two.js' ], 294 | names: [], 295 | mappings: 'AAAA', 296 | file: 'a/b/two.js', 297 | sourcesContent: [ 'console.log(2);\n' ] 298 | }; 299 | 300 | var base64 = combine.create() 301 | .addFile({ 302 | source: 'console.log(1);\n' + convert.fromObject(gen1Map).toComment(), 303 | sourceFile: 'a/b/one.js' 304 | }) 305 | .addFile({ 306 | source: 'console.log(2);\n' + convert.fromObject(gen2Map).toComment(), 307 | sourceFile: 'a/b/two.js' 308 | }, {line: 1}) 309 | .base64() 310 | 311 | var sm = convert.fromBase64(base64).toObject(); 312 | 313 | t.deepEqual(sm.sources, ['a/b/one.js', 'a/b/two.js'], 314 | 'include the correct source'); 315 | 316 | t.deepEqual( 317 | mappingsFromMap(sm) 318 | , [ { original: { column: 0, line: 1 }, 319 | generated: { column: 0, line: 1 }, 320 | source: 'a/b/one.js', 321 | name: null }, 322 | { original: { column: 0, line: 1 }, 323 | generated: { column: 0, line: 2 }, 324 | source: 'a/b/two.js', 325 | name: null } ], 'should properly map multiple files'); 326 | t.end() 327 | }); 328 | 329 | test('remove comments', function (t) { 330 | var mapComment = convert.fromObject(foo).toComment(); 331 | 332 | function sourcemapComments(src) { 333 | var matches = src.match(commentRegex); 334 | return matches ? matches.length : 0; 335 | } 336 | 337 | t.equal(sourcemapComments('var a = 1;\n' + mapComment), 1); 338 | 339 | [ '' 340 | , 'var a = 1;\n' + mapComment 341 | , 'var a = 1;\n' + mapComment + '\nvar b = 5;\n' + mapComment 342 | ] .forEach(function (x) { 343 | var removed = combine.removeComments(x) 344 | t.equal(sourcemapComments(removed), 0) 345 | }) 346 | t.end() 347 | }) 348 | --------------------------------------------------------------------------------