Skip to content

Commit c1614c8

Browse files
authored
Fill optional AST properties when both estree and typescript parser plugin are enabled (Part 2) (#17226)
* make cloneIdentifier a parser method Allows the estree plugin to provide optional properties to the cloned identifier * move castNodeTo to node parser * apply unpad on input code * apply tokens hotfix on ts-eslint AST * fix spread element support * Represent `yield` using a `Keyword` token * fix directive typing error * fill optional properties for AccessorProperty * use castNodeTo for all AST type manipulations * represent let as a Keyword token * remove invalid test case * test cleanup * address review comment * add more test cases
1 parent 7979e3f commit c1614c8

16 files changed

Lines changed: 280 additions & 118 deletions

File tree

eslint/babel-eslint-parser/src/convert/convertTokens.cts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,12 @@ function convertToken(
107107
newToken.range = [token.start, token.end];
108108

109109
if (label === tl.name) {
110-
if (token.value === "static") {
110+
const tokenValue = token.value;
111+
if (
112+
tokenValue === "let" ||
113+
tokenValue === "static" ||
114+
tokenValue === "yield"
115+
) {
111116
newToken.type = "Keyword";
112117
} else {
113118
newToken.type = "Identifier";

eslint/babel-eslint-parser/test/index.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@ describe("Babel and Espree", () => {
281281
parseAndAssertSame("a = 1");
282282
});
283283

284+
it("let declaration", () => {
285+
parseAndAssertSame("let a = 1");
286+
});
287+
284288
it("logical NOT", () => {
285289
parseAndAssertSame("!0");
286290
});
@@ -293,10 +297,22 @@ describe("Babel and Espree", () => {
293297
parseAndAssertSame("class Foo {}");
294298
});
295299

300+
it("static class method", () => {
301+
parseAndAssertSame("class Foo { static m() {} }");
302+
});
303+
296304
it("class expression", () => {
297305
parseAndAssertSame("var a = class Foo {}");
298306
});
299307

308+
it("yield expression", () => {
309+
parseAndAssertSame("function *g() { yield* g }");
310+
});
311+
312+
it("await expression", () => {
313+
parseAndAssertSame("async function a() { await a() }");
314+
});
315+
300316
it("jsx expression", () => {
301317
parseAndAssertSame("<App />");
302318
});

eslint/babel-eslint-parser/test/typescript-estree.test.js

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import path from "node:path";
22
import { parseForESLint } from "../lib/index.cjs";
3+
import unpad from "dedent";
34
import { ESLint } from "eslint";
45
import { itDummy, commonJS, IS_BABEL_8 } from "$repo-utils";
56

@@ -20,6 +21,36 @@ const PROPS_TO_REMOVE = [
2021
{ key: "attributes", type: "ImportExpression" },
2122
];
2223

24+
// TODO: remove the ESLint token fixes after they are fixed in upstream
25+
function fixTSESLintTokens(ast) {
26+
const { tokens } = ast;
27+
for (let i = 0; i < tokens.length; i++) {
28+
const token = tokens[i];
29+
const { type, value } = token;
30+
switch (type) {
31+
case "Identifier":
32+
{
33+
if (value.match(/^\d.*n$/)) {
34+
token.type = "Numeric";
35+
} else if (value.match(/^#/)) {
36+
token.type = "PrivateIdentifier";
37+
token.value = value.slice(1);
38+
}
39+
}
40+
break;
41+
case "Keyword":
42+
{
43+
if (value === "null") {
44+
token.type = "Null";
45+
}
46+
}
47+
break;
48+
default:
49+
break;
50+
}
51+
}
52+
}
53+
2354
function deeplyRemoveProperties(obj, props) {
2455
for (const [k, v] of Object.entries(obj)) {
2556
if (
@@ -72,6 +103,7 @@ function deeplyRemoveProperties(obj, props) {
72103
};
73104

74105
function parseAndAssertSame(code, babelEcmaFeatures = null) {
106+
code = unpad(code);
75107
const tsEstreeAST = tsEstree.parse(code, tsEstreeOptions);
76108
const babelAST = parseForESLint(code, {
77109
eslintVisitorKeys: true,
@@ -81,12 +113,19 @@ function deeplyRemoveProperties(obj, props) {
81113
babelOptions: {
82114
configFile: false,
83115
parserOpts: {
84-
plugins: ["jsx", "typescript"],
116+
plugins: [
117+
"decorators",
118+
"decoratorAutoAccessors",
119+
"explicitResourceManagement",
120+
"jsx",
121+
"typescript",
122+
],
85123
},
86124
},
87125
}).ast;
88126

89127
deeplyRemoveProperties(babelAST, PROPS_TO_REMOVE);
128+
fixTSESLintTokens(tsEstreeAST);
90129
expect(babelAST).toEqual(tsEstreeAST);
91130
}
92131

@@ -108,16 +147,36 @@ function deeplyRemoveProperties(obj, props) {
108147
["boolean", "true"],
109148
["boolean", "false"],
110149
["numeric", "0"],
111-
// ["bigint", "0n"],
150+
["bigint", "0n"],
112151
["string", `"string"`],
113152
["regexp without flag", `/foo/;`],
114153
["regexp u flag", `/foo/dgimsuy;`],
115154
["regexp v flag", `/foo/dgimsvy;`],
116155
["identifier", "i"],
117156

157+
["null", "null"],
158+
159+
[
160+
"template with braces",
161+
`
162+
{ \`\${foo} \${bar}\` }
163+
`,
164+
],
165+
166+
["directive", `"use strict";`],
167+
168+
// https://github.com/typescript-eslint/typescript-eslint/issues/11026
169+
// ["empty program with line comment", "// single comment"],
170+
// ["empty program with block comment", " /* multiline\n * comment\n*/"],
171+
118172
["logical NOT", `!0`],
119173
["bitwise NOT", `~0`],
120174

175+
["nullish coalescing", "a ?? b"],
176+
177+
["yield expression", "function *f() { yield; yield* f; yield f }"],
178+
["await expression", "async function f() { await f() }"],
179+
121180
["function declaration", "function f(p) {}"],
122181
["function expression", "0, function f(p) {}"],
123182
["arrow function expression", "() => {}"],
@@ -131,49 +190,64 @@ function deeplyRemoveProperties(obj, props) {
131190
["class method", "class C { m() {} }"],
132191
["class property", "class C { p; }"],
133192
["class static block", "class C { static {}; }"],
193+
["class accessor", "class C { accessor a = 0 }"],
194+
["class decorators", "@dec class C { @dec static p; @dec accessor a; }"],
134195

135196
["variable declaration", "var a = 0"],
136197
[
137198
"variable declaration destructuring",
138199
"var [{ a: x = 0 }, ...b] = [{ a: 0 }]",
139200
],
140-
// pending cloneIdentifier support
141-
// ["variable declaration destructuring shorthand", "let { a = 0 } = { a: 0 }"]
201+
[
202+
"variable declaration destructuring shorthand",
203+
"var { a = 0 } = { a: 0 }",
204+
],
205+
["let declaration", "let a = 0"],
206+
["const declaration", "const a = 0"],
207+
["using declaration", "{ using a = 0 }"],
208+
["await using declaration", "async () => { await using a = 0 }"],
142209

143210
["assignment expression", "x = 1"],
144-
// pending toAssignable support
145-
// ["assignment expression destructuring", "[{ a: x = 0 }, ...b] = [{ a: 0 }]"],
211+
[
212+
"assignment expression destructuring",
213+
"[{ a: x = 0 }, ...b] = [{ a: 0 }]",
214+
],
215+
216+
["spread element", "var a = { b, ...[...c] }"],
146217

147218
["async call expression", "async ([ x ])"],
148219

149220
["import declaration", `import "foo"`],
150221
["import declaration default", `import foo from "foo"`],
151-
// pending cloneIdentifier support
152-
// ["import declaration named", `import { foo } from "foo"`]
222+
["import declaration named", `import { foo } from "foo"`],
153223
["import declaration named as", `import { foo as bar } from "foo"`],
154224
[
155225
"import declaration with attributes",
156226
`import foo from "./foo.json" with { type: "json" }`,
157227
],
158228

159-
// pending cloneIdentifier support
160-
// ["export declaration", `const foo = 0;export { foo }`],
229+
["export declaration", `const foo = 0;export { foo }`],
161230
["export declaration as", `const foo = 0;export { foo as bar }`],
231+
["export const declaration", `export const foo = 0`],
232+
["export declaration from", `export { foo } from "foo"`],
162233
["export function declaration", `export function foo() {}`],
163234
["export class declaration", `export class foo {}`],
164235

165236
["member expression", `foo.bar`],
237+
["optional member expression", `foo?.bar`],
166238
["call expression", `foo(bar)`],
239+
["optional call expression", `foo?.(bar)`],
167240
["new expression", `new foo`],
241+
242+
["logical assignment", "x ??= y ||= z &&= x"],
168243
])("%s: %s", (_, input) => {
169244
parseAndAssertSame(input);
170245
});
171246

172247
if (IS_BABEL_8()) {
173248
it.each([
174-
// ["class private method", "class C { #m() {} }"],
249+
["class private method", "class C { #m() {} }"],
175250
["class abstract property", "abstract class C { abstract p; }"],
176-
// ["class abstract private property", "abstract class C { abstract #p; }"]
177251
["class abstract method", "abstract class C { abstract m(): void }"],
178252
])("%s: %s", (_, input) => {
179253
parseAndAssertSame(input);

packages/babel-generator/test/fixtures/flow/parantheses/input.js renamed to packages/babel-generator/test/fixtures/flow/parentheses/input.js

File renamed without changes.

packages/babel-generator/test/fixtures/flow/parantheses/output.js renamed to packages/babel-generator/test/fixtures/flow/parentheses/output.js

File renamed without changes.

packages/babel-generator/test/preserve-format.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const FAILURES = [
3838
"flow/iterator-inside-types/input.js",
3939
"flow/object-literal-types/input.js",
4040
"flow/opaque-type-alias/input.js",
41-
"flow/parantheses/input.js",
41+
"flow/parentheses/input.js",
4242
"flow/predicates/input.js",
4343
"flow/this-param/input.js",
4444
"flow/tuples/input.js",

packages/babel-parser/src/parser/expression.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ import {
6161
type UnparenthesizedPipeBodyTypes,
6262
} from "../parse-error/pipeline-operator-errors.ts";
6363
import { setInnerComments } from "./comments.ts";
64-
import { cloneIdentifier, type Undone } from "./node.ts";
64+
import type { Undone } from "./node.ts";
6565
import type Parser from "./index.ts";
6666

6767
import { OptionFlags, type SourceType } from "../options.ts";
@@ -2297,7 +2297,7 @@ export default abstract class ExpressionParser extends LValParser {
22972297
if (isPattern) {
22982298
prop.value = this.parseMaybeDefault(
22992299
startLoc,
2300-
cloneIdentifier(prop.key),
2300+
this.cloneIdentifier(prop.key),
23012301
);
23022302
} else if (this.match(tt.eq)) {
23032303
const shorthandAssignLoc = this.state.startLoc;
@@ -2310,10 +2310,10 @@ export default abstract class ExpressionParser extends LValParser {
23102310
}
23112311
prop.value = this.parseMaybeDefault(
23122312
startLoc,
2313-
cloneIdentifier(prop.key),
2313+
this.cloneIdentifier(prop.key),
23142314
);
23152315
} else {
2316-
prop.value = cloneIdentifier(prop.key);
2316+
prop.value = this.cloneIdentifier(prop.key);
23172317
}
23182318
prop.shorthand = true;
23192319

packages/babel-parser/src/parser/lval.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export default abstract class LValParser extends NodeUtils {
132132
break;
133133

134134
case "ObjectExpression":
135-
(node as Node).type = "ObjectPattern";
135+
this.castNodeTo(node, "ObjectPattern");
136136
for (
137137
let i = 0, length = node.properties.length, last = length - 1;
138138
i < length;
@@ -172,7 +172,7 @@ export default abstract class LValParser extends NodeUtils {
172172
}
173173

174174
case "ArrayExpression":
175-
(node as Node).type = "ArrayPattern";
175+
this.castNodeTo(node, "ArrayPattern");
176176
this.toAssignableList(
177177
node.elements,
178178
node.extra?.trailingCommaLoc,
@@ -185,7 +185,7 @@ export default abstract class LValParser extends NodeUtils {
185185
this.raise(Errors.MissingEqInAssignment, node.left.loc.end);
186186
}
187187

188-
(node as Node).type = "AssignmentPattern";
188+
this.castNodeTo(node, "AssignmentPattern");
189189
delete node.operator;
190190
this.toAssignable(node.left, isLHS);
191191
break;
@@ -214,7 +214,7 @@ export default abstract class LValParser extends NodeUtils {
214214
prop.key,
215215
);
216216
} else if (prop.type === "SpreadElement") {
217-
(prop as Node).type = "RestElement";
217+
this.castNodeTo(prop, "RestElement");
218218
const arg = prop.argument;
219219
this.checkToRestConversion(arg, /* allowPattern */ false);
220220
this.toAssignable(arg, isLHS);
@@ -259,7 +259,7 @@ export default abstract class LValParser extends NodeUtils {
259259
): void {
260260
const node = exprList[index];
261261
if (node.type === "SpreadElement") {
262-
(node as unknown as RestElement).type = "RestElement";
262+
this.castNodeTo(node, "RestElement");
263263
const arg = node.argument;
264264
this.checkToRestConversion(arg, /* allowPattern */ true);
265265
this.toAssignable(arg, isLHS);

0 commit comments

Comments
 (0)