ember-eslint-parser 0.11.2 → 0.12.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember-eslint-parser",
3
- "version": "0.11.2",
3
+ "version": "0.12.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.6.2",
26
+ "ember-estree": "^0.6.3",
27
27
  "eslint-scope": "^9.1.2",
28
28
  "html-tags": "^5.1.0",
29
29
  "mathml-tag-names": "^4.0.0",
@@ -0,0 +1,105 @@
1
+ import { createRequire } from 'node:module';
2
+
3
+ /**
4
+ * Built-in template keywords shipped from `ember-source` 7.1.0
5
+ * (RFCs 389, 470, 560, 561, 562, 997, 998, 999, 1000). They can be referenced
6
+ * in strict-mode (`.gjs` / `.gts`) templates without an explicit import.
7
+ *
8
+ * `@glimmer/syntax`'s `isKeyword` does not (yet) include these, so the
9
+ * scope-registration pass would otherwise treat `{{eq}}`, `{{on}}`, etc. as
10
+ * free identifiers and ESLint's `no-undef` would flag them.
11
+ *
12
+ * Mirrors Glint's `KeywordsForEmber71` gate in
13
+ * `@glint/ember-tsc/types/-private/dsl/globals.d.ts`.
14
+ */
15
+ export const EMBER_71_BUILT_IN_KEYWORDS = new Set([
16
+ 'and',
17
+ 'array',
18
+ 'element',
19
+ 'eq',
20
+ 'fn',
21
+ 'gt',
22
+ 'gte',
23
+ 'hash',
24
+ 'lt',
25
+ 'lte',
26
+ 'neq',
27
+ 'not',
28
+ 'on',
29
+ 'or',
30
+ ]);
31
+
32
+ /**
33
+ * Returns `true` when `name` is one of the Ember 7.1 built-in template
34
+ * keywords AND the supplied `ember-source` version is >= 7.1.0.
35
+ *
36
+ * Pure — accepts the version string instead of reading the filesystem so it
37
+ * is trivially testable.
38
+ *
39
+ * @param {string} name
40
+ * @param {string | undefined | null} version
41
+ * @returns {boolean}
42
+ */
43
+ export function isEmber71BuiltInKeywordForVersion(name, version) {
44
+ if (!EMBER_71_BUILT_IN_KEYWORDS.has(name)) return false;
45
+ if (typeof version !== 'string') return false;
46
+
47
+ const [major, minor] = version.split('.').map(Number);
48
+ return major > 7 || (major === 7 && minor >= 1);
49
+ }
50
+
51
+ const require = createRequire(import.meta.url);
52
+
53
+ let cachedVersion;
54
+ let cachedVersionResolved = false;
55
+
56
+ function resolveEmberSourceVersion() {
57
+ if (cachedVersionResolved) return cachedVersion;
58
+ cachedVersionResolved = true;
59
+ try {
60
+ // ember-source is a peer of consumers, not of this parser.
61
+ // eslint-disable-next-line n/no-missing-require, import/no-unresolved
62
+ const pkg = require('ember-source/package.json');
63
+ cachedVersion = typeof pkg?.version === 'string' ? pkg.version : undefined;
64
+ } catch {
65
+ cachedVersion = undefined;
66
+ }
67
+ return cachedVersion;
68
+ }
69
+
70
+ /**
71
+ * Decide whether `name` should be treated as a built-in template keyword
72
+ * given the consumer's installed `ember-source`. Returns `false` when
73
+ * `ember-source` is not resolvable or its version is older than 7.1.0, so
74
+ * pre-7.1 projects keep their existing lint behavior and typos like
75
+ * `{{eq}}` continue to surface as `no-undef`.
76
+ *
77
+ * The probe is cached for the lifetime of the process so this is cheap
78
+ * to call from per-node scope registration.
79
+ *
80
+ * @param {string} name
81
+ * @returns {boolean}
82
+ */
83
+ export function isEmber71BuiltInKeyword(name) {
84
+ if (!EMBER_71_BUILT_IN_KEYWORDS.has(name)) return false;
85
+ return isEmber71BuiltInKeywordForVersion(name, resolveEmberSourceVersion());
86
+ }
87
+
88
+ /**
89
+ * Test-only: reset the cached `ember-source` version probe.
90
+ */
91
+ export function _resetEmberSourceVersionCache() {
92
+ cachedVersion = undefined;
93
+ cachedVersionResolved = false;
94
+ }
95
+
96
+ /**
97
+ * Test-only: force the resolved `ember-source` version. Pass `undefined`
98
+ * to clear and fall back to real probing on the next call.
99
+ *
100
+ * @param {string | undefined} version
101
+ */
102
+ export function _setEmberSourceVersionForTesting(version) {
103
+ cachedVersion = version;
104
+ cachedVersionResolved = true;
105
+ }
@@ -2,6 +2,7 @@ import { createRequire } from 'node:module';
2
2
  import { Preprocessor } from 'content-tag';
3
3
  import { isKeyword as glimmerIsKeyword } from '@glimmer/syntax';
4
4
  import { glimmerVisitorKeys } from 'ember-estree';
5
+ import { isEmber71BuiltInKeyword } from './ember71-built-in-keywords.js';
5
6
  import { Reference, Scope, Variable, Definition } from 'eslint-scope';
6
7
  import htmlTags from 'html-tags';
7
8
  import svgTags from 'svg-tags';
@@ -118,6 +119,16 @@ function registerPathExpression(node, path, scopeManager) {
118
119
  const name = node.head.name;
119
120
  if (glimmerIsKeyword(name)) return;
120
121
  const { scope, variable } = findVarInParentScopes(scopeManager, path, name) || {};
122
+ // `@glimmer/syntax`'s `isKeyword` table predates the Ember 7.1 built-in
123
+ // keywords (RFCs 389/470/560/561/562/997-1000). When the consumer is on
124
+ // ember-source >= 7.1, treat them as built-ins too so `no-undef` doesn't
125
+ // flag bare references like `{{eq}}` or `{{on "click" handler}}`.
126
+ //
127
+ // Only when there is NO local binding — a user-authored
128
+ // `import { eq } from 'ember-truth-helpers'` (or a `const eq = …`) must
129
+ // shadow the built-in so the scope reference is registered and the
130
+ // import is marked as used by `no-unused-vars`.
131
+ if (!variable && isEmber71BuiltInKeyword(name)) return;
121
132
  if (scope) {
122
133
  node.head.parent = node;
123
134
  registerNodeInScope(node.head, scope, variable);