Skip to content

Commit

Permalink
Do not delete "fake" source map comments from strings (#9960)
Browse files Browse the repository at this point in the history
Instead of using `convert-source-map`'s `removeComments` method before
parsing the file, we can first parse the file with `@babel/parser` and then
analyze the comments.
This is needed because it is not possible to reliabily detect comments in
JavaScript without fully parsing the file:
thlorenz/convert-source-map#63
  • Loading branch information
nicolo-ribaudo committed Aug 14, 2019
1 parent ee344c3 commit f0c2364
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 34 deletions.
2 changes: 1 addition & 1 deletion lib/third-party-libs.js.flow
Expand Up @@ -181,7 +181,7 @@ declare module "convert-source-map" {
fromJSON(str: string): Converter,
fromBase64(str: string): Converter,
fromComment(str: string): Converter,
fromMapFileComment(str: string): Converter,
fromMapFileComment(str: string, dir: string): Converter,
fromSource(str: string): Converter,
fromMapFileSource(str: string, dir: string): Converter,
removeComments(str: string): string,
Expand Down
102 changes: 70 additions & 32 deletions packages/babel-core/src/transformation/normalize-file.js
Expand Up @@ -27,6 +27,20 @@ export default function normalizeFile(
): File {
code = `${code || ""}`;

if (ast) {
if (ast.type === "Program") {
ast = t.file(ast, [], []);
} else if (ast.type !== "File") {
throw new Error("AST root must be a Program or File node");
}
ast = cloneDeep(ast);
} else {
// The parser's AST types aren't fully compatible with the types generated
// by the logic in babel-types.
// $FlowFixMe
ast = parser(pluginPasses, options, code);
}

let inputMap = null;
if (options.inputSourceMap !== false) {
// If an explicit object is passed in, it overrides the processing of
Expand All @@ -36,54 +50,33 @@ export default function normalizeFile(
}

if (!inputMap) {
try {
inputMap = convertSourceMap.fromSource(code);

if (inputMap) {
code = convertSourceMap.removeComments(code);
const lastComment = extractComments(INLINE_SOURCEMAP_REGEX, ast);
if (lastComment) {
try {
inputMap = convertSourceMap.fromComment(lastComment);
} catch (err) {
debug("discarding unknown inline input sourcemap", err);
}
} catch (err) {
debug("discarding unknown inline input sourcemap", err);
code = convertSourceMap.removeComments(code);
}
}

if (!inputMap) {
if (typeof options.filename === "string") {
const lastComment = extractComments(EXTERNAL_SOURCEMAP_REGEX, ast);
if (typeof options.filename === "string" && lastComment) {
try {
inputMap = convertSourceMap.fromMapFileSource(
code,
inputMap = convertSourceMap.fromMapFileComment(
lastComment,
path.dirname(options.filename),
);

if (inputMap) {
code = convertSourceMap.removeMapFileComments(code);
}
} catch (err) {
debug("discarding unknown file input sourcemap", err);
code = convertSourceMap.removeMapFileComments(code);
}
} else {
} else if (lastComment) {
debug("discarding un-loadable file input sourcemap");
code = convertSourceMap.removeMapFileComments(code);
}
}
}

if (ast) {
if (ast.type === "Program") {
ast = t.file(ast, [], []);
} else if (ast.type !== "File") {
throw new Error("AST root must be a Program or File node");
}
ast = cloneDeep(ast);
} else {
// The parser's AST types aren't fully compatible with the types generated
// by the logic in babel-types.
// $FlowFixMe
ast = parser(pluginPasses, options, code);
}

return new File(options, {
code,
ast,
Expand Down Expand Up @@ -156,3 +149,48 @@ function parser(
throw err;
}
}

// These regexps are copied from the convert-source-map package,
// but without // or /* at the beginning of the comment.

// eslint-disable-next-line max-len
const INLINE_SOURCEMAP_REGEX = /^[@#]\s+sourceMappingURL=data:(?:application|text)\/json;(?:charset[:=]\S+?;)?base64,(?:.*)$/;
const EXTERNAL_SOURCEMAP_REGEX = /^[@#][ \t]+sourceMappingURL=([^\s'"`]+?)[ \t]*$/;

function extractCommentsFromList(regex, comments, lastComment) {
if (comments) {
comments = comments.filter(({ value }) => {
if (regex.test(value)) {
lastComment = value;
return false;
}
return true;
});
}
return [comments, lastComment];
}

function extractComments(regex, ast) {
let lastComment = null;
t.traverseFast(ast, node => {
// $FlowIgnore destructuring with expressions is not supported
[node.leadingComments, lastComment] = extractCommentsFromList(
regex,
node.leadingComments,
lastComment,
);
// $FlowIgnore destructuring with expressions is not supported
[node.innerComments, lastComment] = extractCommentsFromList(
regex,
node.innerComments,
lastComment,
);
// $FlowIgnore destructuring with expressions is not supported
[node.trailingComments, lastComment] = extractCommentsFromList(
regex,
node.trailingComments,
lastComment,
);
});
return lastComment;
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

@@ -0,0 +1,3 @@
{
"inputSourceMap": true
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/babel-types/src/traverse/traverseFast.js
Expand Up @@ -7,7 +7,7 @@ import { VISITOR_KEYS } from "../definitions";
*/
export default function traverseFast(
node: Object,
enter: (node: Node, opts?: Object) => void,
enter: (node: BabelNode, opts?: Object) => void,
opts?: Object,
): void {
if (!node) return;
Expand Down

0 comments on commit f0c2364

Please sign in to comment.