Skip to content

Commit

Permalink
fix: allow $ref pointers to point to a null value (#374)
Browse files Browse the repository at this point in the history
* fix: allow `$ref` pointers to point to a `null` value

* fix: removing a `.only()` test designation

* fix: pr feedback
  • Loading branch information
erunion authored Feb 27, 2025
1 parent 5303da9 commit da0fda2
Showing 5 changed files with 58 additions and 2 deletions.
12 changes: 12 additions & 0 deletions lib/pointer.ts
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@ import * as url from "./util/url.js";
import { JSONParserError, InvalidPointerError, MissingPointerError, isHandledError } from "./util/errors.js";
import type { JSONSchema } from "./types";

export const nullSymbol = Symbol('null');

const slashes = /\//g;
const tildes = /~/g;
const escapedSlash = /~1/g;
@@ -121,6 +123,16 @@ class Pointer<S extends object = JSONSchema, O extends ParserOptions<S> = Parser
continue;
}

// If the token we're looking for ended up not containing any slashes but is
// actually instead pointing to an existing `null` value then we should use that
// `null` value.
if (token in this.value && this.value[token] === null) {
// We use a `null` symbol for internal tracking to differntiate between a general `null`
// value and our expected `null` value.
this.value = nullSymbol;
continue;
}

this.value = null;

const path = this.$ref.path || "";
12 changes: 10 additions & 2 deletions lib/ref.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Pointer from "./pointer.js";
import Pointer, { nullSymbol } from "./pointer.js";
import type { JSONParserError, MissingPointerError, ParserError, ResolverError } from "./util/errors.js";
import { InvalidPointerError, isHandledError, normalizeError } from "./util/errors.js";
import { safePointerToPath, stripHash, getHash } from "./util/url.js";
@@ -119,7 +119,12 @@ class $Ref<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOpt
resolve(path: string, options?: O, friendlyPath?: string, pathFromRoot?: string) {
const pointer = new Pointer<S, O>(this, path, friendlyPath);
try {
return pointer.resolve(this.value, options, pathFromRoot);
const resolved = pointer.resolve(this.value, options, pathFromRoot);
if (resolved.value === nullSymbol) {
resolved.value = null;
}

return resolved;
} catch (err: any) {
if (!options || !options.continueOnError || !isHandledError(err)) {
throw err;
@@ -148,6 +153,9 @@ class $Ref<S extends object = JSONSchema, O extends ParserOptions<S> = ParserOpt
set(path: string, value: any) {
const pointer = new Pointer(this, path);
this.value = pointer.set(this.value, value);
if (this.value === nullSymbol) {
this.value = null;
}
}

/**
14 changes: 14 additions & 0 deletions test/specs/dereference-null-ref/dereference-null-ref.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { describe, it } from "vitest";
import { expect } from "vitest";
import $RefParser from "../../../lib/index.js";
import path from "../../utils/path";
import dereferenced from "./dereferenced.js";

describe("dereferencing a `$ref` that points to a `null` value", () => {
it("should dereference successfully", async () => {
const parser = new $RefParser();
const schema = await parser.dereference(path.rel("test/specs/dereference-null-ref/dereference-null-ref.yaml"));
expect(schema).to.equal(parser.schema);
expect(schema).to.deep.equal(dereferenced);
});
});
8 changes: 8 additions & 0 deletions test/specs/dereference-null-ref/dereference-null-ref.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
components:
examples:
product:
value:
data:
admission:
$ref: "#/components/examples/product/value/data/pas"
pas: null
14 changes: 14 additions & 0 deletions test/specs/dereference-null-ref/dereferenced.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default {
components: {
examples: {
product: {
value: {
data: {
admission: null,
pas: null,
},
},
},
},
},
};

0 comments on commit da0fda2

Please sign in to comment.