forked from ember-cli/eslint-plugin-ember
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtemplate-no-this-in-template-only-components.js
More file actions
75 lines (72 loc) · 2.41 KB
/
template-no-this-in-template-only-components.js
File metadata and controls
75 lines (72 loc) · 2.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'disallow this in template-only components (gjs/gts)',
category: 'Best Practices',
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-this-in-template-only-components.md',
templateMode: 'both',
},
fixable: 'code',
schema: [],
messages: {
noThis:
"Usage of 'this' in path '{{original}}' is not allowed in a template-only component. Use '{{fixed}}' if it is a named argument.",
},
originallyFrom: {
name: 'ember-template-lint',
rule: 'lib/rules/no-this-in-template-only-components.js',
docs: 'docs/rule/no-this-in-template-only-components.md',
tests: 'test/unit/rules/no-this-in-template-only-components-test.js',
},
},
create(context) {
// Properties that should not be auto-fixed (built-in component properties)
const BUILTIN_PROPERTIES = new Set([
'action',
'element',
'parentView',
'attrs',
'elementId',
'tagName',
'ariaRole',
'class',
'classNames',
'classNameBindings',
'attributeBindings',
'isVisible',
'isDestroying',
'isDestroyed',
]);
return {
GlimmerPathExpression(node) {
// Only flag template-only components, not class components.
// Walk ancestors to check if the <template> is inside a class body.
const sourceCode = context.sourceCode;
const ancestors = sourceCode.getAncestors
? sourceCode.getAncestors(node)
: context.getAncestors();
const isInsideClass = ancestors.some(
(ancestor) => ancestor.type === 'ClassBody' || ancestor.type === 'ClassDeclaration'
);
if (isInsideClass) {
return;
}
// In gjs/gts files with <template> tags, check for this.* usage
if (node.head?.type === 'ThisHead' && node.tail?.length > 0) {
const original = node.original;
const firstPart = node.tail[0];
const fixed = `@${node.tail.join('.')}`;
const canFix = !BUILTIN_PROPERTIES.has(firstPart);
context.report({
node,
messageId: 'noThis',
data: { original, fixed },
fix: canFix ? (fixer) => fixer.replaceText(node, fixed) : undefined,
});
}
},
};
},
};