@json-schema-tools/referencer 1.1.2 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/build/index.d.ts +7 -1
- package/build/index.js +106 -34
- package/package.json +18 -15
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</span>
|
|
10
10
|
</center>
|
|
11
11
|
|
|
12
|
-
Referencer is a package that exports a single function - a function that accepts and returns a JSON Schema. The returned schema is 'flat', as in, any subschemas of the schema have been converted into $refs. Further, any of the subschemas' subschema are also $reffed,
|
|
12
|
+
Referencer is a package that exports a single function - a function that accepts and returns a JSON Schema. The returned schema is 'flat', as in, any subschemas of the schema have been converted into $refs. Further, any of the subschemas' subschema are also $reffed, recursively until everything is a $ref, and the definition section is fully populated.
|
|
13
13
|
|
|
14
14
|
The input schema may have refs, but the refs must already be in the definitions section.
|
|
15
15
|
The input schema, as well as all of its subschemas must have titles. Their titles must also be unique for their content. We would like to use $id, but it has special meaning and therefor title is used as the unique identifier on-which schemas will be referenced.
|
package/build/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSONSchema } from "@json-schema-tools/meta-schema";
|
|
2
|
-
export declare const stringifyCircular: (obj:
|
|
2
|
+
export declare const stringifyCircular: (obj: unknown) => string;
|
|
3
3
|
export declare class NoTitleError implements Error {
|
|
4
4
|
name: string;
|
|
5
5
|
message: string;
|
|
@@ -13,6 +13,11 @@ export interface ReferencerOptions {
|
|
|
13
13
|
* If true, the schema passed in will be referenced in-place, mutating the original. Default is false.
|
|
14
14
|
*/
|
|
15
15
|
mutate?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* By setting this to `true`, only complex types (`object`) will be replaced.
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
20
|
+
onlyComplex?: boolean;
|
|
16
21
|
}
|
|
17
22
|
/**
|
|
18
23
|
* Returns the schema where all subschemas have been replaced with $refs and added to definitions.
|
|
@@ -22,6 +27,7 @@ export interface ReferencerOptions {
|
|
|
22
27
|
* exists in the input schema's definitions object.
|
|
23
28
|
*
|
|
24
29
|
* @param s The schema to create references for (ie 'flatten' it)
|
|
30
|
+
* @param options The custom options
|
|
25
31
|
*
|
|
26
32
|
* @returns input schema where subschemas are turned into refs (recursively)
|
|
27
33
|
*
|
package/build/index.js
CHANGED
|
@@ -15,10 +15,51 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.NoTitleError = exports.stringifyCircular = void 0;
|
|
18
|
+
exports.default = referencer;
|
|
18
19
|
var traverse_1 = __importDefault(require("@json-schema-tools/traverse"));
|
|
19
20
|
var deleteAllProps = function (o) {
|
|
20
21
|
Object.keys(o)
|
|
21
|
-
.forEach(function (k) {
|
|
22
|
+
.forEach(function (k) { if (k !== 'title')
|
|
23
|
+
delete o[k]; });
|
|
24
|
+
};
|
|
25
|
+
var isObject = function (obj) {
|
|
26
|
+
return obj && obj === Object(obj);
|
|
27
|
+
};
|
|
28
|
+
var isComplex = function (obj) {
|
|
29
|
+
// TODO: evaluate if `array` should be considered complex
|
|
30
|
+
return (obj.type === 'object' && typeof obj.properties === 'object');
|
|
31
|
+
};
|
|
32
|
+
var hasReference = function (obj) {
|
|
33
|
+
return typeof obj.$ref === 'string' && obj.$ref.length > 0 && obj.$ref.indexOf("#") !== -1;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Replaces direct references to the root schema with a $ref to its definition.
|
|
37
|
+
*
|
|
38
|
+
* Since @json-schema-tools/traverse 1.12, a cycle back to the root invokes the
|
|
39
|
+
* mutation with the root's mutable copy (not the original schema), and the return
|
|
40
|
+
* value becomes the new root. The cycle site therefore cannot be fixed from inside
|
|
41
|
+
* the callback; instead the callback records the root definition and this post-pass
|
|
42
|
+
* swaps every remaining identity-reference to the root for the $ref.
|
|
43
|
+
*/
|
|
44
|
+
var breakRootCycles = function (root, originalRoot, title) {
|
|
45
|
+
var visited = new Set();
|
|
46
|
+
var visit = function (node) {
|
|
47
|
+
if (typeof node !== "object" || node === null || visited.has(node)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
visited.add(node);
|
|
51
|
+
for (var _i = 0, _a = Object.keys(node); _i < _a.length; _i++) {
|
|
52
|
+
var key = _a[_i];
|
|
53
|
+
var child = node[key];
|
|
54
|
+
if (child === root || child === originalRoot) {
|
|
55
|
+
node[key] = { $ref: "#/definitions/".concat(title) };
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
visit(child);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
visit(root);
|
|
22
63
|
};
|
|
23
64
|
var stringifyCircular = function (obj) {
|
|
24
65
|
var cache = [];
|
|
@@ -41,13 +82,13 @@ var NoTitleError = /** @class */ (function () {
|
|
|
41
82
|
try {
|
|
42
83
|
schemaStr = JSON.stringify(schema);
|
|
43
84
|
}
|
|
44
|
-
catch (
|
|
85
|
+
catch (_a) {
|
|
45
86
|
schemaStr = (0, exports.stringifyCircular)(schema);
|
|
46
87
|
}
|
|
47
88
|
try {
|
|
48
89
|
parentSchemaStr = JSON.stringify(parentSchema);
|
|
49
90
|
}
|
|
50
|
-
catch (
|
|
91
|
+
catch (_b) {
|
|
51
92
|
parentSchemaStr = (0, exports.stringifyCircular)(parentSchema);
|
|
52
93
|
}
|
|
53
94
|
this.message = [
|
|
@@ -75,6 +116,7 @@ var defaultReferencerOptions = {
|
|
|
75
116
|
* exists in the input schema's definitions object.
|
|
76
117
|
*
|
|
77
118
|
* @param s The schema to create references for (ie 'flatten' it)
|
|
119
|
+
* @param options The custom options
|
|
78
120
|
*
|
|
79
121
|
* @returns input schema where subschemas are turned into refs (recursively)
|
|
80
122
|
*
|
|
@@ -84,54 +126,84 @@ var defaultReferencerOptions = {
|
|
|
84
126
|
*/
|
|
85
127
|
function referencer(s, options) {
|
|
86
128
|
if (options === void 0) { options = defaultReferencerOptions; }
|
|
129
|
+
var _a = options.onlyComplex, onlyComplex = _a === void 0 ? false : _a, _b = options.mutate, mutate = _b === void 0 ? false : _b;
|
|
87
130
|
var definitions = {};
|
|
131
|
+
// Set when a cycle back to the root schema is found. Kept as a flag because
|
|
132
|
+
// traverse >= 1.12 reports the root cycle against the root's mutable copy
|
|
133
|
+
// (whose return value becomes the new root), so the cycle site is repaired
|
|
134
|
+
// in a post-pass via breakRootCycles instead of inside the callback.
|
|
135
|
+
var rootCycleTitle;
|
|
88
136
|
var newS = (0, traverse_1.default)(s, function (subSchema, isRootCycle) {
|
|
89
137
|
var t = "";
|
|
90
|
-
if (
|
|
91
|
-
if (
|
|
92
|
-
|
|
138
|
+
if (!isObject(subSchema)) { // For schema that is boolean
|
|
139
|
+
if (onlyComplex) {
|
|
140
|
+
return subSchema;
|
|
141
|
+
}
|
|
142
|
+
if (subSchema === true) {
|
|
143
|
+
t = "AlwaysTrue";
|
|
144
|
+
definitions[t] = true;
|
|
145
|
+
}
|
|
146
|
+
else if (subSchema === false) {
|
|
147
|
+
t = "AlwaysFalse";
|
|
148
|
+
definitions[t] = false;
|
|
149
|
+
}
|
|
150
|
+
return subSchema;
|
|
151
|
+
}
|
|
152
|
+
// Otherwise it is a object schema
|
|
153
|
+
var objectSchema = subSchema;
|
|
154
|
+
if (isRootCycle) {
|
|
155
|
+
if (objectSchema.$ref) {
|
|
156
|
+
var title = objectSchema.$ref.replace("#/definitions/", "");
|
|
93
157
|
var hasDefForRef = definitions[title];
|
|
94
158
|
if (hasDefForRef === undefined) {
|
|
95
|
-
throw new Error("Encountered unknown $ref: ".concat(
|
|
159
|
+
throw new Error("Encountered unknown $ref: ".concat(objectSchema.$ref));
|
|
96
160
|
}
|
|
97
|
-
return
|
|
161
|
+
return objectSchema;
|
|
98
162
|
}
|
|
99
|
-
if (
|
|
163
|
+
if (objectSchema === s) {
|
|
100
164
|
definitions[s.title] = { $ref: "#" };
|
|
101
165
|
return { $ref: "#/definitions/".concat(s.title) };
|
|
102
166
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
167
|
+
// A cycle back to the root schema. Since traverse 1.12 the callback
|
|
168
|
+
// receives the root's mutable copy rather than the original schema,
|
|
169
|
+
// so the root is detected by title (titles are unique by contract)
|
|
170
|
+
// instead of object identity. The cycle site itself is repaired by
|
|
171
|
+
// breakRootCycles after traversal, because the callback's return
|
|
172
|
+
// value now becomes the new root.
|
|
173
|
+
if (objectSchema.title === s.title) {
|
|
174
|
+
var rootTitle = s.title;
|
|
175
|
+
if (typeof rootTitle !== "string") {
|
|
176
|
+
// A cycle back to the root cannot be represented without a title
|
|
177
|
+
// to key the definition by (see #585).
|
|
178
|
+
throw new NoTitleError(objectSchema, s);
|
|
179
|
+
}
|
|
180
|
+
definitions[rootTitle] = { $ref: "#" };
|
|
181
|
+
rootCycleTitle = rootTitle;
|
|
182
|
+
return objectSchema;
|
|
183
|
+
}
|
|
184
|
+
definitions[objectSchema.title] = __assign({}, objectSchema);
|
|
185
|
+
objectSchema.$ref = "#/definitions/".concat(objectSchema.title);
|
|
186
|
+
return objectSchema;
|
|
117
187
|
}
|
|
118
|
-
|
|
119
|
-
if (typeof
|
|
120
|
-
throw new NoTitleError(
|
|
188
|
+
if (!hasReference(objectSchema) && (!onlyComplex || isComplex(objectSchema))) {
|
|
189
|
+
if (typeof objectSchema.title !== "string") {
|
|
190
|
+
throw new NoTitleError(objectSchema, s);
|
|
121
191
|
}
|
|
122
|
-
t =
|
|
123
|
-
definitions[t] = __assign({},
|
|
124
|
-
deleteAllProps(
|
|
125
|
-
|
|
192
|
+
t = objectSchema.title;
|
|
193
|
+
definitions[t] = __assign({}, objectSchema);
|
|
194
|
+
deleteAllProps(objectSchema);
|
|
195
|
+
objectSchema.$ref = "#/definitions/".concat(t);
|
|
126
196
|
}
|
|
127
|
-
return
|
|
128
|
-
}, { mutable:
|
|
129
|
-
if (
|
|
197
|
+
return objectSchema;
|
|
198
|
+
}, { mutable: mutate, skipFirstMutation: true });
|
|
199
|
+
if (mutate === true) {
|
|
130
200
|
newS = s;
|
|
131
201
|
}
|
|
132
202
|
if (typeof newS === "object" && Object.keys(definitions).length > 0) {
|
|
133
203
|
newS.definitions = __assign(__assign({}, newS.definitions), definitions);
|
|
134
204
|
}
|
|
205
|
+
if (rootCycleTitle !== undefined && typeof newS === "object" && newS !== null) {
|
|
206
|
+
breakRootCycles(newS, s, rootCycleTitle);
|
|
207
|
+
}
|
|
135
208
|
return newS;
|
|
136
209
|
}
|
|
137
|
-
exports.default = referencer;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@json-schema-tools/referencer",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"main": "build/index.js",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "npm run build:code && typedoc --out docs && touch docs/.nojekyll",
|
|
10
10
|
"build:code": "tsc",
|
|
11
|
-
"lint": "eslint .
|
|
11
|
+
"lint": "eslint .",
|
|
12
12
|
"test": "npm run test:unit",
|
|
13
|
-
"test:unit": "jest --coverage"
|
|
13
|
+
"test:unit": "jest --coverage --runInBand",
|
|
14
|
+
"coverage:bump": "jest-it-up"
|
|
14
15
|
},
|
|
15
16
|
"author": "BelfordZ <belfordz66@gmail.com>",
|
|
16
17
|
"license": "Apache-2.0",
|
|
@@ -26,22 +27,24 @@
|
|
|
26
27
|
"!build/**/*.test.*"
|
|
27
28
|
],
|
|
28
29
|
"devDependencies": {
|
|
29
|
-
"@
|
|
30
|
-
"@json-schema-tools/
|
|
31
|
-
"@json-schema-tools/
|
|
32
|
-
"@json-schema-tools/
|
|
30
|
+
"@eslint/js": "^9.9.1",
|
|
31
|
+
"@json-schema-tools/dereferencer": "^1.6.3",
|
|
32
|
+
"@json-schema-tools/meta-schema": "^1.7.5",
|
|
33
|
+
"@json-schema-tools/titleizer": "^1.0.9",
|
|
34
|
+
"@json-schema-tools/transpiler": "^1.10.4",
|
|
33
35
|
"@types/jest": "^29.2.0",
|
|
34
|
-
"@types/node": "^
|
|
35
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
36
|
-
"@typescript-eslint/parser": "^
|
|
37
|
-
"eslint": "^
|
|
36
|
+
"@types/node": "^22.5.0",
|
|
37
|
+
"@typescript-eslint/eslint-plugin": "^8.2.0",
|
|
38
|
+
"@typescript-eslint/parser": "^8.2.0",
|
|
39
|
+
"eslint": "^10.12.0",
|
|
38
40
|
"jest": "^29.2.2",
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
41
|
+
"jest-it-up": "^4.0.1",
|
|
42
|
+
"ts-jest": "^29.2.5",
|
|
43
|
+
"typedoc": "^0.28.20",
|
|
44
|
+
"typescript": "^5.5.4"
|
|
42
45
|
},
|
|
43
46
|
"dependencies": {
|
|
44
|
-
"@json-schema-tools/traverse": "^1.
|
|
47
|
+
"@json-schema-tools/traverse": "^1.15.1"
|
|
45
48
|
},
|
|
46
49
|
"homepage": "https://github.com/json-schema-tools/referencer#readme",
|
|
47
50
|
"description": "Get a copy of a JSON Schema where all subschemas are turned into `$ref`s with corresponding definitions section."
|