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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember-eslint-parser",
3
- "version": "0.14.7",
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) {
@@ -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 registerBlockParams(node, path, scopeManager, isTypescript) {
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
- const scope = isTypescript
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
- v.identifiers.push(b);
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
  }