Skip to content

refactor: switch JSONSourceCode to extend TextSourceCodeBase #96

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 4, 2025
Merged
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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@
],
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.12.0",
"@eslint/plugin-kit": "^0.2.7",
"@eslint/core": "^0.14.0",
"@eslint/plugin-kit": "^0.3.1",
"@humanwhocodes/momoa": "^3.3.4",
"natural-compare": "^1.4.0"
},
Expand Down
23 changes: 13 additions & 10 deletions src/languages/json-source-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import {
//-----------------------------------------------------------------------------

/**
* @import { DocumentNode, Node, Token } from "@humanwhocodes/momoa";
* @import { SourceLocation, FileProblem, DirectiveType, RulesConfig, TextSourceCode} from "@eslint/core";
* @import { DocumentNode, AnyNode, Token } from "@humanwhocodes/momoa";
* @import { SourceLocation, FileProblem, DirectiveType, RulesConfig } from "@eslint/core";
* @import { JSONSyntaxElement } from "../types.ts";
* @import { JSONLanguageOptions } from "./json-language.js";
*/
Expand All @@ -41,14 +41,14 @@ const INLINE_CONFIG =
class JSONTraversalStep extends VisitNodeStep {
/**
* The target of the step.
* @type {Node}
* @type {AnyNode}
*/
target = undefined;

/**
* Creates a new instance.
* @param {Object} options The options for the step.
* @param {Node} options.target The target of the step.
* @param {AnyNode} options.target The target of the step.
* @param {1|2} options.phase The phase of the step.
* @param {Array<any>} options.args The arguments of the step.
*/
Expand All @@ -65,7 +65,7 @@ class JSONTraversalStep extends VisitNodeStep {

/**
* JSON Source Code Object
* @implements {TextSourceCode<{LangOptions: JSONLanguageOptions, RootNode: DocumentNode, SyntaxElementWithLoc: JSONSyntaxElement, ConfigNode: Token}>}
* @extends {TextSourceCodeBase<{LangOptions: JSONLanguageOptions, RootNode: DocumentNode, SyntaxElementWithLoc: JSONSyntaxElement, ConfigNode: Token}>}
*/
export class JSONSourceCode extends TextSourceCodeBase {
/**
Expand All @@ -76,7 +76,7 @@ export class JSONSourceCode extends TextSourceCodeBase {

/**
* Cache of parent nodes.
* @type {WeakMap<Node, Node>}
* @type {WeakMap<AnyNode, AnyNode>}
*/
#parents = new WeakMap();

Expand Down Expand Up @@ -244,8 +244,8 @@ export class JSONSourceCode extends TextSourceCodeBase {

/**
* Returns the parent of the given node.
* @param {Node} node The node to get the parent of.
* @returns {Node|undefined} The parent of the node.
* @param {AnyNode} node The node to get the parent of.
* @returns {AnyNode|undefined} The parent of the node.
*/
getParent(node) {
return this.#parents.get(node);
Expand All @@ -266,12 +266,15 @@ export class JSONSourceCode extends TextSourceCodeBase {

for (const { node, parent, phase } of iterator(this.ast)) {
if (parent) {
this.#parents.set(node, parent);
this.#parents.set(
/** @type {AnyNode} */ (node),
/** @type {AnyNode} */ (parent),
);
}

steps.push(
new JSONTraversalStep({
target: node,
target: /** @type {AnyNode} */ (node),
phase: phase === "enter" ? 1 : 2,
args: [node, parent],
}),
Expand Down
77 changes: 75 additions & 2 deletions tests/types/types.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
import json from "@eslint/json";
import json, { JSONSourceCode } from "@eslint/json";
import { ESLint } from "eslint";
import type { JSONSyntaxElement, JSONRuleDefinition } from "@eslint/json/types";
import type {
JSONSyntaxElement,
JSONRuleDefinition,
JSONRuleVisitor,
} from "@eslint/json/types";
import type {
AnyNode,
ArrayNode,
BooleanNode,
DocumentNode,
ElementNode,
IdentifierNode,
InfinityNode,
MemberNode,
NaNNode,
NullNode,
NumberNode,
ObjectNode,
StringNode,
} from "@humanwhocodes/momoa";
import type { SourceLocation, SourceRange } from "@eslint/core";

json satisfies ESLint.Plugin;
json.meta.name satisfies string;
Expand Down Expand Up @@ -34,6 +54,59 @@ json.configs.recommended.plugins satisfies object;
end: { line: 1, column: 1, offset: 1 },
}) satisfies JSONSyntaxElement["loc"];

(): JSONRuleDefinition => ({
create({ sourceCode }): JSONRuleVisitor {
sourceCode satisfies JSONSourceCode;
sourceCode.ast satisfies DocumentNode;
sourceCode.lines satisfies string[];
sourceCode.text satisfies string;

function testVisitor<NodeType extends AnyNode>(
node: NodeType,
parent?:
| DocumentNode
| MemberNode
| ElementNode
| ArrayNode
| ObjectNode,
) {
sourceCode.getLoc(node) satisfies SourceLocation;
sourceCode.getRange(node) satisfies SourceRange;
sourceCode.getParent(node) satisfies AnyNode | undefined;
sourceCode.getAncestors(node) satisfies JSONSyntaxElement[];
sourceCode.getText(node) satisfies string;
}

return {
Array: (...args) => testVisitor<ArrayNode>(...args),
"Array:exit": (...args) => testVisitor<ArrayNode>(...args),
Boolean: (...args) => testVisitor<BooleanNode>(...args),
"Boolean:exit": (...args) => testVisitor<BooleanNode>(...args),
Document: (...args) => testVisitor<DocumentNode>(...args),
"Document:exit": (...args) => testVisitor<DocumentNode>(...args),
Element: (...args) => testVisitor<ElementNode>(...args),
"Element:exit": (...args) => testVisitor<ElementNode>(...args),
Identifier: (...args) => testVisitor<IdentifierNode>(...args),
"Identifier:exit": (...args) =>
testVisitor<IdentifierNode>(...args),
Infinity: (...args) => testVisitor<InfinityNode>(...args),
"Infinity:exit": (...args) => testVisitor<InfinityNode>(...args),
Member: (...args) => testVisitor<MemberNode>(...args),
"Member:exit": (...args) => testVisitor<MemberNode>(...args),
NaN: (...args) => testVisitor<NaNNode>(...args),
"NaN:exit": (...args) => testVisitor<NaNNode>(...args),
Null: (...args) => testVisitor<NullNode>(...args),
"Null:exit": (...args) => testVisitor<NullNode>(...args),
Number: (...args) => testVisitor<NumberNode>(...args),
"Number:exit": (...args) => testVisitor<NumberNode>(...args),
Object: (...args) => testVisitor<ObjectNode>(...args),
"Object:exit": (...args) => testVisitor<ObjectNode>(...args),
String: (...args) => testVisitor<StringNode>(...args),
"String:exit": (...args) => testVisitor<StringNode>(...args),
};
},
});

// All options optional - JSONRuleDefinition and JSONRuleDefinition<{}>
// should be the same type.
(rule1: JSONRuleDefinition, rule2: JSONRuleDefinition<{}>) => {
Expand Down