Skip to content

Commit 67480b6

Browse files
Merge pull request #2592 from NullVoxPopuli/nvp/template-lint-extract-rule-template-no-whitespace-for-layout
Extract rule: template-no-whitespace-for-layout
2 parents 9801f32 + 03b283b commit 67480b6

4 files changed

Lines changed: 178 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ rules in templates can be disabled with eslint directives with mustache or html
255255
| [template-no-obsolete-elements](docs/rules/template-no-obsolete-elements.md) | disallow obsolete HTML elements | | | |
256256
| [template-no-outlet-outside-routes](docs/rules/template-no-outlet-outside-routes.md) | disallow {{outlet}} outside of route templates | | | |
257257
| [template-no-page-title-component](docs/rules/template-no-page-title-component.md) | disallow usage of ember-page-title component | | | |
258+
| [template-no-whitespace-for-layout](docs/rules/template-no-whitespace-for-layout.md) | disallow using whitespace for layout purposes | | | |
258259
| [template-no-yield-block-params-to-else-inverse](docs/rules/template-no-yield-block-params-to-else-inverse.md) | disallow yielding block params to else or inverse block | | | |
259260
| [template-no-yield-only](docs/rules/template-no-yield-only.md) | disallow components that only yield | | | |
260261
| [template-no-yield-to-default](docs/rules/template-no-yield-to-default.md) | disallow yield to default block | | | |
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# ember/template-no-whitespace-for-layout
2+
3+
<!-- end auto-generated rule header -->
4+
5+
Formatting of text through the use of multiple whitespace is entirely visual, and therefore is incompatible with screen-reading assistive technology tools.
6+
7+
The rule applies to the content of Handlebars AST TextNodes, and performs a RegExp search for two consecutive white space characters that might indicate the use of whitespace used for layout.
8+
9+
## Examples
10+
11+
This rule **forbids** the following:
12+
13+
```gjs
14+
<template>
15+
Mon.&nbsp;&nbsp;&nbsp;&nbsp;Eggs&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Tomato soup&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;House salad<br>
16+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bacon&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hamburger&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Fried chicken<br>
17+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Toast&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Onion rings&nbsp;&nbsp;&nbsp;&nbsp;Green beans<br>
18+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Cookie&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Mashed potatoes
19+
</template>
20+
```
21+
22+
This rule **allows** the following:
23+
24+
```gjs
25+
<template>
26+
<p>Start to finish</p>
27+
</template>
28+
```
29+
30+
```gjs
31+
<template>
32+
<p>Start&nbsp;to&nbsp;Finish</p>
33+
</template>
34+
```
35+
36+
## Migration
37+
38+
To fix issues caused by using whitespace for layout, the following are recommended:
39+
40+
- use the appropriate HTML markup to contain the information
41+
- use CSS to add padding or margins to the semantic HTML markup
42+
43+
## References
44+
45+
- [F33: Using white space characters to create multiple columns in plain text content](https://www.w3.org/TR/WCAG20-TECHS/failures.html#F33)
46+
- [F34: Using white space characters to format tables in plain text content](https://www.w3.org/TR/WCAG20-TECHS/failures.html#F34)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/** @type {import('eslint').Rule.RuleModule} */
2+
const ERROR_MESSAGE = 'Excess whitespace detected.';
3+
4+
module.exports = {
5+
meta: {
6+
type: 'suggestion',
7+
docs: {
8+
description: 'disallow using whitespace for layout purposes',
9+
category: 'Best Practices',
10+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-whitespace-for-layout.md',
11+
templateMode: 'both',
12+
},
13+
schema: [],
14+
messages: {
15+
noWhitespaceForLayout: ERROR_MESSAGE,
16+
},
17+
originallyFrom: {
18+
name: 'ember-template-lint',
19+
rule: 'lib/rules/no-whitespace-for-layout.js',
20+
docs: 'docs/rule/no-whitespace-for-layout.md',
21+
tests: 'test/unit/rules/no-whitespace-for-layout-test.js',
22+
},
23+
},
24+
25+
create(context) {
26+
const sourceCode = context.getSourceCode();
27+
28+
return {
29+
GlimmerTextNode(node) {
30+
const text = sourceCode.getText(node);
31+
if (!text) {
32+
return;
33+
}
34+
35+
const lines = text.split('\n');
36+
for (const line of lines) {
37+
// Ignore whitespace at the start and end of the line
38+
const trimmed = line.trim();
39+
40+
// Check for two consecutive ` ` or `&nbsp;` in a row
41+
if (/(( )|(&nbsp;))(( )|(&nbsp;))/.test(trimmed)) {
42+
context.report({
43+
node,
44+
messageId: 'noWhitespaceForLayout',
45+
});
46+
return;
47+
}
48+
}
49+
},
50+
};
51+
},
52+
};
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//------------------------------------------------------------------------------
2+
// Requirements
3+
//------------------------------------------------------------------------------
4+
5+
const rule = require('../../../lib/rules/template-no-whitespace-for-layout');
6+
const RuleTester = require('eslint').RuleTester;
7+
8+
const validHbs = [
9+
'Start to finish',
10+
'Start&nbsp;to&nbsp;finish',
11+
'Start<br>to<br>finish',
12+
`<div>
13+
example
14+
</div>`,
15+
`<div
16+
foo="bar"
17+
baz="qux"
18+
>
19+
example
20+
</div>`,
21+
];
22+
23+
const invalidHbs = [
24+
{
25+
code: 'START FINISH',
26+
output: null,
27+
errors: [{ message: 'Excess whitespace detected.' }],
28+
},
29+
{
30+
code: 'START&nbsp;&nbsp;FINISH',
31+
output: null,
32+
errors: [{ message: 'Excess whitespace detected.' }],
33+
},
34+
{
35+
code: 'START&nbsp; FINISH',
36+
output: null,
37+
errors: [{ message: 'Excess whitespace detected.' }],
38+
},
39+
{
40+
code: 'START &nbsp;FINISH',
41+
output: null,
42+
errors: [{ message: 'Excess whitespace detected.' }],
43+
},
44+
];
45+
46+
function wrapTemplate(entry) {
47+
if (typeof entry === 'string') {
48+
return `<template>${entry}</template>`;
49+
}
50+
51+
return {
52+
...entry,
53+
code: `<template>${entry.code}</template>`,
54+
output: entry.output ? `<template>${entry.output}</template>` : entry.output,
55+
};
56+
}
57+
58+
const gjsRuleTester = new RuleTester({
59+
parser: require.resolve('ember-eslint-parser'),
60+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
61+
});
62+
63+
gjsRuleTester.run('template-no-whitespace-for-layout', rule, {
64+
valid: validHbs.map(wrapTemplate),
65+
invalid: invalidHbs.map(wrapTemplate),
66+
});
67+
68+
const hbsRuleTester = new RuleTester({
69+
parser: require.resolve('ember-eslint-parser/hbs'),
70+
parserOptions: {
71+
ecmaVersion: 2022,
72+
sourceType: 'module',
73+
},
74+
});
75+
76+
hbsRuleTester.run('template-no-whitespace-for-layout', rule, {
77+
valid: validHbs,
78+
invalid: invalidHbs,
79+
});

0 commit comments

Comments
 (0)