ember-eslint-parser 0.2.6 → 0.3.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember-eslint-parser",
3
- "version": "0.2.6",
3
+ "version": "0.3.1",
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
- "jest": "^29.7.0",
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.85.13",
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",
@@ -52,6 +52,7 @@ module.exports = {
52
52
  return result;
53
53
  }
54
54
  const preprocessedResult = preprocessGlimmerTemplates(info, code);
55
+ preprocessedResult.code = code;
55
56
  const { templateVisitorKeys } = preprocessedResult;
56
57
  const visitorKeys = { ...result.visitorKeys, ...templateVisitorKeys };
57
58
  result.isTypescript = isTypescript;
@@ -125,6 +125,16 @@ function traverse(visitorKeys, node, visitor) {
125
125
  if (!currentPath.node) continue;
126
126
 
127
127
  const visitorKeys = allVisitorKeys[currentPath.node.type];
128
+ if (currentPath.node.type === 'GlimmerElementNode') {
129
+ if (!visitorKeys.includes('blockParamNodes')) {
130
+ visitorKeys.push('blockParamNodes', 'parts');
131
+ }
132
+ }
133
+ if (currentPath.node.type === 'GlimmerProgram') {
134
+ if (!visitorKeys.includes('blockParamNodes')) {
135
+ visitorKeys.push('blockParamNodes');
136
+ }
137
+ }
128
138
  if (!visitorKeys) {
129
139
  continue;
130
140
  }
@@ -240,15 +250,13 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
240
250
  for (const tpl of templateInfos) {
241
251
  const currentComments = [];
242
252
  const textNodes = [];
243
- const range = tpl.range;
244
- const template = sliceByteRange(code, ...range);
253
+ const emptyTextNodes = [];
254
+ const range = tpl.utf16Range;
255
+ const offset = range[0];
256
+ const template = code.slice(...range);
245
257
  const docLines = new DocumentLines(template);
246
258
  const ast = glimmer.preprocess(template, { mode: 'codemod' });
247
- ast.tokens = tokenize(
248
- sliceByteRange(code, ...tpl.templateRange),
249
- codeLines,
250
- tpl.templateRange[0]
251
- );
259
+ ast.tokens = tokenize(sliceByteRange(code, ...tpl.templateRange), codeLines, tpl.utf16Range[0]);
252
260
  const allNodes = [];
253
261
  glimmer.traverse(ast, {
254
262
  All(node, path) {
@@ -261,7 +269,13 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
261
269
  }
262
270
  if (node.type === 'TextNode') {
263
271
  n.value = node.chars;
264
- textNodes.push(node);
272
+ // empty text nodes are not allowed, it's empty if its only whitespace or line terminators
273
+ // it's okay for AttrNodes
274
+ if (n.value.trim().length !== 0 || n.parent.type === 'AttrNode') {
275
+ textNodes.push(node);
276
+ } else {
277
+ emptyTextNodes.push(node);
278
+ }
265
279
  }
266
280
  },
267
281
  });
@@ -270,8 +284,8 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
270
284
  for (const n of allNodes) {
271
285
  if (n.type === 'PathExpression') {
272
286
  n.head.range = [
273
- range[0] + docLines.positionToOffset(n.head.loc.start),
274
- range[0] + docLines.positionToOffset(n.head.loc.end),
287
+ offset + docLines.positionToOffset(n.head.loc.start),
288
+ offset + docLines.positionToOffset(n.head.loc.end),
275
289
  ];
276
290
  n.head.loc = {
277
291
  start: codeLines.offsetToPosition(n.head.range[0]),
@@ -280,12 +294,11 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
280
294
  }
281
295
  n.range =
282
296
  n.type === 'Template'
283
- ? [tpl.templateRange[0], tpl.templateRange[1]]
297
+ ? [...tpl.utf16Range]
284
298
  : [
285
- range[0] + docLines.positionToOffset(n.loc.start),
286
- range[0] + docLines.positionToOffset(n.loc.end),
299
+ offset + docLines.positionToOffset(n.loc.start),
300
+ offset + docLines.positionToOffset(n.loc.end),
287
301
  ];
288
-
289
302
  n.start = n.range[0];
290
303
  n.end = n.range[1];
291
304
  n.loc = {
@@ -293,70 +306,47 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
293
306
  end: codeLines.offsetToPosition(n.range[1]),
294
307
  };
295
308
  if (n.type === 'Template') {
296
- n.loc.start = codeLines.offsetToPosition(tpl.templateRange[0]);
297
- n.loc.end = codeLines.offsetToPosition(tpl.templateRange[1]);
309
+ n.loc.start = codeLines.offsetToPosition(tpl.utf16Range[0]);
310
+ n.loc.end = codeLines.offsetToPosition(tpl.utf16Range[1]);
298
311
  }
299
- // split up element node into sub nodes to be able to reference tag name
300
- // parts <Foo.Bar /> -> nodes for `Foo` and `Bar`
312
+
301
313
  if (n.type === 'ElementNode') {
302
314
  n.name = n.tag;
303
- n.parts = [];
304
- let start = n.range[0];
305
- let codeSlice = sliceByteRange(code, ...n.range);
306
- for (const part of n.tag.split('.')) {
307
- const idx = codeSlice.indexOf(part);
308
- const range = [start + idx, 0];
309
- range[1] = range[0] + part.length;
310
- codeSlice = sliceByteRange(code, range[1], n.range[1]);
311
- start = range[1];
312
- n.parts.push({
313
- type: 'GlimmerElementNodePart',
314
- name: part,
315
- range,
316
- parent: n,
317
- loc: {
318
- start: codeLines.offsetToPosition(range[0]),
319
- end: codeLines.offsetToPosition(range[1]),
320
- },
321
- });
322
- }
315
+ n.parts = n.parts.map((p) => ({
316
+ ...p,
317
+ parent: n,
318
+ type: 'GlimmerElementNodePart',
319
+ name: p.value,
320
+ range: [
321
+ offset + docLines.positionToOffset(p.loc.start),
322
+ offset + docLines.positionToOffset(p.loc.end),
323
+ ],
324
+ }));
323
325
  }
324
- // block params do not have location information
325
- // add our own nodes so we can reference them
326
+
326
327
  if ('blockParams' in n) {
327
- n.params = [];
328
- }
329
- if ('blockParams' in n && n.parent) {
330
- // for blocks {{x as |b|}} the block range does not contain the block params...
331
- // for element tag it does <x as b />
332
- const blockRange = n.type === 'Block' ? n.parent.range : n.range;
333
- let part = sliceByteRange(code, ...blockRange);
334
- let start = blockRange[0];
335
- let idx = part.indexOf('|') + 1;
336
- start += idx;
337
- part = part.slice(idx, -1);
338
- idx = part.indexOf('|');
339
- part = part.slice(0, idx);
340
- for (const param of n.blockParams) {
341
- const regex = new RegExp(`\\b${param}\\b`);
342
- const match = part.match(regex);
343
- const range = [start + match.index, 0];
344
- range[1] = range[0] + param.length;
345
- n.params.push({
346
- type: 'BlockParam',
347
- name: param,
348
- range,
349
- parent: n,
350
- loc: {
351
- start: codeLines.offsetToPosition(range[0]),
352
- end: codeLines.offsetToPosition(range[1]),
353
- },
354
- });
355
- }
328
+ // program does have blockParams but not blockParamNodes
329
+ n.params = (n.blockParamNodes || []).map((p) => ({
330
+ ...p,
331
+ parent: n,
332
+ name: p.value,
333
+ range: [
334
+ offset + docLines.positionToOffset(p.loc.start),
335
+ offset + docLines.positionToOffset(p.loc.end),
336
+ ],
337
+ }));
356
338
  }
357
339
  n.type = `Glimmer${n.type}`;
358
340
  allNodeTypes.add(n.type);
359
341
  }
342
+
343
+ // ast should not contains empty text nodes
344
+ for (const node of emptyTextNodes) {
345
+ const children = node.parent.children || node.parent.body;
346
+ const idx = children.indexOf(node);
347
+ children.splice(idx, 1);
348
+ }
349
+
360
350
  // ast should not contain comment nodes
361
351
  for (const comment of currentComments) {
362
352
  const parentBody = comment.parent.body || comment.parent.children;
@@ -416,8 +406,8 @@ module.exports.convertAst = function convertAst(result, preprocessedResult, visi
416
406
  result.ast.comments.push(...preprocessedResult.comments);
417
407
 
418
408
  for (const ti of templateInfos) {
419
- const firstIdx = result.ast.tokens.findIndex((t) => t.range[0] === ti.templateRange[0]);
420
- const lastIdx = result.ast.tokens.findIndex((t) => t.range[1] === ti.templateRange[1]);
409
+ const firstIdx = result.ast.tokens.findIndex((t) => t.range[0] === ti.utf16Range[0]);
410
+ const lastIdx = result.ast.tokens.findIndex((t) => t.range[1] === ti.utf16Range[1]);
421
411
  result.ast.tokens.splice(firstIdx, lastIdx - firstIdx + 1, ...ti.ast.tokens);
422
412
  }
423
413
 
@@ -425,6 +415,7 @@ module.exports.convertAst = function convertAst(result, preprocessedResult, visi
425
415
  traverse(visitorKeys, result.ast, (path) => {
426
416
  const node = path.node;
427
417
  if (!node) return null;
418
+
428
419
  if (
429
420
  node.type === 'ExpressionStatement' ||
430
421
  node.type === 'StaticBlock' ||
@@ -504,6 +495,21 @@ module.exports.convertAst = function convertAst(result, preprocessedResult, visi
504
495
  return null;
505
496
  });
506
497
 
498
+ traverse(visitorKeys, result.ast, (path) => {
499
+ const node = path.node;
500
+ if (!node) return null;
501
+
502
+ if (
503
+ ['GlimmerMustacheStatement', 'GlimmerBlockStatement', 'GlimmerSubExpression'].includes(
504
+ node.type
505
+ )
506
+ ) {
507
+ if (node.hash && node.hash.pairs.length === 0) {
508
+ node.hash = null;
509
+ }
510
+ }
511
+ });
512
+
507
513
  if (counter !== templateInfos.length) {
508
514
  throw new Error('failed to process all templates');
509
515
  }
@@ -572,3 +578,6 @@ module.exports.transformForLint = function transformForLint(code) {
572
578
  output: jsCode,
573
579
  };
574
580
  };
581
+
582
+ module.exports.traverse = traverse;
583
+ module.exports.tokenize = tokenize;