@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 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, recurively until everything is a $ref, and the definition section is fully populated.
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: any) => string;
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) { delete o[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 (e) {
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 (e) {
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 (isRootCycle && subSchema !== true && subSchema !== false) {
91
- if (subSchema.$ref) {
92
- var title = subSchema.$ref.replace("#/definitions/", "");
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(subSchema.$ref));
159
+ throw new Error("Encountered unknown $ref: ".concat(objectSchema.$ref));
96
160
  }
97
- return subSchema;
161
+ return objectSchema;
98
162
  }
99
- if (subSchema === s) {
163
+ if (objectSchema === s) {
100
164
  definitions[s.title] = { $ref: "#" };
101
165
  return { $ref: "#/definitions/".concat(s.title) };
102
166
  }
103
- definitions[subSchema.title] = __assign({}, subSchema);
104
- subSchema.$ref = "#/definitions/".concat(subSchema.title);
105
- return subSchema;
106
- }
107
- if (subSchema === true) {
108
- t = "AlwaysTrue";
109
- definitions[t] = true;
110
- }
111
- else if (subSchema === false) {
112
- t = "AlwaysFalse";
113
- definitions[t] = false;
114
- }
115
- else if (subSchema.$ref !== undefined && subSchema.$ref.indexOf("#") !== -1) {
116
- return subSchema;
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
- else {
119
- if (typeof subSchema.title !== "string") {
120
- throw new NoTitleError(subSchema, s);
188
+ if (!hasReference(objectSchema) && (!onlyComplex || isComplex(objectSchema))) {
189
+ if (typeof objectSchema.title !== "string") {
190
+ throw new NoTitleError(objectSchema, s);
121
191
  }
122
- t = subSchema.title;
123
- definitions[t] = __assign({}, subSchema);
124
- deleteAllProps(subSchema);
125
- subSchema.$ref = "#/definitions/".concat(t);
192
+ t = objectSchema.title;
193
+ definitions[t] = __assign({}, objectSchema);
194
+ deleteAllProps(objectSchema);
195
+ objectSchema.$ref = "#/definitions/".concat(t);
126
196
  }
127
- return subSchema;
128
- }, { mutable: options.mutate, skipFirstMutation: true });
129
- if (options.mutate === true) {
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.2",
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 . --ext .ts",
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
- "@json-schema-tools/dereferencer": "^1.6.2",
30
- "@json-schema-tools/meta-schema": "^1.7.0",
31
- "@json-schema-tools/titleizer": "^1.0.8",
32
- "@json-schema-tools/transpiler": "^1.10.3",
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": "^20.12.7",
35
- "@typescript-eslint/eslint-plugin": "^5.41.0",
36
- "@typescript-eslint/parser": "^5.41.0",
37
- "eslint": "^8.26.0",
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
- "ts-jest": "^29.0.3",
40
- "typedoc": "^0.25.13",
41
- "typescript": "^5.4.4"
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.10.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."