From 7ea283eb82074244adcba2b44f39cdb994bec3df Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Fri, 9 Dec 2016 09:55:42 +0000 Subject: [PATCH 1/3] babel-generator: Expose raw mappings Exposes raw mappings when source map generation is enabled. To avoid the cost of source map generation for consumers of the raw mappings only, `.map` is changed to a getter that generates the source map lazily on first access. --- packages/babel-generator/src/buffer.js | 19 +++- packages/babel-generator/src/source-map.js | 51 ++++++---- packages/babel-generator/test/index.js | 106 +++++++++++++++++++++ 3 files changed, 156 insertions(+), 20 deletions(-) diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index 51ddd6a953..15be5e7736 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -38,10 +38,25 @@ export default class Buffer { get(): Object { this._flush(); - return { + const map = this._map; + const result = { code: trimEnd(this._buf.join("")), - map: this._map ? this._map.get() : null, + map: null, + rawMappings: map && map.getRawMappings(), }; + + if (map) { + Object.defineProperty(result, "map", { + get() { + return this.map = map.get(); + }, + set(value) { + Object.defineProperty(this, "map", {value, writable: true}); + }, + }); + } + + return result; } /** diff --git a/packages/babel-generator/src/source-map.js b/packages/babel-generator/src/source-map.js index dc839c654e..c94c9140cf 100644 --- a/packages/babel-generator/src/source-map.js +++ b/packages/babel-generator/src/source-map.js @@ -6,19 +6,10 @@ import sourceMap from "source-map"; export default class SourceMap { constructor(opts, code) { - this._opts = opts; - this._map = new sourceMap.SourceMapGenerator({ - file: opts.sourceMapTarget, - sourceRoot: opts.sourceRoot - }); - - if (typeof code === "string") { - this._map.setSourceContent(opts.sourceFileName, code); - } else if (typeof code === "object") { - Object.keys(code).forEach((sourceFileName) => { - this._map.setSourceContent(sourceFileName, code[sourceFileName]); - }); - } + this._cachedMap = null; + this._code = code; + this._opts = opts; + this._rawMappings = []; } /** @@ -26,7 +17,29 @@ export default class SourceMap { */ get() { - return this._map.toJSON(); + if (!this._cachedMap) { + const map = this._cachedMap = new sourceMap.SourceMapGenerator({ + file: this._opts.sourceMapTarget, + sourceRoot: this._opts.sourceRoot, + }); + + const code = this._code; + if (typeof code === "string") { + map.setSourceContent(this._opts.sourceFileName, code); + } else if (typeof code === "object") { + Object.keys(code).forEach((sourceFileName) => { + map.setSourceContent(sourceFileName, code[sourceFileName]); + }); + } + + this._rawMappings.forEach(map.addMapping, map); + } + + return this._cachedMap.toJSON(); + } + + getRawMappings() { + return this._rawMappings.slice(); } /** @@ -52,18 +65,20 @@ export default class SourceMap { return; } + this._cachedMap = null; this._lastGenLine = generatedLine; this._lastSourceLine = line; this._lastSourceColumn = column; - this._map.addMapping({ - name: identifierName, + this._rawMappings.push({ + // undefined to allow for more compact json serialization + name: identifierName || undefined, generated: { line: generatedLine, column: generatedColumn, }, - source: line == null ? null : filename || this._opts.sourceFileName, - original: line == null ? null : { + source: line == null ? undefined : filename || this._opts.sourceFileName, + original: line == null ? undefined : { line: line, column: column, }, diff --git a/packages/babel-generator/test/index.js b/packages/babel-generator/test/index.js index bbdb06eddb..47e6499a64 100644 --- a/packages/babel-generator/test/index.js +++ b/packages/babel-generator/test/index.js @@ -58,6 +58,69 @@ describe("generation", function () { ] }, "sourcemap was incorrectly generated"); + chai.expect(generated.rawMappings).to.deep.equal([ + { name: undefined, + generated: { line: 1, column: 0 }, + source: "a.js", + original: { line: 1, column: 0 } }, + { name: "hi", + generated: { line: 1, column: 9 }, + source: "a.js", + original: { line: 1, column: 9 } }, + { name: undefined, + generated: { line: 1, column: 11 }, + source: "a.js", + original: { line: 1, column: 0 } }, + { name: "msg", + generated: { line: 1, column: 12 }, + source: "a.js", + original: { line: 1, column: 13 } }, + { name: undefined, + generated: { line: 1, column: 15 }, + source: "a.js", + original: { line: 1, column: 0 } }, + { name: undefined, + generated: { line: 1, column: 17 }, + source: "a.js", + original: { line: 1, column: 18 } }, + { name: "console", + generated: { line: 2, column: 0 }, + source: "a.js", + original: { line: 1, column: 20 } }, + { name: "log", + generated: { line: 2, column: 10 }, + source: "a.js", + original: { line: 1, column: 28 } }, + { name: undefined, + generated: { line: 2, column: 13 }, + source: "a.js", + original: { line: 1, column: 20 } }, + { name: "msg", + generated: { line: 2, column: 14 }, + source: "a.js", + original: { line: 1, column: 32 } }, + { name: undefined, + generated: { line: 2, column: 17 }, + source: "a.js", + original: { line: 1, column: 20 } }, + { name: undefined, + generated: { line: 3, column: 0 }, + source: "a.js", + original: { line: 1, column: 39 } }, + { name: "hi", + generated: { line: 5, column: 0 }, + source: "b.js", + original: { line: 1, column: 0 } }, + { name: undefined, + generated: { line: 5, column: 3 }, + source: "b.js", + original: { line: 1, column: 3 } }, + { name: undefined, + generated: { line: 5, column: 10 }, + source: "b.js", + original: { line: 1, column: 0 } }, + ], "raw mappings were incorrectly generated"); + chai.expect(generated.code).to.equal( "function hi(msg) {\n console.log(msg);\n}\n\nhi('hello');", "code was incorrectly generated" @@ -92,11 +155,54 @@ describe("generation", function () { sourcesContent: [ "function foo() { bar; }\n" ] }, "sourcemap was incorrectly generated"); + chai.expect(generated.rawMappings).to.deep.equal([ + { name: undefined, + generated: { line: 1, column: 0 }, + source: "inline", + original: { line: 1, column: 0 } }, + { name: "foo", + generated: { line: 1, column: 9 }, + source: "inline", + original: { line: 1, column: 9 } }, + { name: undefined, + generated: { line: 1, column: 13 }, + source: "inline", + original: { line: 1, column: 0 } }, + { name: undefined, + generated: { line: 1, column: 16 }, + source: "inline", + original: { line: 1, column: 15 } }, + { name: "bar", + generated: { line: 2, column: 0 }, + source: "inline", + original: { line: 1, column: 17 } }, + { name: undefined, + generated: { line: 3, column: 0 }, + source: "inline", + original: { line: 1, column: 23 } }, + ], "raw mappings were incorrectly generated"); + chai.expect(generated.code).to.equal( "function foo2() {\n bar2;\n}", "code was incorrectly generated" ); }); + + it("lazy source map generation", function() { + let code = "function hi (msg) { console.log(msg); }\n"; + + let ast = parse(code, { filename: "a.js" }).program; + let generated = generate.default(ast, { + sourceFileName: "a.js", + sourceMaps: true, + }); + + chai.expect(generated.rawMappings).to.be.an("array"); + + chai.expect(generated).ownPropertyDescriptor("map").not.to.have.property("value"); + + chai.expect(generated.map).to.be.an("object"); + }); }); From 4cb7e5009a1ae0343837c96e7f9a3c1f1d2c5271 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Wed, 14 Dec 2016 10:18:18 +0000 Subject: [PATCH 2/3] Add missing property descriptor values --- packages/babel-generator/src/buffer.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index 15be5e7736..76db204668 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -47,6 +47,8 @@ export default class Buffer { if (map) { Object.defineProperty(result, "map", { + configurable: true, + enumerable: true, get() { return this.map = map.get(); }, From 2907d663dc80f034af158b90a2193f11134f07e0 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Fri, 16 Dec 2016 01:12:50 +0000 Subject: [PATCH 3/3] add comments --- packages/babel-generator/src/buffer.js | 2 ++ packages/babel-generator/src/source-map.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index 76db204668..69c61db864 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -46,6 +46,8 @@ export default class Buffer { }; if (map) { + // The `.map` property is lazy to allow callers to use the raw mappings + // without any overhead Object.defineProperty(result, "map", { configurable: true, enumerable: true, diff --git a/packages/babel-generator/src/source-map.js b/packages/babel-generator/src/source-map.js index c94c9140cf..4388debc89 100644 --- a/packages/babel-generator/src/source-map.js +++ b/packages/babel-generator/src/source-map.js @@ -70,6 +70,8 @@ export default class SourceMap { this._lastSourceLine = line; this._lastSourceColumn = column; + // We are deliberately not using the `source-map` library here to allow + // callers to use these mappings without any overhead this._rawMappings.push({ // undefined to allow for more compact json serialization name: identifierName || undefined,