Skip to content

Commit 7765ff2

Browse files
committed
Extract rule: template-no-forbidden-elements
1 parent 484c3c6 commit 7765ff2

4 files changed

Lines changed: 169 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ rules in templates can be disabled with eslint directives with mustache or html
203203
| [template-no-chained-this](docs/rules/template-no-chained-this.md) | disallow redundant `this.this` in templates | | 🔧 | |
204204
| [template-no-debugger](docs/rules/template-no-debugger.md) | disallow {{debugger}} in templates | | | |
205205
| [template-no-element-event-actions](docs/rules/template-no-element-event-actions.md) | disallow element event actions (use {{on}} modifier instead) | | | |
206+
| [template-no-forbidden-elements](docs/rules/template-no-forbidden-elements.md) | disallow specific HTML elements | | | |
206207
| [template-no-inline-event-handlers](docs/rules/template-no-inline-event-handlers.md) | disallow DOM event handler attributes | | | |
207208
| [template-no-inline-styles](docs/rules/template-no-inline-styles.md) | disallow inline styles | | | |
208209
| [template-no-input-placeholder](docs/rules/template-no-input-placeholder.md) | disallow placeholder attribute on input elements | | | |
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# ember/template-no-forbidden-elements
2+
3+
<!-- end auto-generated rule header -->
4+
5+
This rule disallows the use of forbidden elements in template files.
6+
7+
The rule is configurable so teams can add their own disallowed elements.
8+
The default list of forbidden elements are `meta`, `style`, `html`, and `script`.
9+
10+
## Examples
11+
12+
This rule **forbids** the following:
13+
14+
```gjs
15+
<template><script></script></template>
16+
```
17+
18+
```gjs
19+
<template><style></style></template>
20+
```
21+
22+
```gjs
23+
<template><html></html></template>
24+
```
25+
26+
```gjs
27+
<template><meta charset='utf-8' /></template>
28+
```
29+
30+
This rule **allows** the following:
31+
32+
```gjs
33+
<template><header></header></template>
34+
```
35+
36+
```gjs
37+
<template><div></div></template>
38+
```
39+
40+
```gjs
41+
<template>
42+
<head>
43+
<meta charset='utf-8' />
44+
</head>
45+
</template>
46+
```
47+
48+
Note: `<meta>` inside `<head>` is allowed as an exception.
49+
50+
## Configuration
51+
52+
- `boolean``true` to enable with defaults / `false` to disable
53+
- `string[]` — an array of element names to forbid (default: `['meta', 'style', 'html', 'script']`)
54+
55+
## References
56+
57+
- [Ember guides/template restrictions](https://guides.emberjs.com/release/components/#toc_restrictions)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
const DEFAULT_FORBIDDEN = ['meta', 'style', 'html', 'script'];
2+
3+
/** @type {import('eslint').Rule.RuleModule} */
4+
module.exports = {
5+
meta: {
6+
type: 'suggestion',
7+
docs: {
8+
description: 'disallow specific HTML elements',
9+
category: 'Best Practices',
10+
recommendedGjs: false,
11+
recommendedGts: false,
12+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-forbidden-elements.md',
13+
},
14+
schema: [
15+
{
16+
oneOf: [{ type: 'array', items: { type: 'string' } }, { type: 'boolean' }],
17+
},
18+
],
19+
messages: { forbidden: 'Use of forbidden element <{{element}}>' },
20+
originallyFrom: {
21+
name: 'ember-template-lint',
22+
rule: 'lib/rules/no-forbidden-elements.js',
23+
docs: 'docs/rule/no-forbidden-elements.md',
24+
tests: 'test/unit/rules/no-forbidden-elements-test.js',
25+
},
26+
},
27+
create(context) {
28+
const rawConfig = context.options[0];
29+
let forbiddenList;
30+
31+
if (rawConfig === true || rawConfig === undefined) {
32+
forbiddenList = DEFAULT_FORBIDDEN;
33+
} else if (Array.isArray(rawConfig)) {
34+
forbiddenList = rawConfig;
35+
} else {
36+
forbiddenList = [];
37+
}
38+
39+
const forbidden = new Set(forbiddenList);
40+
41+
// Track element stack for <meta> in <head> exception
42+
const elementStack = [];
43+
44+
return {
45+
GlimmerElementNode(node) {
46+
elementStack.push(node.tag);
47+
48+
if (!forbidden.has(node.tag)) {
49+
return;
50+
}
51+
52+
// Exception: <meta> inside <head> is allowed
53+
if (node.tag === 'meta' && elementStack.includes('head')) {
54+
return;
55+
}
56+
57+
context.report({ node, messageId: 'forbidden', data: { element: node.tag } });
58+
},
59+
'GlimmerElementNode:exit'() {
60+
elementStack.pop();
61+
},
62+
};
63+
},
64+
};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
const rule = require('../../../lib/rules/template-no-forbidden-elements');
2+
const RuleTester = require('eslint').RuleTester;
3+
4+
const ruleTester = new RuleTester({
5+
parser: require.resolve('ember-eslint-parser'),
6+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
7+
});
8+
ruleTester.run('template-no-forbidden-elements', rule, {
9+
valid: [
10+
{ code: '<template><div></div></template>', options: [['script']] },
11+
// Test cases ported from ember-template-lint
12+
'<template><header></header></template>',
13+
'<template><footer></footer></template>',
14+
'<template><p></p></template>',
15+
'<template><head><meta charset="utf-8"></head></template>',
16+
],
17+
invalid: [
18+
{
19+
code: '<template><script></script></template>',
20+
output: null,
21+
options: [['script']],
22+
errors: [{ messageId: 'forbidden' }],
23+
},
24+
25+
// Test cases ported from ember-template-lint
26+
{
27+
code: '<template><html></html></template>',
28+
output: null,
29+
errors: [{ messageId: 'forbidden' }],
30+
},
31+
{
32+
code: '<template><style></style></template>',
33+
output: null,
34+
errors: [{ messageId: 'forbidden' }],
35+
},
36+
{
37+
code: '<template><meta charset="utf-8"></template>',
38+
output: null,
39+
errors: [{ messageId: 'forbidden' }],
40+
},
41+
{
42+
code: '<template><head><html></html></head></template>',
43+
output: null,
44+
errors: [{ messageId: 'forbidden' }],
45+
},
46+
],
47+
});

0 commit comments

Comments
 (0)