Skip to content

Commit 995560f

Browse files
Merge pull request #2479 from NullVoxPopuli/nvp/template-lint-extract-rule-template-no-model-argument-in-route-templates
Extract rule: template-no-model-argument-in-route-templates
2 parents e585210 + dc6d3be commit 995560f

4 files changed

Lines changed: 212 additions & 25 deletions

File tree

README.md

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -194,31 +194,32 @@ rules in templates can be disabled with eslint directives with mustache or html
194194

195195
### Best Practices
196196

197-
| Name                                       | Description | 💼 | 🔧 | 💡 |
198-
| :----------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------- | :- | :- | :- |
199-
| [template-builtin-component-arguments](docs/rules/template-builtin-component-arguments.md) | disallow setting certain attributes on builtin components | | | |
200-
| [template-no-action-modifiers](docs/rules/template-no-action-modifiers.md) | disallow usage of {{action}} modifiers | | | |
201-
| [template-no-arguments-for-html-elements](docs/rules/template-no-arguments-for-html-elements.md) | disallow @arguments on HTML elements | | | |
202-
| [template-no-array-prototype-extensions](docs/rules/template-no-array-prototype-extensions.md) | disallow usage of Ember Array prototype extensions | | | |
203-
| [template-no-block-params-for-html-elements](docs/rules/template-no-block-params-for-html-elements.md) | disallow block params on HTML elements | | | |
204-
| [template-no-capital-arguments](docs/rules/template-no-capital-arguments.md) | disallow capital arguments (use lowercase @arg instead of @Arg) | | | |
205-
| [template-no-chained-this](docs/rules/template-no-chained-this.md) | disallow redundant `this.this` in templates | | 🔧 | |
206-
| [template-no-debugger](docs/rules/template-no-debugger.md) | disallow {{debugger}} in templates | | | |
207-
| [template-no-element-event-actions](docs/rules/template-no-element-event-actions.md) | disallow element event actions (use {{on}} modifier instead) | | | |
208-
| [template-no-inline-event-handlers](docs/rules/template-no-inline-event-handlers.md) | disallow DOM event handler attributes | | | |
209-
| [template-no-inline-styles](docs/rules/template-no-inline-styles.md) | disallow inline styles | | | |
210-
| [template-no-input-placeholder](docs/rules/template-no-input-placeholder.md) | disallow placeholder attribute on input elements | | | |
211-
| [template-no-input-tagname](docs/rules/template-no-input-tagname.md) | disallow tagName attribute on {{input}} helper | | | |
212-
| [template-no-log](docs/rules/template-no-log.md) | disallow {{log}} in templates | | | |
213-
| [template-no-multiple-empty-lines](docs/rules/template-no-multiple-empty-lines.md) | disallow multiple consecutive empty lines in templates | | | |
214-
| [template-no-mut-helper](docs/rules/template-no-mut-helper.md) | disallow usage of (mut) helper | | | |
215-
| [template-no-negated-comparison](docs/rules/template-no-negated-comparison.md) | disallow negated comparisons in templates | | | |
216-
| [template-no-negated-condition](docs/rules/template-no-negated-condition.md) | disallow negated conditions in if/unless | | | |
217-
| [template-no-nested-splattributes](docs/rules/template-no-nested-splattributes.md) | disallow nested ...attributes usage | | | |
218-
| [template-no-obscure-array-access](docs/rules/template-no-obscure-array-access.md) | disallow obscure array access patterns like `objectPath.@each.property` | | | |
219-
| [template-no-obsolete-elements](docs/rules/template-no-obsolete-elements.md) | disallow obsolete HTML elements | | | |
220-
| [template-no-outlet-outside-routes](docs/rules/template-no-outlet-outside-routes.md) | disallow {{outlet}} outside of route templates | | | |
221-
| [template-no-page-title-component](docs/rules/template-no-page-title-component.md) | disallow usage of ember-page-title component | | | |
197+
| Name                                          | Description | 💼 | 🔧 | 💡 |
198+
| :----------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------- | :- | :- | :- |
199+
| [template-builtin-component-arguments](docs/rules/template-builtin-component-arguments.md) | disallow setting certain attributes on builtin components | | | |
200+
| [template-no-action-modifiers](docs/rules/template-no-action-modifiers.md) | disallow usage of {{action}} modifiers | | | |
201+
| [template-no-arguments-for-html-elements](docs/rules/template-no-arguments-for-html-elements.md) | disallow @arguments on HTML elements | | | |
202+
| [template-no-array-prototype-extensions](docs/rules/template-no-array-prototype-extensions.md) | disallow usage of Ember Array prototype extensions | | | |
203+
| [template-no-block-params-for-html-elements](docs/rules/template-no-block-params-for-html-elements.md) | disallow block params on HTML elements | | | |
204+
| [template-no-capital-arguments](docs/rules/template-no-capital-arguments.md) | disallow capital arguments (use lowercase @arg instead of @Arg) | | | |
205+
| [template-no-chained-this](docs/rules/template-no-chained-this.md) | disallow redundant `this.this` in templates | | 🔧 | |
206+
| [template-no-debugger](docs/rules/template-no-debugger.md) | disallow {{debugger}} in templates | | | |
207+
| [template-no-element-event-actions](docs/rules/template-no-element-event-actions.md) | disallow element event actions (use {{on}} modifier instead) | | | |
208+
| [template-no-inline-event-handlers](docs/rules/template-no-inline-event-handlers.md) | disallow DOM event handler attributes | | | |
209+
| [template-no-inline-styles](docs/rules/template-no-inline-styles.md) | disallow inline styles | | | |
210+
| [template-no-input-placeholder](docs/rules/template-no-input-placeholder.md) | disallow placeholder attribute on input elements | | | |
211+
| [template-no-input-tagname](docs/rules/template-no-input-tagname.md) | disallow tagName attribute on {{input}} helper | | | |
212+
| [template-no-log](docs/rules/template-no-log.md) | disallow {{log}} in templates | | | |
213+
| [template-no-model-argument-in-route-templates](docs/rules/template-no-model-argument-in-route-templates.md) | disallow @model argument in route templates | | 🔧 | |
214+
| [template-no-multiple-empty-lines](docs/rules/template-no-multiple-empty-lines.md) | disallow multiple consecutive empty lines in templates | | | |
215+
| [template-no-mut-helper](docs/rules/template-no-mut-helper.md) | disallow usage of (mut) helper | | | |
216+
| [template-no-negated-comparison](docs/rules/template-no-negated-comparison.md) | disallow negated comparisons in templates | | | |
217+
| [template-no-negated-condition](docs/rules/template-no-negated-condition.md) | disallow negated conditions in if/unless | | | |
218+
| [template-no-nested-splattributes](docs/rules/template-no-nested-splattributes.md) | disallow nested ...attributes usage | | | |
219+
| [template-no-obscure-array-access](docs/rules/template-no-obscure-array-access.md) | disallow obscure array access patterns like `objectPath.@each.property` | | | |
220+
| [template-no-obsolete-elements](docs/rules/template-no-obsolete-elements.md) | disallow obsolete HTML elements | | | |
221+
| [template-no-outlet-outside-routes](docs/rules/template-no-outlet-outside-routes.md) | disallow {{outlet}} outside of route templates | | | |
222+
| [template-no-page-title-component](docs/rules/template-no-page-title-component.md) | disallow usage of ember-page-title component | | | |
222223

