ember-eslint-parser 0.10.0 → 0.11.0
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 -2
- package/src/parser/gjs-gts-parser.js +45 -3
- package/src/parser/hbs-parser.js +9 -2
- package/src/parser/transforms.js +20 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ember-eslint-parser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"repository": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"@glimmer/syntax": "^0.95.0",
|
|
24
24
|
"@typescript-eslint/tsconfig-utils": "^8.57.1",
|
|
25
25
|
"content-tag": "^4.1.1",
|
|
26
|
-
"ember-estree": "^0.
|
|
26
|
+
"ember-estree": "^0.6.2",
|
|
27
27
|
"eslint-scope": "^9.1.2",
|
|
28
28
|
"html-tags": "^5.1.0",
|
|
29
29
|
"mathml-tag-names": "^4.0.0",
|
|
@@ -161,12 +161,14 @@ export function parseForESLint(code, options) {
|
|
|
161
161
|
const useTS = isTypescript || useTypescript;
|
|
162
162
|
|
|
163
163
|
// Both paths create scopeManager inside the parser callback so it's
|
|
164
|
-
// available when toTree invokes
|
|
164
|
+
// available when toTree invokes the visitor factory — no second pass.
|
|
165
165
|
let scopeManager = null;
|
|
166
|
+
const glimmerComments = [];
|
|
166
167
|
|
|
167
168
|
try {
|
|
168
169
|
const result = toTree(code, {
|
|
169
170
|
filePath,
|
|
171
|
+
tokens: true,
|
|
170
172
|
parser: useTS
|
|
171
173
|
? (placeholderJS) => {
|
|
172
174
|
let parseCode = placeholderJS;
|
|
@@ -196,11 +198,44 @@ export function parseForESLint(code, options) {
|
|
|
196
198
|
});
|
|
197
199
|
return { ast: program, scopeManager };
|
|
198
200
|
},
|
|
199
|
-
|
|
201
|
+
// Factory form: scopeManager is set by the parser callback before this
|
|
202
|
+
// runs. Returns null when unavailable so ember-estree skips the walk.
|
|
203
|
+
// The comments array is passed through so comment handlers share a
|
|
204
|
+
// single closure over it.
|
|
205
|
+
visitors: () => buildGlimmerVisitors(scopeManager, useTS, glimmerComments),
|
|
200
206
|
});
|
|
201
207
|
|
|
202
208
|
if (!result.scopeManager) result.scopeManager = scopeManager;
|
|
203
209
|
|
|
210
|
+
// Forward each placeholder's tsNode onto its GlimmerTemplate replacement,
|
|
211
|
+
// so typed rules calling `getTypeAtLocation(glimmerTemplate)` see the
|
|
212
|
+
// placeholder's type (string for expression templates, void for class-
|
|
213
|
+
// member static blocks) instead of the TS error intrinsic.
|
|
214
|
+
const esMap = result.services?.esTreeNodeToTSNodeMap;
|
|
215
|
+
if (esMap && result.templateInfos) {
|
|
216
|
+
for (const ti of result.templateInfos) {
|
|
217
|
+
const ts = esMap.get(ti.placeholder);
|
|
218
|
+
if (ts && !esMap.has(ti.ast)) esMap.set(ti.ast, ts);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Surface Glimmer template comments in program.comments as type:'Block'
|
|
223
|
+
// so ESLint's inline-config scanner and plugin rules recognise them.
|
|
224
|
+
if (glimmerComments.length > 0) {
|
|
225
|
+
const programNode = result.ast?.program ?? result.ast;
|
|
226
|
+
if (programNode) {
|
|
227
|
+
programNode.comments = [
|
|
228
|
+
...(programNode.comments || []),
|
|
229
|
+
...glimmerComments.map((c) => ({
|
|
230
|
+
type: 'Block',
|
|
231
|
+
value: c.value,
|
|
232
|
+
range: c.range,
|
|
233
|
+
loc: c.loc,
|
|
234
|
+
})),
|
|
235
|
+
].sort((a, b) => a.range[0] - b.range[0]);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
204
239
|
if (result.services?.program) {
|
|
205
240
|
const programAllowJs = result.services.program.getCompilerOptions?.()?.allowJs;
|
|
206
241
|
if (
|
|
@@ -231,7 +266,14 @@ export function parseForESLint(code, options) {
|
|
|
231
266
|
.split(':')
|
|
232
267
|
.slice(-2)
|
|
233
268
|
.map((x) => parseInt(x));
|
|
234
|
-
|
|
269
|
+
// e.source_code is the full diagnostic block. Strip ANSI escape codes
|
|
270
|
+
// (content-tag colours output in TTY environments, which breaks
|
|
271
|
+
// grep-based test:check assertions) and trim leading/trailing blank
|
|
272
|
+
// lines so consumers see a clean message.
|
|
273
|
+
// eslint-disable-next-line no-control-regex
|
|
274
|
+
const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
275
|
+
const errorText = e.source_code ? stripAnsi(e.source_code).trim() : e.message;
|
|
276
|
+
const err = new Error(errorText);
|
|
235
277
|
err.lineNumber = line;
|
|
236
278
|
err.column = column;
|
|
237
279
|
err.fileName = filePath;
|
package/src/parser/hbs-parser.js
CHANGED
|
@@ -27,7 +27,7 @@ export function parseForESLint(code, options) {
|
|
|
27
27
|
|
|
28
28
|
let result;
|
|
29
29
|
try {
|
|
30
|
-
result = toTree(code, { templateOnly: true });
|
|
30
|
+
result = toTree(code, { templateOnly: true, tokens: true });
|
|
31
31
|
} catch (e) {
|
|
32
32
|
// Transform glimmer parse error to ESLint-compatible error
|
|
33
33
|
const loc = e.location || (e.hash && e.hash.loc);
|
|
@@ -55,7 +55,14 @@ export function parseForESLint(code, options) {
|
|
|
55
55
|
type: 'Program',
|
|
56
56
|
body: [templateNode],
|
|
57
57
|
tokens: templateNode.tokens,
|
|
58
|
-
|
|
58
|
+
// Normalize Glimmer comment nodes to type:'Block' so ESLint's inline-config
|
|
59
|
+
// scanner and plugin rules that filter on type recognise them as comments.
|
|
60
|
+
comments: (comments || []).map((c) => ({
|
|
61
|
+
type: 'Block',
|
|
62
|
+
value: c.value,
|
|
63
|
+
range: c.range,
|
|
64
|
+
loc: c.loc,
|
|
65
|
+
})),
|
|
59
66
|
range: [0, code.length],
|
|
60
67
|
start: 0,
|
|
61
68
|
end: code.length,
|
package/src/parser/transforms.js
CHANGED
|
@@ -28,13 +28,10 @@ try {
|
|
|
28
28
|
// ── Scope helpers ─────────────────────────────────────────────────────
|
|
29
29
|
|
|
30
30
|
function findParentScope(scopeManager, nodePath) {
|
|
31
|
-
let scope = null;
|
|
32
31
|
let path = nodePath;
|
|
33
32
|
while (path) {
|
|
34
|
-
scope = scopeManager.acquire(path.node, true);
|
|
35
|
-
if (scope)
|
|
36
|
-
return scope;
|
|
37
|
-
}
|
|
33
|
+
const scope = scopeManager.acquire(path.node, true);
|
|
34
|
+
if (scope) return scope;
|
|
38
35
|
path = path.parentPath;
|
|
39
36
|
}
|
|
40
37
|
return null;
|
|
@@ -151,26 +148,33 @@ function registerElementNode(node, path, scopeManager) {
|
|
|
151
148
|
|
|
152
149
|
/**
|
|
153
150
|
* Build Glimmer visitors for toTree that register scopes during traversal.
|
|
154
|
-
*
|
|
155
|
-
*
|
|
151
|
+
* Returns null when scopeManager is unavailable so the caller can skip the walk.
|
|
152
|
+
* When `collectComments` is provided, appends Glimmer comment nodes to it so
|
|
153
|
+
* callers can forward them into program.comments after the walk.
|
|
154
|
+
* @param {object|null} scopeManager
|
|
156
155
|
* @param {boolean} isTypescript
|
|
157
|
-
* @
|
|
156
|
+
* @param {Array|null} [collectComments] - optional array to collect Glimmer comment nodes into
|
|
157
|
+
* @returns {object|null} visitors for toTree, or null to skip
|
|
158
158
|
*/
|
|
159
|
-
export function buildGlimmerVisitors(
|
|
160
|
-
return
|
|
159
|
+
export function buildGlimmerVisitors(scopeManager, isTypescript, collectComments) {
|
|
160
|
+
if (!scopeManager) return null;
|
|
161
|
+
const visitors = {
|
|
161
162
|
GlimmerPathExpression(node, path) {
|
|
162
|
-
|
|
163
|
-
if (sm) registerPathExpression(node, path, sm);
|
|
163
|
+
registerPathExpression(node, path, scopeManager);
|
|
164
164
|
},
|
|
165
165
|
GlimmerElementNode(node, path) {
|
|
166
|
-
|
|
167
|
-
if (sm) registerElementNode(node, path, sm);
|
|
166
|
+
registerElementNode(node, path, scopeManager);
|
|
168
167
|
},
|
|
169
168
|
GlimmerBlockParams(node, path) {
|
|
170
|
-
|
|
171
|
-
if (sm) registerBlockParams(node, path, sm, isTypescript);
|
|
169
|
+
registerBlockParams(node, path, scopeManager, isTypescript);
|
|
172
170
|
},
|
|
173
171
|
};
|
|
172
|
+
if (collectComments) {
|
|
173
|
+
const push = (node) => collectComments.push(node);
|
|
174
|
+
visitors.GlimmerMustacheCommentStatement = push;
|
|
175
|
+
visitors.GlimmerCommentStatement = push;
|
|
176
|
+
}
|
|
177
|
+
return visitors;
|
|
174
178
|
}
|
|
175
179
|
|
|
176
180
|
// ── registerGlimmerScopes (fallback for JS/oxc path) ──────────────────
|