Skip to content

Commit 3a7cb52

Browse files
committed
refactor(template-no-obsolete-elements): use scope manager for block params
Replaces the manual block-param stack with sourceCode.getScope(node.parent) + a walk up the scope chain. GTS templates work today (36/37 tests pass). BLOCKED BY UPSTREAM: ember-tooling/ember-eslint-parser#189. The HBS parser currently builds an empty scope manager, so Glimmer block params aren't registered for .hbs files. The failing test `{{#let ... as |plaintext|}}<plaintext />` documents this gap and will start passing once PR 189 is merged and consumed here. Uses getScope(node.parent) rather than getScope(node) so an element's own `as |x|` params (attached to that element's block scope) don't shadow its own tag — e.g. `<marquee as |marquee|>` must still flag the outer <marquee>.
1 parent 44f81f7 commit 3a7cb52

File tree

1 file changed

+25
-30
lines changed

1 file changed

+25
-30
lines changed

lib/rules/template-no-obsolete-elements.js

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ const OBSOLETE = [
2929
'tt',
3030
'xmp',
3131
];
32+
33+
// BLOCKED BY UPSTREAM: ember-tooling/ember-eslint-parser#189
34+
// The HBS parser currently creates an empty scope manager, so Glimmer block
35+
// params (`{{#let ... as |x|}}` / `<Comp as |x|>`) are not registered in scope
36+
// for `.hbs` files. Until PR 189 lands and a release is consumed here, the
37+
// HBS test for `{{#let ... as |plaintext|}}<plaintext />` will fail. GTS
38+
// templates work today because the gjs/gts parser already registers block
39+
// params.
40+
3241
/** @type {import('eslint').Rule.RuleModule} */
3342
module.exports = {
3443
meta: {
@@ -50,42 +59,28 @@ module.exports = {
5059
},
5160
create(context) {
5261
const obsolete = new Set(OBSOLETE);
62+
const sourceCode = context.sourceCode;
5363

54-
// Manual block-param tracking is intentional here. In HBS mode the ESLint
55-
// scope manager does not register Glimmer block params (from
56-
// {{#let ... as |x|}} or <Comp as |x|>), so getScope() would not see them.
57-
// The push/pop stack handles both GlimmerBlockStatement and
58-
// GlimmerElementNode uniformly.
59-
const blockParamsInScope = [];
64+
function hasBindingInScopeChain(scope, name) {
65+
for (let s = scope; s; s = s.upper) {
66+
if (s.set && s.set.has(name)) return true;
67+
}
68+
return false;
69+
}
6070

6171
return {
62-
GlimmerBlockStatement(node) {
63-
const params = node.program?.blockParams || [];
64-
blockParamsInScope.push(...params);
65-
},
66-
'GlimmerBlockStatement:exit'(node) {
67-
const params = node.program?.blockParams || [];
68-
for (let i = 0; i < params.length; i++) {
69-
blockParamsInScope.pop();
70-
}
71-
},
7272
GlimmerElementNode(node) {
73-
// Check the element's own tag before pushing its block params, so an
74-
// element's own params don't shadow its own tag name (e.g.
75-
// `<marquee as |marquee|>` should still flag the outer <marquee>).
76-
if (!blockParamsInScope.includes(node.tag) && obsolete.has(node.tag)) {
77-
context.report({ node, messageId: 'obsolete', data: { element: node.tag } });
73+
if (!obsolete.has(node.tag)) {
74+
return;
7875
}
79-
// Element-level block params (e.g. `<Comp as |param|>`) are scoped to
80-
// the children, so push them after the obsolete check. Pop on exit.
81-
const elementParams = node.blockParams || [];
82-
blockParamsInScope.push(...elementParams);
83-
},
84-
'GlimmerElementNode:exit'(node) {
85-
const elementParams = node.blockParams || [];
86-
for (let i = 0; i < elementParams.length; i++) {
87-
blockParamsInScope.pop();
76+
// Use the parent's scope so that the element's own `as |x|` params
77+
// (which attach a block scope to this node) don't shadow its own tag
78+
// name. e.g. `<marquee as |marquee|>` must still flag the outer tag.
79+
const scope = sourceCode.getScope(node.parent);
80+
if (hasBindingInScopeChain(scope, node.tag)) {
81+
return;
8882
}
83+
context.report({ node, messageId: 'obsolete', data: { element: node.tag } });
8984
},
9085
};
9186
},

0 commit comments

Comments
 (0)