Skip to content

Commit 03afb9b

Browse files
NullVoxPopulibmish
andauthored
Add new rule no-at-ember-render-modifiers (#1902)
* Implementation -- needs tests * Rename lint * Add tests, run update commands * Add docs * Lint docs * Update docs/rules/no-at-ember-render-modifiers.md Co-authored-by: Bryan Mishkin <[email protected]> * Use more specific import path check * Change category to Deprecations * Add link to ember-template-lint * Run update script --------- Co-authored-by: Bryan Mishkin <[email protected]>
1 parent 8d70560 commit 03afb9b

File tree

4 files changed

+158
-0
lines changed

4 files changed

+158
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ module.exports = {
101101
| [closure-actions](docs/rules/closure-actions.md) | enforce usage of closure actions || | |
102102
| [new-module-imports](docs/rules/new-module-imports.md) | enforce using "New Module Imports" from Ember RFC #176 || | |
103103
| [no-array-prototype-extensions](docs/rules/no-array-prototype-extensions.md) | disallow usage of Ember's `Array` prototype extensions | | 🔧 | |
104+
| [no-at-ember-render-modifiers](docs/rules/no-at-ember-render-modifiers.md) | disallow importing from @ember/render-modifiers | | | |
104105
| [no-deprecated-router-transition-methods](docs/rules/no-deprecated-router-transition-methods.md) | enforce usage of router service transition methods | | 🔧 | |
105106
| [no-function-prototype-extensions](docs/rules/no-function-prototype-extensions.md) | disallow usage of Ember's `function` prototype extensions || | |
106107
| [no-implicit-injections](docs/rules/no-implicit-injections.md) | enforce usage of implicit service injections | | 🔧 | |
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# ember/no-at-ember-render-modifiers
2+
3+
<!-- end auto-generated rule header -->
4+
5+
What's wrong with `{{did-insert}}`, `{{did-update}}`, and `{{will-destroy}}`?
6+
7+
These modifiers were meant for temporary migration purposes for quickly migrating `@ember/component` from before Octane to the `@glimmer/component` we have today. Since `@ember/component` implicitly had a wrapping `div` around each component and `@glimmer/component`s have "outer HTML" semantics, an automated migration could have ended up looking something like:
8+
9+
```hbs
10+
<div
11+
{{did-insert this.doInsertBehavior}}
12+
{{did-update this.doUpdateBehavior @arg1 @arg2}}
13+
{{will-destroy this.doDestroyBehavior}}
14+
>
15+
...
16+
</div>
17+
```
18+
19+
It was intended that this would be a temporary step to help get folks off of `@ember/components` quickly in early 2020 when folks migrated to the Octane editon, but some folks continued using these modifiers.
20+
21+
Additionally, this style of mananging data flow has some flaws:
22+
23+
- an element is required
24+
- this can be mitigated by using helpers, but they have the same problems mentioned below
25+
- the behavior that is used with these modifiers can cause extra renders and infinite rendering loops
26+
- this is the nature of "effect"-driven development / data-flow, every time an effect runs, rendering must re-occur.
27+
- behavior that needs to be co-located is spread out, making maintenance and debugging harder
28+
- each part of the responsibility of a "behavior" or "feature" is spread out, making it harder to find and comprehend the full picture of that behavior or feature.
29+
30+
## Examples
31+
32+
This rule **forbids** the following:
33+
34+
```js
35+
import didInsert from '@ember/render-modifiers/modifiers/did-insert';
36+
```
37+
38+
```js
39+
import didUpdate from '@ember/render-modifiers/modifiers/did-update';
40+
```
41+
42+
```js
43+
import willDestroy from '@ember/render-modifiers/modifiers/will-destroy';
44+
```
45+
46+
For more examples, see [the docs on ember-template-lint](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/no-at-ember-render-modifiers.md).
47+
48+
## References
49+
50+
- [Editions](https://emberjs.com/editions/)
51+
- [Octane Upgrade Guide](https://guides.emberjs.com/release/upgrading/current-edition/)
52+
- [Component Documentation](https://guides.emberjs.com/release/components/)
53+
- [Avoiding Lifecycle in Component](https://nullvoxpopuli.com/avoiding-lifecycle)
54+
- [The `ember-template-lint` version of this rule](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/no-at-ember-render-modifiers.md)
55+
- [`ember-modifier`](https://github.com/ember-modifier/ember-modifier)
56+
- [`@ember/render-modifiers`](https://github.com/emberjs/ember-render-modifiers) (deprecated)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use strict';
2+
3+
const ERROR_MESSAGE =
4+
'Do not use @ember/render-modifiers. Instead, use derived data patterns, and/or co-locate destruction via @ember/destroyable';
5+
//------------------------------------------------------------------------------
6+
// Rule Definition
7+
//------------------------------------------------------------------------------
8+
9+
function importDeclarationIsPackageName(node, path) {
10+
return node.source.value === path || node.source.value.startsWith(`${path}/`);
11+
}
12+
13+
/** @type {import('eslint').Rule.RuleModule} */
14+
module.exports = {
15+
meta: {
16+
type: 'suggestion',
17+
docs: {
18+
description: 'disallow importing from @ember/render-modifiers',
19+
category: 'Deprecations',
20+
recommended: false,
21+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/no-at-ember-render-modifiers.md',
22+
},
23+
fixable: null,
24+
schema: [],
25+
},
26+
27+
ERROR_MESSAGE,
28+
29+
create(context) {
30+
return {
31+
ImportDeclaration(node) {
32+
if (importDeclarationIsPackageName(node, '@ember/render-modifiers')) {
33+
context.report({
34+
node,
35+
message: ERROR_MESSAGE,
36+
});
37+
}
38+
},
39+
};
40+
},
41+
};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//------------------------------------------------------------------------------
2+
// Requirements
3+
//------------------------------------------------------------------------------
4+
5+
const rule = require('../../../lib/rules/no-at-ember-render-modifiers');
6+
const RuleTester = require('eslint').RuleTester;
7+
8+
const { ERROR_MESSAGE } = rule;
9+
10+
//------------------------------------------------------------------------------
11+
// Tests
12+
//------------------------------------------------------------------------------
13+
14+
const ruleTester = new RuleTester({
15+
parserOptions: {
16+
ecmaVersion: 2020,
17+
sourceType: 'module',
18+
},
19+
});
20+
ruleTester.run('no-at-ember-render-modifiers', rule, {
21+
valid: [
22+
'import x from "x"',
23+
'',
24+
"import { x } from 'foo';",
25+
"import x from '@ember/foo';",
26+
"import x from '@ember/render-modifiers-foo';",
27+
],
28+
invalid: [
29+
{
30+
code: 'import "@ember/render-modifiers";',
31+
output: null,
32+
errors: [{ message: ERROR_MESSAGE, type: 'ImportDeclaration' }],
33+
},
34+
{
35+
code: 'import x from "@ember/render-modifiers";',
36+
output: null,
37+
errors: [{ message: ERROR_MESSAGE, type: 'ImportDeclaration' }],
38+
},
39+
{
40+
code: 'import { x } from "@ember/render-modifiers";',
41+
output: null,
42+
errors: [{ message: ERROR_MESSAGE, type: 'ImportDeclaration' }],
43+
},
44+
{
45+
code: 'import didInsert from "@ember/render-modifiers/modifiers/did-insert";',
46+
output: null,
47+
errors: [{ message: ERROR_MESSAGE, type: 'ImportDeclaration' }],
48+
},
49+
{
50+
code: 'import didUpdate from "@ember/render-modifiers/modifiers/did-update";',
51+
output: null,
52+
errors: [{ message: ERROR_MESSAGE, type: 'ImportDeclaration' }],
53+
},
54+
{
55+
code: 'import willDestroy from "@ember/render-modifiers/modifiers/will-destroy";',
56+
output: null,
57+
errors: [{ message: ERROR_MESSAGE, type: 'ImportDeclaration' }],
58+
},
59+
],
60+
});

0 commit comments

Comments
 (0)