Skip to content

Commit

Permalink
feat(typescript-estree): support Import Assertions (#4074)
Browse files Browse the repository at this point in the history
* test(eslint-plugin): update tests

* feat(ast-spec): support import assertions

* feat(typescript-estree): support import assertions

* test(typescript-estree): update tests for import assertions

* test(typescript-estree): fix ast-alignment tests

* test(typescript-estree): update snapshots

* test(typescript-estree): add fixtures for import assertions

* chore(typescript-estree): remove todo comments

* fix(typescript-estree): fix condition for dynamic import error

* test(typescript-estree): update tests

* feat(visitor-keys): add ImportAttribute to visitor-keys

* test(eslint-plugin): fix test

* fix(visitor-keys): fix additionalKeys for ImportAttribute

* feat(scope-manager): add empty method for ImportAttribute
  • Loading branch information
sosukesuzuki committed Nov 11, 2021
1 parent 981490d commit ae0fb5a
Show file tree
Hide file tree
Showing 103 changed files with 1,741 additions and 5 deletions.
1 change: 1 addition & 0 deletions packages/ast-spec/src/ast-node-types.ts
Expand Up @@ -32,6 +32,7 @@ export enum AST_NODE_TYPES {
FunctionExpression = 'FunctionExpression',
Identifier = 'Identifier',
IfStatement = 'IfStatement',
ImportAttribute = 'ImportAttribute',
ImportDeclaration = 'ImportDeclaration',
ImportDefaultSpecifier = 'ImportDefaultSpecifier',
ImportExpression = 'ImportExpression',
Expand Down
Expand Up @@ -2,11 +2,13 @@ import type { AST_NODE_TYPES } from '../../ast-node-types';
import type { BaseNode } from '../../base/BaseNode';
import type { Identifier } from '../../expression/Identifier/spec';
import type { StringLiteral } from '../../expression/literal/StringLiteral/spec';
import type { ImportAttribute } from '../../special/ImportAttribute/spec';
import type { ExportKind } from '../ExportAndImportKind';

export interface ExportAllDeclaration extends BaseNode {
type: AST_NODE_TYPES.ExportAllDeclaration;
source: StringLiteral | null;
exportKind: ExportKind;
exported: Identifier | null;
assertions: ImportAttribute[];
}
Expand Up @@ -2,6 +2,7 @@ import type { AST_NODE_TYPES } from '../../ast-node-types';
import type { BaseNode } from '../../base/BaseNode';
import type { StringLiteral } from '../../expression/literal/StringLiteral/spec';
import type { ExportSpecifier } from '../../special/ExportSpecifier/spec';
import type { ImportAttribute } from '../../special/ImportAttribute/spec';
import type { ExportDeclaration } from '../../unions/ExportDeclaration';
import type { ExportKind } from '../ExportAndImportKind';

Expand All @@ -11,4 +12,5 @@ export interface ExportNamedDeclaration extends BaseNode {
specifiers: ExportSpecifier[];
source: StringLiteral | null;
exportKind: ExportKind;
assertions: ImportAttribute[];
}
2 changes: 2 additions & 0 deletions packages/ast-spec/src/declaration/ImportDeclaration/spec.ts
@@ -1,6 +1,7 @@
import type { AST_NODE_TYPES } from '../../ast-node-types';
import type { BaseNode } from '../../base/BaseNode';
import type { StringLiteral } from '../../expression/literal/StringLiteral/spec';
import type { ImportAttribute } from '../../special/ImportAttribute/spec';
import type { ImportClause } from '../../unions/ImportClause';
import type { ImportKind } from '../ExportAndImportKind';

Expand All @@ -9,4 +10,5 @@ export interface ImportDeclaration extends BaseNode {
source: StringLiteral;
specifiers: ImportClause[];
importKind: ImportKind;
assertions: ImportAttribute[];
}
1 change: 1 addition & 0 deletions packages/ast-spec/src/expression/ImportExpression/spec.ts
Expand Up @@ -5,4 +5,5 @@ import type { Expression } from '../../unions/Expression';
export interface ImportExpression extends BaseNode {
type: AST_NODE_TYPES.ImportExpression;
source: Expression;
attributes: Expression | null;
}
10 changes: 10 additions & 0 deletions packages/ast-spec/src/special/ImportAttribute/spec.ts
@@ -0,0 +1,10 @@
import type { AST_NODE_TYPES } from '../../ast-node-types';
import type { BaseNode } from '../../base/BaseNode';
import type { Identifier } from '../../expression/Identifier/spec';
import type { Literal } from '../../unions/Literal';

export interface ImportAttribute extends BaseNode {
type: AST_NODE_TYPES.ImportAttribute;
key: Identifier | Literal;
value: Literal;
}
1 change: 1 addition & 0 deletions packages/ast-spec/src/special/spec.ts
Expand Up @@ -3,6 +3,7 @@ export * from './ClassBody/spec';
export * from './Decorator/spec';
export * from './EmptyStatement/spec';
export * from './ExportSpecifier/spec';
export * from './ImportAttribute/spec';
export * from './ImportDefaultSpecifier/spec';
export * from './ImportNamespaceSpecifier/spec';
export * from './ImportSpecifier/spec';
Expand Down
2 changes: 2 additions & 0 deletions packages/ast-spec/src/unions/Node.ts
Expand Up @@ -79,6 +79,7 @@ import type { ClassBody } from '../special/ClassBody/spec';
import type { Decorator } from '../special/Decorator/spec';
import type { EmptyStatement } from '../special/EmptyStatement/spec';
import type { ExportSpecifier } from '../special/ExportSpecifier/spec';
import type { ImportAttribute } from '../special/ImportAttribute/spec';
import type { ImportDefaultSpecifier } from '../special/ImportDefaultSpecifier/spec';
import type { ImportNamespaceSpecifier } from '../special/ImportNamespaceSpecifier/spec';
import type { ImportSpecifier } from '../special/ImportSpecifier/spec';
Expand Down Expand Up @@ -200,6 +201,7 @@ export type Node =
| FunctionExpression
| Identifier
| IfStatement
| ImportAttribute
| ImportDeclaration
| ImportDefaultSpecifier
| ImportExpression
Expand Down
4 changes: 4 additions & 0 deletions packages/scope-manager/src/referencer/Referencer.ts
Expand Up @@ -778,6 +778,10 @@ class Referencer extends Visitor {

this.close(node);
}

protected ImportAttribute(): void {
// import assertions are module metadata and thus have no variables to reference
}
}

export { Referencer, ReferencerOptions };
@@ -1 +1 @@
import('foo', '')
import('foo', '', '')
@@ -0,0 +1 @@
import("foo", { assert: { type: "json" } });
@@ -0,0 +1 @@
export * from "mod" assert { type: "json" };
@@ -0,0 +1 @@
export { foo } from "mod" assert { type: "json" };
@@ -0,0 +1 @@
import foo from "mod" assert { type: "json" };
27 changes: 25 additions & 2 deletions packages/typescript-estree/src/convert.ts
Expand Up @@ -193,6 +193,7 @@ export class Converter {
source: null,
exportKind: isType || isDeclare ? 'type' : 'value',
range: [exportKeyword.getStart(this.ast), result.range[1]],
assertions: [],
});
}
}
Expand Down Expand Up @@ -663,6 +664,14 @@ export class Converter {
return result;
}

