Skip to content

Commit fa3cccb

Browse files
committed
feat: preserve compound schema refs when bundling
1 parent 53d0c1e commit fa3cccb

2 files changed

Lines changed: 41 additions & 2 deletions

File tree

lib/bundle.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface InventoryEntry {
2020
circular: any;
2121
extended: any;
2222
external: any;
23+
nestedResource: boolean;
2324
indirections: any;
2425
}
2526
/**
@@ -194,6 +195,7 @@ function inventory$Ref<S extends object = JSONSchema, O extends ParserOptions<S>
194195
const file = url.stripHash(pointer.path);
195196
const hash = url.getHash(pointer.path);
196197
const external = file !== $refs._root$Ref.path && !$refs._aliases[file];
198+
const nestedResource = Boolean($refs._aliases[file]) && pointer.$ref.value !== $refs._root$Ref.value;
197199
const extended = $Ref.isExtended$Ref($ref);
198200
indirections += pointer.indirections;
199201

@@ -219,6 +221,7 @@ function inventory$Ref<S extends object = JSONSchema, O extends ParserOptions<S>
219221
circular: pointer.circular, // Is this $ref pointer DIRECTLY circular? (i.e. it references itself)
220222
extended, // Does this $ref extend its resolved value? (i.e. it has extra properties, in addition to "$ref")
221223
external, // Does this $ref pointer point to a file other than the main JSON Schema file?
224+
nestedResource, // Does this $ref resolve to an embedded schema resource with its own $id?
222225
indirections, // The number of indirect references that were traversed to resolve the value
223226
});
224227

@@ -316,8 +319,10 @@ function remap<S extends object = JSONSchema, O extends ParserOptions<S> = Parse
316319
if (!entry.external) {
317320
// This $ref already resolves to the main JSON Schema file.
318321
// When optimizeInternalRefs is false, preserve the original internal ref path
319-
// instead of rewriting it to the fully resolved hash.
320-
if (bundleOpts.optimizeInternalRefs !== false) {
322+
// instead of rewriting it to the fully resolved hash. References to nested
323+
// resources must also retain their resource URI so that "#" does not point
324+
// at the document root instead.
325+
if (bundleOpts.optimizeInternalRefs !== false && !entry.nestedResource) {
321326
entry.$ref.$ref = entry.hash;
322327
}
323328
} else if (entry.file === file && entry.hash === hash) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { describe, expect, it } from "vitest";
2+
import $RefParser from "../../../lib/index.js";
3+
4+
describe("Compound schema documents", () => {
5+
it("preserves references to embedded schema resources when bundling (issue #422)", async () => {
6+
const schema = {
7+
$id: "https://example.com/schemas/customer",
8+
$schema: "https://json-schema.org/draft/2020-12/schema",
9+
type: "object",
10+
properties: {
11+
shipping_address: { $ref: "/schemas/address" },
12+
billing_address: { $ref: "/schemas/address" },
13+
},
14+
$defs: {
15+
address: {
16+
$id: "https://example.com/schemas/address",
17+
type: "object",
18+
properties: {
19+
state: { $ref: "#/definitions/state" },
20+
},
21+
definitions: {
22+
state: { enum: ["CA", "NY"] },
23+
},
24+
},
25+
},
26+
};
27+
28+
const bundled = await $RefParser.bundle(schema);
29+
30+
expect(bundled.properties.shipping_address.$ref).toBe("/schemas/address");
31+
expect(bundled.properties.billing_address.$ref).toBe("/schemas/address");
32+
expect(bundled.$defs.address.properties.state.$ref).toBe("#/definitions/state");
33+
});
34+
});

0 commit comments

Comments
 (0)