diff --git a/.node-version b/.node-version index 2a4e4ab..72e4a48 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -16.17.0 +18.14.2 diff --git a/README.md b/README.md index ff05478..2ca41f0 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ The input schema, as well as all of its subschemas must have titles. Their title Features: - Cyclic schemas become cyclic references - Completely synchronous + - immutable by default, option to mutate in place. - No external dependencies - Fully typed against the generated [meta-schema typings](https://github.com/json-schema-tools/meta-schema/) - magically makes your json schema smaller. diff --git a/package-lock.json b/package-lock.json index 8310c0f..20cb024 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3490,9 +3490,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -7393,9 +7393,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "jsonc-parser": { diff --git a/src/index.test.ts b/src/index.test.ts index 99d93c5..eb1e49a 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -103,21 +103,6 @@ describe("referencer", () => { expect(props.anotherFoo.$ref).toBe("#/definitions/foo"); }); - it("does mutate the input schema", () => { - const testSchema = { - title: "foo", - type: "object", - properties: { bar: { title: "bar", type: "number" } }, - }; - - const reffed = referencer(testSchema as JSONSchema) as JSONSchemaObject; - - const props = reffed.properties as Properties; - - expect(props.bar.$ref).toBe("#/definitions/bar"); - expect(reffed).toBe(testSchema); - }); - it("already has some refs with dupes", () => { const testSchema = { title: "anyOfTheThings", @@ -163,4 +148,37 @@ describe("referencer", () => { expect(defs.allOfFoo.allOf[1].$ref).toBe("#/definitions/baz"); expect(defs.baz.properties.cba.$ref).toBe("#/definitions/cba"); }); + + describe("Mutable", () => { + it("does not mutate the input schema", () => { + const testSchema = Object.freeze({ + title: "foo", + type: "object", + properties: { bar: { title: "bar", type: "number" } }, + }); + + const reffed = referencer(testSchema as JSONSchema) as JSONSchemaObject; + + const props = reffed.properties as Properties; + + expect(props.bar.$ref).toBe("#/definitions/bar"); + expect(reffed).not.toBe(testSchema); + }); + + it("does mutate the input schema when options.mutate === true", () => { + const testSchema = { + title: "foo", + type: "object", + properties: { bar: { title: "bar", type: "number" } }, + }; + + const reffed = referencer(testSchema as JSONSchema, { mutate: true }) as JSONSchemaObject; + + const props = reffed.properties as Properties; + + expect(props.bar.$ref).toBe("#/definitions/bar"); + expect(reffed).toBe(testSchema); + }); + + }); }); diff --git a/src/index.ts b/src/index.ts index 74f38c1..9a773f4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -52,6 +52,20 @@ export class NoTitleError implements Error { } } +/** + * Options that can be passed into the Referencer. + */ +export interface ReferencerOptions { + /** + * If true, the schema passed in will be referenced in-place, mutating the original. Default is false. + */ + mutate?: boolean; +} + +const defaultReferencerOptions = { + mutate: false, +}; + /** * Returns the schema where all subschemas have been replaced with $refs and added to definitions. * @@ -67,10 +81,10 @@ export class NoTitleError implements Error { * @category SchemaImprover * */ -export default function referencer(s: JSONSchema): JSONSchema { +export default function referencer(s: JSONSchema, options: ReferencerOptions = defaultReferencerOptions): JSONSchema { const definitions: any = {}; - traverse( + let newS = traverse( s, (subSchema: JSONSchema, isRootCycle: boolean) => { let t = ""; @@ -117,12 +131,16 @@ export default function referencer(s: JSONSchema): JSONSchema { return subSchema; }, - { mutable: true, skipFirstMutation: true }, + { mutable: options.mutate, skipFirstMutation: true }, ); - if (typeof s === "object" && Object.keys(definitions).length > 0) { - s.definitions = { ...s.definitions, ...definitions }; + if (options.mutate === true) { + newS = s; + } + + if (typeof newS === "object" && Object.keys(definitions).length > 0) { + newS.definitions = { ...newS.definitions, ...definitions }; } - return s; + return newS; }