private convertAssertClasue(
node: ts.AssertClause | undefined,
): TSESTree.ImportAttribute[] {
return node === undefined
? []
: node.elements.map(element => this.convertChild(element));
}

/**
* Applies the given TS modifiers to the given result object.
* @param result
Expand Down Expand Up @@ -1737,6 +1746,7 @@ export class Converter {
source: this.convertChild(node.moduleSpecifier),
specifiers: [],
importKind: 'value',
assertions: this.convertAssertClasue(node.assertClause),
});

if (node.importClause) {
Expand Down Expand Up @@ -1805,6 +1815,7 @@ export class Converter {
),
exportKind: node.isTypeOnly ? 'type' : 'value',
declaration: null,
assertions: this.convertAssertClasue(node.assertClause),
});
} else {
return this.createNode<TSESTree.ExportAllDeclaration>(node, {
Expand All @@ -1820,6 +1831,7 @@ export class Converter {
node.exportClause.kind === SyntaxKind.NamespaceExport
? this.convertChild(node.exportClause.name)
: null,
assertions: this.convertAssertClasue(node.assertClause),
});
}
}
Expand Down Expand Up @@ -1989,16 +2001,19 @@ export class Converter {

case SyntaxKind.CallExpression: {
if (node.expression.kind === SyntaxKind.ImportKeyword) {
if (node.arguments.length !== 1) {
if (node.arguments.length !== 1 && node.arguments.length !== 2) {
throw createError(
this.ast,
node.arguments.pos,
'Dynamic import must have one specifier as an argument.',
'Dynamic import requires exactly one or two arguments.',
);
}
return this.createNode<TSESTree.ImportExpression>(node, {
type: AST_NODE_TYPES.ImportExpression,
source: this.convertChild(node.arguments[0]),
attributes: node.arguments[1]
? this.convertChild(node.arguments[1])
: null,
});
}

Expand Down Expand Up @@ -2871,6 +2886,14 @@ export class Converter {
});
}

case SyntaxKind.AssertEntry: {
return this.createNode<TSESTree.ImportAttribute>(node, {
type: AST_NODE_TYPES.ImportAttribute,
key: this.convertChild(node.name),
value: this.convertChild(node.value),
});
}

default:
return this.deeplyCopy(node);
}
Expand Down
Expand Up @@ -74,6 +74,7 @@ export interface EstreeToTsNodeTypes {
| ts.Token<ts.SyntaxKind.NewKeyword | ts.SyntaxKind.ImportKeyword>;
[AST_NODE_TYPES.PrivateIdentifier]: ts.PrivateIdentifier;
[AST_NODE_TYPES.IfStatement]: ts.IfStatement;
[AST_NODE_TYPES.ImportAttribute]: ts.AssertEntry;
[AST_NODE_TYPES.ImportDeclaration]: ts.ImportDeclaration;
[AST_NODE_TYPES.ImportDefaultSpecifier]: ts.ImportClause;
[AST_NODE_TYPES.ImportExpression]: ts.CallExpression;
Expand Down
2 changes: 2 additions & 0 deletions packages/typescript-estree/src/ts-estree/ts-nodes.ts
Expand Up @@ -14,6 +14,8 @@ declare module 'typescript' {
export type TSToken = ts.Token<ts.SyntaxKind>;

export type TSNode =
| ts.AssertClause
| ts.AssertEntry
| ts.Modifier
| ts.Identifier
| ts.PrivateIdentifier
Expand Down
1 change: 1 addition & 0 deletions packages/typescript-estree/tests/ast-alignment/parse.ts
Expand Up @@ -33,6 +33,7 @@ function parseWithBabelParser(text: string, jsx = true): File {
],
'decorators-legacy',
'classStaticBlock',
'importAssertions',
'typescript',
];
if (jsx) {
Expand Down
18 changes: 18 additions & 0 deletions packages/typescript-estree/tests/ast-alignment/utils.ts
Expand Up @@ -275,6 +275,24 @@ export function preprocessBabylonAST(ast: File): any {
ExportSpecifier(node) {
delete node.exportKind;
},
/*
* Babel's AST has no `assertions` property if there are no assertions.
*/
ImportDeclaration(node) {
if (!node.assertions) {
node.assertions = [];
}
},
ExportNamedDeclaration(node) {
if (!node.assertions) {
node.assertions = [];
}
},
ExportAllDeclaration(node) {
if (!node.assertions) {
node.assertions = [];
}
},
},
);
}
Expand Down
Expand Up @@ -598,7 +598,7 @@ TSError {
"column": 7,
"index": 7,
"lineNumber": 1,
"message": "Dynamic import must have one specifier as an argument.",
"message": "Dynamic import requires exactly one or two arguments.",
}
`;

Expand Down Expand Up @@ -1888,6 +1888,10 @@ exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" e

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/directive-in-namespace.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/dynamic-import-with-import-assertions.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/export-all-with-import-assertions.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/export-as-namespace.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/export-assignment.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;
Expand Down Expand Up @@ -1940,6 +1944,8 @@ exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" e

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/export-type-star-from.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/export-with-import-assertions.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/function-anonymus-with-type-parameters.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/function-anynomus-with-return-type.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;
Expand Down Expand Up @@ -1984,6 +1990,8 @@ exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" e

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/import-type-star-as-ns.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/import-with-import-assertions.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/interface-extends.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/interface-extends-multiple.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;
Expand Down
Expand Up @@ -304,6 +304,7 @@ exports[`semanticInfo fixtures/import-file.src 1`] = `
Object {
"body": Array [
Object {
"assertions": Array [],
"importKind": "value",
"loc": Object {
"end": Object {
Expand Down
Expand Up @@ -38,6 +38,7 @@ Object {
},
},
"object": Object {
"attributes": null,
"loc": Object {
"end": Object {
"column": 13,
Expand Down
Expand Up @@ -5,6 +5,6 @@ TSError {
"column": 7,
"index": 7,
"lineNumber": 1,
"message": "Dynamic import must have one specifier as an argument.",
"message": "Dynamic import requires exactly one or two arguments.",
}
`;
Expand Up @@ -4,6 +4,7 @@ exports[`javascript modules error-delete.src 1`] = `
Object {
"body": Array [
Object {
"assertions": Array [],
"importKind": "value",
"loc": Object {
"end": Object {
Expand Down
Expand Up @@ -4,6 +4,7 @@ exports[`javascript modules error-strict.src 1`] = `
Object {
"body": Array [
Object {
"assertions": Array [],
"importKind": "value",
"loc": Object {
"end": Object {
Expand Down
Expand Up @@ -4,6 +4,7 @@ exports[`javascript modules export-async-named-function.src 1`] = `
Object {
"body": Array [
Object {
"assertions": Array [],
"declaration": Object {
"async": true,
"body": Object {
Expand Down
Expand Up @@ -4,6 +4,7 @@ exports[`javascript modules export-const.src 1`] = `
Object {
"body": Array [
Object {
"assertions": Array [],
"declaration": Object {
"declarations": Array [
Object {
Expand Down
Expand Up @@ -4,6 +4,7 @@ exports[`javascript modules export-from-batch.src 1`] = `
Object {
"body": Array [
Object {
"assertions": Array [],
"exportKind": "value",
"exported": null,
"loc": Object {
Expand Down
Expand Up @@ -4,6 +4,7 @@ exports[`javascript modules export-from-default.src 1`] = `
Object {
"body": Array [
Object {
"assertions": Array [],
"declaration": null,
"exportKind": "value",
"loc": Object {
Expand Down
Expand Up @@ -4,6 +4,7 @@ exports[`javascript modules export-from-named-as-default.src 1`] = `
Object {
"body": Array [
Object {
"assertions": Array [],
"declaration": null,
"exportKind": "value",
"loc": Object {
Expand Down
Expand Up @@ -4,6 +4,7 @@ exports[`javascript modules export-from-named-as-specifier.src 1`] = `
Object {
"body": Array [
Object {
"assertions": Array [],
"declaration": null,
"exportKind": "value",
"loc": Object {
Expand Down
Expand Up @@ -4,6 +4,7 @@ exports[`javascript modules export-from-named-as-specifiers.src 1`] = `
Object {
"body": Array [
Object {
"assertions": Array [],
"declaration": null,
"exportKind": "value",
"loc": Object {
Expand Down
Expand Up @@ -4,6 +4,7 @@ exports[`javascript modules export-from-specifier.src 1`] = `
Object {
"body": Array [
Object {
"assertions": Array [],
"declaration": null,
"exportKind": "value",
"loc": Object {
Expand Down
Expand Up @@ -4,6 +4,7 @@ exports[`javascript modules export-from-specifiers.src 1`] = `
Object {
"body": Array [
Object {
"assertions": Array [],
"declaration": null,
"exportKind": "value",
"loc": Object {
Expand Down

0 comments on commit ae0fb5a

Please sign in to comment.