From f03c968d7acc0aef33b26510d4e9bb6be5aadb39 Mon Sep 17 00:00:00 2001 From: fisker Date: Thu, 2 Mar 2023 13:30:25 +0800 Subject: [PATCH 01/11] feat: add `location` to `TSError` --- .../snapshots/1-TSESTree-Error.shot | 2 +- .../snapshots/1-TSESTree-Error.shot | 2 +- .../snapshots/1-TSESTree-Error.shot | 2 +- .../snapshots/1-TSESTree-Error.shot | 8 +- .../snapshots/1-TSESTree-Error.shot | 8 +- .../snapshots/1-TSESTree-Error.shot | 8 +- .../snapshots/1-TSESTree-Error.shot | 7 +- .../snapshots/1-TSESTree-Error.shot | 2 +- .../snapshots/1-TSESTree-Error.shot | 2 +- .../ast-spec/tests/util/serialize-error.ts | 12 +- packages/typescript-estree/src/convert.ts | 171 +++++++++--------- packages/typescript-estree/src/node-utils.ts | 41 ++++- 12 files changed, 154 insertions(+), 111 deletions(-) diff --git a/packages/ast-spec/src/declaration/ExportAllDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/ExportAllDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot index 52c5c7981ca..a5f4a06ec87 100644 --- a/packages/ast-spec/src/declaration/ExportAllDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/declaration/ExportAllDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot @@ -3,6 +3,6 @@ exports[`AST Fixtures declaration ExportAllDeclaration _error_ non-string-source TSESTree - Error 1`] = ` "TSError > 1 | export * from module; - | ^ Module specifier must be a string literal. + | ^^^^^^^ Module specifier must be a string literal. 2 |" `; diff --git a/packages/ast-spec/src/declaration/ImportDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/ImportDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot index 93ead06a43f..9a5769f89a0 100644 --- a/packages/ast-spec/src/declaration/ImportDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/declaration/ImportDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot @@ -3,6 +3,6 @@ exports[`AST Fixtures declaration ImportDeclaration _error_ non-string-source TSESTree - Error 1`] = ` "TSError > 1 | import * as x from module; - | ^ Module specifier must be a string literal. + | ^^^^^^^ Module specifier must be a string literal. 2 |" `; diff --git a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/_error_/class-with-export-parameter-properties/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/_error_/class-with-export-parameter-properties/snapshots/1-TSESTree-Error.shot index 1cbe0b6996d..19143f9db1a 100644 --- a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/_error_/class-with-export-parameter-properties/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/_error_/class-with-export-parameter-properties/snapshots/1-TSESTree-Error.shot @@ -5,7 +5,7 @@ exports[`AST Fixtures legacy-fixtures basics _error_ class-with-export-parameter 2 | 3 | class Foo { > 4 | constructor(export a: string) { - | ^ A parameter cannot have an export modifier. + | ^^^^^^^^^^^^^^^^ A parameter cannot have an export modifier. 5 | 6 | } 7 | }" diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-index-signature-export/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-index-signature-export/snapshots/1-TSESTree-Error.shot index 026026fb4c0..86d51372d18 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-index-signature-export/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-index-signature-export/snapshots/1-TSESTree-Error.shot @@ -5,8 +5,10 @@ exports[`AST Fixtures legacy-fixtures errorRecovery _error_ interface-index-sign 1 | // TODO: This fixture might be too large, and if so should be split up. 2 | > 3 | interface Foo { - | ^ An index signature cannot have an export modifier. - 4 | export [baz: string]: string; + | ^ +> 4 | export [baz: string]: string; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ An index signature cannot have an export modifier. 5 | } - 6 |" + 6 | + 7 |" `; diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-method-export/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-method-export/snapshots/1-TSESTree-Error.shot index 460cd78d0cc..b2ddd093e7e 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-method-export/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-method-export/snapshots/1-TSESTree-Error.shot @@ -5,8 +5,10 @@ exports[`AST Fixtures legacy-fixtures errorRecovery _error_ interface-method-exp 1 | // TODO: This fixture might be too large, and if so should be split up. 2 | > 3 | interface Foo { - | ^ A method signature cannot have an export modifier. - 4 | export g(bar: string): void; + | ^ +> 4 | export g(bar: string): void; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A method signature cannot have an export modifier. 5 | } - 6 |" + 6 | + 7 |" `; diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-export/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-export/snapshots/1-TSESTree-Error.shot index 95024673e53..7932b4ee228 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-export/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-export/snapshots/1-TSESTree-Error.shot @@ -5,8 +5,10 @@ exports[`AST Fixtures legacy-fixtures errorRecovery _error_ interface-property-e 1 | // TODO: This fixture might be too large, and if so should be split up. 2 | > 3 | interface Foo { - | ^ A property signature cannot have an export modifier. - 4 | export a: string; + | ^ +> 4 | export a: string; + | ^^^^^^^^^^^^^^^^^^^^ A property signature cannot have an export modifier. 5 | } - 6 |" + 6 | + 7 |" `; diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-with-default-value/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-with-default-value/snapshots/1-TSESTree-Error.shot index 9cc70379ece..0a76a14521c 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-with-default-value/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-with-default-value/snapshots/1-TSESTree-Error.shot @@ -2,11 +2,10 @@ exports[`AST Fixtures legacy-fixtures errorRecovery _error_ interface-property-with-default-value TSESTree - Error 1`] = ` "TSError - 1 | // TODO: This fixture might be too large, and if so should be split up. 2 | -> 3 | interface Foo { - | ^ A property signature cannot have an initializer. - 4 | bar: string = 'a'; + 3 | interface Foo { +> 4 | bar: string = 'a'; + | ^^^^ A property signature cannot have an initializer. 5 | } 6 |" `; diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/object-assertion-not-allowed/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/object-assertion-not-allowed/snapshots/1-TSESTree-Error.shot index 52debdfd81d..4ed8a518fdc 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/object-assertion-not-allowed/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/object-assertion-not-allowed/snapshots/1-TSESTree-Error.shot @@ -5,6 +5,6 @@ exports[`AST Fixtures legacy-fixtures errorRecovery _error_ object-assertion-not 1 | // TODO: This fixture might be too large, and if so should be split up. 2 | > 3 | ({a!} = {}) - | ^ A shorthand property assignment cannot have an exclamation token. + | ^ A shorthand property assignment cannot have an exclamation token. 4 |" `; diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/object-optional-not-allowed/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/object-optional-not-allowed/snapshots/1-TSESTree-Error.shot index a12f4a288f5..684488f02c2 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/object-optional-not-allowed/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/object-optional-not-allowed/snapshots/1-TSESTree-Error.shot @@ -5,6 +5,6 @@ exports[`AST Fixtures legacy-fixtures errorRecovery _error_ object-optional-not- 1 | // TODO: This fixture might be too large, and if so should be split up. 2 | > 3 | ({a?} = {}) - | ^ A shorthand property assignment cannot have a question token. + | ^ A shorthand property assignment cannot have a question token. 4 |" `; diff --git a/packages/ast-spec/tests/util/serialize-error.ts b/packages/ast-spec/tests/util/serialize-error.ts index 31e3715f247..ee8597c6148 100644 --- a/packages/ast-spec/tests/util/serialize-error.ts +++ b/packages/ast-spec/tests/util/serialize-error.ts @@ -10,13 +10,21 @@ export function serializeError( return error; } - const { message, lineNumber: line, column, name } = error; + const { + name, + message, + location: { start, end }, + } = error; + return ( name + '\n' + codeFrameColumns( contents, - { start: { line, column: column + 1 } }, + { + start: { line: start.line, column: start.column + 1 }, + end: { line: end.line, column: end.column + 1 }, + }, { highlightCode: false, message }, ) ); diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index a6a66c0d854..f88c4d8925a 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -50,9 +50,9 @@ export function convertError( error: ts.DiagnosticWithLocation | SemanticOrSyntacticError, ): TSError { return createError( + ('message' in error && error.message) || (error.messageText as string), error.file!, error.start!, - ('message' in error && error.message) || (error.messageText as string), ); } @@ -444,9 +444,8 @@ export class Converter { */ private deeplyCopy(node: TSNode): any { if (node.kind === ts.SyntaxKind.JSDocFunctionType) { - throw createError( - this.ast, - node.pos, + this.#throwError( + node, 'JSDoc types can only be used inside documentation comments.', ); } @@ -616,9 +615,8 @@ export class Converter { | ts.SetAccessorDeclaration, ): TSESTree.TSMethodSignature { if (hasModifier(SyntaxKind.ExportKeyword, node)) { - throw createError( - this.ast, - node.pos, + this.#throwError( + node, 'A method signature cannot have an export modifier.', ); } @@ -685,20 +683,15 @@ export class Converter { allowNull: boolean, ): void { if (!allowNull && node.moduleSpecifier == null) { - throw createError( - this.ast, - node.pos, - 'Module specifier must be a string literal.', - ); + this.#throwError(node, 'Module specifier must be a string literal.'); } if ( node.moduleSpecifier && node.moduleSpecifier?.kind !== SyntaxKind.StringLiteral ) { - throw createError( - this.ast, - node.moduleSpecifier.pos, + this.#throwError( + node.moduleSpecifier, 'Module specifier must be a string literal.', ); } @@ -1019,20 +1012,23 @@ export class Converter { } } - case SyntaxKind.PropertyAssignment: - this.#throwErrorIfDeprecatedPropertyExists( - node, - // eslint-disable-next-line deprecation/deprecation - node.questionToken, - 'A property assignment cannot have a question token.', - ); + case SyntaxKind.PropertyAssignment: { + // eslint-disable-next-line deprecation/deprecation + const { questionToken, exclamationToken } = node; - this.#throwErrorIfDeprecatedPropertyExists( - node, - // eslint-disable-next-line deprecation/deprecation - node.exclamationToken, - 'A property assignment cannot have an exclamation token.', - ); + if (questionToken) { + this.#throwError( + questionToken, + 'A property assignment cannot have a question token.', + ); + } + + if (exclamationToken) { + this.#throwError( + exclamationToken, + 'A property assignment cannot have a question token.', + ); + } return this.createNode(node, { type: AST_NODE_TYPES.Property, @@ -1044,28 +1040,32 @@ export class Converter { shorthand: false, kind: 'init', }); + } case SyntaxKind.ShorthandPropertyAssignment: { - this.#throwErrorIfDeprecatedPropertyExists( - node, - // eslint-disable-next-line deprecation/deprecation - node.modifiers, - 'A shorthand property assignment cannot have modifiers.', - ); + // eslint-disable-next-line deprecation/deprecation + const { modifiers, questionToken, exclamationToken } = node; - this.#throwErrorIfDeprecatedPropertyExists( - node, - // eslint-disable-next-line deprecation/deprecation - node.questionToken, - 'A shorthand property assignment cannot have a question token.', - ); + if (modifiers) { + this.#throwError( + modifiers[0], + 'A shorthand property assignment cannot have modifiers.', + ); + } - this.#throwErrorIfDeprecatedPropertyExists( - node, - // eslint-disable-next-line deprecation/deprecation - node.exclamationToken, - 'A shorthand property assignment cannot have an exclamation token.', - ); + if (questionToken) { + this.#throwError( + questionToken, + 'A shorthand property assignment cannot have a question token.', + ); + } + + if (exclamationToken) { + this.#throwError( + exclamationToken, + 'A shorthand property assignment cannot have an exclamation token.', + ); + } if (node.objectAssignmentInitializer) { return this.createNode(node, { @@ -1613,9 +1613,8 @@ export class Converter { const modifiers = getModifiers(node); if (modifiers) { if (hasModifier(SyntaxKind.ExportKeyword, node)) { - throw createError( - this.ast, - node.pos, + this.#throwError( + node, 'A parameter cannot have an export modifier.', ); } @@ -1648,9 +1647,8 @@ export class Converter { ); if (superClass?.types && superClass.types.length > 1) { - throw createError( - this.ast, - superClass.types[1].pos, + this.#throwError( + superClass.types[1], 'Classes can only extend a single class.', ); } @@ -1691,9 +1689,8 @@ export class Converter { if (superClass) { if (superClass.types.length > 1) { - throw createError( - this.ast, - superClass.types[1].pos, + this.#throwError( + superClass.types[1], 'Classes can only extend a single class.', ); } @@ -1987,9 +1984,8 @@ export class Converter { case SyntaxKind.CallExpression: { if (node.expression.kind === SyntaxKind.ImportKeyword) { if (node.arguments.length !== 1 && node.arguments.length !== 2) { - throw createError( - this.ast, - node.arguments.pos, + this.#throwError( + node, 'Dynamic import requires exactly one or two arguments.', ); } @@ -2456,17 +2452,18 @@ export class Converter { } case SyntaxKind.PropertySignature: { - this.#throwErrorIfDeprecatedPropertyExists( - node, - // eslint-disable-next-line deprecation/deprecation - node.initializer, - 'A property signature cannot have an initializer.', - ); + // eslint-disable-next-line deprecation/deprecation + const { initializer } = node; + if (initializer) { + this.#throwError( + initializer, + 'A property signature cannot have an initializer.', + ); + } if (hasModifier(SyntaxKind.ExportKeyword, node)) { - throw createError( - this.ast, - node.pos, + this.#throwError( + node, 'A property signature cannot have an export modifier.', ); } @@ -2486,9 +2483,8 @@ export class Converter { case SyntaxKind.IndexSignature: { if (hasModifier(SyntaxKind.ExportKeyword, node)) { - throw createError( - this.ast, - node.pos, + this.#throwError( + node, 'An index signature cannot have an export modifier.', ); } @@ -2518,13 +2514,16 @@ export class Converter { }); } - case SyntaxKind.FunctionType: - this.#throwErrorIfDeprecatedPropertyExists( - node, - // eslint-disable-next-line deprecation/deprecation - node.modifiers, - 'A function type cannot have modifiers.', - ); + case SyntaxKind.FunctionType: { + // eslint-disable-next-line deprecation/deprecation + const { modifiers } = node; + if (modifiers) { + this.#throwError( + modifiers[0], + 'A function type cannot have modifiers.', + ); + } + } // intentional fallthrough case SyntaxKind.ConstructSignature: case SyntaxKind.CallSignature: { @@ -2981,13 +2980,17 @@ export class Converter { } } - #throwErrorIfDeprecatedPropertyExists( - node: Node, - property: unknown, - message: string, - ): void { - if (property) { - throw createError(this.ast, node.pos, message); + #throwError(node: ts.Node | number, message: string): void { + let start: number; + let end: number; + if (typeof node === 'number') { + start = node; + end = node; + } else { + start = node.pos; + end = node.end; } + + throw createError(message, this.ast, start, end); } } diff --git a/packages/typescript-estree/src/node-utils.ts b/packages/typescript-estree/src/node-utils.ts index 22d3772974a..cb956ee7941 100644 --- a/packages/typescript-estree/src/node-utils.ts +++ b/packages/typescript-estree/src/node-utils.ts @@ -600,9 +600,18 @@ export class TSError extends Error { constructor( message: string, public readonly fileName: string, - public readonly index: number, - public readonly lineNumber: number, - public readonly column: number, + public readonly location: { + start: { + line: number; + column: number; + offset: number; + }; + end: { + line: number; + column: number; + offset: number; + }; + }, ) { super(message); Object.defineProperty(this, 'name', { @@ -611,21 +620,39 @@ export class TSError extends Error { configurable: true, }); } + + get index(): number { + return this.location.start.offset; + } + + get lineNumber(): number { + return this.location.start.line; + } + + get column(): number { + return this.location.start.column; + } } /** * @param ast the AST object * @param start the index at which the error starts + * @param end the index at which the error ends * @param message the error message * @returns converted error object */ export function createError( - ast: ts.SourceFile, - start: number, message: string, + ast: ts.SourceFile, + startIndex: number, + endIndex: number = startIndex, ): TSError { - const loc = ast.getLineAndCharacterOfPosition(start); - return new TSError(message, ast.fileName, start, loc.line + 1, loc.character); + const [start, end] = [startIndex, endIndex].map(offset => { + const { line, character: column } = + ast.getLineAndCharacterOfPosition(offset); + return { line: line + 1, column, offset }; + }); + return new TSError(message, ast.fileName, { start, end }); } /** From 1fb9ae05503948d361b82c57ba057d25f4d187db Mon Sep 17 00:00:00 2001 From: fisker Date: Thu, 2 Mar 2023 14:13:55 +0800 Subject: [PATCH 02/11] Improve error location --- .../snapshots/1-TSESTree-Error.shot | 2 +- .../snapshots/1-TSESTree-Error.shot | 2 +- .../snapshots/1-TSESTree-Error.shot | 2 +- .../snapshots/1-TSESTree-Error.shot | 6 ++-- .../snapshots/1-TSESTree-Error.shot | 6 ++-- .../snapshots/1-TSESTree-Error.shot | 6 ++-- .../snapshots/1-TSESTree-Error.shot | 2 +- packages/typescript-estree/src/convert.ts | 29 +++++++++++-------- packages/typescript-estree/src/node-utils.ts | 15 ++++++++++ 9 files changed, 42 insertions(+), 28 deletions(-) diff --git a/packages/ast-spec/src/declaration/ExportAllDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/ExportAllDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot index a5f4a06ec87..f3e5cca5923 100644 --- a/packages/ast-spec/src/declaration/ExportAllDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/declaration/ExportAllDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot @@ -3,6 +3,6 @@ exports[`AST Fixtures declaration ExportAllDeclaration _error_ non-string-source TSESTree - Error 1`] = ` "TSError > 1 | export * from module; - | ^^^^^^^ Module specifier must be a string literal. + | ^^^^^^ Module specifier must be a string literal. 2 |" `; diff --git a/packages/ast-spec/src/declaration/ImportDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/ImportDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot index 9a5769f89a0..9c245972b5d 100644 --- a/packages/ast-spec/src/declaration/ImportDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/declaration/ImportDeclaration/fixtures/_error_/non-string-source/snapshots/1-TSESTree-Error.shot @@ -3,6 +3,6 @@ exports[`AST Fixtures declaration ImportDeclaration _error_ non-string-source TSESTree - Error 1`] = ` "TSError > 1 | import * as x from module; - | ^^^^^^^ Module specifier must be a string literal. + | ^^^^^^ Module specifier must be a string literal. 2 |" `; diff --git a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/_error_/class-with-export-parameter-properties/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/_error_/class-with-export-parameter-properties/snapshots/1-TSESTree-Error.shot index 19143f9db1a..c54977ea56f 100644 --- a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/_error_/class-with-export-parameter-properties/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/_error_/class-with-export-parameter-properties/snapshots/1-TSESTree-Error.shot @@ -5,7 +5,7 @@ exports[`AST Fixtures legacy-fixtures basics _error_ class-with-export-parameter 2 | 3 | class Foo { > 4 | constructor(export a: string) { - | ^^^^^^^^^^^^^^^^ A parameter cannot have an export modifier. + | ^^^^^^ A parameter cannot have an export modifier. 5 | 6 | } 7 | }" diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-index-signature-export/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-index-signature-export/snapshots/1-TSESTree-Error.shot index 86d51372d18..699355afeef 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-index-signature-export/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-index-signature-export/snapshots/1-TSESTree-Error.shot @@ -2,12 +2,10 @@ exports[`AST Fixtures legacy-fixtures errorRecovery _error_ interface-index-signature-export TSESTree - Error 1`] = ` "TSError - 1 | // TODO: This fixture might be too large, and if so should be split up. 2 | -> 3 | interface Foo { - | ^ + 3 | interface Foo { > 4 | export [baz: string]: string; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ An index signature cannot have an export modifier. + | ^^^^^^ An index signature cannot have an export modifier. 5 | } 6 | 7 |" diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-method-export/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-method-export/snapshots/1-TSESTree-Error.shot index b2ddd093e7e..36f8fd7fddd 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-method-export/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-method-export/snapshots/1-TSESTree-Error.shot @@ -2,12 +2,10 @@ exports[`AST Fixtures legacy-fixtures errorRecovery _error_ interface-method-export TSESTree - Error 1`] = ` "TSError - 1 | // TODO: This fixture might be too large, and if so should be split up. 2 | -> 3 | interface Foo { - | ^ + 3 | interface Foo { > 4 | export g(bar: string): void; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A method signature cannot have an export modifier. + | ^^^^^^ A method signature cannot have an export modifier. 5 | } 6 | 7 |" diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-export/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-export/snapshots/1-TSESTree-Error.shot index 7932b4ee228..f21b92669e1 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-export/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-export/snapshots/1-TSESTree-Error.shot @@ -2,12 +2,10 @@ exports[`AST Fixtures legacy-fixtures errorRecovery _error_ interface-property-export TSESTree - Error 1`] = ` "TSError - 1 | // TODO: This fixture might be too large, and if so should be split up. 2 | -> 3 | interface Foo { - | ^ + 3 | interface Foo { > 4 | export a: string; - | ^^^^^^^^^^^^^^^^^^^^ A property signature cannot have an export modifier. + | ^^^^^^ A property signature cannot have an export modifier. 5 | } 6 | 7 |" diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-with-default-value/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-with-default-value/snapshots/1-TSESTree-Error.shot index 0a76a14521c..9e58d0d1603 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-with-default-value/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-property-with-default-value/snapshots/1-TSESTree-Error.shot @@ -5,7 +5,7 @@ exports[`AST Fixtures legacy-fixtures errorRecovery _error_ interface-property-w 2 | 3 | interface Foo { > 4 | bar: string = 'a'; - | ^^^^ A property signature cannot have an initializer. + | ^^^ A property signature cannot have an initializer. 5 | } 6 |" `; diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index f88c4d8925a..2fdca998715 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -13,6 +13,7 @@ import { getLastModifier, getLineAndCharacterFor, getLocFor, + getModifier, getRange, getTextForTokenKind, getTSNodeAccessibility, @@ -588,13 +589,13 @@ export class Converter { if (node.name.kind === SyntaxKind.PrivateIdentifier) { // This is one of the few times where TS explicitly errors, and doesn't even gracefully handle the syntax. // So we shouldn't ever get into this state to begin with. - throw new Error('Non-private identifier expected.'); + this.#throwError(node.name, 'Non-private identifier expected.'); } result = this.createNode(node, { type: AST_NODE_TYPES.JSXMemberExpression, object: this.convertJSXTagName(node.expression, parent), - property: this.convertJSXIdentifier(node.name), + property: this.convertJSXIdentifier(node.name as ts.Identifier), }); break; @@ -614,9 +615,10 @@ export class Converter { | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration, ): TSESTree.TSMethodSignature { - if (hasModifier(SyntaxKind.ExportKeyword, node)) { + const exportKeyword = getModifier(SyntaxKind.ExportKeyword, node); + if (exportKeyword) { this.#throwError( - node, + exportKeyword, 'A method signature cannot have an export modifier.', ); } @@ -1612,9 +1614,10 @@ export class Converter { const modifiers = getModifiers(node); if (modifiers) { - if (hasModifier(SyntaxKind.ExportKeyword, node)) { + const exportKeyword = getModifier(SyntaxKind.ExportKeyword, node); + if (exportKeyword) { this.#throwError( - node, + exportKeyword, 'A parameter cannot have an export modifier.', ); } @@ -2461,9 +2464,10 @@ export class Converter { ); } - if (hasModifier(SyntaxKind.ExportKeyword, node)) { + const exportKeyword = getModifier(SyntaxKind.ExportKeyword, node); + if (exportKeyword) { this.#throwError( - node, + exportKeyword, 'A property signature cannot have an export modifier.', ); } @@ -2482,9 +2486,10 @@ export class Converter { } case SyntaxKind.IndexSignature: { - if (hasModifier(SyntaxKind.ExportKeyword, node)) { + const exportKeyword = getModifier(SyntaxKind.ExportKeyword, node); + if (exportKeyword) { this.#throwError( - node, + exportKeyword, 'An index signature cannot have an export modifier.', ); } @@ -2987,8 +2992,8 @@ export class Converter { start = node; end = node; } else { - start = node.pos; - end = node.end; + start = node.getStart(); + end = node.getEnd(); } throw createError(message, this.ast, start, end); diff --git a/packages/typescript-estree/src/node-utils.ts b/packages/typescript-estree/src/node-utils.ts index cb956ee7941..174f252f663 100644 --- a/packages/typescript-estree/src/node-utils.ts +++ b/packages/typescript-estree/src/node-utils.ts @@ -87,6 +87,21 @@ export function hasModifier( return modifiers?.some(modifier => modifier.kind === modifierKind) === true; } +/** + * Get a modifier from a ts.Node + * @param modifierKind TypeScript SyntaxKind modifier + * @param node TypeScript AST node + * @returns matched modifier if present or null + */ +export function getModifier( + modifierKind: ts.KeywordSyntaxKind, + node: ts.Node, +): ts.Modifier | null { + return ( + getModifiers(node)?.find(modifier => modifier.kind === modifierKind) ?? null + ); +} + /** * Get last last modifier in ast * @param node TypeScript AST node From 013cb264f735d932ea66ae417baa6f2f2e8368b7 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Thu, 2 Mar 2023 22:52:42 +0800 Subject: [PATCH 03/11] Update packages/typescript-estree/src/convert.ts Co-authored-by: Armano --- packages/typescript-estree/src/convert.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 2fdca998715..db30f82d794 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -2985,7 +2985,10 @@ export class Converter { } } - #throwError(node: ts.Node | number, message: string): void { + #throwError( + node: ts.Node | number, + message: string, + ): asserts node is never { let start: number; let end: number; if (typeof node === 'number') { From be72eb171c7f7e4c59ee3f940609c3bac618ea5f Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Thu, 2 Mar 2023 22:53:40 +0800 Subject: [PATCH 04/11] Update convert.ts --- packages/typescript-estree/src/convert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index db30f82d794..0804f3bf3fc 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -595,7 +595,7 @@ export class Converter { result = this.createNode(node, { type: AST_NODE_TYPES.JSXMemberExpression, object: this.convertJSXTagName(node.expression, parent), - property: this.convertJSXIdentifier(node.name as ts.Identifier), + property: this.convertJSXIdentifier(node.name), }); break; From b49256919d61ee957aae8aaca75e4c3e6d46992d Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Thu, 2 Mar 2023 23:28:07 +0800 Subject: [PATCH 05/11] Update convert.ts --- packages/typescript-estree/src/convert.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 0804f3bf3fc..637e6623eac 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -2985,10 +2985,7 @@ export class Converter { } } - #throwError( - node: ts.Node | number, - message: string, - ): asserts node is never { + #throwError(node: ts.Node | number, message: string): asserts node is never { let start: number; let end: number; if (typeof node === 'number') { From 95d3f2845fb559c2dc997f66a97a04aeed4612b8 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Thu, 2 Mar 2023 23:36:10 +0800 Subject: [PATCH 06/11] Add links --- packages/typescript-estree/src/node-utils.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/typescript-estree/src/node-utils.ts b/packages/typescript-estree/src/node-utils.ts index 174f252f663..05cf0ababbb 100644 --- a/packages/typescript-estree/src/node-utils.ts +++ b/packages/typescript-estree/src/node-utils.ts @@ -636,14 +636,17 @@ export class TSError extends Error { }); } + // For old version of ESLint https://github.com/typescript-eslint/typescript-eslint/pull/6556#discussion_r1123237311 get index(): number { return this.location.start.offset; } + // https://github.com/eslint/eslint/blob/b09a512107249a4eb19ef5a37b0bd672266eafdb/lib/linter/linter.js#L853 get lineNumber(): number { return this.location.start.line; } + // https://github.com/eslint/eslint/blob/b09a512107249a4eb19ef5a37b0bd672266eafdb/lib/linter/linter.js#L854 get column(): number { return this.location.start.column; } From 9f0d1009589a6854d07325f57dc4b692fcf1a366 Mon Sep 17 00:00:00 2001 From: Armano Date: Sun, 5 Mar 2023 23:06:28 +0100 Subject: [PATCH 07/11] fix: remove file used to merge --- packages/typescript-estree/src/convert2.ts | 3001 -------------------- 1 file changed, 3001 deletions(-) delete mode 100644 packages/typescript-estree/src/convert2.ts diff --git a/packages/typescript-estree/src/convert2.ts b/packages/typescript-estree/src/convert2.ts deleted file mode 100644 index 637e6623eac..00000000000 --- a/packages/typescript-estree/src/convert2.ts +++ /dev/null @@ -1,3001 +0,0 @@ -// There's lots of funny stuff due to the typing of ts.Node -/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access */ -import * as ts from 'typescript'; - -import { getDecorators, getModifiers } from './getModifiers'; -import type { TSError } from './node-utils'; -import { - canContainDirective, - createError, - findNextToken, - getBinaryExpressionType, - getDeclarationKind, - getLastModifier, - getLineAndCharacterFor, - getLocFor, - getModifier, - getRange, - getTextForTokenKind, - getTSNodeAccessibility, - hasModifier, - isChainExpression, - isChildUnwrappableOptionalChain, - isComma, - isComputedProperty, - isESTreeClassMember, - isOptional, - isThisInTypeQuery, - unescapeStringLiteralText, -} from './node-utils'; -import type { - ParserWeakMap, - ParserWeakMapESTreeToTSNode, -} from './parser-options'; -import type { SemanticOrSyntacticError } from './semantic-or-syntactic-errors'; -import type { TSESTree, TSESTreeToTSNode, TSNode } from './ts-estree'; -import { AST_NODE_TYPES } from './ts-estree'; - -const SyntaxKind = ts.SyntaxKind; - -interface ConverterOptions { - errorOnUnknownASTType: boolean; - shouldPreserveNodeMaps: boolean; -} - -/** - * Extends and formats a given error object - * @param error the error object - * @returns converted error object - */ -export function convertError( - error: ts.DiagnosticWithLocation | SemanticOrSyntacticError, -): TSError { - return createError( - ('message' in error && error.message) || (error.messageText as string), - error.file!, - error.start!, - ); -} - -export interface ASTMaps { - esTreeNodeToTSNodeMap: ParserWeakMapESTreeToTSNode; - tsNodeToESTreeNodeMap: ParserWeakMap; -} - -export class Converter { - private readonly ast: ts.SourceFile; - private readonly options: ConverterOptions; - private readonly esTreeNodeToTSNodeMap = new WeakMap(); - private readonly tsNodeToESTreeNodeMap = new WeakMap(); - - private allowPattern = false; - - /** - * Converts a TypeScript node into an ESTree node - * @param ast the full TypeScript AST - * @param options additional options for the conversion - * @returns the converted ESTreeNode - */ - constructor(ast: ts.SourceFile, options: ConverterOptions) { - this.ast = ast; - this.options = { ...options }; - } - - getASTMaps(): ASTMaps { - return { - esTreeNodeToTSNodeMap: this.esTreeNodeToTSNodeMap, - tsNodeToESTreeNodeMap: this.tsNodeToESTreeNodeMap, - }; - } - - convertProgram(): TSESTree.Program { - return this.converter(this.ast) as TSESTree.Program; - } - - /** - * Converts a TypeScript node into an ESTree node. - * @param node the child ts.Node - * @param parent parentNode - * @param allowPattern flag to determine if patterns are allowed - * @returns the converted ESTree node - */ - private converter( - node?: ts.Node, - parent?: ts.Node, - allowPattern?: boolean, - ): any { - /** - * Exit early for null and undefined - */ - if (!node) { - return null; - } - - const pattern = this.allowPattern; - if (allowPattern !== undefined) { - this.allowPattern = allowPattern; - } - - const result = this.convertNode( - node as TSNode, - (parent ?? node.parent) as TSNode, - ); - - this.registerTSNodeInNodeMap(node, result); - - this.allowPattern = pattern; - return result; - } - - /** - * Fixes the exports of the given ts.Node - * @param node the ts.Node - * @param result result - * @returns the ESTreeNode with fixed exports - */ - private fixExports< - T extends - | TSESTree.DefaultExportDeclarations - | TSESTree.NamedExportDeclarations, - >( - node: - | ts.FunctionDeclaration - | ts.VariableStatement - | ts.ClassDeclaration - | ts.ClassExpression - | ts.TypeAliasDeclaration - | ts.ImportEqualsDeclaration - | ts.InterfaceDeclaration - | ts.EnumDeclaration - | ts.ModuleDeclaration, - result: T, - ): TSESTree.ExportDefaultDeclaration | TSESTree.ExportNamedDeclaration | T { - const modifiers = getModifiers(node); - if (modifiers?.[0].kind === SyntaxKind.ExportKeyword) { - /** - * Make sure that original node is registered instead of export - */ - this.registerTSNodeInNodeMap(node, result); - - const exportKeyword = modifiers[0]; - const nextModifier = modifiers[1]; - const declarationIsDefault = - nextModifier?.kind === SyntaxKind.DefaultKeyword; - - const varToken = declarationIsDefault - ? findNextToken(nextModifier, this.ast, this.ast) - : findNextToken(exportKeyword, this.ast, this.ast); - - result.range[0] = varToken!.getStart(this.ast); - result.loc = getLocFor(result.range[0], result.range[1], this.ast); - - if (declarationIsDefault) { - return this.createNode( - node as Exclude, - { - type: AST_NODE_TYPES.ExportDefaultDeclaration, - declaration: result as TSESTree.DefaultExportDeclarations, - range: [exportKeyword.getStart(this.ast), result.range[1]], - exportKind: 'value', - }, - ); - } else { - const isType = - result.type === AST_NODE_TYPES.TSInterfaceDeclaration || - result.type === AST_NODE_TYPES.TSTypeAliasDeclaration; - const isDeclare = 'declare' in result && result.declare === true; - return this.createNode(node, { - type: AST_NODE_TYPES.ExportNamedDeclaration, - // @ts-expect-error - TODO, narrow the types here - declaration: result, - specifiers: [], - source: null, - exportKind: isType || isDeclare ? 'type' : 'value', - range: [exportKeyword.getStart(this.ast), result.range[1]], - assertions: [], - }); - } - } - - return result; - } - - /** - * Register specific TypeScript node into map with first ESTree node provided - */ - private registerTSNodeInNodeMap( - node: ts.Node, - result: TSESTree.Node | null, - ): void { - if (result && this.options.shouldPreserveNodeMaps) { - if (!this.tsNodeToESTreeNodeMap.has(node)) { - this.tsNodeToESTreeNodeMap.set(node, result); - } - } - } - - /** - * Converts a TypeScript node into an ESTree node. - * @param child the child ts.Node - * @param parent parentNode - * @returns the converted ESTree node - */ - private convertPattern(child?: ts.Node, parent?: ts.Node): any | null { - return this.converter(child, parent, true); - } - - /** - * Converts a TypeScript node into an ESTree node. - * @param child the child ts.Node - * @param parent parentNode - * @returns the converted ESTree node - */ - private convertChild(child?: ts.Node, parent?: ts.Node): any | null { - return this.converter(child, parent, false); - } - - private createNode( - // The 'parent' property will be added later if specified - node: Omit, 'parent'>, - data: Omit, 'parent'>, - ): T { - const result = data; - result.range ??= getRange(node, this.ast); - result.loc ??= getLocFor(result.range[0], result.range[1], this.ast); - - if (result && this.options.shouldPreserveNodeMaps) { - this.esTreeNodeToTSNodeMap.set(result, node); - } - return result as T; - } - - private convertBindingNameWithTypeAnnotation( - name: ts.BindingName, - tsType: ts.TypeNode | undefined, - parent?: ts.Node, - ): TSESTree.BindingName { - const id = this.convertPattern(name) as TSESTree.BindingName; - - if (tsType) { - id.typeAnnotation = this.convertTypeAnnotation(tsType, parent); - this.fixParentLocation(id, id.typeAnnotation.range); - } - - return id; - } - - /** - * Converts a child into a type annotation. This creates an intermediary - * TypeAnnotation node to match what Flow does. - * @param child The TypeScript AST node to convert. - * @param parent parentNode - * @returns The type annotation node. - */ - private convertTypeAnnotation( - child: ts.TypeNode, - parent: ts.Node | undefined, - ): TSESTree.TSTypeAnnotation { - // in FunctionType and ConstructorType typeAnnotation has 2 characters `=>` and in other places is just colon - const offset = - parent?.kind === SyntaxKind.FunctionType || - parent?.kind === SyntaxKind.ConstructorType - ? 2 - : 1; - const annotationStartCol = child.getFullStart() - offset; - - const loc = getLocFor(annotationStartCol, child.end, this.ast); - return { - type: AST_NODE_TYPES.TSTypeAnnotation, - loc, - range: [annotationStartCol, child.end], - typeAnnotation: this.convertChild(child), - } as TSESTree.TSTypeAnnotation; - } - - /** - * Coverts body Nodes and add a directive field to StringLiterals - * @param nodes of ts.Node - * @param parent parentNode - * @returns Array of body statements - */ - private convertBodyExpressions( - nodes: ts.NodeArray, - parent: - | ts.SourceFile - | ts.Block - | ts.ModuleBlock - | ts.ClassStaticBlockDeclaration, - ): TSESTree.Statement[] { - let allowDirectives = canContainDirective(parent); - - return ( - nodes - .map(statement => { - const child = this.convertChild(statement); - if (allowDirectives) { - if ( - child?.expression && - ts.isExpressionStatement(statement) && - ts.isStringLiteral(statement.expression) - ) { - const raw = child.expression.raw; - child.directive = raw.slice(1, -1); - return child; // child can be null, but it's filtered below - } else { - allowDirectives = false; - } - } - return child; // child can be null, but it's filtered below - }) - // filter out unknown nodes for now - .filter(statement => statement) - ); - } - - /** - * Converts a ts.Node's typeArguments to TSTypeParameterInstantiation node - * @param typeArguments ts.NodeArray typeArguments - * @param node parent used to create this node - * @returns TypeParameterInstantiation node - */ - private convertTypeArgumentsToTypeParameterInstantiation( - typeArguments: ts.NodeArray, - node: TSESTreeToTSNode, - ): TSESTree.TSTypeParameterInstantiation { - const greaterThanToken = findNextToken(typeArguments, this.ast, this.ast)!; - - return this.createNode(node, { - type: AST_NODE_TYPES.TSTypeParameterInstantiation, - range: [typeArguments.pos - 1, greaterThanToken.end], - params: typeArguments.map(typeArgument => - this.convertChild(typeArgument), - ), - }); - } - - /** - * Converts a ts.Node's typeParameters to TSTypeParameterDeclaration node - * @param typeParameters ts.Node typeParameters - * @returns TypeParameterDeclaration node - */ - private convertTSTypeParametersToTypeParametersDeclaration( - typeParameters: ts.NodeArray, - ): TSESTree.TSTypeParameterDeclaration { - const greaterThanToken = findNextToken(typeParameters, this.ast, this.ast)!; - - return { - type: AST_NODE_TYPES.TSTypeParameterDeclaration, - range: [typeParameters.pos - 1, greaterThanToken.end], - loc: getLocFor(typeParameters.pos - 1, greaterThanToken.end, this.ast), - params: typeParameters.map(typeParameter => - this.convertChild(typeParameter), - ), - } as TSESTree.TSTypeParameterDeclaration; - } - - /** - * Converts an array of ts.Node parameters into an array of ESTreeNode params - * @param parameters An array of ts.Node params to be converted - * @returns an array of converted ESTreeNode params - */ - private convertParameters( - parameters: ts.NodeArray, - ): TSESTree.Parameter[] { - if (!parameters?.length) { - return []; - } - return parameters.map(param => { - const convertedParam = this.convertChild(param) as TSESTree.Parameter; - - convertedParam.decorators = - getDecorators(param)?.map(el => this.convertChild(el)) ?? []; - - return convertedParam; - }); - } - - private convertChainExpression( - node: TSESTree.ChainElement, - tsNode: - | ts.PropertyAccessExpression - | ts.ElementAccessExpression - | ts.CallExpression - | ts.NonNullExpression, - ): TSESTree.ChainExpression | TSESTree.ChainElement { - const { child, isOptional } = ((): { - child: TSESTree.Node; - isOptional: boolean; - } => { - if (node.type === AST_NODE_TYPES.MemberExpression) { - return { child: node.object, isOptional: node.optional }; - } - if (node.type === AST_NODE_TYPES.CallExpression) { - return { child: node.callee, isOptional: node.optional }; - } - return { child: node.expression, isOptional: false }; - })(); - const isChildUnwrappable = isChildUnwrappableOptionalChain(tsNode, child); - - if (!isChildUnwrappable && !isOptional) { - return node; - } - - if (isChildUnwrappable && isChainExpression(child)) { - // unwrap the chain expression child - const newChild = child.expression; - if (node.type === AST_NODE_TYPES.MemberExpression) { - node.object = newChild; - } else if (node.type === AST_NODE_TYPES.CallExpression) { - node.callee = newChild; - } else { - node.expression = newChild; - } - } - - return this.createNode(tsNode, { - type: AST_NODE_TYPES.ChainExpression, - expression: node, - }); - } - - /** - * For nodes that are copied directly from the TypeScript AST into - * ESTree mostly as-is. The only difference is the addition of a type - * property instead of a kind property. Recursively copies all children. - */ - private deeplyCopy(node: TSNode): any { - if (node.kind === ts.SyntaxKind.JSDocFunctionType) { - this.#throwError( - node, - 'JSDoc types can only be used inside documentation comments.', - ); - } - - const customType = `TS${SyntaxKind[node.kind]}` as AST_NODE_TYPES; - - /** - * If the "errorOnUnknownASTType" option is set to true, throw an error, - * otherwise fallback to just including the unknown type as-is. - */ - if (this.options.errorOnUnknownASTType && !AST_NODE_TYPES[customType]) { - throw new Error(`Unknown AST_NODE_TYPE: "${customType}"`); - } - - const result = this.createNode(node, { - type: customType, - }); - - if ('type' in node) { - result.typeAnnotation = - node.type && 'kind' in node.type && ts.isTypeNode(node.type) - ? this.convertTypeAnnotation(node.type, node) - : null; - } - if ('typeArguments' in node) { - result.typeArguments = result.typeParameters = - node.typeArguments && 'pos' in node.typeArguments - ? this.convertTypeArgumentsToTypeParameterInstantiation( - node.typeArguments, - node, - ) - : null; - } - if ('typeParameters' in node) { - result.typeParameters = - node.typeParameters && 'pos' in node.typeParameters - ? this.convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters, - ) - : null; - } - const decorators = getDecorators(node); - if (decorators?.length) { - result.decorators = decorators.map(el => this.convertChild(el)); - } - - // keys we never want to clone from the base typescript node as they - // introduce garbage into our AST - const KEYS_TO_NOT_COPY = new Set([ - '_children', - 'decorators', - 'end', - 'flags', - 'illegalDecorators', - 'heritageClauses', - 'locals', - 'localSymbol', - 'jsDoc', - 'kind', - 'modifierFlagsCache', - 'modifiers', - 'nextContainer', - 'parent', - 'pos', - 'symbol', - 'transformFlags', - 'type', - 'typeArguments', - 'typeParameters', - ]); - - Object.entries(node) - .filter(([key]) => !KEYS_TO_NOT_COPY.has(key)) - .forEach(([key, value]) => { - if (Array.isArray(value)) { - result[key] = value.map(el => this.convertChild(el as TSNode)); - } else if (value && typeof value === 'object' && value.kind) { - // need to check node[key].kind to ensure we don't try to convert a symbol - result[key] = this.convertChild(value as TSNode); - } else { - result[key] = value; - } - }); - return result; - } - - private convertJSXIdentifier( - node: ts.Identifier | ts.ThisExpression, - ): TSESTree.JSXIdentifier { - const result = this.createNode(node, { - type: AST_NODE_TYPES.JSXIdentifier, - name: node.getText(), - }); - this.registerTSNodeInNodeMap(node, result); - return result; - } - - private convertJSXNamespaceOrIdentifier( - node: ts.Identifier | ts.ThisExpression, - ): TSESTree.JSXIdentifier | TSESTree.JSXNamespacedName { - const text = node.getText(); - const colonIndex = text.indexOf(':'); - // this is intentional we can ignore conversion if `:` is in first character - if (colonIndex > 0) { - const range = getRange(node, this.ast); - const result = this.createNode(node, { - type: AST_NODE_TYPES.JSXNamespacedName, - namespace: this.createNode(node, { - type: AST_NODE_TYPES.JSXIdentifier, - name: text.slice(0, colonIndex), - range: [range[0], range[0] + colonIndex], - }), - name: this.createNode(node, { - type: AST_NODE_TYPES.JSXIdentifier, - name: text.slice(colonIndex + 1), - range: [range[0] + colonIndex + 1, range[1]], - }), - range, - }); - this.registerTSNodeInNodeMap(node, result); - return result; - } - - return this.convertJSXIdentifier(node); - } - - /** - * Converts a TypeScript JSX node.tagName into an ESTree node.name - * @param node the tagName object from a JSX ts.Node - * @param parent - * @returns the converted ESTree name object - */ - private convertJSXTagName( - node: ts.JsxTagNameExpression, - parent: ts.Node, - ): TSESTree.JSXTagNameExpression { - let result: TSESTree.JSXTagNameExpression; - switch (node.kind) { - case SyntaxKind.PropertyAccessExpression: - if (node.name.kind === SyntaxKind.PrivateIdentifier) { - // This is one of the few times where TS explicitly errors, and doesn't even gracefully handle the syntax. - // So we shouldn't ever get into this state to begin with. - this.#throwError(node.name, 'Non-private identifier expected.'); - } - - result = this.createNode(node, { - type: AST_NODE_TYPES.JSXMemberExpression, - object: this.convertJSXTagName(node.expression, parent), - property: this.convertJSXIdentifier(node.name), - }); - break; - - case SyntaxKind.ThisKeyword: - case SyntaxKind.Identifier: - default: - return this.convertJSXNamespaceOrIdentifier(node); - } - - this.registerTSNodeInNodeMap(node, result); - return result; - } - - private convertMethodSignature( - node: - | ts.MethodSignature - | ts.GetAccessorDeclaration - | ts.SetAccessorDeclaration, - ): TSESTree.TSMethodSignature { - const exportKeyword = getModifier(SyntaxKind.ExportKeyword, node); - if (exportKeyword) { - this.#throwError( - exportKeyword, - 'A method signature cannot have an export modifier.', - ); - } - - return this.createNode(node, { - type: AST_NODE_TYPES.TSMethodSignature, - accessibility: getTSNodeAccessibility(node), - computed: isComputedProperty(node.name), - key: this.convertChild(node.name), - kind: ((): 'get' | 'set' | 'method' => { - switch (node.kind) { - case SyntaxKind.GetAccessor: - return 'get'; - - case SyntaxKind.SetAccessor: - return 'set'; - - case SyntaxKind.MethodSignature: - return 'method'; - } - })(), - optional: isOptional(node), - params: this.convertParameters(node.parameters), - returnType: node.type && this.convertTypeAnnotation(node.type, node), - readonly: hasModifier(SyntaxKind.ReadonlyKeyword, node), - static: hasModifier(SyntaxKind.StaticKeyword, node), - typeParameters: - node.typeParameters && - this.convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters, - ), - }); - } - - private convertAssertClasue( - node: ts.AssertClause | undefined, - ): TSESTree.ImportAttribute[] { - return node === undefined - ? [] - : node.elements.map(element => this.convertChild(element)); - } - - /** - * Uses the provided range location to adjust the location data of the given Node - * @param result The node that will have its location data mutated - * @param childRange The child node range used to expand location - */ - private fixParentLocation( - result: TSESTree.BaseNode, - childRange: [number, number], - ): void { - if (childRange[0] < result.range[0]) { - result.range[0] = childRange[0]; - result.loc.start = getLineAndCharacterFor(result.range[0], this.ast); - } - if (childRange[1] > result.range[1]) { - result.range[1] = childRange[1]; - result.loc.end = getLineAndCharacterFor(result.range[1], this.ast); - } - } - - private assertModuleSpecifier( - node: ts.ExportDeclaration | ts.ImportDeclaration, - allowNull: boolean, - ): void { - if (!allowNull && node.moduleSpecifier == null) { - this.#throwError(node, 'Module specifier must be a string literal.'); - } - - if ( - node.moduleSpecifier && - node.moduleSpecifier?.kind !== SyntaxKind.StringLiteral - ) { - this.#throwError( - node.moduleSpecifier, - 'Module specifier must be a string literal.', - ); - } - } - - /** - * Converts a TypeScript node into an ESTree node. - * The core of the conversion logic: - * Identify and convert each relevant TypeScript SyntaxKind - * @param node the child ts.Node - * @param parent parentNode - * @returns the converted ESTree node - */ - private convertNode(node: TSNode, parent: TSNode): TSESTree.Node | null { - switch (node.kind) { - case SyntaxKind.SourceFile: { - return this.createNode(node, { - type: AST_NODE_TYPES.Program, - body: this.convertBodyExpressions(node.statements, node), - comments: undefined, - range: [node.getStart(this.ast), node.endOfFileToken.end], - sourceType: node.externalModuleIndicator ? 'module' : 'script', - tokens: undefined, - }); - } - - case SyntaxKind.Block: { - return this.createNode(node, { - type: AST_NODE_TYPES.BlockStatement, - body: this.convertBodyExpressions(node.statements, node), - }); - } - - case SyntaxKind.Identifier: { - if (isThisInTypeQuery(node)) { - // special case for `typeof this.foo` - TS emits an Identifier for `this` - // but we want to treat it as a ThisExpression for consistency - return this.createNode(node, { - type: AST_NODE_TYPES.ThisExpression, - }); - } - return this.createNode(node, { - type: AST_NODE_TYPES.Identifier, - decorators: [], - name: node.text, - optional: false, - typeAnnotation: undefined, - }); - } - - case SyntaxKind.PrivateIdentifier: { - return this.createNode(node, { - type: AST_NODE_TYPES.PrivateIdentifier, - // typescript includes the `#` in the text - name: node.text.slice(1), - }); - } - - case SyntaxKind.WithStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.WithStatement, - object: this.convertChild(node.expression), - body: this.convertChild(node.statement), - }); - - // Control Flow - - case SyntaxKind.ReturnStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.ReturnStatement, - argument: this.convertChild(node.expression), - }); - - case SyntaxKind.LabeledStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.LabeledStatement, - label: this.convertChild(node.label), - body: this.convertChild(node.statement), - }); - - case SyntaxKind.ContinueStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.ContinueStatement, - label: this.convertChild(node.label), - }); - - case SyntaxKind.BreakStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.BreakStatement, - label: this.convertChild(node.label), - }); - - // Choice - - case SyntaxKind.IfStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.IfStatement, - test: this.convertChild(node.expression), - consequent: this.convertChild(node.thenStatement), - alternate: this.convertChild(node.elseStatement), - }); - - case SyntaxKind.SwitchStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.SwitchStatement, - discriminant: this.convertChild(node.expression), - cases: node.caseBlock.clauses.map(el => this.convertChild(el)), - }); - - case SyntaxKind.CaseClause: - case SyntaxKind.DefaultClause: - return this.createNode(node, { - type: AST_NODE_TYPES.SwitchCase, - // expression is present in case only - test: - node.kind === SyntaxKind.CaseClause - ? this.convertChild(node.expression) - : null, - consequent: node.statements.map(el => this.convertChild(el)), - }); - - // Exceptions - - case SyntaxKind.ThrowStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.ThrowStatement, - argument: this.convertChild(node.expression), - }); - - case SyntaxKind.TryStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.TryStatement, - block: this.convertChild(node.tryBlock), - handler: this.convertChild(node.catchClause), - finalizer: this.convertChild(node.finallyBlock), - }); - - case SyntaxKind.CatchClause: - return this.createNode(node, { - type: AST_NODE_TYPES.CatchClause, - param: node.variableDeclaration - ? this.convertBindingNameWithTypeAnnotation( - node.variableDeclaration.name, - node.variableDeclaration.type, - ) - : null, - body: this.convertChild(node.block), - }); - - // Loops - - case SyntaxKind.WhileStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.WhileStatement, - test: this.convertChild(node.expression), - body: this.convertChild(node.statement), - }); - - /** - * Unlike other parsers, TypeScript calls a "DoWhileStatement" - * a "DoStatement" - */ - case SyntaxKind.DoStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.DoWhileStatement, - test: this.convertChild(node.expression), - body: this.convertChild(node.statement), - }); - - case SyntaxKind.ForStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.ForStatement, - init: this.convertChild(node.initializer), - test: this.convertChild(node.condition), - update: this.convertChild(node.incrementor), - body: this.convertChild(node.statement), - }); - - case SyntaxKind.ForInStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.ForInStatement, - left: this.convertPattern(node.initializer), - right: this.convertChild(node.expression), - body: this.convertChild(node.statement), - }); - - case SyntaxKind.ForOfStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.ForOfStatement, - left: this.convertPattern(node.initializer), - right: this.convertChild(node.expression), - body: this.convertChild(node.statement), - await: Boolean( - node.awaitModifier && - node.awaitModifier.kind === SyntaxKind.AwaitKeyword, - ), - }); - - // Declarations - - case SyntaxKind.FunctionDeclaration: { - const isDeclare = hasModifier(SyntaxKind.DeclareKeyword, node); - - const result = this.createNode< - TSESTree.TSDeclareFunction | TSESTree.FunctionDeclaration - >(node, { - type: - isDeclare || !node.body - ? AST_NODE_TYPES.TSDeclareFunction - : AST_NODE_TYPES.FunctionDeclaration, - async: hasModifier(SyntaxKind.AsyncKeyword, node), - body: this.convertChild(node.body) || undefined, - declare: isDeclare, - expression: false, - generator: !!node.asteriskToken, - id: this.convertChild(node.name), - params: this.convertParameters(node.parameters), - returnType: node.type && this.convertTypeAnnotation(node.type, node), - typeParameters: - node.typeParameters && - this.convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters, - ), - }); - - return this.fixExports(node, result); - } - - case SyntaxKind.VariableDeclaration: { - return this.createNode(node, { - type: AST_NODE_TYPES.VariableDeclarator, - definite: !!node.exclamationToken, - id: this.convertBindingNameWithTypeAnnotation( - node.name, - node.type, - node, - ), - init: this.convertChild(node.initializer), - }); - } - - case SyntaxKind.VariableStatement: { - const result = this.createNode(node, { - type: AST_NODE_TYPES.VariableDeclaration, - declarations: node.declarationList.declarations.map(el => - this.convertChild(el), - ), - declare: hasModifier(SyntaxKind.DeclareKeyword, node), - kind: getDeclarationKind(node.declarationList), - }); - - /** - * Semantically, decorators are not allowed on variable declarations, - * Pre 4.8 TS would include them in the AST, so we did as well. - * However as of 4.8 TS no longer includes it (as it is, well, invalid). - * - * So for consistency across versions, we no longer include it either. - */ - return this.fixExports(node, result); - } - - // mostly for for-of, for-in - case SyntaxKind.VariableDeclarationList: - return this.createNode(node, { - type: AST_NODE_TYPES.VariableDeclaration, - declarations: node.declarations.map(el => this.convertChild(el)), - declare: false, - kind: getDeclarationKind(node), - }); - - // Expressions - - case SyntaxKind.ExpressionStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.ExpressionStatement, - directive: undefined, - expression: this.convertChild(node.expression), - }); - - case SyntaxKind.ThisKeyword: - return this.createNode(node, { - type: AST_NODE_TYPES.ThisExpression, - }); - - case SyntaxKind.ArrayLiteralExpression: { - // TypeScript uses ArrayLiteralExpression in destructuring assignment, too - if (this.allowPattern) { - return this.createNode(node, { - type: AST_NODE_TYPES.ArrayPattern, - decorators: [], - elements: node.elements.map(el => this.convertPattern(el)), - optional: false, - typeAnnotation: undefined, - }); - } else { - return this.createNode(node, { - type: AST_NODE_TYPES.ArrayExpression, - elements: node.elements.map(el => this.convertChild(el)), - }); - } - } - - case SyntaxKind.ObjectLiteralExpression: { - // TypeScript uses ObjectLiteralExpression in destructuring assignment, too - if (this.allowPattern) { - return this.createNode(node, { - type: AST_NODE_TYPES.ObjectPattern, - decorators: [], - optional: false, - properties: node.properties.map(el => this.convertPattern(el)), - typeAnnotation: undefined, - }); - } else { - return this.createNode(node, { - type: AST_NODE_TYPES.ObjectExpression, - properties: node.properties.map(el => this.convertChild(el)), - }); - } - } - - case SyntaxKind.PropertyAssignment: { - // eslint-disable-next-line deprecation/deprecation - const { questionToken, exclamationToken } = node; - - if (questionToken) { - this.#throwError( - questionToken, - 'A property assignment cannot have a question token.', - ); - } - - if (exclamationToken) { - this.#throwError( - exclamationToken, - 'A property assignment cannot have a question token.', - ); - } - - return this.createNode(node, { - type: AST_NODE_TYPES.Property, - key: this.convertChild(node.name), - value: this.converter(node.initializer, node, this.allowPattern), - computed: isComputedProperty(node.name), - method: false, - optional: false, - shorthand: false, - kind: 'init', - }); - } - - case SyntaxKind.ShorthandPropertyAssignment: { - // eslint-disable-next-line deprecation/deprecation - const { modifiers, questionToken, exclamationToken } = node; - - if (modifiers) { - this.#throwError( - modifiers[0], - 'A shorthand property assignment cannot have modifiers.', - ); - } - - if (questionToken) { - this.#throwError( - questionToken, - 'A shorthand property assignment cannot have a question token.', - ); - } - - if (exclamationToken) { - this.#throwError( - exclamationToken, - 'A shorthand property assignment cannot have an exclamation token.', - ); - } - - if (node.objectAssignmentInitializer) { - return this.createNode(node, { - type: AST_NODE_TYPES.Property, - key: this.convertChild(node.name), - value: this.createNode(node, { - type: AST_NODE_TYPES.AssignmentPattern, - decorators: [], - left: this.convertPattern(node.name), - optional: false, - right: this.convertChild(node.objectAssignmentInitializer), - typeAnnotation: undefined, - }), - computed: false, - method: false, - optional: false, - shorthand: true, - kind: 'init', - }); - } else { - return this.createNode(node, { - type: AST_NODE_TYPES.Property, - computed: false, - key: this.convertChild(node.name), - kind: 'init', - method: false, - optional: false, - shorthand: true, - value: this.convertChild(node.name), - }); - } - } - - case SyntaxKind.ComputedPropertyName: - return this.convertChild(node.expression); - - case SyntaxKind.PropertyDeclaration: { - const isAbstract = hasModifier(SyntaxKind.AbstractKeyword, node); - const isAccessor = hasModifier(SyntaxKind.AccessorKeyword, node); - - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -- TODO - add ignore IIFE option - const type = (() => { - if (isAccessor) { - if (isAbstract) { - return AST_NODE_TYPES.TSAbstractAccessorProperty; - } - return AST_NODE_TYPES.AccessorProperty; - } - - if (isAbstract) { - return AST_NODE_TYPES.TSAbstractPropertyDefinition; - } - return AST_NODE_TYPES.PropertyDefinition; - })(); - - const key = this.convertChild(node.name); - - return this.createNode< - | TSESTree.TSAbstractAccessorProperty - | TSESTree.TSAbstractPropertyDefinition - | TSESTree.PropertyDefinition - | TSESTree.AccessorProperty - >(node, { - type, - key, - accessibility: getTSNodeAccessibility(node), - value: isAbstract ? null : this.convertChild(node.initializer), - computed: isComputedProperty(node.name), - static: hasModifier(SyntaxKind.StaticKeyword, node), - readonly: hasModifier(SyntaxKind.ReadonlyKeyword, node), - decorators: - getDecorators(node)?.map(el => this.convertChild(el)) ?? [], - - declare: hasModifier(SyntaxKind.DeclareKeyword, node), - override: hasModifier(SyntaxKind.OverrideKeyword, node), - typeAnnotation: - node.type && this.convertTypeAnnotation(node.type, node), - optional: - (key.type === AST_NODE_TYPES.Literal || - node.name.kind === SyntaxKind.Identifier || - node.name.kind === SyntaxKind.ComputedPropertyName || - node.name.kind === SyntaxKind.PrivateIdentifier) && - !!node.questionToken, - definite: !!node.exclamationToken, - }); - } - - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: { - if ( - node.parent.kind === SyntaxKind.InterfaceDeclaration || - node.parent.kind === SyntaxKind.TypeLiteral - ) { - return this.convertMethodSignature(node); - } - } - // otherwise, it is a non-type accessor - intentional fallthrough - case SyntaxKind.MethodDeclaration: { - const method = this.createNode< - TSESTree.TSEmptyBodyFunctionExpression | TSESTree.FunctionExpression - >(node, { - type: !node.body - ? AST_NODE_TYPES.TSEmptyBodyFunctionExpression - : AST_NODE_TYPES.FunctionExpression, - id: null, - generator: !!node.asteriskToken, - expression: false, // ESTreeNode as ESTreeNode here - async: hasModifier(SyntaxKind.AsyncKeyword, node), - body: this.convertChild(node.body), - declare: false, - range: [node.parameters.pos - 1, node.end], - params: [], - returnType: node.type && this.convertTypeAnnotation(node.type, node), - typeParameters: - node.typeParameters && - this.convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters, - ), - }); - - if (method.typeParameters) { - this.fixParentLocation(method, method.typeParameters.range); - } - - let result: - | TSESTree.Property - | TSESTree.TSAbstractMethodDefinition - | TSESTree.MethodDefinition; - - if (parent.kind === SyntaxKind.ObjectLiteralExpression) { - method.params = node.parameters.map(el => this.convertChild(el)); - - result = this.createNode(node, { - type: AST_NODE_TYPES.Property, - key: this.convertChild(node.name), - value: method, - computed: isComputedProperty(node.name), - optional: !!node.questionToken, - method: node.kind === SyntaxKind.MethodDeclaration, - shorthand: false, - kind: 'init', - }); - } else { - // class - - /** - * Unlike in object literal methods, class method params can have decorators - */ - method.params = this.convertParameters(node.parameters); - - /** - * TypeScript class methods can be defined as "abstract" - */ - const methodDefinitionType = hasModifier( - SyntaxKind.AbstractKeyword, - node, - ) - ? AST_NODE_TYPES.TSAbstractMethodDefinition - : AST_NODE_TYPES.MethodDefinition; - - result = this.createNode< - TSESTree.TSAbstractMethodDefinition | TSESTree.MethodDefinition - >(node, { - type: methodDefinitionType, - accessibility: getTSNodeAccessibility(node), - computed: isComputedProperty(node.name), - decorators: - getDecorators(node)?.map(el => this.convertChild(el)) ?? [], - key: this.convertChild(node.name), - kind: 'method', - optional: !!node.questionToken, - override: hasModifier(SyntaxKind.OverrideKeyword, node), - static: hasModifier(SyntaxKind.StaticKeyword, node), - value: method, - }); - } - - if (node.kind === SyntaxKind.GetAccessor) { - result.kind = 'get'; - } else if (node.kind === SyntaxKind.SetAccessor) { - result.kind = 'set'; - } else if ( - !(result as TSESTree.MethodDefinition).static && - node.name.kind === SyntaxKind.StringLiteral && - node.name.text === 'constructor' && - result.type !== AST_NODE_TYPES.Property - ) { - result.kind = 'constructor'; - } - return result; - } - - // TypeScript uses this even for static methods named "constructor" - case SyntaxKind.Constructor: { - const lastModifier = getLastModifier(node); - const constructorToken = - (lastModifier && findNextToken(lastModifier, node, this.ast)) || - node.getFirstToken()!; - - const constructor = this.createNode< - TSESTree.TSEmptyBodyFunctionExpression | TSESTree.FunctionExpression - >(node, { - type: !node.body - ? AST_NODE_TYPES.TSEmptyBodyFunctionExpression - : AST_NODE_TYPES.FunctionExpression, - async: false, - body: this.convertChild(node.body), - declare: false, - expression: false, // is not present in ESTreeNode - generator: false, - id: null, - params: this.convertParameters(node.parameters), - range: [node.parameters.pos - 1, node.end], - returnType: node.type && this.convertTypeAnnotation(node.type, node), - typeParameters: - node.typeParameters && - this.convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters, - ), - }); - - if (constructor.typeParameters) { - this.fixParentLocation(constructor, constructor.typeParameters.range); - } - - const constructorKey = this.createNode(node, { - type: AST_NODE_TYPES.Identifier, - decorators: [], - name: 'constructor', - optional: false, - range: [constructorToken.getStart(this.ast), constructorToken.end], - typeAnnotation: undefined, - }); - - const isStatic = hasModifier(SyntaxKind.StaticKeyword, node); - - return this.createNode< - TSESTree.TSAbstractMethodDefinition | TSESTree.MethodDefinition - >(node, { - type: hasModifier(SyntaxKind.AbstractKeyword, node) - ? AST_NODE_TYPES.TSAbstractMethodDefinition - : AST_NODE_TYPES.MethodDefinition, - accessibility: getTSNodeAccessibility(node), - computed: false, - decorators: [], - optional: false, - key: constructorKey, - kind: isStatic ? 'method' : 'constructor', - override: false, - static: isStatic, - value: constructor, - }); - } - - case SyntaxKind.FunctionExpression: { - return this.createNode(node, { - type: AST_NODE_TYPES.FunctionExpression, - async: hasModifier(SyntaxKind.AsyncKeyword, node), - body: this.convertChild(node.body), - declare: false, - expression: false, - generator: !!node.asteriskToken, - id: this.convertChild(node.name), - params: this.convertParameters(node.parameters), - returnType: node.type && this.convertTypeAnnotation(node.type, node), - typeParameters: - node.typeParameters && - this.convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters, - ), - }); - } - - case SyntaxKind.SuperKeyword: - return this.createNode(node, { - type: AST_NODE_TYPES.Super, - }); - - case SyntaxKind.ArrayBindingPattern: - return this.createNode(node, { - type: AST_NODE_TYPES.ArrayPattern, - decorators: [], - elements: node.elements.map(el => this.convertPattern(el)), - optional: false, - typeAnnotation: undefined, - }); - - // occurs with missing array elements like [,] - case SyntaxKind.OmittedExpression: - return null; - - case SyntaxKind.ObjectBindingPattern: - return this.createNode(node, { - type: AST_NODE_TYPES.ObjectPattern, - decorators: [], - optional: false, - properties: node.elements.map(el => this.convertPattern(el)), - typeAnnotation: undefined, - }); - - case SyntaxKind.BindingElement: { - if (parent.kind === SyntaxKind.ArrayBindingPattern) { - const arrayItem = this.convertChild(node.name, parent); - - if (node.initializer) { - return this.createNode(node, { - type: AST_NODE_TYPES.AssignmentPattern, - decorators: [], - left: arrayItem, - optional: false, - right: this.convertChild(node.initializer), - typeAnnotation: undefined, - }); - } else if (node.dotDotDotToken) { - return this.createNode(node, { - type: AST_NODE_TYPES.RestElement, - argument: arrayItem, - decorators: [], - optional: false, - typeAnnotation: undefined, - value: undefined, - }); - } else { - return arrayItem; - } - } else { - let result: TSESTree.RestElement | TSESTree.Property; - if (node.dotDotDotToken) { - result = this.createNode(node, { - type: AST_NODE_TYPES.RestElement, - argument: this.convertChild(node.propertyName ?? node.name), - decorators: [], - optional: false, - typeAnnotation: undefined, - value: undefined, - }); - } else { - result = this.createNode(node, { - type: AST_NODE_TYPES.Property, - key: this.convertChild(node.propertyName ?? node.name), - value: this.convertChild(node.name), - computed: Boolean( - node.propertyName && - node.propertyName.kind === SyntaxKind.ComputedPropertyName, - ), - method: false, - optional: false, - shorthand: !node.propertyName, - kind: 'init', - }); - } - - if (node.initializer) { - result.value = this.createNode(node, { - type: AST_NODE_TYPES.AssignmentPattern, - decorators: [], - left: this.convertChild(node.name), - optional: false, - range: [node.name.getStart(this.ast), node.initializer.end], - right: this.convertChild(node.initializer), - typeAnnotation: undefined, - }); - } - return result; - } - } - - case SyntaxKind.ArrowFunction: { - return this.createNode(node, { - type: AST_NODE_TYPES.ArrowFunctionExpression, - generator: false, - id: null, - params: this.convertParameters(node.parameters), - body: this.convertChild(node.body), - async: hasModifier(SyntaxKind.AsyncKeyword, node), - expression: node.body.kind !== SyntaxKind.Block, - returnType: node.type && this.convertTypeAnnotation(node.type, node), - typeParameters: - node.typeParameters && - this.convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters, - ), - }); - } - - case SyntaxKind.YieldExpression: - return this.createNode(node, { - type: AST_NODE_TYPES.YieldExpression, - delegate: !!node.asteriskToken, - argument: this.convertChild(node.expression), - }); - - case SyntaxKind.AwaitExpression: - return this.createNode(node, { - type: AST_NODE_TYPES.AwaitExpression, - argument: this.convertChild(node.expression), - }); - - // Template Literals - - case SyntaxKind.NoSubstitutionTemplateLiteral: - return this.createNode(node, { - type: AST_NODE_TYPES.TemplateLiteral, - quasis: [ - this.createNode(node, { - type: AST_NODE_TYPES.TemplateElement, - value: { - raw: this.ast.text.slice( - node.getStart(this.ast) + 1, - node.end - 1, - ), - cooked: node.text, - }, - tail: true, - }), - ], - expressions: [], - }); - - case SyntaxKind.TemplateExpression: { - const result = this.createNode(node, { - type: AST_NODE_TYPES.TemplateLiteral, - quasis: [this.convertChild(node.head)], - expressions: [], - }); - - node.templateSpans.forEach(templateSpan => { - result.expressions.push( - this.convertChild(templateSpan.expression) as TSESTree.Expression, - ); - result.quasis.push( - this.convertChild(templateSpan.literal) as TSESTree.TemplateElement, - ); - }); - return result; - } - - case SyntaxKind.TaggedTemplateExpression: { - const typeArguments = node.typeArguments - ? this.convertTypeArgumentsToTypeParameterInstantiation( - node.typeArguments, - node, - ) - : undefined; - return this.createNode(node, { - type: AST_NODE_TYPES.TaggedTemplateExpression, - typeArguments, - typeParameters: typeArguments, - tag: this.convertChild(node.tag), - quasi: this.convertChild(node.template), - }); - } - - case SyntaxKind.TemplateHead: - case SyntaxKind.TemplateMiddle: - case SyntaxKind.TemplateTail: { - const tail = node.kind === SyntaxKind.TemplateTail; - return this.createNode(node, { - type: AST_NODE_TYPES.TemplateElement, - value: { - raw: this.ast.text.slice( - node.getStart(this.ast) + 1, - node.end - (tail ? 1 : 2), - ), - cooked: node.text, - }, - tail, - }); - } - - // Patterns - - case SyntaxKind.SpreadAssignment: - case SyntaxKind.SpreadElement: { - if (this.allowPattern) { - return this.createNode(node, { - type: AST_NODE_TYPES.RestElement, - argument: this.convertPattern(node.expression), - decorators: [], - optional: false, - typeAnnotation: undefined, - value: undefined, - }); - } else { - return this.createNode(node, { - type: AST_NODE_TYPES.SpreadElement, - argument: this.convertChild(node.expression), - }); - } - } - - case SyntaxKind.Parameter: { - let parameter: TSESTree.RestElement | TSESTree.BindingName; - let result: TSESTree.RestElement | TSESTree.AssignmentPattern; - - if (node.dotDotDotToken) { - parameter = result = this.createNode(node, { - type: AST_NODE_TYPES.RestElement, - argument: this.convertChild(node.name), - decorators: [], - optional: false, - typeAnnotation: undefined, - value: undefined, - }); - } else if (node.initializer) { - parameter = this.convertChild(node.name) as TSESTree.BindingName; - result = this.createNode(node, { - type: AST_NODE_TYPES.AssignmentPattern, - decorators: [], - left: parameter, - optional: false, - right: this.convertChild(node.initializer), - typeAnnotation: undefined, - }); - - const modifiers = getModifiers(node); - if (modifiers) { - // AssignmentPattern should not contain modifiers in range - result.range[0] = parameter.range[0]; - result.loc = getLocFor(result.range[0], result.range[1], this.ast); - } - } else { - parameter = result = this.convertChild(node.name, parent); - } - - if (node.type) { - parameter.typeAnnotation = this.convertTypeAnnotation( - node.type, - node, - ); - this.fixParentLocation(parameter, parameter.typeAnnotation.range); - } - - if (node.questionToken) { - if (node.questionToken.end > parameter.range[1]) { - parameter.range[1] = node.questionToken.end; - parameter.loc.end = getLineAndCharacterFor( - parameter.range[1], - this.ast, - ); - } - parameter.optional = true; - } - - const modifiers = getModifiers(node); - if (modifiers) { - const exportKeyword = getModifier(SyntaxKind.ExportKeyword, node); - if (exportKeyword) { - this.#throwError( - exportKeyword, - 'A parameter cannot have an export modifier.', - ); - } - - return this.createNode(node, { - type: AST_NODE_TYPES.TSParameterProperty, - accessibility: getTSNodeAccessibility(node), - decorators: [], - override: hasModifier(SyntaxKind.OverrideKeyword, node), - parameter: result, - readonly: hasModifier(SyntaxKind.ReadonlyKeyword, node), - static: hasModifier(SyntaxKind.StaticKeyword, node), - }); - } - return result; - } - - // Classes - - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: { - const heritageClauses = node.heritageClauses ?? []; - const classNodeType = - node.kind === SyntaxKind.ClassDeclaration - ? AST_NODE_TYPES.ClassDeclaration - : AST_NODE_TYPES.ClassExpression; - - const superClass = heritageClauses.find( - clause => clause.token === SyntaxKind.ExtendsKeyword, - ); - - if (superClass?.types && superClass.types.length > 1) { - this.#throwError( - superClass.types[1], - 'Classes can only extend a single class.', - ); - } - - const implementsClause = heritageClauses.find( - clause => clause.token === SyntaxKind.ImplementsKeyword, - ); - - const result = this.createNode< - TSESTree.ClassDeclaration | TSESTree.ClassExpression - >(node, { - type: classNodeType, - abstract: hasModifier(SyntaxKind.AbstractKeyword, node), - body: this.createNode(node, { - type: AST_NODE_TYPES.ClassBody, - body: node.members - .filter(isESTreeClassMember) - .map(el => this.convertChild(el)), - range: [node.members.pos - 1, node.end], - }), - declare: hasModifier(SyntaxKind.DeclareKeyword, node), - decorators: - getDecorators(node)?.map(el => this.convertChild(el)) ?? [], - id: this.convertChild(node.name), - implements: - implementsClause?.types.map(el => this.convertChild(el)) ?? [], - superClass: superClass?.types[0] - ? this.convertChild(superClass.types[0].expression) - : null, - superTypeArguments: undefined, - superTypeParameters: undefined, - typeParameters: - node.typeParameters && - this.convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters, - ), - }); - - if (superClass) { - if (superClass.types.length > 1) { - this.#throwError( - superClass.types[1], - 'Classes can only extend a single class.', - ); - } - - if (superClass.types[0]?.typeArguments) { - // eslint-disable-next-line deprecation/deprecation - result.superTypeArguments = result.superTypeParameters = - this.convertTypeArgumentsToTypeParameterInstantiation( - superClass.types[0].typeArguments, - superClass.types[0], - ); - } - } - - return this.fixExports(node, result); - } - - // Modules - case SyntaxKind.ModuleBlock: - return this.createNode(node, { - type: AST_NODE_TYPES.TSModuleBlock, - body: this.convertBodyExpressions(node.statements, node), - }); - - case SyntaxKind.ImportDeclaration: { - this.assertModuleSpecifier(node, false); - - const result = this.createNode(node, { - type: AST_NODE_TYPES.ImportDeclaration, - source: this.convertChild(node.moduleSpecifier), - specifiers: [], - importKind: 'value', - assertions: this.convertAssertClasue(node.assertClause), - }); - - if (node.importClause) { - if (node.importClause.isTypeOnly) { - result.importKind = 'type'; - } - - if (node.importClause.name) { - result.specifiers.push( - this.convertChild(node.importClause) as TSESTree.ImportClause, - ); - } - - if (node.importClause.namedBindings) { - switch (node.importClause.namedBindings.kind) { - case SyntaxKind.NamespaceImport: - result.specifiers.push( - this.convertChild( - node.importClause.namedBindings, - ) as TSESTree.ImportClause, - ); - break; - case SyntaxKind.NamedImports: - result.specifiers = result.specifiers.concat( - node.importClause.namedBindings.elements.map(el => - this.convertChild(el), - ), - ); - break; - } - } - } - return result; - } - - case SyntaxKind.NamespaceImport: - return this.createNode(node, { - type: AST_NODE_TYPES.ImportNamespaceSpecifier, - local: this.convertChild(node.name), - }); - - case SyntaxKind.ImportSpecifier: - return this.createNode(node, { - type: AST_NODE_TYPES.ImportSpecifier, - local: this.convertChild(node.name), - imported: this.convertChild(node.propertyName ?? node.name), - importKind: node.isTypeOnly ? 'type' : 'value', - }); - - case SyntaxKind.ImportClause: { - const local = this.convertChild(node.name); - return this.createNode(node, { - type: AST_NODE_TYPES.ImportDefaultSpecifier, - local, - range: local.range, - }); - } - - case SyntaxKind.ExportDeclaration: { - if (node.exportClause?.kind === SyntaxKind.NamedExports) { - this.assertModuleSpecifier(node, true); - return this.createNode(node, { - type: AST_NODE_TYPES.ExportNamedDeclaration, - source: this.convertChild(node.moduleSpecifier), - specifiers: node.exportClause.elements.map(el => - this.convertChild(el), - ), - exportKind: node.isTypeOnly ? 'type' : 'value', - declaration: null, - assertions: this.convertAssertClasue(node.assertClause), - }); - } else { - this.assertModuleSpecifier(node, false); - return this.createNode(node, { - type: AST_NODE_TYPES.ExportAllDeclaration, - source: this.convertChild(node.moduleSpecifier), - exportKind: node.isTypeOnly ? 'type' : 'value', - exported: - // note - for compat with 3.7.x, where node.exportClause is always undefined and - // SyntaxKind.NamespaceExport does not exist yet (i.e. is undefined), this - // cannot be shortened to an optional chain, or else you end up with - // undefined === undefined, and the true path will hard error at runtime - node.exportClause && - node.exportClause.kind === SyntaxKind.NamespaceExport - ? this.convertChild(node.exportClause.name) - : null, - assertions: this.convertAssertClasue(node.assertClause), - }); - } - } - - case SyntaxKind.ExportSpecifier: - return this.createNode(node, { - type: AST_NODE_TYPES.ExportSpecifier, - local: this.convertChild(node.propertyName ?? node.name), - exported: this.convertChild(node.name), - exportKind: node.isTypeOnly ? 'type' : 'value', - }); - - case SyntaxKind.ExportAssignment: - if (node.isExportEquals) { - return this.createNode(node, { - type: AST_NODE_TYPES.TSExportAssignment, - expression: this.convertChild(node.expression), - }); - } else { - return this.createNode(node, { - type: AST_NODE_TYPES.ExportDefaultDeclaration, - declaration: this.convertChild(node.expression), - exportKind: 'value', - }); - } - - // Unary Operations - - case SyntaxKind.PrefixUnaryExpression: - case SyntaxKind.PostfixUnaryExpression: { - const operator = getTextForTokenKind(node.operator); - /** - * ESTree uses UpdateExpression for ++/-- - */ - if (operator === '++' || operator === '--') { - return this.createNode(node, { - type: AST_NODE_TYPES.UpdateExpression, - operator, - prefix: node.kind === SyntaxKind.PrefixUnaryExpression, - argument: this.convertChild(node.operand), - }); - } else { - return this.createNode(node, { - type: AST_NODE_TYPES.UnaryExpression, - operator, - prefix: node.kind === SyntaxKind.PrefixUnaryExpression, - argument: this.convertChild(node.operand), - }); - } - } - - case SyntaxKind.DeleteExpression: - return this.createNode(node, { - type: AST_NODE_TYPES.UnaryExpression, - operator: 'delete', - prefix: true, - argument: this.convertChild(node.expression), - }); - - case SyntaxKind.VoidExpression: - return this.createNode(node, { - type: AST_NODE_TYPES.UnaryExpression, - operator: 'void', - prefix: true, - argument: this.convertChild(node.expression), - }); - - case SyntaxKind.TypeOfExpression: - return this.createNode(node, { - type: AST_NODE_TYPES.UnaryExpression, - operator: 'typeof', - prefix: true, - argument: this.convertChild(node.expression), - }); - - case SyntaxKind.TypeOperator: - return this.createNode(node, { - type: AST_NODE_TYPES.TSTypeOperator, - operator: getTextForTokenKind(node.operator), - typeAnnotation: this.convertChild(node.type), - }); - - // Binary Operations - - case SyntaxKind.BinaryExpression: { - // TypeScript uses BinaryExpression for sequences as well - if (isComma(node.operatorToken)) { - const result = this.createNode(node, { - type: AST_NODE_TYPES.SequenceExpression, - expressions: [], - }); - - const left = this.convertChild(node.left) as TSESTree.Expression; - if ( - left.type === AST_NODE_TYPES.SequenceExpression && - node.left.kind !== SyntaxKind.ParenthesizedExpression - ) { - result.expressions = result.expressions.concat(left.expressions); - } else { - result.expressions.push(left); - } - - result.expressions.push( - this.convertChild(node.right) as TSESTree.Expression, - ); - return result; - } else { - const type = getBinaryExpressionType(node.operatorToken); - if ( - this.allowPattern && - type === AST_NODE_TYPES.AssignmentExpression - ) { - return this.createNode(node, { - type: AST_NODE_TYPES.AssignmentPattern, - decorators: [], - left: this.convertPattern(node.left, node), - optional: false, - right: this.convertChild(node.right), - typeAnnotation: undefined, - }); - } - return this.createNode< - | TSESTree.AssignmentExpression - | TSESTree.LogicalExpression - | TSESTree.BinaryExpression - >(node, { - type, - operator: getTextForTokenKind(node.operatorToken.kind), - left: this.converter( - node.left, - node, - type === AST_NODE_TYPES.AssignmentExpression, - ), - right: this.convertChild(node.right), - }); - } - } - - case SyntaxKind.PropertyAccessExpression: { - const object = this.convertChild(node.expression); - const property = this.convertChild(node.name); - const computed = false; - - const result = this.createNode(node, { - type: AST_NODE_TYPES.MemberExpression, - object, - property, - computed, - optional: node.questionDotToken !== undefined, - }); - - return this.convertChainExpression(result, node); - } - - case SyntaxKind.ElementAccessExpression: { - const object = this.convertChild(node.expression); - const property = this.convertChild(node.argumentExpression); - const computed = true; - - const result = this.createNode(node, { - type: AST_NODE_TYPES.MemberExpression, - object, - property, - computed, - optional: node.questionDotToken !== undefined, - }); - - return this.convertChainExpression(result, node); - } - - case SyntaxKind.CallExpression: { - if (node.expression.kind === SyntaxKind.ImportKeyword) { - if (node.arguments.length !== 1 && node.arguments.length !== 2) { - this.#throwError( - node, - 'Dynamic import requires exactly one or two arguments.', - ); - } - return this.createNode(node, { - type: AST_NODE_TYPES.ImportExpression, - source: this.convertChild(node.arguments[0]), - attributes: node.arguments[1] - ? this.convertChild(node.arguments[1]) - : null, - }); - } - - const callee = this.convertChild(node.expression); - const args = node.arguments.map(el => this.convertChild(el)); - const typeArguments = - node.typeArguments && - this.convertTypeArgumentsToTypeParameterInstantiation( - node.typeArguments, - node, - ); - - const result = this.createNode(node, { - type: AST_NODE_TYPES.CallExpression, - callee, - arguments: args, - optional: node.questionDotToken !== undefined, - typeArguments, - typeParameters: typeArguments, - }); - - return this.convertChainExpression(result, node); - } - - case SyntaxKind.NewExpression: { - const typeArguments = - node.typeArguments && - this.convertTypeArgumentsToTypeParameterInstantiation( - node.typeArguments, - node, - ); - - // NOTE - NewExpression cannot have an optional chain in it - return this.createNode(node, { - type: AST_NODE_TYPES.NewExpression, - arguments: node.arguments - ? node.arguments.map(el => this.convertChild(el)) - : [], - callee: this.convertChild(node.expression), - typeArguments, - typeParameters: typeArguments, - }); - } - - case SyntaxKind.ConditionalExpression: - return this.createNode(node, { - type: AST_NODE_TYPES.ConditionalExpression, - test: this.convertChild(node.condition), - consequent: this.convertChild(node.whenTrue), - alternate: this.convertChild(node.whenFalse), - }); - - case SyntaxKind.MetaProperty: { - return this.createNode(node, { - type: AST_NODE_TYPES.MetaProperty, - meta: this.createNode( - // TODO: do we really want to convert it to Token? - node.getFirstToken()! as ts.Token, - { - type: AST_NODE_TYPES.Identifier, - decorators: [], - name: getTextForTokenKind(node.keywordToken), - optional: false, - typeAnnotation: undefined, - }, - ), - property: this.convertChild(node.name), - }); - } - - case SyntaxKind.Decorator: { - return this.createNode(node, { - type: AST_NODE_TYPES.Decorator, - expression: this.convertChild(node.expression), - }); - } - - // Literals - - case SyntaxKind.StringLiteral: { - return this.createNode(node, { - type: AST_NODE_TYPES.Literal, - value: - parent.kind === SyntaxKind.JsxAttribute - ? unescapeStringLiteralText(node.text) - : node.text, - raw: node.getText(), - }); - } - - case SyntaxKind.NumericLiteral: { - return this.createNode(node, { - type: AST_NODE_TYPES.Literal, - value: Number(node.text), - raw: node.getText(), - }); - } - - case SyntaxKind.BigIntLiteral: { - const range = getRange(node, this.ast); - const rawValue = this.ast.text.slice(range[0], range[1]); - const bigint = rawValue - // remove suffix `n` - .slice(0, -1) - // `BigInt` doesn't accept numeric separator - // and `bigint` property should not include numeric separator - .replace(/_/g, ''); - const value = typeof BigInt !== 'undefined' ? BigInt(bigint) : null; - return this.createNode(node, { - type: AST_NODE_TYPES.Literal, - raw: rawValue, - value: value, - bigint: value == null ? bigint : String(value), - range, - }); - } - - case SyntaxKind.RegularExpressionLiteral: { - const pattern = node.text.slice(1, node.text.lastIndexOf('/')); - const flags = node.text.slice(node.text.lastIndexOf('/') + 1); - - let regex = null; - try { - regex = new RegExp(pattern, flags); - } catch { - // Intentionally blank, so regex stays null - } - - return this.createNode(node, { - type: AST_NODE_TYPES.Literal, - value: regex, - raw: node.text, - regex: { - pattern, - flags, - }, - }); - } - - case SyntaxKind.TrueKeyword: - return this.createNode(node, { - type: AST_NODE_TYPES.Literal, - value: true, - raw: 'true', - }); - - case SyntaxKind.FalseKeyword: - return this.createNode(node, { - type: AST_NODE_TYPES.Literal, - value: false, - raw: 'false', - }); - - case SyntaxKind.NullKeyword: { - return this.createNode(node, { - type: AST_NODE_TYPES.Literal, - value: null, - raw: 'null', - }); - } - - case SyntaxKind.EmptyStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.EmptyStatement, - }); - - case SyntaxKind.DebuggerStatement: - return this.createNode(node, { - type: AST_NODE_TYPES.DebuggerStatement, - }); - - // JSX - - case SyntaxKind.JsxElement: - return this.createNode(node, { - type: AST_NODE_TYPES.JSXElement, - openingElement: this.convertChild(node.openingElement), - closingElement: this.convertChild(node.closingElement), - children: node.children.map(el => this.convertChild(el)), - }); - - case SyntaxKind.JsxFragment: - return this.createNode(node, { - type: AST_NODE_TYPES.JSXFragment, - openingFragment: this.convertChild(node.openingFragment), - closingFragment: this.convertChild(node.closingFragment), - children: node.children.map(el => this.convertChild(el)), - }); - - case SyntaxKind.JsxSelfClosingElement: { - const typeArguments = node.typeArguments - ? this.convertTypeArgumentsToTypeParameterInstantiation( - node.typeArguments, - node, - ) - : undefined; - return this.createNode(node, { - type: AST_NODE_TYPES.JSXElement, - /** - * Convert SyntaxKind.JsxSelfClosingElement to SyntaxKind.JsxOpeningElement, - * TypeScript does not seem to have the idea of openingElement when tag is self-closing - */ - openingElement: this.createNode(node, { - type: AST_NODE_TYPES.JSXOpeningElement, - typeArguments, - typeParameters: typeArguments, - selfClosing: true, - name: this.convertJSXTagName(node.tagName, node), - attributes: node.attributes.properties.map(el => - this.convertChild(el), - ), - range: getRange(node, this.ast), - }), - closingElement: null, - children: [], - }); - } - - case SyntaxKind.JsxOpeningElement: { - const typeArguments = - node.typeArguments && - this.convertTypeArgumentsToTypeParameterInstantiation( - node.typeArguments, - node, - ); - - return this.createNode(node, { - type: AST_NODE_TYPES.JSXOpeningElement, - typeArguments, - typeParameters: typeArguments, - selfClosing: false, - name: this.convertJSXTagName(node.tagName, node), - attributes: node.attributes.properties.map(el => - this.convertChild(el), - ), - }); - } - - case SyntaxKind.JsxClosingElement: - return this.createNode(node, { - type: AST_NODE_TYPES.JSXClosingElement, - name: this.convertJSXTagName(node.tagName, node), - }); - - case SyntaxKind.JsxOpeningFragment: - return this.createNode(node, { - type: AST_NODE_TYPES.JSXOpeningFragment, - }); - - case SyntaxKind.JsxClosingFragment: - return this.createNode(node, { - type: AST_NODE_TYPES.JSXClosingFragment, - }); - - case SyntaxKind.JsxExpression: { - const expression = node.expression - ? this.convertChild(node.expression) - : this.createNode(node, { - type: AST_NODE_TYPES.JSXEmptyExpression, - range: [node.getStart(this.ast) + 1, node.getEnd() - 1], - }); - - if (node.dotDotDotToken) { - return this.createNode(node, { - type: AST_NODE_TYPES.JSXSpreadChild, - expression, - }); - } else { - return this.createNode(node, { - type: AST_NODE_TYPES.JSXExpressionContainer, - expression, - }); - } - } - - case SyntaxKind.JsxAttribute: { - return this.createNode(node, { - type: AST_NODE_TYPES.JSXAttribute, - name: this.convertJSXNamespaceOrIdentifier(node.name), - value: this.convertChild(node.initializer), - }); - } - - case SyntaxKind.JsxText: { - const start = node.getFullStart(); - const end = node.getEnd(); - const text = this.ast.text.slice(start, end); - - return this.createNode(node, { - type: AST_NODE_TYPES.JSXText, - value: unescapeStringLiteralText(text), - raw: text, - range: [start, end], - }); - } - - case SyntaxKind.JsxSpreadAttribute: - return this.createNode(node, { - type: AST_NODE_TYPES.JSXSpreadAttribute, - argument: this.convertChild(node.expression), - }); - - case SyntaxKind.QualifiedName: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSQualifiedName, - left: this.convertChild(node.left), - right: this.convertChild(node.right), - }); - } - - // TypeScript specific - - case SyntaxKind.TypeReference: { - const typeArguments = node.typeArguments - ? this.convertTypeArgumentsToTypeParameterInstantiation( - node.typeArguments, - node, - ) - : undefined; - return this.createNode(node, { - type: AST_NODE_TYPES.TSTypeReference, - typeName: this.convertChild(node.typeName), - typeArguments, - typeParameters: typeArguments, - }); - } - - case SyntaxKind.TypeParameter: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSTypeParameter, - name: this.convertChild(node.name), - constraint: node.constraint && this.convertChild(node.constraint), - default: node.default ? this.convertChild(node.default) : undefined, - in: hasModifier(SyntaxKind.InKeyword, node), - out: hasModifier(SyntaxKind.OutKeyword, node), - }); - } - - case SyntaxKind.ThisType: - return this.createNode(node, { - type: AST_NODE_TYPES.TSThisType, - }); - - case SyntaxKind.AnyKeyword: - case SyntaxKind.BigIntKeyword: - case SyntaxKind.BooleanKeyword: - case SyntaxKind.NeverKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.ObjectKeyword: - case SyntaxKind.StringKeyword: - case SyntaxKind.SymbolKeyword: - case SyntaxKind.UnknownKeyword: - case SyntaxKind.VoidKeyword: - case SyntaxKind.UndefinedKeyword: - case SyntaxKind.IntrinsicKeyword: { - return this.createNode(node, { - type: AST_NODE_TYPES[`TS${SyntaxKind[node.kind]}` as AST_NODE_TYPES], - }); - } - - case SyntaxKind.NonNullExpression: { - const nnExpr = this.createNode(node, { - type: AST_NODE_TYPES.TSNonNullExpression, - expression: this.convertChild(node.expression), - }); - - return this.convertChainExpression(nnExpr, node); - } - - case SyntaxKind.TypeLiteral: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSTypeLiteral, - members: node.members.map(el => this.convertChild(el)), - }); - } - - case SyntaxKind.ArrayType: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSArrayType, - elementType: this.convertChild(node.elementType), - }); - } - - case SyntaxKind.IndexedAccessType: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSIndexedAccessType, - objectType: this.convertChild(node.objectType), - indexType: this.convertChild(node.indexType), - }); - } - - case SyntaxKind.ConditionalType: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSConditionalType, - checkType: this.convertChild(node.checkType), - extendsType: this.convertChild(node.extendsType), - trueType: this.convertChild(node.trueType), - falseType: this.convertChild(node.falseType), - }); - } - - case SyntaxKind.TypeQuery: { - const typeArguments = - node.typeArguments && - this.convertTypeArgumentsToTypeParameterInstantiation( - node.typeArguments, - node, - ); - return this.createNode(node, { - type: AST_NODE_TYPES.TSTypeQuery, - exprName: this.convertChild(node.exprName), - typeArguments, - typeParameters: typeArguments, - }); - } - - case SyntaxKind.MappedType: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSMappedType, - nameType: this.convertChild(node.nameType) ?? null, - optional: - node.questionToken && - (node.questionToken.kind === SyntaxKind.QuestionToken || - getTextForTokenKind(node.questionToken.kind)), - readonly: - node.readonlyToken && - (node.readonlyToken.kind === SyntaxKind.ReadonlyKeyword || - getTextForTokenKind(node.readonlyToken.kind)), - typeAnnotation: node.type && this.convertChild(node.type), - typeParameter: this.convertChild(node.typeParameter), - }); - } - - case SyntaxKind.ParenthesizedExpression: - return this.convertChild(node.expression, parent); - - case SyntaxKind.TypeAliasDeclaration: { - const result = this.createNode(node, { - type: AST_NODE_TYPES.TSTypeAliasDeclaration, - declare: hasModifier(SyntaxKind.DeclareKeyword, node), - id: this.convertChild(node.name), - typeAnnotation: this.convertChild(node.type), - typeParameters: - node.typeParameters && - this.convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters, - ), - }); - - return this.fixExports(node, result); - } - - case SyntaxKind.MethodSignature: { - return this.convertMethodSignature(node); - } - - case SyntaxKind.PropertySignature: { - // eslint-disable-next-line deprecation/deprecation - const { initializer } = node; - if (initializer) { - this.#throwError( - initializer, - 'A property signature cannot have an initializer.', - ); - } - - const exportKeyword = getModifier(SyntaxKind.ExportKeyword, node); - if (exportKeyword) { - this.#throwError( - exportKeyword, - 'A property signature cannot have an export modifier.', - ); - } - - return this.createNode(node, { - type: AST_NODE_TYPES.TSPropertySignature, - accessibility: getTSNodeAccessibility(node), - computed: isComputedProperty(node.name), - key: this.convertChild(node.name), - optional: isOptional(node), - readonly: hasModifier(SyntaxKind.ReadonlyKeyword, node), - static: hasModifier(SyntaxKind.StaticKeyword, node), - typeAnnotation: - node.type && this.convertTypeAnnotation(node.type, node), - }); - } - - case SyntaxKind.IndexSignature: { - const exportKeyword = getModifier(SyntaxKind.ExportKeyword, node); - if (exportKeyword) { - this.#throwError( - exportKeyword, - 'An index signature cannot have an export modifier.', - ); - } - - return this.createNode(node, { - type: AST_NODE_TYPES.TSIndexSignature, - accessibility: getTSNodeAccessibility(node), - parameters: node.parameters.map(el => this.convertChild(el)), - readonly: hasModifier(SyntaxKind.ReadonlyKeyword, node), - static: hasModifier(SyntaxKind.StaticKeyword, node), - typeAnnotation: - node.type && this.convertTypeAnnotation(node.type, node), - }); - } - - case SyntaxKind.ConstructorType: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSConstructorType, - abstract: hasModifier(SyntaxKind.AbstractKeyword, node), - params: this.convertParameters(node.parameters), - returnType: node.type && this.convertTypeAnnotation(node.type, node), - typeParameters: - node.typeParameters && - this.convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters, - ), - }); - } - - case SyntaxKind.FunctionType: { - // eslint-disable-next-line deprecation/deprecation - const { modifiers } = node; - if (modifiers) { - this.#throwError( - modifiers[0], - 'A function type cannot have modifiers.', - ); - } - } - // intentional fallthrough - case SyntaxKind.ConstructSignature: - case SyntaxKind.CallSignature: { - const type = - node.kind === SyntaxKind.ConstructSignature - ? AST_NODE_TYPES.TSConstructSignatureDeclaration - : node.kind === SyntaxKind.CallSignature - ? AST_NODE_TYPES.TSCallSignatureDeclaration - : AST_NODE_TYPES.TSFunctionType; - - return this.createNode< - | TSESTree.TSFunctionType - | TSESTree.TSCallSignatureDeclaration - | TSESTree.TSConstructSignatureDeclaration - >(node, { - type, - params: this.convertParameters(node.parameters), - returnType: node.type && this.convertTypeAnnotation(node.type, node), - typeParameters: - node.typeParameters && - this.convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters, - ), - }); - } - - case SyntaxKind.ExpressionWithTypeArguments: { - const parentKind = parent.kind; - const type = - parentKind === SyntaxKind.InterfaceDeclaration - ? AST_NODE_TYPES.TSInterfaceHeritage - : parentKind === SyntaxKind.HeritageClause - ? AST_NODE_TYPES.TSClassImplements - : AST_NODE_TYPES.TSInstantiationExpression; - - const typeArguments = - node.typeArguments && - this.convertTypeArgumentsToTypeParameterInstantiation( - node.typeArguments, - node, - ); - - const result = this.createNode< - | TSESTree.TSInterfaceHeritage - | TSESTree.TSClassImplements - | TSESTree.TSInstantiationExpression - >(node, { - type, - expression: this.convertChild(node.expression), - typeArguments, - typeParameters: typeArguments, - }); - - return result; - } - - case SyntaxKind.InterfaceDeclaration: { - const interfaceHeritageClauses = node.heritageClauses ?? []; - const result = this.createNode(node, { - type: AST_NODE_TYPES.TSInterfaceDeclaration, - body: this.createNode(node, { - type: AST_NODE_TYPES.TSInterfaceBody, - body: node.members.map(member => this.convertChild(member)), - range: [node.members.pos - 1, node.end], - }), - declare: hasModifier(SyntaxKind.DeclareKeyword, node), - extends: interfaceHeritageClauses - .filter( - heritageClause => - heritageClause.token === SyntaxKind.ExtendsKeyword, - ) - .flatMap(heritageClause => - heritageClause.types.map( - n => this.convertChild(n, node) as TSESTree.TSInterfaceHeritage, - ), - ), - - id: this.convertChild(node.name), - typeParameters: - node.typeParameters && - this.convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters, - ), - }); - - return this.fixExports(node, result); - } - - case SyntaxKind.TypePredicate: { - const result = this.createNode(node, { - type: AST_NODE_TYPES.TSTypePredicate, - asserts: node.assertsModifier !== undefined, - parameterName: this.convertChild(node.parameterName), - typeAnnotation: null, - }); - /** - * Specific fix for type-guard location data - */ - if (node.type) { - result.typeAnnotation = this.convertTypeAnnotation(node.type, node); - result.typeAnnotation.loc = result.typeAnnotation.typeAnnotation.loc; - result.typeAnnotation.range = - result.typeAnnotation.typeAnnotation.range; - } - return result; - } - - case SyntaxKind.ImportType: { - const range = getRange(node, this.ast); - if (node.isTypeOf) { - const token = findNextToken(node.getFirstToken()!, node, this.ast)!; - range[0] = token.getStart(this.ast); - } - const typeArguments = node.typeArguments - ? this.convertTypeArgumentsToTypeParameterInstantiation( - node.typeArguments, - node, - ) - : null; - const result = this.createNode(node, { - type: AST_NODE_TYPES.TSImportType, - argument: this.convertChild(node.argument), - qualifier: this.convertChild(node.qualifier), - typeArguments, - typeParameters: typeArguments, - range: range, - }); - if (node.isTypeOf) { - return this.createNode(node, { - type: AST_NODE_TYPES.TSTypeQuery, - exprName: result, - typeArguments: undefined, - typeParameters: undefined, - }); - } - return result; - } - - case SyntaxKind.EnumDeclaration: { - const result = this.createNode(node, { - type: AST_NODE_TYPES.TSEnumDeclaration, - const: hasModifier(SyntaxKind.ConstKeyword, node), - declare: hasModifier(SyntaxKind.DeclareKeyword, node), - id: this.convertChild(node.name), - members: node.members.map(el => this.convertChild(el)), - }); - - return this.fixExports(node, result); - } - - case SyntaxKind.EnumMember: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSEnumMember, - computed: node.name.kind === ts.SyntaxKind.ComputedPropertyName, - id: this.convertChild(node.name), - initializer: node.initializer && this.convertChild(node.initializer), - }); - } - - case SyntaxKind.ModuleDeclaration: { - const result = this.createNode(node, { - type: AST_NODE_TYPES.TSModuleDeclaration, - ...((): TSESTree.OptionalRangeAndLoc< - Omit - > => { - // the constraints checked by this function are syntactically enforced by TS - // the checks mostly exist for type's sake - - if (node.flags & ts.NodeFlags.GlobalAugmentation) { - const id: TSESTree.Identifier | TSESTree.StringLiteral = - this.convertChild(node.name); - const body: - | TSESTree.TSModuleBlock - | TSESTree.TSModuleDeclaration - | null = this.convertChild(node.body); - - if ( - body == null || - body.type === AST_NODE_TYPES.TSModuleDeclaration - ) { - throw new Error('Expected a valid module body'); - } - if (id.type !== AST_NODE_TYPES.Identifier) { - throw new Error( - 'global module augmentation must have an Identifier id', - ); - } - return { - kind: 'global', - body, - declare: false, - global: false, - id, - }; - } - - if (!(node.flags & ts.NodeFlags.Namespace)) { - const body: TSESTree.TSModuleBlock | null = this.convertChild( - node.body, - ); - return { - kind: 'module', - ...(body != null ? { body } : {}), - declare: false, - global: false, - id: this.convertChild(node.name), - }; - } - - // Nested module declarations are stored in TypeScript as nested tree nodes. - // We "unravel" them here by making our own nested TSQualifiedName, - // with the innermost node's body as the actual node body. - - if (node.body == null) { - throw new Error('Expected a module body'); - } - if (node.name.kind !== ts.SyntaxKind.Identifier) { - throw new Error('`namespace`s must have an Identifier id'); - } - - let name: TSESTree.Identifier | TSESTree.TSQualifiedName = - this.createNode(node.name, { - decorators: [], - name: node.name.text, - optional: false, - range: [node.name.getStart(this.ast), node.name.getEnd()], - type: AST_NODE_TYPES.Identifier, - typeAnnotation: undefined, - }); - - while ( - node.body && - ts.isModuleDeclaration(node.body) && - node.body.name - ) { - node = node.body; - - const nextName = node.name as ts.Identifier; - - const right = this.createNode(nextName, { - decorators: [], - name: nextName.text, - optional: false, - range: [nextName.getStart(this.ast), nextName.getEnd()], - type: AST_NODE_TYPES.Identifier, - typeAnnotation: undefined, - }); - - name = this.createNode(nextName, { - left: name, - right: right, - range: [name.range[0], right.range[1]], - type: AST_NODE_TYPES.TSQualifiedName, - }); - } - - return { - kind: 'namespace', - body: this.convertChild(node.body), - declare: false, - global: false, - id: name, - }; - })(), - }); - - if (hasModifier(SyntaxKind.DeclareKeyword, node)) { - result.declare = true; - } - - if (node.flags & ts.NodeFlags.GlobalAugmentation) { - result.global = true; - } - - return this.fixExports(node, result); - } - - // TypeScript specific types - case SyntaxKind.ParenthesizedType: { - return this.convertChild(node.type); - } - case SyntaxKind.UnionType: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSUnionType, - types: node.types.map(el => this.convertChild(el)), - }); - } - case SyntaxKind.IntersectionType: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSIntersectionType, - types: node.types.map(el => this.convertChild(el)), - }); - } - case SyntaxKind.AsExpression: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSAsExpression, - expression: this.convertChild(node.expression), - typeAnnotation: this.convertChild(node.type), - }); - } - case SyntaxKind.InferType: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSInferType, - typeParameter: this.convertChild(node.typeParameter), - }); - } - case SyntaxKind.LiteralType: { - if (node.literal.kind === SyntaxKind.NullKeyword) { - // 4.0 started nesting null types inside a LiteralType node - // but our AST is designed around the old way of null being a keyword - return this.createNode( - node.literal as ts.NullLiteral, - { - type: AST_NODE_TYPES.TSNullKeyword, - }, - ); - } else { - return this.createNode(node, { - type: AST_NODE_TYPES.TSLiteralType, - literal: this.convertChild(node.literal), - }); - } - } - case SyntaxKind.TypeAssertionExpression: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSTypeAssertion, - typeAnnotation: this.convertChild(node.type), - expression: this.convertChild(node.expression), - }); - } - case SyntaxKind.ImportEqualsDeclaration: { - return this.fixExports( - node, - this.createNode(node, { - type: AST_NODE_TYPES.TSImportEqualsDeclaration, - id: this.convertChild(node.name), - importKind: node.isTypeOnly ? 'type' : 'value', - moduleReference: this.convertChild(node.moduleReference), - }), - ); - } - case SyntaxKind.ExternalModuleReference: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSExternalModuleReference, - expression: this.convertChild(node.expression), - }); - } - case SyntaxKind.NamespaceExportDeclaration: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSNamespaceExportDeclaration, - id: this.convertChild(node.name), - }); - } - case SyntaxKind.AbstractKeyword: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSAbstractKeyword, - }); - } - - // Tuple - case SyntaxKind.TupleType: { - // In TS 4.0, the `elementTypes` property was changed to `elements`. - // To support both at compile time, we cast to access the newer version - // if the former does not exist. - const elementTypes = - 'elementTypes' in node - ? (node as any).elementTypes.map((el: ts.Node) => - this.convertChild(el), - ) - : node.elements.map(el => this.convertChild(el)); - - return this.createNode(node, { - type: AST_NODE_TYPES.TSTupleType, - elementTypes, - }); - } - case SyntaxKind.NamedTupleMember: { - const member = this.createNode(node, { - type: AST_NODE_TYPES.TSNamedTupleMember, - elementType: this.convertChild(node.type, node), - label: this.convertChild(node.name, node), - optional: node.questionToken != null, - }); - - if (node.dotDotDotToken) { - // adjust the start to account for the "..." - member.range[0] = member.label.range[0]; - member.loc.start = member.label.loc.start; - return this.createNode(node, { - type: AST_NODE_TYPES.TSRestType, - typeAnnotation: member, - }); - } - - return member; - } - case SyntaxKind.OptionalType: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSOptionalType, - typeAnnotation: this.convertChild(node.type), - }); - } - case SyntaxKind.RestType: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSRestType, - typeAnnotation: this.convertChild(node.type), - }); - } - - // Template Literal Types - case SyntaxKind.TemplateLiteralType: { - const result = this.createNode(node, { - type: AST_NODE_TYPES.TSTemplateLiteralType, - quasis: [this.convertChild(node.head)], - types: [], - }); - - node.templateSpans.forEach(templateSpan => { - result.types.push( - this.convertChild(templateSpan.type) as TSESTree.TypeNode, - ); - result.quasis.push( - this.convertChild(templateSpan.literal) as TSESTree.TemplateElement, - ); - }); - return result; - } - - case SyntaxKind.ClassStaticBlockDeclaration: { - return this.createNode(node, { - type: AST_NODE_TYPES.StaticBlock, - body: this.convertBodyExpressions(node.body.statements, node), - }); - } - - case SyntaxKind.AssertEntry: { - return this.createNode(node, { - type: AST_NODE_TYPES.ImportAttribute, - key: this.convertChild(node.name), - value: this.convertChild(node.value), - }); - } - - case SyntaxKind.SatisfiesExpression: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSSatisfiesExpression, - expression: this.convertChild(node.expression), - typeAnnotation: this.convertChild(node.type), - }); - } - - default: - return this.deeplyCopy(node); - } - } - - #throwError(node: ts.Node | number, message: string): asserts node is never { - let start: number; - let end: number; - if (typeof node === 'number') { - start = node; - end = node; - } else { - start = node.getStart(); - end = node.getEnd(); - } - - throw createError(message, this.ast, start, end); - } -} From e286fc82ddfc28e6d4ac41e7213c37e3db11b6a9 Mon Sep 17 00:00:00 2001 From: Armano Date: Sun, 5 Mar 2023 23:16:24 +0100 Subject: [PATCH 08/11] fix: correct issue from merge conflict --- packages/typescript-estree/src/convert.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 209c5dd19f6..4859f26c0ca 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -447,7 +447,7 @@ export class Converter { */ private deeplyCopy(node: TSNode): any { if (node.kind === ts.SyntaxKind.JSDocFunctionType) { - this.#throwUnlessAllowInvalidAST( + this.#throwError( node, 'JSDoc types can only be used inside documentation comments.', ); @@ -591,10 +591,7 @@ export class Converter { if (node.name.kind === SyntaxKind.PrivateIdentifier) { // This is one of the few times where TS explicitly errors, and doesn't even gracefully handle the syntax. // So we shouldn't ever get into this state to begin with. - this.#throwUnlessAllowInvalidAST( - node.name, - 'Non-private identifier expected.', - ); + this.#throwError(node.name, 'Non-private identifier expected.'); } result = this.createNode(node, { From c9617fcf952718c9af567ff8e22a79d4bf29c4c3 Mon Sep 17 00:00:00 2001 From: fisker Date: Mon, 6 Mar 2023 09:54:36 +0800 Subject: [PATCH 09/11] Fix decorator position --- .../_error_/decorator/snapshots/1-TSESTree-Error.shot | 2 +- .../_error_/decorator/snapshots/1-TSESTree-Error.shot | 2 +- .../_error_/decorator/snapshots/1-TSESTree-Error.shot | 2 +- .../_error_/decorator/snapshots/1-TSESTree-Error.shot | 2 +- .../snapshots/1-TSESTree-Error.shot | 2 +- .../snapshots/1-TSESTree-Error.shot | 5 ++--- .../decorator-on-variable/snapshots/1-TSESTree-Error.shot | 5 ++--- packages/typescript-estree/src/convert.ts | 4 ++-- 8 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot index cf449f1f0e9..7e24abcf6f6 100644 --- a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot @@ -3,6 +3,6 @@ exports[`AST Fixtures declaration TSEnumDeclaration _error_ decorator TSESTree - Error 1`] = ` "TSError > 1 | @decl enum Test {} - | ^^^^^^^^^^^^^^^^^^ Decorators are not valid here. + | ^^^^^ Decorators are not valid here. 2 |" `; diff --git a/packages/ast-spec/src/declaration/TSInterfaceDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/TSInterfaceDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot index a262444d4fd..de2011df9ce 100644 --- a/packages/ast-spec/src/declaration/TSInterfaceDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/declaration/TSInterfaceDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot @@ -3,6 +3,6 @@ exports[`AST Fixtures declaration TSInterfaceDeclaration _error_ decorator TSESTree - Error 1`] = ` "TSError > 1 | @decl interface Test {} - | ^^^^^^^^^^^^^^^^^^^^^^^ Decorators are not valid here. + | ^^^^^ Decorators are not valid here. 2 |" `; diff --git a/packages/ast-spec/src/declaration/TSTypeAliasDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/TSTypeAliasDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot index 3d6af552eb3..1392b9b0742 100644 --- a/packages/ast-spec/src/declaration/TSTypeAliasDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/declaration/TSTypeAliasDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot @@ -3,6 +3,6 @@ exports[`AST Fixtures declaration TSTypeAliasDeclaration _error_ decorator TSESTree - Error 1`] = ` "TSError > 1 | @decl type Test = {}; - | ^^^^^^^^^^^^^^^^^^^^^ Decorators are not valid here. + | ^^^^^ Decorators are not valid here. 2 |" `; diff --git a/packages/ast-spec/src/declaration/VariableDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/VariableDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot index 5ce094304cb..fb0803d0ca0 100644 --- a/packages/ast-spec/src/declaration/VariableDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/declaration/VariableDeclaration/fixtures/_error_/decorator/snapshots/1-TSESTree-Error.shot @@ -3,6 +3,6 @@ exports[`AST Fixtures declaration VariableDeclaration _error_ decorator TSESTree - Error 1`] = ` "TSError > 1 | @decl type Test = {}; - | ^^^^^^^^^^^^^^^^^^^^^ Decorators are not valid here. + | ^^^^^ Decorators are not valid here. 2 |" `; diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/decorator-on-enum-declaration/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/decorator-on-enum-declaration/snapshots/1-TSESTree-Error.shot index 7580d3a58a1..3055b0dc449 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/decorator-on-enum-declaration/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/decorator-on-enum-declaration/snapshots/1-TSESTree-Error.shot @@ -5,5 +5,5 @@ exports[`AST Fixtures legacy-fixtures errorRecovery _error_ decorator-on-enum-de 1 | // TODO: This fixture might be too large, and if so should be split up. 2 | > 3 | @dec enum E {} - | ^^^^^^^^^^^^^^ Decorators are not valid here." + | ^^^^ Decorators are not valid here." `; diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/decorator-on-interface-declaration/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/decorator-on-interface-declaration/snapshots/1-TSESTree-Error.shot index 9ecece74bc4..45ea7f53044 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/decorator-on-interface-declaration/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/decorator-on-interface-declaration/snapshots/1-TSESTree-Error.shot @@ -5,7 +5,6 @@ exports[`AST Fixtures legacy-fixtures errorRecovery _error_ decorator-on-interfa 1 | // TODO: This fixture might be too large, and if so should be split up. 2 | > 3 | @deco() - | ^^^^^^^ -> 4 | interface M {} - | ^^^^^^^^^^^^^^^ Decorators are not valid here." + | ^^^^^^^ Decorators are not valid here. + 4 | interface M {}" `; diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/decorator-on-variable/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/decorator-on-variable/snapshots/1-TSESTree-Error.shot index da20caae726..69123261b06 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/decorator-on-variable/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/decorator-on-variable/snapshots/1-TSESTree-Error.shot @@ -5,8 +5,7 @@ exports[`AST Fixtures legacy-fixtures errorRecovery _error_ decorator-on-variabl 1 | // TODO: This fixture might be too large, and if so should be split up. 2 | > 3 | @deco() - | ^^^^^^^ -> 4 | const a = 1 - | ^^^^^^^^^^^^ Decorators are not valid here. + | ^^^^^^^ Decorators are not valid here. + 4 | const a = 1 5 |" `; diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 4859f26c0ca..824b621141c 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -3033,8 +3033,8 @@ export class Converter { #checkIllegalDecorators(node: ts.Node): void { if (nodeHasIllegalDecorators(node)) { this.#throwUnlessAllowInvalidAST( - // eslint-disable-next-line deprecation/deprecation - node.decorators?.[0] ?? node, + // @ts-expect-error -- this is safe as it's guarded + node.illegalDecorators?.[0] ?? node, 'Decorators are not valid here.', ); } From ac42e34f0abee204dc5f5b45373e28ce645ae955 Mon Sep 17 00:00:00 2001 From: fisker Date: Mon, 6 Mar 2023 10:00:33 +0800 Subject: [PATCH 10/11] Remove unnecessary optional chaining & fallback --- packages/typescript-estree/src/convert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 824b621141c..5f85b64f8b4 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -3034,7 +3034,7 @@ export class Converter { if (nodeHasIllegalDecorators(node)) { this.#throwUnlessAllowInvalidAST( // @ts-expect-error -- this is safe as it's guarded - node.illegalDecorators?.[0] ?? node, + node.illegalDecorators[0], 'Decorators are not valid here.', ); } From fb9e9feb14ae7649c4b2a9346891423178003811 Mon Sep 17 00:00:00 2001 From: fisker Date: Mon, 6 Mar 2023 10:32:50 +0800 Subject: [PATCH 11/11] Fix? --- packages/typescript-estree/src/convert.ts | 1 - packages/typescript-estree/src/node-utils.ts | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 5f85b64f8b4..5d7558dae43 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -3033,7 +3033,6 @@ export class Converter { #checkIllegalDecorators(node: ts.Node): void { if (nodeHasIllegalDecorators(node)) { this.#throwUnlessAllowInvalidAST( - // @ts-expect-error -- this is safe as it's guarded node.illegalDecorators[0], 'Decorators are not valid here.', ); diff --git a/packages/typescript-estree/src/node-utils.ts b/packages/typescript-estree/src/node-utils.ts index 2bce5d484a3..9386a76a2a8 100644 --- a/packages/typescript-estree/src/node-utils.ts +++ b/packages/typescript-estree/src/node-utils.ts @@ -673,7 +673,9 @@ export function createError( return new TSError(message, ast.fileName, { start, end }); } -export function nodeHasIllegalDecorators(node: ts.Node): boolean { +export function nodeHasIllegalDecorators( + node: ts.Node, +): node is ts.Node & { illegalDecorators: ts.Node[] } { return !!( 'illegalDecorators' in node && (node.illegalDecorators as unknown[] | undefined)?.length