223224
### Components
224225

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# ember/template-no-model-argument-in-route-templates
2+
3+
🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
4+
5+
> **HBS Only**: This rule applies to classic `.hbs` template files only (loose mode). It is not relevant for `gjs`/`gts` files (strict mode), where these patterns cannot occur.
6+
7+
<!-- end auto-generated rule header -->
8+
9+
In Ember route templates, the model should be accessed via `this.model` in the controller or component, not as an `@model` argument. The `@model` argument pattern is more appropriate for components. This rule primarily targets `.hbs` files in the `templates/` directory.
10+
11+
## Rule Details
12+
13+
This rule disallows the use of `@model` argument in route templates (`.hbs` files in `templates/` directory).
14+
15+
## Examples
16+
17+
Examples of **incorrect** code for this rule (in route templates):
18+
19+
```hbs
20+
<!-- app/templates/index.hbs -->
21+
{{@model}}
22+
```
23+
24+
```hbs
25+
<!-- app/templates/users.hbs -->
26+
{{@model.name}}
27+
```
28+
29+
```hbs
30+
<!-- app/templates/posts/show.hbs -->
31+
{{@model.id}}
32+
```
33+
34+
Examples of **correct** code for this rule:
35+
36+
```hbs
37+
<!-- app/templates/index.hbs -->
38+
{{this.model}}
39+
```
40+
41+
```hbs
42+
// app/components/user-card.gjs
43+
{{@model.name}}
44+
```
45+
46+
```hbs
47+
{{this.model}}
48+
```
49+
50+
## Migration
51+
52+
This rule includes a fixer in order to handle the migration for you automatically.
53+
54+
## References
55+
56+
- [eslint-plugin-ember template-no-model-argument-in-route-templates](https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/template-no-model-argument-in-route-templates.md)
57+
- [Ember Guides: Controllers](https://guides.emberjs.com/release/routing/controllers/)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/** @type {import('eslint').Rule.RuleModule} */
2+
module.exports = {
3+
meta: {
4+
type: 'suggestion',
5+
docs: {
6+
description: 'disallow @model argument in route templates',
7+
category: 'Best Practices',
8+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-model-argument-in-route-templates.md',
9+
templateMode: 'loose',
10+
},
11+
fixable: 'code',
12+
schema: [],
13+
messages: {
14+
noModelArgumentInRouteTemplates:
15+
'Unexpected @model in route template. Use this.model in the controller or component instead.',
16+
},
17+
originallyFrom: {
18+
name: 'ember-template-lint',
19+
rule: 'lib/rules/no-model-argument-in-route-templates.js',
20+
docs: 'docs/rule/no-model-argument-in-route-templates.md',
21+
tests: 'test/unit/rules/no-model-argument-in-route-templates-test.js',
22+
},
23+
},
24+
25+
create(context) {
26+
const filename = context.filename || context.getFilename();
27+
const isRouteTemplate =
28+
filename.includes('/templates/') &&
29+
!filename.includes('/components/') &&
30+
filename.endsWith('.hbs');
31+
const sourceCode = context.sourceCode || context.getSourceCode();
32+
33+
return {
34+
GlimmerPathExpression(node) {
35+
// Check for @model usage
36+
if (node.original === '@model' || node.original.startsWith('@model.')) {
37+
// Only report in route templates (hbs files in templates/ directory)
38+
if (isRouteTemplate) {
39+
const replacement = node.original.replace('@model', 'this.model');
40+
context.report({
41+
node,
42+
messageId: 'noModelArgumentInRouteTemplates',
43+
fix(fixer) {
44+
return fixer.replaceText(node, replacement);
45+
},
46+
});
47+
}
48+
}
49+
},
50+
};
51+
},
52+
};
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//------------------------------------------------------------------------------
2+
// Requirements
3+
//------------------------------------------------------------------------------
4+
5+
const rule = require('../../../lib/rules/template-no-model-argument-in-route-templates');
6+
const RuleTester = require('eslint').RuleTester;
7+
8+
const ruleTester = new RuleTester({
9+
parser: require.resolve('ember-eslint-parser'),
10+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
11+
});
12+
13+
ruleTester.run('template-no-model-argument-in-route-templates', rule, {
14+
valid: [
15+
{
16+
filename: 'app/templates/index.hbs',
17+
code: '<template>{{this.model}}</template>',
18+
},
19+
{
20+
filename: 'app/templates/users.hbs',
21+
code: '<template>{{@data}}</template>',
22+
},
23+
{
24+
filename: 'app/components/user-card.gjs',
25+
code: '<template>{{@model}}</template>',
26+
},
27+
28+
'<template>{{model}}</template>',
29+
'<template>{{@modelythingy}}</template>',
30+
],
31+
invalid: [
32+
{
33+
filename: 'app/templates/index.hbs',
34+
code: '<template>{{@model}}</template>',
35+
output: '<template>{{this.model}}</template>',
36+
errors: [{ messageId: 'noModelArgumentInRouteTemplates' }],
37+
},
38+
{
39+
filename: 'app/templates/users.hbs',
40+
code: '<template>{{@model.name}}</template>',
41+
output: '<template>{{this.model.name}}</template>',
42+
errors: [{ messageId: 'noModelArgumentInRouteTemplates' }],
43+
},
44+
{
45+
filename: 'app/templates/posts/show.hbs',
46+
code: '<template>{{@model.id}}</template>',
47+
output: '<template>{{this.model.id}}</template>',
48+
errors: [{ messageId: 'noModelArgumentInRouteTemplates' }],
49+
},
50+
51+
{
52+
filename: 'app/templates/index.hbs',
53+
code: '<template>{{@model.foo}}</template>',
54+
output: '<template>{{this.model.foo}}</template>',
55+
errors: [{ messageId: 'noModelArgumentInRouteTemplates' }],
56+
},
57+
{
58+
filename: 'app/templates/index.hbs',
59+
code: '<template>{{@model.foo.bar}}</template>',
60+
output: '<template>{{this.model.foo.bar}}</template>',
61+
errors: [{ messageId: 'noModelArgumentInRouteTemplates' }],
62+
},
63+
],
64+
});
65+
66+
const hbsRuleTester = new RuleTester({
67+
parser: require.resolve('ember-eslint-parser/hbs'),
68+
parserOptions: {
69+
ecmaVersion: 2022,
70+
sourceType: 'module',
71+
},
72+
});
73+
74+
hbsRuleTester.run('template-no-model-argument-in-route-templates', rule, {
75+
valid: ['{{model}}', '{{this.model}}', '{{@modelythingy}}', '{{@model}}'],
76+
invalid: [],
77+
});

0 commit comments

Comments
 (0)