@@ -92,6 +92,8 @@ module.exports = {
9292 return { } ;
9393 }
9494
95+ const sourceCode = context . sourceCode ;
96+
9597 // Track block params in a scope stack so yielded names are not flagged.
9698 const blockParamScopes = [ ] ;
9799
@@ -112,6 +114,37 @@ module.exports = {
112114 return false ;
113115 }
114116
117+ /**
118+ * In gjs/gts, check whether a name resolves to a JS-scope variable
119+ * (import, const, let, function param, etc.). If it does, it's a local
120+ * binding and should be exempt from restriction checks — same as block params.
121+ */
122+ function isJsScopeVariable ( node ) {
123+ if ( ! sourceCode ) return false ;
124+
125+ try {
126+ if ( node . type === 'GlimmerElementNode' ) {
127+ // Element nodes use parts[0] for scope lookup, and need parent scope
128+ if ( ! node . parts || ! node . parts [ 0 ] ) return false ;
129+ const scope = sourceCode . getScope ( node . parent ) ;
130+ const ref = scope . references . find ( ( r ) => r . identifier === node . parts [ 0 ] ) ;
131+ // Only exempt if the reference actually resolves to a JS variable definition
132+ return ref != null && ref . resolved != null ;
133+ }
134+
135+ // For mustache/block/sub/modifier statements, check the path's head
136+ if ( node . path && node . path . head ) {
137+ const scope = sourceCode . getScope ( node ) ;
138+ const ref = scope . references . find ( ( r ) => r . identifier === node . path . head ) ;
139+ return ref != null && ref . resolved != null ;
140+ }
141+ } catch {
142+ // sourceCode.getScope may not be available in .hbs-only mode; ignore.
143+ }
144+
145+ return false ;
146+ }
147+
115148 function isRestricted ( name ) {
116149 for ( const item of config ) {
117150 if ( typeof item === 'string' ) {
@@ -209,6 +242,14 @@ module.exports = {
209242 return ;
210243 }
211244
245+ // In gjs/gts, skip if the tag resolves to a JS-scope variable (import, const, etc.)
246+ if ( isJsScopeVariable ( node ) ) {
247+ if ( node . blockParams && node . blockParams . length > 0 ) {
248+ pushBlockParams ( node . blockParams ) ;
249+ }
250+ return ;
251+ }
252+
212253 const name = getComponentOrHelperName ( node ) ;
213254 if ( name && ! isBlockParam ( name ) ) {
214255 const result = isRestricted ( name ) ;
@@ -236,6 +277,7 @@ module.exports = {
236277 modifier . path . original ;
237278 if ( ! modName ) continue ;
238279 if ( isBlockParam ( modName ) ) continue ;
280+ if ( isJsScopeVariable ( modifier ) ) continue ;
239281
240282 const modResult = isRestricted ( modName ) ;
241283 if ( modResult . restricted ) {
@@ -264,6 +306,9 @@ module.exports = {
264306 if ( isBlockParam ( name ) ) {
265307 return ;
266308 }
309+ if ( isJsScopeVariable ( node ) ) {
310+ return ;
311+ }
267312
268313 const result = isRestricted ( name ) ;
269314 if ( result . restricted ) {
@@ -278,7 +323,7 @@ module.exports = {
278323
279324 GlimmerBlockStatement ( node ) {
280325 const name = getComponentOrHelperName ( node ) ;
281- if ( name && ! isBlockParam ( name ) ) {
326+ if ( name && ! isBlockParam ( name ) && ! isJsScopeVariable ( node ) ) {
282327 const result = isRestricted ( name ) ;
283328 if ( result . restricted ) {
284329 context . report ( {
@@ -310,6 +355,9 @@ module.exports = {
310355 if ( isBlockParam ( name ) ) {
311356 return ;
312357 }
358+ if ( isJsScopeVariable ( node ) ) {
359+ return ;
360+ }
313361
314362 const result = isRestricted ( name ) ;
315363 if ( result . restricted ) {
@@ -329,6 +377,9 @@ module.exports = {
329377 if ( isBlockParam ( name ) ) {
330378 return ;
331379 }
380+ if ( isJsScopeVariable ( node ) ) {
381+ return ;
382+ }
332383
333384 const result = isRestricted ( name ) ;
334385 if ( result . restricted ) {
0 commit comments