Skip to content

Commit 506418a

Browse files
Merge pull request #2591 from NullVoxPopuli/nvp/template-lint-extract-rule-template-no-valueless-arguments
Extract rule: template-no-valueless-arguments
2 parents 67480b6 + 48bb2a1 commit 506418a

4 files changed

Lines changed: 136 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ rules in templates can be disabled with eslint directives with mustache or html
255255
| [template-no-obsolete-elements](docs/rules/template-no-obsolete-elements.md) | disallow obsolete HTML elements | | | |
256256
| [template-no-outlet-outside-routes](docs/rules/template-no-outlet-outside-routes.md) | disallow {{outlet}} outside of route templates | | | |
257257
| [template-no-page-title-component](docs/rules/template-no-page-title-component.md) | disallow usage of ember-page-title component | | | |
258+
| [template-no-valueless-arguments](docs/rules/template-no-valueless-arguments.md) | disallow valueless named arguments | | | |
258259
| [template-no-whitespace-for-layout](docs/rules/template-no-whitespace-for-layout.md) | disallow using whitespace for layout purposes | | | |
259260
| [template-no-yield-block-params-to-else-inverse](docs/rules/template-no-yield-block-params-to-else-inverse.md) | disallow yielding block params to else or inverse block | | | |
260261
| [template-no-yield-only](docs/rules/template-no-yield-only.md) | disallow components that only yield | | | |
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# ember/template-no-valueless-arguments
2+
3+
<!-- end auto-generated rule header -->
4+
5+
Similar to HTML attributes, component arguments will default to an empty string when they are not explicitly assigned a value. This behavior isn't documented anywhere so depending on it isn't recommended and usually the result of a user-error. Since it _is_ valid syntax, accidental use of this behavior can be hard to detect and cause confusing bugs.
6+
7+
## Examples
8+
9+
This rule **forbids** the following:
10+
11+
```gjs
12+
<template><Editor @defaultText /></template>
13+
```
14+
15+
```gjs
16+
<template><SomeComponent @valuelessByAccident{{@canBeAModifier}} /></template>
17+
```
18+
19+
This rule **allows** the following:
20+
21+
```gjs
22+
<template><Editor @defaultText="" /></template>
23+
```
24+
25+
## Migration
26+
27+
Explicitly assign a value to all arguments.
28+
29+
## Related Rules
30+
31+
- [ember/no-quoteless-attributes](no-quoteless-attributes.md)
32+
33+
## References
34+
35+
- [RFC-311: angle bracket invocation](https://emberjs.github.io/rfcs/0311-angle-bracket-invocation.html)
36+
- [Attributes in the HTML specification](https://html.spec.whatwg.org/#syntax-attributes))
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/** @type {import('eslint').Rule.RuleModule} */
2+
function isNamedArgument(attrName) {
3+
return attrName.startsWith('@');
4+
}
5+
6+
module.exports = {
7+
meta: {
8+
type: 'problem',
9+
docs: {
10+
description: 'disallow valueless named arguments',
11+
category: 'Best Practices',
12+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-valueless-arguments.md',
13+
templateMode: 'both',
14+
},
15+
schema: [],
16+
messages: { valueless: 'Named arguments should have an explicitly assigned value.' },
17+
originallyFrom: {
18+
name: 'ember-template-lint',
19+
rule: 'lib/rules/no-valueless-arguments.js',
20+
docs: 'docs/rule/no-valueless-arguments.md',
21+
tests: 'test/unit/rules/no-valueless-arguments-test.js',
22+
},
23+
},
24+
create(context) {
25+
const sourceCode = context.sourceCode || context.getSourceCode();
26+
27+
return {
28+
GlimmerAttrNode(node) {
29+
if (!isNamedArgument(node.name)) {
30+
return;
31+
}
32+
33+
if (!sourceCode.getText(node).includes('=')) {
34+
context.report({ node, messageId: 'valueless' });
35+
}
36+
},
37+
};
38+
},
39+
};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
const rule = require('../../../lib/rules/template-no-valueless-arguments');
2+
const RuleTester = require('eslint').RuleTester;
3+
4+
const validHbs = [
5+
'<SomeComponent @emptyString="" data-test-some-component />',
6+
'<button type="submit" disabled {{on "click" this.submit}}></button>',
7+
];
8+
9+
const invalidHbs = [
10+
{
11+
code: '<SomeComponent @valueless />',
12+
output: null,
13+
errors: [{ messageId: 'valueless' }],
14+
},
15+
{
16+
code: '<SomeComponent @valuelessByAccident{{this.canBeAModifier}} />',
17+
output: null,
18+
errors: [{ messageId: 'valueless' }],
19+
},
20+
{
21+
code: '<SomeComponent @valuelessByAccident{{@canBeAModifier}} />',
22+
output: null,
23+
errors: [{ messageId: 'valueless' }],
24+
},
25+
];
26+
27+
function wrapTemplate(entry) {
28+
if (typeof entry === 'string') {
29+
return `<template>${entry}</template>`;
30+
}
31+
32+
return {
33+
...entry,
34+
code: `<template>${entry.code}</template>`,
35+
output: entry.output ? `<template>${entry.output}</template>` : entry.output,
36+
};
37+
}
38+
39+
const gjsRuleTester = new RuleTester({
40+
parser: require.resolve('ember-eslint-parser'),
41+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
42+
});
43+
44+
gjsRuleTester.run('template-no-valueless-arguments', rule, {
45+
valid: validHbs.map(wrapTemplate),
46+
invalid: invalidHbs.map(wrapTemplate),
47+
});
48+
49+
const hbsRuleTester = new RuleTester({
50+
parser: require.resolve('ember-eslint-parser/hbs'),
51+
parserOptions: {
52+
ecmaVersion: 2022,
53+
sourceType: 'module',
54+
},
55+
});
56+
57+
hbsRuleTester.run('template-no-valueless-arguments', rule, {
58+
valid: validHbs,
59+
invalid: invalidHbs,
60+
});

0 commit comments

Comments
 (0)