Skip to content

Commit d14359e

Browse files
committed
Extract rule: template-template-length
1 parent a1378e5 commit d14359e

4 files changed

Lines changed: 293 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ rules in templates can be disabled with eslint directives with mustache or html
414414
| [template-linebreak-style](docs/rules/template-linebreak-style.md) | enforce consistent linebreaks in templates | | 🔧 | |
415415
| [template-modifier-name-case](docs/rules/template-modifier-name-case.md) | require dasherized names for modifiers | | 🔧 | |
416416
| [template-no-only-default-slot](docs/rules/template-no-only-default-slot.md) | disallow using only the default slot | | 🔧 | |
417+
| [template-template-length](docs/rules/template-template-length.md) | enforce template size constraints | | | |
417418

418419
### Testing
419420

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# ember/template-template-length
2+
3+
<!-- end auto-generated rule header -->
4+
5+
Enforce template size constraints.
6+
7+
Very long templates can indicate that markup should be split into smaller components.
8+
Very short templates can indicate that markup might be better inlined.
9+
10+
## Config
11+
12+
This rule accepts either:
13+
14+
- `true` / `false`
15+
- an object with:
16+
- `max` (integer): maximum allowed template length in lines
17+
- `min` (integer): minimum allowed template length in lines
18+
19+
Using `true` defaults to:
20+
21+
- `max: 200`
22+
- `min: 5`
23+
24+
## Examples
25+
26+
Examples of **incorrect** code for this rule:
27+
28+
```gjs
29+
<template>
30+
<div>short</div>
31+
</template>
32+
```
33+
34+
with config:
35+
36+
```json
37+
{ "min": 10 }
38+
```
39+
40+
Examples of **correct** code for this rule:
41+
42+
```gjs
43+
<template>
44+
<div>line 1</div>
45+
<div>line 2</div>
46+
<div>line 3</div>
47+
</template>
48+
```
49+
50+
## Related rules
51+
52+
- [eslint/max-lines](https://eslint.org/docs/rules/max-lines)
53+
54+
## References
55+
56+
- [eslint-plugin-ember template-length](https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/template-template-length.md)
57+
- [eslint/max-lines](https://eslint.org/docs/latest/rules/max-lines)
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
const DEFAULT_MAX_LENGTH = 200;
2+
const DEFAULT_MIN_LENGTH = 5;
3+
4+
const DEFAULT_CONFIG = {
5+
max: DEFAULT_MAX_LENGTH,
6+
min: DEFAULT_MIN_LENGTH,
7+
};
8+
9+
function isValidConfigObjectFormat(config) {
10+
for (const key of Object.keys(config)) {
11+
const value = config[key];
12+
13+
if (key !== 'min' && key !== 'max') {
14+
return false;
15+
}
16+
17+
if (!Number.isInteger(value)) {
18+
return false;
19+
}
20+
}
21+
22+
return true;
23+
}
24+
25+
function parseConfig(config) {
26+
const configType = typeof config;
27+
28+
switch (configType) {
29+
case 'boolean': {
30+
return config ? DEFAULT_CONFIG : {};
31+
}
32+
case 'object': {
33+
if (config === null) {
34+
break;
35+
}
36+
37+
if (isValidConfigObjectFormat(config)) {
38+
return config;
39+
}
40+
break;
41+
}
42+
case 'undefined': {
43+
return {};
44+
}
45+
// No default
46+
}
47+
48+
throw new Error(
49+
'The ember/template-template-length rule accepts: boolean, or object with integer "min" and/or "max" keys.'
50+
);
51+
}
52+
53+
/** @type {import('eslint').Rule.RuleModule} */
54+
module.exports = {
55+
meta: {
56+
type: 'suggestion',
57+
docs: {
58+
description: 'enforce template size constraints',
59+
category: 'Stylistic Issues',
60+
recommendedGjs: false,
61+
recommendedGts: false,
62+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-template-length.md',
63+
templateMode: 'both',
64+
},
65+
schema: [
66+
{
67+
oneOf: [
68+
{ type: 'boolean' },
69+
{
70+
type: 'object',
71+
properties: {
72+
min: { type: 'integer' },
73+
max: { type: 'integer' },
74+
},
75+
additionalProperties: false,
76+
},
77+
],
78+
},
79+
],
80+
messages: {
81+
tooLong: 'Template length of {{length}} exceeds {{max}}',
82+
tooShort: 'Template length of {{length}} is smaller than {{min}}',
83+
},
84+
originallyFrom: {
85+
name: 'ember-template-lint',
86+
rule: 'lib/rules/template-length.js',
87+
docs: 'docs/rule/template-length.md',
88+
tests: 'test/unit/rules/template-length-test.js',
89+
},
90+
},
91+
92+
create(context) {
93+
const config = parseConfig(context.options[0]);
94+
95+
if (!config.min && !config.max) {
96+
return {};
97+
}
98+
99+
return {
100+
'GlimmerTemplate:exit'(node) {
101+
const length = node.loc.end.line - node.loc.start.line + 1;
102+
103+
if (config.max && length > config.max) {
104+
context.report({
105+
node,
106+
messageId: 'tooLong',
107+
data: { length, max: config.max },
108+
});
109+
} else if (config.min && length < config.min) {
110+
context.report({
111+
node,
112+
messageId: 'tooShort',
113+
data: { length, min: config.min },
114+
});
115+
}
116+
},
117+
};
118+
},
119+
};
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
const rule = require('../../../lib/rules/template-template-length');
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+
9+
ruleTester.run('template-template-length', rule, {
10+
valid: [
11+
{
12+
code: `<template>
13+
one
14+
two
15+
</template>`,
16+
options: [{ max: 5 }],
17+
},
18+
{
19+
code: `<template>
20+
one
21+
two
22+
three
23+
</template>`,
24+
options: [{ min: 3 }],
25+
},
26+
{
27+
code: `<template>
28+
one
29+
</template>`,
30+
options: [false],
31+
},
32+
33+
`<template>testing this
34+
and
35+
this
36+
and his</template>`,
37+
],
38+
invalid: [
39+
{
40+
code: `<template>
41+
one
42+
two
43+
</template>`,
44+
output: null,
45+
options: [{ min: 10 }],
46+
errors: [{ message: 'Template length of 4 is smaller than 10' }],
47+
},
48+
{
49+
code: `<template>
50+
one
51+
two
52+
three
53+
</template>`,
54+
output: null,
55+
options: [{ max: 3 }],
56+
errors: [{ message: 'Template length of 5 exceeds 3' }],
57+
},
58+
],
59+
});
60+
61+
const hbsRuleTester = new RuleTester({
62+
parser: require.resolve('ember-eslint-parser/hbs'),
63+
parserOptions: {
64+
ecmaVersion: 2022,
65+
sourceType: 'module',
66+
},
67+
});
68+
69+
hbsRuleTester.run('template-template-length', rule, {
70+
valid: [
71+
`testing this
72+
and
73+
this
74+
and his`,
75+
`testing
76+
this
77+
`,
78+
`testing
79+
this
80+
and his
81+
`,
82+
`testing
83+
this
84+
andthis
85+
`,
86+
// Config: max option
87+
{
88+
code: 'testing\nthis\n',
89+
options: [{ max: 10 }],
90+
},
91+
// Config: min option
92+
{
93+
code: 'testing\nthis\nand\this\n',
94+
options: [{ min: 1 }],
95+
},
96+
// Config: min + max options
97+
{
98+
code: 'testing\nthis\nandthis\n',
99+
options: [{ min: 1, max: 5 }],
100+
},
101+
],
102+
invalid: [
103+
{
104+
code: 'testing\ntoo-short template\n',
105+
output: null,
106+
options: [{ min: 10 }],
107+
errors: [{ message: 'Template length of 3 is smaller than 10' }],
108+
},
109+
{
110+
code: 'test\nthis\nand\nthis\n',
111+
output: null,
112+
options: [{ max: 3 }],
113+
errors: [{ message: 'Template length of 5 exceeds 3' }],
114+
},
115+
],
116+
});

0 commit comments

Comments
 (0)