Skip to content

Commit 7126ccc

Browse files
authoredApr 26, 2024··
fix: crash in scope analysis when using espree (#318)
* fix: crash in scope analysis when using espree * fix * fix * Create chilly-numbers-smell.md
1 parent b427ec8 commit 7126ccc

11 files changed

+3829
-30
lines changed
 

‎.changeset/chilly-numbers-smell.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"astro-eslint-parser": patch
3+
---
4+
5+
fix: crash in scope analysis when using espree

‎.github/workflows/NodeCI.yml

+5-24
Original file line numberDiff line numberDiff line change
@@ -25,34 +25,15 @@ jobs:
2525
runs-on: ubuntu-latest
2626
strategy:
2727
matrix:
28-
node-version: [20.x, 22.x]
28+
node-version: [18.x, 20.x, 22.x]
2929
steps:
3030
- uses: actions/checkout@v4
3131
- name: Use Node.js ${{ matrix.node-version }}
3232
uses: actions/setup-node@v4
3333
with:
3434
node-version: ${{ matrix.node-version }}
3535
- name: Install Packages
36-
run: npm install --legacy-peer-deps
37-
- name: Test
38-
run: npm test
39-
test-for-old-node:
40-
runs-on: ubuntu-latest
41-
strategy:
42-
matrix:
43-
node-version: [18.x]
44-
steps:
45-
- uses: actions/checkout@v4
46-
- name: Use Node.js ${{ matrix.node-version }}
47-
uses: actions/setup-node@v4
48-
with:
49-
node-version: ${{ matrix.node-version }}
50-
- name: Install old versions
51-
run: |+
52-
npm i -D @typescript-eslint/parser@5 @typescript-eslint/eslint-plugin@5 --legacy-peer-deps
53-
npx rimraf node_modules
54-
- name: Install Packages
55-
run: npm install --legacy-peer-deps
36+
run: npm install -f
5637
- name: Test
5738
run: npm test
5839
test-for-eslint-v7:
@@ -68,10 +49,10 @@ jobs:
6849
node-version: ${{ matrix.node-version }}
6950
- name: Install eslint v7
7051
run: |+
71-
npm i -D eslint@7 @typescript-eslint/parser@5 @typescript-eslint/eslint-plugin@5 --legacy-peer-deps
52+
npm i -D eslint@7 @typescript-eslint/parser@5 @typescript-eslint/eslint-plugin@5 -f
7253
npx rimraf node_modules
7354
- name: Install Packages
74-
run: npm install --legacy-peer-deps
55+
run: npm install -f
7556
- name: Test
7657
run: npm test
7758
test-and-coverage:
@@ -84,6 +65,6 @@ jobs:
8465
- name: Test
8566
run: npm run cover
8667
- name: Coveralls GitHub Action
87-
uses: coverallsapp/github-action@3dfc5567390f6fa9267c0ee9c251e4c8c3f18949 # v2.2.3
68+
uses: coverallsapp/github-action@v2.2.3
8869
with:
8970
github-token: ${{ secrets.GITHUB_TOKEN }}

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"astrojs-compiler-sync": "^1.0.0",
5656
"debug": "^4.3.4",
5757
"entities": "^4.5.0",
58+
"eslint-scope": "^8.0.1",
5859
"eslint-visitor-keys": "^4.0.0",
5960
"espree": "^10.0.0",
6061
"globby": "^11.1.0",

‎src/parser/script.ts

+40-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import type { Context } from "../context";
22
import { debug } from "../debug";
3-
import type { ParserOptionsContext } from "../context/parser-options";
3+
import type {
4+
ParserOptions,
5+
ParserOptionsContext,
6+
} from "../context/parser-options";
47
import type { ESLintExtendedProgram } from "../types";
58
import { tsPatch } from "./ts-patch";
69
import { isEnhancedParserObject } from "../context/resolve-parser/parser-object";
7-
import { analyze } from "@typescript-eslint/scope-manager";
10+
import type { ScopeManager } from "@typescript-eslint/scope-manager";
11+
import { analyze as analyzeForTypeScript } from "@typescript-eslint/scope-manager";
12+
import { analyze as analyzeForEcmaScript } from "eslint-scope";
13+
import { KEYS } from "../visitor-keys";
14+
import { getKeys } from "../traverse";
815
/**
916
* Parse for script
1017
*/
@@ -17,17 +24,45 @@ export function parseScript(
1724

1825
const parserOptions = parserOptionsCtx.parserOptions;
1926
if (!result.scopeManager && parserOptions.eslintScopeManager) {
20-
result.scopeManager = analyze(result.ast, {
21-
ecmaVersion: 1e8,
27+
result.scopeManager = analyzeScope(result, parserOptions);
28+
}
29+
30+
return result;
31+
}
32+
33+
/**
34+
* Analyze scope
35+
*/
36+
function analyzeScope(
37+
result: ESLintExtendedProgram,
38+
parserOptions: ParserOptions,
39+
): ScopeManager {
40+
try {
41+
return analyzeForTypeScript(result.ast, {
2242
globalReturn: parserOptions.ecmaFeatures?.globalReturn,
2343
jsxPragma: parserOptions.jsxPragma,
2444
jsxFragmentName: parserOptions.jsxFragmentName,
2545
lib: parserOptions.lib,
2646
sourceType: parserOptions.sourceType,
2747
});
48+
} catch {
49+
// ignore
2850
}
51+
const ecmaFeatures = parserOptions.ecmaFeatures || {};
2952

30-
return result;
53+
return analyzeForEcmaScript(result.ast, {
54+
ignoreEval: true,
55+
nodejsScope: ecmaFeatures.globalReturn,
56+
impliedStrict: ecmaFeatures.impliedStrict as never,
57+
ecmaVersion: 1e8,
58+
sourceType:
59+
parserOptions.sourceType === "commonjs"
60+
? "script"
61+
: parserOptions.sourceType || "script",
62+
// @ts-expect-error -- Type bug?
63+
childVisitorKeys: result.visitorKeys || KEYS,
64+
fallback: getKeys,
65+
}) as ScopeManager;
3166
}
3267

3368
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"scope": {
3+
"@typescript-eslint/parser" : ">=7"
4+
}
5+
}
Original file line numberDiff line numberDiff line change
@@ -1 +1,180 @@
1-
null
1+
{
2+
"type": "global",
3+
"variables": [],
4+
"references": [],
5+
"childScopes": [
6+
{
7+
"type": "module",
8+
"variables": [
9+
{
10+
"name": "MyComponent",
11+
"identifiers": [
12+
{
13+
"type": "Identifier",
14+
"name": "MyComponent",
15+
"range": [
16+
11,
17+
22
18+
],
19+
"loc": {
20+
"start": {
21+
"line": 2,
22+
"column": 7
23+
},
24+
"end": {
25+
"line": 2,
26+
"column": 18
27+
}
28+
}
29+
}
30+
],
31+
"defs": [
32+
{
33+
"type": "ImportBinding",
34+
"name": {
35+
"type": "Identifier",
36+
"name": "MyComponent",
37+
"range": [
38+
11,
39+
22
40+
],
41+
"loc": {
42+
"start": {
43+
"line": 2,
44+
"column": 7
45+
},
46+
"end": {
47+
"line": 2,
48+
"column": 18
49+
}
50+
}
51+
},
52+
"node": {
53+
"type": "ImportDefaultSpecifier",
54+
"local": {
55+
"type": "Identifier",
56+
"name": "MyComponent",
57+
"range": [
58+
11,
59+
22
60+
],
61+
"loc": {
62+
"start": {
63+
"line": 2,
64+
"column": 7
65+
},
66+
"end": {
67+
"line": 2,
68+
"column": 18
69+
}
70+
}
71+
},
72+
"range": [
73+
11,
74+
22
75+
],
76+
"loc": {
77+
"start": {
78+
"line": 2,
79+
"column": 7
80+
},
81+
"end": {
82+
"line": 2,
83+
"column": 18
84+
}
85+
}
86+
}
87+
}
88+
],
89+
"references": [
90+
{
91+
"identifier": {
92+
"type": "JSXIdentifier",
93+
"name": "MyComponent",
94+
"range": [
95+
57,
96+
68
97+
],
98+
"loc": {
99+
"start": {
100+
"line": 5,
101+
"column": 1
102+
},
103+
"end": {
104+
"line": 5,
105+
"column": 12
106+
}
107+
}
108+
},
109+
"from": "module",
110+
"init": null,
111+
"resolved": {
112+
"type": "Identifier",
113+
"name": "MyComponent",
114+
"range": [
115+
11,
116+
22
117+
],
118+
"loc": {
119+
"start": {
120+
"line": 2,
121+
"column": 7
122+
},
123+
"end": {
124+
"line": 2,
125+
"column": 18
126+
}
127+
}
128+
}
129+
}
130+
]
131+
}
132+
],
133+
"references": [
134+
{
135+
"identifier": {
136+
"type": "JSXIdentifier",
137+
"name": "MyComponent",
138+
"range": [
139+
57,
140+
68
141+
],
142+
"loc": {
143+
"start": {
144+
"line": 5,
145+
"column": 1
146+
},
147+
"end": {
148+
"line": 5,
149+
"column": 12
150+
}
151+
}
152+
},
153+
"from": "module",
154+
"init": null,
155+
"resolved": {
156+
"type": "Identifier",
157+
"name": "MyComponent",
158+
"range": [
159+
11,
160+
22
161+
],
162+
"loc": {
163+
"start": {
164+
"line": 2,
165+
"column": 7
166+
},
167+
"end": {
168+
"line": 2,
169+
"column": 18
170+
}
171+
}
172+
}
173+
}
174+
],
175+
"childScopes": [],
176+
"through": []
177+
}
178+
],
179+
"through": []
180+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
const sidebarSections = [{ text: "foo" }]
3+
---
4+
5+
<nav aria-labelledby="grid-left">
6+
<ul class="nav-groups">
7+
{sidebarSections.map((section) => (
8+
<li>
9+
<div class="nav-group">{section.text}</div>
10+
</li>
11+
))}
12+
</ul>
13+
</nav>
14+
15+
<style>
16+
.nav-groups > li + li {
17+
margin-top: 2rem;
18+
}
19+
</style>

‎tests/fixtures/parser/ast/js-test01-output.json

+2,683
Large diffs are not rendered by default.

‎tests/fixtures/parser/ast/js-test01-scope-output.json

+882
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"ruleId": "semi",
4+
"code": "\n",
5+
"line": 2,
6+
"column": 42
7+
}
8+
]

‎tests/src/parser/test-utils.ts

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
project: require.resolve("../../fixtures/tsconfig.test.json"),
3232
extraFileExtensions: [".astro", ".md"],
3333
sourceType: "module",
34+
eslintScopeManager: true,
3435
};
3536
}
3637

@@ -63,7 +64,7 @@
6364
}> {
6465
for (const filename of fs.readdirSync(dir)) {
6566
if (filename === "omit-end-tag-input.astro") {
6667
// FIXME The astro parser used to parse well, but since 0.26.1 it no longer parses well.
6768
continue;
6869
}
6970
const inputFileName = path.join(dir, filename);

0 commit comments

Comments
 (0)
Please sign in to comment.