Skip to content

Commit

Permalink
feat: Add getLoc and getRange to JSONSourceCode (#13)
Browse files Browse the repository at this point in the history
* feat: Add getLoc and getRange to JSONSourceCode

* Add tests
  • Loading branch information
nzakas authored Jul 22, 2024
1 parent f419757 commit 2225f63
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 18 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"@humanwhocodes/momoa": "^3.1.1"
},
"devDependencies": {
"@eslint/core": "^0.1.0",
"@eslint/core": "^0.3.0",
"@types/eslint": "^8.56.10",
"c8": "^9.1.0",
"eslint": "^9.6.0",
Expand Down
9 changes: 5 additions & 4 deletions src/languages/json-language.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import { visitorKeys } from "@humanwhocodes/momoa";
//-----------------------------------------------------------------------------

/** @typedef {import("@humanwhocodes/momoa").DocumentNode} DocumentNode */
/** @typedef {import("@humanwhocodes/momoa").Node} JSONNode */
/** @typedef {import("@eslint/core").Language} Language */
/** @typedef {import("@eslint/core").OkParseResult<DocumentNode>} OkParseResult */
/** @typedef {import("@eslint/core").ParseResult<DocumentNode>} ParseResult */
/** @typedef {import("@eslint/core").File} File */
/** @typedef {import("@eslint/core").ParseResult} ParseResult */
/** @typedef {import("@eslint/core").SyntaxElement} SyntaxElement */

//-----------------------------------------------------------------------------
// Exports
Expand Down Expand Up @@ -111,7 +112,7 @@ export class JSONLanguage {

return {
ok: true,
ast: /** @type {DocumentNode & SyntaxElement} */ (root),
ast: root,
};
} catch (ex) {
// error messages end with (line:column) so we strip that off for ESLint
Expand All @@ -135,7 +136,7 @@ export class JSONLanguage {
/**
* Creates a new `JSONSourceCode` object from the given information.
* @param {File} file The virtual file to create a `JSONSourceCode` object from.
* @param {ParseResult} parseResult The result returned from `parse()`.
* @param {OkParseResult} parseResult The result returned from `parse()`.
* @returns {JSONSourceCode} The new `JSONSourceCode` object.
*/
createSourceCode(file, parseResult) {
Expand Down
46 changes: 33 additions & 13 deletions src/languages/json-source-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@ import { iterator } from "@humanwhocodes/momoa";
/** @typedef {import("@humanwhocodes/momoa").DocumentNode} DocumentNode */
/** @typedef {import("@humanwhocodes/momoa").Node} JSONNode */
/** @typedef {import("@humanwhocodes/momoa").Token} JSONToken */
/** @typedef {import("@eslint/core").SyntaxElement} SyntaxElement */
/** @typedef {import("@eslint/core").Language} Language */
/** @typedef {import("@eslint/core").SourceRange} SourceRange */
/** @typedef {import("@eslint/core").SourceLocation} SourceLocation */
/** @typedef {import("@eslint/core").File} File */
/** @typedef {import("@eslint/core").TraversalStep} TraversalStep */
/** @typedef {import("@eslint/core").VisitTraversalStep} VisitTraversalStep */
/** @typedef {import("@eslint/core").TextSourceCode} TextSourceCode */
/** @typedef {import("@eslint/core").ParseResult} ParseResult */
/** @typedef {import("@eslint/core").VisitTraversalStep} VisitTraversalStep */

//-----------------------------------------------------------------------------
// Helpers
Expand Down Expand Up @@ -50,7 +49,7 @@ class JSONTraversalStep {

/**
* The target of the step.
* @type {JSONNode & SyntaxElement}
* @type {JSONNode}
*/
target;

Expand All @@ -69,7 +68,7 @@ class JSONTraversalStep {
/**
* Creates a new instance.
* @param {Object} options The options for the step.
* @param {JSONNode & SyntaxElement} options.target The target of the step.
* @param {JSONNode} 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 Down Expand Up @@ -109,7 +108,7 @@ export class JSONSourceCode {

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

Expand All @@ -129,7 +128,7 @@ export class JSONSourceCode {
* Creates a new instance.
* @param {Object} options The options for the instance.
* @param {string} options.text The source code text.
* @param {DocumentNode & SyntaxElement} options.ast The root AST node.
* @param {DocumentNode} options.ast The root AST node.
*/
constructor({ text, ast }) {
this.ast = ast;
Expand All @@ -139,6 +138,28 @@ export class JSONSourceCode {
);
}

/* 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;
}

/* 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 Down Expand Up @@ -205,23 +226,22 @@ export class JSONSourceCode {

/**
* Traverse the source code and return the steps that were taken.
* @returns {Iterable<TraversalStep>} The steps that were taken while traversing the source code.
* @returns {Iterable<JSONTraversalStep>} The steps that were taken while traversing the source code.
*/
traverse() {
// Because the AST doesn't mutate, we can cache the steps
if (this.#steps) {
return this.#steps.values();
}

/** @type {Array<JSONTraversalStep>} */
const steps = (this.#steps = []);

for (const { node, parent, phase } of iterator(
/** @type {DocumentNode} */ (this.ast),
)) {
for (const { node, parent, phase } of iterator(this.ast)) {
this.#parents.set(node, parent);
steps.push(
new JSONTraversalStep({
target: /** @type {JSONNode & SyntaxElement} */ (node),
target: node,
phase: phase === "enter" ? 1 : 2,
args: [node, parent],
}),
Expand Down
55 changes: 55 additions & 0 deletions tests/languages/json-source-code.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,61 @@ describe("JSONSourceCode", () => {
});
});

describe("getLoc()", () => {
it("should return the loc property of a node", () => {
const loc = {
start: {
line: 1,
column: 1,
offset: 0,
},
end: {
line: 1,
column: 2,
offset: 1,
},
};
const ast = {
type: "Document",
body: {
type: "Object",
properties: [],
},
tokens: [],
loc,
};
const text = "{}";
const sourceCode = new JSONSourceCode({
text,
ast,
});

assert.strictEqual(sourceCode.getLoc(ast), loc);
});
});

describe("getRange()", () => {
it("should return the range property of a node", () => {
const range = [0, 1];
const ast = {
type: "Document",
body: {
type: "Object",
properties: [],
},
tokens: [],
range,
};
const text = "{}";
const sourceCode = new JSONSourceCode({
text,
ast,
});

assert.strictEqual(sourceCode.getRange(ast), range);
});
});

describe("comments", () => {
it("should contain an empty array when parsing JSON", () => {
const file = { body: "{}", path: "test.json" };
Expand Down

0 comments on commit 2225f63

Please sign in to comment.