Skip to content

Commit e61d1d7

Browse files
authored
Merge branch 'main' into fix/dates-as-strings
2 parents 402904c + 78f2cc4 commit e61d1d7

File tree

11 files changed

+223
-13
lines changed

11 files changed

+223
-13
lines changed

docs/options.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ $RefParser.dereference("my-schema.yaml", {
2929
}
3030
},
3131
dereference: {
32-
circular: false // Don't allow circular $refs
32+
circular: false, // Don't allow circular $refs
33+
excludedPathMatcher: (path) => // Skip dereferencing content under any 'example' key
34+
path.includes("/example/")
3335
}
3436
});
3537
```
@@ -75,3 +77,4 @@ The `dereference` options control how JSON Schema $Ref Parser will dereference `
7577
|Option(s) |Type |Description
7678
|:---------------------|:-------------------|:------------
7779
|`circular`|`boolean` or `"ignore"`|Determines whether [circular `$ref` pointers](README.md#circular-refs) are handled.<br><br>If set to `false`, then a `ReferenceError` will be thrown if the schema contains any circular references.<br><br> If set to `"ignore"`, then circular references will simply be ignored. No error will be thrown, but the [`$Refs.circular`](refs.md#circular) property will still be set to `true`.
80+
|`excludedPathMatcher`|`(string) => boolean`|A function, called for each path, which can return true to stop this path and all subpaths from being dereferenced further. This is useful in schemas where some subpaths contain literal $ref keys that should not be dereferenced.

lib/dereference.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ function crawl (obj, path, pathFromRoot, parents, processedObjects, dereferenced
4141
circular: false
4242
};
4343

44+
let isExcludedPath = options.dereference.excludedPathMatcher;
45+
4446
if (options.dereference.circular === "ignore" || !processedObjects.has(obj)) {
45-
if (obj && typeof obj === "object" && !ArrayBuffer.isView(obj)) {
47+
if (obj && typeof obj === "object" && !ArrayBuffer.isView(obj) && !isExcludedPath(pathFromRoot)) {
4648
parents.add(obj);
4749
processedObjects.add(obj);
4850

@@ -55,6 +57,11 @@ function crawl (obj, path, pathFromRoot, parents, processedObjects, dereferenced
5557
for (const key of Object.keys(obj)) {
5658
let keyPath = Pointer.join(path, key);
5759
let keyPathFromRoot = Pointer.join(pathFromRoot, key);
60+
61+
if (isExcludedPath(keyPathFromRoot)) {
62+
continue;
63+
}
64+
5865
let value = obj[key];
5966
let circular = false;
6067

lib/index.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,13 @@ declare namespace $RefParser {
231231
* If set to `"ignore"`, then circular references will simply be ignored. No error will be thrown, but the `$Refs.circular` property will still be set to `true`.
232232
*/
233233
circular?: boolean | "ignore";
234+
235+
/**
236+
* A function, called for each path, which can return true to stop this path and all
237+
* subpaths from being dereferenced further. This is useful in schemas where some
238+
* subpaths contain literal $ref keys that should not be dereferenced.
239+
*/
240+
excludedPathMatcher?(path: string): boolean;
234241
};
235242
}
236243

lib/options.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,16 @@ $RefParserOptions.defaults = {
7373
*
7474
* @type {boolean|string}
7575
*/
76-
circular: true
76+
circular: true,
77+
78+
/**
79+
* A function, called for each path, which can return true to stop this path and all
80+
* subpaths from being dereferenced further. This is useful in schemas where some
81+
* subpaths contain literal $ref keys that should not be dereferenced.
82+
*
83+
* @type {function}
84+
*/
85+
excludedPathMatcher: () => false
7786
},
7887
};
7988

lib/pointer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Pointer.prototype.resolve = function (obj, options, pathFromRoot) {
9191
let token = tokens[i];
9292
if (this.value[token] === undefined || this.value[token] === null) {
9393
this.value = null;
94-
throw new MissingPointerError(token, this.originalPath);
94+
throw new MissingPointerError(token, decodeURI(this.originalPath));
9595
}
9696
else {
9797
this.value = this.value[token];

lib/ref.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ $Ref.prototype.resolve = function (path, options, friendlyPath, pathFromRoot) {
135135
if (err instanceof InvalidPointerError) {
136136
// this is a special case - InvalidPointerError is thrown when dereferencing external file,
137137
// but the issue is caused by the source file that referenced the file that undergoes dereferencing
138-
err.source = stripHash(pathFromRoot);
138+
err.source = decodeURI(stripHash(pathFromRoot));
139139
}
140140

141141
this.addError(err);

lib/resolve-external.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ async function resolve$Ref ($ref, path, $refs, options) {
120120
}
121121

122122
if ($refs._$refs[withoutHash]) {
123-
err.source = url.stripHash(path);
123+
err.source = decodeURI(url.stripHash(path));
124124
err.path = url.safePointerToPath(url.getHash(path));
125125
}
126126

test/specs/error-source/error-source.spec.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ const { expect } = chai;
88
const $RefParser = require("../../..");
99
const helper = require("../../utils/helper");
1010
const path = require("../../utils/path");
11-
const { host } = require("@jsdevtools/host-environment");
1211
const { InvalidPointerError, ResolverError, MissingPointerError } = require("../../../lib/util/errors");
1312

1413

@@ -31,12 +30,6 @@ describe("Report correct error source and path for", () => {
3130
}
3231
});
3332

34-
if (host.node && host.os.windows && path.cwd().includes(" ")) {
35-
// The tests below don't support Windows file paths that contain spaces.
36-
// TODO: Fix the tests below, rather than skipping them
37-
return;
38-
}
39-
4033
it("schema with a local reference pointing at property with broken external reference", async () => {
4134
const parser = new $RefParser();
4235
try {
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
"use strict";
2+
3+
module.exports = {
4+
components: {
5+
examples: {
6+
"confirmation-failure": {
7+
value: {
8+
$ref: "#/literal-component-example"
9+
}
10+
},
11+
"confirmation-success": {
12+
value: {
13+
abc: "def"
14+
}
15+
},
16+
"query-example": {
17+
value: "abc"
18+
}
19+
},
20+
parameters: {
21+
a: {
22+
example: {
23+
$ref: "#/literal-param-component-example"
24+
}
25+
},
26+
b: {
27+
examples: {
28+
example1: {
29+
value: {
30+
$ref: "#/literal-param-component-examples1"
31+
}
32+
}
33+
}
34+
}
35+
}
36+
},
37+
paths: {
38+
"/x/{id}": {
39+
parameters: [
40+
{
41+
example: 123,
42+
in: "path",
43+
name: "id"
44+
},
45+
{
46+
examples: {
47+
e1: {
48+
value: {
49+
$ref: "#/literal-h1"
50+
}
51+
}
52+
},
53+
in: "header",
54+
name: "h1"
55+
},
56+
{
57+
example: {
58+
$ref: "#/literal-q1"
59+
},
60+
in: "query",
61+
name: "q1"
62+
},
63+
{
64+
examples: {
65+
q2: {
66+
value: "abc"
67+
}
68+
},
69+
in: "query",
70+
name: "q2"
71+
}
72+
],
73+
responses: {
74+
200: {
75+
content: {
76+
"application/json": {
77+
examples: {
78+
"confirmation-failure": {
79+
value: {
80+
$ref: "#/literal-component-example"
81+
}
82+
},
83+
"confirmation-in-progress": {
84+
summary: "In progress response",
85+
value: {
86+
$ref: "#/abc"
87+
}
88+
},
89+
"confirmation-success": {
90+
value: {
91+
abc: "def"
92+
}
93+
}
94+
}
95+
}
96+
}
97+
},
98+
400: {
99+
content: {
100+
"application/json": {
101+
example: {
102+
$ref: "#/literal-example"
103+
}
104+
}
105+
}
106+
}
107+
}
108+
}
109+
}
110+
};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"use strict";
2+
3+
const { expect } = require("chai");
4+
const $RefParser = require("../../..");
5+
const path = require("../../utils/path");
6+
const dereferencedSchema = require("./dereferenced");
7+
8+
describe("Schema with literal $refs in examples", () => {
9+
it("should exclude the given paths from dereferencing", async () => {
10+
let parser = new $RefParser();
11+
const schema = await parser.dereference(path.rel("specs/ref-in-excluded-path/ref-in-excluded-path.yaml"), {
12+
dereference: {
13+
excludedPathMatcher: (schemaPath) => {
14+
return /\/example(\/|$|s\/[^\/]+\/value(\/|$))/.test(schemaPath);
15+
}
16+
}
17+
});
18+
expect(schema).to.equal(parser.schema);
19+
expect(schema).to.deep.equal(dereferencedSchema);
20+
});
21+
});

0 commit comments

Comments
 (0)