Skip to content
Open
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
11 changes: 7 additions & 4 deletions src/rules/no-unnormalized-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
* @author Bradley Meck Farias
*/

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

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

//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -58,10 +64,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
29 changes: 12 additions & 17 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, getRawKey } 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 @@ -144,6 +134,7 @@ const rule = {
},

create(context) {
const { sourceCode } = context;
const [
directionShort,
{ allowLineSeparatedGroups, caseSensitive, natural, minKeys },
Expand All @@ -156,7 +147,7 @@ const rule = {

// Note that @humanwhocodes/momoa doesn't include comments in the object.members tree, so we can't just see if a member is preceded by a comment
const commentLineNums = new Set();
for (const comment of context.sourceCode.comments) {
for (const comment of sourceCode.comments) {
for (
let lineNum = comment.loc.start.line;
lineNum <= comment.loc.end.line;
Expand Down Expand Up @@ -187,9 +178,7 @@ const rule = {
) {
if (
!commentLineNums.has(lineNum) &&
!hasNonWhitespace.test(
context.sourceCode.lines[lineNum - 1],
)
!hasNonWhitespace.test(sourceCode.lines[lineNum - 1])
) {
return true;
}
Expand All @@ -200,15 +189,20 @@ const rule = {

return {
Object(node) {
/** @type {MemberNode} */
let prevMember;
/** @type {string} */
let prevName;
/** @type {string} */
let prevRawName;

if (node.members.length < minKeys) {
return;
}

for (const member of node.members) {
const thisName = getKey(member);
const thisRawName = getRawKey(member, sourceCode);

if (
prevMember &&
Expand All @@ -220,8 +214,8 @@ const rule = {
loc: member.name.loc,
messageId: "sortKeys",
data: {
thisName,
prevName,
thisName: thisRawName,
prevName: prevRawName,
direction,
sensitivity,
sortName,
Expand All @@ -231,6 +225,7 @@ const rule = {

prevMember = member;
prevName = thisName;
prevRawName = thisRawName;
}
},
};
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
40 changes: 40 additions & 0 deletions tests/rules/sort-keys.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2029,5 +2029,45 @@ ruleTester.run("sort-keys", rule, {
},
],
},

// Escape sequences in keys
{
code: '{"\\u0061":1, "\\u0063":2, "\\u0062":3}',
errors: [
{
messageId: "sortKeys",
data: {
sortName: "alphanumeric",
sensitivity: "sensitive",
direction: "ascending",
thisName: "\\u0062",
prevName: "\\u0063",
},
line: 1,
column: 26,
endLine: 1,
endColumn: 34,
},
],
},
{
code: '{"\\u0061\\n":1, "\\u0063\\n":2, "\\u0062\\n":3}',
errors: [
{
messageId: "sortKeys",
data: {
sortName: "alphanumeric",
sensitivity: "sensitive",
direction: "ascending",
thisName: "\\u0062\\n",
prevName: "\\u0063\\n",
},
line: 1,
column: 30,
endLine: 1,
endColumn: 40,
},
],
},
],
});
Loading