From b92dfa71ee759284f504981631fb3002eecb0050 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 16 Dec 2017 14:21:10 -0500 Subject: [PATCH 1/6] New: Add some parserServices --- lib/convert.js | 3 ++- lib/node-utils.js | 16 +++++++++++++++- parser.js | 38 ++++++++++++++++++++++++++++++++------ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/lib/convert.js b/lib/convert.js index 00eb207..8915caf 100644 --- a/lib/convert.js +++ b/lib/convert.js @@ -55,7 +55,8 @@ module.exports = function convert(config) { let result = { type: "", range: [node.getStart(), node.end], - loc: nodeUtils.getLoc(node, ast) + loc: nodeUtils.getLoc(node, ast), + [nodeUtils.originalNodeSymbol]: node }; /** diff --git a/lib/node-utils.js b/lib/node-utils.js index 928ff14..5184178 100644 --- a/lib/node-utils.js +++ b/lib/node-utils.js @@ -98,6 +98,8 @@ TOKEN_TO_TEXT[SyntaxKind.CaretEqualsToken] = "^="; TOKEN_TO_TEXT[SyntaxKind.AtToken] = "@"; TOKEN_TO_TEXT[SyntaxKind.InKeyword] = "in"; +const originalNodeSymbol = Symbol("Original TypeScript AST node"); + /** * Find the first matching child based on the given sourceFile and predicate function. * @param {TSNode} node The current TSNode @@ -155,6 +157,8 @@ module.exports = { * Expose the enum of possible TSNode `kind`s. */ SyntaxKind, + originalNodeSymbol, + getTSNode, isAssignmentOperator, isLogicalOperator, getTextForTokenKind, @@ -189,6 +193,15 @@ module.exports = { }; /* eslint-enable no-use-before-define */ +/** + * Get the TSNode associated with this ESTree node + * @param {ASTNode} node the ESTree node + * @returns {?TSNode} the TypeScript node + */ +function getTSNode(node) { + return node[originalNodeSymbol]; +} + /** * Returns true if the given TSToken is the assignment operator * @param {TSToken} operator the operator token @@ -550,7 +563,8 @@ function fixExports(node, result, ast) { type: declarationType, declaration: result, range: [exportKeyword.getStart(), result.range[1]], - loc: getLocFor(exportKeyword.getStart(), result.range[1], ast) + loc: getLocFor(exportKeyword.getStart(), result.range[1], ast), + [originalNodeSymbol]: node }; if (!declarationIsDefault) { diff --git a/parser.js b/parser.js index da4e0c5..7160d51 100644 --- a/parser.js +++ b/parser.js @@ -11,7 +11,8 @@ const astNodeTypes = require("./lib/ast-node-types"), ts = require("typescript"), convert = require("./lib/ast-converter"), - semver = require("semver"); + semver = require("semver"), + nodeUtils = require("./lib/node-utils"); const SUPPORTED_TYPESCRIPT_VERSIONS = require("./package.json").devDependencies.typescript; const ACTIVE_TYPESCRIPT_VERSION = ts.version; @@ -44,12 +45,14 @@ function resetExtra() { // Parser //------------------------------------------------------------------------------ +/** @typedef {{ ast: Program, program: ts.Program }} Result */ + /** * Parses the given source code to produce a valid AST * @param {mixed} code TypeScript code * @param {Object} options configuration object for the parser * @param {Object} additionalParsingContext additional internal configuration - * @returns {Object} the AST + * @returns {Result} the result */ function generateAST(code, options, additionalParsingContext) { additionalParsingContext = additionalParsingContext || {}; @@ -176,7 +179,27 @@ function generateAST(code, options, additionalParsingContext) { const ast = program.getSourceFile(FILENAME); extra.code = code; - return convert(ast, extra); + return { + ast: convert(ast, extra), + program + }; +} + +/** + * Generate the `parserServices` object for a parse result + * @param {Result} result The return value of `generateAST` + * @returns {Object} The `parserServices` object + */ +function getServices(result) { + const typeChecker = this.program.getTypeChecker(); + return { + program: result.program, + getTSNode: nodeUtils.getTSNode, + typeChecker + getType(node) { + return typeChecker.getTypeAtLocation(nodeUtils.getTSNode(node)); + } + }; } //------------------------------------------------------------------------------ @@ -186,12 +209,15 @@ function generateAST(code, options, additionalParsingContext) { exports.version = require("./package.json").version; exports.parse = function parse(code, options) { - return generateAST(code, options, { isParseForESLint: false }); + return generateAST(code, options, { isParseForESLint: false }).ast; }; exports.parseForESLint = function parseForESLint(code, options) { - const ast = generateAST(code, options, { isParseForESLint: true }); - return { ast }; + const result = generateAST(code, options, { isParseForESLint: true }); + return { + ast: result.ast, + services: getServices(result) + }; }; // Deep copy. From 437f5c12b999eba0043e8fa6083be2409a4d6c4f Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 16 Dec 2017 14:37:47 -0500 Subject: [PATCH 2/6] Commas exist for a reason --- parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.js b/parser.js index 7160d51..88a6f58 100644 --- a/parser.js +++ b/parser.js @@ -195,7 +195,7 @@ function getServices(result) { return { program: result.program, getTSNode: nodeUtils.getTSNode, - typeChecker + typeChecker, getType(node) { return typeChecker.getTypeAtLocation(nodeUtils.getTSNode(node)); } From e77984789ed91359d8cfe30b971e0b33354ff6a9 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 16 Dec 2017 14:41:50 -0500 Subject: [PATCH 3/6] This is not the result --- parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.js b/parser.js index 88a6f58..4945dbf 100644 --- a/parser.js +++ b/parser.js @@ -191,7 +191,7 @@ function generateAST(code, options, additionalParsingContext) { * @returns {Object} The `parserServices` object */ function getServices(result) { - const typeChecker = this.program.getTypeChecker(); + const typeChecker = result.program.getTypeChecker(); return { program: result.program, getTSNode: nodeUtils.getTSNode, From 3298aeca0646b5b4ffbcf8d2179cdfa5405c5e58 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 16 Dec 2017 16:55:54 -0500 Subject: [PATCH 4/6] =?UTF-8?q?Make=20the=20original=20node=20non-enumerab?= =?UTF-8?q?le=20so=20it=20doesn=E2=80=99t=20get=20logged?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/convert.js | 7 +++++-- lib/node-utils.js | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/convert.js b/lib/convert.js index 8915caf..90dfb23 100644 --- a/lib/convert.js +++ b/lib/convert.js @@ -55,9 +55,12 @@ module.exports = function convert(config) { let result = { type: "", range: [node.getStart(), node.end], - loc: nodeUtils.getLoc(node, ast), - [nodeUtils.originalNodeSymbol]: node + loc: nodeUtils.getLoc(node, ast) }; + Object.defineProperty(result, nodeUtils.originalNodeSymbol, { + value: node, + enumerable: false + }); /** * Copies the result object into an ESTree node with just a type property. diff --git a/lib/node-utils.js b/lib/node-utils.js index 5184178..cc6c07d 100644 --- a/lib/node-utils.js +++ b/lib/node-utils.js @@ -563,9 +563,12 @@ function fixExports(node, result, ast) { type: declarationType, declaration: result, range: [exportKeyword.getStart(), result.range[1]], - loc: getLocFor(exportKeyword.getStart(), result.range[1], ast), - [originalNodeSymbol]: node + loc: getLocFor(exportKeyword.getStart(), result.range[1], ast) }; + Object.defineProperty(newResult, originalNodeSymbol, { + value: node, + enumerable: false + }); if (!declarationIsDefault) { newResult.specifiers = []; From 300f1ca03d38a50320e63cc96653a7c01056a595 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Fri, 5 Jan 2018 08:35:52 -0500 Subject: [PATCH 5/6] Use WeakMaps to map between nodes --- lib/convert.js | 7 +++---- lib/node-utils.js | 26 ++++++++++---------------- parser.js | 5 +++-- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/lib/convert.js b/lib/convert.js index 90dfb23..ab37a67 100644 --- a/lib/convert.js +++ b/lib/convert.js @@ -57,10 +57,9 @@ module.exports = function convert(config) { range: [node.getStart(), node.end], loc: nodeUtils.getLoc(node, ast) }; - Object.defineProperty(result, nodeUtils.originalNodeSymbol, { - value: node, - enumerable: false - }); + + nodeUtils.map.set(result, node); + nodeUtils.reverseMap.set(node, result); /** * Copies the result object into an ESTree node with just a type property. diff --git a/lib/node-utils.js b/lib/node-utils.js index cc6c07d..d91adc1 100644 --- a/lib/node-utils.js +++ b/lib/node-utils.js @@ -98,7 +98,11 @@ TOKEN_TO_TEXT[SyntaxKind.CaretEqualsToken] = "^="; TOKEN_TO_TEXT[SyntaxKind.AtToken] = "@"; TOKEN_TO_TEXT[SyntaxKind.InKeyword] = "in"; -const originalNodeSymbol = Symbol("Original TypeScript AST node"); +// ASTNode => TSNode +const map = new WeakMap(); + +// TSNode => ASTNode +const reverseMap = new WeakMap(); /** * Find the first matching child based on the given sourceFile and predicate function. @@ -157,8 +161,8 @@ module.exports = { * Expose the enum of possible TSNode `kind`s. */ SyntaxKind, - originalNodeSymbol, - getTSNode, + map, + reverseMap, isAssignmentOperator, isLogicalOperator, getTextForTokenKind, @@ -193,15 +197,6 @@ module.exports = { }; /* eslint-enable no-use-before-define */ -/** - * Get the TSNode associated with this ESTree node - * @param {ASTNode} node the ESTree node - * @returns {?TSNode} the TypeScript node - */ -function getTSNode(node) { - return node[originalNodeSymbol]; -} - /** * Returns true if the given TSToken is the assignment operator * @param {TSToken} operator the operator token @@ -565,10 +560,9 @@ function fixExports(node, result, ast) { range: [exportKeyword.getStart(), result.range[1]], loc: getLocFor(exportKeyword.getStart(), result.range[1], ast) }; - Object.defineProperty(newResult, originalNodeSymbol, { - value: node, - enumerable: false - }); + + map.set(newResult, node); + reverseMap.set(node, newResult); if (!declarationIsDefault) { newResult.specifiers = []; diff --git a/parser.js b/parser.js index 4945dbf..909faa9 100644 --- a/parser.js +++ b/parser.js @@ -194,10 +194,11 @@ function getServices(result) { const typeChecker = result.program.getTypeChecker(); return { program: result.program, - getTSNode: nodeUtils.getTSNode, + getTSNode: node => nodeUtils.map.get(node), + getESNode: tsNode => nodeUtils.reverseMap.get(tsNode), typeChecker, getType(node) { - return typeChecker.getTypeAtLocation(nodeUtils.getTSNode(node)); + return typeChecker.getTypeAtLocation(nodeUils.map.get(node)); } }; } From 6b2777f59033ebc011bf0adc3cdf2ad2befd1c60 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Fri, 5 Jan 2018 12:05:01 -0500 Subject: [PATCH 6/6] typo --- parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.js b/parser.js index 909faa9..7a7a54a 100644 --- a/parser.js +++ b/parser.js @@ -198,7 +198,7 @@ function getServices(result) { getESNode: tsNode => nodeUtils.reverseMap.get(tsNode), typeChecker, getType(node) { - return typeChecker.getTypeAtLocation(nodeUils.map.get(node)); + return typeChecker.getTypeAtLocation(nodeUtils.map.get(node)); } }; }