Skip to content

Commit bb1572a

Browse files
Bebersohlsindresorhus
authored andcommitted
Add escape-case rule - fixes #54 (#63)
1 parent 39b877f commit bb1572a

File tree

5 files changed

+182
-0
lines changed

5 files changed

+182
-0
lines changed

docs/rules/escape-case.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Require escape sequences to use uppercase values
2+
3+
Enforces defining escape sequence values with uppercase characters rather than lowercase ones. This promotes readability by making the escaped value more distinguishable from the identifier.
4+
5+
6+
## Fail
7+
8+
```js
9+
const foo = '\xa9';
10+
const foo = '\ud834';
11+
const foo = '\u{1d306}';
12+
const foo = '\ca';
13+
```
14+
15+
16+
## Pass
17+
18+
```js
19+
const foo = '\xA9';
20+
const foo = '\uD834';
21+
const foo = '\u{1D306}';
22+
const foo = '\cA';
23+
```

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ module.exports = {
2020
'unicorn/no-process-exit': 'error',
2121
'unicorn/throw-new-error': 'error',
2222
'unicorn/number-literal-case': 'error',
23+
'unicorn/escape-case': 'error',
2324
'unicorn/no-array-instanceof': 'error',
2425
'unicorn/no-new-buffer': 'error',
2526
'unicorn/no-hex-escape': 'error'

readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Configure it in `package.json`.
4040
"unicorn/no-process-exit": "error",
4141
"unicorn/throw-new-error": "error",
4242
"unicorn/number-literal-case": "error",
43+
"unicorn/escape-case": "error",
4344
"unicorn/no-array-instanceof": "error",
4445
"unicorn/no-new-buffer": "error",
4546
"unicorn/no-hex-escape": "error"
@@ -58,6 +59,7 @@ Configure it in `package.json`.
5859
- [no-process-exit](docs/rules/no-process-exit.md) - Disallow `process.exit()`.
5960
- [throw-new-error](docs/rules/throw-new-error.md) - Require `new` when throwing an error. *(fixable)*
6061
- [number-literal-case](docs/rules/number-literal-case.md) - Enforce lowercase identifier and uppercase value for number literals. *(fixable)*
62+
- [escape-case](docs/rules/escape-case.md) - Require escape sequences to use uppercase values. *(fixable)*
6163
- [no-array-instanceof](docs/rules/no-array-instanceof.md) - Require `Array.isArray()` instead of `instanceof Array`. *(fixable)*
6264
- [no-new-buffer](docs/rules/no-new-buffer.md) - Enforce the use of `Buffer.from()` and `Buffer.alloc()` instead of the deprecated `new Buffer()`. *(fixable)*
6365
- [no-hex-escape](docs/rules/no-hex-escape.md) - Enforce the use of unicode escapes instead of hexadecimal escapes. *(fixable)*

rules/escape-case.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
const escapeWithLowercase = /\\(x[a-f0-9]{2}|u[a-f0-9]{4}|u\{([0-9a-f]{1,})\}|c[a-z])/;
3+
const message = 'Use uppercase characters for the value of the escape sequence.';
4+
5+
const fix = value => {
6+
const results = escapeWithLowercase.exec(value);
7+
8+
if (results) {
9+
const fixedEscape = results[0].slice(0, 2) + results[0].slice(2).toUpperCase();
10+
return value.slice(0, results.index) + fixedEscape + value.slice(results.index + results[0].length);
11+
}
12+
13+
return value;
14+
};
15+
16+
const create = context => {
17+
return {
18+
Literal(node) {
19+
if (typeof node.value === 'string' && node.raw.match(escapeWithLowercase)) {
20+
context.report({
21+
node,
22+
message,
23+
fix: fixer => fixer.replaceTextRange([node.start, node.end], fix(node.raw))
24+
});
25+
}
26+
},
27+
TemplateElement(node) {
28+
if (typeof node.value.raw === 'string' && node.value.raw.match(escapeWithLowercase)) {
29+
context.report({
30+
node,
31+
message,
32+
fix: fixer => fixer.replaceTextRange([node.start, node.end], fix(node.value.raw))
33+
});
34+
}
35+
}
36+
};
37+
};
38+
39+
module.exports = {
40+
create,
41+
meta: {
42+
fixable: 'code'
43+
}
44+
};

test/escape-case.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/* eslint-disable no-template-curly-in-string */
2+
import test from 'ava';
3+
import avaRuleTester from 'eslint-ava-rule-tester';
4+
import rule from '../rules/escape-case';
5+
6+
const ruleTester = avaRuleTester(test, {
7+
env: {
8+
es6: true
9+
}
10+
});
11+
12+
const errors = [{
13+
ruleId: 'escape-case',
14+
message: 'Use uppercase characters for the value of the escape sequence.'
15+
}];
16+
17+
ruleTester.run('escape-case', rule, {
18+
valid: [
19+
'const foo = "\\xA9";',
20+
'const foo = "\\uD834";',
21+
'const foo = "\\u{1D306}";',
22+
'const foo = "\\cA";',
23+
'const foo = `\\xA9`;',
24+
'const foo = `\\uD834`;',
25+
'const foo = `\\u{1D306}`;',
26+
'const foo = `\\cA`;',
27+
'const foo = `\\uD834foo`;',
28+
'const foo = `foo\\uD834`;',
29+
'const foo = `foo \\uD834`;',
30+
'const foo = `${"\uD834 foo"} \\uD834`;',
31+
'const foo = "\\uD834foo";',
32+
'const foo = "foo\\uD834";',
33+
'const foo = "foo \\uD834";'
34+
],
35+
invalid: [
36+
{
37+
code: 'const foo = "\\xa9";',
38+
errors,
39+
output: 'const foo = "\\xA9";'
40+
},
41+
{
42+
code: 'const foo = "\\ud834";',
43+
errors,
44+
output: 'const foo = "\\uD834";'
45+
},
46+
{
47+
code: 'const foo = "\\u{1d306}";',
48+
errors,
49+
output: 'const foo = "\\u{1D306}";'
50+
},
51+
{
52+
code: 'const foo = "\\ca";',
53+
errors,
54+
output: 'const foo = "\\cA";'
55+
},
56+
{
57+
code: 'const foo = `\\xa9`;',
58+
errors,
59+
output: 'const foo = `\\xA9`;'
60+
},
61+
{
62+
code: 'const foo = `\\ud834`;',
63+
errors,
64+
output: 'const foo = `\\uD834`;'
65+
},
66+
{
67+
code: 'const foo = `\\u{1d306}`;',
68+
errors,
69+
output: 'const foo = `\\u{1D306}`;'
70+
},
71+
{
72+
code: 'const foo = `\\ca`;',
73+
errors,
74+
output: 'const foo = `\\cA`;'
75+
},
76+
{
77+
code: 'const foo = `\\ud834foo`;',
78+
errors,
79+
output: 'const foo = `\\uD834foo`;'
80+
},
81+
{
82+
code: 'const foo = `foo\\ud834`;',
83+
errors,
84+
output: 'const foo = `foo\\uD834`;'
85+
},
86+
{
87+
code: 'const foo = `foo \\ud834`;',
88+
errors,
89+
output: 'const foo = `foo \\uD834`;'
90+
},
91+
{
92+
code: 'const foo = `${"\ud834 foo"} \\ud834`;',
93+
errors,
94+
output: 'const foo = `${"\uD834 foo"} \\uD834`;'
95+
},
96+
{
97+
code: 'const foo = "\\ud834foo";',
98+
errors,
99+
output: 'const foo = "\\uD834foo";'
100+
},
101+
{
102+
code: 'const foo = "foo\\ud834";',
103+
errors,
104+
output: 'const foo = "foo\\uD834";'
105+
},
106+
{
107+
code: 'const foo = "foo \\ud834";',
108+
errors,
109+
output: 'const foo = "foo \\uD834";'
110+
}
111+
]
112+
});

0 commit comments

Comments
 (0)