diff --git a/README.md b/README.md index 76ceb34..4da3c34 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Source.prototype.sourceAndMap(options: Object) -> { } ``` -Returns both, source code (like `Source.prototype.source()` and SourceMap (like `Source.prototype.map()`). This method could have better performance than calling `source()` and `map()` separatly. +Returns both, source code (like `Source.prototype.source()` and SourceMap (like `Source.prototype.map()`). This method could have better performance than calling `source()` and `map()` separately. See `map()` for `options`. diff --git a/lib/OriginalSource.js b/lib/OriginalSource.js index 11aff07..f3b5227 100644 --- a/lib/OriginalSource.js +++ b/lib/OriginalSource.js @@ -41,7 +41,10 @@ class OriginalSource extends Source { } return new SourceNode(null, null, null, _splitCode(line + (idx != lines.length - 1 ? "\n" : "")).map(function(item) { - if(/^\s*$/.test(item)) return item; + if(/^\s*$/.test(item)) { + pos += item.length; + return item; + } var res = new SourceNode(idx + 1, pos, name, item); pos += item.length; return res; diff --git a/lib/ReplaceSource.js b/lib/ReplaceSource.js index a4b9ad8..f2f781f 100644 --- a/lib/ReplaceSource.js +++ b/lib/ReplaceSource.js @@ -10,24 +10,35 @@ var SourceListMap = require("source-list-map").SourceListMap; var fromStringWithSourceMap = require("source-list-map").fromStringWithSourceMap; var SourceMapConsumer = require("source-map").SourceMapConsumer; +class Replacement { + constructor(start, end, content, insertIndex, name) { + this.start = start; + this.end = end; + this.content = content; + this.insertIndex = insertIndex; + this.name = name; + } +} + class ReplaceSource extends Source { constructor(source, name) { super(); this._source = source; this._name = name; + /** @type {Replacement[]} */ this.replacements = []; } - replace(start, end, newValue) { + replace(start, end, newValue, name) { if(typeof newValue !== "string") throw new Error("insertion must be a string, but is a " + typeof newValue); - this.replacements.push([start, end, newValue, this.replacements.length]); + this.replacements.push(new Replacement(start, end, newValue, this.replacements.length, name)); } - insert(pos, newValue) { + insert(pos, newValue, name) { if(typeof newValue !== "string") throw new Error("insertion must be a string, but is a " + typeof newValue + ": " + newValue); - this.replacements.push([pos, pos - 1, newValue, this.replacements.length]); + this.replacements.push(new Replacement(pos, pos - 1, newValue, this.replacements.length, name)); } source(options) { @@ -40,13 +51,13 @@ class ReplaceSource extends Source { _sortReplacements() { this.replacements.sort(function(a, b) { - var diff = b[1] - a[1]; + var diff = b.end - a.end; if(diff !== 0) return diff; - diff = b[0] - a[0]; + diff = b.start - a.start; if(diff !== 0) return diff; - return b[3] - a[3]; + return b.insertIndex - a.insertIndex; }); } @@ -57,9 +68,9 @@ class ReplaceSource extends Source { var result = [str]; this.replacements.forEach(function(repl) { var remSource = result.pop(); - var splitted1 = this._splitString(remSource, Math.floor(repl[1] + 1)); - var splitted2 = this._splitString(splitted1[0], Math.floor(repl[0])); - result.push(splitted1[1], repl[2], splitted2[0]); + var splitted1 = this._splitString(remSource, Math.floor(repl.end + 1)); + var splitted2 = this._splitString(splitted1[0], Math.floor(repl.start)); + result.push(splitted1[1], repl.content, splitted2[0]); }, this); // write out result array in reverse order @@ -146,18 +157,18 @@ class ReplaceSource extends Source { removeChars = 0; } var finalStr = ""; - while(idxReplacement >= 0 && replacements[idxReplacement][0] < newCurrentIndex) { + while(idxReplacement >= 0 && replacements[idxReplacement].start < newCurrentIndex) { var repl = replacements[idxReplacement]; - var start = Math.floor(repl[0]); - var end = Math.floor(repl[1] + 1); + var start = Math.floor(repl.start); + var end = Math.floor(repl.end + 1); var before = str.substr(0, Math.max(0, start - currentIndex)); if(end <= newCurrentIndex) { var after = str.substr(Math.max(0, end - currentIndex)); - finalStr += before + repl[2]; + finalStr += before + repl.content; str = after; currentIndex = Math.max(currentIndex, end); } else { - finalStr += before + repl[2]; + finalStr += before + repl.content; str = ""; removeChars = end - newCurrentIndex; } @@ -170,7 +181,7 @@ class ReplaceSource extends Source { }); var extraCode = ""; while(idxReplacement >= 0) { - extraCode += replacements[idxReplacement][2]; + extraCode += replacements[idxReplacement].content; idxReplacement--; } if(extraCode) { @@ -246,7 +257,8 @@ class ReplaceSource extends Source { mapping.line, mapping.column, mapping.source, - replace.value + replace.value, + mapping.name || replace.name )); } } @@ -259,6 +271,9 @@ class ReplaceSource extends Source { } class ReplacementEnumerator { + /** + * @param {Replacement[]} replacements list of replacements + */ constructor(replacements) { this.replacements = replacements || []; this.index = this.replacements.length; @@ -274,9 +289,10 @@ class ReplacementEnumerator { if(this.emit) { // Start point found. stop emitting. set position to find end var repl = this.replacements[this.index]; - var end = Math.floor(repl[1] + 1); + var end = Math.floor(repl.end + 1); this.position = end; - this.value = repl[2]; + this.value = repl.content; + this.name = repl.name; } else { // End point found. start emitting. set position to find next start this.index--; @@ -284,7 +300,7 @@ class ReplacementEnumerator { this.done = true; } else { var nextRepl = this.replacements[this.index]; - var start = Math.floor(nextRepl[0]); + var start = Math.floor(nextRepl.start); this.position = start; } } @@ -303,7 +319,9 @@ class ReplacementEnumerator { var resultStr = ""; for(var i = this.index; i >= 0; i--) { var repl = this.replacements[i]; - resultStr += repl[2]; + // this doesn't need to handle repl.name, because in SourceMaps generated code + // without pointer to original source can't have a name + resultStr += repl.content; } return resultStr; } diff --git a/package.json b/package.json index 56f8a83..d5d3775 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "istanbul": "^0.4.1", "js-beautify": "^1.5.10", "mocha": "^3.4.2", - "should": "^11.2.1" + "should": "^11.2.1", + "sourcemap-validator": "^1.1.0" }, "files": [ "lib/" diff --git a/test/ReplaceSource.js b/test/ReplaceSource.js index d11f14f..0510337 100644 --- a/test/ReplaceSource.js +++ b/test/ReplaceSource.js @@ -2,6 +2,7 @@ var should = require("should"); var ReplaceSource = require("../lib/ReplaceSource"); var RawSource = require("../lib/RawSource"); var OriginalSource = require("../lib/OriginalSource"); +var validate = require('sourcemap-validator'); describe("ReplaceSource", function() { it("should replace correctly", function() { @@ -154,4 +155,23 @@ describe("ReplaceSource", function() { resultMap.map.mappings.should.be.eql("AAAA"); resultListMap.map.mappings.should.be.eql("AAAA;;"); }); + + it("should produce correct source map", function() { + var bootstrapCode = ' var hello\n var world\n'; + + should(function() { + var source = new ReplaceSource(new OriginalSource(bootstrapCode, "file.js")); + source.replace(7, 11, 'h', 'incorrect'); + source.replace(20, 24, 'w', 'identifiers'); + var resultMap = source.sourceAndMap(); + validate(resultMap.source, JSON.stringify(resultMap.map)); + }).throw(); + + var source = new ReplaceSource(new OriginalSource(bootstrapCode, "file.js")); + source.replace(7, 11, 'h', 'hello'); + source.replace(20, 24, 'w', 'world'); + var resultMap = source.sourceAndMap(); + validate(resultMap.source, JSON.stringify(resultMap.map)); + + }); }); diff --git a/yarn.lock b/yarn.lock index 04a85e2..cfeb84b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -969,6 +969,10 @@ jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" +jsesc@~0.3.x: + version "0.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.3.0.tgz#1bf5ee63b4539fe2e26d0c1e99c240b97a457972" + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -1032,6 +1036,14 @@ lodash._baseassign@^3.0.0: lodash._basecopy "^3.0.0" lodash.keys "^3.0.0" +lodash._basebind@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._basebind/-/lodash._basebind-2.3.0.tgz#2b5bc452a0e106143b21869f233bdb587417d248" + dependencies: + lodash._basecreate "~2.3.0" + lodash._setbinddata "~2.3.0" + lodash.isobject "~2.3.0" + lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" @@ -1040,14 +1052,106 @@ lodash._basecreate@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" +lodash._basecreate@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-2.3.0.tgz#9b88a86a4dcff7b7f3c61d83a2fcfc0671ec9de0" + dependencies: + lodash._renative "~2.3.0" + lodash.isobject "~2.3.0" + lodash.noop "~2.3.0" + +lodash._basecreatecallback@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._basecreatecallback/-/lodash._basecreatecallback-2.3.0.tgz#37b2ab17591a339e988db3259fcd46019d7ac362" + dependencies: + lodash._setbinddata "~2.3.0" + lodash.bind "~2.3.0" + lodash.identity "~2.3.0" + lodash.support "~2.3.0" + +lodash._basecreatewrapper@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._basecreatewrapper/-/lodash._basecreatewrapper-2.3.0.tgz#aa0c61ad96044c3933376131483a9759c3651247" + dependencies: + lodash._basecreate "~2.3.0" + lodash._setbinddata "~2.3.0" + lodash._slice "~2.3.0" + lodash.isobject "~2.3.0" + +lodash._createwrapper@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._createwrapper/-/lodash._createwrapper-2.3.0.tgz#d1aae1102dadf440e8e06fc133a6edd7fe146075" + dependencies: + lodash._basebind "~2.3.0" + lodash._basecreatewrapper "~2.3.0" + lodash.isfunction "~2.3.0" + +lodash._escapehtmlchar@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.3.0.tgz#d03da6bd82eedf38dc0a5b503d740ecd0e894592" + dependencies: + lodash._htmlescapes "~2.3.0" + +lodash._escapestringchar@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._escapestringchar/-/lodash._escapestringchar-2.3.0.tgz#cce73ae60fc6da55d2bf8a0679c23ca2bab149fc" + lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" +lodash._htmlescapes@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._htmlescapes/-/lodash._htmlescapes-2.3.0.tgz#1ca98863cadf1fa1d82c84f35f31e40556a04f3a" + lodash._isiterateecall@^3.0.0: version "3.0.9" resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" +lodash._objecttypes@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._objecttypes/-/lodash._objecttypes-2.3.0.tgz#6a3ea3987dd6eeb8021b2d5c9c303549cc2bae1e" + +lodash._reinterpolate@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-2.3.0.tgz#03ee9d85c0e55cbd590d71608a295bdda51128ec" + +lodash._renative@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._renative/-/lodash._renative-2.3.0.tgz#77d8edd4ced26dd5971f9e15a5f772e4e317fbd3" + +lodash._reunescapedhtml@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.3.0.tgz#db920b55ac7f3ff825939aceb9ba2c231713d24d" + dependencies: + lodash._htmlescapes "~2.3.0" + lodash.keys "~2.3.0" + +lodash._setbinddata@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._setbinddata/-/lodash._setbinddata-2.3.0.tgz#e5610490acd13277d59858d95b5f2727f1508f04" + dependencies: + lodash._renative "~2.3.0" + lodash.noop "~2.3.0" + +lodash._shimkeys@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._shimkeys/-/lodash._shimkeys-2.3.0.tgz#611f93149e3e6c721096b48769ef29537ada8ba9" + dependencies: + lodash._objecttypes "~2.3.0" + +lodash._slice@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash._slice/-/lodash._slice-2.3.0.tgz#147198132859972e4680ca29a5992c855669aa5c" + +lodash.bind@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-2.3.0.tgz#c2a8e18b68e5ecc152e2b168266116fea5b016cc" + dependencies: + lodash._createwrapper "~2.3.0" + lodash._renative "~2.3.0" + lodash._slice "~2.3.0" + lodash.create@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" @@ -1056,6 +1160,40 @@ lodash.create@3.1.1: lodash._basecreate "^3.0.0" lodash._isiterateecall "^3.0.0" +lodash.defaults@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-2.3.0.tgz#a832b001f138f3bb9721c2819a2a7cc5ae21ed25" + dependencies: + lodash._objecttypes "~2.3.0" + lodash.keys "~2.3.0" + +lodash.escape@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-2.3.0.tgz#844c38c58f844e1362ebe96726159b62cf5f2a58" + dependencies: + lodash._escapehtmlchar "~2.3.0" + lodash._reunescapedhtml "~2.3.0" + lodash.keys "~2.3.0" + +lodash.foreach@~2.3.x: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-2.3.0.tgz#083404c91e846ee77245fdf9d76519c68b2af168" + dependencies: + lodash._basecreatecallback "~2.3.0" + lodash.forown "~2.3.0" + +lodash.forown@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.forown/-/lodash.forown-2.3.0.tgz#24fb4aaf800d45fc2dc60bfec3ce04c836a3ad7f" + dependencies: + lodash._basecreatecallback "~2.3.0" + lodash._objecttypes "~2.3.0" + lodash.keys "~2.3.0" + +lodash.identity@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.identity/-/lodash.identity-2.3.0.tgz#6b01a210c9485355c2a913b48b6711219a173ded" + lodash.isarguments@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" @@ -1064,6 +1202,16 @@ lodash.isarray@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" +lodash.isfunction@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-2.3.0.tgz#6b2973e47a647cf12e70d676aea13643706e5267" + +lodash.isobject@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-2.3.0.tgz#2e16d3fc583da9831968953f2d8e6d73434f6799" + dependencies: + lodash._objecttypes "~2.3.0" + lodash.keys@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" @@ -1072,6 +1220,49 @@ lodash.keys@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" +lodash.keys@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-2.3.0.tgz#b350f4f92caa9f45a4a2ecf018454cf2f28ae253" + dependencies: + lodash._renative "~2.3.0" + lodash._shimkeys "~2.3.0" + lodash.isobject "~2.3.0" + +lodash.noop@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-2.3.0.tgz#3059d628d51bbf937cd2a0b6fc3a7f212a669c2c" + +lodash.support@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.support/-/lodash.support-2.3.0.tgz#7eaf038af4f0d6aab776b44aa6dcfc80334c9bfd" + dependencies: + lodash._renative "~2.3.0" + +lodash.template@~2.3.x: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-2.3.0.tgz#4e3e29c433b4cfea675ec835e6f12391c61fd22b" + dependencies: + lodash._escapestringchar "~2.3.0" + lodash._reinterpolate "~2.3.0" + lodash.defaults "~2.3.0" + lodash.escape "~2.3.0" + lodash.keys "~2.3.0" + lodash.templatesettings "~2.3.0" + lodash.values "~2.3.0" + +lodash.templatesettings@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-2.3.0.tgz#303d132c342710040d5a18efaa2d572fd03f8cdc" + dependencies: + lodash._reinterpolate "~2.3.0" + lodash.escape "~2.3.0" + +lodash.values@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-2.3.0.tgz#ca96fbe60a20b0b0ec2ba2ba5fc6a765bd14a3ba" + dependencies: + lodash.keys "~2.3.0" + lodash@^4.0.0, lodash@^4.3.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -1491,6 +1682,12 @@ source-map@^0.4.4: dependencies: amdefine ">=0.0.4" +source-map@~0.1.x: + version "0.1.43" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" + dependencies: + amdefine ">=0.0.4" + source-map@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" @@ -1505,6 +1702,15 @@ source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" +sourcemap-validator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/sourcemap-validator/-/sourcemap-validator-1.1.0.tgz#00454547d1682186e1498a7208e022e8dfa8738f" + dependencies: + jsesc "~0.3.x" + lodash.foreach "~2.3.x" + lodash.template "~2.3.x" + source-map "~0.1.x" + split@~0.2.10: version "0.2.10" resolved "https://registry.yarnpkg.com/split/-/split-0.2.10.tgz#67097c601d697ce1368f418f06cd201cf0521a57"