ember-eslint-parser 0.14.7 → 0.14.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/parser/gjs-gts-parser.js +14 -0
- package/src/parser/transforms.js +50 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ember-eslint-parser",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.9",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"repository": {
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"@babel/core": "^7.23.6",
|
|
34
34
|
"@babel/eslint-parser": "^7.28.6",
|
|
35
35
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
36
|
+
"@typescript-eslint/eslint-plugin": "^8.46.4",
|
|
36
37
|
"@typescript-eslint/parser": "^8.0.0",
|
|
37
38
|
"@typescript-eslint/scope-manager": "^8.0.0",
|
|
38
39
|
"@typescript-eslint/visitor-keys": "^8.0.0",
|
|
@@ -130,6 +130,20 @@ export function parseForESLint(code, options) {
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
// Scope analysis ran on the placeholder JS, so a write of a template
|
|
134
|
+
// (`const Foo = <template>`, an assignment, a default parameter) still
|
|
135
|
+
// has the placeholder as its writeExpr. The placeholder is out of the
|
|
136
|
+
// AST and never gets a `parent`, which rules read (#257).
|
|
137
|
+
if (result.scopeManager && result.templateInfos?.length) {
|
|
138
|
+
const templates = new Map(result.templateInfos.map((ti) => [ti.placeholder, ti.ast]));
|
|
139
|
+
for (const scope of result.scopeManager.scopes) {
|
|
140
|
+
for (const ref of scope.references) {
|
|
141
|
+
const template = ref.writeExpr && templates.get(ref.writeExpr);
|
|
142
|
+
if (template) ref.writeExpr = template;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
133
147
|
// Surface Glimmer template comments in program.comments as type:'Block'
|
|
134
148
|
// so ESLint's inline-config scanner and plugin rules recognise them.
|
|
135
149
|
if (glimmerComments.length > 0) {
|
package/src/parser/transforms.js
CHANGED
|
@@ -15,6 +15,7 @@ const mathMLTagsSet = new Set(mathmlTagNames);
|
|
|
15
15
|
const require = createRequire(import.meta.url);
|
|
16
16
|
|
|
17
17
|
let TypescriptScope = null;
|
|
18
|
+
let TypescriptTemplateScope = null;
|
|
18
19
|
try {
|
|
19
20
|
const parserPath = require.resolve('@typescript-eslint/parser');
|
|
20
21
|
// eslint-disable-next-line n/no-unpublished-require
|
|
@@ -22,6 +23,26 @@ try {
|
|
|
22
23
|
paths: [parserPath],
|
|
23
24
|
});
|
|
24
25
|
TypescriptScope = require(scopeManagerPath);
|
|
26
|
+
TypescriptTemplateScope = class TemplateScope extends TypescriptScope.FunctionScope {
|
|
27
|
+
constructor(scopeManager, upperScope, block) {
|
|
28
|
+
// `isMethodDefinition` makes it strict without scanning the body for a
|
|
29
|
+
// "use strict" directive, which throws on a template's body.
|
|
30
|
+
super(scopeManager, upperScope, block, true);
|
|
31
|
+
// A template has no `arguments`, unlike the function FunctionScope models.
|
|
32
|
+
const args = this.set.get('arguments');
|
|
33
|
+
if (args) {
|
|
34
|
+
this.set.delete('arguments');
|
|
35
|
+
this.variables.splice(this.variables.indexOf(args), 1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Never called for a template scope: only closing a scope during analysis
|
|
40
|
+
// calls it, and templates get their scopes after that. The base would
|
|
41
|
+
// throw on a template's `body`, so guard it anyway.
|
|
42
|
+
isValidResolution() {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
25
46
|
} catch {
|
|
26
47
|
// not available
|
|
27
48
|
}
|
|
@@ -85,13 +106,28 @@ function isUpperCase(char) {
|
|
|
85
106
|
return char.toUpperCase() === char;
|
|
86
107
|
}
|
|
87
108
|
|
|
88
|
-
function
|
|
89
|
-
const blockParamNodes = node.blockParamNodes || [];
|
|
90
|
-
if (blockParamNodes.length === 0) return;
|
|
109
|
+
function createBlockScope(node, path, scopeManager, isTypescript) {
|
|
91
110
|
const upperScope = findParentScope(scopeManager, path);
|
|
92
|
-
|
|
111
|
+
return isTypescript
|
|
93
112
|
? new TypescriptScope.BlockScope(scopeManager, upperScope, node)
|
|
94
113
|
: new Scope(scopeManager, 'block', upperScope, node);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// A template compiles to a closure (`scope: () => ({ Foo })`), so give it a
|
|
117
|
+
// function scope: its references run in an execution context of their own,
|
|
118
|
+
// and no-use-before-define accepts `<Foo />` in `const Foo = <template>`.
|
|
119
|
+
function registerTemplateScope(node, path, scopeManager, isTypescript) {
|
|
120
|
+
const upperScope = findParentScope(scopeManager, path);
|
|
121
|
+
if (isTypescript) return new TypescriptTemplateScope(scopeManager, upperScope, node);
|
|
122
|
+
// `isMethodDefinition` as in TemplateScope. eslint-scope's base Scope
|
|
123
|
+
// declares no `arguments`.
|
|
124
|
+
return new Scope(scopeManager, 'function', upperScope, node, true);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function registerBlockParams(node, path, scopeManager, isTypescript) {
|
|
128
|
+
const blockParamNodes = node.blockParamNodes || [];
|
|
129
|
+
if (blockParamNodes.length === 0) return;
|
|
130
|
+
const scope = createBlockScope(node, path, scopeManager, isTypescript);
|
|
95
131
|
const declaredVariables = scopeManager.declaredVariables || scopeManager.__declaredVariables;
|
|
96
132
|
const vars = [];
|
|
97
133
|
declaredVariables.set(node, vars);
|
|
@@ -104,7 +140,10 @@ function registerBlockParams(node, path, scopeManager, isTypescript) {
|
|
|
104
140
|
};
|
|
105
141
|
for (const [i, b] of blockParamNodes.entries()) {
|
|
106
142
|
const v = new Variable(b.name, scope);
|
|
107
|
-
|
|
143
|
+
// The block param, parented to the virtual function, so rules that walk
|
|
144
|
+
// up from a declaration (no-use-before-define's initializer check) stop
|
|
145
|
+
// there, as they do for a function parameter.
|
|
146
|
+
v.identifiers.push({ ...b, parent: virtualJSParentNode });
|
|
108
147
|
scope.variables.push(v);
|
|
109
148
|
scope.set.set(b.name, v);
|
|
110
149
|
vars.push(v);
|
|
@@ -177,6 +216,9 @@ function registerElementNode(node, path, scopeManager) {
|
|
|
177
216
|
export function buildGlimmerVisitors(scopeManager, isTypescript, collectComments) {
|
|
178
217
|
if (!scopeManager) return null;
|
|
179
218
|
const visitors = {
|
|
219
|
+
GlimmerTemplate(node, path) {
|
|
220
|
+
registerTemplateScope(node, path, scopeManager, isTypescript);
|
|
221
|
+
},
|
|
180
222
|
GlimmerPathExpression(node, path) {
|
|
181
223
|
registerPathExpression(node, path, scopeManager);
|
|
182
224
|
},
|
|
@@ -251,6 +293,9 @@ export function registerGlimmerScopes(result) {
|
|
|
251
293
|
traverse(result.visitorKeys, result.ast, (path) => {
|
|
252
294
|
const node = path.node;
|
|
253
295
|
if (!node) return;
|
|
296
|
+
if (node.type === 'GlimmerTemplate') {
|
|
297
|
+
registerTemplateScope(node, path, result.scopeManager, result.isTypescript);
|
|
298
|
+
}
|
|
254
299
|
if (node.type === 'GlimmerPathExpression') {
|
|
255
300
|
registerPathExpression(node, path, result.scopeManager);
|
|
256
301
|
}
|