ember-eslint-parser 0.11.3 → 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
|
@@ -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
|
+
}
|
package/src/parser/transforms.js
CHANGED
|
@@ -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);
|