Skip to content

Commit

Permalink
refactor: Use @eslint/plugin-kit (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
nzakas authored Aug 29, 2024
1 parent bc7c371 commit e70e756
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 130 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
],
"license": "Apache-2.0",
"dependencies": {
"@eslint/plugin-kit": "^0.1.0",
"@humanwhocodes/momoa": "^3.2.0"
},
"devDependencies": {
Expand Down
144 changes: 15 additions & 129 deletions src/languages/json-source-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
//-----------------------------------------------------------------------------

import { iterator } from "@humanwhocodes/momoa";
import { VisitNodeStep, TextSourceCodeBase } from "@eslint/plugin-kit";

//-----------------------------------------------------------------------------
// Types
Expand All @@ -29,41 +30,13 @@ import { iterator } from "@humanwhocodes/momoa";

/**
* A class to represent a step in the traversal process.
* @implements {VisitTraversalStep}
*/
class JSONTraversalStep {
/**
* The type of the step.
* @type {"visit"}
* @readonly
*/
type = "visit";

/**
* The kind of the step. Represents the same data as the `type` property
* but it's a number for performance.
* @type {1}
* @readonly
*/
kind = 1;

class JSONTraversalStep extends VisitNodeStep {
/**
* The target of the step.
* @type {JSONNode}
*/
target;

/**
* The phase of the step.
* @type {1|2}
*/
phase;

/**
* The arguments of the step.
* @type {Array<any>}
*/
args;
target = undefined;

/**
* Creates a new instance.
Expand All @@ -73,9 +46,9 @@ class JSONTraversalStep {
* @param {Array<any>} options.args The arguments of the step.
*/
constructor({ target, phase, args }) {
super({ target, phase, args });

this.target = target;
this.phase = phase;
this.args = args;
}
}

Expand All @@ -85,9 +58,8 @@ class JSONTraversalStep {

/**
* JSON Source Code Object
* @implements {TextSourceCode}
*/
export class JSONSourceCode {
export class JSONSourceCode extends TextSourceCodeBase {
/**
* Cached traversal steps.
* @type {Array<JSONTraversalStep>|undefined}
Expand All @@ -100,23 +72,11 @@ export class JSONSourceCode {
*/
#parents = new WeakMap();

/**
* The lines of text in the source code.
* @type {Array<string>}
*/
#lines;

/**
* The AST of the source code.
* @type {DocumentNode}
*/
ast;

/**
* The text of the source code.
* @type {string}
*/
text;
ast = undefined;

/**
* The comment node in the source code.
Expand All @@ -131,35 +91,13 @@ export class JSONSourceCode {
* @param {DocumentNode} options.ast The root AST node.
*/
constructor({ text, ast }) {
super({ text, ast });
this.ast = ast;
this.text = text;
this.comments = ast.tokens.filter(token =>
token.type.endsWith("Comment"),
);
}

/* eslint-disable class-methods-use-this -- Required to complete interface. */

/**
* Returns the loc information for the given node or token.
* @param {JSONNode|JSONToken} nodeOrToken The node or token to get the loc information for.
* @returns {SourceLocation} The loc information for the node or token.
*/
getLoc(nodeOrToken) {
return nodeOrToken.loc;
}

/**
* Returns the range information for the given node or token.
* @param {JSONNode|JSONToken} nodeOrToken The node or token to get the range information for.
* @returns {SourceRange} The range information for the node or token.
*/
getRange(nodeOrToken) {
return nodeOrToken.range;
this.comments = ast.tokens
? ast.tokens.filter(token => token.type.endsWith("Comment"))
: [];
}

/* eslint-enable class-methods-use-this -- Required to complete interface. */

/**
* Returns the parent of the given node.
* @param {JSONNode} node The node to get the parent of.
Expand All @@ -169,61 +107,6 @@ export class JSONSourceCode {
return this.#parents.get(node);
}

/**
* Gets all the ancestors of a given node
* @param {JSONNode} node The node
* @returns {Array<JSONNode>} All the ancestor nodes in the AST, not including the provided node, starting
* from the root node at index 0 and going inwards to the parent node.
* @throws {TypeError} When `node` is missing.
*/
getAncestors(node) {
if (!node) {
throw new TypeError("Missing required argument: node.");
}

const ancestorsStartingAtParent = [];

for (
let ancestor = this.#parents.get(node);
ancestor;
ancestor = this.#parents.get(ancestor)
) {
ancestorsStartingAtParent.push(ancestor);
}

return ancestorsStartingAtParent.reverse();
}

/**
* Gets the source code for the given node.
* @param {JSONNode} [node] The AST node to get the text for.
* @param {number} [beforeCount] The number of characters before the node to retrieve.
* @param {number} [afterCount] The number of characters after the node to retrieve.
* @returns {string} The text representing the AST node.
* @public
*/
getText(node, beforeCount, afterCount) {
if (node) {
return this.text.slice(
Math.max(node.range[0] - (beforeCount || 0), 0),
node.range[1] + (afterCount || 0),
);
}
return this.text;
}

/**
* Gets the entire source text split into an array of lines.
* @returns {Array} The source text as an array of lines.
* @public
*/
get lines() {
if (!this.#lines) {
this.#lines = this.text.split(/\r?\n/gu);
}
return this.#lines;
}

/**
* Traverse the source code and return the steps that were taken.
* @returns {Iterable<JSONTraversalStep>} The steps that were taken while traversing the source code.
Expand All @@ -238,7 +121,10 @@ export class JSONSourceCode {
const steps = (this.#steps = []);

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

steps.push(
new JSONTraversalStep({
target: node,
Expand Down
122 changes: 121 additions & 1 deletion tests/languages/json-source-code.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ describe("JSONSourceCode", () => {
});
});

describe("get lines", () => {
describe("lines", () => {
it("should return an array of lines", () => {
const file = { body: "{\n//test\n}", path: "test.jsonc" };
const language = new JSONLanguage({ mode: "jsonc" });
Expand All @@ -155,4 +155,124 @@ describe("JSONSourceCode", () => {
assert.deepStrictEqual(sourceCode.lines, ["{", "//test", "}"]);
});
});

describe("getParent()", () => {
it("should return the parent node for a given node", () => {
const ast = {
type: "Document",
body: {
type: "Object",
properties: [],
},
tokens: [],
};
const text = "{}";
const sourceCode = new JSONSourceCode({
text,
ast,
});
const node = ast.body;

// call traverse to initialize the parent map
sourceCode.traverse();

assert.strictEqual(sourceCode.getParent(node), ast);
});

it("should return the parent node for a deeply nested node", () => {
const ast = {
type: "Document",
body: {
type: "Object",
members: [
{
type: "Member",
name: {
type: "Identifier",
name: "foo",
},
value: {
type: "Object",
properties: [],
},
},
],
},
tokens: [],
};
const text = '{"foo":{}}';
const sourceCode = new JSONSourceCode({
text,
ast,
});
const node = ast.body.members[0].value;

// call traverse to initialize the parent map
sourceCode.traverse();

assert.strictEqual(sourceCode.getParent(node), ast.body.members[0]);
});
});

describe("getAncestors()", () => {
it("should return an array of ancestors for a given node", () => {
const ast = {
type: "Document",
body: {
type: "Object",
members: [],
},
tokens: [],
};
const text = "{}";
const sourceCode = new JSONSourceCode({
text,
ast,
});
const node = ast.body;

// call traverse to initialize the parent map
sourceCode.traverse();

assert.deepStrictEqual(sourceCode.getAncestors(node), [ast]);
});

it("should return an array of ancestors for a deeply nested node", () => {
const ast = {
type: "Document",
body: {
type: "Object",
members: [
{
type: "Member",
name: {
type: "Identifier",
name: "foo",
},
value: {
type: "Object",
members: [],
},
},
],
},
tokens: [],
};
const text = '{"foo":{}}';
const sourceCode = new JSONSourceCode({
text,
ast,
});
const node = ast.body.members[0].value;

// call traverse to initialize the parent map
sourceCode.traverse();

assert.deepStrictEqual(sourceCode.getAncestors(node), [
ast,
ast.body,
ast.body.members[0],
]);
});
});
});

0 comments on commit e70e756

Please sign in to comment.