Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow inputSourceMap on .parse() #824

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions lib/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { Options } from "./options";
export function parse(source: string, options?: Partial<Options>) {
options = normalizeOptions(options);

const lines = fromString(source, options);
const map = options.inputSourceMap ? ({source, inputSourceMap: options.inputSourceMap}) : util.extractInlineSourceMaps(source);
const lines = fromString(map.source, options);

const sourceWithoutTabs = lines.toString({
tabWidth: options.tabWidth,
Expand Down Expand Up @@ -120,7 +121,17 @@ export function parse(source: string, options?: Partial<Options>) {

// Return a copy of the original AST so that any changes made may be
// compared to the original.
return new TreeCopier(lines, tokens).copy(file);
const copy = new TreeCopier(lines, tokens).copy(file);

// Attach inline sourcemap metadata when possible
Object.defineProperty(copy, "inputSourceMap", {
enumerable: false,
configurable: false,
writable: false,
value: map.inputSourceMap
});

return copy;
}

interface TreeCopierType {
Expand Down
2 changes: 1 addition & 1 deletion lib/printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ const Printer = (function Printer(this: PrinterType, config?: any) {
return new PrintResult(
lines.toString(config),
util.composeSourceMaps(
config.inputSourceMap,
config.inputSourceMap || ast.inputSourceMap,
lines.getSourceMap(config.sourceMapName, config.sourceRoot),
),
);
Expand Down
10 changes: 10 additions & 0 deletions lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,13 @@ export function isTrailingCommaEnabled(options: any, context: any) {
}
return !!trailingComma;
}

export function extractInlineSourceMaps(source: string) {
const re = /\s*\/\/(?:@|#) sourceMappingURL=data:application\/json;base64,(\S*)$/m;
const map = source.match(re);

return {
source: map ? source.replace(re, '') : source,
inputSourceMap: map ? (Buffer.from(map[1], 'base64')).toString() : undefined
};
}
91 changes: 91 additions & 0 deletions test/mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,97 @@ describe("source maps", function () {
});
});

it("should extract inline sourcemap from source", function() {
function addUseStrict(ast: any) {
return recast.visit(ast, {
visitFunction: function(path) {
path.get("body", "body").unshift(
b.expressionStatement(b.literal("use strict"))
);
this.traverse(path);
}
});
}

function stripConsole(ast: any) {
return recast.visit(ast, {
visitCallExpression: function(path) {
const node = path.value;
if (n.MemberExpression.check(node.callee) &&
n.Identifier.check(node.callee.object) &&
node.callee.object.name === "console") {
n.ExpressionStatement.assert(path.parent.node);
path.parent.replace();
return false;
}

return;
}
});
}

const code = [
"function add(a, b) {",
" var sum = a + b;",
" console.log(a, b);",
" return sum;",
"}"
].join("\n");

const ast = parse(code, {
sourceFileName: "original.js"
});

const useStrictResult = new Printer({
sourceMapName: "useStrict.map.json"
}).print(addUseStrict(ast));

const useStrictResultCodeAndMap = [
useStrictResult.code,
"//# sourceMappingURL=data:application/json;base64,",
Buffer.from(JSON.stringify(useStrictResult.map)).toString('base64')
].join('');

const useStrictAst = parse(useStrictResultCodeAndMap, {
sourceFileName: "useStrict.js"
});

const oneStepResult = new Printer({
sourceMapName: "oneStep.map.json"
}).print(stripConsole(ast));

const twoStepResult = new Printer({
sourceMapName: "twoStep.map.json"
}).print(stripConsole(useStrictAst));

assert.strictEqual(
oneStepResult.code,
twoStepResult.code
);

const smc1 = new sourceMap.SourceMapConsumer(oneStepResult.map);
const smc2 = new sourceMap.SourceMapConsumer(twoStepResult.map);

smc1.eachMapping(function(mapping) {
const pos = {
line: mapping.generatedLine,
column: mapping.generatedColumn
};

const orig1 = smc1.originalPositionFor(pos);
const orig2 = smc2.originalPositionFor(pos);

// The composition of the source maps generated separately from
// the two transforms should be equivalent to the source map
// generated from the composition of the two transforms.
assert.deepEqual(orig1, orig2);

// Make sure the two-step source map refers back to the original
// source instead of the intermediate source.
assert.strictEqual(orig2.source, "original.js");
});
});

it("should work when a child node becomes null", function () {
// https://github.com/facebook/regenerator/issues/103
const code = ["for (var i = 0; false; i++)", " log(i);"].join(eol);
Expand Down