Skip to content

Commit 4dbe5ac

Browse files
committed
Fix template-no-unnecessary-component-helper: skip invalid-identifier autofix in GJS/GTS
The rule converts {{component "my-component" ...}} to {{my-component ...}}. In GJS/GTS, a kebab-case component name is not a valid JS identifier, so the autofix would produce unparseable output. Keep detection (the user is still told to invoke directly), but skip the autofix in strict mode when the component name contains non-identifier characters.
1 parent b705850 commit 4dbe5ac

2 files changed

Lines changed: 50 additions & 10 deletions

File tree

lib/rules/template-no-unnecessary-component-helper.js

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,21 @@ module.exports = {
5959

6060
create(context) {
6161
const sourceCode = context.sourceCode;
62+
const filename = context.filename;
63+
const isStrictMode = filename.endsWith('.gjs') || filename.endsWith('.gts');
6264
let inAttribute = 0;
6365

66+
// In strict mode, a kebab-case / slash component name cannot become
67+
// a bare mustache invocation — the resulting identifier would not be
68+
// a valid JS binding. Detect the rule's violation, but skip autofix
69+
// when it would produce unparseable GJS/GTS output.
70+
function canAutofix(componentName) {
71+
if (!isStrictMode) {
72+
return true;
73+
}
74+
return /^[$A-Z_a-z][\w$]*$/.test(componentName);
75+
}
76+
6477
return {
6578
GlimmerAttrNode() {
6679
inAttribute++;
@@ -79,13 +92,14 @@ module.exports = {
7992

8093
const componentName = node.params[0].value || node.params[0].original;
8194
const invocation = getComponentInvocationText(sourceCode, node, componentName);
82-
context.report({
95+
const report = {
8396
node,
8497
messageId: 'noUnnecessaryComponent',
85-
fix(fixer) {
86-
return fixer.replaceText(node, `{{${invocation}}}`);
87-
},
88-
});
98+
};
99+
if (canAutofix(componentName)) {
100+
report.fix = (fixer) => fixer.replaceText(node, `{{${invocation}}}`);
101+
}
102+
context.report(report);
89103
},
90104

91105
GlimmerBlockStatement(node) {
@@ -99,10 +113,12 @@ module.exports = {
99113
const componentName = node.params[0].value || node.params[0].original;
100114
const invocation = getComponentInvocationText(sourceCode, node, componentName);
101115

102-
context.report({
116+
const report = {
103117
node,
104118
messageId: 'noUnnecessaryComponent',
105-
fix(fixer) {
119+
};
120+
if (canAutofix(componentName)) {
121+
report.fix = (fixer) => {
106122
const openInvocationEnd = getOpenInvocationEnd(node);
107123
const closingPathEnd = node.range[1] - 2;
108124
const closingPathStart = closingPathEnd - node.path.original.length;
@@ -111,8 +127,9 @@ module.exports = {
111127
fixer.replaceTextRange([node.path.range[0], openInvocationEnd], invocation),
112128
fixer.replaceTextRange([closingPathStart, closingPathEnd], componentName),
113129
];
114-
},
115-
});
130+
};
131+
}
132+
context.report(report);
116133
},
117134
};
118135
},

tests/lib/rules/template-no-unnecessary-component-helper.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,30 @@ const gjsRuleTester = new RuleTester({
9191

9292
gjsRuleTester.run('template-no-unnecessary-component-helper', rule, {
9393
valid: validGjs,
94-
invalid: invalidHbs.map(wrapTemplate),
94+
invalid: [
95+
...invalidHbs.map(wrapTemplate),
96+
// GJS/GTS: autofix is skipped when the component name isn't a valid JS
97+
// identifier. The error is still reported so the user sees the issue.
98+
{
99+
filename: 'test.gjs',
100+
code: '<template>{{component "my-component-name" foo=123}}</template>',
101+
output: null,
102+
errors: [{ messageId: 'noUnnecessaryComponent' }],
103+
},
104+
{
105+
filename: 'test.gts',
106+
code: '<template>{{#component "my-component-name"}}content{{/component}}</template>',
107+
output: null,
108+
errors: [{ messageId: 'noUnnecessaryComponent' }],
109+
},
110+
// GJS/GTS: valid JS identifier → autofix still applies
111+
{
112+
filename: 'test.gjs',
113+
code: '<template>{{component "myComponent" foo=123}}</template>',
114+
output: '<template>{{myComponent foo=123}}</template>',
115+
errors: [{ messageId: 'noUnnecessaryComponent' }],
116+
},
117+
],
95118
});
96119

97120
const hbsRuleTester = new RuleTester({

0 commit comments

Comments
 (0)