Skip to content

Commit 92d4939

Browse files
committed
Extract rule: template-no-forbidden-elements
1 parent 0149ef1 commit 92d4939

4 files changed

Lines changed: 159 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ rules in templates can be disabled with eslint directives with mustache or html
199199
| [template-no-capital-arguments](docs/rules/template-no-capital-arguments.md) | disallow capital arguments (use lowercase @arg instead of @Arg) | | | |
200200
| [template-no-debugger](docs/rules/template-no-debugger.md) | disallow {{debugger}} in templates | | | |
201201
| [template-no-element-event-actions](docs/rules/template-no-element-event-actions.md) | disallow element event actions (use {{on}} modifier instead) | | | |
202+
| [template-no-forbidden-elements](docs/rules/template-no-forbidden-elements.md) | disallow specific HTML elements | | | |
202203
| [template-no-log](docs/rules/template-no-log.md) | disallow {{log}} in templates | | | |
203204

204205
### Components
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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`, `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><html></html></template>
20+
```
21+
22+
```gjs
23+
<template><meta charset='utf-8' /></template>
24+
```
25+
26+
This rule **allows** the following:
27+
28+
```gjs
29+
<template><header></header></template>
30+
```
31+
32+
```gjs
33+
<template><div></div></template>
34+
```
35+
36+
```gjs
37+
<template>
38+
<head>
39+
<meta charset='utf-8' />
40+
</head>
41+
</template>
42+
```
43+
44+
Note: `<meta>` inside `<head>` is allowed as an exception.
45+
46+
## Configuration
47+
48+
- `boolean``true` to enable with defaults / `false` to disable
49+
- `string[]` — an array of element names to forbid (default: `['meta', 'html', 'script']`)
50+
51+
## References
52+
53+
- [Ember guides/template restrictions](https://guides.emberjs.com/release/components/#toc_restrictions)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
const DEFAULT_FORBIDDEN = ['meta', '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+
},
21+
create(context) {
22+
const rawConfig = context.options[0];
23+
let forbiddenList;
24+
25+
if (rawConfig === true || rawConfig === undefined) {
26+
forbiddenList = DEFAULT_FORBIDDEN;
27+
} else if (Array.isArray(rawConfig)) {
28+
forbiddenList = rawConfig;
29+
} else {
30+
forbiddenList = [];
31+
}
32+
33+
const forbidden = new Set(forbiddenList);
34+
35+
// Track element stack for <meta> in <head> exception
36+
const elementStack = [];
37+
38+
return {
39+
GlimmerElementNode(node) {
40+
elementStack.push(node.tag);
41+
42+
if (!forbidden.has(node.tag)) {
43+
return;
44+
}
45+
46+
// Exception: <meta> inside <head> is allowed
47+
if (node.tag === 'meta' && elementStack.includes('head')) {
48+
return;
49+
}
50+
51+
context.report({ node, messageId: 'forbidden', data: { element: node.tag } });
52+
},
53+
'GlimmerElementNode:exit'() {
54+
elementStack.pop();
55+
},
56+
};
57+
},
58+
};
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)