Skip to content

Commit 2fd4222

Browse files
authored
feat: add support for Import Attributes and RegExp Modifiers (#639)
* feat: add support for import attributes and RegExp modifiers * test: fix test case * test: add test case to eslint-scope
1 parent 28456b4 commit 2fd4222

File tree

32 files changed

+4896
-5
lines changed

32 files changed

+4896
-5
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/**
2+
* @fileoverview Tests for ES2025 Import Attributes.
3+
* @author Yosuke Ota
4+
*/
5+
6+
import assert from "node:assert";
7+
import * as espree from "espree";
8+
import { KEYS } from "eslint-visitor-keys";
9+
import { analyze } from "../lib/index.js";
10+
11+
describe("Import Attributes", () => {
12+
13+
describe("const type = \"json\"; import pkg from \"./package.json\" with { type: \"json\" };", () => {
14+
let ast;
15+
let scopeManager;
16+
let globalScope;
17+
18+
beforeEach(() => {
19+
ast = espree.parse("const type = \"json\"; import pkg from \"./package.json\" with { type: \"json\" };", { ecmaVersion: 16, sourceType: "module" });
20+
scopeManager = analyze(ast, { ecmaVersion: 16, sourceType: "module", childVisitorKeys: KEYS });
21+
({ globalScope } = scopeManager);
22+
});
23+
24+
it("the global scope should not have any variables", () => {
25+
assert.strictEqual(globalScope.variables.length, 0);
26+
});
27+
28+
it("the global scope should have one child scope, a module scope", () => {
29+
assert.strictEqual(globalScope.childScopes.length, 1);
30+
assert.strictEqual(globalScope.childScopes[0].type, "module");
31+
});
32+
33+
it("the module scope should not have any child scopes", () => {
34+
const moduleScope = globalScope.childScopes[0];
35+
36+
assert.strictEqual(moduleScope.childScopes.length, 0);
37+
});
38+
39+
it("the module scope should have two variables", () => {
40+
const moduleScope = globalScope.childScopes[0];
41+
42+
assert.strictEqual(moduleScope.variables.length, 2);
43+
assert.strictEqual(moduleScope.variables[0].name, "type");
44+
assert.strictEqual(moduleScope.variables[1].name, "pkg");
45+
});
46+
47+
it("the type variable should have one reference, a variable declaration", () => {
48+
const moduleScope = globalScope.childScopes[0];
49+
const typeVariable = moduleScope.variables[0];
50+
51+
assert.strictEqual(typeVariable.references.length, 1);
52+
assert.strictEqual(typeVariable.references[0].identifier, ast.body[0].declarations[0].id);
53+
});
54+
});
55+
56+
describe("const type = \"json\"; export * from \"./package.json\" with { type: \"json\" };", () => {
57+
let ast;
58+
let scopeManager;
59+
let globalScope;
60+
61+
beforeEach(() => {
62+
ast = espree.parse("const type = \"json\"; export * from \"./package.json\" with { type: \"json\" };", { ecmaVersion: 16, sourceType: "module" });
63+
scopeManager = analyze(ast, { ecmaVersion: 16, sourceType: "module", childVisitorKeys: KEYS });
64+
({ globalScope } = scopeManager);
65+
});
66+
67+
it("the global scope should not have any variables", () => {
68+
assert.strictEqual(globalScope.variables.length, 0);
69+
});
70+
71+
it("the global scope should have one child scope, a module scope", () => {
72+
assert.strictEqual(globalScope.childScopes.length, 1);
73+
assert.strictEqual(globalScope.childScopes[0].type, "module");
74+
});
75+
76+
it("the module scope should not have any child scopes", () => {
77+
const moduleScope = globalScope.childScopes[0];
78+
79+
assert.strictEqual(moduleScope.childScopes.length, 0);
80+
});
81+
82+
it("the module scope should have one variable, a type variable", () => {
83+
const moduleScope = globalScope.childScopes[0];
84+
85+
assert.strictEqual(moduleScope.variables.length, 1);
86+
assert.strictEqual(moduleScope.variables[0].name, "type");
87+
});
88+
89+
it("the type variable should have one reference, a variable declaration", () => {
90+
const moduleScope = globalScope.childScopes[0];
91+
const typeVariable = moduleScope.variables[0];
92+
93+
assert.strictEqual(typeVariable.references.length, 1);
94+
assert.strictEqual(typeVariable.references[0].identifier, ast.body[0].declarations[0].id);
95+
});
96+
});
97+
98+
99+
describe("const type = \"json\"; export { default } from \"./package.json\" with { type: \"json\" };", () => {
100+
let ast;
101+
let scopeManager;
102+
let globalScope;
103+
104+
beforeEach(() => {
105+
ast = espree.parse("const type = \"json\"; export { default } from \"./package.json\" with { type: \"json\" };", { ecmaVersion: 16, sourceType: "module" });
106+
scopeManager = analyze(ast, { ecmaVersion: 16, sourceType: "module", childVisitorKeys: KEYS });
107+
({ globalScope } = scopeManager);
108+
});
109+
110+
it("the global scope should not have any variables", () => {
111+
assert.strictEqual(globalScope.variables.length, 0);
112+
});
113+
114+
it("the global scope should have one child scope, a module scope", () => {
115+
assert.strictEqual(globalScope.childScopes.length, 1);
116+
assert.strictEqual(globalScope.childScopes[0].type, "module");
117+
});
118+
119+
it("the module scope should not have any child scopes", () => {
120+
const moduleScope = globalScope.childScopes[0];
121+
122+
assert.strictEqual(moduleScope.childScopes.length, 0);
123+
});
124+
125+
it("the module scope should have one variable, a type variable", () => {
126+
const moduleScope = globalScope.childScopes[0];
127+
128+
assert.strictEqual(moduleScope.variables.length, 1);
129+
assert.strictEqual(moduleScope.variables[0].name, "type");
130+
});
131+
132+
it("the type variable should have one reference, a variable declaration", () => {
133+
const moduleScope = globalScope.childScopes[0];
134+
const typeVariable = moduleScope.variables[0];
135+
136+
assert.strictEqual(typeVariable.references.length, 1);
137+
assert.strictEqual(typeVariable.references[0].identifier, ast.body[0].declarations[0].id);
138+
});
139+
});
140+
141+
142+
describe("const type = \"json\"; import(\"./package.json\", { with: { type } });", () => {
143+
let ast;
144+
let scopeManager;
145+
let globalScope;
146+
147+
beforeEach(() => {
148+
ast = espree.parse("const type = \"json\"; import(\"./package.json\", { with: { type } });", { ecmaVersion: 16, sourceType: "module" });
149+
scopeManager = analyze(ast, { ecmaVersion: 16, sourceType: "module", childVisitorKeys: KEYS });
150+
({ globalScope } = scopeManager);
151+
});
152+
153+
it("the global scope should not have any variables", () => {
154+
assert.strictEqual(globalScope.variables.length, 0);
155+
});
156+
157+
it("the global scope should have one child scope, a module scope", () => {
158+
assert.strictEqual(globalScope.childScopes.length, 1);
159+
assert.strictEqual(globalScope.childScopes[0].type, "module");
160+
});
161+
162+
it("the module scope should not have any child scopes", () => {
163+
const moduleScope = globalScope.childScopes[0];
164+
165+
assert.strictEqual(moduleScope.childScopes.length, 0);
166+
});
167+
168+
it("the module scope should have one variable, a type variable", () => {
169+
const moduleScope = globalScope.childScopes[0];
170+
171+
assert.strictEqual(moduleScope.variables.length, 1);
172+
assert.strictEqual(moduleScope.variables[0].name, "type");
173+
});
174+
175+
176+
it("the type variable should have two references, a variable declaration and import options", () => {
177+
const moduleScope = globalScope.childScopes[0];
178+
const typeVariable = moduleScope.variables[0];
179+
180+
assert.strictEqual(typeVariable.references.length, 2);
181+
assert.strictEqual(typeVariable.references[0].identifier, ast.body[0].declarations[0].id);
182+
assert.strictEqual(typeVariable.references[1].identifier, ast.body[1].expression.options.properties[0].value.properties[0].value);
183+
});
184+
});
185+
});

packages/eslint-visitor-keys/lib/visitor-keys.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,17 @@ const KEYS = {
8787
],
8888
ExportAllDeclaration: [
8989
"exported",
90-
"source"
90+
"source",
91+
"attributes"
9192
],
9293
ExportDefaultDeclaration: [
9394
"declaration"
9495
],
9596
ExportNamedDeclaration: [
9697
"declaration",
9798
"specifiers",
98-
"source"
99+
"source",
100+
"attributes"
99101
],
100102
ExportSpecifier: [
101103
"exported",
@@ -136,15 +138,21 @@ const KEYS = {
136138
"consequent",
137139
"alternate"
138140
],
141+
ImportAttribute: [
142+
"key",
143+
"value"
144+
],
139145
ImportDeclaration: [
140146
"specifiers",
141-
"source"
147+
"source",
148+
"attributes"
142149
],
143150
ImportDefaultSpecifier: [
144151
"local"
145152
],
146153
ImportExpression: [
147-
"source"
154+
"source",
155+
"options"
148156
],
149157
ImportNamespaceSpecifier: [
150158
"local"

packages/espree/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"funding": "https://opencollective.com/eslint",
3333
"license": "BSD-2-Clause",
3434
"dependencies": {
35-
"acorn": "^8.12.0",
35+
"acorn": "^8.14.0",
3636
"acorn-jsx": "^5.3.2",
3737
"eslint-visitor-keys": "^4.1.0"
3838
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
"index": 41,
3+
"lineNumber": 1,
4+
"column": 42,
5+
"message": "Duplicate attribute key 'type'"
6+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
"index": 0,
3+
"lineNumber": 1,
4+
"column": 1,
5+
"message": "'import' and 'export' may appear only with 'sourceType: module'"
6+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import "./foo.json" with { type: "json", type: "html" };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
"index": 19,
3+
"lineNumber": 1,
4+
"column": 20,
5+
"message": "Unexpected token ,"
6+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import("foo.json", , );
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
"index": 41,
3+
"lineNumber": 1,
4+
"column": 42,
5+
"message": "Unexpected token ,"
6+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
"index": 0,
3+
"lineNumber": 1,
4+
"column": 1,
5+
"message": "'import' and 'export' may appear only with 'sourceType: module'"
6+
};

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy