ember-eslint-parser 0.2.5 → 0.3.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 +5 -3
- package/src/parser/gjs-gts-parser.js +24 -14
- package/src/parser/transforms.js +60 -67
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ember-eslint-parser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"repository": {
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"eslint-plugin-promise": "^6.0.0",
|
|
31
31
|
"execa": "^8.0.1",
|
|
32
32
|
"fs-extra": "^11.2.0",
|
|
33
|
-
"
|
|
33
|
+
"vite": "^5.0.12",
|
|
34
|
+
"vitest": "^1.2.2",
|
|
34
35
|
"prettier": "^3.1.1",
|
|
35
36
|
"publint": "^0.2.6",
|
|
36
37
|
"release-plan": "^0.6.0",
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
40
41
|
"@babel/eslint-parser": "^7.23.3",
|
|
41
|
-
"@glimmer/syntax": "^0.
|
|
42
|
+
"@glimmer/syntax": "^0.88.0",
|
|
42
43
|
"@typescript-eslint/scope-manager": "^6.15.0",
|
|
43
44
|
"content-tag": "^1.2.2",
|
|
44
45
|
"eslint-scope": "^7.2.2",
|
|
@@ -53,6 +54,7 @@
|
|
|
53
54
|
"typescript": "^5.3.3"
|
|
54
55
|
},
|
|
55
56
|
"scripts": {
|
|
57
|
+
"test": "vitest run",
|
|
56
58
|
"lint": "concurrently \"npm:lint:*(!fix)\" --names \"lint:\"",
|
|
57
59
|
"lint:js": "eslint .",
|
|
58
60
|
"lint:package": "pnpm publint",
|
|
@@ -40,20 +40,30 @@ module.exports = {
|
|
|
40
40
|
throw new Error('Please install typescript to process gts');
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
try {
|
|
44
|
+
result = isTypescript
|
|
45
|
+
? typescriptParser.parseForESLint(jsCode, { ...options, ranges: true, filePath })
|
|
46
|
+
: babelParser.parseForESLint(jsCode, {
|
|
47
|
+
...options,
|
|
48
|
+
requireConfigFile: false,
|
|
49
|
+
ranges: true,
|
|
50
|
+
});
|
|
51
|
+
if (!info.templateInfos?.length) {
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
const preprocessedResult = preprocessGlimmerTemplates(info, code);
|
|
55
|
+
preprocessedResult.code = code;
|
|
56
|
+
const { templateVisitorKeys } = preprocessedResult;
|
|
57
|
+
const visitorKeys = { ...result.visitorKeys, ...templateVisitorKeys };
|
|
58
|
+
result.isTypescript = isTypescript;
|
|
59
|
+
convertAst(result, preprocessedResult, visitorKeys);
|
|
60
|
+
if (result.services?.program) {
|
|
61
|
+
syncMtsGtsSourceFiles(result.services?.program);
|
|
62
|
+
}
|
|
63
|
+
return { ...result, visitorKeys };
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.error(e);
|
|
66
|
+
throw e;
|
|
48
67
|
}
|
|
49
|
-
const preprocessedResult = preprocessGlimmerTemplates(info, code);
|
|
50
|
-
const { templateVisitorKeys } = preprocessedResult;
|
|
51
|
-
const visitorKeys = { ...result.visitorKeys, ...templateVisitorKeys };
|
|
52
|
-
result.isTypescript = isTypescript;
|
|
53
|
-
convertAst(result, preprocessedResult, visitorKeys);
|
|
54
|
-
if (result.services?.program) {
|
|
55
|
-
syncMtsGtsSourceFiles(result.services?.program);
|
|
56
|
-
}
|
|
57
|
-
return { ...result, visitorKeys };
|
|
58
68
|
},
|
|
59
69
|
};
|
package/src/parser/transforms.js
CHANGED
|
@@ -122,6 +122,8 @@ function traverse(visitorKeys, node, visitor) {
|
|
|
122
122
|
|
|
123
123
|
visitor(currentPath);
|
|
124
124
|
|
|
125
|
+
if (!currentPath.node) continue;
|
|
126
|
+
|
|
125
127
|
const visitorKeys = allVisitorKeys[currentPath.node.type];
|
|
126
128
|
if (!visitorKeys) {
|
|
127
129
|
continue;
|
|
@@ -238,15 +240,12 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
|
|
|
238
240
|
for (const tpl of templateInfos) {
|
|
239
241
|
const currentComments = [];
|
|
240
242
|
const textNodes = [];
|
|
241
|
-
const
|
|
242
|
-
const
|
|
243
|
+
const emptyTextNodes = [];
|
|
244
|
+
const range = tpl.utf16Range;
|
|
245
|
+
const template = code.slice(...range);
|
|
243
246
|
const docLines = new DocumentLines(template);
|
|
244
247
|
const ast = glimmer.preprocess(template, { mode: 'codemod' });
|
|
245
|
-
ast.tokens = tokenize(
|
|
246
|
-
sliceByteRange(code, ...tpl.templateRange),
|
|
247
|
-
codeLines,
|
|
248
|
-
tpl.templateRange[0]
|
|
249
|
-
);
|
|
248
|
+
ast.tokens = tokenize(sliceByteRange(code, ...tpl.templateRange), codeLines, tpl.utf16Range[0]);
|
|
250
249
|
const allNodes = [];
|
|
251
250
|
glimmer.traverse(ast, {
|
|
252
251
|
All(node, path) {
|
|
@@ -259,7 +258,13 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
|
|
|
259
258
|
}
|
|
260
259
|
if (node.type === 'TextNode') {
|
|
261
260
|
n.value = node.chars;
|
|
262
|
-
|
|
261
|
+
// empty text nodes are not allowed, it's empty if its only whitespace or line terminators
|
|
262
|
+
// it's okay for AttrNodes
|
|
263
|
+
if (n.value.trim().length !== 0 || n.parent.type === 'AttrNode') {
|
|
264
|
+
textNodes.push(node);
|
|
265
|
+
} else {
|
|
266
|
+
emptyTextNodes.push(node);
|
|
267
|
+
}
|
|
263
268
|
}
|
|
264
269
|
},
|
|
265
270
|
});
|
|
@@ -278,12 +283,11 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
|
|
|
278
283
|
}
|
|
279
284
|
n.range =
|
|
280
285
|
n.type === 'Template'
|
|
281
|
-
? [tpl.
|
|
286
|
+
? [tpl.utf16Range[0], tpl.utf16Range[1]]
|
|
282
287
|
: [
|
|
283
288
|
range[0] + docLines.positionToOffset(n.loc.start),
|
|
284
289
|
range[0] + docLines.positionToOffset(n.loc.end),
|
|
285
290
|
];
|
|
286
|
-
|
|
287
291
|
n.start = n.range[0];
|
|
288
292
|
n.end = n.range[1];
|
|
289
293
|
n.loc = {
|
|
@@ -291,70 +295,39 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
|
|
|
291
295
|
end: codeLines.offsetToPosition(n.range[1]),
|
|
292
296
|
};
|
|
293
297
|
if (n.type === 'Template') {
|
|
294
|
-
n.loc.start = codeLines.offsetToPosition(tpl.
|
|
295
|
-
n.loc.end = codeLines.offsetToPosition(tpl.
|
|
298
|
+
n.loc.start = codeLines.offsetToPosition(tpl.utf16Range[0]);
|
|
299
|
+
n.loc.end = codeLines.offsetToPosition(tpl.utf16Range[1]);
|
|
296
300
|
}
|
|
297
|
-
|
|
298
|
-
// parts <Foo.Bar /> -> nodes for `Foo` and `Bar`
|
|
301
|
+
|
|
299
302
|
if (n.type === 'ElementNode') {
|
|
300
303
|
n.name = n.tag;
|
|
301
|
-
n.parts =
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
range[1] = range[0] + part.length;
|
|
308
|
-
codeSlice = sliceByteRange(code, range[1], n.range[1]);
|
|
309
|
-
start = range[1];
|
|
310
|
-
n.parts.push({
|
|
311
|
-
type: 'GlimmerElementNodePart',
|
|
312
|
-
name: part,
|
|
313
|
-
range,
|
|
314
|
-
parent: n,
|
|
315
|
-
loc: {
|
|
316
|
-
start: codeLines.offsetToPosition(range[0]),
|
|
317
|
-
end: codeLines.offsetToPosition(range[1]),
|
|
318
|
-
},
|
|
319
|
-
});
|
|
320
|
-
}
|
|
304
|
+
n.parts = n.parts.map((p) => ({
|
|
305
|
+
...p,
|
|
306
|
+
parent: n,
|
|
307
|
+
type: 'GlimmerElementNodePart',
|
|
308
|
+
name: p.value,
|
|
309
|
+
}));
|
|
321
310
|
}
|
|
322
|
-
|
|
323
|
-
// add our own nodes so we can reference them
|
|
311
|
+
|
|
324
312
|
if ('blockParams' in n) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
let part = sliceByteRange(code, ...blockRange);
|
|
332
|
-
let start = blockRange[0];
|
|
333
|
-
let idx = part.indexOf('|') + 1;
|
|
334
|
-
start += idx;
|
|
335
|
-
part = part.slice(idx, -1);
|
|
336
|
-
idx = part.indexOf('|');
|
|
337
|
-
part = part.slice(0, idx);
|
|
338
|
-
for (const param of n.blockParams) {
|
|
339
|
-
const regex = new RegExp(`\\b${param}\\b`);
|
|
340
|
-
const match = part.match(regex);
|
|
341
|
-
const range = [start + match.index, 0];
|
|
342
|
-
range[1] = range[0] + param.length;
|
|
343
|
-
n.params.push({
|
|
344
|
-
type: 'BlockParam',
|
|
345
|
-
name: param,
|
|
346
|
-
range,
|
|
347
|
-
parent: n,
|
|
348
|
-
loc: {
|
|
349
|
-
start: codeLines.offsetToPosition(range[0]),
|
|
350
|
-
end: codeLines.offsetToPosition(range[1]),
|
|
351
|
-
},
|
|
352
|
-
});
|
|
353
|
-
}
|
|
313
|
+
// program does have blockParams but not blockParamNodes
|
|
314
|
+
n.params = (n.blockParamNodes || []).map((p) => ({
|
|
315
|
+
...p,
|
|
316
|
+
parent: n,
|
|
317
|
+
name: p.value,
|
|
318
|
+
}));
|
|
354
319
|
}
|
|
355
320
|
n.type = `Glimmer${n.type}`;
|
|
356
321
|
allNodeTypes.add(n.type);
|
|
357
322
|
}
|
|
323
|
+
|
|
324
|
+
// ast should not contains empty text nodes
|
|
325
|
+
for (const node of emptyTextNodes) {
|
|
326
|
+
const children = node.parent.children || node.parent.body;
|
|
327
|
+
const idx = children.indexOf(node);
|
|
328
|
+
children.splice(idx, 1);
|
|
329
|
+
}
|
|
330
|
+
|
|
358
331
|
// ast should not contain comment nodes
|
|
359
332
|
for (const comment of currentComments) {
|
|
360
333
|
const parentBody = comment.parent.body || comment.parent.children;
|
|
@@ -414,14 +387,16 @@ module.exports.convertAst = function convertAst(result, preprocessedResult, visi
|
|
|
414
387
|
result.ast.comments.push(...preprocessedResult.comments);
|
|
415
388
|
|
|
416
389
|
for (const ti of templateInfos) {
|
|
417
|
-
const firstIdx = result.ast.tokens.findIndex((t) => t.range[0] === ti.
|
|
418
|
-
const lastIdx = result.ast.tokens.findIndex((t) => t.range[1] === ti.
|
|
390
|
+
const firstIdx = result.ast.tokens.findIndex((t) => t.range[0] === ti.utf16Range[0]);
|
|
391
|
+
const lastIdx = result.ast.tokens.findIndex((t) => t.range[1] === ti.utf16Range[1]);
|
|
419
392
|
result.ast.tokens.splice(firstIdx, lastIdx - firstIdx + 1, ...ti.ast.tokens);
|
|
420
393
|
}
|
|
421
394
|
|
|
422
395
|
// eslint-disable-next-line complexity
|
|
423
396
|
traverse(visitorKeys, result.ast, (path) => {
|
|
424
397
|
const node = path.node;
|
|
398
|
+
if (!node) return null;
|
|
399
|
+
|
|
425
400
|
if (
|
|
426
401
|
node.type === 'ExpressionStatement' ||
|
|
427
402
|
node.type === 'StaticBlock' ||
|
|
@@ -501,6 +476,21 @@ module.exports.convertAst = function convertAst(result, preprocessedResult, visi
|
|
|
501
476
|
return null;
|
|
502
477
|
});
|
|
503
478
|
|
|
479
|
+
traverse(visitorKeys, result.ast, (path) => {
|
|
480
|
+
const node = path.node;
|
|
481
|
+
if (!node) return null;
|
|
482
|
+
|
|
483
|
+
if (
|
|
484
|
+
['GlimmerMustacheStatement', 'GlimmerBlockStatement', 'GlimmerSubExpression'].includes(
|
|
485
|
+
node.type
|
|
486
|
+
)
|
|
487
|
+
) {
|
|
488
|
+
if (node.hash && node.hash.pairs.length === 0) {
|
|
489
|
+
node.hash = null;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
|
|
504
494
|
if (counter !== templateInfos.length) {
|
|
505
495
|
throw new Error('failed to process all templates');
|
|
506
496
|
}
|
|
@@ -569,3 +559,6 @@ module.exports.transformForLint = function transformForLint(code) {
|
|
|
569
559
|
output: jsCode,
|
|
570
560
|
};
|
|
571
561
|
};
|
|
562
|
+
|
|
563
|
+
module.exports.traverse = traverse;
|
|
564
|
+
module.exports.tokenize = tokenize;
|