Skip to content

Commit adc3983

Browse files
committed
minimum repro of bundle error for bug with json schema validator site
1 parent e4ecbc5 commit adc3983

File tree

7 files changed

+112
-546
lines changed

7 files changed

+112
-546
lines changed

.yarn/install-state.gz

15.7 KB
Binary file not shown.

lib/bundle.ts

+49-16
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@ import type $RefParser from "./index";
66
import type { ParserOptions } from "./index";
77
import type { JSONSchema } from "./index";
88

9+
export interface InventoryEntry {
10+
$ref: any;
11+
parent: any;
12+
key: any;
13+
pathFromRoot: any;
14+
depth: any;
15+
file: any;
16+
hash: any;
17+
value: any;
18+
circular: any;
19+
extended: any;
20+
external: any;
21+
indirections: any;
22+
}
923
/**
1024
* Bundles all external JSON references into the main JSON schema, thus resulting in a schema that
1125
* only has *internal* references, not any *external* references.
@@ -21,7 +35,7 @@ function bundle<S extends object = JSONSchema, O extends ParserOptions<S> = Pars
2135
// console.log('Bundling $ref pointers in %s', parser.$refs._root$Ref.path);
2236

2337
// Build an inventory of all $ref pointers in the JSON Schema
24-
const inventory: any = [];
38+
const inventory: InventoryEntry[] = [];
2539
crawl<S, O>(parser, "schema", parser.$refs._root$Ref.path + "#", "#", 0, inventory, parser.$refs, options);
2640

2741
// Remap all $ref pointers
@@ -41,16 +55,16 @@ function bundle<S extends object = JSONSchema, O extends ParserOptions<S> = Pars
4155
* @param options
4256
*/
4357
function crawl<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(
44-
parent: any,
58+
parent: object | $RefParser<S, O>,
4559
key: string | null,
4660
path: string,
4761
pathFromRoot: string,
4862
indirections: number,
49-
inventory: unknown[],
63+
inventory: InventoryEntry[],
5064
$refs: $Refs<S, O>,
5165
options: O,
5266
) {
53-
const obj = key === null ? parent : parent[key];
67+
const obj = key === null ? parent : parent[key as keyof typeof parent];
5468

5569
if (obj && typeof obj === "object" && !ArrayBuffer.isView(obj)) {
5670
if ($Ref.isAllowed$Ref(obj)) {
@@ -71,7 +85,7 @@ function crawl<S extends object = JSONSchema, O extends ParserOptions<S> = Parse
7185
// This produces the shortest possible bundled references
7286
return a.length - b.length;
7387
}
74-
});
88+
}) as (keyof typeof obj)[];
7589

7690
// eslint-disable-next-line no-shadow
7791
for (const key of keys) {
@@ -104,11 +118,11 @@ function crawl<S extends object = JSONSchema, O extends ParserOptions<S> = Parse
104118
*/
105119
function inventory$Ref<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOptions<S>>(
106120
$refParent: any,
107-
$refKey: any,
121+
$refKey: string | null,
108122
path: string,
109-
pathFromRoot: any,
110-
indirections: any,
111-
inventory: any,
123+
pathFromRoot: string,
124+
indirections: number,
125+
inventory: InventoryEntry[],
112126
$refs: $Refs<S, O>,
113127
options: O,
114128
) {
@@ -118,7 +132,8 @@ function inventory$Ref<S extends object = JSONSchema, O extends ParserOptions<S>
118132
if (pointer === null) {
119133
return;
120134
}
121-
const depth = Pointer.parse(pathFromRoot).length;
135+
const parsed = Pointer.parse(pathFromRoot);
136+
const depth = parsed.length;
122137
const file = url.stripHash(pointer.path);
123138
const hash = url.getHash(pointer.path);
124139
const external = file !== $refs._root$Ref.path;
@@ -178,9 +193,9 @@ function inventory$Ref<S extends object = JSONSchema, O extends ParserOptions<S>
178193
*
179194
* @param inventory
180195
*/
181-
function remap(inventory: any) {
196+
function remap(inventory: InventoryEntry[]) {
182197
// Group & sort all the $ref pointers, so they're in the order that we need to dereference/remap them
183-
inventory.sort((a: any, b: any) => {
198+
inventory.sort((a: InventoryEntry, b: InventoryEntry) => {
184199
if (a.file !== b.file) {
185200
// Group all the $refs that point to the same file
186201
return a.file < b.file ? -1 : +1;
@@ -243,23 +258,41 @@ function remap(inventory: any) {
243258
entry.$ref.$ref = entry.pathFromRoot;
244259
}
245260
}
246-
247-
// console.log(' new value: %s', (entry.$ref && entry.$ref.$ref) ? entry.$ref.$ref : '[object Object]');
248261
}
262+
263+
// we want to ensure that any $refs that point to another $ref are remapped to point to the final value
264+
// let hadChange = true;
265+
// while (hadChange) {
266+
// hadChange = false;
267+
// for (const entry of inventory) {
268+
// if (entry.$ref && typeof entry.$ref === "object" && "$ref" in entry.$ref) {
269+
// const resolved = inventory.find((e: InventoryEntry) => e.pathFromRoot === entry.$ref.$ref);
270+
// if (resolved) {
271+
// const resolvedPointsToAnotherRef =
272+
// resolved.$ref && typeof resolved.$ref === "object" && "$ref" in resolved.$ref;
273+
// if (resolvedPointsToAnotherRef && entry.$ref.$ref !== resolved.$ref.$ref) {
274+
// // console.log('Re-mapping $ref pointer "%s" at %s', entry.$ref.$ref, entry.pathFromRoot);
275+
// entry.$ref.$ref = resolved.$ref.$ref;
276+
// hadChange = true;
277+
// }
278+
// }
279+
// }
280+
// }
281+
// }
249282
}
250283

251284
/**
252285
* TODO
253286
*/
254-
function findInInventory(inventory: any, $refParent: any, $refKey: any) {
287+
function findInInventory(inventory: InventoryEntry[], $refParent: any, $refKey: any) {
255288
for (const existingEntry of inventory) {
256289
if (existingEntry && existingEntry.parent === $refParent && existingEntry.key === $refKey) {
257290
return existingEntry;
258291
}
259292
}
260293
}
261294

262-
function removeFromInventory(inventory: any, entry: any) {
295+
function removeFromInventory(inventory: InventoryEntry[], entry: any) {
263296
const index = inventory.indexOf(entry);
264297
inventory.splice(index, 1);
265298
}

test/specs/bundle/bundle.spec.ts

-8
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,12 @@ import { expect } from "vitest";
33
import $RefParser from "../../../lib/index.js";
44
import path from "../../utils/path";
55
import dereferencedSchema from "./bundled";
6-
import Ajv from "ajv";
7-
import addFormats from "ajv-formats";
86

97
describe("Bundles", () => {
108
it("should bundle correctly", async () => {
119
const parser = new $RefParser();
1210
const schema = path.rel("test/specs/bundle/schemaA.json");
1311
const bundled = await parser.bundle(schema);
14-
const derefed = await parser.dereference(bundled);
15-
const ajv = new Ajv();
16-
addFormats(ajv);
17-
18-
const compiled = ajv.compile(derefed);
19-
const compiledDerefed = ajv.compile(bundled);
2012
expect(bundled).to.deep.equal(dereferencedSchema);
2113
});
2214
});

0 commit comments

Comments
 (0)