Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"@eslint/core": "^0.12.0",
"@eslint/plugin-kit": "^0.2.7",
"@humanwhocodes/momoa": "^3.3.4",
"momoa": "^1.2.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unnecessary, this repo only uses @humanwhocodes/momoa, which is already installed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops good catch! That's actually a completely different and unrelated package.

"natural-compare": "^1.4.0"
},
"devDependencies": {
Expand Down
4 changes: 3 additions & 1 deletion src/rules/no-duplicate-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//-----------------------------------------------------------------------------

/** @type {NoDuplicateKeysRuleDefinition} */
export default {
const rule = {
meta: {
type: "problem",

Expand Down Expand Up @@ -66,3 +66,5 @@ export default {
};
},
};

export default rule;
4 changes: 3 additions & 1 deletion src/rules/no-empty-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//-----------------------------------------------------------------------------

/** @type {NoEmptyKeysRuleDefinition} */
export default {
const rule = {
meta: {
type: "problem",

Expand Down Expand Up @@ -46,3 +46,5 @@ export default {
};
},
};

export default rule;
24 changes: 19 additions & 5 deletions src/rules/no-unnormalized-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,19 @@
* @author Bradley Meck Farias
*/

export default {
//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------

/** @typedef {"unnormalizedKey"} NoUnnormalizedKeysMessageIds */
/** @typedef {import("../types.ts").JSONRuleDefinition<[], NoUnnormalizedKeysMessageIds>} NoUnnormalizedKeysRuleDefinition */

//-----------------------------------------------------------------------------
// Rule Definition
//-----------------------------------------------------------------------------

/** @type {NoUnnormalizedKeysRuleDefinition} */
const rule = {
meta: {
type: /** @type {const} */ ("problem"),

Expand All @@ -29,17 +41,17 @@ export default {
},

create(context) {
const normalization = context.options.length
? text => text.normalize(context.options[0].form)
: text => text.normalize();
const options = /** @type {{form:string}[]} */ (context.options);
const form = options.length ? options[0].form : undefined;

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

if (normalization(key) !== key) {
if (key.normalize(form) !== key) {
context.report({
loc: node.name.loc,
messageId: "unnormalizedKey",
Expand All @@ -52,3 +64,5 @@ export default {
};
},
};

export default rule;
4 changes: 3 additions & 1 deletion src/rules/no-unsafe-values.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const NON_ZERO = /[1-9]/u;
//-----------------------------------------------------------------------------

/** @type {NoUnsafeValuesRuleDefinition} */
export default {
const rule = {
meta: {
type: "problem",

Expand Down Expand Up @@ -145,3 +145,5 @@ export default {
};
},
};

export default rule;
70 changes: 64 additions & 6 deletions src/rules/sort-keys.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,97 @@
/**
* @fileoverview Rule to require JSON object keys to be sorted. Copied largely from https://github.com/eslint/eslint/blob/main/lib/rules/sort-keys.js
* @fileoverview Rule to require JSON object keys to be sorted.
* Copied largely from https://github.com/eslint/eslint/blob/main/lib/rules/sort-keys.js
* @author Robin Thomas
*/

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

import naturalCompare from "natural-compare";

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

/** @typedef {"sortKeys"} SortKeysMessageIds */
/** @typedef {import("../types.ts").JSONRuleDefinition<[], SortKeysMessageIds>} SortKeysRuleDefinition */
/** @typedef {(a:string,b:string) => boolean} Comparator */
/** @typedef {import("@humanwhocodes/momoa").MemberNode} MemberNode */
/**
* @typedef {Object} SortOptions
* @property {boolean} caseSensitive
* @property {boolean} natural
* @property {number} minKeys
* @property {boolean} allowLineSeparatedGroups
*/
/** @typedef {"asc"|"desc"} SortDirection */
/** @typedef {[SortDirection, SortOptions]} SortKeysRuleOptions */

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

const hasNonWhitespace = /\S/u;

const comparators = {
ascending: {
alphanumeric: {
/** @type {Comparator} */
sensitive: (a, b) => a <= b,

/** @type {Comparator} */
insensitive: (a, b) => a.toLowerCase() <= b.toLowerCase(),
},
natural: {
/** @type {Comparator} */
sensitive: (a, b) => naturalCompare(a, b) <= 0,

/** @type {Comparator} */
insensitive: (a, b) =>
naturalCompare(a.toLowerCase(), b.toLowerCase()) <= 0,
},
},
descending: {
alphanumeric: {
/** @type {Comparator} */
sensitive: (a, b) =>
comparators.ascending.alphanumeric.sensitive(b, a),

/** @type {Comparator} */
insensitive: (a, b) =>
comparators.ascending.alphanumeric.insensitive(b, a),
},
natural: {
/** @type {Comparator} */
sensitive: (a, b) => comparators.ascending.natural.sensitive(b, a),

/** @type {Comparator} */
insensitive: (a, b) =>
comparators.ascending.natural.insensitive(b, a),
},
},
};

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

export default {
//-----------------------------------------------------------------------------
// Rule Definition
//-----------------------------------------------------------------------------

/** @type {SortKeysRuleDefinition} */
const rule = {
meta: {
type: /** @type {const} */ ("suggestion"),
type: "suggestion",

defaultOptions: [
"asc",
Expand Down Expand Up @@ -90,10 +139,11 @@ export default {
},

create(context) {
const options = /** @type {Array<string|Object>} */ (context.options);
const [
directionShort,
{ allowLineSeparatedGroups, caseSensitive, natural, minKeys },
] = context.options;
] = /** @type { SortKeysRuleOptions} */ (options);

const direction = directionShort === "asc" ? "ascending" : "descending";
const sortName = natural ? "natural" : "alphanumeric";
Expand All @@ -112,8 +162,14 @@ export default {
}
}

// Note that there can be comments *inside* members, e.g. `{"foo: /* comment *\/ "bar"}`, but these are ignored when calculating line-separated groups
/**
* Checks if two members are line-separated.
* @param {MemberNode} prevMember The previous member.
* @param {MemberNode} member The current member.
* @return {boolean}
*/
function isLineSeparated(prevMember, member) {
// Note that there can be comments *inside* members, e.g. `{"foo: /* comment *\/ "bar"}`, but these are ignored when calculating line-separated groups
const prevMemberEndLine = prevMember.loc.end.line;
const thisStartLine = member.loc.start.line;
if (thisStartLine - prevMemberEndLine < 2) {
Expand Down Expand Up @@ -176,3 +232,5 @@ export default {
};
},
};

export default rule;
18 changes: 16 additions & 2 deletions src/rules/top-level-interop.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,21 @@
* @author Joe Hildebrand
*/

export default {
//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------

/** @typedef {"topLevel"} TopLevelInteropMessageIds */
/** @typedef {import("../types.ts").JSONRuleDefinition<[], TopLevelInteropMessageIds>} TopLevelInteropRuleDefinition */

//-----------------------------------------------------------------------------
// Rule Definition
//-----------------------------------------------------------------------------

/** @type {TopLevelInteropRuleDefinition} */
const rule = {
meta: {
type: /** @type {const} */ ("problem"),
type: "problem",

docs: {
description:
Expand Down Expand Up @@ -33,3 +45,5 @@ export default {
};
},
};

export default rule;
10 changes: 10 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ export interface IJSONSourceCode
beforeCount?: number,
afterCount?: number,
): string;

/**
* Any comments found in a JSONC or JSON5 file.
*/
comments: Array<Token> | undefined;

/**
* The lines of text found in the file.
*/
lines: Array<string>;
}

export type IJSONLanguage = Language<{
Expand Down