Skip to content

Commit f07f842

Browse files
NullVoxPopuliclaude
andcommitted
Fix: skip argument validation when builtin names are shadowed by JS imports
In gjs/gts files, when a developer imports a custom component with a name that matches an Ember built-in (Input, Textarea, LinkTo), the rule would incorrectly flag unknown arguments. This checks scope analysis to determine if the tag name resolves to a JS-scope variable with a definition (e.g. an import binding), and skips validation in that case. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent 3a0613f commit f07f842

2 files changed

Lines changed: 50 additions & 0 deletions

File tree

lib/rules/template-no-unknown-arguments-for-builtin-components.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,8 @@ module.exports = {
364364
},
365365

366366
create(context) {
367+
const sourceCode = context.sourceCode;
368+
367369
return {
368370
GlimmerElementNode(node) {
369371
if (!node.tag || !node.attributes) {
@@ -375,6 +377,22 @@ module.exports = {
375377
return;
376378
}
377379

380+
// In gjs/gts, if the tag name resolves to a JS-scope variable with a
381+
// definition (e.g. an import or local variable), then it shadows the
382+
// Ember built-in component — skip validation.
383+
if (node.parent && node.parts && node.parts[0]) {
384+
const scope = sourceCode.getScope(node.parent);
385+
const isShadowed = scope.references.some(
386+
(ref) =>
387+
ref.identifier === node.parts[0] &&
388+
ref.resolved &&
389+
ref.resolved.defs.length > 0
390+
);
391+
if (isShadowed) {
392+
return;
393+
}
394+
}
395+
378396
const seen = [];
379397
const warns = [];
380398

tests/lib/rules/template-no-unknown-arguments-for-builtin-components.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,38 @@ ruleTester.run('template-no-unknown-arguments-for-builtin-components', rule, {
3939
'<template><LinkTo @query={{hash foo=bar}} /></template>',
4040
'<template><LinkTo @model={{this.model}} /></template>',
4141
'<template><LinkTo @models={{array comment.photo comment}} /></template>',
42+
43+
// Custom component imported with the same name as a built-in — should NOT be flagged
44+
{
45+
filename: 'my-component.gjs',
46+
code: `
47+
import Input from './my-custom-input';
48+
<template><Input @unknownArg="x" /></template>
49+
`,
50+
},
51+
{
52+
filename: 'my-component.gjs',
53+
code: `
54+
import Textarea from './my-custom-textarea';
55+
<template><Textarea @customProp={{this.val}} /></template>
56+
`,
57+
},
58+
{
59+
filename: 'my-component.gjs',
60+
code: `
61+
import LinkTo from './my-custom-link';
62+
<template><LinkTo @anythingGoes="yes" /></template>
63+
`,
64+
},
65+
{
66+
filename: 'my-component.gjs',
67+
code: `
68+
import Input from './my-custom-input';
69+
export default class MyComponent {
70+
<template><Input @customArg="value" @anotherArg={{this.foo}} /></template>
71+
}
72+
`,
73+
},
4274
],
4375

4476
invalid: [

0 commit comments

Comments
 (0)