Skip to content

Commit 204a095

Browse files
matijssindresorhus
authored andcommitted
Add no-hex-escape rule - fixes #51 (#62)
1 parent 0fc9e7d commit 204a095

File tree

5 files changed

+159
-1
lines changed

5 files changed

+159
-1
lines changed

docs/rules/no-hex-escape.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Enforce the use of unicode escapes instead of hexadecimal escapes
2+
3+
Enforces a convention of using [unicode escapes](https://mathiasbynens.be/notes/javascript-escapes#unicode) instead of [hexadecimal escapes](https://mathiasbynens.be/notes/javascript-escapes#hexadecimal) for consistency and clarity.
4+
5+
6+
## Fail
7+
8+
```js
9+
const foo = '\x1B';
10+
const foo = `\x1B${bar}`;
11+
```
12+
13+
14+
## Pass
15+
16+
```js
17+
const foo = '\u001B';
18+
const foo = `\u001B${bar}`;
19+
```

index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ module.exports = {
2121
'unicorn/throw-new-error': 'error',
2222
'unicorn/number-literal-case': 'error',
2323
'unicorn/no-array-instanceof': 'error',
24-
'unicorn/no-new-buffer': 'error'
24+
'unicorn/no-new-buffer': 'error',
25+
'unicorn/no-hex-escape': 'error'
2526
}
2627
}
2728
}

readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Configure it in `package.json`.
5959
- [number-literal-case](docs/rules/number-literal-case.md) - Enforce lowercase identifier and uppercase value for number literals. *(fixable)*
6060
- [no-array-instanceof](docs/rules/no-array-instanceof.md) - Require `Array.isArray()` instead of `instanceof Array`. *(fixable)*
6161
- [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)*
62+
- [no-hex-escape](docs/rules/no-hex-escape.md) - Enforce the use of unicode escapes instead of hexadecimal escapes. *(fixable)*
6263

6364

6465
## Recommended config

rules/no-hex-escape.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
function checkEscape(context, node, value) {
4+
const fixedValue = typeof value === 'string' ? value.replace(/\\x/g, '\\u00') : value;
5+
6+
if (value !== fixedValue) {
7+
context.report({
8+
node,
9+
message: 'Use unicode escapes instead of hexadecimal escapes.',
10+
fix: fixer => fixer.replaceTextRange([node.start, node.end], fixedValue)
11+
});
12+
}
13+
}
14+
15+
const create = context => {
16+
return {
17+
Literal: node => {
18+
checkEscape(context, node, node.raw);
19+
},
20+
TemplateElement: node => {
21+
checkEscape(context, node, node.value.raw);
22+
}
23+
};
24+
};
25+
26+
module.exports = {
27+
create,
28+
meta: {
29+
fixable: 'code'
30+
}
31+
};

test/no-hex-escape.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import test from 'ava';
2+
import avaRuleTester from 'eslint-ava-rule-tester';
3+
import rule from '../rules/no-hex-escape';
4+
5+
const ruleTester = avaRuleTester(test, {
6+
env: {
7+
es6: true
8+
}
9+
});
10+
11+
const error = {
12+
ruleId: 'no-hex-escape',
13+
message: 'Use unicode escapes instead of hexadecimal escapes.'
14+
};
15+
16+
ruleTester.run('no-hex-escape', rule, {
17+
valid: [
18+
`const foo = 'foo'`,
19+
`const foo = '\\u00b1'`,
20+
`const foo = '\\u00b1\\u00b1'`,
21+
`const foo = 'foo\\u00b1'`,
22+
`const foo = 'foo\\u00b1foo'`,
23+
`const foo = '\\u00b1foo'`,
24+
'const foo = 42',
25+
'const foo = `foo`',
26+
'const foo = `\\u00b1`',
27+
'const foo = `\\u00b1\\u00b1`',
28+
'const foo = `foo\\u00b1`',
29+
'const foo = `foo\\u00b1foo`',
30+
'const foo = `\\u00b1foo`',
31+
'const foo = `42`'
32+
],
33+
invalid: [
34+
{
35+
code: `const foo = '\\xb1'`,
36+
errors: [error],
37+
output: `const foo = '\\u00b1'`
38+
},
39+
{
40+
code: `const foo = '\\xb1\\xb1'`,
41+
errors: [error],
42+
output: `const foo = '\\u00b1\\u00b1'`
43+
},
44+
{
45+
code: `const foo = '\\xb1foo'`,
46+
errors: [error],
47+
output: `const foo = '\\u00b1foo'`
48+
},
49+
{
50+
code: `const foo = '\\xd8\\x3d\\xdc\\xa9'`,
51+
errors: [error],
52+
output: `const foo = '\\u00d8\\u003d\\u00dc\\u00a9'`
53+
},
54+
{
55+
code: `const foo = 'foo\\xb1'`,
56+
errors: [error],
57+
output: `const foo = 'foo\\u00b1'`
58+
},
59+
{
60+
code: `const foo = 'foo\\x12foo\\x34'`,
61+
errors: [error],
62+
output: `const foo = 'foo\\u0012foo\\u0034'`
63+
},
64+
{
65+
code: `const foo = '42\\x1242\\x34'`,
66+
errors: [error],
67+
output: `const foo = '42\\u001242\\u0034'`
68+
},
69+
// Test template literals
70+
{
71+
code: 'const foo = `\\xb1`',
72+
errors: [error],
73+
output: 'const foo = `\\u00b1`'
74+
},
75+
{
76+
code: 'const foo = `\\xb1\\xb1`',
77+
errors: [error],
78+
output: 'const foo = `\\u00b1\\u00b1`'
79+
},
80+
{
81+
code: 'const foo = `\\xb1foo`',
82+
errors: [error],
83+
output: 'const foo = `\\u00b1foo`'
84+
},
85+
{
86+
code: 'const foo = `\\xd8\\x3d\\xdc\\xa9`',
87+
errors: [error],
88+
output: 'const foo = `\\u00d8\\u003d\\u00dc\\u00a9`'
89+
},
90+
{
91+
code: 'const foo = `foo\\xb1`',
92+
errors: [error],
93+
output: 'const foo = `foo\\u00b1`'
94+
},
95+
{
96+
code: 'const foo = `foo\\x12foo\\x34`',
97+
errors: [error],
98+
output: 'const foo = `foo\\u0012foo\\u0034`'
99+
},
100+
{
101+
code: 'const foo = `42\\x1242\\x34`',
102+
errors: [error],
103+
output: 'const foo = `42\\u001242\\u0034`'
104+
}
105+
]
106+
});

0 commit comments

Comments
 (0)