From 2fd004f144e31f55bac7f38696e0e8cb52522307 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Sat, 21 Jul 2018 18:37:30 -0700 Subject: [PATCH] Support inline base64 source maps --- src/assets/JSAsset.js | 36 +++++++++++++----- test/graphql.js | 1 + test/integration/sourcemap-existing/sum.js | 14 ++++--- test/integration/sourcemap-existing/sum.map | 20 +++------- test/integration/sourcemap-inline/index.js | 5 +++ test/integration/sourcemap-inline/sum.js | 9 +++++ test/sourcemaps.js | 42 +++++++++++++++++++-- 7 files changed, 94 insertions(+), 33 deletions(-) create mode 100644 test/integration/sourcemap-inline/index.js create mode 100644 test/integration/sourcemap-inline/sum.js diff --git a/src/assets/JSAsset.js b/src/assets/JSAsset.js index 3acf6ee4f43..c9e6c1422b3 100644 --- a/src/assets/JSAsset.js +++ b/src/assets/JSAsset.js @@ -23,6 +23,8 @@ const GLOBAL_RE = /\b(?:process|__dirname|__filename|global|Buffer|define)\b/; const FS_RE = /\breadFileSync\b/; const SW_RE = /\bnavigator\s*\.\s*serviceWorker\s*\.\s*register\s*\(/; const WORKER_RE = /\bnew\s*Worker\s*\(/; +const SOURCEMAP_RE = /\/\/\s*[@#]\s*sourceMappingURL\s*=\s*([^\s]+)/; +const DATA_URL_RE = /^data:[^;]+(?:;charset=[^;]+)?;base64,(.*)/; class JSAsset extends Asset { constructor(name, options) { @@ -107,26 +109,40 @@ class JSAsset extends Asset { walk.ancestor(this.ast, collectDependencies, this); } - async pretransform() { + async loadSourceMap() { // Get original sourcemap if there is any - let sourceMapLine = this.contents.lastIndexOf('//# sourceMappingURL='); - if (sourceMapLine > -1) { - let sourceMapReference = this.contents.substring(sourceMapLine + 21); - this.contents = this.contents.substring(0, sourceMapLine); + let match = this.contents.match(SOURCEMAP_RE); + if (match) { + this.contents = this.contents.replace(SOURCEMAP_RE, ''); + + let url = match[1]; + let dataURLMatch = url.match(DATA_URL_RE); try { - this.sourceMap = JSON.parse( - await fs.readFile( - path.join(path.dirname(this.name), sourceMapReference) - ) - ); + let json; + if (dataURLMatch) { + json = new Buffer(dataURLMatch[1], 'base64').toString(); + } else { + json = await fs.readFile( + path.join(path.dirname(this.name), url), + 'utf8' + ); + } + + this.sourceMap = JSON.parse(json); + + // Add as a dep so we watch the source map for changes. + this.addDependency(match[1], {includedInParent: true}); } catch (e) { logger.warn( `Could not load existing sourcemap of ${this.relativeName}.` ); } } + } + async pretransform() { + await this.loadSourceMap(); await babel(this); // Inline environment variables diff --git a/test/graphql.js b/test/graphql.js index b532006994a..ed6eacc93af 100644 --- a/test/graphql.js +++ b/test/graphql.js @@ -31,6 +31,7 @@ describe('graphql', function() { firstName lastName } + `.definitions ); }); diff --git a/test/integration/sourcemap-existing/sum.js b/test/integration/sourcemap-existing/sum.js index 634f219de04..00d08409ec2 100644 --- a/test/integration/sourcemap-existing/sum.js +++ b/test/integration/sourcemap-existing/sum.js @@ -1,7 +1,11 @@ -function sum(a, b) { - return a + b; -} +// Generated by CoffeeScript 1.12.6 +(function() { + module.exports = (function(_this) { + return function(a, b) { + return a + b; + }; + })(this); -module.exports = sum; +}).call(this); -//# sourceMappingURL=/sum.map \ No newline at end of file +//# sourceMappingURL=sum.map diff --git a/test/integration/sourcemap-existing/sum.map b/test/integration/sourcemap-existing/sum.map index 6334648a7cf..a445462081d 100644 --- a/test/integration/sourcemap-existing/sum.map +++ b/test/integration/sourcemap-existing/sum.map @@ -1,19 +1,11 @@ { "version": 3, + "file": "sum.js", + "sourceRoot": "", "sources": [ - "sum.js" + "sum.coffee" ], - "names": [ - "sum", - "a", - "b", - "module", - "exports" - ], - "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAASA,GAAT,CAAaC,CAAb,EAAgBC,CAAhB,EAAmB;AACjB,SAAOD,IAAIC,CAAX;AACD;;AAEDC,OAAOC,OAAP,GAAiBJ,GAAjB", - "file": "sum.map", - "sourceRoot": '', - "sourcesContent": [ - "function sum(a, b) {\n return a + b;\n}\n\nmodule.exports = sum;\n\n" - ] + "names": [], + "mappings": ";AAAA;EAAA,MAAM,CAAC,OAAP,GAAiB,CAAA,SAAA,KAAA;WAAA,SAAC,CAAD,EAAI,CAAJ;aAAU,CAAA,GAAI;IAAd;EAAA,CAAA,CAAA,CAAA,IAAA;AAAjB", + "sourcesContent": ["module.exports = (a, b) => a + b"] } \ No newline at end of file diff --git a/test/integration/sourcemap-inline/index.js b/test/integration/sourcemap-inline/index.js new file mode 100644 index 00000000000..66b67d1dbc5 --- /dev/null +++ b/test/integration/sourcemap-inline/index.js @@ -0,0 +1,5 @@ +const sum = require('./sum'); + +module.exports = function() { + return sum(1, 2); +} \ No newline at end of file diff --git a/test/integration/sourcemap-inline/sum.js b/test/integration/sourcemap-inline/sum.js new file mode 100644 index 00000000000..11a42a89d75 --- /dev/null +++ b/test/integration/sourcemap-inline/sum.js @@ -0,0 +1,9 @@ +// Generated by CoffeeScript 2.3.1 +(function() { + module.exports = (a, b) => { + return a + b; + }; + +}).call(this); + +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3VtLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsic3VtLmNvZmZlZSJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7RUFBQSxNQUFNLENBQUMsT0FBUCxHQUFpQixDQUFDLENBQUQsRUFBSSxDQUFKLENBQUEsR0FBQTtXQUFVLENBQUEsR0FBSTtFQUFkO0FBQWpCIiwic291cmNlc0NvbnRlbnQiOlsibW9kdWxlLmV4cG9ydHMgPSAoYSwgYikgPT4gYSArIGJcbiJdfQ== diff --git a/test/sourcemaps.js b/test/sourcemaps.js index adb932f9362..c8bb5f51a88 100644 --- a/test/sourcemaps.js +++ b/test/sourcemaps.js @@ -220,7 +220,7 @@ describe('sourcemaps', function() { ] }); - let jsOutput = fs.readFileSync(b.name).toString(); + let jsOutput = await fs.readFile(b.name, 'utf8'); let sourcemapReference = path.join( __dirname, @@ -229,13 +229,47 @@ describe('sourcemaps', function() { ); assert( - fs.existsSync(path.join(sourcemapReference)), + await fs.exists(path.join(sourcemapReference)), + 'referenced sourcemap should exist' + ); + + let map = await fs.readFile(path.join(sourcemapReference), 'utf8'); + assert( + map.indexOf('module.exports = (a, b) => a + b') > -1, + 'Sourcemap should contain the existing sourcemap' + ); + mapValidator(jsOutput, map); + }); + + it('should load inline sourcemaps of libraries', async function() { + let b = await bundle(__dirname + '/integration/sourcemap-inline/index.js'); + + assertBundleTree(b, { + name: 'index.js', + assets: ['index.js', 'sum.js'], + childBundles: [ + { + type: 'map' + } + ] + }); + + let jsOutput = await fs.readFile(b.name, 'utf8'); + + let sourcemapReference = path.join( + __dirname, + '/dist/', + jsOutput.substring(jsOutput.lastIndexOf('//# sourceMappingURL') + 22) + ); + + assert( + await fs.exists(path.join(sourcemapReference)), 'referenced sourcemap should exist' ); - let map = fs.readFileSync(path.join(sourcemapReference)).toString(); + let map = await fs.readFile(path.join(sourcemapReference), 'utf8'); assert( - map.indexOf('return a + b;') > -1, + map.indexOf('module.exports = (a, b) => a + b') > -1, 'Sourcemap should contain the existing sourcemap' ); mapValidator(jsOutput, map);