Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/rules/no-duplicate-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
* @author Nicholas C. Zakas
*/

//-----------------------------------------------------------------------------
// Imports
//-----------------------------------------------------------------------------

import { getKey, getRawKey } from "../util.js";

//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -49,23 +55,22 @@ const rule = {
},

Member(node) {
const key =
node.name.type === "String"
? node.name.value
: node.name.name;
const key = getKey(node);
const rawKey = getRawKey(node, context.sourceCode);

if (keys.has(key)) {
context.report({
loc: node.name.loc,
messageId: "duplicateKey",
data: {
key,
key: rawKey,
},
});
} else {
keys.set(key, node);
}
},

"Object:exit"() {
keys = objectKeys.pop();
},
Expand Down
11 changes: 7 additions & 4 deletions src/rules/no-empty-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
* @author Nicholas C. Zakas
*/

//-----------------------------------------------------------------------------
// Imports
//-----------------------------------------------------------------------------

import { getKey } from "../util.js";

//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -37,10 +43,7 @@ const rule = {
create(context) {
return {
Member(node) {
const key =
node.name.type === "String"
? node.name.value
: node.name.name;
const key = getKey(node);

if (key.trim() === "") {
context.report({
Expand Down
10 changes: 6 additions & 4 deletions src/rules/no-unnormalized-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
* @fileoverview Rule to detect unnormalized keys in JSON.
* @author Bradley Meck Farias
*/
//-----------------------------------------------------------------------------
// Imports
//-----------------------------------------------------------------------------

import { getKey } from "../util.js";

//-----------------------------------------------------------------------------
// Type Definitions
Expand Down Expand Up @@ -58,10 +63,7 @@ const rule = {

return {
Member(node) {
const key =
node.name.type === "String"
? node.name.value
: node.name.name;
const key = getKey(node);

if (key.normalize(form) !== key) {
context.report({
Expand Down
14 changes: 2 additions & 12 deletions src/rules/sort-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//-----------------------------------------------------------------------------

import naturalCompare from "natural-compare";
import { getKey } from "../util.js";

//-----------------------------------------------------------------------------
// Type Definitions
Expand Down Expand Up @@ -76,17 +77,6 @@ const comparators = {
},
};

/**
* Gets the MemberNode's string key value.
* @param {MemberNode} member
* @return {string}
*/
function getKey(member) {
return member.name.type === "Identifier"
? member.name.name
: member.name.value;
}

//-----------------------------------------------------------------------------
// Rule Definition
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -114,7 +104,7 @@ const rule = {

messages: {
sortKeys:
"Expected object keys to be in {{sortName}} case-{{sensitivity}} {{direction}} order. '{{thisName}}' should be before '{{prevName}}'.",
"Expected object keys to be in {{sortName}} case-{{sensitivity}} {{direction}} order. '{{thisName}}' should be before '{{prevName}}'.", // TODO
},

schema: [
Expand Down
38 changes: 38 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* @fileoverview Utility Library
* @author 루밀LuMir(lumirlumir)
*/

//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------

/**
* @import { MemberNode } from "@humanwhocodes/momoa";
* @import { JSONSourceCode } from "./languages/json-source-code.js";
*/

//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------

/**
* Gets the `MemberNode`'s key value.
* @param {MemberNode} node The node to get the key from.
* @returns {string} The key value.
*/
export function getKey(node) {
return node.name.type === "String" ? node.name.value : node.name.name;
}

/**
* Gets the `MemberNode`'s raw key value.
* @param {MemberNode} node The node to get the raw key from.
* @param {JSONSourceCode} sourceCode The JSON source code object.
* @returns {string} The raw key value.
*/
export function getRawKey(node, sourceCode) {
return node.name.type === "String"
? sourceCode.getText(node.name, -1, -1)
: sourceCode.getText(node.name);
}
63 changes: 59 additions & 4 deletions tests/rules/no-duplicate-keys.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,67 @@ ruleTester.run("no-duplicate-keys", rule, {
},
],
},
{
code: '{"foot": 1, "fo\\u006ft": 2}',
errors: [
{
messageId: "duplicateKey",
data: { key: "fo\\u006ft" },
line: 1,
column: 13,
endLine: 1,
endColumn: 24,
},
],
},
{
code: '{"foot": 1, "fo\\u006ft": 2}',
language: "json/jsonc",
errors: [
{
messageId: "duplicateKey",
data: { key: "fo\\u006ft" },
line: 1,
column: 13,
endLine: 1,
endColumn: 24,
},
],
},
{
code: '{"foot": 1, "fo\\u006ft": 2}',
language: "json/json5",
errors: [
{
messageId: "duplicateKey",
data: { key: "fo\\u006ft" },
line: 1,
column: 13,
endLine: 1,
endColumn: 24,
},
],
},
{
code: "{foot: 1, fo\\u006ft: 2}",
language: "json/json5",
errors: [
{
messageId: "duplicateKey",
data: { key: "fo\\u006ft" },
line: 1,
column: 11,
endLine: 1,
endColumn: 20,
},
],
},
{
code: '{"f\\u006fot": 1, "fo\\u006ft": 2}',
errors: [
{
messageId: "duplicateKey",
data: { key: "foot" },
data: { key: "fo\\u006ft" },
line: 1,
column: 18,
endLine: 1,
Expand All @@ -170,7 +225,7 @@ ruleTester.run("no-duplicate-keys", rule, {
errors: [
{
messageId: "duplicateKey",
data: { key: "foot" },
data: { key: "fo\\u006ft" },
line: 1,
column: 18,
endLine: 1,
Expand All @@ -184,7 +239,7 @@ ruleTester.run("no-duplicate-keys", rule, {
errors: [
{
messageId: "duplicateKey",
data: { key: "foot" },
data: { key: "fo\\u006ft" },
line: 1,
column: 18,
endLine: 1,
Expand All @@ -198,7 +253,7 @@ ruleTester.run("no-duplicate-keys", rule, {
errors: [
{
messageId: "duplicateKey",
data: { key: "foot" },
data: { key: "fo\\u006ft" },
line: 1,
column: 16,
endLine: 1,
Expand Down
56 changes: 56 additions & 0 deletions tests/util.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* @fileoverview Tests for util.js
* @author 루밀LuMir(lumirlumir)
*/

//------------------------------------------------------------------------------
// Imports
//------------------------------------------------------------------------------

import assert from "node:assert";
// import { JSONLanguage } from "../../src/languages/json-language.js";
// import { JSONSourceCode } from "../../src/languages/json-source-code.js";
import {
getKey,
// getRawKey
} from "../src/util.js";

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

describe("util", () => {
describe("getKey()", () => {
it("should return the correct key for `String` nodes", () => {
const node = {
name: { type: "String", value: "value" },
};

assert.strictEqual(getKey(node), "value");
});

it("should return the correct key for `Identifier` nodes", () => {
const node = {
name: { type: "Identifier", name: "name" },
};

assert.strictEqual(getKey(node), "name");
});
});

/*
describe("getRawKey()", () => {
const file = { body: `{"foo": 1, 'bar': 2, baz: 3}` };
const language = new JSONLanguage({ mode: "jsonc" });
const parseResult = language.parse(file);
const sourceCode = new JSONSourceCode({
text: file.body,
ast: parseResult.ast,
});
it("TODO", () => {
// TODO
});
});
*/
});