diff --git a/.prettierignore b/.prettierignore index 77ec15cde8cf..c5f057dfff86 100644 --- a/.prettierignore +++ b/.prettierignore @@ -25,8 +25,9 @@ packages/ast-spec/src/special/ExportSpecifier/fixtures/value-export-specifier/fi # TODO - remove this once prettier supports it # https://github.com/prettier/prettier/issues/16072 -packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-import-attributes-assert/fixture.ts packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-import-attributes-with/fixture.ts +# https://github.com/prettier/prettier/issues/17405 +packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/fixture.ts # Ignore CHANGELOG.md files to avoid issues with automated release job CHANGELOG.md diff --git a/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/config.ts b/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/config.ts new file mode 100644 index 000000000000..0b761eb56eb3 --- /dev/null +++ b/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/config.ts @@ -0,0 +1,4 @@ +export default { + expectBabelToNotSupport: + 'waiting for https://github.com/babel/babel/pull/17465 to be released', +} satisfies ASTFixtureConfig; diff --git a/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/fixture.ts b/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/fixture.ts new file mode 100644 index 000000000000..f5ad318ad5f4 --- /dev/null +++ b/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/fixture.ts @@ -0,0 +1 @@ +type TrailingComma = import("A", { with: { "resolution-mode": "import", }, } ); diff --git a/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/snapshots/1-TSESTree-AST.shot new file mode 100644 index 000000000000..f1f332ffa5de --- /dev/null +++ b/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/snapshots/1-TSESTree-AST.shot @@ -0,0 +1,149 @@ +Program { + type: "Program", + body: [ + TSTypeAliasDeclaration { + type: "TSTypeAliasDeclaration", + declare: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "TrailingComma", + optional: false, + + range: [5, 18], + loc: { + start: { column: 5, line: 1 }, + end: { column: 18, line: 1 }, + }, + }, + typeAnnotation: TSImportType { + type: "TSImportType", + argument: TSLiteralType { + type: "TSLiteralType", + literal: Literal { + type: "Literal", + raw: ""A"", + value: "A", + + range: [28, 31], + loc: { + start: { column: 28, line: 1 }, + end: { column: 31, line: 1 }, + }, + }, + + range: [28, 31], + loc: { + start: { column: 28, line: 1 }, + end: { column: 31, line: 1 }, + }, + }, + options: ObjectExpression { + type: "ObjectExpression", + properties: [ + Property { + type: "Property", + computed: false, + key: Identifier { + type: "Identifier", + decorators: [], + name: "with", + optional: false, + + range: [35, 39], + loc: { + start: { column: 35, line: 1 }, + end: { column: 39, line: 1 }, + }, + }, + kind: "init", + method: false, + optional: false, + shorthand: false, + value: ObjectExpression { + type: "ObjectExpression", + properties: [ + Property { + type: "Property", + computed: false, + key: Literal { + type: "Literal", + raw: ""resolution-mode"", + value: "resolution-mode", + + range: [43, 60], + loc: { + start: { column: 43, line: 1 }, + end: { column: 60, line: 1 }, + }, + }, + kind: "init", + method: false, + optional: false, + shorthand: false, + value: Literal { + type: "Literal", + raw: ""import"", + value: "import", + + range: [62, 70], + loc: { + start: { column: 62, line: 1 }, + end: { column: 70, line: 1 }, + }, + }, + + range: [43, 70], + loc: { + start: { column: 43, line: 1 }, + end: { column: 70, line: 1 }, + }, + }, + ], + + range: [41, 73], + loc: { + start: { column: 41, line: 1 }, + end: { column: 73, line: 1 }, + }, + }, + + range: [35, 73], + loc: { + start: { column: 35, line: 1 }, + end: { column: 73, line: 1 }, + }, + }, + ], + + range: [33, 76], + loc: { + start: { column: 33, line: 1 }, + end: { column: 76, line: 1 }, + }, + }, + qualifier: null, + typeArguments: null, + + range: [21, 78], + loc: { + start: { column: 21, line: 1 }, + end: { column: 78, line: 1 }, + }, + }, + + range: [0, 79], + loc: { + start: { column: 0, line: 1 }, + end: { column: 79, line: 1 }, + }, + }, + ], + sourceType: "script", + + range: [0, 80], + loc: { + start: { column: 0, line: 1 }, + end: { column: 0, line: 2 }, + }, +} \ No newline at end of file diff --git a/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/snapshots/2-TSESTree-Tokens.shot b/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/snapshots/2-TSESTree-Tokens.shot new file mode 100644 index 000000000000..cae5e0bcfd9e --- /dev/null +++ b/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/snapshots/2-TSESTree-Tokens.shot @@ -0,0 +1,202 @@ +[ + Identifier { + type: "Identifier", + value: "type", + + range: [0, 4], + loc: { + start: { column: 0, line: 1 }, + end: { column: 4, line: 1 }, + }, + }, + Identifier { + type: "Identifier", + value: "TrailingComma", + + range: [5, 18], + loc: { + start: { column: 5, line: 1 }, + end: { column: 18, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "=", + + range: [19, 20], + loc: { + start: { column: 19, line: 1 }, + end: { column: 20, line: 1 }, + }, + }, + Keyword { + type: "Keyword", + value: "import", + + range: [21, 27], + loc: { + start: { column: 21, line: 1 }, + end: { column: 27, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "(", + + range: [27, 28], + loc: { + start: { column: 27, line: 1 }, + end: { column: 28, line: 1 }, + }, + }, + String { + type: "String", + value: ""A"", + + range: [28, 31], + loc: { + start: { column: 28, line: 1 }, + end: { column: 31, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ",", + + range: [31, 32], + loc: { + start: { column: 31, line: 1 }, + end: { column: 32, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "{", + + range: [33, 34], + loc: { + start: { column: 33, line: 1 }, + end: { column: 34, line: 1 }, + }, + }, + Keyword { + type: "Keyword", + value: "with", + + range: [35, 39], + loc: { + start: { column: 35, line: 1 }, + end: { column: 39, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ":", + + range: [39, 40], + loc: { + start: { column: 39, line: 1 }, + end: { column: 40, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "{", + + range: [41, 42], + loc: { + start: { column: 41, line: 1 }, + end: { column: 42, line: 1 }, + }, + }, + String { + type: "String", + value: ""resolution-mode"", + + range: [43, 60], + loc: { + start: { column: 43, line: 1 }, + end: { column: 60, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ":", + + range: [60, 61], + loc: { + start: { column: 60, line: 1 }, + end: { column: 61, line: 1 }, + }, + }, + String { + type: "String", + value: ""import"", + + range: [62, 70], + loc: { + start: { column: 62, line: 1 }, + end: { column: 70, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ",", + + range: [70, 71], + loc: { + start: { column: 70, line: 1 }, + end: { column: 71, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "}", + + range: [72, 73], + loc: { + start: { column: 72, line: 1 }, + end: { column: 73, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ",", + + range: [73, 74], + loc: { + start: { column: 73, line: 1 }, + end: { column: 74, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: "}", + + range: [75, 76], + loc: { + start: { column: 75, line: 1 }, + end: { column: 76, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ")", + + range: [77, 78], + loc: { + start: { column: 77, line: 1 }, + end: { column: 78, line: 1 }, + }, + }, + Punctuator { + type: "Punctuator", + value: ";", + + range: [78, 79], + loc: { + start: { column: 78, line: 1 }, + end: { column: 79, line: 1 }, + }, + }, +] \ No newline at end of file diff --git a/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/snapshots/3-Babel-Error.shot b/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/snapshots/3-Babel-Error.shot new file mode 100644 index 000000000000..8f216bc26e47 --- /dev/null +++ b/packages/ast-spec/src/type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/snapshots/3-Babel-Error.shot @@ -0,0 +1,4 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`AST Fixtures > type > TSImportType > type-import-type-with-trailing-comma-in-import-attributes > Babel - Error`] +SyntaxError: Unexpected token, expected "}" (1:73) diff --git a/packages/ast-spec/tests/fixtures-with-differences-errors.shot b/packages/ast-spec/tests/fixtures-with-differences-errors.shot index 7051aa777fbb..006a68578577 100644 --- a/packages/ast-spec/tests/fixtures-with-differences-errors.shot +++ b/packages/ast-spec/tests/fixtures-with-differences-errors.shot @@ -47,7 +47,8 @@ exports[`AST Fixtures > List fixtures with Error differences`] "legacy-fixtures/errorRecovery/fixtures/_error_/interface-with-optional-index-signature/fixture.ts", "legacy-fixtures/parameter-decorators/fixtures/_error_/parameter-array-pattern-decorator/fixture.ts", "legacy-fixtures/parameter-decorators/fixtures/_error_/parameter-rest-element-decorator/fixture.ts", - "type/TSImportType/fixtures/_error_/type-import-type-with-import-attributes-assert/fixture.ts" + "type/TSImportType/fixtures/_error_/type-import-type-with-import-attributes-assert/fixture.ts", + "type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/fixture.ts" ], "TSESTree errored but Babel didn't": [ "declaration/TSDeclareFunction/fixtures/_error_/generator-ambient/fixture.ts", diff --git a/packages/ast-spec/tests/fixtures-without-babel-support.shot b/packages/ast-spec/tests/fixtures-without-babel-support.shot index 0a63977a3f5e..a13f680d01a0 100644 --- a/packages/ast-spec/tests/fixtures-without-babel-support.shot +++ b/packages/ast-spec/tests/fixtures-without-babel-support.shot @@ -1,4 +1,9 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`AST Fixtures > List fixtures we expect babel to not support`] -[] +[ + [ + "type/TSImportType/fixtures/type-import-type-with-trailing-comma-in-import-attributes/fixture.ts", + "waiting for https://github.com/babel/babel/pull/17465 to be released" + ] +] diff --git a/packages/ast-spec/tests/fixtures.test.ts b/packages/ast-spec/tests/fixtures.test.ts index 16534e6f0c1e..b6400540425c 100644 --- a/packages/ast-spec/tests/fixtures.test.ts +++ b/packages/ast-spec/tests/fixtures.test.ts @@ -4,7 +4,7 @@ import * as path from 'node:path'; import { pathToFileURL } from 'node:url'; import { VitestSnapshotEnvironment } from 'vitest/snapshot'; -import type { ASTFixtureConfig, Fixture } from './util/parsers/parser-types.js'; +import type { Fixture } from './util/parsers/parser-types.js'; import { getErrorLabel } from './util/getErrorLabel.js'; import { parseBabel } from './util/parsers/babel.js'; diff --git a/packages/ast-spec/tests/util/parsers/parser-types.ts b/packages/ast-spec/tests/util/parsers/parser-types.ts index e1d382876654..e95a648c6f24 100644 --- a/packages/ast-spec/tests/util/parsers/parser-types.ts +++ b/packages/ast-spec/tests/util/parsers/parser-types.ts @@ -5,27 +5,6 @@ interface SuccessSnapshotPaths { readonly tokens: SnapshotPathFn; } -/** - * We define this as a global type to make it easier to consume from fixtures. - * It saves us having to import the type into `src` files from a test utils folder. - * This is a convenient property because it saves us from a lot of `../`! - */ -export interface ASTFixtureConfig { - /** - * Prevents the parser from throwing an error if it receives an invalid AST from TypeScript. - * This case only usually occurs when attempting to lint invalid code. - */ - readonly allowInvalidAST?: boolean; - - /** - * Specifies that we expect that babel doesn't yet support the code in this fixture, so we expect that it will error. - * This should not be used if we expect babel to throw for this feature due to a valid parser error! - * - * The value should be a description of why there isn't support - for example a github issue URL. - */ - readonly expectBabelToNotSupport?: string; -} - export interface Fixture { readonly absolute: string; readonly babelParsed: ParserResponse; diff --git a/packages/ast-spec/tsconfig.spec.json b/packages/ast-spec/tsconfig.spec.json index fa52d4c4e988..0f776aea58e3 100644 --- a/packages/ast-spec/tsconfig.spec.json +++ b/packages/ast-spec/tsconfig.spec.json @@ -3,6 +3,14 @@ "compilerOptions": { "outDir": "../../dist/packages/ast-spec" }, + "include": [ + "tests", + "typings", + "**/fixtures/**/config.ts", + "vitest.config.mts", + "package.json" + ], + "exclude": ["**/fixtures/**/fixture.ts"], "references": [ { "path": "../typescript-estree/tsconfig.build.json" diff --git a/packages/ast-spec/typings/global.d.ts b/packages/ast-spec/typings/global.d.ts new file mode 100644 index 000000000000..7dd0713ea5d4 --- /dev/null +++ b/packages/ast-spec/typings/global.d.ts @@ -0,0 +1,20 @@ +/** + * We define this as a global type to make it easier to consume from fixtures. + * It saves us having to import the type into `src` files from a test utils folder. + * This is a convenient property because it saves us from a lot of `../`! + */ +interface ASTFixtureConfig { + /** + * Prevents the parser from throwing an error if it receives an invalid AST from TypeScript. + * This case only usually occurs when attempting to lint invalid code. + */ + readonly allowInvalidAST?: boolean; + + /** + * Specifies that we expect that babel doesn't yet support the code in this fixture, so we expect that it will error. + * This should not be used if we expect babel to throw for this feature due to a valid parser error! + * + * The value should be a description of why there isn't support - for example a github issue URL. + */ + readonly expectBabelToNotSupport?: string; +} diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index e4564c92aa72..dccb674537c6 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -3166,11 +3166,16 @@ export class Converter { const commaToken = findNextToken(node.argument, node, this.ast)!; const openBraceToken = findNextToken(commaToken, node, this.ast)!; - const closeBraceToken = findNextToken( + const tokenAfterAttributes = findNextToken( node.attributes, node, this.ast, )!; + // Since TS 5.9, there could be a trailing comma, i.e. `{ with: { ... }, }` + const closeBraceToken = + tokenAfterAttributes.kind === ts.SyntaxKind.CommaToken + ? findNextToken(tokenAfterAttributes, node, this.ast)! + : tokenAfterAttributes; const withOrAssertToken = findNextToken( openBraceToken, node,