diff --git a/lib/convert.js b/lib/convert.js index 00eb207..ab37a67 100644 --- a/lib/convert.js +++ b/lib/convert.js @@ -58,6 +58,9 @@ module.exports = function convert(config) { loc: nodeUtils.getLoc(node, ast) }; + nodeUtils.map.set(result, node); + nodeUtils.reverseMap.set(node, result); + /** * Copies the result object into an ESTree node with just a type property. * This is used only for leaf nodes that have no other properties. diff --git a/lib/node-utils.js b/lib/node-utils.js index 928ff14..d91adc1 100644 --- a/lib/node-utils.js +++ b/lib/node-utils.js @@ -98,6 +98,12 @@ TOKEN_TO_TEXT[SyntaxKind.CaretEqualsToken] = "^="; TOKEN_TO_TEXT[SyntaxKind.AtToken] = "@"; TOKEN_TO_TEXT[SyntaxKind.InKeyword] = "in"; +// 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. * @param {TSNode} node The current TSNode @@ -155,6 +161,8 @@ module.exports = { * Expose the enum of possible TSNode `kind`s. */ SyntaxKind, + map, + reverseMap, isAssignmentOperator, isLogicalOperator, getTextForTokenKind, @@ -553,6 +561,9 @@ function fixExports(node, result, ast) { loc: getLocFor(exportKeyword.getStart(), result.range[1], ast) }; + map.set(newResult, node); + reverseMap.set(node, newResult); + if (!declarationIsDefault) { newResult.specifiers = []; newResult.source = null; diff --git a/parser.js b/parser.js index da4e0c5..7a7a54a 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,28 @@ 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 = result.program.getTypeChecker(); + return { + program: result.program, + getTSNode: node => nodeUtils.map.get(node), + getESNode: tsNode => nodeUtils.reverseMap.get(tsNode), + typeChecker, + getType(node) { + return typeChecker.getTypeAtLocation(nodeUtils.map.get(node)); + } + }; } //------------------------------------------------------------------------------ @@ -186,12 +210,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.