diff --git a/lib/third-party-libs.js.flow b/lib/third-party-libs.js.flow
index 884b1e68bbfb..f2c81c237d72 100644
--- a/lib/third-party-libs.js.flow
+++ b/lib/third-party-libs.js.flow
@@ -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,
diff --git a/packages/babel-core/src/transformation/normalize-file.js b/packages/babel-core/src/transformation/normalize-file.js
index 1df42ad763b2..0bc88c8a0c1b 100644
--- a/packages/babel-core/src/transformation/normalize-file.js
+++ b/packages/babel-core/src/transformation/normalize-file.js
@@ -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
@@ -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,
@@ -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;
+}
diff --git a/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/input.js b/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/input.js
new file mode 100644
index 000000000000..b883f5d7b9a4
--- /dev/null
+++ b/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/input.js
@@ -0,0 +1,8 @@
+// https://github.com/babel/babel/issues/9790
+const comment = `//# sourceMappingURL=${path.basename(
+ sourceMapFilename
+)}`
+
+// https://github.com/babel/babel/issues/9956
+this.shadowRoot.innerHTML = ``;
diff --git a/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/options.json b/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/options.json
new file mode 100644
index 000000000000..0e6084f210d8
--- /dev/null
+++ b/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/options.json
@@ -0,0 +1,3 @@
+{
+ "inputSourceMap": true
+}
diff --git a/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/output.js b/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/output.js
new file mode 100644
index 000000000000..9188ec5abc3b
--- /dev/null
+++ b/packages/babel-core/test/fixtures/transformation/source-maps/comment-inside-string/output.js
@@ -0,0 +1,5 @@
+// https://github.com/babel/babel/issues/9790
+const comment = `//# sourceMappingURL=${path.basename(sourceMapFilename)}`; // https://github.com/babel/babel/issues/9956
+
+this.shadowRoot.innerHTML = ``;
diff --git a/packages/babel-types/src/traverse/traverseFast.js b/packages/babel-types/src/traverse/traverseFast.js
index 580628343cfb..e262c10ad47d 100644
--- a/packages/babel-types/src/traverse/traverseFast.js
+++ b/packages/babel-types/src/traverse/traverseFast.js
@@ -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;