Skip to content

Commit 1496ad3

Browse files
committed
Extract rule: template-linebreak-style
1 parent 9837162 commit 1496ad3

4 files changed

Lines changed: 246 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ rules in templates can be disabled with eslint directives with mustache or html
386386
| [order-in-models](docs/rules/order-in-models.md) | enforce proper order of properties in models | | 🔧 | |
387387
| [order-in-routes](docs/rules/order-in-routes.md) | enforce proper order of properties in routes | | 🔧 | |
388388
| [template-attribute-order](docs/rules/template-attribute-order.md) | enforce consistent ordering of attributes in template elements | | | |
389+
| [template-linebreak-style](docs/rules/template-linebreak-style.md) | enforce consistent linebreaks in templates | | 🔧 | |
389390
| [template-no-only-default-slot](docs/rules/template-no-only-default-slot.md) | disallow using only the default slot | | 🔧 | |
390391

391392
### Testing
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# ember/template-linebreak-style
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+
<!-- end auto-generated rule header -->
6+
7+
Enforce consistent linebreaks in templates.
8+
9+
Having consistent linebreaks is important to make sure that the source code is rendered correctly in editors.
10+
11+
## Rule Details
12+
13+
This rule enforces consistent line endings in templates, independent of the operating system.
14+
15+
## Config
16+
17+
This rule accepts a single string option:
18+
19+
- `"unix"` (default) — enforces the usage of Unix line endings: `\n` for LF
20+
- `"windows"` — enforces the usage of Windows line endings: `\r\n` for CRLF
21+
- `"system"` — enforces the usage of the current platform's line ending
22+
23+
## Examples
24+
25+
Examples of **incorrect** code with the default `"unix"` config:
26+
27+
```hbs
28+
<div>test</div>\r\n
29+
```
30+
31+
Examples of **correct** code with the default `"unix"` config:
32+
33+
```hbs
34+
<div>test</div>\n
35+
```
36+
37+
Examples of **incorrect** code with the `"windows"` config:
38+
39+
```hbs
40+
<div>test</div>\n
41+
```
42+
43+
Examples of **correct** code with the `"windows"` config:
44+
45+
```hbs
46+
<div>test</div>\r\n
47+
```
48+
49+
## Related Rules
50+
51+
- [linebreak-style](https://eslint.org/docs/rules/linebreak-style) from eslint
52+
53+
## References
54+
55+
- [ember-template-lint linebreak-style](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/linebreak-style.md)
56+
- [Git/line endings](https://docs.github.com/en/github/using-git/configuring-git-to-handle-line-endings)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
function toDisplay(value) {
2+
return value.replaceAll('\r', 'CR').replaceAll('\n', 'LF');
3+
}
4+
5+
/** @type {import('eslint').Rule.RuleModule} */
6+
module.exports = {
7+
meta: {
8+
type: 'layout',
9+
docs: {
10+
description: 'enforce consistent linebreaks in templates',
11+
category: 'Stylistic Issues',
12+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-linebreak-style.md',
13+
templateMode: 'both',
14+
},
15+
fixable: 'whitespace',
16+
schema: [
17+
{
18+
enum: ['unix', 'windows', 'system'],
19+
},
20+
],
21+
messages: {
22+
wrongLinebreak: 'Wrong linebreak used. Expected {{expected}} but found {{found}}.',
23+
},
24+
originallyFrom: {
25+
name: 'ember-template-lint',
26+
rule: 'lib/rules/linebreak-style.js',
27+
docs: 'docs/rule/linebreak-style.md',
28+
tests: 'test/unit/rules/linebreak-style-test.js',
29+
},
30+
},
31+
32+
create(context) {
33+
const os = require('node:os');
34+
const option = context.options[0] || 'unix';
35+
let expectedLinebreak;
36+
if (option === 'system') {
37+
expectedLinebreak = os.EOL;
38+
} else if (option === 'windows') {
39+
expectedLinebreak = '\r\n';
40+
} else {
41+
expectedLinebreak = '\n';
42+
}
43+
44+
const sourceCode = context.getSourceCode();
45+
46+
return {
47+
'GlimmerTemplate:exit'(node) {
48+
const text = sourceCode.getText(node);
49+
const re = /\r\n?|\n/g;
50+
let match;
51+
52+
while ((match = re.exec(text)) !== null) {
53+
const found = match[0];
54+
if (found !== expectedLinebreak) {
55+
const startIndex = node.range[0] + match.index;
56+
context.report({
57+
loc: {
58+
start: sourceCode.getLocFromIndex(startIndex),
59+
end: sourceCode.getLocFromIndex(startIndex + found.length),
60+
},
61+
messageId: 'wrongLinebreak',
62+
data: {
63+
expected: toDisplay(expectedLinebreak),
64+
found: toDisplay(found),
65+
},
66+
fix(fixer) {
67+
return fixer.replaceTextRange(
68+
[startIndex, startIndex + found.length],
69+
expectedLinebreak
70+
);
71+
},
72+
});
73+
}
74+
}
75+
},
76+
};
77+
},
78+
};
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//------------------------------------------------------------------------------
2+
// Requirements
3+
//------------------------------------------------------------------------------
4+
5+
const rule = require('../../../lib/rules/template-linebreak-style');
6+
const RuleTester = require('eslint').RuleTester;
7+
8+
//------------------------------------------------------------------------------
9+
// Tests
10+
//------------------------------------------------------------------------------
11+
12+
const ruleTester = new RuleTester({
13+
parser: require.resolve('ember-eslint-parser'),
14+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
15+
});
16+
17+
ruleTester.run('template-linebreak-style', rule, {
18+
valid: [
19+
// default 'unix' — LF only
20+
'<template>\n<div>test</div>\n</template>',
21+
],
22+
23+
invalid: [
24+
{
25+
code: '<template>\r\n<div>test</div>\r\n</template>',
26+
output: '<template>\n<div>test</div>\n</template>',
27+
options: ['unix'],
28+
errors: [{ messageId: 'wrongLinebreak' }, { messageId: 'wrongLinebreak' }],
29+
},
30+
],
31+
});
32+
33+
const hbsRuleTester = new RuleTester({
34+
parser: require.resolve('ember-eslint-parser/hbs'),
35+
parserOptions: {
36+
ecmaVersion: 2022,
37+
sourceType: 'module',
38+
},
39+
});
40+
41+
hbsRuleTester.run('template-linebreak-style', rule, {
42+
valid: [
43+
// default 'unix' — all LF
44+
'testing this',
45+
'testing \n this',
46+
{ code: 'testing\nthis', options: ['unix'] },
47+
// windows — all CRLF
48+
{ code: 'testing\r\nthis', options: ['windows'] },
49+
// no linebreaks at all
50+
'single line no linebreaks',
51+
],
52+
53+
invalid: [
54+
// default 'unix' — mixed linebreaks, first is LF so CRLF is wrong
55+
{
56+
code: 'something\ngoes\r\n',
57+
output: 'something\ngoes\n',
58+
options: ['unix'],
59+
errors: [{ messageId: 'wrongLinebreak' }],
60+
},
61+
// unix — CRLF standalone
62+
{
63+
code: '\r\n',
64+
output: '\n',
65+
options: ['unix'],
66+
errors: [{ messageId: 'wrongLinebreak' }],
67+
},
68+
// unix — CRLF in block mustache
69+
{
70+
code: '{{#if test}}\r\n{{/if}}',
71+
output: '{{#if test}}\n{{/if}}',
72+
options: ['unix'],
73+
errors: [{ messageId: 'wrongLinebreak' }],
74+
},
75+
// unix — CRLF between mustaches
76+
{
77+
code: '{{blah}}\r\n{{blah}}',
78+
output: '{{blah}}\n{{blah}}',
79+
options: ['unix'],
80+
errors: [{ messageId: 'wrongLinebreak' }],
81+
},
82+
// unix — CRLF trailing
83+
{
84+
code: '{{blah}}\r\n',
85+
output: '{{blah}}\n',
86+
options: ['unix'],
87+
errors: [{ messageId: 'wrongLinebreak' }],
88+
},
89+
// unix — CRLF in attribute value
90+
{
91+
code: '{{blah arg="\r\n"}}',
92+
output: '{{blah arg="\n"}}',
93+
options: ['unix'],
94+
errors: [{ messageId: 'wrongLinebreak' }],
95+
},
96+
// unix — CRLF in element attribute
97+
{
98+
code: '<blah arg="\r\n" />',
99+
output: '<blah arg="\n" />',
100+
options: ['unix'],
101+
errors: [{ messageId: 'wrongLinebreak' }],
102+
},
103+
// windows — LF is wrong
104+
{
105+
code: '\n',
106+
output: '\r\n',
107+
options: ['windows'],
108+
errors: [{ messageId: 'wrongLinebreak' }],
109+
},
110+
],
111+
});

0 commit comments

Comments
 (0)