Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions lib/rules/template-no-unnecessary-component-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,21 @@ module.exports = {

create(context) {
const sourceCode = context.sourceCode;
const filename = context.filename;
const isStrictMode = filename.endsWith('.gjs') || filename.endsWith('.gts');
let inAttribute = 0;

// In strict mode, a kebab-case / slash component name cannot become
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we know how to do this tho ;)

// a bare mustache invocation — the resulting identifier would not be
// a valid JS binding. Detect the rule's violation, but skip autofix
// when it would produce unparseable GJS/GTS output.
function canAutofix(componentName) {
if (!isStrictMode) {
return true;
}
return /^[$A-Z_a-z][\w$]*$/.test(componentName);
}

return {
GlimmerAttrNode() {
inAttribute++;
Expand All @@ -79,13 +92,14 @@ module.exports = {

const componentName = node.params[0].value || node.params[0].original;
const invocation = getComponentInvocationText(sourceCode, node, componentName);
context.report({
const report = {
node,
messageId: 'noUnnecessaryComponent',
fix(fixer) {
return fixer.replaceText(node, `{{${invocation}}}`);
},
});
};
if (canAutofix(componentName)) {
report.fix = (fixer) => fixer.replaceText(node, `{{${invocation}}}`);
}
context.report(report);
},

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

context.report({
const report = {
node,
messageId: 'noUnnecessaryComponent',
fix(fixer) {
};
if (canAutofix(componentName)) {
report.fix = (fixer) => {
const openInvocationEnd = getOpenInvocationEnd(node);
const closingPathEnd = node.range[1] - 2;
const closingPathStart = closingPathEnd - node.path.original.length;
Expand All @@ -111,8 +127,9 @@ module.exports = {
fixer.replaceTextRange([node.path.range[0], openInvocationEnd], invocation),
fixer.replaceTextRange([closingPathStart, closingPathEnd], componentName),
];
},
});
};
}
context.report(report);
},
};
},
Expand Down
25 changes: 24 additions & 1 deletion tests/lib/rules/template-no-unnecessary-component-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,30 @@ const gjsRuleTester = new RuleTester({

gjsRuleTester.run('template-no-unnecessary-component-helper', rule, {
valid: validGjs,
invalid: invalidHbs.map(wrapTemplate),
invalid: [
...invalidHbs.map(wrapTemplate),
// GJS/GTS: autofix is skipped when the component name isn't a valid JS
// identifier. The error is still reported so the user sees the issue.
{
filename: 'test.gjs',
code: '<template>{{component "my-component-name" foo=123}}</template>',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can autofix this, I think

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean like the angle-brackets-codemod?

Autofix inside here would produce undefined identifier without the import i think — Added noUnnecessaryComponentKebab messageId that tells the user the PascalCase equivalent (e.g. my-component → MyComponent) and points to the angle-brackets-codemod.

output: null,
errors: [{ messageId: 'noUnnecessaryComponent' }],
},
{
filename: 'test.gts',
code: '<template>{{#component "my-component-name"}}content{{/component}}</template>',
output: null,
errors: [{ messageId: 'noUnnecessaryComponent' }],
},
// GJS/GTS: valid JS identifier → autofix still applies
{
filename: 'test.gjs',
code: '<template>{{component "myComponent" foo=123}}</template>',
output: '<template>{{myComponent foo=123}}</template>',
errors: [{ messageId: 'noUnnecessaryComponent' }],
},
],
});

const hbsRuleTester = new RuleTester({
Expand Down
Loading