Skip to content

Commit

Permalink
Add jref-util tests and related bugfix/cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
jdesrosiers committed Feb 12, 2025
1 parent 18920f3 commit 4535c97
Show file tree
Hide file tree
Showing 19 changed files with 688 additions and 106 deletions.
31 changes: 18 additions & 13 deletions src/hyperjump/embedded.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { fromJref, toJref } from "../jref/index.js";

/**
* @import { DocumentNode } from "./index.js"
* @import { JrefNode } from "../jref/index.js"
* @import { Reviver } from "../jref/index.js"
*/


Expand All @@ -16,22 +18,25 @@ describe("JSON Browser", () => {
const testMediaType = "application/prs.hyperjump-embedded-test";

beforeAll(() => {
/** @type (uri: string, text: string, embedded?: Record<string, any>) => DocumentNode */
/** @type (uri: string, text: string, embedded?: Record<string, DocumentNode>) => DocumentNode */
const parseToDocument = (uri, text, embedded = {}) => {
/** @type Reviver<JrefNode | undefined> */
const embeddedReviver = (node, key) => {
if (key === "$embedded" && node.type === "json" && node.jsonType === "object") {
for (const propertyNode of node.children) {
const embeddedUri = toAbsoluteIri(propertyNode.children[0].value);
const embeddedJref = toJref(propertyNode.children[1], uri);
embedded[embeddedUri] = parseToDocument(embeddedUri, embeddedJref, embedded);
}
return;
} else {
return node;
}
};

return {
uri: uri,
children: [fromJref(text, uri, (node, key) => {
if (key === "$embedded" && node.type === "json" && node.jsonType === "object") {
for (const propertyNode of node.children) {
const embeddedUri = toAbsoluteIri(propertyNode.children[0].value);
const embeddedJref = toJref(propertyNode.children[1], uri);
embedded[embeddedUri] = parseToDocument(embeddedUri, embeddedJref, embedded);
}
return;
} else {
return node;
}
})],
children: [fromJref(text, uri, embeddedReviver)],
fragmentKind: "json-pointer",
embedded: embedded
};
Expand Down
2 changes: 1 addition & 1 deletion src/hyperjump/get.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe("JSON Browser", () => {
const browser = new Hyperjump();
const subject = await browser.get(uri);

expect(toJref(subject, uri)).to.eql(jref);
expect(toJref(subject, uri, undefined, " ")).to.eql(jref);
});

test("follow fragment-only reference", async () => {
Expand Down
8 changes: 4 additions & 4 deletions src/hyperjump/hyperjump.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import { JrefMediaTypePlugin } from "./media-types/jref-media-type-plugin.js";
import { pointerGet, pointerStep } from "../jref/jref-util.js";

/**
* @import { JrefNode } from "../jref/jref-ast.d.ts"
* @import { JsonCompatible, JsonType } from "../json/jsonast.d.ts"
* @import { UriSchemePlugin } from "./uri-schemes/uri-scheme-plugin.d.ts"
* @import { DocumentNode, MediaTypePlugin } from "./media-types/media-type-plugin.d.ts"
* @import { JrefNode } from "../jref/jref-ast.js"
* @import { JsonCompatible, JsonType } from "../json/jsonast.js"
* @import { UriSchemePlugin } from "./uri-schemes/uri-scheme-plugin.js"
* @import { DocumentNode, MediaTypePlugin } from "./media-types/media-type-plugin.js"
*/


Expand Down
8 changes: 3 additions & 5 deletions src/hyperjump/media-types/jref-media-type-plugin.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { fromJref } from "../../jref/jref-util.js";

/**
* @import { MediaTypePlugin } from "./media-type-plugin.d.ts"
* @import { JrefDocumentNode, JrefNode } from "../../jref/jref-ast.d.ts"
* @import { MediaTypePlugin } from "./media-type-plugin.js"
* @import { JrefDocumentNode } from "../../jref/jref-ast.js"
*/


Expand All @@ -22,9 +22,7 @@ export class JrefMediaTypePlugin {
async parse(response) {
return {
type: "jref-document",
children: [
/** @type JrefNode */ (fromJref(await response.text(), response.url))
],
children: [fromJref(await response.text(), response.url)],
uri: response.url,
fragmentKind: "json-pointer"
};
Expand Down
8 changes: 3 additions & 5 deletions src/hyperjump/media-types/json-media-type-plugin.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { fromJson } from "../../json/jsonast-util.js";

/**
* @import { MediaTypePlugin } from "./media-type-plugin.d.ts"
* @import { JsonDocumentNode, JsonNode } from "../../json/jsonast.js"
* @import { MediaTypePlugin } from "./media-type-plugin.js"
* @import { JsonDocumentNode } from "../../json/jsonast.js"
*/


Expand All @@ -22,9 +22,7 @@ export class JsonMediaTypePlugin {
async parse(response) {
return {
type: "json-document",
children: [
/** @type JsonNode */ (fromJson(await response.text()))
]
children: [fromJson(await response.text())]
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/hyperjump/uri-schemes/file-scheme-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Readable } from "node:stream";

/**
* @import { Hyperjump } from "../index.js"
* @import { UriSchemePlugin } from "./uri-scheme-plugin.d.ts"
* @import { UriSchemePlugin } from "./uri-scheme-plugin.js"
*/


Expand Down
2 changes: 1 addition & 1 deletion src/hyperjump/uri-schemes/http-scheme-plugin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* @import { Hyperjump } from "../index.js"
* @import { UriSchemePlugin } from "./uri-scheme-plugin.d.ts"
* @import { UriSchemePlugin } from "./uri-scheme-plugin.js"
*/


Expand Down
3 changes: 3 additions & 0 deletions src/jref/jref-ast.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Data, Position } from "unist";
import {
JsonArrayNode,
JsonBooleanNode,
JsonCompatible,
JsonNullNode,
JsonNumberNode,
JsonObjectNode,
Expand All @@ -26,6 +27,8 @@ export type JrefNode = JsonObjectNode<JrefNode>
| JsonNullNode
| JrefReferenceNode;

export type JrefCompatible<A> = JsonCompatible<A> | JrefReferenceNode;

export type JrefDocumentNode = {
type: "jref-document";
children: JrefNode[];
Expand Down
14 changes: 3 additions & 11 deletions src/jref/jref-parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { fromJref } from "./jref-util.js";
* @import { Plugin } from "unified"
* @import { VFile } from "vfile"
* @import { Options } from "vfile-message"
* @import { JrefDocumentNode } from "./jref-ast.d.ts"
* @import { JrefDocumentNode } from "./jref-ast.js"
*/


Expand All @@ -16,20 +16,12 @@ export function jrefParse() {
this.parser = function (document, file) {
try {
const uri = pathToFileURL(file.path).toString();
/** @type JrefDocumentNode */
const jrefDocument = {
return {
type: "jref-document",
children: [],
children: [fromJref(document, uri)],
uri: uri,
fragmentKind: "json-pointer"
};

const node = fromJref(document, uri);
if (node) {
jrefDocument.children.push(node);
}

return jrefDocument;
} catch (error) {
if (error instanceof VFileMessage) {
return file.fail(error.message, /** @type Options */ (error));
Expand Down
2 changes: 1 addition & 1 deletion src/jref/jref-stringify.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { toJref } from "./jref-util.js";
/**
* @import { Plugin } from "unified"
* @import { Node } from "unist"
* @import { JrefDocumentNode } from "./jref-ast.d.ts"
* @import { JrefDocumentNode } from "./jref-ast.js"
* @import { Replacer } from "./jref-util.js"
*/

Expand Down
31 changes: 14 additions & 17 deletions src/jref/jref-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,39 @@ import {
} from "../json/jsonast-util.js";

/**
* @import { JsonObjectNode } from "../json/jsonast.d.ts"
* @import { JrefNode } from "./jref-ast.d.ts"
* @import { JsonObjectNode } from "../json/jsonast.js"
* @import { JrefCompatible, JrefNode } from "./jref-ast.js"
*/


/**
* @template [A = JrefNode]
* @typedef {(node: JrefNode, key?: string) => A | undefined} Reviver
* @template {JrefNode | undefined} A
* @typedef {(node: JrefCompatible<NonNullable<A>>, key?: string) => A} Reviver
*/

/** @type Reviver<any> */
const defaultReviver = (value) => value;

/** @type (jref: string, uri: string, reviver?: Reviver) => JrefNode | undefined */
/** @type <A extends JrefNode | undefined = JrefNode>(jref: string, uri: string, reviver?: Reviver<A>) => A */
export const fromJref = (jref, uri, reviver = defaultReviver) => {
return fromJson(jref, (node, key) => {
/** @type JrefNode */
let jrefNode = node;

if (node.jsonType === "object") {
const href = isReference(node);
if (href) {
jrefNode = {
return reviver({
type: "jref-reference",
value: resolveIri(href, uri),
documentUri: uri,
position: node.position
};
}, key);
}
}

return reviver(jrefNode, key);
return reviver(node, key);
});
};

/** @type (node: JsonObjectNode<JrefNode>) => string | undefined */
/** @type <A extends JrefNode>(node: JsonObjectNode<A>) => string | undefined */
const isReference = (objectNode) => {
for (const propertyNode of objectNode.children) {
if (propertyNode.children[0].value === "$ref") {
Expand All @@ -55,16 +52,16 @@ const isReference = (objectNode) => {
};

/**
* @typedef {(key: string | undefined, value: JrefNode) => JrefNode} Replacer
* @typedef {(value: JrefNode, key?: string) => JrefNode} Replacer
*/

/** @type Replacer */
const defaultReplacer = (_key, node) => node;
const defaultReplacer = (node) => node;

/** @type (node: JrefNode, uri: string, replacer?: Replacer, space?: string) => string */
export const toJref = (node, uri, replacer = defaultReplacer, space = " ") => {
return toJson(node, (key, node) => {
node = replacer.call(this, key, node);
export const toJref = (node, uri, replacer = defaultReplacer, space = "") => {
return toJson(node, (node, key) => {
node = replacer(node, key);

if (node.type === "jref-reference") {
/** @type JsonObjectNode<JrefNode> */
Expand Down
Loading

0 comments on commit 4535c97

Please sign in to comment.