diff --git a/src/language-js/parser-babylon.js b/src/language-js/parser-babylon.js index aa26b2783d56..faa6403aebd6 100644 --- a/src/language-js/parser-babylon.js +++ b/src/language-js/parser-babylon.js @@ -3,6 +3,7 @@ const createError = require("../common/parser-create-error"); const hasPragma = require("./pragma").hasPragma; const locFns = require("./loc"); +const postprocess = require("./postprocess"); function babylonOptions(extraOptions, extraPlugins) { return Object.assign( @@ -74,7 +75,7 @@ function parse(text, parsers, opts) { ); } delete ast.tokens; - return ast; + return postprocess(ast, Object.assign({}, opts, { originalText: text })); } function tryCombinations(fn, combinations) { diff --git a/src/language-js/parser-flow.js b/src/language-js/parser-flow.js index d913d0642fc6..132713c33794 100644 --- a/src/language-js/parser-flow.js +++ b/src/language-js/parser-flow.js @@ -4,8 +4,9 @@ const createError = require("../common/parser-create-error"); const includeShebang = require("../common/parser-include-shebang"); const hasPragma = require("./pragma").hasPragma; const locFns = require("./loc"); +const postprocess = require("./postprocess"); -function parse(text /*, parsers, opts*/) { +function parse(text, parsers, opts) { // Fixes Node 4 issue (#1986) "use strict"; // eslint-disable-line // Inline the require to avoid loading all the JS if we don't use it @@ -28,7 +29,7 @@ function parse(text /*, parsers, opts*/) { } includeShebang(text, ast); - return ast; + return postprocess(ast, Object.assign({}, opts, { originalText: text })); } // Export as a plugin so we can reuse the same bundle for UMD loading diff --git a/src/language-js/parser-typescript.js b/src/language-js/parser-typescript.js index 66e61027291d..b8a4c309dee1 100644 --- a/src/language-js/parser-typescript.js +++ b/src/language-js/parser-typescript.js @@ -4,8 +4,9 @@ const createError = require("../common/parser-create-error"); const includeShebang = require("../common/parser-include-shebang"); const hasPragma = require("./pragma").hasPragma; const locFns = require("./loc"); +const postprocess = require("./postprocess"); -function parse(text /*, parsers, opts*/) { +function parse(text, parsers, opts) { const jsx = isProbablyJsx(text); let ast; try { @@ -31,7 +32,7 @@ function parse(text /*, parsers, opts*/) { delete ast.tokens; includeShebang(text, ast); - return ast; + return postprocess(ast, Object.assign({}, opts, { originalText: text })); } function tryParseTypeScript(text, jsx) { diff --git a/src/language-js/postprocess.js b/src/language-js/postprocess.js new file mode 100644 index 000000000000..cdd12d36997f --- /dev/null +++ b/src/language-js/postprocess.js @@ -0,0 +1,70 @@ +"use strict"; + +const { getLast } = require("../common/util"); + +// fix unexpected locEnd caused by --no-semi style +function postprocess(ast, options) { + visitNode(ast, node => { + switch (node.type) { + case "VariableDeclaration": { + const lastDeclaration = getLast(node.declarations); + if (lastDeclaration && lastDeclaration.init) { + overrideLocEnd(node, lastDeclaration); + } + break; + } + } + }); + + return ast; + + /** + * - `toOverrideNode` must be the last thing in `toBeOverriddenNode` + * - do nothing if there's a semicolon on `toOverrideNode.end` (no need to fix) + */ + function overrideLocEnd(toBeOverriddenNode, toOverrideNode) { + if (options.originalText[locEnd(toOverrideNode)] === ";") { + return; + } + if (options.parser === "flow") { + toBeOverriddenNode.range = [ + toBeOverriddenNode.range[0], + toOverrideNode.range[1] + ]; + } else { + toBeOverriddenNode.end = toOverrideNode.end; + } + toBeOverriddenNode.loc = Object.assign({}, toBeOverriddenNode.loc, { + end: toBeOverriddenNode.loc.end + }); + } + + function locEnd(node) { + return options.parser === "flow" ? node.range[1] : node.end; + } +} + +function visitNode(node, fn) { + if (!node || typeof node !== "object") { + return; + } + + if (Array.isArray(node)) { + for (const subNode of node) { + visitNode(subNode, fn); + } + return; + } + + if (typeof node.type !== "string") { + return; + } + + for (const key of Object.keys(node)) { + visitNode(node[key], fn); + } + + fn(node); +} + +module.exports = postprocess; diff --git a/tests/comments/__snapshots__/jsfmt.spec.js.snap b/tests/comments/__snapshots__/jsfmt.spec.js.snap index 3a3b5bc88863..d960ddc2602a 100644 --- a/tests/comments/__snapshots__/jsfmt.spec.js.snap +++ b/tests/comments/__snapshots__/jsfmt.spec.js.snap @@ -2057,6 +2057,10 @@ let // Comment let // Comment foo = 'val', bar = 'val'; + +const foo = 123 +// Nothing to see here. +;["2", "3"].forEach(x => console.log(x)) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ let obj = // Comment @@ -2127,6 +2131,10 @@ let // Comment foo = "val", bar = "val"; +const foo = 123; +// Nothing to see here. +["2", "3"].forEach(x => console.log(x)); + `; exports[`while.js - flow-verify 1`] = ` diff --git a/tests/comments/variable_declarator.js b/tests/comments/variable_declarator.js index dbd7f7a30f09..1901da5689c3 100644 --- a/tests/comments/variable_declarator.js +++ b/tests/comments/variable_declarator.js @@ -60,3 +60,7 @@ let // Comment let // Comment foo = 'val', bar = 'val'; + +const foo = 123 +// Nothing to see here. +;["2", "3"].forEach(x => console.log(x))