Skip to content

Commit 460e7c7

Browse files
nzakasfasttimeJoshuaKGoldberg
authoredFeb 6, 2025··
fix: Use updated types from @eslint/core (#66)
* chore: Update types based on @eslint/core * feat: Update to new types * fix: Update types from @eslint/core * Update types * Add types.ts to JSR definition * Clean up no-unsafe-values.js * Fix tests * Update tools/dedupe-types.js Co-authored-by: Francesco Trotta <github@fasttime.org> * Update src/rules/no-duplicate-keys.js Co-authored-by: Josh Goldberg ✨ <git@joshuakgoldberg.com> * clean up * Fix CJS type definitions * Fix build step --------- Co-authored-by: Francesco Trotta <github@fasttime.org> Co-authored-by: Josh Goldberg ✨ <git@joshuakgoldberg.com>
1 parent 2faf9d3 commit 460e7c7

14 files changed

+229
-22
lines changed
 

‎jsr.json

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"include": [
77
"dist/esm/index.js",
88
"dist/esm/index.d.ts",
9+
"dist/esm/types.ts",
910
"README.md",
1011
"jsr.json",
1112
"LICENSE"

‎package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"homepage": "https://github.com/eslint/json#readme",
4343
"scripts": {
4444
"build:dedupe-types": "node tools/dedupe-types.js dist/cjs/index.cjs dist/esm/index.js",
45-
"build:cts": "node -e \"fs.copyFileSync('dist/esm/index.d.ts', 'dist/cjs/index.d.cts')\"",
45+
"build:cts": "node tools/build-cts.js",
4646
"build": "rollup -c && npm run build:dedupe-types && tsc -p tsconfig.esm.json && npm run build:cts",
4747
"build:readme": "node tools/update-readme.js",
4848
"test:jsr": "npx jsr@latest publish --dry-run",
@@ -69,7 +69,6 @@
6969
"natural-compare": "^1.4.0"
7070
},
7171
"devDependencies": {
72-
"@types/eslint": "^8.56.10",
7372
"c8": "^9.1.0",
7473
"dedent": "^1.5.3",
7574
"eslint": "^9.11.1",

‎rollup.config.js

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import copy from "rollup-plugin-copy";
2+
13
export default {
24
input: "src/index.js",
35
output: [
@@ -11,4 +13,12 @@ export default {
1113
banner: '// @ts-self-types="./index.d.ts"',
1214
},
1315
],
16+
plugins: [
17+
copy({
18+
targets: [
19+
{ src: "src/types.ts", dest: "dist/cjs", rename: "types.cts" },
20+
{ src: "src/types.ts", dest: "dist/esm" },
21+
],
22+
}),
23+
],
1424
};

‎src/languages/json-language.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,16 @@ import { visitorKeys } from "@humanwhocodes/momoa";
2121
/** @typedef {import("@eslint/core").OkParseResult<DocumentNode>} OkParseResult */
2222
/** @typedef {import("@eslint/core").ParseResult<DocumentNode>} ParseResult */
2323
/** @typedef {import("@eslint/core").File} File */
24-
/**
25-
* @typedef {Object} JSONLanguageOptions
26-
* @property {boolean} [allowTrailingCommas] Whether to allow trailing commas.
27-
*/
24+
/** @typedef {import("../types.ts").IJSONLanguage} IJSONLanguage */
25+
/** @typedef {import("../types.ts").JSONLanguageOptions} JSONLanguageOptions */
2826

2927
//-----------------------------------------------------------------------------
3028
// Exports
3129
//-----------------------------------------------------------------------------
3230

3331
/**
3432
* JSON Language Object
35-
* @implements {Language}
33+
* @implements {IJSONLanguage}
3634
*/
3735
export class JSONLanguage {
3836
/**

‎src/languages/json-source-code.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ import {
2626
/** @typedef {import("@eslint/core").SourceLocation} SourceLocation */
2727
/** @typedef {import("@eslint/core").File} File */
2828
/** @typedef {import("@eslint/core").TraversalStep} TraversalStep */
29-
/** @typedef {import("@eslint/core").TextSourceCode} TextSourceCode */
3029
/** @typedef {import("@eslint/core").VisitTraversalStep} VisitTraversalStep */
3130
/** @typedef {import("@eslint/core").FileProblem} FileProblem */
3231
/** @typedef {import("@eslint/core").DirectiveType} DirectiveType */
3332
/** @typedef {import("@eslint/core").RulesConfig} RulesConfig */
33+
/** @typedef {import("../types.ts").IJSONSourceCode} IJSONSourceCode */
34+
/** @typedef {import("../types.ts").JSONSyntaxElement} JSONSyntaxElement */
3435

3536
//-----------------------------------------------------------------------------
3637
// Helpers
@@ -71,6 +72,7 @@ class JSONTraversalStep extends VisitNodeStep {
7172

7273
/**
7374
* JSON Source Code Object
75+
* @implements {IJSONSourceCode}
7476
*/
7577
export class JSONSourceCode extends TextSourceCodeBase {
7678
/**
@@ -147,7 +149,7 @@ export class JSONSourceCode extends TextSourceCodeBase {
147149
);
148150
}
149151

150-
return this.#inlineConfigComments;
152+
return this.#inlineConfigComments ?? [];
151153
}
152154

153155
/**

‎src/rules/no-duplicate-keys.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,22 @@
33
* @author Nicholas C. Zakas
44
*/
55

6+
//-----------------------------------------------------------------------------
7+
// Type Definitions
8+
//-----------------------------------------------------------------------------
9+
10+
/** @typedef {"duplicateKey"} NoDuplicateKeysMessageIds */
11+
/** @typedef {import("../types.ts").JSONRuleDefinition<[], NoDuplicateKeysMessageIds>} NoDuplicateKeysRuleDefinition */
12+
/** @typedef {import("@humanwhocodes/momoa").MemberNode} MemberNode */
13+
614
//-----------------------------------------------------------------------------
715
// Rule Definition
816
//-----------------------------------------------------------------------------
917

18+
/** @type {NoDuplicateKeysRuleDefinition} */
1019
export default {
1120
meta: {
12-
type: /** @type {const} */ ("problem"),
21+
type: "problem",
1322

1423
docs: {
1524
description: "Disallow duplicate keys in JSON objects",
@@ -21,7 +30,10 @@ export default {
2130
},
2231

2332
create(context) {
33+
/** @type {Array<Map<string, MemberNode>|undefined>} */
2434
const objectKeys = [];
35+
36+
/** @type {Map<string, MemberNode>|undefined} */
2537
let keys;
2638

2739
return {

‎src/rules/no-empty-keys.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,21 @@
33
* @author Nicholas C. Zakas
44
*/
55

6+
//-----------------------------------------------------------------------------
7+
// Type Definitions
8+
//-----------------------------------------------------------------------------
9+
10+
/** @typedef {"emptyKey"} NoEmptyKeysMessageIds */
11+
/** @typedef {import("../types.ts").JSONRuleDefinition<[], NoEmptyKeysMessageIds>} NoEmptyKeysRuleDefinition */
12+
13+
//-----------------------------------------------------------------------------
14+
// Rule Definition
15+
//-----------------------------------------------------------------------------
16+
17+
/** @type {NoEmptyKeysRuleDefinition} */
618
export default {
719
meta: {
8-
type: /** @type {const} */ ("problem"),
20+
type: "problem",
921

1022
docs: {
1123
description: "Disallow empty keys in JSON objects",

‎src/rules/no-unsafe-values.js

+27-4
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,36 @@
33
* @author Bradley Meck Farias
44
*/
55

6-
// RFC 8259's `number` production, as a regex. Capture the integer part
7-
// and the fractional part.
6+
//-----------------------------------------------------------------------------
7+
// Type Definitions
8+
//-----------------------------------------------------------------------------
9+
10+
/** @typedef {"unsafeNumber"|"unsafeInteger"|"unsafeZero"|"subnormal"|"loneSurrogate"} NoUnsafeValuesMessageIds */
11+
/** @typedef {import("../types.ts").JSONRuleDefinition<[], NoUnsafeValuesMessageIds>} NoUnsafeValuesRuleDefinition */
12+
13+
//-----------------------------------------------------------------------------
14+
// Helpers
15+
//-----------------------------------------------------------------------------
16+
17+
/*
18+
* This rule is based on the JSON grammar from RFC 8259, section 6.
19+
* https://tools.ietf.org/html/rfc8259#section-6
20+
*
21+
* We separately capture the integer and fractional parts of a number, so that
22+
* we can check for unsafe numbers that will evaluate to Infinity.
23+
*/
824
const NUMBER =
925
/^-?(?<int>0|([1-9][0-9]*))(?:\.(?<frac>[0-9]+))?(?:[eE][+-]?[0-9]+)?$/u;
1026
const NON_ZERO = /[1-9]/u;
1127

28+
//-----------------------------------------------------------------------------
29+
// Rule Definition
30+
//-----------------------------------------------------------------------------
31+
32+
/** @type {NoUnsafeValuesRuleDefinition} */
1233
export default {
1334
meta: {
14-
type: /** @type {const} */ ("problem"),
35+
type: "problem",
1536

1637
docs: {
1738
description: "Disallow JSON values that are unsafe for interchange",
@@ -85,7 +106,9 @@ export default {
85106
loc: node.loc,
86107
messageId: "subnormal",
87108
// Value included so that it's seen in scientific notation
88-
data: node,
109+
data: {
110+
value,
111+
},
89112
});
90113
}
91114
}

‎src/types.ts

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/**
2+
* @fileoverview Additional types for this package.
3+
* @author Nicholas C. Zakas
4+
*/
5+
6+
//------------------------------------------------------------------------------
7+
// Imports
8+
//------------------------------------------------------------------------------
9+
10+
import type {
11+
RuleVisitor,
12+
TextSourceCode,
13+
Language,
14+
LanguageOptions,
15+
RuleDefinition,
16+
} from "@eslint/core";
17+
import {
18+
DocumentNode,
19+
MemberNode,
20+
ElementNode,
21+
ObjectNode,
22+
ArrayNode,
23+
StringNode,
24+
NullNode,
25+
NumberNode,
26+
BooleanNode,
27+
NaNNode,
28+
InfinityNode,
29+
IdentifierNode,
30+
AnyNode,
31+
Token,
32+
} from "@humanwhocodes/momoa";
33+
34+
//------------------------------------------------------------------------------
35+
// Types
36+
//------------------------------------------------------------------------------
37+
38+
type ValueNodeParent = DocumentNode | MemberNode | ElementNode;
39+
40+
/**
41+
* A JSON syntax element, including nodes and tokens.
42+
*/
43+
export type JSONSyntaxElement = Token | AnyNode;
44+
45+
/**
46+
* Language options provided for JSON files.
47+
*/
48+
export interface JSONLanguageOptions extends LanguageOptions {
49+
/**
50+
* Whether to allow trailing commas. Only valid in JSONC.
51+
*/
52+
allowTrailingCommas?: boolean;
53+
}
54+
55+
/**
56+
* The visitor format returned from rules in this package.
57+
*/
58+
export interface JSONRuleVisitor extends RuleVisitor {
59+
Document?(node: DocumentNode): void;
60+
Member?(node: MemberNode, parent?: ObjectNode): void;
61+
Element?(node: ElementNode, parent?: ArrayNode): void;
62+
Object?(node: ObjectNode, parent?: ValueNodeParent): void;
63+
Array?(node: ArrayNode, parent?: ValueNodeParent): void;
64+
String?(node: StringNode, parent?: ValueNodeParent): void;
65+
Null?(node: NullNode, parent?: ValueNodeParent): void;
66+
Number?(node: NumberNode, parent?: ValueNodeParent): void;
67+
Boolean?(node: BooleanNode, parent?: ValueNodeParent): void;
68+
NaN?(node: NaNNode, parent?: ValueNodeParent): void;
69+
Infinity?(node: InfinityNode, parent?: ValueNodeParent): void;
70+
Identifier?(node: IdentifierNode, parent?: ValueNodeParent): void;
71+
72+
"Document:exit"?(node: DocumentNode): void;
73+
"Member:exit"?(node: MemberNode, parent?: ObjectNode): void;
74+
"Element:exit"?(node: ElementNode, parent?: ArrayNode): void;
75+
"Object:exit"?(node: ObjectNode, parent?: ValueNodeParent): void;
76+
"Array:exit"?(node: ArrayNode, parent?: ValueNodeParent): void;
77+
"String:exit"?(node: StringNode, parent?: ValueNodeParent): void;
78+
"Null:exit"?(node: NullNode, parent?: ValueNodeParent): void;
79+
"Number:exit"?(node: NumberNode, parent?: ValueNodeParent): void;
80+
"Boolean:exit"?(node: BooleanNode, parent?: ValueNodeParent): void;
81+
"NaN:exit"?(node: NaNNode, parent?: ValueNodeParent): void;
82+
"Infinity:exit"?(node: InfinityNode, parent?: ValueNodeParent): void;
83+
"Identifier:exit"?(node: IdentifierNode, parent?: ValueNodeParent): void;
84+
}
85+
86+
/**
87+
* The `SourceCode` implementation for JSON files.
88+
*/
89+
export interface IJSONSourceCode
90+
extends TextSourceCode<{
91+
LangOptions: JSONLanguageOptions;
92+
RootNode: DocumentNode;
93+
SyntaxElementWithLoc: JSONSyntaxElement;
94+
ConfigNode: Token;
95+
}> {
96+
/**
97+
* Get the text of a syntax element.
98+
* @param syntaxElement The syntax element to get the text of.
99+
* @param beforeCount The number of characters to include before the syntax element.
100+
* @param afterCount The number of characters to include after the syntax element.
101+
* @returns The text of the syntax element.
102+
*/
103+
getText(
104+
syntaxElement: JSONSyntaxElement,
105+
beforeCount?: number,
106+
afterCount?: number,
107+
): string;
108+
}
109+
110+
export type IJSONLanguage = Language<{
111+
LangOptions: JSONLanguageOptions;
112+
Code: IJSONSourceCode;
113+
RootNode: DocumentNode;
114+
Node: AnyNode;
115+
}>;
116+
117+
export type JSONRuleDefinition<
118+
JSONRuleOptions extends unknown[],
119+
JSONRuleMessageIds extends string = "",
120+
> = RuleDefinition<{
121+
LangOptions: JSONLanguageOptions;
122+
Code: IJSONSourceCode;
123+
RuleOptions: JSONRuleOptions;
124+
Visitor: JSONRuleVisitor;
125+
Node: AnyNode;
126+
MessageIds: JSONRuleMessageIds;
127+
ExtRuleDocs: {};
128+
}>;

‎tests/rules/no-unsafe-values.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ ruleTester.run("no-unsafe-values", rule, {
251251
{
252252
messageId: "subnormal",
253253
data: {
254-
value: "2.225073858507201e-308",
254+
value: "2.2250738585072009e-308",
255255
},
256256
line: 1,
257257
column: 1,
@@ -266,7 +266,7 @@ ruleTester.run("no-unsafe-values", rule, {
266266
{
267267
messageId: "subnormal",
268268
data: {
269-
value: "-2.225073858507201e-308",
269+
value: "-2.2250738585072009e-308",
270270
},
271271
line: 1,
272272
column: 1,

‎tests/types/types.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import json from "@eslint/json";
2-
import { ESLint } from "eslint";
2+
// import { ESLint } from "eslint";
33

4-
json satisfies ESLint.Plugin;
4+
// json satisfies ESLint.Plugin;
55
json.meta.name satisfies string;
66
json.meta.version satisfies string;
77

‎tools/build-cts.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* @fileoverview Rewrites import expressions for CommonJS compatibility.
3+
* This script creates "dist/cjs/index.d.cts" from "dist/esm/index.d.ts" by modifying imports
4+
* from `"./types.ts"` to `"./types.cts"`.
5+
*
6+
* @author Francesco Trotta
7+
*/
8+
9+
import { readFile, writeFile } from "node:fs/promises";
10+
11+
const oldSourceText = await readFile("dist/esm/index.d.ts", "utf-8");
12+
const newSourceText = oldSourceText.replaceAll(
13+
'import("./types.ts")',
14+
'import("./types.cts")',
15+
);
16+
await writeFile("dist/cjs/index.d.cts", newSourceText);

‎tools/dedupe-types.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,10 @@ files.forEach(filePath => {
3939
return true;
4040
});
4141

42-
fs.writeFileSync(filePath, remainingLines.join("\n"), "utf8");
42+
// replace references to ../types.ts with ./types.ts
43+
const text = remainingLines
44+
.join("\n")
45+
.replace(/\.\.\/types\.ts/gu, "./types.ts");
46+
47+
fs.writeFileSync(filePath, text, "utf8");
4348
});

‎tsconfig.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
"allowJs": true,
77
"checkJs": true,
88
"outDir": "dist/esm",
9-
"target": "ES2022",
9+
"target": "ESNext",
1010
"moduleResolution": "NodeNext",
11-
"module": "NodeNext"
11+
"module": "NodeNext",
12+
"allowImportingTsExtensions": true
1213
}
1314
}

0 commit comments

Comments
 (0)
Please sign in to comment.