diff --git a/.gitignore b/.gitignore index 2ff4964..557d485 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ node_modules npm-debug.log _test.js .DS_Store -.vscode \ No newline at end of file +.vscode +dist \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 8a20fa6..9763bdf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ script: - commitlint-travis - yarn check-format - yarn test + - yarn build after_success: - npm run travis-deploy-once "npm run semantic-release" branches: diff --git a/README.md b/README.md index 42184b4..7b39ea0 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ ## About -This parser is somewhat generic and robust, and could be used to power any use-case which requires taking TypeScript source code and producing an ESTree-compatiable AST. +This parser is somewhat generic and robust, and could be used to power any use-case which requires taking TypeScript source code and producing an ESTree-compatible AST. In fact, it is already used within these hyper-popular open-source projects to power their TypeScript support: diff --git a/package.json b/package.json index 7bdc8e0..93f53d6 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,12 @@ "name": "typescript-estree", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "homepage": "https://github.com/JamesHenry/typescript-estree", - "main": "parser.js", + "main": "dist/parser.js", "version": "0.0.0-development", "files": [ - "lib", - "parser.js" + "dist", + "README.md", + "LICENSE" ], "engines": { "node": ">=6.14.0" @@ -20,6 +21,12 @@ "@commitlint/cli": "^7.1.2", "@commitlint/config-conventional": "^7.1.2", "@commitlint/travis-cli": "^7.1.2", + "@types/babel-code-frame": "^6.20.1", + "@types/jest": "^23.3.9", + "@types/lodash.isplainobject": "^4.0.4", + "@types/lodash.unescape": "^4.0.4", + "@types/semver": "^5.5.0", + "@types/shelljs": "^0.8.0", "babel-code-frame": "6.26.0", "babylon": "7.0.0-beta.39", "cz-conventional-changelog": "2.1.0", @@ -32,6 +39,7 @@ "semantic-release": "^15.9.16", "shelljs": "0.8.2", "travis-deploy-once": "^5.0.8", + "ts-jest": "^23.10.4", "typescript": "~3.1.1" }, "keywords": [ @@ -44,6 +52,7 @@ "syntax" ], "scripts": { + "build": "tsc", "test": "npm run unit-tests && npm run ast-alignment-tests", "unit-tests": "jest", "ast-alignment-tests": "jest --config=./tests/ast-alignment/jest.config.js", @@ -79,8 +88,18 @@ }, "jest": { "testEnvironment": "node", - "testRegex": "tests/lib/.+\\.js$", - "testPathIgnorePatterns": [], + "transform": { + "^.+\\.tsx?$": "ts-jest" + }, + "testRegex": "(/tests/lib/.*)\\.(jsx?|tsx?)$", + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "jsx", + "json", + "node" + ], "collectCoverage": true, "coverageReporters": [ "text-summary" diff --git a/lib/ast-converter.js b/src/ast-converter.ts similarity index 62% rename from lib/ast-converter.js rename to src/ast-converter.ts index 924a98e..f46ca74 100644 --- a/lib/ast-converter.js +++ b/src/ast-converter.ts @@ -5,27 +5,17 @@ * @copyright jQuery Foundation and other contributors, https://jquery.org/ * MIT License */ - -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const convert = require('./convert'), - convertComments = require('./convert-comments').convertComments, - nodeUtils = require('./node-utils'); - -//------------------------------------------------------------------------------ -// Private -//------------------------------------------------------------------------------ +import { convert } from './convert'; +import { convertComments } from './convert-comments'; +import nodeUtils from './node-utils'; +import { Extra } from './temp-types-based-on-js-source'; /** * Extends and formats a given error object * @param {Object} error the error object * @returns {Object} converted error object */ -function convertError(error) { +function convertError(error: any) { return nodeUtils.createError( error.file, error.start, @@ -33,11 +23,7 @@ function convertError(error) { ); } -//------------------------------------------------------------------------------ -// Public -//------------------------------------------------------------------------------ - -module.exports = (ast, extra) => { +export default (ast: any, extra: Extra) => { /** * The TypeScript compiler produced fundamental parse errors when parsing the * source. @@ -49,7 +35,7 @@ module.exports = (ast, extra) => { /** * Recursively convert the TypeScript AST into an ESTree-compatible AST */ - const estree = convert({ + const estree: any = convert({ node: ast, parent: null, ast, diff --git a/lib/ast-node-types.js b/src/ast-node-types.ts similarity index 93% rename from lib/ast-node-types.js rename to src/ast-node-types.ts index 162a895..ea3ce8d 100644 --- a/lib/ast-node-types.js +++ b/src/ast-node-types.ts @@ -5,20 +5,7 @@ * @copyright jQuery Foundation and other contributors, https://jquery.org/ * MIT License */ - -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -// None! - -//------------------------------------------------------------------------------ -// Public -//------------------------------------------------------------------------------ - -module.exports = { +export const AST_NODE_TYPES: { [key: string]: string } = { ArrayExpression: 'ArrayExpression', ArrayPattern: 'ArrayPattern', ArrowFunctionExpression: 'ArrowFunctionExpression', diff --git a/lib/convert-comments.js b/src/convert-comments.ts similarity index 68% rename from lib/convert-comments.js rename to src/convert-comments.ts index 728ca2c..25d4287 100644 --- a/lib/convert-comments.js +++ b/src/convert-comments.ts @@ -5,39 +5,34 @@ * MIT License */ -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const ts = require('typescript'), - nodeUtils = require('./node-utils'); - -//------------------------------------------------------------------------------ -// Private -//------------------------------------------------------------------------------ +import ts from 'typescript'; +import nodeUtils from './node-utils'; +import { + ESTreeComment, + ESTreeToken, + LineAndColumnData +} from './temp-types-based-on-js-source'; /** * Converts a TypeScript comment to an Esprima comment. * @param {boolean} block True if it's a block comment, false if not. * @param {string} text The text of the comment. - * @param {int} start The index at which the comment starts. - * @param {int} end The index at which the comment ends. - * @param {Location} startLoc The location at which the comment starts. - * @param {Location} endLoc The location at which the comment ends. + * @param {number} start The index at which the comment starts. + * @param {number} end The index at which the comment ends. + * @param {LineAndColumnData} startLoc The location at which the comment starts. + * @param {LineAndColumnData} endLoc The location at which the comment ends. * @returns {Object} The comment object. * @private */ function convertTypeScriptCommentToEsprimaComment( - block, - text, - start, - end, - startLoc, - endLoc -) { - const comment = { + block: boolean, + text: string, + start: number, + end: number, + startLoc: LineAndColumnData, + endLoc: LineAndColumnData +): ESTreeComment { + const comment: Partial = { type: block ? 'Block' : 'Line', value: text }; @@ -53,18 +48,22 @@ function convertTypeScriptCommentToEsprimaComment( }; } - return comment; + return comment as ESTreeComment; } /** * Convert comment from TypeScript Triva Scanner. - * @param {Object} triviaScanner TS Scanner - * @param {Object} ast the AST object + * @param {ts.Scanner} triviaScanner TS Scanner + * @param {ts.SourceFile} ast the AST object * @param {string} code TypeScript code * @returns {ESTreeComment} the converted ESTreeComment * @private */ -function getCommentFromTriviaScanner(triviaScanner, ast, code) { +function getCommentFromTriviaScanner( + triviaScanner: ts.Scanner, + ast: ts.SourceFile, + code: string +): ESTreeComment { const kind = triviaScanner.getToken(); const isBlock = kind === ts.SyntaxKind.MultiLineCommentTrivia; const range = { @@ -91,23 +90,18 @@ function getCommentFromTriviaScanner(triviaScanner, ast, code) { return esprimaComment; } -//------------------------------------------------------------------------------ -// Public -//------------------------------------------------------------------------------ - -module.exports = { - convertComments -}; - /** * Convert all comments for the given AST. - * @param {Object} ast the AST object + * @param {ts.SourceFile} ast the AST object * @param {string} code the TypeScript code * @returns {ESTreeComment[]} the converted ESTreeComment * @private */ -function convertComments(ast, code) { - const comments = []; +export function convertComments( + ast: ts.SourceFile, + code: string +): ESTreeComment[] { + const comments: ESTreeComment[] = []; /** * Create a TypeScript Scanner, with skipTrivia set to false so that @@ -120,7 +114,7 @@ function convertComments(ast, code) { const start = triviaScanner.getTokenPos(); const end = triviaScanner.getTextPos(); - let container = null; + let container: ts.Token | null = null; switch (kind) { case ts.SyntaxKind.SingleLineCommentTrivia: case ts.SyntaxKind.MultiLineCommentTrivia: { @@ -130,7 +124,7 @@ function convertComments(ast, code) { break; } case ts.SyntaxKind.CloseBraceToken: - container = nodeUtils.getNodeContainer(ast, start, end); + container = nodeUtils.getNodeContainer(ast, start, end) as ts.Node; if ( container.kind === ts.SyntaxKind.TemplateMiddle || @@ -142,7 +136,7 @@ function convertComments(ast, code) { break; case ts.SyntaxKind.SlashToken: case ts.SyntaxKind.SlashEqualsToken: - container = nodeUtils.getNodeContainer(ast, start, end); + container = nodeUtils.getNodeContainer(ast, start, end) as ts.Node; if (container.kind === ts.SyntaxKind.RegularExpressionLiteral) { kind = triviaScanner.reScanSlashToken(); diff --git a/lib/convert.js b/src/convert.ts similarity index 64% rename from lib/convert.js rename to src/convert.ts index fc49c3d..13ead9d 100644 --- a/lib/convert.js +++ b/src/convert.ts @@ -5,38 +5,24 @@ * @copyright jQuery Foundation and other contributors, https://jquery.org/ * MIT License */ +import ts from 'typescript'; +import nodeUtils from './node-utils'; +import { AST_NODE_TYPES } from './ast-node-types'; +import { ESTreeNode } from './temp-types-based-on-js-source'; -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const nodeUtils = require('./node-utils'), - AST_NODE_TYPES = require('./ast-node-types'); - -//------------------------------------------------------------------------------ -// Private -//------------------------------------------------------------------------------ - -const SyntaxKind = nodeUtils.SyntaxKind; - -//------------------------------------------------------------------------------ -// Public -//------------------------------------------------------------------------------ +const SyntaxKind = ts.SyntaxKind; /** * Converts a TypeScript node into an ESTree node * @param {Object} config configuration options for the conversion - * @param {TSNode} config.node the TSNode - * @param {TSNode} config.parent the parent TSNode - * @param {TSNode} config.ast the full TypeScript AST + * @param {ts.Node} config.node the ts.Node + * @param {ts.Node} config.parent the parent ts.Node + * @param {ts.SourceFile} config.ast the full TypeScript AST * @param {Object} config.additionalOptions additional options for the conversion - * @param {Object} config.additionalOptions.errorOnUnknownASTType whether whether or not to throw an error if an unknown AST Node Type is encountered - * @returns {ESTreeNode} the converted ESTreeNode + * @returns {ESTreeNode|null} the converted ESTreeNode */ -module.exports = function convert(config) { - const node = config.node; +export function convert(config: any): ESTreeNode | null { + const node = config.node as ts.Node; const parent = config.parent; const ast = config.ast; const additionalOptions = config.additionalOptions || {}; @@ -51,7 +37,7 @@ module.exports = function convert(config) { /** * Create a new ESTree node */ - let result = { + let result: Partial = { type: '', range: [node.getStart(), node.end], loc: nodeUtils.getLoc(node, ast) @@ -62,7 +48,7 @@ module.exports = function convert(config) { * This is used only for leaf nodes that have no other properties. * @returns {void} */ - function simplyCopy() { + function simplyCopy(): void { Object.assign(result, { type: SyntaxKind[node.kind] }); @@ -70,20 +56,20 @@ module.exports = function convert(config) { /** * Converts a TypeScript node into an ESTree node. - * @param {TSNode} child the child TSNode - * @returns {ESTreeNode} the converted ESTree node + * @param {ts.Node} child the child ts.Node + * @returns {ESTreeNode|null} the converted ESTree node */ - function convertChild(child) { + function convertChild(child: ts.Node): ESTreeNode | null { return convert({ node: child, parent: node, ast, additionalOptions }); } /** * Converts a child into a type annotation. This creates an intermediary * TypeAnnotation node to match what Flow does. - * @param {TSNode} child The TypeScript AST node to convert. + * @param {ts.Node} child The TypeScript AST node to convert. * @returns {ESTreeNode} The type annotation node. */ - function convertTypeAnnotation(child) { + function convertTypeAnnotation(child: ts.Node): ESTreeNode { const annotation = convertChild(child); const annotationStartCol = child.getFullStart() - 1; const loc = nodeUtils.getLocFor(annotationStartCol, child.end, ast); @@ -96,11 +82,13 @@ module.exports = function convert(config) { } /** - * Converts a TSNode's typeArguments array to a flow-like typeParameters node - * @param {TSNode[]} typeArguments TSNode typeArguments - * @returns {TypeParameterInstantiation} TypeParameterInstantiation node + * Converts a ts.Node's typeArguments ts.NodeArray to a flow-like typeParameters node + * @param {ts.NodeArray} typeArguments ts.Node typeArguments + * @returns {ESTreeNode} TypeParameterInstantiation node */ - function convertTypeArgumentsToTypeParameters(typeArguments) { + function convertTypeArgumentsToTypeParameters( + typeArguments: ts.NodeArray + ): ESTreeNode { /** * Even if typeArguments is an empty array, TypeScript sets a `pos` and `end` * property on the array object so we can safely read the values here @@ -158,11 +146,13 @@ module.exports = function convert(config) { } /** - * Converts a TSNode's typeParameters array to a flow-like TypeParameterDeclaration node - * @param {TSNode[]} typeParameters TSNode typeParameters - * @returns {TypeParameterDeclaration} TypeParameterDeclaration node + * Converts a ts.Node's typeParameters ts.ts.NodeArray to a flow-like TypeParameterDeclaration node + * @param {ts.NodeArray} typeParameters ts.Node typeParameters + * @returns {ESTreeNode} TypeParameterDeclaration node */ - function convertTSTypeParametersToTypeParametersDeclaration(typeParameters) { + function convertTSTypeParametersToTypeParametersDeclaration( + typeParameters: ts.NodeArray + ): ESTreeNode { const firstTypeParameter = typeParameters[0]; const lastTypeParameter = typeParameters[typeParameters.length - 1]; @@ -212,12 +202,12 @@ module.exports = function convert(config) { /** * Converts a child into a class implements node. This creates an intermediary * ClassImplements node to match what Flow does. - * @param {TSNode} child The TypeScript AST node to convert. + * @param {ts.Node} child The TypeScript AST node to convert. * @returns {ESTreeNode} The type annotation node. */ - function convertClassImplements(child) { - const id = convertChild(child.expression); - const classImplementsNode = { + function convertClassImplements(child: any): ESTreeNode { + const id = convertChild(child.expression) as ESTreeNode; + const classImplementsNode: ESTreeNode = { type: AST_NODE_TYPES.ClassImplements, loc: id.loc, range: id.range, @@ -233,12 +223,12 @@ module.exports = function convert(config) { /** * Converts a child into a interface heritage node. - * @param {TSNode} child The TypeScript AST node to convert. + * @param {ts.Node} child The TypeScript AST node to convert. * @returns {ESTreeNode} The type annotation node. */ - function convertInterfaceHeritageClause(child) { - const id = convertChild(child.expression); - const classImplementsNode = { + function convertInterfaceHeritageClause(child: any): ESTreeNode { + const id = convertChild(child.expression) as ESTreeNode; + const classImplementsNode: ESTreeNode = { type: AST_NODE_TYPES.TSInterfaceHeritage, loc: id.loc, range: id.range, @@ -254,11 +244,13 @@ module.exports = function convert(config) { } /** - * Converts an array of TSNode decorators into an array of ESTreeNode decorators - * @param {TSNode[]} decorators An array of TSNode decorators to be converted + * Converts a ts.NodeArray of ts.Decorators into an array of ESTreeNode decorators + * @param {ts.NodeArray} decorators A ts.NodeArray of ts.Decorators to be converted * @returns {ESTreeNode[]} an array of converted ESTreeNode decorators */ - function convertDecorators(decorators) { + function convertDecorators( + decorators: ts.NodeArray + ): ESTreeNode[] { if (!decorators || !decorators.length) { return []; } @@ -274,16 +266,16 @@ module.exports = function convert(config) { } /** - * Converts an array of TSNode parameters into an array of ESTreeNode params - * @param {TSNode[]} parameters An array of TSNode params to be converted + * Converts an array of ts.Node parameters into an array of ESTreeNode params + * @param {ts.Node[]} parameters An array of ts.Node params to be converted * @returns {ESTreeNode[]} an array of converted ESTreeNode params */ - function convertParameters(parameters) { + function convertParameters(parameters: ts.Node[]): ESTreeNode[] { if (!parameters || !parameters.length) { return []; } return parameters.map(param => { - const convertedParam = convertChild(param); + const convertedParam = convertChild(param) as ESTreeNode; if (!param.decorators || !param.decorators.length) { return convertedParam; } @@ -299,7 +291,7 @@ module.exports = function convert(config) { * property instead of a kind property. Recursively copies all children. * @returns {void} */ - function deeplyCopy() { + function deeplyCopy(): void { const customType = `TS${SyntaxKind[node.kind]}`; /** * If the "errorOnUnknownASTType" option is set to true, throw an error, @@ -321,31 +313,34 @@ module.exports = function convert(config) { ) .forEach(key => { if (key === 'type') { - result.typeAnnotation = node.type - ? convertTypeAnnotation(node.type) + result.typeAnnotation = (node as any).type + ? convertTypeAnnotation((node as any).type) : null; } else if (key === 'typeArguments') { - result.typeParameters = node.typeArguments - ? convertTypeArgumentsToTypeParameters(node.typeArguments) + result.typeParameters = (node as any).typeArguments + ? convertTypeArgumentsToTypeParameters((node as any).typeArguments) : null; } else if (key === 'typeParameters') { - result.typeParameters = node.typeParameters + result.typeParameters = (node as any).typeParameters ? convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters + (node as any).typeParameters ) : null; } else if (key === 'decorators') { - const decorators = convertDecorators(node.decorators); + const decorators = convertDecorators((node as any).decorators); if (decorators && decorators.length) { result.decorators = decorators; } } else { - if (Array.isArray(node[key])) { - result[key] = node[key].map(convertChild); - } else if (node[key] && typeof node[key] === 'object') { - result[key] = convertChild(node[key]); + if (Array.isArray((node as any)[key])) { + (result as any)[key] = (node as any)[key].map(convertChild); + } else if ( + (node as any)[key] && + typeof (node as any)[key] === 'object' + ) { + (result as any)[key] = convertChild((node as any)[key]); } else { - result[key] = node[key]; + (result as any)[key] = (node as any)[key]; } } }); @@ -353,28 +348,28 @@ module.exports = function convert(config) { /** * Converts a TypeScript JSX node.tagName into an ESTree node.name - * @param {Object} tagName the tagName object from a JSX TSNode - * @param {Object} ast the AST object + * @param {Object} tagName the tagName object from a JSX ts.Node * @returns {Object} the converted ESTree name object */ - function convertTypeScriptJSXTagNameToESTreeName(tagName) { + function convertTypeScriptJSXTagNameToESTreeName(tagName: any): any { const tagNameToken = nodeUtils.convertToken(tagName, ast); if (tagNameToken.type === AST_NODE_TYPES.JSXMemberExpression) { const isNestedMemberExpression = - node.tagName.expression.kind === SyntaxKind.PropertyAccessExpression; + (node as any).tagName.expression.kind === + SyntaxKind.PropertyAccessExpression; // Convert TSNode left and right objects into ESTreeNode object // and property objects - tagNameToken.object = convertChild(node.tagName.expression); - tagNameToken.property = convertChild(node.tagName.name); + tagNameToken.object = convertChild((node as any).tagName.expression); + tagNameToken.property = convertChild((node as any).tagName.name); // Assign the appropriate types tagNameToken.object.type = isNestedMemberExpression ? AST_NODE_TYPES.JSXMemberExpression : AST_NODE_TYPES.JSXIdentifier; tagNameToken.property.type = AST_NODE_TYPES.JSXIdentifier; - if (tagName.expression.kind === SyntaxKind.ThisKeyword) { + if ((tagName as any).expression.kind === SyntaxKind.ThisKeyword) { tagNameToken.object.name = 'this'; } } else { @@ -389,10 +384,10 @@ module.exports = function convert(config) { /** * Applies the given TS modifiers to the given result object. - * @param {TSNode[]} modifiers original TSNodes from the node.modifiers array + * @param {ts.Node[]} modifiers original ts.Nodes from the node.modifiers array * @returns {void} (the current result object will be mutated) */ - function applyModifiersToResult(modifiers) { + function applyModifiersToResult(modifiers: ts.Node[]): void { if (!modifiers || !modifiers.length) { return; } @@ -402,7 +397,7 @@ module.exports = function convert(config) { * to the result, we remove them from the array, so that they * are not handled twice. */ - const handledModifierIndices = {}; + const handledModifierIndices: { [key: number]: boolean } = {}; for (let i = 0; i < modifiers.length; i++) { const modifier = modifiers[i]; switch (modifier.kind) { @@ -445,8 +440,10 @@ module.exports = function convert(config) { * @param {ESTreeNode} typeAnnotationParent The node that will have its location data mutated * @returns {void} */ - function fixTypeAnnotationParentLocation(typeAnnotationParent) { - const end = node.type.getEnd(); + function fixTypeAnnotationParentLocation( + typeAnnotationParent: ESTreeNode + ): void { + const end = (node as any).type.getEnd(); typeAnnotationParent.range[1] = end; const loc = nodeUtils.getLocFor( typeAnnotationParent.range[0], @@ -465,40 +462,44 @@ module.exports = function convert(config) { Object.assign(result, { type: AST_NODE_TYPES.Program, body: [], - sourceType: node.externalModuleIndicator ? 'module' : 'script' + sourceType: (node as any).externalModuleIndicator ? 'module' : 'script' }); // filter out unknown nodes for now - node.statements.forEach(statement => { + (node as any).statements.forEach((statement: any) => { const convertedStatement = convertChild(statement); if (convertedStatement) { result.body.push(convertedStatement); } }); - result.range[1] = node.endOfFileToken.end; - result.loc = nodeUtils.getLocFor(node.getStart(), result.range[1], ast); + (result as any).range[1] = (node as any).endOfFileToken.end; + result.loc = nodeUtils.getLocFor( + node.getStart(), + (result as any).range[1], + ast + ); break; case SyntaxKind.Block: Object.assign(result, { type: AST_NODE_TYPES.BlockStatement, - body: node.statements.map(convertChild) + body: (node as any).statements.map(convertChild) }); break; case SyntaxKind.Identifier: Object.assign(result, { type: AST_NODE_TYPES.Identifier, - name: node.text + name: (node as any).text }); break; case SyntaxKind.WithStatement: Object.assign(result, { type: AST_NODE_TYPES.WithStatement, - object: convertChild(node.expression), - body: convertChild(node.statement) + object: convertChild((node as any).expression), + body: convertChild((node as any).statement) }); break; @@ -507,15 +508,15 @@ module.exports = function convert(config) { case SyntaxKind.ReturnStatement: Object.assign(result, { type: AST_NODE_TYPES.ReturnStatement, - argument: convertChild(node.expression) + argument: convertChild((node as any).expression) }); break; case SyntaxKind.LabeledStatement: Object.assign(result, { type: AST_NODE_TYPES.LabeledStatement, - label: convertChild(node.label), - body: convertChild(node.statement) + label: convertChild((node as any).label), + body: convertChild((node as any).statement) }); break; @@ -523,7 +524,7 @@ module.exports = function convert(config) { case SyntaxKind.ContinueStatement: Object.assign(result, { type: SyntaxKind[node.kind], - label: convertChild(node.label) + label: convertChild((node as any).label) }); break; @@ -532,17 +533,17 @@ module.exports = function convert(config) { case SyntaxKind.IfStatement: Object.assign(result, { type: AST_NODE_TYPES.IfStatement, - test: convertChild(node.expression), - consequent: convertChild(node.thenStatement), - alternate: convertChild(node.elseStatement) + test: convertChild((node as any).expression), + consequent: convertChild((node as any).thenStatement), + alternate: convertChild((node as any).elseStatement) }); break; case SyntaxKind.SwitchStatement: Object.assign(result, { type: AST_NODE_TYPES.SwitchStatement, - discriminant: convertChild(node.expression), - cases: node.caseBlock.clauses.map(convertChild) + discriminant: convertChild((node as any).expression), + cases: (node as any).caseBlock.clauses.map(convertChild) }); break; @@ -550,8 +551,8 @@ module.exports = function convert(config) { case SyntaxKind.DefaultClause: Object.assign(result, { type: AST_NODE_TYPES.SwitchCase, - test: convertChild(node.expression), - consequent: node.statements.map(convertChild) + test: convertChild((node as any).expression), + consequent: (node as any).statements.map(convertChild) }); break; @@ -560,7 +561,7 @@ module.exports = function convert(config) { case SyntaxKind.ThrowStatement: Object.assign(result, { type: AST_NODE_TYPES.ThrowStatement, - argument: convertChild(node.expression) + argument: convertChild((node as any).expression) }); break; @@ -568,23 +569,23 @@ module.exports = function convert(config) { Object.assign(result, { type: AST_NODE_TYPES.TryStatement, block: convert({ - node: node.tryBlock, + node: (node as any).tryBlock, parent: null, ast, additionalOptions }), - handler: convertChild(node.catchClause), - finalizer: convertChild(node.finallyBlock) + handler: convertChild((node as any).catchClause), + finalizer: convertChild((node as any).finallyBlock) }); break; case SyntaxKind.CatchClause: Object.assign(result, { type: AST_NODE_TYPES.CatchClause, - param: node.variableDeclaration - ? convertChild(node.variableDeclaration.name) + param: (node as any).variableDeclaration + ? convertChild((node as any).variableDeclaration.name) : null, - body: convertChild(node.block) + body: convertChild((node as any).block) }); break; @@ -593,8 +594,8 @@ module.exports = function convert(config) { case SyntaxKind.WhileStatement: Object.assign(result, { type: AST_NODE_TYPES.WhileStatement, - test: convertChild(node.expression), - body: convertChild(node.statement) + test: convertChild((node as any).expression), + body: convertChild((node as any).statement) }); break; @@ -605,32 +606,32 @@ module.exports = function convert(config) { case SyntaxKind.DoStatement: Object.assign(result, { type: AST_NODE_TYPES.DoWhileStatement, - test: convertChild(node.expression), - body: convertChild(node.statement) + test: convertChild((node as any).expression), + body: convertChild((node as any).statement) }); break; case SyntaxKind.ForStatement: Object.assign(result, { type: AST_NODE_TYPES.ForStatement, - init: convertChild(node.initializer), - test: convertChild(node.condition), - update: convertChild(node.incrementor), - body: convertChild(node.statement) + init: convertChild((node as any).initializer), + test: convertChild((node as any).condition), + update: convertChild((node as any).incrementor), + body: convertChild((node as any).statement) }); break; case SyntaxKind.ForInStatement: case SyntaxKind.ForOfStatement: { const isAwait = !!( - node.awaitModifier && - node.awaitModifier.kind === SyntaxKind.AwaitKeyword + (node as any).awaitModifier && + (node as any).awaitModifier.kind === SyntaxKind.AwaitKeyword ); Object.assign(result, { type: SyntaxKind[node.kind], - left: convertChild(node.initializer), - right: convertChild(node.expression), - body: convertChild(node.statement), + left: convertChild((node as any).initializer), + right: convertChild((node as any).expression), + body: convertChild((node as any).statement), await: isAwait }); break; @@ -653,28 +654,28 @@ module.exports = function convert(config) { Object.assign(result, { type: functionDeclarationType, - id: convertChild(node.name), - generator: !!node.asteriskToken, + id: convertChild((node as any).name), + generator: !!(node as any).asteriskToken, expression: false, async: nodeUtils.hasModifier(SyntaxKind.AsyncKeyword, node), - params: convertParameters(node.parameters), - body: convertChild(node.body) + params: convertParameters((node as any).parameters), + body: convertChild((node as any).body) }); // Process returnType - if (node.type) { - result.returnType = convertTypeAnnotation(node.type); + if ((node as any).type) { + (result as any).returnType = convertTypeAnnotation((node as any).type); } // Process typeParameters - if (node.typeParameters && node.typeParameters.length) { + if ((node as any).typeParameters && (node as any).typeParameters.length) { result.typeParameters = convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters + (node as any).typeParameters ); } // check for exports - result = nodeUtils.fixExports(node, result, ast); + result = nodeUtils.fixExports(node, result as any, ast); break; } @@ -682,17 +683,19 @@ module.exports = function convert(config) { case SyntaxKind.VariableDeclaration: { Object.assign(result, { type: AST_NODE_TYPES.VariableDeclarator, - id: convertChild(node.name), - init: convertChild(node.initializer) + id: convertChild((node as any).name), + init: convertChild((node as any).initializer) }); - if (node.exclamationToken) { - result.definite = true; + if ((node as any).exclamationToken) { + (result as any).definite = true; } - if (node.type) { - result.id.typeAnnotation = convertTypeAnnotation(node.type); - fixTypeAnnotationParentLocation(result.id); + if ((node as any).type) { + (result as any).id.typeAnnotation = convertTypeAnnotation( + (node as any).type + ); + fixTypeAnnotationParentLocation((result as any).id); } break; } @@ -700,19 +703,21 @@ module.exports = function convert(config) { case SyntaxKind.VariableStatement: Object.assign(result, { type: AST_NODE_TYPES.VariableDeclaration, - declarations: node.declarationList.declarations.map(convertChild), - kind: nodeUtils.getDeclarationKind(node.declarationList) + declarations: (node as any).declarationList.declarations.map( + convertChild + ), + kind: nodeUtils.getDeclarationKind((node as any).declarationList) }); // check for exports - result = nodeUtils.fixExports(node, result, ast); + result = nodeUtils.fixExports(node, result as any, ast); break; // mostly for for-of, for-in case SyntaxKind.VariableDeclarationList: Object.assign(result, { type: AST_NODE_TYPES.VariableDeclaration, - declarations: node.declarations.map(convertChild), + declarations: (node as any).declarations.map(convertChild), kind: nodeUtils.getDeclarationKind(node) }); break; @@ -722,7 +727,7 @@ module.exports = function convert(config) { case SyntaxKind.ExpressionStatement: Object.assign(result, { type: AST_NODE_TYPES.ExpressionStatement, - expression: convertChild(node.expression) + expression: convertChild((node as any).expression) }); break; @@ -747,15 +752,16 @@ module.exports = function convert(config) { if (node.parent.kind === SyntaxKind.CallExpression) { arrayIsInAssignment = false; } else if ( - nodeUtils.getBinaryExpressionType(arrayAssignNode.operatorToken) === - AST_NODE_TYPES.AssignmentExpression + nodeUtils.getBinaryExpressionType( + (arrayAssignNode as any).operatorToken + ) === AST_NODE_TYPES.AssignmentExpression ) { arrayIsInAssignment = nodeUtils.findChildOfKind( - arrayAssignNode.left, + (arrayAssignNode as any).left, SyntaxKind.ArrayLiteralExpression, ast - ) === node || arrayAssignNode.left === node; + ) === node || (arrayAssignNode as any).left === node; } else { arrayIsInAssignment = false; } @@ -765,12 +771,12 @@ module.exports = function convert(config) { if (arrayIsInAssignment || arrayIsInForOf || arrayIsInForIn) { Object.assign(result, { type: AST_NODE_TYPES.ArrayPattern, - elements: node.elements.map(convertChild) + elements: (node as any).elements.map(convertChild) }); } else { Object.assign(result, { type: AST_NODE_TYPES.ArrayExpression, - elements: node.elements.map(convertChild) + elements: (node as any).elements.map(convertChild) }); } break; @@ -786,21 +792,21 @@ module.exports = function convert(config) { const objectAssignNode = ancestorNode && ancestorNode.kind === SyntaxKind.BinaryExpression && - ancestorNode.operatorToken.kind === SyntaxKind.FirstAssignment + (ancestorNode as any).operatorToken.kind === SyntaxKind.FirstAssignment ? ancestorNode : null; let objectIsInAssignment = false; if (objectAssignNode) { - if (objectAssignNode.left === node) { + if ((objectAssignNode as any).left === node) { objectIsInAssignment = true; } else if (node.parent.kind === SyntaxKind.CallExpression) { objectIsInAssignment = false; } else { objectIsInAssignment = nodeUtils.findChildOfKind( - objectAssignNode.left, + (objectAssignNode as any).left, SyntaxKind.ObjectLiteralExpression, ast ) === node; @@ -811,12 +817,12 @@ module.exports = function convert(config) { if (objectIsInAssignment) { Object.assign(result, { type: AST_NODE_TYPES.ObjectPattern, - properties: node.properties.map(convertChild) + properties: (node as any).properties.map(convertChild) }); } else { Object.assign(result, { type: AST_NODE_TYPES.ObjectExpression, - properties: node.properties.map(convertChild) + properties: (node as any).properties.map(convertChild) }); } @@ -826,9 +832,9 @@ module.exports = function convert(config) { case SyntaxKind.PropertyAssignment: Object.assign(result, { type: AST_NODE_TYPES.Property, - key: convertChild(node.name), - value: convertChild(node.initializer), - computed: nodeUtils.isComputedProperty(node.name), + key: convertChild((node as any).name), + value: convertChild((node as any).initializer), + computed: nodeUtils.isComputedProperty((node as any).name), method: false, shorthand: false, kind: 'init' @@ -836,14 +842,14 @@ module.exports = function convert(config) { break; case SyntaxKind.ShorthandPropertyAssignment: { - if (node.objectAssignmentInitializer) { + if ((node as any).objectAssignmentInitializer) { Object.assign(result, { type: AST_NODE_TYPES.Property, - key: convertChild(node.name), + key: convertChild((node as any).name), value: { type: AST_NODE_TYPES.AssignmentPattern, - left: convertChild(node.name), - right: convertChild(node.objectAssignmentInitializer), + left: convertChild((node as any).name), + right: convertChild((node as any).objectAssignmentInitializer), loc: result.loc, range: result.range }, @@ -855,8 +861,8 @@ module.exports = function convert(config) { } else { Object.assign(result, { type: AST_NODE_TYPES.Property, - key: convertChild(node.name), - value: convertChild(node.initializer || node.name), + key: convertChild((node as any).name), + value: convertChild((node as any).initializer || (node as any).name), computed: false, method: false, shorthand: true, @@ -870,15 +876,15 @@ module.exports = function convert(config) { if (parent.kind === SyntaxKind.ObjectLiteralExpression) { Object.assign(result, { type: AST_NODE_TYPES.Property, - key: convertChild(node.name), - value: convertChild(node.name), + key: convertChild((node as any).name), + value: convertChild((node as any).name), computed: false, method: false, shorthand: true, kind: 'init' }); } else { - return convertChild(node.expression); + return convertChild((node as any).expression); } break; @@ -891,16 +897,16 @@ module.exports = function convert(config) { type: isAbstract ? AST_NODE_TYPES.TSAbstractClassProperty : AST_NODE_TYPES.ClassProperty, - key: convertChild(node.name), - value: convertChild(node.initializer), - computed: nodeUtils.isComputedProperty(node.name), + key: convertChild((node as any).name), + value: convertChild((node as any).initializer), + computed: nodeUtils.isComputedProperty((node as any).name), static: nodeUtils.hasStaticModifierFlag(node), readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node) || undefined }); - if (node.type) { - result.typeAnnotation = convertTypeAnnotation(node.type); + if ((node as any).type) { + result.typeAnnotation = convertTypeAnnotation((node as any).type); } if (node.decorators) { @@ -909,19 +915,25 @@ module.exports = function convert(config) { const accessibility = nodeUtils.getTSNodeAccessibility(node); if (accessibility) { - result.accessibility = accessibility; + (result as any).accessibility = accessibility; } - if (node.name.kind === SyntaxKind.Identifier && node.questionToken) { - result.optional = true; + if ( + (node as any).name.kind === SyntaxKind.Identifier && + (node as any).questionToken + ) { + (result as any).optional = true; } - if (node.exclamationToken) { - result.definite = true; + if ((node as any).exclamationToken) { + (result as any).definite = true; } - if (result.key.type === AST_NODE_TYPES.Literal && node.questionToken) { - result.optional = true; + if ( + (result as any).key.type === AST_NODE_TYPES.Literal && + (node as any).questionToken + ) { + (result as any).optional = true; } break; } @@ -930,9 +942,9 @@ module.exports = function convert(config) { case SyntaxKind.SetAccessor: case SyntaxKind.MethodDeclaration: { const openingParen = nodeUtils.findFirstMatchingToken( - node.name, + (node as any).name, ast, - token => { + (token: any) => { if (!token || !token.kind) { return false; } @@ -941,38 +953,38 @@ module.exports = function convert(config) { ); const methodLoc = ast.getLineAndCharacterOfPosition( - openingParen.getStart() + (openingParen as any).getStart() ), nodeIsMethod = node.kind === SyntaxKind.MethodDeclaration, method = { type: AST_NODE_TYPES.FunctionExpression, id: null, - generator: !!node.asteriskToken, + generator: !!(node as any).asteriskToken, expression: false, async: nodeUtils.hasModifier(SyntaxKind.AsyncKeyword, node), - body: convertChild(node.body), - range: [node.parameters.pos - 1, result.range[1]], + body: convertChild((node as any).body), + range: [(node as any).parameters.pos - 1, (result as any).range[1]], loc: { start: { line: methodLoc.line + 1, column: methodLoc.character }, - end: result.loc.end + end: (result as any).loc.end } }; - if (node.type) { - method.returnType = convertTypeAnnotation(node.type); + if ((node as any).type) { + (method as any).returnType = convertTypeAnnotation((node as any).type); } if (parent.kind === SyntaxKind.ObjectLiteralExpression) { - method.params = node.parameters.map(convertChild); + (method as any).params = (node as any).parameters.map(convertChild); Object.assign(result, { type: AST_NODE_TYPES.Property, - key: convertChild(node.name), + key: convertChild((node as any).name), value: method, - computed: nodeUtils.isComputedProperty(node.name), + computed: nodeUtils.isComputedProperty((node as any).name), method: nodeIsMethod, shorthand: false, kind: 'init' @@ -983,7 +995,7 @@ module.exports = function convert(config) { /** * Unlike in object literal methods, class method params can have decorators */ - method.params = convertParameters(node.parameters); + (method as any).params = convertParameters((node as any).parameters); /** * TypeScript class methods can be defined as "abstract" @@ -997,9 +1009,9 @@ module.exports = function convert(config) { Object.assign(result, { type: methodDefinitionType, - key: convertChild(node.name), + key: convertChild((node as any).name), value: method, - computed: nodeUtils.isComputedProperty(node.name), + computed: nodeUtils.isComputedProperty((node as any).name), static: nodeUtils.hasStaticModifierFlag(node), kind: 'method' }); @@ -1010,30 +1022,33 @@ module.exports = function convert(config) { const accessibility = nodeUtils.getTSNodeAccessibility(node); if (accessibility) { - result.accessibility = accessibility; + (result as any).accessibility = accessibility; } } - if (result.key.type === AST_NODE_TYPES.Identifier && node.questionToken) { - result.key.optional = true; + if ( + (result as any).key.type === AST_NODE_TYPES.Identifier && + (node as any).questionToken + ) { + (result as any).key.optional = true; } if (node.kind === SyntaxKind.GetAccessor) { - result.kind = 'get'; + (result as any).kind = 'get'; } else if (node.kind === SyntaxKind.SetAccessor) { - result.kind = 'set'; + (result as any).kind = 'set'; } else if ( - !result.static && - node.name.kind === SyntaxKind.StringLiteral && - node.name.text === 'constructor' + !(result as any).static && + (node as any).name.kind === SyntaxKind.StringLiteral && + (node as any).name.text === 'constructor' ) { - result.kind = 'constructor'; + (result as any).kind = 'constructor'; } // Process typeParameters - if (node.typeParameters && node.typeParameters.length) { - method.typeParameters = convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters + if ((node as any).typeParameters && (node as any).typeParameters.length) { + (method as any).typeParameters = convertTSTypeParametersToTypeParametersDeclaration( + (node as any).typeParameters ); } @@ -1048,37 +1063,38 @@ module.exports = function convert(config) { node ), firstConstructorToken = constructorIsStatic - ? nodeUtils.findNextToken(node.getFirstToken(), ast) + ? nodeUtils.findNextToken((node as any).getFirstToken(), ast) : node.getFirstToken(), constructorLoc = ast.getLineAndCharacterOfPosition( - node.parameters.pos - 1 + (node as any).parameters.pos - 1 ), constructor = { type: AST_NODE_TYPES.FunctionExpression, id: null, - params: convertParameters(node.parameters), + params: convertParameters((node as any).parameters), generator: false, expression: false, async: false, - body: convertChild(node.body), - range: [node.parameters.pos - 1, result.range[1]], + body: convertChild((node as any).body), + range: [(node as any).parameters.pos - 1, (result as any).range[1]], loc: { start: { line: constructorLoc.line + 1, column: constructorLoc.character }, - end: result.loc.end + end: (result as any).loc.end } }; const constructorIdentifierLocStart = ast.getLineAndCharacterOfPosition( - firstConstructorToken.getStart() + (firstConstructorToken as any).getStart() ), constructorIdentifierLocEnd = ast.getLineAndCharacterOfPosition( - firstConstructorToken.getEnd() + (firstConstructorToken as any).getEnd() ), constructorIsComputed = - !!node.name && nodeUtils.isComputedProperty(node.name); + !!(node as any).name && + nodeUtils.isComputedProperty((node as any).name); let constructorKey; @@ -1086,8 +1102,11 @@ module.exports = function convert(config) { constructorKey = { type: AST_NODE_TYPES.Literal, value: 'constructor', - raw: node.name.getText(), - range: [firstConstructorToken.getStart(), firstConstructorToken.end], + raw: (node as any).name.getText(), + range: [ + (firstConstructorToken as any).getStart(), + (firstConstructorToken as any).end + ], loc: { start: { line: constructorIdentifierLocStart.line + 1, @@ -1103,7 +1122,10 @@ module.exports = function convert(config) { constructorKey = { type: AST_NODE_TYPES.Identifier, name: 'constructor', - range: [firstConstructorToken.getStart(), firstConstructorToken.end], + range: [ + (firstConstructorToken as any).getStart(), + (firstConstructorToken as any).end + ], loc: { start: { line: constructorIdentifierLocStart.line + 1, @@ -1133,7 +1155,7 @@ module.exports = function convert(config) { const accessibility = nodeUtils.getTSNodeAccessibility(node); if (accessibility) { - result.accessibility = accessibility; + (result as any).accessibility = accessibility; } break; @@ -1142,23 +1164,23 @@ module.exports = function convert(config) { case SyntaxKind.FunctionExpression: Object.assign(result, { type: AST_NODE_TYPES.FunctionExpression, - id: convertChild(node.name), - generator: !!node.asteriskToken, - params: convertParameters(node.parameters), - body: convertChild(node.body), + id: convertChild((node as any).name), + generator: !!(node as any).asteriskToken, + params: convertParameters((node as any).parameters), + body: convertChild((node as any).body), async: nodeUtils.hasModifier(SyntaxKind.AsyncKeyword, node), expression: false }); // Process returnType - if (node.type) { - result.returnType = convertTypeAnnotation(node.type); + if ((node as any).type) { + (result as any).returnType = convertTypeAnnotation((node as any).type); } // Process typeParameters - if (node.typeParameters && node.typeParameters.length) { + if ((node as any).typeParameters && (node as any).typeParameters.length) { result.typeParameters = convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters + (node as any).typeParameters ); } break; @@ -1172,7 +1194,7 @@ module.exports = function convert(config) { case SyntaxKind.ArrayBindingPattern: Object.assign(result, { type: AST_NODE_TYPES.ArrayPattern, - elements: node.elements.map(convertChild) + elements: (node as any).elements.map(convertChild) }); break; @@ -1183,26 +1205,26 @@ module.exports = function convert(config) { case SyntaxKind.ObjectBindingPattern: Object.assign(result, { type: AST_NODE_TYPES.ObjectPattern, - properties: node.elements.map(convertChild) + properties: (node as any).elements.map(convertChild) }); break; case SyntaxKind.BindingElement: if (parent.kind === SyntaxKind.ArrayBindingPattern) { const arrayItem = convert({ - node: node.name, + node: (node as any).name, parent, ast, additionalOptions }); - if (node.initializer) { + if ((node as any).initializer) { Object.assign(result, { type: AST_NODE_TYPES.AssignmentPattern, left: arrayItem, - right: convertChild(node.initializer) + right: convertChild((node as any).initializer) }); - } else if (node.dotDotDotToken) { + } else if ((node as any).dotDotDotToken) { Object.assign(result, { type: AST_NODE_TYPES.RestElement, argument: arrayItem @@ -1211,35 +1233,41 @@ module.exports = function convert(config) { return arrayItem; } } else if (parent.kind === SyntaxKind.ObjectBindingPattern) { - if (node.dotDotDotToken) { + if ((node as any).dotDotDotToken) { Object.assign(result, { type: AST_NODE_TYPES.RestElement, - argument: convertChild(node.propertyName || node.name) + argument: convertChild( + (node as any).propertyName || (node as any).name + ) }); } else { Object.assign(result, { type: AST_NODE_TYPES.Property, - key: convertChild(node.propertyName || node.name), - value: convertChild(node.name), + key: convertChild((node as any).propertyName || (node as any).name), + value: convertChild((node as any).name), computed: Boolean( - node.propertyName && - node.propertyName.kind === SyntaxKind.ComputedPropertyName + (node as any).propertyName && + (node as any).propertyName.kind === + SyntaxKind.ComputedPropertyName ), method: false, - shorthand: !node.propertyName, + shorthand: !(node as any).propertyName, kind: 'init' }); } - if (node.initializer) { - result.value = { + if ((node as any).initializer) { + (result as any).value = { type: AST_NODE_TYPES.AssignmentPattern, - left: convertChild(node.name), - right: convertChild(node.initializer), - range: [node.name.getStart(), node.initializer.end], + left: convertChild((node as any).name), + right: convertChild((node as any).initializer), + range: [ + (node as any).name.getStart(), + (node as any).initializer.end + ], loc: nodeUtils.getLocFor( - node.name.getStart(), - node.initializer.end, + (node as any).name.getStart(), + (node as any).initializer.end, ast ) }; @@ -1252,21 +1280,21 @@ module.exports = function convert(config) { type: AST_NODE_TYPES.ArrowFunctionExpression, generator: false, id: null, - params: convertParameters(node.parameters), - body: convertChild(node.body), + params: convertParameters((node as any).parameters), + body: convertChild((node as any).body), async: nodeUtils.hasModifier(SyntaxKind.AsyncKeyword, node), - expression: node.body.kind !== SyntaxKind.Block + expression: (node as any).body.kind !== SyntaxKind.Block }); // Process returnType - if (node.type) { - result.returnType = convertTypeAnnotation(node.type); + if ((node as any).type) { + (result as any).returnType = convertTypeAnnotation((node as any).type); } // Process typeParameters - if (node.typeParameters && node.typeParameters.length) { + if ((node as any).typeParameters && (node as any).typeParameters.length) { result.typeParameters = convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters + (node as any).typeParameters ); } break; @@ -1274,15 +1302,15 @@ module.exports = function convert(config) { case SyntaxKind.YieldExpression: Object.assign(result, { type: AST_NODE_TYPES.YieldExpression, - delegate: !!node.asteriskToken, - argument: convertChild(node.expression) + delegate: !!(node as any).asteriskToken, + argument: convertChild((node as any).expression) }); break; case SyntaxKind.AwaitExpression: Object.assign(result, { type: AST_NODE_TYPES.AwaitExpression, - argument: convertChild(node.expression) + argument: convertChild((node as any).expression) }); break; @@ -1296,7 +1324,7 @@ module.exports = function convert(config) { type: AST_NODE_TYPES.TemplateElement, value: { raw: ast.text.slice(node.getStart() + 1, node.end - 1), - cooked: node.text + cooked: (node as any).text }, tail: true, range: result.range, @@ -1310,24 +1338,24 @@ module.exports = function convert(config) { case SyntaxKind.TemplateExpression: Object.assign(result, { type: AST_NODE_TYPES.TemplateLiteral, - quasis: [convertChild(node.head)], + quasis: [convertChild((node as any).head)], expressions: [] }); - node.templateSpans.forEach(templateSpan => { - result.expressions.push(convertChild(templateSpan.expression)); - result.quasis.push(convertChild(templateSpan.literal)); + (node as any).templateSpans.forEach((templateSpan: any) => { + (result as any).expressions.push(convertChild(templateSpan.expression)); + (result as any).quasis.push(convertChild(templateSpan.literal)); }); break; case SyntaxKind.TaggedTemplateExpression: Object.assign(result, { type: AST_NODE_TYPES.TaggedTemplateExpression, - typeParameters: node.typeArguments - ? convertTypeArgumentsToTypeParameters(node.typeArguments) + typeParameters: (node as any).typeArguments + ? convertTypeArgumentsToTypeParameters((node as any).typeArguments) : undefined, - tag: convertChild(node.tag), - quasi: convertChild(node.template) + tag: convertChild((node as any).tag), + quasi: convertChild((node as any).template) }); break; @@ -1339,7 +1367,7 @@ module.exports = function convert(config) { type: AST_NODE_TYPES.TemplateElement, value: { raw: ast.text.slice(node.getStart() + 1, node.end - (tail ? 1 : 2)), - cooked: node.text + cooked: (node as any).text }, tail }); @@ -1356,16 +1384,16 @@ module.exports = function convert(config) { node.parent.parent && node.parent.parent.kind === SyntaxKind.BinaryExpression ) { - if (node.parent.parent.left === node.parent) { + if ((node as any).parent.parent.left === node.parent) { type = AST_NODE_TYPES.RestElement; - } else if (node.parent.parent.right === node.parent) { + } else if ((node as any).parent.parent.right === node.parent) { type = AST_NODE_TYPES.SpreadElement; } } Object.assign(result, { type, - argument: convertChild(node.expression) + argument: convertChild((node as any).expression) }); break; } @@ -1377,16 +1405,16 @@ module.exports = function convert(config) { node.parent.parent && node.parent.parent.kind === SyntaxKind.BinaryExpression ) { - if (node.parent.parent.right === node.parent) { + if ((node as any).parent.parent.right === node.parent) { type = AST_NODE_TYPES.SpreadElement; - } else if (node.parent.parent.left === node.parent) { + } else if ((node as any).parent.parent.left === node.parent) { type = AST_NODE_TYPES.RestElement; } } Object.assign(result, { type, - argument: convertChild(node.expression) + argument: convertChild((node as any).expression) }); break; } @@ -1394,36 +1422,38 @@ module.exports = function convert(config) { case SyntaxKind.Parameter: { let parameter; - if (node.dotDotDotToken) { - parameter = convertChild(node.name); + if ((node as any).dotDotDotToken) { + parameter = convertChild((node as any).name); Object.assign(result, { type: AST_NODE_TYPES.RestElement, argument: parameter }); - } else if (node.initializer) { - parameter = convertChild(node.name); + } else if ((node as any).initializer) { + parameter = convertChild((node as any).name); Object.assign(result, { type: AST_NODE_TYPES.AssignmentPattern, left: parameter, - right: convertChild(node.initializer) + right: convertChild((node as any).initializer) }); } else { parameter = convert({ - node: node.name, + node: (node as any).name, parent, ast, additionalOptions }); - result = parameter; + (result as any) = parameter; } - if (node.type) { - parameter.typeAnnotation = convertTypeAnnotation(node.type); - fixTypeAnnotationParentLocation(parameter); + if ((node as any).type) { + (parameter as any).typeAnnotation = convertTypeAnnotation( + (node as any).type + ); + fixTypeAnnotationParentLocation(parameter as any); } - if (node.questionToken) { - parameter.optional = true; + if ((node as any).questionToken) { + (parameter as any).optional = true; } if (node.modifiers) { @@ -1450,22 +1480,23 @@ module.exports = function convert(config) { case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: { - const heritageClauses = node.heritageClauses || []; + const heritageClauses = (node as any).heritageClauses || []; let classNodeType = SyntaxKind[node.kind]; let lastClassToken = heritageClauses.length ? heritageClauses[heritageClauses.length - 1] - : node.name; + : (node as any).name; - if (node.typeParameters && node.typeParameters.length) { - const lastTypeParameter = - node.typeParameters[node.typeParameters.length - 1]; + if ((node as any).typeParameters && (node as any).typeParameters.length) { + const lastTypeParameter = (node as any).typeParameters[ + (node as any).typeParameters.length - 1 + ]; if (!lastClassToken || lastTypeParameter.pos > lastClassToken.pos) { lastClassToken = nodeUtils.findNextToken(lastTypeParameter, ast); } result.typeParameters = convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters + (node as any).typeParameters ); } @@ -1495,7 +1526,7 @@ module.exports = function convert(config) { const openBrace = nodeUtils.findNextToken(lastClassToken, ast); const superClass = heritageClauses.find( - clause => clause.token === SyntaxKind.ExtendsKeyword + (clause: any) => clause.token === SyntaxKind.ExtendsKeyword ); if (superClass) { @@ -1508,25 +1539,25 @@ module.exports = function convert(config) { } if (superClass.types[0] && superClass.types[0].typeArguments) { - result.superTypeParameters = convertTypeArgumentsToTypeParameters( + (result as any).superTypeParameters = convertTypeArgumentsToTypeParameters( superClass.types[0].typeArguments ); } } const implementsClause = heritageClauses.find( - clause => clause.token === SyntaxKind.ImplementsKeyword + (clause: any) => clause.token === SyntaxKind.ImplementsKeyword ); Object.assign(result, { type: classNodeType, - id: convertChild(node.name), + id: convertChild((node as any).name), body: { type: AST_NODE_TYPES.ClassBody, body: [], // TODO: Fix location info - range: [openBrace.getStart(), result.range[1]], + range: [openBrace.getStart(), (result as any).range[1]], loc: nodeUtils.getLocFor(openBrace.getStart(), node.end, ast) }, superClass: @@ -1536,14 +1567,16 @@ module.exports = function convert(config) { }); if (implementsClause) { - result.implements = implementsClause.types.map(convertClassImplements); + (result as any).implements = implementsClause.types.map( + convertClassImplements + ); } if (node.decorators) { result.decorators = convertDecorators(node.decorators); } - const filteredMembers = node.members.filter( + const filteredMembers = (node as any).members.filter( nodeUtils.isESTreeClassMember ); @@ -1552,7 +1585,7 @@ module.exports = function convert(config) { } // check for exports - result = nodeUtils.fixExports(node, result, ast); + result = nodeUtils.fixExports(node, result as any, ast); break; } @@ -1561,32 +1594,37 @@ module.exports = function convert(config) { case SyntaxKind.ModuleBlock: Object.assign(result, { type: AST_NODE_TYPES.TSModuleBlock, - body: node.statements.map(convertChild) + body: (node as any).statements.map(convertChild) }); break; case SyntaxKind.ImportDeclaration: Object.assign(result, { type: AST_NODE_TYPES.ImportDeclaration, - source: convertChild(node.moduleSpecifier), + source: convertChild((node as any).moduleSpecifier), specifiers: [] }); - if (node.importClause) { - if (node.importClause.name) { - result.specifiers.push(convertChild(node.importClause)); + if ((node as any).importClause) { + if ((node as any).importClause.name) { + (result as any).specifiers.push( + convertChild((node as any).importClause) + ); } - if (node.importClause.namedBindings) { + if ((node as any).importClause.namedBindings) { if ( - node.importClause.namedBindings.kind === SyntaxKind.NamespaceImport + (node as any).importClause.namedBindings.kind === + SyntaxKind.NamespaceImport ) { - result.specifiers.push( - convertChild(node.importClause.namedBindings) + (result as any).specifiers.push( + convertChild((node as any).importClause.namedBindings) ); } else { - result.specifiers = result.specifiers.concat( - node.importClause.namedBindings.elements.map(convertChild) + result.specifiers = (result as any).specifiers.concat( + (node as any).importClause.namedBindings.elements.map( + convertChild + ) ); } } @@ -1597,48 +1635,52 @@ module.exports = function convert(config) { case SyntaxKind.NamespaceImport: Object.assign(result, { type: AST_NODE_TYPES.ImportNamespaceSpecifier, - local: convertChild(node.name) + local: convertChild((node as any).name) }); break; case SyntaxKind.ImportSpecifier: Object.assign(result, { type: AST_NODE_TYPES.ImportSpecifier, - local: convertChild(node.name), - imported: convertChild(node.propertyName || node.name) + local: convertChild((node as any).name), + imported: convertChild((node as any).propertyName || (node as any).name) }); break; case SyntaxKind.ImportClause: Object.assign(result, { type: AST_NODE_TYPES.ImportDefaultSpecifier, - local: convertChild(node.name) + local: convertChild((node as any).name) }); // have to adjust location information due to tree differences - result.range[1] = node.name.end; - result.loc = nodeUtils.getLocFor(result.range[0], result.range[1], ast); + (result as any).range[1] = (node as any).name.end; + result.loc = nodeUtils.getLocFor( + (result as any).range[0], + (result as any).range[1], + ast + ); break; case SyntaxKind.NamedImports: Object.assign(result, { type: AST_NODE_TYPES.ImportDefaultSpecifier, - local: convertChild(node.name) + local: convertChild((node as any).name) }); break; case SyntaxKind.ExportDeclaration: - if (node.exportClause) { + if ((node as any).exportClause) { Object.assign(result, { type: AST_NODE_TYPES.ExportNamedDeclaration, - source: convertChild(node.moduleSpecifier), - specifiers: node.exportClause.elements.map(convertChild), + source: convertChild((node as any).moduleSpecifier), + specifiers: (node as any).exportClause.elements.map(convertChild), declaration: null }); } else { Object.assign(result, { type: AST_NODE_TYPES.ExportAllDeclaration, - source: convertChild(node.moduleSpecifier) + source: convertChild((node as any).moduleSpecifier) }); } break; @@ -1646,21 +1688,21 @@ module.exports = function convert(config) { case SyntaxKind.ExportSpecifier: Object.assign(result, { type: AST_NODE_TYPES.ExportSpecifier, - local: convertChild(node.propertyName || node.name), - exported: convertChild(node.name) + local: convertChild((node as any).propertyName || (node as any).name), + exported: convertChild((node as any).name) }); break; case SyntaxKind.ExportAssignment: - if (node.isExportEquals) { + if ((node as any).isExportEquals) { Object.assign(result, { type: AST_NODE_TYPES.TSExportAssignment, - expression: convertChild(node.expression) + expression: convertChild((node as any).expression) }); } else { Object.assign(result, { type: AST_NODE_TYPES.ExportDefaultDeclaration, - declaration: convertChild(node.expression) + declaration: convertChild((node as any).expression) }); } break; @@ -1669,7 +1711,7 @@ module.exports = function convert(config) { case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.PostfixUnaryExpression: { - const operator = nodeUtils.getTextForTokenKind(node.operator); + const operator = nodeUtils.getTextForTokenKind((node as any).operator); Object.assign(result, { /** * ESTree uses UpdateExpression for ++/-- @@ -1679,7 +1721,7 @@ module.exports = function convert(config) { : AST_NODE_TYPES.UnaryExpression, operator, prefix: node.kind === SyntaxKind.PrefixUnaryExpression, - argument: convertChild(node.operand) + argument: convertChild((node as any).operand) }); break; } @@ -1689,7 +1731,7 @@ module.exports = function convert(config) { type: AST_NODE_TYPES.UnaryExpression, operator: 'delete', prefix: true, - argument: convertChild(node.expression) + argument: convertChild((node as any).expression) }); break; @@ -1698,7 +1740,7 @@ module.exports = function convert(config) { type: AST_NODE_TYPES.UnaryExpression, operator: 'void', prefix: true, - argument: convertChild(node.expression) + argument: convertChild((node as any).expression) }); break; @@ -1707,15 +1749,15 @@ module.exports = function convert(config) { type: AST_NODE_TYPES.UnaryExpression, operator: 'typeof', prefix: true, - argument: convertChild(node.expression) + argument: convertChild((node as any).expression) }); break; case SyntaxKind.TypeOperator: Object.assign(result, { type: AST_NODE_TYPES.TSTypeOperator, - operator: nodeUtils.getTextForTokenKind(node.operator), - typeAnnotation: convertChild(node.type) + operator: nodeUtils.getTextForTokenKind((node as any).operator), + typeAnnotation: convertChild((node as any).type) }); break; @@ -1723,42 +1765,51 @@ module.exports = function convert(config) { case SyntaxKind.BinaryExpression: // TypeScript uses BinaryExpression for sequences as well - if (nodeUtils.isComma(node.operatorToken)) { + if (nodeUtils.isComma((node as any).operatorToken)) { Object.assign(result, { type: AST_NODE_TYPES.SequenceExpression, expressions: [] }); - const left = convertChild(node.left), - right = convertChild(node.right); + const left = convertChild((node as any).left), + right = convertChild((node as any).right); - if (left.type === AST_NODE_TYPES.SequenceExpression) { - result.expressions = result.expressions.concat(left.expressions); + if ((left as any).type === AST_NODE_TYPES.SequenceExpression) { + (result as any).expressions = (result as any).expressions.concat( + (left as any).expressions + ); } else { - result.expressions.push(left); + (result as any).expressions.push(left); } - if (right.type === AST_NODE_TYPES.SequenceExpression) { - result.expressions = result.expressions.concat(right.expressions); + if ((right as any).type === AST_NODE_TYPES.SequenceExpression) { + (result as any).expressions = (result as any).expressions.concat( + (right as any).expressions + ); } else { - result.expressions.push(right); + (result as any).expressions.push(right); } } else if ( - node.operatorToken && - node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken + (node as any).operatorToken && + (node as any).operatorToken.kind === + SyntaxKind.AsteriskAsteriskEqualsToken ) { Object.assign(result, { type: AST_NODE_TYPES.AssignmentExpression, - operator: nodeUtils.getTextForTokenKind(node.operatorToken.kind), - left: convertChild(node.left), - right: convertChild(node.right) + operator: nodeUtils.getTextForTokenKind( + (node as any).operatorToken.kind + ), + left: convertChild((node as any).left), + right: convertChild((node as any).right) }); } else { Object.assign(result, { - type: nodeUtils.getBinaryExpressionType(node.operatorToken), - operator: nodeUtils.getTextForTokenKind(node.operatorToken.kind), - left: convertChild(node.left), - right: convertChild(node.right) + type: nodeUtils.getBinaryExpressionType((node as any).operatorToken), + operator: nodeUtils.getTextForTokenKind( + (node as any).operatorToken.kind + ), + left: convertChild((node as any).left), + right: convertChild((node as any).right) }); // if the binary expression is in a destructured array, switch it @@ -1777,12 +1828,12 @@ module.exports = function convert(config) { let upperArrayIsInAssignment; if (upperArrayAssignNode) { - if (upperArrayAssignNode.left === upperArrayNode) { + if ((upperArrayAssignNode as any).left === upperArrayNode) { upperArrayIsInAssignment = true; } else { upperArrayIsInAssignment = nodeUtils.findChildOfKind( - upperArrayAssignNode.left, + (upperArrayAssignNode as any).left, SyntaxKind.ArrayLiteralExpression, ast ) === upperArrayNode; @@ -1790,7 +1841,7 @@ module.exports = function convert(config) { } if (upperArrayIsInAssignment) { - delete result.operator; + delete (result as any).operator; result.type = AST_NODE_TYPES.AssignmentPattern; } } @@ -1801,25 +1852,26 @@ module.exports = function convert(config) { if (nodeUtils.isJSXToken(parent)) { const jsxMemberExpression = { type: AST_NODE_TYPES.MemberExpression, - object: convertChild(node.expression), - property: convertChild(node.name) + object: convertChild((node as any).expression), + property: convertChild((node as any).name) }; const isNestedMemberExpression = - node.expression.kind === SyntaxKind.PropertyAccessExpression; - if (node.expression.kind === SyntaxKind.ThisKeyword) { - jsxMemberExpression.object.name = 'this'; + (node as any).expression.kind === SyntaxKind.PropertyAccessExpression; + if ((node as any).expression.kind === SyntaxKind.ThisKeyword) { + (jsxMemberExpression as any).object.name = 'this'; } - jsxMemberExpression.object.type = isNestedMemberExpression + (jsxMemberExpression as any).object.type = isNestedMemberExpression ? AST_NODE_TYPES.MemberExpression : AST_NODE_TYPES.JSXIdentifier; - jsxMemberExpression.property.type = AST_NODE_TYPES.JSXIdentifier; + (jsxMemberExpression as any).property.type = + AST_NODE_TYPES.JSXIdentifier; Object.assign(result, jsxMemberExpression); } else { Object.assign(result, { type: AST_NODE_TYPES.MemberExpression, - object: convertChild(node.expression), - property: convertChild(node.name), + object: convertChild((node as any).expression), + property: convertChild((node as any).name), computed: false }); } @@ -1828,8 +1880,8 @@ module.exports = function convert(config) { case SyntaxKind.ElementAccessExpression: Object.assign(result, { type: AST_NODE_TYPES.MemberExpression, - object: convertChild(node.expression), - property: convertChild(node.argumentExpression), + object: convertChild((node as any).expression), + property: convertChild((node as any).argumentExpression), computed: true }); break; @@ -1837,21 +1889,21 @@ module.exports = function convert(config) { case SyntaxKind.ConditionalExpression: Object.assign(result, { type: AST_NODE_TYPES.ConditionalExpression, - test: convertChild(node.condition), - consequent: convertChild(node.whenTrue), - alternate: convertChild(node.whenFalse) + test: convertChild((node as any).condition), + consequent: convertChild((node as any).whenTrue), + alternate: convertChild((node as any).whenFalse) }); break; case SyntaxKind.CallExpression: Object.assign(result, { type: AST_NODE_TYPES.CallExpression, - callee: convertChild(node.expression), - arguments: node.arguments.map(convertChild) + callee: convertChild((node as any).expression), + arguments: (node as any).arguments.map(convertChild) }); - if (node.typeArguments && node.typeArguments.length) { + if ((node as any).typeArguments && (node as any).typeArguments.length) { result.typeParameters = convertTypeArgumentsToTypeParameters( - node.typeArguments + (node as any).typeArguments ); } break; @@ -1859,27 +1911,32 @@ module.exports = function convert(config) { case SyntaxKind.NewExpression: Object.assign(result, { type: AST_NODE_TYPES.NewExpression, - callee: convertChild(node.expression), - arguments: node.arguments ? node.arguments.map(convertChild) : [] + callee: convertChild((node as any).expression), + arguments: (node as any).arguments + ? (node as any).arguments.map(convertChild) + : [] }); - if (node.typeArguments && node.typeArguments.length) { + if ((node as any).typeArguments && (node as any).typeArguments.length) { result.typeParameters = convertTypeArgumentsToTypeParameters( - node.typeArguments + (node as any).typeArguments ); } break; case SyntaxKind.MetaProperty: { - const newToken = nodeUtils.convertToken(node.getFirstToken(), ast); + const newToken = nodeUtils.convertToken( + (node as any).getFirstToken(), + ast + ); Object.assign(result, { type: AST_NODE_TYPES.MetaProperty, meta: { type: AST_NODE_TYPES.Identifier, range: newToken.range, loc: newToken.loc, - name: nodeUtils.getTextForTokenKind(node.keywordToken) + name: nodeUtils.getTextForTokenKind((node as any).keywordToken) }, - property: convertChild(node.name) + property: convertChild((node as any).name) }); break; } @@ -1889,26 +1946,33 @@ module.exports = function convert(config) { case SyntaxKind.StringLiteral: Object.assign(result, { type: AST_NODE_TYPES.Literal, - raw: ast.text.slice(result.range[0], result.range[1]) + raw: ast.text.slice((result as any).range[0], (result as any).range[1]) }); if (parent.name && parent.name === node) { - result.value = node.text; + (result as any).value = (node as any).text; } else { - result.value = nodeUtils.unescapeStringLiteralText(node.text); + (result as any).value = nodeUtils.unescapeStringLiteralText( + (node as any).text + ); } break; case SyntaxKind.NumericLiteral: Object.assign(result, { type: AST_NODE_TYPES.Literal, - value: Number(node.text), - raw: ast.text.slice(result.range[0], result.range[1]) + value: Number((node as any).text), + raw: ast.text.slice((result as any).range[0], (result as any).range[1]) }); break; case SyntaxKind.RegularExpressionLiteral: { - const pattern = node.text.slice(1, node.text.lastIndexOf('/')); - const flags = node.text.slice(node.text.lastIndexOf('/') + 1); + const pattern = (node as any).text.slice( + 1, + (node as any).text.lastIndexOf('/') + ); + const flags = (node as any).text.slice( + (node as any).text.lastIndexOf('/') + 1 + ); let regex = null; try { @@ -1920,7 +1984,7 @@ module.exports = function convert(config) { Object.assign(result, { type: AST_NODE_TYPES.Literal, value: regex, - raw: node.text, + raw: (node as any).text, regex: { pattern, flags @@ -1976,9 +2040,9 @@ module.exports = function convert(config) { case SyntaxKind.JsxElement: Object.assign(result, { type: AST_NODE_TYPES.JSXElement, - openingElement: convertChild(node.openingElement), - closingElement: convertChild(node.closingElement), - children: node.children.map(convertChild) + openingElement: convertChild((node as any).openingElement), + closingElement: convertChild((node as any).closingElement), + children: (node as any).children.map(convertChild) }); break; @@ -1986,9 +2050,9 @@ module.exports = function convert(config) { case SyntaxKind.JsxFragment: Object.assign(result, { type: AST_NODE_TYPES.JSXFragment, - openingElement: convertChild(node.openingElement), - closingElement: convertChild(node.closingElement), - children: node.children.map(convertChild) + openingElement: convertChild((node as any).openingElement), + closingElement: convertChild((node as any).closingElement), + children: (node as any).children.map(convertChild) }); break; @@ -2000,7 +2064,7 @@ module.exports = function convert(config) { node.kind = SyntaxKind.JsxOpeningElement; const openingElement = convertChild(node); - openingElement.selfClosing = true; + (openingElement as any).selfClosing = true; Object.assign(result, { type: AST_NODE_TYPES.JSXElement, @@ -2015,19 +2079,19 @@ module.exports = function convert(config) { case SyntaxKind.JsxOpeningElement: Object.assign(result, { type: AST_NODE_TYPES.JSXOpeningElement, - typeParameters: node.typeArguments - ? convertTypeArgumentsToTypeParameters(node.typeArguments) + typeParameters: (node as any).typeArguments + ? convertTypeArgumentsToTypeParameters((node as any).typeArguments) : undefined, selfClosing: false, - name: convertTypeScriptJSXTagNameToESTreeName(node.tagName), - attributes: node.attributes.properties.map(convertChild) + name: convertTypeScriptJSXTagNameToESTreeName((node as any).tagName), + attributes: (node as any).attributes.properties.map(convertChild) }); break; case SyntaxKind.JsxClosingElement: Object.assign(result, { type: AST_NODE_TYPES.JSXClosingElement, - name: convertTypeScriptJSXTagNameToESTreeName(node.tagName) + name: convertTypeScriptJSXTagNameToESTreeName((node as any).tagName) }); break; @@ -2044,9 +2108,11 @@ module.exports = function convert(config) { break; case SyntaxKind.JsxExpression: { - const eloc = ast.getLineAndCharacterOfPosition(result.range[0] + 1); - const expression = node.expression - ? convertChild(node.expression) + const eloc = ast.getLineAndCharacterOfPosition( + (result as any).range[0] + 1 + ); + const expression = (node as any).expression + ? convertChild((node as any).expression) : { type: AST_NODE_TYPES.JSXEmptyExpression, loc: { @@ -2055,15 +2121,15 @@ module.exports = function convert(config) { column: eloc.character }, end: { - line: result.loc.end.line, - column: result.loc.end.column - 1 + line: (result as any).loc.end.line, + column: (result as any).loc.end.column - 1 } }, - range: [result.range[0] + 1, result.range[1] - 1] + range: [(result as any).range[0] + 1, (result as any).range[1] - 1] }; Object.assign(result, { - type: node.dotDotDotToken + type: (node as any).dotDotDotToken ? AST_NODE_TYPES.JSXSpreadChild : AST_NODE_TYPES.JSXExpressionContainer, expression @@ -2073,7 +2139,7 @@ module.exports = function convert(config) { } case SyntaxKind.JsxAttribute: { - const attributeName = nodeUtils.convertToken(node.name, ast); + const attributeName = nodeUtils.convertToken((node as any).name, ast); attributeName.type = AST_NODE_TYPES.JSXIdentifier; attributeName.name = attributeName.value; delete attributeName.value; @@ -2081,7 +2147,7 @@ module.exports = function convert(config) { Object.assign(result, { type: AST_NODE_TYPES.JSXAttribute, name: attributeName, - value: convertChild(node.initializer) + value: convertChild((node as any).initializer) }); break; @@ -2116,7 +2182,7 @@ module.exports = function convert(config) { case SyntaxKind.JsxSpreadAttribute: Object.assign(result, { type: AST_NODE_TYPES.JSXSpreadAttribute, - argument: convertChild(node.expression) + argument: convertChild((node as any).expression) }); break; @@ -2124,8 +2190,8 @@ module.exports = function convert(config) { case SyntaxKind.FirstNode: { Object.assign(result, { type: AST_NODE_TYPES.TSQualifiedName, - left: convertChild(node.left), - right: convertChild(node.right) + left: convertChild((node as any).left), + right: convertChild((node as any).right) }); break; @@ -2134,7 +2200,12 @@ module.exports = function convert(config) { // TypeScript specific case SyntaxKind.ParenthesizedExpression: - return convert({ node: node.expression, parent, ast, additionalOptions }); + return convert({ + node: (node as any).expression, + parent, + ast, + additionalOptions + }); /** * Convert TypeAliasDeclaration node into VariableDeclaration @@ -2143,21 +2214,21 @@ module.exports = function convert(config) { case SyntaxKind.TypeAliasDeclaration: { const typeAliasDeclarator = { type: AST_NODE_TYPES.VariableDeclarator, - id: convertChild(node.name), - init: convertChild(node.type), - range: [node.name.getStart(), node.end] + id: convertChild((node as any).name), + init: convertChild((node as any).type), + range: [(node as any).name.getStart(), (node as any).end] }; - typeAliasDeclarator.loc = nodeUtils.getLocFor( + (typeAliasDeclarator as any).loc = nodeUtils.getLocFor( typeAliasDeclarator.range[0], typeAliasDeclarator.range[1], ast ); // Process typeParameters - if (node.typeParameters && node.typeParameters.length) { - typeAliasDeclarator.typeParameters = convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters + if ((node as any).typeParameters && (node as any).typeParameters.length) { + (typeAliasDeclarator as any).typeParameters = convertTSTypeParametersToTypeParametersDeclaration( + (node as any).typeParameters ); } @@ -2168,7 +2239,7 @@ module.exports = function convert(config) { }); // check for exports - result = nodeUtils.fixExports(node, result, ast); + result = nodeUtils.fixExports(node, result as any, ast); break; } @@ -2177,10 +2248,12 @@ module.exports = function convert(config) { Object.assign(result, { type: AST_NODE_TYPES.TSMethodSignature, optional: nodeUtils.isOptional(node), - computed: nodeUtils.isComputedProperty(node.name), - key: convertChild(node.name), - params: convertParameters(node.parameters), - typeAnnotation: node.type ? convertTypeAnnotation(node.type) : null, + computed: nodeUtils.isComputedProperty((node as any).name), + key: convertChild((node as any).name), + params: convertParameters((node as any).parameters), + typeAnnotation: (node as any).type + ? convertTypeAnnotation((node as any).type) + : null, readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node) || undefined, static: nodeUtils.hasModifier(SyntaxKind.StaticKeyword, node), @@ -2190,12 +2263,12 @@ module.exports = function convert(config) { const accessibility = nodeUtils.getTSNodeAccessibility(node); if (accessibility) { - result.accessibility = accessibility; + (result as any).accessibility = accessibility; } - if (node.typeParameters) { - result.typeParameters = convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters + if ((node as any).typeParameters) { + (result as any).typeParameters = convertTSTypeParametersToTypeParametersDeclaration( + (node as any).typeParameters ); } @@ -2206,12 +2279,12 @@ module.exports = function convert(config) { Object.assign(result, { type: AST_NODE_TYPES.TSPropertySignature, optional: nodeUtils.isOptional(node) || undefined, - computed: nodeUtils.isComputedProperty(node.name), - key: convertChild(node.name), - typeAnnotation: node.type - ? convertTypeAnnotation(node.type) + computed: nodeUtils.isComputedProperty((node as any).name), + key: convertChild((node as any).name), + typeAnnotation: (node as any).type + ? convertTypeAnnotation((node as any).type) : undefined, - initializer: convertChild(node.initializer) || undefined, + initializer: convertChild((node as any).initializer) || undefined, readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node) || undefined, static: @@ -2222,7 +2295,7 @@ module.exports = function convert(config) { const accessibility = nodeUtils.getTSNodeAccessibility(node); if (accessibility) { - result.accessibility = accessibility; + (result as any).accessibility = accessibility; } break; @@ -2231,8 +2304,10 @@ module.exports = function convert(config) { case SyntaxKind.IndexSignature: { Object.assign(result, { type: AST_NODE_TYPES.TSIndexSignature, - index: convertChild(node.parameters[0]), - typeAnnotation: node.type ? convertTypeAnnotation(node.type) : null, + index: convertChild((node as any).parameters[0]), + typeAnnotation: (node as any).type + ? convertTypeAnnotation((node as any).type) + : null, readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node) || undefined, static: nodeUtils.hasModifier(SyntaxKind.StaticKeyword, node), @@ -2242,7 +2317,7 @@ module.exports = function convert(config) { const accessibility = nodeUtils.getTSNodeAccessibility(node); if (accessibility) { - result.accessibility = accessibility; + (result as any).accessibility = accessibility; } break; @@ -2251,13 +2326,15 @@ module.exports = function convert(config) { case SyntaxKind.ConstructSignature: { Object.assign(result, { type: AST_NODE_TYPES.TSConstructSignature, - params: convertParameters(node.parameters), - typeAnnotation: node.type ? convertTypeAnnotation(node.type) : null + params: convertParameters((node as any).parameters), + typeAnnotation: (node as any).type + ? convertTypeAnnotation((node as any).type) + : null }); - if (node.typeParameters) { + if ((node as any).typeParameters) { result.typeParameters = convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters + (node as any).typeParameters ); } @@ -2265,15 +2342,16 @@ module.exports = function convert(config) { } case SyntaxKind.InterfaceDeclaration: { - const interfaceHeritageClauses = node.heritageClauses || []; + const interfaceHeritageClauses = (node as any).heritageClauses || []; let interfaceLastClassToken = interfaceHeritageClauses.length ? interfaceHeritageClauses[interfaceHeritageClauses.length - 1] - : node.name; + : (node as any).name; - if (node.typeParameters && node.typeParameters.length) { - const interfaceLastTypeParameter = - node.typeParameters[node.typeParameters.length - 1]; + if ((node as any).typeParameters && (node as any).typeParameters.length) { + const interfaceLastTypeParameter = (node as any).typeParameters[ + (node as any).typeParameters.length - 1 + ]; if ( !interfaceLastClassToken || @@ -2285,7 +2363,7 @@ module.exports = function convert(config) { ); } result.typeParameters = convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters + (node as any).typeParameters ); } @@ -2301,8 +2379,8 @@ module.exports = function convert(config) { const interfaceBody = { type: AST_NODE_TYPES.TSInterfaceBody, - body: node.members.map(member => convertChild(member)), - range: [interfaceOpenBrace.getStart(), result.range[1]], + body: (node as any).members.map((member: any) => convertChild(member)), + range: [interfaceOpenBrace.getStart(), (result as any).range[1]], loc: nodeUtils.getLocFor(interfaceOpenBrace.getStart(), node.end, ast) }; @@ -2310,7 +2388,7 @@ module.exports = function convert(config) { abstract: hasAbstractKeyword, type: AST_NODE_TYPES.TSInterfaceDeclaration, body: interfaceBody, - id: convertChild(node.name), + id: convertChild((node as any).name), heritage: hasImplementsClause ? interfaceHeritageClauses[0].types.map( convertInterfaceHeritageClause @@ -2326,7 +2404,7 @@ module.exports = function convert(config) { result.decorators = convertDecorators(node.decorators); } // check for exports - result = nodeUtils.fixExports(node, result, ast); + result = nodeUtils.fixExports(node, result as any, ast); break; } @@ -2334,24 +2412,24 @@ module.exports = function convert(config) { case SyntaxKind.FirstTypeNode: Object.assign(result, { type: AST_NODE_TYPES.TSTypePredicate, - parameterName: convertChild(node.parameterName), - typeAnnotation: convertTypeAnnotation(node.type) + parameterName: convertChild((node as any).parameterName), + typeAnnotation: convertTypeAnnotation((node as any).type) }); /** * Specific fix for type-guard location data */ - result.typeAnnotation.loc = result.typeAnnotation.typeAnnotation.loc; - result.typeAnnotation.range = result.typeAnnotation.typeAnnotation.range; + (result as any).typeAnnotation.loc = (result as any).typeAnnotation.typeAnnotation.loc; + (result as any).typeAnnotation.range = (result as any).typeAnnotation.typeAnnotation.range; break; case SyntaxKind.ImportType: Object.assign(result, { type: AST_NODE_TYPES.TSImportType, - isTypeOf: !!node.isTypeOf, - parameter: convertChild(node.argument), - qualifier: convertChild(node.qualifier), - typeParameters: node.typeArguments - ? convertTypeArgumentsToTypeParameters(node.typeArguments) + isTypeOf: !!(node as any).isTypeOf, + parameter: convertChild((node as any).argument), + qualifier: convertChild((node as any).qualifier), + typeParameters: (node as any).typeArguments + ? convertTypeArgumentsToTypeParameters((node as any).typeArguments) : null }); break; @@ -2359,13 +2437,13 @@ module.exports = function convert(config) { case SyntaxKind.EnumDeclaration: { Object.assign(result, { type: AST_NODE_TYPES.TSEnumDeclaration, - id: convertChild(node.name), - members: node.members.map(convertChild) + id: convertChild((node as any).name), + members: (node as any).members.map(convertChild) }); // apply modifiers first... - applyModifiersToResult(node.modifiers); + applyModifiersToResult((node as any).modifiers); // ...then check for exports - result = nodeUtils.fixExports(node, result, ast); + result = nodeUtils.fixExports(node, result as any, ast); /** * Semantically, decorators are not allowed on enum declarations, * but the TypeScript compiler will parse them and produce a valid AST, @@ -2380,10 +2458,10 @@ module.exports = function convert(config) { case SyntaxKind.EnumMember: { Object.assign(result, { type: AST_NODE_TYPES.TSEnumMember, - id: convertChild(node.name) + id: convertChild((node as any).name) }); - if (node.initializer) { - result.initializer = convertChild(node.initializer); + if ((node as any).initializer) { + (result as any).initializer = convertChild((node as any).initializer); } break; } @@ -2398,15 +2476,15 @@ module.exports = function convert(config) { case SyntaxKind.ModuleDeclaration: { Object.assign(result, { type: AST_NODE_TYPES.TSModuleDeclaration, - id: convertChild(node.name) + id: convertChild((node as any).name) }); - if (node.body) { - result.body = convertChild(node.body); + if ((node as any).body) { + result.body = convertChild((node as any).body); } // apply modifiers first... - applyModifiersToResult(node.modifiers); + applyModifiersToResult((node as any).modifiers); // ...then check for exports - result = nodeUtils.fixExports(node, result, ast); + result = nodeUtils.fixExports(node, result as any, ast); break; } @@ -2414,5 +2492,5 @@ module.exports = function convert(config) { deeplyCopy(); } - return result; -}; + return result as any; +} diff --git a/src/estree/experimental.ts b/src/estree/experimental.ts new file mode 100644 index 0000000..103d9ff --- /dev/null +++ b/src/estree/experimental.ts @@ -0,0 +1,30 @@ +import { Node, Expression, MethodDefinition, Property, Class } from './spec'; + +/** + * Decorator + */ +export interface Decorator extends Node { + type: 'Decorator'; + expression: Expression; +} + +/** + * MethodDefinition + */ +export interface ExtendedMethodDefinition extends MethodDefinition { + decorators: Decorator[]; +} + +/** + * Property + */ +export interface ExtendedProperty extends Property { + decorators: Decorator[]; +} + +/** + * Class + */ +export interface ExtendedClass extends Class { + decorators: Decorator[]; +} diff --git a/src/estree/extensions.ts b/src/estree/extensions.ts new file mode 100644 index 0000000..5e03691 --- /dev/null +++ b/src/estree/extensions.ts @@ -0,0 +1,63 @@ +/** + * Based on https://github.com/estree/estree/blob/master/extensions/type-annotations.md + */ + +import { + Identifier, + Function, + ObjectPattern, + ArrayPattern, + RestElement +} from './spec'; + +/** + * Type Annotations + */ + +/** + * Any type annotation. + */ +export interface TypeAnnotation extends Node {} + +/** + * Identifier + * + * The `typeAnnotation` property is used only in the case of variable declarations with type annotations or function arguments with type annotations. + */ +export interface ExtendedIdentifier extends Identifier { + typeAnnotation: TypeAnnotation | null; +} + +/** + * Functions + * + * The `returnType` property is used to specify the type annotation for the return value of the function. + */ +export interface ExtendedFunction extends Function { + returnType: TypeAnnotation | null; +} + +/** + * Patterns + */ + +/** + * ObjectPattern + */ +export interface ExtendedObjectPattern extends ObjectPattern { + typeAnnotation: TypeAnnotation | null; +} + +/** + * ArrayPattern + */ +export interface ExtendedArrayPattern extends ArrayPattern { + typeAnnotation: TypeAnnotation | null; +} + +/** + * RestElement + */ +export interface ExtendedRestElement extends RestElement { + typeAnnotation: TypeAnnotation | null; +} diff --git a/src/estree/spec.ts b/src/estree/spec.ts new file mode 100644 index 0000000..1d2a0a7 --- /dev/null +++ b/src/estree/spec.ts @@ -0,0 +1,985 @@ +/** + * This document specifies the core ESTree AST node types based on: + * - ES5: https://github.com/estree/estree/blob/master/es5.md + * - ES2015: https://github.com/estree/estree/blob/master/es2015.md + * - ES2016: https://github.com/estree/estree/blob/master/es2016.md + * - ES2017: https://github.com/estree/estree/blob/master/es2017.md + * - ES2018: https://github.com/estree/estree/blob/master/es2018.md + */ + +/** + * Node objects + * + * ESTree AST nodes are represented as `Node` objects, which may have any prototype inheritance but which implement the following interface: + */ + +export interface Node { + type: string; + loc: SourceLocation | null; +} + +/** + * The `type` field is a string representing the AST variant type. Each subtype of `Node` is documented below with the specific string of its `type` field. You can use this field to determine which interface a node implements. + * + * The `loc` field represents the source location information of the node. If the node contains no information about the source location, the field is `null`; otherwise it is an object consisting of a start position (the position of the first character of the parsed source region) and an end position (the position of the first character after the parsed source region): + */ +export interface SourceLocation { + source: string | null; + start: Position; + end: Position; +} + +/** + * Each `Position` object consists of a `line` number (1-indexed) and a `column` number (0-indexed): + */ +export interface Position { + line: number; // >= 1 + column: number; // >= 0 +} + +/** + * Identifier + * + * An identifier. Note that an identifier may be an expression or a destructuring pattern. + */ +export interface Identifier extends Expression, Pattern { + type: 'Identifier'; + name: string; +} + +/** + * Literal + * + * A literal token. Note that a literal can be an expression. + */ +export interface Literal extends Expression { + type: 'Literal'; + value: string | boolean | null | number | RegExp; +} + +/** + * RegExpLiteral + * + * The `regex` property allows regexes to be represented in environments that don’t + * support certain flags such as `y` or `u`. In environments that don't support + * these flags `value` will be `null` as the regex can't be represented natively. + */ +export interface RegExpLiteral extends Literal { + regex: { + pattern: string; + flags: string; + }; +} + +/** + * Programs + * + * A complete program source tree. + * + * ES2015+ Parsers must specify sourceType as "module" if the source has been parsed as an ES6 module. Otherwise, sourceType must be "script". + */ +export interface Program extends Node { + type: 'Program'; + body: Array; + sourceType: 'script' | 'module'; +} + +/** + * Functions + * + * A function declaration or expression. + */ +export interface Function extends Node { + id: Identifier | null; + params: Pattern[]; + body: FunctionBody; + generator: boolean; + async: boolean; +} + +/** + * Statements + * + * Any statement. + */ +export interface Statement extends Node {} + +/** + * ExpressionStatement + * + * An expression statement, i.e., a statement consisting of a single expression. + */ +export interface ExpressionStatement extends Statement { + type: 'ExpressionStatement'; + expression: Expression; +} + +/** + * Directive + * + * A directive from the directive prologue of a script or function. + * The `directive` property is the raw string source of the directive without quotes. + */ +export interface Directive extends Node { + type: 'ExpressionStatement'; + expression: Literal; + directive: string; +} + +/** + * BlockStatement + * + * A block statement, i.e., a sequence of statements surrounded by braces. + */ +export interface BlockStatement extends Statement { + type: 'BlockStatement'; + body: Statement[]; +} + +/** + * FunctionBody + * + * The body of a function, which is a block statement that may begin with directives. + */ +export interface FunctionBody extends BlockStatement { + body: Array; +} + +/** + * EmptyStatement + * + * An empty statement, i.e., a solitary semicolon. + */ +export interface EmptyStatement extends Statement { + type: 'EmptyStatement'; +} + +/** + * DebuggerStatement + * + * A `debugger` statement. + */ +export interface DebuggerStatement extends Statement { + type: 'DebuggerStatement'; +} + +/** + * WithStatement + * + * A `with` statement. + */ +export interface WithStatement extends Statement { + type: 'WithStatement'; + object: Expression; + body: Statement; +} + +/** + * Control flow + */ + +/** + * ReturnStatement + * + * A `return` statement. + */ +export interface ReturnStatement extends Statement { + type: 'ReturnStatement'; + argument: Expression | null; +} + +/** + * LabeledStatement + * + * A labeled statement, i.e., a statement prefixed by a `break`/`continue` label. + */ +export interface LabeledStatement extends Statement { + type: 'LabeledStatement'; + label: Identifier; + body: Statement; +} + +/** + * BreakStatement + * + * A `break` statement. + */ +export interface BreakStatement extends Statement { + type: 'BreakStatement'; + label: Identifier | null; +} + +/** + * ContinueStatement + * + * A `continue` statement. + */ +export interface ContinueStatement extends Statement { + type: 'ContinueStatement'; + label: Identifier | null; +} + +/** + * Choice + */ + +/** + * IfStatement + * + * An `if` statement. + */ +export interface IfStatement extends Statement { + type: 'IfStatement'; + test: Expression; + consequent: Statement; + alternate: Statement | null; +} + +/** + * SwitchStatement + * + * A `switch` statement. + */ +export interface SwitchStatement extends Statement { + type: 'SwitchStatement'; + discriminant: Expression; + cases: SwitchCase[]; +} + +/** + * SwitchCase + * + * A `case` (if `test` is an `Expression`) or `default` (if `test === null`) clause in the body of a `switch` statement. + */ +export interface SwitchCase extends Node { + type: 'SwitchCase'; + test: Expression | null; + consequent: Statement[]; +} + +/** + * Exceptions + */ + +/** + * ThrowStatement + * + * A `throw` statement. + */ +export interface ThrowStatement extends Statement { + type: 'ThrowStatement'; + argument: Expression; +} + +/** + * TryStatement + * + * A `try` statement. If `handler` is `null` then `finalizer` must be a `BlockStatement`. + */ +export interface TryStatement extends Statement { + type: 'TryStatement'; + block: BlockStatement; + handler: CatchClause | null; + finalizer: BlockStatement | null; +} + +/** + * CatchClause + * + * A `catch` clause following a `try` block. + * + * The param is null if the catch binding is omitted. E.g., try { foo() } catch { bar() } + */ +export interface CatchClause extends Node { + type: 'CatchClause'; + param: Pattern | null; + body: BlockStatement; +} + +/** + * Loops + */ + +/** + * WhileStatement + * + * A `while` statement. + */ +export interface WhileStatement extends Statement { + type: 'WhileStatement'; + test: Expression; + body: Statement; +} + +/** + * DoWhileStatement + * + * A `do`/`while` statement. + */ +export interface DoWhileStatement extends Statement { + type: 'DoWhileStatement'; + body: Statement; + test: Expression; +} + +/** + * ForStatement + * + * A `for` statement. + */ +export interface ForStatement extends Statement { + type: 'ForStatement'; + init: VariableDeclaration | Expression | null; + test: Expression | null; + update: Expression | null; + body: Statement; +} + +/** + * ForInStatement + * + * A `for`/`in` statement. + */ +export interface ForInStatement extends Statement { + type: 'ForInStatement'; + left: VariableDeclaration | Pattern; + right: Expression; + body: Statement; +} + +/** + * ForOfStatement + * + * A `for`/`of` statement and for-await-of statements, e.g., for await (const x of xs) { + */ +export interface ForOfStatement { + type: 'ForOfStatement'; + left: VariableDeclaration | Pattern; + right: Expression; + body: Statement; + await: boolean; +} + +/** + * Declarations + * + * Any declaration node. Note that declarations are considered statements; this is because declarations can appear in any statement context. + */ +export interface Declaration extends Statement {} + +/** + * FunctionDeclaration + * + * A function declaration. Note that unlike in the parent interface `Function`, the `id` cannot be `null`. + */ +export interface FunctionDeclaration extends Function, Declaration { + type: 'FunctionDeclaration'; + id: Identifier; +} + +/** + * VariableDeclaration + * + * A variable declaration. + */ +export interface VariableDeclaration extends Declaration { + type: 'VariableDeclaration'; + declarations: VariableDeclarator[]; + kind: 'var' | 'let' | 'const'; +} + +/** + * VariableDeclarator + * + * A variable declarator. + */ +export interface VariableDeclarator extends Node { + type: 'VariableDeclarator'; + id: Pattern; + init: Expression | null; +} + +/** + * Expressions + * + * Any expression node. Since the left-hand side of an assignment may be any expression in general, an expression can also be a pattern. + */ +export interface Expression extends Node {} + +/** + * Super + * + * A super pseudo-expression. + */ +export interface Super extends Node { + type: 'Super'; +} + +/** + * ThisExpression + * + * A `this` expression. + */ +export interface ThisExpression extends Expression { + type: 'ThisExpression'; +} + +/** + * SpreadElement + * + * Spread expression, e.g., [head, ...iter, tail], f(head, ...iter, ...tail). + */ +export interface SpreadElement extends Node { + type: 'SpreadElement'; + argument: Expression; +} + +/** + * ArrayExpression + * + * An array expression. + */ +export interface ArrayExpression extends Expression { + type: 'ArrayExpression'; + elements: Array; +} + +/** + * ObjectExpression + * + * An object expression. + * + * Spread properties, e.g., {a: 1, ...obj, b: 2}. + */ +export interface ObjectExpression extends Expression { + type: 'ObjectExpression'; + properties: Array; +} + +/** + * Property + * + * A literal property in an object expression can have either a string or number as its `value`. Ordinary property initializers have a `kind` value `"init"`; getters and setters have the kind values `"get"` and `"set"`, respectively. + */ +export interface Property extends Node { + type: 'Property'; + key: Literal | Identifier | Expression; + value: Expression; + kind: 'init' | 'get' | 'set'; + method: boolean; + shorthand: boolean; + computed: boolean; +} + +/** + * FunctionExpression + * + * A `function` expression. + */ +export interface FunctionExpression extends Function, Expression { + type: 'FunctionExpression'; +} + +/** + * ArrowFunctionExpression + * + * A fat arrow function expression, e.g., let foo = (bar) => { / body / }. + */ +export interface ArrowFunctionExpression extends Expression { + id: Identifier | null; + params: Pattern[]; + type: 'ArrowFunctionExpression'; + body: FunctionBody | Expression; + generator: boolean; + expression: boolean; +} + +/** + * Unary operations + */ + +/** + * UnaryExpression + * + * A unary operator expression. + */ +export interface UnaryExpression extends Expression { + type: 'UnaryExpression'; + operator: UnaryOperator; + prefix: boolean; + argument: Expression; +} + +/** + * UnaryOperator + * + * A unary operator token. + */ +export type UnaryOperator = + | '-' + | '+' + | '!' + | '~' + | 'typeof' + | 'void' + | 'delete'; + +/** + * UpdateExpression + * + * An update (increment or decrement) operator expression. + */ +export interface UpdateExpression extends Expression { + type: 'UpdateExpression'; + operator: UpdateOperator; + argument: Expression; + prefix: boolean; +} + +/** + * UpdateOperator + * + * An update (increment or decrement) operator token. + */ +export type UpdateOperator = '++' | '--'; + +/** + * Binary operations + */ + +/** + * BinaryExpression + * + * A binary operator expression. + */ +export interface BinaryExpression extends Expression { + type: 'BinaryExpression'; + operator: BinaryOperator; + left: Expression; + right: Expression; +} + +/** + * BinaryOperator + * + * A binary operator token. + */ +export type BinaryOperator = + | '==' + | '!=' + | '===' + | '!==' + | '<' + | '<=' + | '>' + | '>=' + | '<<' + | '>>' + | '>>>' + | '+' + | '-' + | '*' + | '/' + | '%' + | '|' + | '^' + | '&' + | 'in' + | 'instanceof' + | '**'; + +/** + * AssignmentExpression + * + * An assignment operator expression. + * + * FROM ESTREE DOCS: + * + * ``` + * FIXME: This describes the Esprima and Acorn behaviors, which is not currently aligned with the SpiderMonkey behavior. + * + * extend interface AssignmentExpression { + * left: Pattern; + * } + * + * Note that pre-ES6 code was allowed to pass references around and so left was much more liberal; an implementation might choose to continue using old definition if it needs to support such legacy code. + * ``` + */ +export interface AssignmentExpression extends Expression { + type: 'AssignmentExpression'; + operator: AssignmentOperator; + left: Pattern | Expression; + right: Expression; +} + +/** + * AssignmentOperator + * + * An assignment operator token. + */ +export type AssignmentOperator = + | '=' + | '+=' + | '-=' + | '*=' + | '/=' + | '%=' + | '<<=' + | '>>=' + | '>>>=' + | '|=' + | '^=' + | '&=' + | '**='; + +/** + * LogicalExpression + * + * A logical operator expression. + */ +export interface LogicalExpression extends Expression { + type: 'LogicalExpression'; + operator: LogicalOperator; + left: Expression; + right: Expression; +} + +/** + * LogicalOperator + * + * A logical operator token. + */ +export type LogicalOperator = '||' | '&&'; + +/** + * MemberExpression + * + * A member expression. If `computed` is `true`, the node corresponds to a computed (`a[b]`) member expression and `property` is an `Expression`. If `computed` is `false`, the node corresponds to a static (`a.b`) member expression and `property` is an `Identifier`. + */ +export interface MemberExpression extends Expression, Pattern { + type: 'MemberExpression'; + object: Expression | Super; + property: Expression; + computed: boolean; +} + +/** + * ConditionalExpression + * + * A conditional expression, i.e., a ternary `?`/`:` expression. + */ +export interface ConditionalExpression extends Expression { + type: 'ConditionalExpression'; + test: Expression; + alternate: Expression; + consequent: Expression; +} + +/** + * CallExpression + * + * A function or method call expression. + */ +export interface CallExpression extends Expression { + type: 'CallExpression'; + callee: Expression | Super; + arguments: Array; +} + +/** + * NewExpression + * + * A `new` expression. + */ +export interface NewExpression extends Expression { + type: 'NewExpression'; + callee: Expression; + arguments: Array; +} + +/** + * SequenceExpression + * + * A sequence expression, i.e., a comma-separated sequence of expressions. + */ +export interface SequenceExpression extends Expression { + type: 'SequenceExpression'; + expressions: Expression[]; +} + +/** + * YieldExpression + * + * A yield expression. + */ +export interface YieldExpression extends Expression { + type: 'YieldExpression'; + argument: Expression | null; + delegate: boolean; +} + +/** + * AwaitExpression + */ +export interface AwaitExpression extends Expression { + type: 'AwaitExpression'; + argument: Expression; +} + +/** + * Template Literals + */ + +/** + * TemplateLiteral + */ +export interface TemplateLiteral extends Expression { + type: 'TemplateLiteral'; + quasis: TemplateElement[]; + expressions: Expression[]; +} + +/** + * TaggedTemplateExpression + */ +export interface TaggedTemplateExpression extends Expression { + type: 'TaggedTemplateExpression'; + tag: Expression; + quasi: TemplateLiteral; +} + +/** + * TemplateElement + * + * If the template literal is tagged and the text has an invalid escape, cooked will be null, e.g., tag`\unicode and \u{55}` + */ +export interface TemplateElement extends Node { + type: 'TemplateElement'; + tail: boolean; + value: { + cooked: string | null; + raw: string; + }; +} + +/** + * Patterns + * + * Destructuring binding and assignment are not part of ES5, but all binding positions accept Pattern to allow for destructuring in ES6. Nevertheless, for ES5, the only Pattern subtype is Identifier. + */ +export interface Pattern extends Node {} + +/** + * AssignmentProperty + */ +export interface AssignmentProperty extends Property { + type: 'Property'; // inherited + value: Pattern; + kind: 'init'; + method: false; +} + +/** + * ObjectPattern + * + * Rest properties, e.g., {a, ...rest} = obj. + */ +export interface ObjectPattern extends Pattern { + type: 'ObjectPattern'; + properties: Array; +} + +/** + * ArrayPattern + */ +export interface ArrayPattern extends Pattern { + type: 'ArrayPattern'; + elements: Array; +} + +/** + * RestElement + */ +export interface RestElement extends Pattern { + type: 'RestElement'; + argument: Pattern; +} + +/** + * AssignmentPattern + */ +export interface AssignmentPattern extends Pattern { + type: 'AssignmentPattern'; + left: Pattern; + right: Expression; +} + +/** + * Classes + */ + +/** + * Class + */ +export interface Class extends Node { + id: Identifier | null; + superClass: Expression | null; + body: ClassBody; +} + +/** + * ClassBody + */ +export interface ClassBody extends Node { + type: 'ClassBody'; + body: MethodDefinition[]; +} + +/** + * MethodDefinition + */ +export interface MethodDefinition extends Node { + type: 'MethodDefinition'; + key: Expression; + value: FunctionExpression; + kind: 'constructor' | 'method' | 'get' | 'set'; + computed: boolean; + static: boolean; +} + +/** + * ClassDeclaration + */ +export interface ClassDeclaration extends Class, Declaration { + type: 'ClassDeclaration'; + id: Identifier; +} + +/** + * ClassExpression + */ +export interface ClassExpression extends Class, Expression { + type: 'ClassExpression'; +} + +/** + * MetaProperty + */ +export interface MetaProperty extends Expression { + type: 'MetaProperty'; + meta: Identifier; + property: Identifier; +} + +/** + * Modules + */ + +/** + * ModuleDeclaration + * + * A module `import` or `export` declaration. + */ +export interface ModuleDeclaration extends Node {} + +/** + * ModuleSpecifier + * + * A specifier in an import or export declaration. + */ +export interface ModuleSpecifier extends Node { + local: Identifier; +} + +/** + * Imports + */ + +/** + * ImportDeclaration + * + * An import declaration, e.g., `import foo from "mod";`. + */ +export interface ImportDeclaration extends ModuleDeclaration { + type: 'ImportDeclaration'; + specifiers: Array< + ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier + >; + source: Literal; +} + +/** + * ImportSpecifier + * + * An imported variable binding, e.g., `{foo}` in `import {foo} from "mod"` or `{foo as bar}` in `import {foo as bar} from "mod"`. The `imported` field refers to the name of the export imported from the module. The `local` field refers to the binding imported into the local module scope. If it is a basic named import, such as in `import {foo} from "mod"`, both `imported` and `local` are equivalent `Identifier` nodes; in this case an `Identifier` node representing `foo`. If it is an aliased import, such as in `import {foo as bar} from "mod"`, the `imported` field is an `Identifier` node representing `foo`, and the `local` field is an `Identifier` node representing `bar`. + */ +export interface ImportSpecifier extends ModuleSpecifier { + type: 'ImportSpecifier'; + imported: Identifier; +} + +/** + * ImportDefaultSpecifier + * + * A default import specifier, e.g., `foo` in `import foo from "mod.js"`. + */ +export interface ImportDefaultSpecifier extends ModuleSpecifier { + type: 'ImportDefaultSpecifier'; +} + +/** + * ImportNamespaceSpecifier + * + * A namespace import specifier, e.g., `* as foo` in `import * as foo from "mod.js"`. + */ +export interface ImportNamespaceSpecifier extends ModuleSpecifier { + type: 'ImportNamespaceSpecifier'; +} + +/** + * Exports + */ + +/** + * ExportNamedDeclaration + * + * An export named declaration, e.g., `export {foo, bar};`, `export {foo} from "mod";` or `export var foo = 1;`. + * + * _Note: Having `declaration` populated with non-empty `specifiers` or non-null `source` results in an invalid state._ + */ +export interface ExportNamedDeclaration extends ModuleDeclaration { + type: 'ExportNamedDeclaration'; + declaration: Declaration | null; + specifiers: [ExportSpecifier]; + source: Literal | null; +} + +/** + * ExportSpecifier + * + * An exported variable binding, e.g., `{foo}` in `export {foo}` or `{bar as foo}` in `export {bar as foo}`. The `exported` field refers to the name exported in the module. The `local` field refers to the binding into the local module scope. If it is a basic named export, such as in `export {foo}`, both `exported` and `local` are equivalent `Identifier` nodes; in this case an `Identifier` node representing `foo`. If it is an aliased export, such as in `export {bar as foo}`, the `exported` field is an `Identifier` node representing `foo`, and the `local` field is an `Identifier` node representing `bar`. + */ +export interface ExportSpecifier extends ModuleSpecifier { + type: 'ExportSpecifier'; + exported: Identifier; +} + +/** + * ExportDefaultDeclaration + * + * An export default declaration, e.g., `export default function () {};` or `export default 1;`. + */ +export interface ExportDefaultDeclaration extends ModuleDeclaration { + type: 'ExportDefaultDeclaration'; + declaration: Declaration | Expression; +} + +/** + * ExportAllDeclaration + * + * An export batch declaration, e.g., `export * from "mod";`. + */ +export interface ExportAllDeclaration extends ModuleDeclaration { + type: 'ExportAllDeclaration'; + source: Literal; +} diff --git a/lib/node-utils.js b/src/node-utils.ts similarity index 67% rename from lib/node-utils.js rename to src/node-utils.ts index 5f83a67..89618cd 100644 --- a/lib/node-utils.js +++ b/src/node-utils.ts @@ -1,22 +1,16 @@ /** - * @fileoverview Utilities for finding and converting TSNodes into ESTreeNodes + * @fileoverview Utilities for finding and converting ts.Nodes into ESTreeNodes * @author James Henry * @copyright jQuery Foundation and other contributors, https://jquery.org/ * MIT License */ - -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const ts = require('typescript'), - unescape = require('lodash.unescape'); - -//------------------------------------------------------------------------------ -// Private -//------------------------------------------------------------------------------ +import ts from 'typescript'; +import unescape from 'lodash.unescape'; +import { + ESTreeNodeLoc, + ESTreeNode, + ESTreeToken +} from './temp-types-based-on-js-source'; const SyntaxKind = ts.SyntaxKind; @@ -40,7 +34,7 @@ const LOGICAL_OPERATORS = [ SyntaxKind.AmpersandAmpersandToken ]; -const TOKEN_TO_TEXT = {}; +const TOKEN_TO_TEXT: { [key: number]: string } = {}; TOKEN_TO_TEXT[SyntaxKind.OpenBraceToken] = '{'; TOKEN_TO_TEXT[SyntaxKind.CloseBraceToken] = '}'; TOKEN_TO_TEXT[SyntaxKind.OpenParenToken] = '('; @@ -104,12 +98,16 @@ TOKEN_TO_TEXT[SyntaxKind.ImportKeyword] = 'import'; /** * Find the first matching child based on the given sourceFile and predicate function. - * @param {TSNode} node The current TSNode - * @param {Object} sourceFile The full AST source file + * @param {ts.Node} node The current ts.Node + * @param {ts.SourceFile} sourceFile The full AST source file * @param {Function} predicate The predicate function to apply to each checked child - * @returns {TSNode|undefined} a matching child TSNode + * @returns {ts.Node|undefined} a matching child ts.Node */ -function findFirstMatchingChild(node, sourceFile, predicate) { +function findFirstMatchingChild( + node: ts.Node, + sourceFile: ts.SourceFile, + predicate: (node: ts.Node) => boolean +): ts.Node | undefined { const children = node.getChildren(sourceFile); for (let i = 0; i < children.length; i++) { const child = children[i]; @@ -125,11 +123,7 @@ function findFirstMatchingChild(node, sourceFile, predicate) { return undefined; } -//------------------------------------------------------------------------------ -// Public -//------------------------------------------------------------------------------ - -module.exports = { +export default { /** * Expose the enum of possible TSNode `kind`s. */ @@ -170,20 +164,20 @@ module.exports = { }; /** - * Returns true if the given TSToken is the assignment operator - * @param {TSToken} operator the operator token + * Returns true if the given ts.Token is the assignment operator + * @param {ts.Token} operator the operator token * @returns {boolean} is assignment */ -function isAssignmentOperator(operator) { +function isAssignmentOperator(operator: ts.Token): boolean { return ASSIGNMENT_OPERATORS.indexOf(operator.kind) > -1; } /** - * Returns true if the given TSToken is a logical operator - * @param {TSToken} operator the operator token + * Returns true if the given ts.Token is a logical operator + * @param {ts.Token} operator the operator token * @returns {boolean} is a logical operator */ -function isLogicalOperator(operator) { +function isLogicalOperator(operator: ts.Token): boolean { return LOGICAL_OPERATORS.indexOf(operator.kind) > -1; } @@ -192,26 +186,26 @@ function isLogicalOperator(operator) { * @param {number} kind the token's SyntaxKind * @returns {string} the token applicable token as a string */ -function getTextForTokenKind(kind) { +function getTextForTokenKind(kind: number): string { return TOKEN_TO_TEXT[kind]; } /** - * Returns true if the given TSNode is a valid ESTree class member - * @param {TSNode} node TypeScript AST node + * Returns true if the given ts.Node is a valid ESTree class member + * @param {ts.Node} node TypeScript AST node * @returns {boolean} is valid ESTree class member */ -function isESTreeClassMember(node) { +function isESTreeClassMember(node: ts.Node): boolean { return node.kind !== SyntaxKind.SemicolonClassElement; } /** - * Checks if a TSNode has a modifier - * @param {SyntaxKind} modifierKind TypeScript SyntaxKind modifier - * @param {TSNode} node TypeScript AST node + * Checks if a ts.Node has a modifier + * @param {number} modifierKind TypeScript SyntaxKind modifier + * @param {ts.Node} node TypeScript AST node * @returns {boolean} has the modifier specified */ -function hasModifier(modifierKind, node) { +function hasModifier(modifierKind: number, node: ts.Node): boolean { return ( !!node.modifiers && !!node.modifiers.length && @@ -220,20 +214,20 @@ function hasModifier(modifierKind, node) { } /** - * Returns true if the given TSToken is a comma - * @param {TSToken} token the TypeScript token + * Returns true if the given ts.Token is a comma + * @param {ts.Token} token the TypeScript token * @returns {boolean} is comma */ -function isComma(token) { +function isComma(token: ts.Token): boolean { return token.kind === SyntaxKind.CommaToken; } /** - * Returns true if the given TSNode is a comment - * @param {TSNode} node the TypeScript node - * @returns {boolean} is commment + * Returns true if the given ts.Node is a comment + * @param {ts.Node} node the TypeScript node + * @returns {boolean} is comment */ -function isComment(node) { +function isComment(node: ts.Node): boolean { return ( node.kind === SyntaxKind.SingleLineCommentTrivia || node.kind === SyntaxKind.MultiLineCommentTrivia @@ -241,20 +235,20 @@ function isComment(node) { } /** - * Returns true if the given TSNode is a JSDoc comment - * @param {TSNode} node the TypeScript node + * Returns true if the given ts.Node is a JSDoc comment + * @param {ts.Node} node the TypeScript node * @returns {boolean} is JSDoc comment */ -function isJSDocComment(node) { +function isJSDocComment(node: ts.Node): boolean { return node.kind === SyntaxKind.JSDocComment; } /** - * Returns the binary expression type of the given TSToken - * @param {TSToken} operator the operator token + * Returns the binary expression type of the given ts.Token + * @param {ts.Token} operator the operator token * @returns {string} the binary expression type */ -function getBinaryExpressionType(operator) { +function getBinaryExpressionType(operator: ts.Token): string { if (isAssignmentOperator(operator)) { return 'AssignmentExpression'; } else if (isLogicalOperator(operator)) { @@ -266,12 +260,16 @@ function getBinaryExpressionType(operator) { /** * Returns line and column data for the given start and end positions, * for the given AST - * @param {Object} start start data - * @param {Object} end end data - * @param {Object} ast the AST object - * @returns {Object} the loc data - */ -function getLocFor(start, end, ast) { + * @param {number} start start data + * @param {number} end end data + * @param {ts.SourceFile} ast the AST object + * @returns {ESTreeNodeLoc} the loc data + */ +function getLocFor( + start: number, + end: number, + ast: ts.SourceFile +): ESTreeNodeLoc { const startLoc = ast.getLineAndCharacterOfPosition(start), endLoc = ast.getLineAndCharacterOfPosition(end); @@ -288,44 +286,47 @@ function getLocFor(start, end, ast) { } /** - * Returns line and column data for the given ESTreeNode or ESTreeToken, + * Returns line and column data for the given ts.Node or ts.Token, * for the given AST - * @param {ESTreeToken|ESTreeNode} nodeOrToken the ESTreeNode or ESTreeToken - * @param {Object} ast the AST object - * @returns {Object} the loc data - */ -function getLoc(nodeOrToken, ast) { + * @param {ts.Token|ts.Node} nodeOrToken the ts.Node or ts.Token + * @param {ts.SourceFile} ast the AST object + * @returns {ESTreeLoc} the loc data + */ +function getLoc( + nodeOrToken: ts.Node | ts.Token, + ast: ts.SourceFile +): ESTreeNodeLoc { return getLocFor(nodeOrToken.getStart(), nodeOrToken.end, ast); } /** - * Returns true if a given TSNode is a token - * @param {TSNode} node the TSNode + * Returns true if a given ts.Node is a token + * @param {ts.Node} node the ts.Node * @returns {boolean} is a token */ -function isToken(node) { +function isToken(node: ts.Node): boolean { return ( node.kind >= SyntaxKind.FirstToken && node.kind <= SyntaxKind.LastToken ); } /** - * Returns true if a given TSNode is a JSX token - * @param {TSNode} node TSNode to be checked + * Returns true if a given ts.Node is a JSX token + * @param {ts.Node} node ts.Node to be checked * @returns {boolean} is a JSX token */ -function isJSXToken(node) { +function isJSXToken(node: ts.Node): boolean { return ( node.kind >= SyntaxKind.JsxElement && node.kind <= SyntaxKind.JsxAttribute ); } /** - * Returns true if the given TSNode.kind value corresponds to a type keyword + * Returns true if the given ts.Node.kind value corresponds to a type keyword * @param {number} kind TypeScript SyntaxKind * @returns {boolean} is a type keyword */ -function isTypeKeyword(kind) { +function isTypeKeyword(kind: number): boolean { switch (kind) { case SyntaxKind.AnyKeyword: case SyntaxKind.BooleanKeyword: @@ -343,11 +344,11 @@ function isTypeKeyword(kind) { } /** - * Returns the declaration kind of the given TSNode - * @param {TSNode} node TypeScript AST node + * Returns the declaration kind of the given ts.Node + * @param {ts.Node} node TypeScript AST node * @returns {string} declaration kind */ -function getDeclarationKind(node) { +function getDeclarationKind(node: ts.Node): string { switch (node.kind) { case SyntaxKind.TypeAliasDeclaration: return 'type'; @@ -365,11 +366,11 @@ function getDeclarationKind(node) { } /** - * Gets a TSNode's accessibility level - * @param {TSNode} node The TSNode + * Gets a ts.Node's accessibility level + * @param {ts.Node} node The ts.Node * @returns {string | null} accessibility "public", "protected", "private", or null */ -function getTSNodeAccessibility(node) { +function getTSNodeAccessibility(node: ts.Node): string | null { const modifiers = node.modifiers; if (!modifiers) { return null; @@ -391,39 +392,46 @@ function getTSNodeAccessibility(node) { } /** - * Returns true if the given TSNode has the modifier flag set which corresponds + * Returns true if the given ts.Node has the modifier flag set which corresponds * to the static keyword. - * @param {TSNode} node The TSNode + * @param {ts.Node} node The ts.Node * @returns {boolean} whether or not the static modifier flag is set */ -function hasStaticModifierFlag(node) { +function hasStaticModifierFlag(node: ts.Node): boolean { /** * TODO: Remove dependency on private TypeScript method */ - return Boolean(ts.getModifierFlags(node) & ts.ModifierFlags.Static); + return Boolean((ts as any).getModifierFlags(node) & ts.ModifierFlags.Static); } /** * Finds the next token based on the previous one and its parent - * @param {TSToken} previousToken The previous TSToken - * @param {TSNode} parent The parent TSNode - * @returns {TSToken} the next TSToken - */ -function findNextToken(previousToken, parent) { + * @param {ts.Token} previousToken The previous ts.Token + * @param {ts.Node} parent The parent ts.Node + * @returns {ts.Token} the next TSToken + */ +function findNextToken( + previousToken: ts.Token, + parent: ts.Node +): ts.Token { /** * TODO: Remove dependency on private TypeScript method */ - return ts.findNextToken(previousToken, parent); + return (ts as any).findNextToken(previousToken, parent); } /** * Find the first matching token based on the given predicate function. - * @param {TSToken} previousToken The previous TSToken - * @param {TSNode} parent The parent TSNode + * @param {ts.Token} previousToken The previous ts.Token + * @param {ts.Node} parent The parent ts.Node * @param {Function} predicate The predicate function to apply to each checked token - * @returns {TSToken|undefined} a matching TSToken + * @returns {ts.Token|undefined} a matching ts.Token */ -function findFirstMatchingToken(previousToken, parent, predicate) { +function findFirstMatchingToken( + previousToken: ts.Token, + parent: ts.Node, + predicate: (node: ts.Node) => boolean +): ts.Token | undefined { while (previousToken) { if (predicate(previousToken)) { return previousToken; @@ -434,23 +442,30 @@ function findFirstMatchingToken(previousToken, parent, predicate) { } /** - * Finds the first child TSNode which matches the given kind - * @param {TSNode} node The parent TSNode - * @param {number} kind The TSNode kind to match against - * @param {Object} sourceFile The full AST source file - * @returns {TSNode|undefined} a matching TSNode + * Finds the first child ts.Node which matches the given kind + * @param {ts.Node} node The parent ts.Node + * @param {number} kind The ts.Node kind to match against + * @param {ts.SourceFile} sourceFile The full AST source file + * @returns {ts.Node|undefined} a matching ts.Node */ -function findChildOfKind(node, kind, sourceFile) { +function findChildOfKind( + node: ts.Node, + kind: number, + sourceFile: ts.SourceFile +): ts.Node | undefined { return findFirstMatchingChild(node, sourceFile, child => child.kind === kind); } /** * Find the first matching ancestor based on the given predicate function. - * @param {TSNode} node The current TSNode + * @param {ts.Node} node The current ts.Node * @param {Function} predicate The predicate function to apply to each checked ancestor - * @returns {TSNode|undefined} a matching parent TSNode + * @returns {ts.Node|undefined} a matching parent ts.Node */ -function findFirstMatchingAncestor(node, predicate) { +function findFirstMatchingAncestor( + node: ts.Node, + predicate: (node: ts.Node) => boolean +): ts.Node | undefined { while (node) { if (predicate(node)) { return node; @@ -461,21 +476,21 @@ function findFirstMatchingAncestor(node, predicate) { } /** - * Finds the first parent TSNode which mastches the given kind - * @param {TSNode} node The current TSNode - * @param {number} kind The TSNode kind to match against - * @returns {TSNode|undefined} a matching parent TSNode + * Finds the first parent ts.Node which matches the given kind + * @param {ts.Node} node The current ts.Node + * @param {number} kind The ts.Node kind to match against + * @returns {ts.Node|undefined} a matching parent ts.Node */ -function findAncestorOfKind(node, kind) { +function findAncestorOfKind(node: ts.Node, kind: number): ts.Node | undefined { return findFirstMatchingAncestor(node, parent => parent.kind === kind); } /** - * Returns true if a given TSNode has a JSX token within its hierarchy - * @param {TSNode} node TSNode to be checked + * Returns true if a given ts.Node has a JSX token within its hierarchy + * @param {ts.Node} node ts.Node to be checked * @returns {boolean} has JSX ancestor */ -function hasJSXAncestor(node) { +function hasJSXAncestor(node: ts.Node): boolean { return !!findFirstMatchingAncestor(node, isJSXToken); } @@ -484,37 +499,37 @@ function hasJSXAncestor(node) { * @param {string} text The escaped string literal text. * @returns {string} The unescaped string literal text. */ -function unescapeStringLiteralText(text) { +function unescapeStringLiteralText(text: string): string { return unescape(text); } /** - * Returns true if a given TSNode is a computed property - * @param {TSNode} node TSNode to be checked + * Returns true if a given ts.Node is a computed property + * @param {ts.Node} node ts.Node to be checked * @returns {boolean} is Computed Property */ -function isComputedProperty(node) { +function isComputedProperty(node: ts.Node): boolean { return node.kind === SyntaxKind.ComputedPropertyName; } /** - * Returns true if a given TSNode is optional (has QuestionToken) - * @param {TSNode} node TSNode to be checked + * Returns true if a given ts.Node is optional (has QuestionToken) + * @param {ts.Node} node ts.Node to be checked * @returns {boolean} is Optional */ -function isOptional(node) { +function isOptional(node: any): boolean { return node.questionToken ? node.questionToken.kind === SyntaxKind.QuestionToken : false; } /** - * Returns true if the given TSNode is within the context of a "typeAnnotation", + * Returns true if the given ts.Node is within the context of a "typeAnnotation", * which effectively means - is it coming from its parent's `type` or `types` property - * @param {TSNode} node TSNode to be checked + * @param {ts.Node} node ts.Node to be checked * @returns {boolean} is within "typeAnnotation context" */ -function isWithinTypeAnnotation(node) { +function isWithinTypeAnnotation(node: any): boolean { return ( node.parent.type === node || (node.parent.types && node.parent.types.indexOf(node) > -1) @@ -522,13 +537,17 @@ function isWithinTypeAnnotation(node) { } /** - * Fixes the exports of the given TSNode - * @param {TSNode} node the TSNode - * @param {Object} result result - * @param {Object} ast the AST - * @returns {TSNode} the TSNode with fixed exports + * Fixes the exports of the given ts.Node + * @param {ts.Node} node the ts.Node + * @param {ESTreeNode} result result + * @param {ts.SourceFile} ast the AST + * @returns {ESTreeNode} the ESTreeNode with fixed exports */ -function fixExports(node, result, ast) { +function fixExports( + node: ts.Node, + result: ESTreeNode, + ast: ts.SourceFile +): ESTreeNode { // check for exports if (node.modifiers && node.modifiers[0].kind === SyntaxKind.ExportKeyword) { const exportKeyword = node.modifiers[0], @@ -545,7 +564,7 @@ function fixExports(node, result, ast) { ? 'ExportDefaultDeclaration' : 'ExportNamedDeclaration'; - const newResult = { + const newResult: any = { type: declarationType, declaration: result, range: [exportKeyword.getStart(), result.range[1]], @@ -564,11 +583,11 @@ function fixExports(node, result, ast) { } /** - * Returns the type of a given ESTreeToken - * @param {ESTreeToken} token the ESTreeToken + * Returns the type of a given ts.Token + * @param {ts.Token} token the ts.Token * @returns {string} the token type */ -function getTokenType(token) { +function getTokenType(token: any): string { // Need two checks for keywords since some are also identifiers if (token.originalKeywordKind) { switch (token.originalKeywordKind) { @@ -671,19 +690,19 @@ function getTokenType(token) { } /** - * Extends and formats a given ESTreeToken, for a given AST - * @param {ESTreeToken} token the ESTreeToken - * @param {Object} ast the AST object + * Extends and formats a given ts.Token, for a given AST + * @param {ts.Token} token the ts.Token + * @param {ts.SourceFile} ast the AST object * @returns {ESTreeToken} the converted ESTreeToken */ -function convertToken(token, ast) { +function convertToken(token: ts.Token, ast: ts.SourceFile): ESTreeToken { const start = token.kind === SyntaxKind.JsxText ? token.getFullStart() : token.getStart(), end = token.getEnd(), value = ast.text.slice(start, end), - newToken = { + newToken: any = { type: getTokenType(token), value, range: [start, end], @@ -702,16 +721,16 @@ function convertToken(token, ast) { /** * Converts all tokens for the given AST - * @param {Object} ast the AST object + * @param {ts.SourceFile} ast the AST object * @returns {ESTreeToken[]} the converted ESTreeTokens */ -function convertTokens(ast) { - const result = []; +function convertTokens(ast: ts.SourceFile): ESTreeToken[] { + const result: ESTreeToken[] = []; /** - * @param {TSNode} node the TSNode - * @returns {undefined} + * @param {ts.Node} node the ts.Node + * @returns {void} */ - function walk(node) { + function walk(node: ts.Node): void { // TypeScript generates tokens for types in JSDoc blocks. Comment tokens // and their children should not be walked or added to the resulting tokens list. if (isComment(node) || isJSDocComment(node)) { @@ -734,20 +753,24 @@ function convertTokens(ast) { /** * Get container token node between range - * @param {Object} ast the AST object - * @param {int} start The index at which the comment starts. - * @param {int} end The index at which the comment ends. - * @returns {TSToken} typescript container token + * @param {ts.SourceFile} ast the AST object + * @param {number} start The index at which the comment starts. + * @param {number} end The index at which the comment ends. + * @returns {ts.Token} typescript container token * @private */ -function getNodeContainer(ast, start, end) { +function getNodeContainer( + ast: ts.SourceFile, + start: number, + end: number +): ts.Token { let container = null; /** - * @param {TSNode} node the TSNode - * @returns {undefined} + * @param {ts.Node} node the ts.Node + * @returns {void} */ - function walk(node) { + function walk(node: ts.Node): void { const nodeStart = node.pos; const nodeEnd = node.end; @@ -761,16 +784,16 @@ function getNodeContainer(ast, start, end) { } walk(ast); - return container; + return (container as unknown) as ts.Token; } /** - * @param {Object} ast the AST object - * @param {int} start the index at which the error starts + * @param {ts.SourceFile} ast the AST object + * @param {number} start the index at which the error starts * @param {string} message the error message * @returns {Object} converted error object */ -function createError(ast, start, message) { +function createError(ast: ts.SourceFile, start: number, message: string) { const loc = ast.getLineAndCharacterOfPosition(start); return { index: start, diff --git a/parser.js b/src/parser.ts similarity index 80% rename from parser.js rename to src/parser.ts index 3268ebc..3d7c5d0 100644 --- a/parser.js +++ b/src/parser.ts @@ -5,30 +5,28 @@ * @copyright jQuery Foundation and other contributors, https://jquery.org/ * MIT License */ +import semver from 'semver'; +import ts from 'typescript'; +import convert from './ast-converter'; +import { Extra, ParserOptions } from './temp-types-based-on-js-source'; -'use strict'; +const packageJSON = require('../package.json'); -const astNodeTypes = require('./lib/ast-node-types'), - ts = require('typescript'), - convert = require('./lib/ast-converter'), - semver = require('semver'); - -const SUPPORTED_TYPESCRIPT_VERSIONS = require('./package.json').devDependencies - .typescript; +const SUPPORTED_TYPESCRIPT_VERSIONS = packageJSON.devDependencies.typescript; const ACTIVE_TYPESCRIPT_VERSION = ts.version; const isRunningSupportedTypeScriptVersion = semver.satisfies( ACTIVE_TYPESCRIPT_VERSION, SUPPORTED_TYPESCRIPT_VERSIONS ); -let extra; +let extra: Extra; let warnedAboutTSVersion = false; /** * Resets the extra config object * @returns {void} */ -function resetExtra() { +function resetExtra(): void { extra = { tokens: null, range: false, @@ -38,7 +36,9 @@ function resetExtra() { strict: false, jsx: false, useJSXTextNode: false, - log: console.log + log: console.log, + errorOnUnknownASTType: false, + code: '' }; } @@ -48,14 +48,14 @@ function resetExtra() { /** * Parses the given source code to produce a valid AST - * @param {mixed} code TypeScript code - * @param {Object} options configuration object for the parser + * @param {string} code TypeScript code + * @param {ParserOptions} options configuration object for the parser * @returns {Object} the AST */ -function generateAST(code, options) { +function generateAST(code: string, options: ParserOptions): any { const toString = String; - if (typeof code !== 'string' && !(code instanceof String)) { + if (typeof code !== 'string' && !((code as any) instanceof String)) { code = toString(code); } @@ -140,17 +140,20 @@ function generateAST(code, options) { getNewLine() { return '\n'; }, - getSourceFile(filename) { + getSourceFile(filename: string) { return ts.createSourceFile(filename, code, ts.ScriptTarget.Latest, true); }, readFile() { - return null; + return undefined; }, useCaseSensitiveFileNames() { return true; }, writeFile() { return null; + }, + getDirectories() { + return []; } }; @@ -159,7 +162,7 @@ function generateAST(code, options) { { noResolve: true, target: ts.ScriptTarget.Latest, - jsx: extra.jsx ? 'preserve' : undefined + jsx: extra.jsx ? ts.JsxEmit.Preserve : undefined }, compilerHost ); @@ -174,10 +177,11 @@ function generateAST(code, options) { // Public //------------------------------------------------------------------------------ -exports.version = require('./package.json').version; +export { AST_NODE_TYPES } from './ast-node-types'; +export { version }; -exports.parse = function parse(code, options) { - return generateAST(code, options); -}; +const version = packageJSON.version; -exports.AST_NODE_TYPES = astNodeTypes; +export function parse(code: string, options: ParserOptions) { + return generateAST(code, options); +} diff --git a/src/temp-types-based-on-js-source.ts b/src/temp-types-based-on-js-source.ts new file mode 100644 index 0000000..9fe32a5 --- /dev/null +++ b/src/temp-types-based-on-js-source.ts @@ -0,0 +1,80 @@ +/** + * NOTE: The following types are inferred from usage within the original JavaScript source. + * + * They will be gradually replaced with the more accurate types derived from the ESTree spec, and its + * applicable extensions + */ +export interface ESTreeToken { + type: string; + value: string; + range: number[]; + loc: ESTreeNodeLoc; + regex?: { + pattern: string; + flags: string; + }; + object?: any; + property?: any; + name?: any; +} + +export interface ESTreeNode { + type: string; + loc: ESTreeNodeLoc; + range: number[]; + declaration?: ESTreeNode; + specifiers?: any[]; + source?: any; + typeAnnotation?: ESTreeNode | null; + typeParameters?: any; + id?: ESTreeNode | null; + expression?: ESTreeNode | null; + decorators?: any; + const?: boolean; + declare?: boolean; + modifiers?: any; + body?: any; + params?: any; + accessibility?: any; + readonly?: boolean; + static?: boolean; + export?: boolean; + parameter?: any; +} + +export interface ESTreeComment extends ESTreeNode {} + +export interface LineAndColumnData { + line: number; + column: number; +} + +export interface ESTreeNodeLoc { + start: LineAndColumnData; + end: LineAndColumnData; +} + +export interface Extra { + errorOnUnknownASTType: boolean; + useJSXTextNode: boolean; + tokens: null | ESTreeToken[]; + comment: boolean; + code: string; + range: boolean; + loc: boolean; + comments: ESTreeComment[]; + strict: boolean; + jsx: boolean; + log: Function; +} + +export interface ParserOptions { + range: boolean; + loc: boolean; + tokens: boolean; + comment: boolean; + jsx: boolean; + errorOnUnknownASTType: boolean; + useJSXTextNode: boolean; + loggerFn: Function | false; +} diff --git a/tests/ast-alignment/fixtures-to-test.js b/tests/ast-alignment/fixtures-to-test.ts similarity index 92% rename from tests/ast-alignment/fixtures-to-test.js rename to tests/ast-alignment/fixtures-to-test.ts index b649810..b6349be 100644 --- a/tests/ast-alignment/fixtures-to-test.js +++ b/tests/ast-alignment/fixtures-to-test.ts @@ -1,14 +1,30 @@ -'use strict'; +import glob from 'glob'; +import path from 'path'; +import jsxKnownIssues from '../jsx-known-issues'; -const path = require('path'); -const glob = require('glob'); +interface Fixture { + filename: string; + config?: any; +} + +interface FixturePatternConfig { + pattern: string; + config?: { + babylonParserOptions?: any; + typeScriptESTreeOptions?: any; + }; +} + +interface CreateFixturePatternConfig { + ignore?: string[]; + fileType?: string; + parseWithSourceTypeModule?: string[]; +} /** * JSX fixtures which have known issues for typescript-estree */ -const jsxFilesWithKnownIssues = require('../jsx-known-issues').map(f => - f.replace('jsx/', '') -); +const jsxFilesWithKnownIssues = jsxKnownIssues.map(f => f.replace('jsx/', '')); /** * Current random error difference on jsx/invalid-no-tag-name.src.js @@ -25,39 +41,30 @@ jsxFilesWithKnownIssues.push('invalid-no-tag-name'); jsxFilesWithKnownIssues.push('shorthand-fragment'); jsxFilesWithKnownIssues.push('shorthand-fragment-with-child'); -/** - * Custom constructs/concepts used in this file: - * - * type Pattern = string; // used for node-glob matching - * - * interface FixturePatternConfig { - * pattern: Pattern, - * config?: { - * babylonParserOptions: any, - * typeScriptESTreeOptions: any - * } - * } - */ - /** * Globally track which fixtures need to be parsed with sourceType: "module" * so that they can be added with the correct FixturePatternConfig */ -let fixturesRequiringSourceTypeModule = []; +let fixturesRequiringSourceTypeModule: FixturePatternConfig[] = []; /** * Utility to generate a FixturePatternConfig object containing the glob pattern for specific subsections of the fixtures/ directory, * including the capability to ignore specific nested patterns. * * @param {string} fixturesSubPath the sub-path within the fixtures/ directory - * @param {Object?} config an optional configuration object with optional sub-paths to ignore and/or parse with sourceType: module + * @param {CreateFixturePatternConfig?} config an optional configuration object with optional sub-paths to ignore and/or parse with sourceType: module * @returns {FixturePatternConfig} an object containing the glob pattern and optional additional config */ -function createFixturePatternConfigFor(fixturesSubPath, config) { +function createFixturePatternConfigFor( + fixturesSubPath: string, + config?: CreateFixturePatternConfig +): FixturePatternConfig { if (!fixturesSubPath) { - return ''; + throw new Error( + 'fixtureSubPath was not provided for the current fixture pattern' + ); } - config = config || {}; + config = config || ({} as CreateFixturePatternConfig); config.ignore = config.ignore || []; config.fileType = config.fileType || 'js'; config.parseWithSourceTypeModule = config.parseWithSourceTypeModule || []; @@ -72,12 +79,17 @@ function createFixturePatternConfigFor(fixturesSubPath, config) { * ignore list, and then add their full config into the global array. */ if (config.parseWithSourceTypeModule.length) { - config.ignore = [].concat(config.ignore, config.parseWithSourceTypeModule); - fixturesRequiringSourceTypeModule = [].concat( + config.ignore = ([] as string[]).concat( + config.ignore, + config.parseWithSourceTypeModule + ); + fixturesRequiringSourceTypeModule = ([] as FixturePatternConfig[]).concat( fixturesRequiringSourceTypeModule, config.parseWithSourceTypeModule.map(fixture => ({ // It needs to be the full path from within fixtures/ for the pattern - pattern: `${fixturesSubPath}/${fixture}.src.${config.fileType}`, + pattern: `${fixturesSubPath}/${fixture}.src.${ + (config as CreateFixturePatternConfig).fileType + }`, config: { babylonParserOptions: { sourceType: 'module' } } })) ); @@ -532,18 +544,12 @@ let fixturePatternConfigsToTest = [ /** * Add in all the fixtures which need to be parsed with sourceType: "module" */ -fixturePatternConfigsToTest = [].concat( +fixturePatternConfigsToTest = ([] as FixturePatternConfig[]).concat( fixturePatternConfigsToTest, fixturesRequiringSourceTypeModule ); -/** - * interface Fixture { - * filename: string, - * config?: any - * } - */ -const fixturesToTest = []; +const fixturesToTest: Fixture[] = []; const fixturesDirPath = path.join(__dirname, '../fixtures'); /** @@ -565,4 +571,4 @@ fixturePatternConfigsToTest.forEach(fixturePatternConfig => { }); }); -module.exports = fixturesToTest; +export { fixturesToTest }; diff --git a/tests/ast-alignment/jest.config.js b/tests/ast-alignment/jest.config.js index b2a29f2..b8b2780 100644 --- a/tests/ast-alignment/jest.config.js +++ b/tests/ast-alignment/jest.config.js @@ -2,5 +2,9 @@ module.exports = { testEnvironment: 'node', - testRegex: 'spec\\.js$' + transform: { + '.+\\.tsx?$': 'ts-jest' + }, + testRegex: 'spec\\.ts$', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'] }; diff --git a/tests/ast-alignment/parse.js b/tests/ast-alignment/parse.ts similarity index 64% rename from tests/ast-alignment/parse.js rename to tests/ast-alignment/parse.ts index d350cb8..d875556 100644 --- a/tests/ast-alignment/parse.js +++ b/tests/ast-alignment/parse.ts @@ -1,19 +1,19 @@ -'use strict'; +import codeFrame from 'babel-code-frame'; +import * as parser from '../../src/parser'; +import { ParserOptions } from '../../src/temp-types-based-on-js-source'; +import * as parseUtils from './utils'; -const codeFrame = require('babel-code-frame'); -const parseUtils = require('./utils'); - -function createError(message, line, column) { +function createError(message: string, line: number, column: number) { // Construct an error similar to the ones thrown by Babylon. const error = new SyntaxError(`${message} (${line}:${column})`); - error.loc = { + (error as any).loc = { line, column }; return error; } -function parseWithBabylonPluginTypescript(text, parserOptions) { +function parseWithBabylonPluginTypescript(text: string, parserOptions?: any) { parserOptions = parserOptions || {}; const babylon = require('babylon'); return babylon.parse( @@ -40,36 +40,41 @@ function parseWithBabylonPluginTypescript(text, parserOptions) { ); } -function parseWithTypeScriptESTree(text, parserOptions) { - parserOptions = parserOptions || {}; - const parser = require('../../parser'); +function parseWithTypeScriptESTree( + text: string, + parserOptions?: ParserOptions +) { + parserOptions = parserOptions || ({} as ParserOptions); try { - return parser.parse( - text, - Object.assign( - { - loc: true, - range: true, - tokens: false, - comment: false, - useJSXTextNode: true, - errorOnUnknownASTType: true, - jsx: true - }, - parserOptions - ) - ); + return parser.parse(text, Object.assign( + { + loc: true, + range: true, + tokens: false, + comment: false, + useJSXTextNode: true, + errorOnUnknownASTType: true, + jsx: true + }, + parserOptions + ) as any); } catch (e) { throw createError(e.message, e.lineNumber, e.column); } } -module.exports = function parse(text, opts) { +interface ASTComparisonParseOptions { + parser: string; + typeScriptESTreeOptions?: ParserOptions; + babylonParserOptions?: any; +} + +export function parse(text: string, opts: ASTComparisonParseOptions) { /** * Always return a consistent interface, there will be times when we expect both * parsers to fail to parse the invalid source. */ - const result = { + const result: any = { parseError: null, ast: null }; @@ -103,4 +108,4 @@ module.exports = function parse(text, opts) { } return result; -}; +} diff --git a/tests/ast-alignment/spec.js b/tests/ast-alignment/spec.ts similarity index 84% rename from tests/ast-alignment/spec.js rename to tests/ast-alignment/spec.ts index b78b017..bf1359a 100644 --- a/tests/ast-alignment/spec.js +++ b/tests/ast-alignment/spec.ts @@ -1,10 +1,7 @@ -'use strict'; - -const fs = require('fs'); - -const parse = require('./parse'); -const parseUtils = require('./utils'); -const fixturesToTest = require('./fixtures-to-test'); +import fs from 'fs'; +import { fixturesToTest } from './fixtures-to-test'; +import { parse } from './parse'; +import * as parseUtils from './utils'; fixturesToTest.forEach(fixture => { const filename = fixture.filename; @@ -40,7 +37,7 @@ fixturesToTest.forEach(fixture => { * FAIL: babylon errored but typescript-estree did not */ if (!typeScriptESTreeResult.parseError) { - test(`TEST FAIL [BABYLON ERRORED, BUT TSEP DID NOT] - ${filename}`, () => { + it(`TEST FAIL [BABYLON ERRORED, BUT TSEP DID NOT] - ${filename}`, () => { expect(typeScriptESTreeResult.parseError).toEqual( babylonTypeScriptPluginResult.parseError ); @@ -50,7 +47,7 @@ fixturesToTest.forEach(fixture => { /** * Both parsers errored - this is OK as long as the errors are of the same "type" */ - test(`[Both parsers error as expected] - ${filename}`, () => { + it(`[Both parsers error as expected] - ${filename}`, () => { expect(babylonTypeScriptPluginResult.parseError.name).toEqual( typeScriptESTreeResult.parseError.name ); @@ -62,7 +59,7 @@ fixturesToTest.forEach(fixture => { * FAIL: typescript-estree errored but babylon did not */ if (typeScriptESTreeResult.parseError) { - test(`TEST FAIL [TSEP ERRORED, BUT BABYLON DID NOT] - ${filename}`, () => { + it(`TEST FAIL [TSEP ERRORED, BUT BABYLON DID NOT] - ${filename}`, () => { expect(babylonTypeScriptPluginResult.parseError).toEqual( typeScriptESTreeResult.parseError ); @@ -73,7 +70,7 @@ fixturesToTest.forEach(fixture => { /** * No errors, assert the two ASTs match */ - test(`${filename}`, () => { + it(`${filename}`, () => { expect(babylonTypeScriptPluginResult.ast).toBeTruthy(); expect(typeScriptESTreeResult.ast).toBeTruthy(); /** diff --git a/tests/ast-alignment/utils.js b/tests/ast-alignment/utils.ts similarity index 86% rename from tests/ast-alignment/utils.js rename to tests/ast-alignment/utils.ts index bd1acca..0d1b1ca 100644 --- a/tests/ast-alignment/utils.js +++ b/tests/ast-alignment/utils.ts @@ -1,6 +1,4 @@ -'use strict'; - -const isPlainObject = require('lodash.isplainobject'); +import isPlainObject from 'lodash.isplainobject'; /** * By default, pretty-format (within Jest matchers) retains the names/types of nodes from the babylon AST, @@ -10,7 +8,7 @@ const isPlainObject = require('lodash.isplainobject'); * @param {Object} ast raw AST * @returns {Object} normalized AST */ -function normalizeNodeTypes(ast) { +export function normalizeNodeTypes(ast: any): any { return JSON.parse(JSON.stringify(ast)); } @@ -20,9 +18,12 @@ function normalizeNodeTypes(ast) { * @param {Object[]} keysToOmit Names and predicate functions use to determine what keys to omit from the final object * @returns {Object} formatted object */ -function omitDeep(obj, keysToOmit) { +export function omitDeep( + obj: any, + keysToOmit: { key: string; predicate: Function }[] +): any { keysToOmit = keysToOmit || []; - function shouldOmit(keyName, val) { + function shouldOmit(keyName: string, val: any) { if (!keysToOmit || !keysToOmit.length) { return false; } @@ -39,10 +40,10 @@ function omitDeep(obj, keysToOmit) { if (!obj.hasOwnProperty(key)) { continue; } - const val = obj[key]; + const val = (obj as any)[key]; if (isPlainObject(val)) { if (shouldOmit(key, val)) { - delete obj[key]; + delete (obj as any)[key]; // re-run with the same arguments // in case the object has multiple keys to omit return omitDeep(obj, keysToOmit); @@ -50,7 +51,7 @@ function omitDeep(obj, keysToOmit) { omitDeep(val, keysToOmit); } else if (Array.isArray(val)) { if (shouldOmit(key, val)) { - delete obj[key]; + delete (obj as any)[key]; // re-run with the same arguments // in case the object has multiple keys to omit return omitDeep(obj, keysToOmit); @@ -59,7 +60,7 @@ function omitDeep(obj, keysToOmit) { omitDeep(i, keysToOmit); } } else if (shouldOmit(key, val)) { - delete obj[key]; + delete (obj as any)[key]; // re-run with the same arguments // in case the object has multiple keys to omit return omitDeep(obj, keysToOmit); @@ -72,7 +73,7 @@ function omitDeep(obj, keysToOmit) { * Common predicates for Babylon AST preprocessing */ const always = () => true; -const ifNumber = val => typeof val === 'number'; +const ifNumber = (val: any) => typeof val === 'number'; /** * - Babylon wraps the "Program" node in an extra "File" node, normalize this for simplicity for now... @@ -82,7 +83,7 @@ const ifNumber = val => typeof val === 'number'; * @param {Object} ast raw babylon AST * @returns {Object} processed babylon AST */ -function preprocessBabylonAST(ast) { +export function preprocessBabylonAST(ast: any): any { return omitDeep(ast.program, [ { key: 'start', @@ -139,14 +140,8 @@ function preprocessBabylonAST(ast) { * @param {Object} ast the raw AST with a Program node at its top level * @returns {Object} the ast with the location data removed from the Program node */ -function removeLocationDataFromProgramNode(ast) { +export function removeLocationDataFromProgramNode(ast: any) { delete ast.loc; delete ast.range; return ast; } - -module.exports = { - normalizeNodeTypes, - preprocessBabylonAST, - removeLocationDataFromProgramNode -}; diff --git a/tests/jsx-known-issues.js b/tests/jsx-known-issues.ts similarity index 93% rename from tests/jsx-known-issues.js rename to tests/jsx-known-issues.ts index 4285552..6f1178c 100644 --- a/tests/jsx-known-issues.js +++ b/tests/jsx-known-issues.ts @@ -1,9 +1,7 @@ -'use strict'; - /** * Export the list to allow it to be used within both unit and AST comparison tests */ -module.exports = [ +export default [ 'jsx/embedded-tags', // https://github.com/Microsoft/TypeScript/issues/7410 'jsx/namespaced-attribute-and-value-inserted', // https://github.com/Microsoft/TypeScript/issues/7411 'jsx/namespaced-name-and-attribute', // https://github.com/Microsoft/TypeScript/issues/7411 diff --git a/tests/lib/__snapshots__/basics.js.snap b/tests/lib/__snapshots__/basics.ts.snap similarity index 100% rename from tests/lib/__snapshots__/basics.js.snap rename to tests/lib/__snapshots__/basics.ts.snap diff --git a/tests/lib/__snapshots__/comments.js.snap b/tests/lib/__snapshots__/comments.ts.snap similarity index 100% rename from tests/lib/__snapshots__/comments.js.snap rename to tests/lib/__snapshots__/comments.ts.snap diff --git a/tests/lib/__snapshots__/ecma-features.js.snap b/tests/lib/__snapshots__/ecma-features.ts.snap similarity index 100% rename from tests/lib/__snapshots__/ecma-features.js.snap rename to tests/lib/__snapshots__/ecma-features.ts.snap diff --git a/tests/lib/__snapshots__/jsx.js.snap b/tests/lib/__snapshots__/jsx.ts.snap similarity index 100% rename from tests/lib/__snapshots__/jsx.js.snap rename to tests/lib/__snapshots__/jsx.ts.snap diff --git a/tests/lib/__snapshots__/parse.js.snap b/tests/lib/__snapshots__/parse.ts.snap similarity index 100% rename from tests/lib/__snapshots__/parse.js.snap rename to tests/lib/__snapshots__/parse.ts.snap diff --git a/tests/lib/__snapshots__/tsx.js.snap b/tests/lib/__snapshots__/tsx.ts.snap similarity index 100% rename from tests/lib/__snapshots__/tsx.js.snap rename to tests/lib/__snapshots__/tsx.ts.snap diff --git a/tests/lib/__snapshots__/typescript.js.snap b/tests/lib/__snapshots__/typescript.ts.snap similarity index 100% rename from tests/lib/__snapshots__/typescript.js.snap rename to tests/lib/__snapshots__/typescript.ts.snap diff --git a/tests/lib/basics.js b/tests/lib/basics.ts similarity index 75% rename from tests/lib/basics.js rename to tests/lib/basics.ts index 302cd7d..364d3bb 100644 --- a/tests/lib/basics.js +++ b/tests/lib/basics.ts @@ -5,16 +5,10 @@ * @copyright jQuery Foundation and other contributors, https://jquery.org/ * MIT License */ - -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const path = require('path'), - shelljs = require('shelljs'), - testUtils = require('../../tools/test-utils'); +import path from 'path'; +import shelljs from 'shelljs'; +import { ParserOptions } from '../../src/temp-types-based-on-js-source'; +import { createSnapshotTestBlock } from '../../tools/test-utils'; //------------------------------------------------------------------------------ // Setup @@ -43,9 +37,9 @@ describe('basics', () => { tokens: true, errorOnUnknownASTType: true }; - test( + it( `fixtures/${filename}.src`, - testUtils.createSnapshotTestBlock(code, config) + createSnapshotTestBlock(code, config as ParserOptions) ); }); }); diff --git a/tests/lib/comments.js b/tests/lib/comments.ts similarity index 76% rename from tests/lib/comments.js rename to tests/lib/comments.ts index d589b4d..936b7a2 100644 --- a/tests/lib/comments.js +++ b/tests/lib/comments.ts @@ -5,16 +5,10 @@ * @copyright jQuery Foundation and other contributors, https://jquery.org/ * MIT License */ - -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const path = require('path'), - shelljs = require('shelljs'), - testUtils = require('../../tools/test-utils'); +import path from 'path'; +import shelljs from 'shelljs'; +import { ParserOptions } from '../../src/temp-types-based-on-js-source'; +import { createSnapshotTestBlock } from '../../tools/test-utils'; //------------------------------------------------------------------------------ // Setup @@ -44,9 +38,9 @@ describe('Comments', () => { comment: true, jsx: true }; - test( + it( `fixtures/${filename}.src`, - testUtils.createSnapshotTestBlock(code, config) + createSnapshotTestBlock(code, config as ParserOptions) ); }); }); diff --git a/tests/lib/ecma-features.js b/tests/lib/ecma-features.ts similarity index 66% rename from tests/lib/ecma-features.js rename to tests/lib/ecma-features.ts index a693a2d..e2a3b98 100644 --- a/tests/lib/ecma-features.js +++ b/tests/lib/ecma-features.ts @@ -5,16 +5,10 @@ * @copyright jQuery Foundation and other contributors, https://jquery.org/ * MIT License */ - -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const path = require('path'), - shelljs = require('shelljs'), - testUtils = require('../../tools/test-utils'); +import path from 'path'; +import shelljs from 'shelljs'; +import { ParserOptions } from '../../src/temp-types-based-on-js-source'; +import { createSnapshotTestBlock } from '../../tools/test-utils'; //------------------------------------------------------------------------------ // Setup @@ -36,8 +30,7 @@ const testFiles = shelljs describe('ecma-features', () => { testFiles.forEach(filename => { - const feature = path.dirname(filename), - code = shelljs.cat(`${path.resolve(FIXTURES_DIR, filename)}.src.js`), + const code = shelljs.cat(`${path.resolve(FIXTURES_DIR, filename)}.src.js`), config = { loc: true, range: true, @@ -45,8 +38,9 @@ describe('ecma-features', () => { errorOnUnknownASTType: true }; - test(`fixtures/${filename}.src`, () => { - testUtils.createSnapshotTestBlock(code, config)(); - }); + it( + `fixtures/${filename}.src`, + createSnapshotTestBlock(code, config as ParserOptions) + ); }); }); diff --git a/tests/lib/jsx.js b/tests/lib/jsx.ts similarity index 75% rename from tests/lib/jsx.js rename to tests/lib/jsx.ts index 43228da..406eab5 100644 --- a/tests/lib/jsx.js +++ b/tests/lib/jsx.ts @@ -5,17 +5,11 @@ * @copyright jQuery Foundation and other contributors, https://jquery.org/ * MIT License */ - -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const path = require('path'), - shelljs = require('shelljs'), - testUtils = require('../../tools/test-utils'), - filesWithKnownIssues = require('../jsx-known-issues'); +import path from 'path'; +import shelljs from 'shelljs'; +import { ParserOptions } from '../../src/temp-types-based-on-js-source'; +import { createSnapshotTestBlock } from '../../tools/test-utils'; +import filesWithKnownIssues from '../jsx-known-issues'; //------------------------------------------------------------------------------ // Setup @@ -51,11 +45,11 @@ const jsxTextTestFiles = shelljs describe('JSX', () => { /** * Test each fixture file - * @param {string} fixturesDir Fixtures Directory - * @param {boolean} useJSXTextNode Use JSX Text Node - * @returns {void} */ - function testFixture(fixturesDir, useJSXTextNode) { + function testFixture( + fixturesDir: string, + useJSXTextNode: boolean + ): (filename: string) => void { return filename => { const code = shelljs.cat(`${path.resolve(fixturesDir, filename)}.src.js`); @@ -68,9 +62,9 @@ describe('JSX', () => { jsx: true }; - test( + it( `fixtures/${filename}.src`, - testUtils.createSnapshotTestBlock(code, config) + createSnapshotTestBlock(code, config as ParserOptions) ); }; } diff --git a/tests/lib/parse.js b/tests/lib/parse.ts similarity index 69% rename from tests/lib/parse.js rename to tests/lib/parse.ts index a7b321c..cbb9beb 100644 --- a/tests/lib/parse.js +++ b/tests/lib/parse.ts @@ -5,15 +5,9 @@ * @copyright jQuery Foundation and other contributors, https://jquery.org/ * MIT License */ - -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const parser = require('../../parser'), - testUtils = require('../../tools/test-utils'); +import * as parser from '../../src/parser'; +import { ParserOptions } from '../../src/temp-types-based-on-js-source'; +import { createSnapshotTestBlock } from '../../tools/test-utils'; //------------------------------------------------------------------------------ // Tests @@ -22,15 +16,15 @@ const parser = require('../../parser'), describe('parse()', () => { describe('basic functionality', () => { it('should parse an empty string', () => { - expect(parser.parse('').body).toEqual([]); - expect(parser.parse('', {}).body).toEqual([]); + expect((parser as any).parse('').body).toEqual([]); + expect(parser.parse('', {} as any).body).toEqual([]); }); }); describe('modules', () => { it('should have correct column number when strict mode error occurs', () => { try { - parser.parse('function fn(a, a) {\n}', { sourceType: 'module' }); + parser.parse('function fn(a, a) {\n}', { sourceType: 'module' } as any); } catch (err) { expect(err.column).toEqual(16); } @@ -46,9 +40,9 @@ describe('parse()', () => { loc: true }; - test( + it( 'output tokens, comments, locs, and ranges when called with those options', - testUtils.createSnapshotTestBlock(code, config) + createSnapshotTestBlock(code, config as ParserOptions) ); }); }); diff --git a/tests/lib/tsx.js b/tests/lib/tsx.ts similarity index 76% rename from tests/lib/tsx.js rename to tests/lib/tsx.ts index 4e0a8d5..7efddec 100644 --- a/tests/lib/tsx.js +++ b/tests/lib/tsx.ts @@ -4,16 +4,10 @@ * @copyright jQuery Foundation and other contributors, https://jquery.org/ * MIT License */ - -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const path = require('path'), - shelljs = require('shelljs'), - testUtils = require('../../tools/test-utils'); +import path from 'path'; +import shelljs from 'shelljs'; +import { ParserOptions } from '../../src/temp-types-based-on-js-source'; +import { createSnapshotTestBlock } from '../../tools/test-utils'; //------------------------------------------------------------------------------ // Setup @@ -46,9 +40,9 @@ describe('TSX', () => { useJSXTextNode: true, jsx: true }; - test( + it( `fixtures/${filename}.src`, - testUtils.createSnapshotTestBlock(code, config) + createSnapshotTestBlock(code, config as ParserOptions) ); }); }); diff --git a/tests/lib/typescript.js b/tests/lib/typescript.ts similarity index 76% rename from tests/lib/typescript.js rename to tests/lib/typescript.ts index 5b8d9f0..92a2092 100644 --- a/tests/lib/typescript.js +++ b/tests/lib/typescript.ts @@ -5,16 +5,10 @@ * @copyright jQuery Foundation and other contributors, https://jquery.org/ * MIT License */ - -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const path = require('path'), - shelljs = require('shelljs'), - testUtils = require('../../tools/test-utils'); +import path from 'path'; +import shelljs from 'shelljs'; +import { ParserOptions } from '../../src/temp-types-based-on-js-source'; +import { createSnapshotTestBlock } from '../../tools/test-utils'; //------------------------------------------------------------------------------ // Setup @@ -43,9 +37,9 @@ describe('typescript', () => { tokens: true, errorOnUnknownASTType: true }; - test( + it( `fixtures/${filename}.src`, - testUtils.createSnapshotTestBlock(code, config) + createSnapshotTestBlock(code, config as ParserOptions) ); }); }); diff --git a/tools/test-utils.js b/tools/test-utils.ts similarity index 68% rename from tools/test-utils.js rename to tools/test-utils.ts index 57fe8c7..453a8db 100644 --- a/tools/test-utils.js +++ b/tools/test-utils.ts @@ -5,25 +5,15 @@ * @copyright jQuery Foundation and other contributors, https://jquery.org/ * MIT License */ - -'use strict'; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const parser = require('../parser'); - -//------------------------------------------------------------------------------ -// Private -//-------------------------------------------------------------------------------- +import * as parser from '../src/parser'; +import { ParserOptions } from '../src/temp-types-based-on-js-source'; /** * Returns a raw copy of the given AST * @param {Object} ast the AST object * @returns {Object} copy of the AST object */ -function getRaw(ast) { +export function getRaw(ast: any) { return JSON.parse( JSON.stringify(ast, (key, value) => { if ((key === 'start' || key === 'end') && typeof value === 'number') { @@ -38,10 +28,10 @@ function getRaw(ast) { * Returns a function which can be used as the callback of a Jest test() block, * and which performs an assertion on the snapshot for the given code and config. * @param {string} code The source code to parse - * @param {*} config the parser configuration - * @returns {Function} callback for Jest test() block + * @param {ParserOptions} config the parser configuration + * @returns {Function} callback for Jest it() block */ -function createSnapshotTestBlock(code, config) { +export function createSnapshotTestBlock(code: string, config: ParserOptions) { /** * @returns {Object} the AST object */ @@ -66,8 +56,3 @@ function createSnapshotTestBlock(code, config) { } }; } - -module.exports = { - getRaw, - createSnapshotTestBlock -}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..207c6dc --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es2017", + "module": "commonjs", + "declaration": true, + "outDir": "./dist", + "strict": true, + "esModuleInterop": true + }, + "include": ["src"] +} diff --git a/yarn.lock b/yarn.lock index a572950..2be682c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -803,6 +803,61 @@ dependencies: defer-to-connect "^1.0.1" +"@types/babel-code-frame@^6.20.1": + version "6.20.1" + resolved "https://registry.yarnpkg.com/@types/babel-code-frame/-/babel-code-frame-6.20.1.tgz#e79a40ea81435034df7b46b5e32e8ed638aea4dd" + +"@types/events@*": + version "1.2.0" + resolved "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" + +"@types/glob@*": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + +"@types/jest@^23.3.9": + version "23.3.9" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.9.tgz#c16b55186ee73ae65e001fbee69d392c51337ad1" + +"@types/lodash.isplainobject@^4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/lodash.isplainobject/-/lodash.isplainobject-4.0.4.tgz#ac28008a145ea19ac9f6dc46045aa92e11a09322" + dependencies: + "@types/lodash" "*" + +"@types/lodash.unescape@^4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/lodash.unescape/-/lodash.unescape-4.0.4.tgz#b8eec58cdd642d3a52adc13fbdee1ef0e878a132" + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.117" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.117.tgz#695a7f514182771a1e0f4345d189052ee33c8778" + +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + +"@types/node@*": + version "10.12.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.2.tgz#d77f9faa027cadad9c912cd47f4f8b07b0fb0864" + +"@types/semver@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" + +"@types/shelljs@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.0.tgz#0caa56b68baae4f68f44e0dd666ab30b098e3632" + dependencies: + "@types/glob" "*" + "@types/node" "*" + JSONStream@^1.0.4, JSONStream@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.4.tgz#615bb2adb0cd34c8f4c447b5f6512fa1d8f16a2e" @@ -1329,6 +1384,12 @@ browserslist@^4.1.0: electron-to-chromium "^1.3.62" node-releases "^1.0.0-alpha.11" +bs-logger@0.x: + version "0.2.5" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.5.tgz#1d82f0cf88864e1341cd9262237f8d0748a49b22" + dependencies: + fast-json-stable-stringify "^2.0.0" + bser@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" @@ -1339,7 +1400,7 @@ btoa-lite@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" -buffer-from@^1.0.0: +buffer-from@1.x, buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -2392,7 +2453,7 @@ fast-glob@^2.0.2: merge2 "^1.2.1" micromatch "^3.1.10" -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -3838,6 +3899,12 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" +json5@2.x: + version "2.1.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" + dependencies: + minimist "^1.2.0" + json5@^0.5.0, json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -4277,6 +4344,10 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" +make-error@1.x: + version "1.3.5" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" + "make-fetch-happen@^2.5.0 || 3 || 4", make-fetch-happen@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz#141497cb878f243ba93136c83d8aba12c216c083" @@ -4560,7 +4631,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -6046,6 +6117,10 @@ semver-regex@^1.0.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" +semver@^5.5: + version "5.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" + semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -6649,6 +6724,19 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" +ts-jest@^23.10.4: + version "23.10.4" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-23.10.4.tgz#a7a953f55c9165bcaa90ff91014a178e87fe0df8" + dependencies: + bs-logger "0.x" + buffer-from "1.x" + fast-json-stable-stringify "2.x" + json5 "2.x" + make-error "1.x" + mkdirp "0.x" + semver "^5.5" + yargs-parser "10.x" + tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" @@ -7017,7 +7105,7 @@ yallist@^3.0.0, yallist@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" -yargs-parser@^10.0.0, yargs-parser@^10.1.0: +yargs-parser@10.x, yargs-parser@^10.0.0, yargs-parser@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" dependencies: