Skip to content

Commit c57882e

Browse files
nzakasbmeckmdjermanovic
authored
feat: rule no-unnormalized-keys (#63)
* feat: rule no-unormalized-keys Fixes #32 * Add no-unnormalized-keys to plugin exports * Cleanup tests and refactor for clarity * Output key into error message and update tests * Update README * Switch to object option * Re-add eslint.test.js * Update src/rules/no-unnormalized-keys.js Co-authored-by: Milos Djermanovic <[email protected]> * Update src/rules/no-unnormalized-keys.js Co-authored-by: Milos Djermanovic <[email protected]> --------- Co-authored-by: Bradley Meck Farias <[email protected]> Co-authored-by: Milos Djermanovic <[email protected]>
1 parent ede448d commit c57882e

File tree

4 files changed

+148
-0
lines changed

4 files changed

+148
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ export default [
155155
- `no-duplicate-keys` - warns when there are two keys in an object with the same text.
156156
- `no-empty-keys` - warns when there is a key in an object that is an empty string or contains only whitespace (note: `package-lock.json` uses empty keys intentionally)
157157
- `no-unsafe-values` - warns on values that are unsafe for interchange, such as numbers outside safe range or lone surrogates.
158+
- `no-unnormalized-keys` - warns on keys containing [unnormalized characters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize#description). You can optionally specify the normalization form via `{ form: "form_name" }`, where `form_name` can be any of `"NFC"`, `"NFD"`, `"NFKC"`, or `"NFKD"`.
158159

159160
## Configuration Comments
160161

src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { JSONSourceCode } from "./languages/json-source-code.js";
1212
import noDuplicateKeys from "./rules/no-duplicate-keys.js";
1313
import noEmptyKeys from "./rules/no-empty-keys.js";
1414
import noUnsafeValues from "./rules/no-unsafe-values.js";
15+
import noUnnormalizedKeys from "./rules/no-unnormalized-keys.js";
1516

1617
//-----------------------------------------------------------------------------
1718
// Plugin
@@ -31,6 +32,7 @@ const plugin = {
3132
"no-duplicate-keys": noDuplicateKeys,
3233
"no-empty-keys": noEmptyKeys,
3334
"no-unsafe-values": noUnsafeValues,
35+
"no-unnormalized-keys": noUnnormalizedKeys,
3436
},
3537
configs: {
3638
recommended: {
@@ -39,6 +41,7 @@ const plugin = {
3941
"json/no-duplicate-keys": "error",
4042
"json/no-empty-keys": "error",
4143
"json/no-unsafe-values": "error",
44+
"json/no-unnormalized-keys": "error",
4245
}),
4346
},
4447
},

src/rules/no-unnormalized-keys.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* @fileoverview Rule to detect unnormalized keys in JSON.
3+
* @author Bradley Meck Farias
4+
*/
5+
6+
export default {
7+
meta: {
8+
type: /** @type {const} */ ("problem"),
9+
10+
docs: {
11+
description: "Disallow JSON keys that are not normalized",
12+
},
13+
14+
messages: {
15+
unnormalizedKey: "Unnormalized key '{{key}}' found.",
16+
},
17+
18+
schema: [
19+
{
20+
type: "object",
21+
properties: {
22+
form: {
23+
enum: ["NFC", "NFD", "NFKC", "NFKD"],
24+
},
25+
},
26+
additionalProperties: false,
27+
},
28+
],
29+
},
30+
31+
create(context) {
32+
const normalization = context.options.length
33+
? text => text.normalize(context.options[0].form)
34+
: text => text.normalize();
35+
return {
36+
Member(node) {
37+
const key =
38+
node.name.type === "String"
39+
? node.name.value
40+
: node.name.name;
41+
42+
if (normalization(key) !== key) {
43+
context.report({
44+
loc: node.name.loc,
45+
messageId: "unnormalizedKey",
46+
data: {
47+
key,
48+
},
49+
});
50+
}
51+
},
52+
};
53+
},
54+
};
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* @fileoverview Tests for no-unnormalized-keys rule.
3+
* @author Bradley Meck Farias
4+
*/
5+
6+
//------------------------------------------------------------------------------
7+
// Imports
8+
//------------------------------------------------------------------------------
9+
10+
import rule from "../../src/rules/no-unnormalized-keys.js";
11+
import json from "../../src/index.js";
12+
import { RuleTester } from "eslint";
13+
14+
//------------------------------------------------------------------------------
15+
// Tests
16+
//------------------------------------------------------------------------------
17+
18+
const ruleTester = new RuleTester({
19+
plugins: {
20+
json,
21+
},
22+
language: "json/json",
23+
});
24+
25+
const o = "\u1E9B\u0323";
26+
27+
ruleTester.run("no-unnormalized-keys", rule, {
28+
valid: [
29+
`{"${o}":"NFC"}`,
30+
{
31+
code: `{"${o}":"NFC"}`,
32+
options: [{ form: "NFC" }],
33+
},
34+
{
35+
code: `{"${o.normalize("NFD")}":"NFD"}`,
36+
options: [{ form: "NFD" }],
37+
},
38+
{
39+
code: `{"${o.normalize("NFKC")}":"NFKC"}`,
40+
options: [{ form: "NFKC" }],
41+
},
42+
{
43+
code: `{"${o.normalize("NFKD")}":"NFKD"}`,
44+
options: [{ form: "NFKD" }],
45+
},
46+
],
47+
invalid: [
48+
{
49+
code: `{"${o.normalize("NFD")}":"NFD"}`,
50+
errors: [
51+
{
52+
messageId: "unnormalizedKey",
53+
data: { key: o.normalize("NFD") },
54+
line: 1,
55+
column: 2,
56+
endLine: 1,
57+
endColumn: 7,
58+
},
59+
],
60+
},
61+
{
62+
code: `{${o.normalize("NFD")}:"NFD"}`,
63+
language: "json/json5",
64+
errors: [
65+
{
66+
messageId: "unnormalizedKey",
67+
data: { key: o.normalize("NFD") },
68+
line: 1,
69+
column: 2,
70+
endLine: 1,
71+
endColumn: 5,
72+
},
73+
],
74+
},
75+
{
76+
code: `{"${o.normalize("NFKC")}":"NFKC"}`,
77+
options: [{ form: "NFKD" }],
78+
errors: [
79+
{
80+
messageId: "unnormalizedKey",
81+
data: { key: o.normalize("NFKC") },
82+
line: 1,
83+
column: 2,
84+
endLine: 1,
85+
endColumn: 5,
86+
},
87+
],
88+
},
89+
],
90+
});

0 commit comments

Comments
 (0)