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

fix(javascript): fix locEnd for VariableDeclaration caused by --no-semi #5434

Merged
merged 2 commits into from Nov 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/language-js/parser-babylon.js
Expand Up @@ -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(
Expand Down Expand Up @@ -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) {
Expand Down
5 changes: 3 additions & 2 deletions src/language-js/parser-flow.js
Expand Up @@ -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
Expand All @@ -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
Expand Down
5 changes: 3 additions & 2 deletions src/language-js/parser-typescript.js
Expand Up @@ -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 {
Expand All @@ -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) {
Expand Down
70 changes: 70 additions & 0 deletions 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;
8 changes: 8 additions & 0 deletions tests/comments/__snapshots__/jsfmt.spec.js.snap
Expand Up @@ -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
Expand Down Expand Up @@ -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`] = `
Expand Down
4 changes: 4 additions & 0 deletions tests/comments/variable_declarator.js
Expand Up @@ -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))