diff --git a/eslint/babel-eslint-parser/src/analyze-scope.js b/eslint/babel-eslint-parser/src/analyze-scope.js index ffb9eb95ba..2a52043854 100644 --- a/eslint/babel-eslint-parser/src/analyze-scope.js +++ b/eslint/babel-eslint-parser/src/analyze-scope.js @@ -318,7 +318,7 @@ class Referencer extends OriginalReferencer { } } -export default function(ast, parserOptions) { +export default function analyzeScope(ast, parserOptions) { const options = { ignoreEval: true, optimistic: false, diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/convertAST.js b/eslint/babel-eslint-parser/src/babylon-to-espree/convertAST.js deleted file mode 100644 index 4e04866871..0000000000 --- a/eslint/babel-eslint-parser/src/babylon-to-espree/convertAST.js +++ /dev/null @@ -1,96 +0,0 @@ -import { types as t, traverse } from "@babel/core"; -import convertProgramNode from "./convertProgramNode"; - -const astTransformVisitor = { - noScope: true, - enter(path) { - const node = path.node; - - // private var to track original node type - node._babelType = node.type; - - if (node.innerComments) { - delete node.innerComments; - } - - if (node.trailingComments) { - delete node.trailingComments; - } - - if (node.leadingComments) { - delete node.leadingComments; - } - }, - exit(path) { - const node = path.node; - - if (path.isTypeParameter()) { - node.type = "Identifier"; - node.typeAnnotation = node.bound; - delete node.bound; - } - - // flow: prevent "no-undef" - // for "Component" in: "let x: React.Component" - if (path.isQualifiedTypeIdentifier()) { - delete node.id; - } - // for "b" in: "var a: { b: Foo }" - if (path.isObjectTypeProperty()) { - delete node.key; - } - // for "indexer" in: "var a: {[indexer: string]: number}" - if (path.isObjectTypeIndexer()) { - delete node.id; - } - // for "param" in: "var a: { func(param: Foo): Bar };" - if (path.isFunctionTypeParam()) { - delete node.name; - } - - // modules - if (path.isImportDeclaration()) { - delete node.isType; - } - - // template string range fixes - if (path.isTemplateLiteral()) { - for (let i = 0; i < node.quasis.length; i++) { - const q = node.quasis[i]; - q.range[0] -= 1; - if (q.tail) { - q.range[1] += 1; - } else { - q.range[1] += 2; - } - q.loc.start.column -= 1; - if (q.tail) { - q.loc.end.column += 1; - } else { - q.loc.end.column += 2; - } - } - } - }, -}; - -export default function(ast, code) { - const state = { source: code }; - - // Monkey patch visitor keys in order to be able to traverse the estree nodes - t.VISITOR_KEYS.Property = t.VISITOR_KEYS.ObjectProperty; - t.VISITOR_KEYS.MethodDefinition = [ - "key", - "value", - "decorators", - "returnType", - "typeParameters", - ]; - - traverse(ast, astTransformVisitor, null, state); - - delete t.VISITOR_KEYS.Property; - delete t.VISITOR_KEYS.MethodDefinition; - - convertProgramNode(ast); -} diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/convertProgramNode.js b/eslint/babel-eslint-parser/src/babylon-to-espree/convertProgramNode.js deleted file mode 100644 index a435487cb6..0000000000 --- a/eslint/babel-eslint-parser/src/babylon-to-espree/convertProgramNode.js +++ /dev/null @@ -1,38 +0,0 @@ -export default function(ast) { - ast.type = "Program"; - ast.sourceType = ast.program.sourceType; - ast.directives = ast.program.directives; - ast.body = ast.program.body; - delete ast.program; - - if (ast.comments.length) { - const lastComment = ast.comments[ast.comments.length - 1]; - - if (!ast.tokens.length) { - // if no tokens, the program starts at the end of the last comment - ast.start = lastComment.end; - ast.loc.start.line = lastComment.loc.end.line; - ast.loc.start.column = lastComment.loc.end.column; - } else { - const lastToken = ast.tokens[ast.tokens.length - 1]; - - if (lastComment.end > lastToken.end) { - // If there is a comment after the last token, the program ends at the - // last token and not the comment - ast.range[1] = lastToken.end; - ast.loc.end.line = lastToken.loc.end.line; - ast.loc.end.column = lastToken.loc.end.column; - } - } - } else { - if (!ast.tokens.length) { - ast.loc.start.line = 1; - ast.loc.end.line = 1; - } - } - - if (ast.body && ast.body.length > 0) { - ast.loc.start.line = ast.body[0].loc.start.line; - ast.range[0] = ast.body[0].start; - } -} diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/convertTemplateType.js b/eslint/babel-eslint-parser/src/babylon-to-espree/convertTemplateType.js deleted file mode 100644 index 3b674adae6..0000000000 --- a/eslint/babel-eslint-parser/src/babylon-to-espree/convertTemplateType.js +++ /dev/null @@ -1,92 +0,0 @@ -import { tokTypes as tt } from "@babel/core"; - -export default function(tokens) { - let curlyBrace = null; - let templateTokens = []; - const result = []; - - function addTemplateType() { - const start = templateTokens[0]; - const end = templateTokens[templateTokens.length - 1]; - - const value = templateTokens.reduce((result, token) => { - if (token.value) { - result += token.value; - } else if (token.type !== tt.template) { - result += token.type.label; - } - - return result; - }, ""); - - result.push({ - type: "Template", - value: value, - start: start.start, - end: end.end, - loc: { - start: start.loc.start, - end: end.loc.end, - }, - }); - - templateTokens = []; - } - - tokens.forEach(token => { - switch (token.type) { - case tt.backQuote: - if (curlyBrace) { - result.push(curlyBrace); - curlyBrace = null; - } - - templateTokens.push(token); - - if (templateTokens.length > 1) { - addTemplateType(); - } - - break; - - case tt.dollarBraceL: - templateTokens.push(token); - addTemplateType(); - break; - - case tt.braceR: - if (curlyBrace) { - result.push(curlyBrace); - } - - curlyBrace = token; - break; - - case tt.template: - if (curlyBrace) { - templateTokens.push(curlyBrace); - curlyBrace = null; - } - - templateTokens.push(token); - break; - - case tt.eof: - if (curlyBrace) { - result.push(curlyBrace); - } - - break; - - default: - if (curlyBrace) { - result.push(curlyBrace); - curlyBrace = null; - } - - result.push(token); - } - }); - - return result; -} diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/convertTokens.js b/eslint/babel-eslint-parser/src/babylon-to-espree/convertTokens.js deleted file mode 100644 index 44f4ef41bc..0000000000 --- a/eslint/babel-eslint-parser/src/babylon-to-espree/convertTokens.js +++ /dev/null @@ -1,8 +0,0 @@ -import convertTemplateType from "./convertTemplateType"; -import convertToken from "./convertToken"; - -export default function(tokens, code) { - return convertTemplateType(tokens) - .filter(t => t.type !== "CommentLine" && t.type !== "CommentBlock") - .map(t => convertToken(t, code)); -} diff --git a/eslint/babel-eslint-parser/src/convert/convertAST.js b/eslint/babel-eslint-parser/src/convert/convertAST.js new file mode 100644 index 0000000000..7dfcd9a6df --- /dev/null +++ b/eslint/babel-eslint-parser/src/convert/convertAST.js @@ -0,0 +1,137 @@ +import { types as t, traverse } from "@babel/core"; + +function convertNodes(ast, code) { + const state = { source: code }; + const astTransformVisitor = { + noScope: true, + enter(path) { + const node = path.node; + + // private var to track original node type + node._babelType = node.type; + + if (node.innerComments) { + delete node.innerComments; + } + + if (node.trailingComments) { + delete node.trailingComments; + } + + if (node.leadingComments) { + delete node.leadingComments; + } + }, + exit(path) { + const node = path.node; + + if (path.isTypeParameter()) { + node.type = "Identifier"; + node.typeAnnotation = node.bound; + delete node.bound; + } + + // flow: prevent "no-undef" + // for "Component" in: "let x: React.Component" + if (path.isQualifiedTypeIdentifier()) { + delete node.id; + } + // for "b" in: "var a: { b: Foo }" + if (path.isObjectTypeProperty()) { + delete node.key; + } + // for "indexer" in: "var a: {[indexer: string]: number}" + if (path.isObjectTypeIndexer()) { + delete node.id; + } + // for "param" in: "var a: { func(param: Foo): Bar };" + if (path.isFunctionTypeParam()) { + delete node.name; + } + + // modules + if (path.isImportDeclaration()) { + delete node.isType; + } + + // template string range fixes + if (path.isTemplateLiteral()) { + for (let i = 0; i < node.quasis.length; i++) { + const q = node.quasis[i]; + q.range[0] -= 1; + if (q.tail) { + q.range[1] += 1; + } else { + q.range[1] += 2; + } + q.loc.start.column -= 1; + if (q.tail) { + q.loc.end.column += 1; + } else { + q.loc.end.column += 2; + } + } + } + }, + }; + + // Monkey patch visitor keys in order to be able to traverse the estree nodes + t.VISITOR_KEYS.Property = t.VISITOR_KEYS.ObjectProperty; + t.VISITOR_KEYS.MethodDefinition = [ + "key", + "value", + "decorators", + "returnType", + "typeParameters", + ]; + + traverse(ast, astTransformVisitor, null, state); + + // These can be safely deleted because they are not defined in the original visitor keys. + delete t.VISITOR_KEYS.Property; + delete t.VISITOR_KEYS.MethodDefinition; +} + +function convertProgramNode(ast) { + ast.type = "Program"; + ast.sourceType = ast.program.sourceType; + ast.directives = ast.program.directives; + ast.body = ast.program.body; + delete ast.program; + + if (ast.comments.length) { + const lastComment = ast.comments[ast.comments.length - 1]; + + if (!ast.tokens.length) { + // if no tokens, the program starts at the end of the last comment + ast.start = lastComment.end; + ast.loc.start.line = lastComment.loc.end.line; + ast.loc.start.column = lastComment.loc.end.column; + } else { + const lastToken = ast.tokens[ast.tokens.length - 1]; + + if (lastComment.end > lastToken.end) { + // If there is a comment after the last token, the program ends at the + // last token and not the comment + ast.range[1] = lastToken.end; + ast.loc.end.line = lastToken.loc.end.line; + ast.loc.end.column = lastToken.loc.end.column; + } + } + } else { + if (!ast.tokens.length) { + ast.loc.start.line = 1; + ast.loc.end.line = 1; + } + } + + if (ast.body && ast.body.length > 0) { + ast.loc.start.line = ast.body[0].loc.start.line; + ast.range[0] = ast.body[0].start; + } +} + +export default function convertAST(ast, code) { + convertNodes(ast, code); + convertProgramNode(ast); +} diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/convertComments.js b/eslint/babel-eslint-parser/src/convert/convertComments.js similarity index 88% rename from eslint/babel-eslint-parser/src/babylon-to-espree/convertComments.js rename to eslint/babel-eslint-parser/src/convert/convertComments.js index b52ec9ff5a..3a675a3385 100644 --- a/eslint/babel-eslint-parser/src/babylon-to-espree/convertComments.js +++ b/eslint/babel-eslint-parser/src/convert/convertComments.js @@ -1,4 +1,4 @@ -export default function(comments) { +export default function convertComments(comments) { for (let i = 0; i < comments.length; i++) { const comment = comments[i]; if (comment.type === "CommentBlock") { diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/convertToken.js b/eslint/babel-eslint-parser/src/convert/convertTokens.js similarity index 55% rename from eslint/babel-eslint-parser/src/babylon-to-espree/convertToken.js rename to eslint/babel-eslint-parser/src/convert/convertTokens.js index d8c9f09092..402f02ebe6 100644 --- a/eslint/babel-eslint-parser/src/babylon-to-espree/convertToken.js +++ b/eslint/babel-eslint-parser/src/convert/convertTokens.js @@ -1,6 +1,97 @@ import { tokTypes as tt } from "@babel/core"; -export default function(token, source) { +function convertTemplateType(tokens) { + let curlyBrace = null; + let templateTokens = []; + const result = []; + + function addTemplateType() { + const start = templateTokens[0]; + const end = templateTokens[templateTokens.length - 1]; + + const value = templateTokens.reduce((result, token) => { + if (token.value) { + result += token.value; + } else if (token.type !== tt.template) { + result += token.type.label; + } + + return result; + }, ""); + + result.push({ + type: "Template", + value: value, + start: start.start, + end: end.end, + loc: { + start: start.loc.start, + end: end.loc.end, + }, + }); + + templateTokens = []; + } + + tokens.forEach(token => { + switch (token.type) { + case tt.backQuote: + if (curlyBrace) { + result.push(curlyBrace); + curlyBrace = null; + } + + templateTokens.push(token); + + if (templateTokens.length > 1) { + addTemplateType(); + } + + break; + + case tt.dollarBraceL: + templateTokens.push(token); + addTemplateType(); + break; + + case tt.braceR: + if (curlyBrace) { + result.push(curlyBrace); + } + + curlyBrace = token; + break; + + case tt.template: + if (curlyBrace) { + templateTokens.push(curlyBrace); + curlyBrace = null; + } + + templateTokens.push(token); + break; + + case tt.eof: + if (curlyBrace) { + result.push(curlyBrace); + } + + break; + + default: + if (curlyBrace) { + result.push(curlyBrace); + curlyBrace = null; + } + + result.push(token); + } + }); + + return result; +} + +function convertToken(token, source) { const type = token.type; token.range = [token.start, token.end]; @@ -86,3 +177,9 @@ export default function(token, source) { return token; } + +export default function convertTokens(tokens, code) { + return convertTemplateType(tokens) + .filter(t => t.type !== "CommentLine" && t.type !== "CommentBlock") + .map(t => convertToken(t, code)); +} diff --git a/eslint/babel-eslint-parser/src/babylon-to-espree/index.js b/eslint/babel-eslint-parser/src/convert/index.js similarity index 100% rename from eslint/babel-eslint-parser/src/babylon-to-espree/index.js rename to eslint/babel-eslint-parser/src/convert/index.js diff --git a/eslint/babel-eslint-parser/src/parse-with-scope.js b/eslint/babel-eslint-parser/src/parse-with-scope.js index 17516f5724..9d29b67f10 100644 --- a/eslint/babel-eslint-parser/src/parse-with-scope.js +++ b/eslint/babel-eslint-parser/src/parse-with-scope.js @@ -2,7 +2,7 @@ import visitorKeys from "./visitor-keys"; import analyzeScope from "./analyze-scope"; import parse from "./parse"; -export default function(code, options) { +export default function parseWithScope(code, options) { const ast = parse(code, options); const scopeManager = analyzeScope(ast, options); diff --git a/eslint/babel-eslint-parser/src/parse.js b/eslint/babel-eslint-parser/src/parse.js index e39f371295..9410fcba01 100644 --- a/eslint/babel-eslint-parser/src/parse.js +++ b/eslint/babel-eslint-parser/src/parse.js @@ -1,5 +1,5 @@ import { parseSync as babelParse } from "@babel/core"; -import babylonToEspree from "./babylon-to-espree"; +import convert from "./convert"; import { normalizeBabelParseConfig } from "./configuration"; export default function parse(code, options) { @@ -17,7 +17,7 @@ export default function parse(code, options) { throw err; } - babylonToEspree(ast, code); + convert(ast, code); return ast; }