json-machete
Version:
776 lines (766 loc) • 35.8 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
const utils = require('@graphql-tools/utils');
const JsonPointer = _interopDefault(require('json-pointer'));
const crossHelpers = require('@graphql-mesh/cross-helpers');
const urlJoin = _interopDefault(require('url-join'));
const fetch = require('@whatwg-node/fetch');
const utils$1 = require('@graphql-mesh/utils');
const toJsonSchema = _interopDefault(require('to-json-schema'));
const identicalFn = (a) => a;
const objectFields = [
'additionalProperties',
'additionalItems',
'contains',
'else',
'if',
'items',
'not',
'then',
];
const dictFields = ['anyOf', 'allOf', 'oneOf', 'definitions', 'properties', 'patternProperties'];
async function visitJSONSchema(schema, { enter = identicalFn, leave = identicalFn, }, { visitedSubschemaResultMap, path } = {
visitedSubschemaResultMap: new WeakMap(),
path: '',
}) {
var _a;
if (typeof schema === 'object') {
if (!visitedSubschemaResultMap.has(schema)) {
const enterResult = await enter(schema, {
visitedSubschemaResultMap,
path,
});
visitedSubschemaResultMap.set(schema, enterResult);
for (const key of objectFields) {
if (enterResult[key]) {
enterResult[key] = await visitJSONSchema(enterResult[key], { enter, leave }, {
visitedSubschemaResultMap,
path: `${path}/${key}`,
});
}
}
for (const key of dictFields) {
if (enterResult[key]) {
const entries = Object.entries(enterResult[key]);
for (const [itemKey, itemValue] of entries) {
enterResult[key][itemKey] = await visitJSONSchema(itemValue, { enter, leave }, { visitedSubschemaResultMap, path: `${path}/${key}/${itemKey}` });
}
}
}
if ((_a = enterResult.components) === null || _a === void 0 ? void 0 : _a.schema) {
const entries = Object.entries(enterResult.components.schemas);
for (const [schemaName, subSchema] of entries) {
enterResult.components.schemas[schemaName] = await visitJSONSchema(subSchema, { enter, leave }, { visitedSubschemaResultMap, path: `${path}/components/schemas/${schemaName}` });
}
}
const leaveResult = await leave(enterResult, {
visitedSubschemaResultMap,
path,
});
visitedSubschemaResultMap.set(schema, leaveResult);
return leaveResult;
}
return visitedSubschemaResultMap.get(schema);
}
const enterResult = await enter(schema, {
visitedSubschemaResultMap,
path,
});
return leave(enterResult, {
visitedSubschemaResultMap,
path,
});
}
const resolvePath = (path, root) => {
var _a;
try {
return JsonPointer.get(root, path);
}
catch (e) {
if ((_a = e.message) === null || _a === void 0 ? void 0 : _a.startsWith('Invalid reference')) {
return undefined;
}
throw e;
}
};
function isRefObject(obj) {
return typeof obj === 'object' && obj.$ref;
}
function isURL(str) {
return /^https?:\/\//.test(str);
}
const getAbsolute$Ref = (given$ref, baseFilePath) => {
const [givenExternalFileRelativePath, givenRefPath] = given$ref.split('#');
if (givenExternalFileRelativePath) {
const cwd = isURL(baseFilePath) ? getCwdForUrl(baseFilePath) : crossHelpers.path.dirname(baseFilePath);
const givenExternalFilePath = getAbsolutePath(givenExternalFileRelativePath, cwd);
if (givenRefPath) {
return `${givenExternalFilePath}#${givenRefPath}`;
}
return givenExternalFilePath;
}
return `${baseFilePath}#${givenRefPath}`;
};
function getCwdForUrl(url) {
const urlParts = url.split('/');
urlParts.pop();
return urlParts.join('/');
}
function normalizeUrl(url) {
return new URL(url).toString();
}
function getAbsolutePath(path, cwd) {
if (isURL(path)) {
return path;
}
if (isURL(cwd)) {
return normalizeUrl(urlJoin(cwd, path));
}
if (crossHelpers.path.isAbsolute(path)) {
return path;
}
return crossHelpers.path.join(cwd, path);
}
function getCwd(path) {
return isURL(path) ? getCwdForUrl(path) : crossHelpers.path.dirname(path);
}
// eslint-disable-next-line @typescript-eslint/ban-types
async function dereferenceObject(obj, { cwd = crossHelpers.process.cwd(), externalFileCache = new Map(), refMap = new Map(), root = obj, fetchFn: fetch$1 = fetch.fetch, importFn = utils$1.defaultImportFn, logger = new utils$1.DefaultLogger('dereferenceObject'), headers, } = {}) {
var _a;
if (obj != null && typeof obj === 'object') {
if (isRefObject(obj)) {
const $ref = obj.$ref;
if (refMap.has($ref)) {
return refMap.get($ref);
}
else {
if (crossHelpers.process.env.DEBUG) {
console.log(`Dereferencing `, obj);
}
const [externalRelativeFilePath, refPath] = $ref.split('#');
if (externalRelativeFilePath) {
const externalFilePath = getAbsolutePath(externalRelativeFilePath, cwd);
const newCwd = getCwd(externalFilePath);
let externalFile = externalFileCache.get(externalFilePath);
if (!externalFile) {
externalFile = await utils$1.readFileOrUrl(externalFilePath, {
fetch: fetch$1,
headers,
cwd,
importFn,
logger,
}).catch(() => {
throw new Error(`Unable to load ${externalRelativeFilePath} from ${cwd}`);
});
externalFileCache.set(externalFilePath, externalFile);
// Title should not be overwritten by the title given from the reference
// Usually Swagger and OpenAPI Schemas have this
if (externalFile.definitions) {
for (const definitionName in externalFile.definitions) {
const definition = externalFile.definitions[definitionName];
if (!definition.title) {
definition.title = definitionName;
}
}
}
if ((_a = externalFile.components) === null || _a === void 0 ? void 0 : _a.schemas) {
for (const definitionName in externalFile.components.schemas) {
const definition = externalFile.components.schemas[definitionName];
if (!definition.title) {
definition.title = definitionName;
}
}
}
}
const result = await dereferenceObject(refPath
? {
$ref: `#${refPath}`,
}
: externalFile, {
cwd: newCwd,
externalFileCache,
refMap: new Proxy(refMap, {
get: (originalRefMap, key) => {
switch (key) {
case 'has':
return (given$ref) => {
const original$Ref = getAbsolute$Ref(given$ref, externalFilePath);
return originalRefMap.has(original$Ref);
};
case 'get':
return (given$ref) => {
const original$Ref = getAbsolute$Ref(given$ref, externalFilePath);
return originalRefMap.get(original$Ref);
};
case 'set':
return (given$ref, val) => {
const original$Ref = getAbsolute$Ref(given$ref, externalFilePath);
return originalRefMap.set(original$Ref, val);
};
}
throw new Error('Not implemented ' + key.toString());
},
}),
fetchFn: fetch$1,
importFn,
logger,
headers,
root: externalFile,
});
refMap.set($ref, result);
if (result && !result.$resolvedRef) {
result.$resolvedRef = refPath;
}
if (obj.title && !result.title) {
result.title = obj.title;
}
return result;
}
else {
const resolvedObj = resolvePath(refPath, root);
if (typeof resolvedObj === 'object' && !resolvedObj.$ref) {
refMap.set($ref, resolvedObj);
}
if (resolvedObj && !resolvedObj.$resolvedRef) {
resolvedObj.$resolvedRef = refPath;
}
const result = await dereferenceObject(resolvedObj, {
cwd,
externalFileCache,
refMap,
root,
fetchFn: fetch$1,
importFn,
logger,
headers,
});
refMap.set($ref, result);
if (result && !result.$resolvedRef) {
result.$resolvedRef = refPath;
}
return result;
}
}
}
else {
for (const key in obj) {
const val = obj[key];
if (typeof val === 'object') {
obj[key] = await dereferenceObject(val, {
cwd,
externalFileCache,
refMap,
root,
fetchFn: fetch$1,
headers,
});
}
}
}
}
return obj;
}
async function compareJSONSchemas(oldSchema, newSchema) {
const breakingChanges = [];
await visitJSONSchema(oldSchema, {
enter: (oldSubSchema, { path }) => {
var _a, _b, _c, _d;
if (typeof newSchema === 'object') {
const newSubSchema = resolvePath(path, newSchema);
if (typeof oldSubSchema === 'boolean') {
if (newSubSchema !== oldSubSchema) {
breakingChanges.push(`${path} is changed from ${oldSubSchema} to ${newSubSchema}`);
}
}
else {
if (oldSubSchema.$ref) {
if ((newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.$ref) !== oldSubSchema.$ref) {
breakingChanges.push(`${path}/$ref is changed from ${oldSubSchema.$ref} to ${newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.$ref}`);
}
}
if (oldSubSchema.const) {
if ((newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.const) !== oldSubSchema.const) {
breakingChanges.push(`${path}/const is changed from ${oldSubSchema.const} to ${newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.const}`);
}
}
if (oldSubSchema.enum) {
for (const enumValue of oldSubSchema.enum) {
if (!((_a = newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.enum) === null || _a === void 0 ? void 0 : _a.includes(enumValue))) {
breakingChanges.push(`${path}/enum doesn't have ${enumValue} anymore`);
}
}
}
if (oldSubSchema.format) {
if ((newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.format) !== oldSubSchema.format) {
breakingChanges.push(`${path}/format is changed from ${oldSubSchema.format} to ${newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.format}`);
}
}
if (oldSubSchema.maxLength) {
if (oldSubSchema.maxLength > (newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.maxLength)) {
breakingChanges.push(`${path}/maxLength is changed from ${oldSubSchema.maxLength} to ${newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.maxLength}`);
}
}
if (oldSubSchema.minLength) {
if (oldSubSchema.minLength < (newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.minLength)) {
breakingChanges.push(`${path}/minLength is changed from ${oldSubSchema.minLength} to ${newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.minLength}`);
}
}
if (oldSubSchema.pattern) {
if (((_b = newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.pattern) === null || _b === void 0 ? void 0 : _b.toString()) !== oldSubSchema.pattern.toString()) {
breakingChanges.push(`${path}/pattern is changed from ${oldSubSchema.pattern} to ${newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.pattern}`);
}
}
if (oldSubSchema.properties) {
for (const propertyName in oldSubSchema.properties) {
if (((_c = newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.properties) === null || _c === void 0 ? void 0 : _c[propertyName]) == null) {
breakingChanges.push(`${path}/properties doesn't have ${propertyName}`);
}
}
}
if (newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.required) {
for (const propertyName of newSubSchema.required) {
if (!((_d = oldSubSchema.required) === null || _d === void 0 ? void 0 : _d.includes(propertyName))) {
breakingChanges.push(`${path}/required has ${propertyName} an extra`);
}
}
}
if (oldSubSchema.title) {
if ((newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.title) !== oldSubSchema.title) {
breakingChanges.push(`${path}/title is changed from ${oldSubSchema.title} to ${newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.title}`);
}
}
if (oldSubSchema.type) {
if (typeof (newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.type) === 'string'
? (newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.type) !== oldSubSchema.type
: Array.isArray(newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.type)
? Array.isArray(oldSubSchema.type)
? oldSubSchema.type.some(typeName => !(newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.type.includes(typeName)))
: !(newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.type.includes(oldSubSchema.type))
: true) {
breakingChanges.push(`${path}/type is changed from ${oldSubSchema.type} to ${newSubSchema === null || newSubSchema === void 0 ? void 0 : newSubSchema.type}`);
}
}
}
}
return oldSubSchema;
},
}, {
visitedSubschemaResultMap: new WeakMap(),
path: '',
});
if (breakingChanges.length > 0) {
throw new utils.AggregateError(breakingChanges.map(breakingChange => new Error(breakingChange)), `Breaking changes are found:\n${breakingChanges.join('\n')}`);
}
}
const asArray = (value) => (Array.isArray(value) ? value : [value]);
const reservedTypeNames = ['Query', 'Mutation', 'Subscription'];
const JSONSchemaStringFormats = [
'date',
'hostname',
'regex',
'json-pointer',
'relative-json-pointer',
'uri-reference',
'uri-template',
'date-time',
'time',
'email',
'ipv4',
'ipv6',
'uri',
];
const AnySchema = {
title: 'Any',
oneOf: [
{ type: 'string' },
{ type: 'integer' },
{ type: 'boolean' },
{ type: 'number' },
{ type: 'object', additionalProperties: true },
],
};
const titleResolvedRefReservedMap = new WeakMap();
function removeTitlesAndResolvedRefs(schema) {
if (typeof schema === 'object' && schema != null && !titleResolvedRefReservedMap.has(schema)) {
if (!schema.$comment) {
const titleReserved = schema.title;
if (titleReserved) {
schema.title = undefined;
}
const resolvedRefReserved = schema.$resolvedRef;
if (resolvedRefReserved) {
schema.$resolvedRef = undefined;
}
titleResolvedRefReservedMap.set(schema, {
title: titleReserved,
$resolvedRef: resolvedRefReserved,
});
for (const key in schema) {
if (key === 'properties') {
for (const propertyName in schema.properties) {
schema[key][propertyName] = removeTitlesAndResolvedRefs(schema[key][propertyName]);
}
}
else {
schema[key] = removeTitlesAndResolvedRefs(schema[key]);
}
}
}
}
return schema;
}
function deduplicateJSONSchema(schema, seenMap = new Map()) {
if (typeof schema === 'object' && schema != null) {
if (!schema.$comment) {
const stringified = utils.inspect(schema.properties || schema)
.split('[Circular]')
.join('[Object]');
const seen = seenMap.get(stringified);
if (seen) {
return seen;
}
seenMap.set(stringified, schema);
for (const key in schema) {
if (key === 'properties') {
for (const propertyName in schema.properties) {
schema.properties[propertyName] = deduplicateJSONSchema(schema.properties[propertyName], seenMap);
}
}
else {
schema[key] = deduplicateJSONSchema(schema[key], seenMap);
}
}
}
}
return schema;
}
const visited = new WeakSet();
function addTitlesAndResolvedRefs(schema) {
if (typeof schema === 'object' && schema != null && !visited.has(schema)) {
visited.add(schema);
if (!schema.$comment) {
const reservedTitleAndResolveRef = titleResolvedRefReservedMap.get(schema);
if (reservedTitleAndResolveRef) {
if (!schema.title && reservedTitleAndResolveRef.title) {
schema.title = reservedTitleAndResolveRef.title;
}
if (!schema.$resolvedRef && reservedTitleAndResolveRef.$resolvedRef) {
schema.$resolvedRef = reservedTitleAndResolveRef.$resolvedRef;
}
}
for (const key in schema) {
if (key === 'properties') {
for (const propertyName in schema.properties) {
schema.properties[propertyName] = addTitlesAndResolvedRefs(schema.properties[propertyName]);
}
}
else {
schema[key] = addTitlesAndResolvedRefs(schema[key]);
}
}
}
}
return schema;
}
async function getDeduplicatedTitles(schema) {
const duplicatedTypeNames = new Set();
const seenTypeNames = new Set();
await visitJSONSchema(schema, {
leave: subSchema => {
if (typeof subSchema === 'object' && subSchema.title) {
if (seenTypeNames.has(subSchema.title)) {
duplicatedTypeNames.add(subSchema.title);
}
else {
seenTypeNames.add(subSchema.title);
}
}
return subSchema;
},
}, {
visitedSubschemaResultMap: new WeakMap(),
path: '',
});
return duplicatedTypeNames;
}
async function healJSONSchema(schema, options = {}) {
let readySchema = schema;
if (!(options === null || options === void 0 ? void 0 : options.noDeduplication)) {
const schemaWithoutResolvedRefAndTitles = removeTitlesAndResolvedRefs(schema);
const deduplicatedSchemaWithoutResolvedRefAndTitles = (options === null || options === void 0 ? void 0 : options.noDeduplication)
? schema
: deduplicateJSONSchema(schemaWithoutResolvedRefAndTitles);
const deduplicatedSchema = addTitlesAndResolvedRefs(deduplicatedSchemaWithoutResolvedRefAndTitles);
readySchema = deduplicatedSchema;
}
const duplicatedTypeNames = await getDeduplicatedTitles(readySchema);
return visitJSONSchema(readySchema, {
enter: async function healSubschema(subSchema, { path }) {
if (typeof subSchema === 'object') {
if (crossHelpers.process.env.DEBUG) {
console.log(`Healing ${path}`);
}
// We don't support following properties
delete subSchema.readOnly;
delete subSchema.writeOnly;
const keys = Object.keys(subSchema);
if (keys.length === 0) {
subSchema.type = 'object';
subSchema.additionalProperties = true;
}
if (typeof subSchema.additionalProperties === 'object') {
delete subSchema.additionalProperties.readOnly;
delete subSchema.additionalProperties.writeOnly;
if (Object.keys(subSchema.additionalProperties).length === 0) {
subSchema.additionalProperties = true;
}
}
if (subSchema.allOf != null && subSchema.allOf.length === 1) {
const realSubschema = subSchema.allOf[0];
delete subSchema.allOf;
return realSubschema;
}
if (subSchema.anyOf != null && subSchema.anyOf.length === 1) {
const realSubschema = subSchema.anyOf[0];
delete subSchema.anyOf;
return realSubschema;
}
if (subSchema.oneOf != null && subSchema.oneOf.length === 1) {
const realSubschema = subSchema.oneOf[0];
delete subSchema.oneOf;
return realSubschema;
}
if (subSchema.description != null) {
subSchema.description = subSchema.description.trim();
if (keys.length === 1) {
subSchema.type = 'object';
subSchema.additionalProperties = true;
}
}
// Some JSON Schemas use this broken pattern and refer the type using `items`
if (subSchema.type === 'object' && subSchema.items) {
const realSubschema = subSchema.items;
delete subSchema.items;
return realSubschema;
}
if (subSchema.properties && subSchema.type !== 'object') {
subSchema.type = 'object';
}
if (duplicatedTypeNames.has(subSchema.title)) {
delete subSchema.title;
}
if (typeof subSchema.example === 'object' && !subSchema.type) {
subSchema.type = 'object';
}
// Try to find the type
if (!subSchema.type) {
// If required exists without properties
if (subSchema.required && !subSchema.properties && !subSchema.anyOf && !subSchema.allOf) {
// Add properties
subSchema.properties = {};
for (const missingPropertyName of subSchema.required) {
subSchema.properties[missingPropertyName] = AnySchema;
}
}
// Properties only exist in objects
if (subSchema.properties || subSchema.patternProperties || 'additionalProperties' in subSchema) {
subSchema.type = 'object';
}
// Items only exist in arrays
if (subSchema.items) {
subSchema.type = 'array';
// Items should be an object
if (Array.isArray(subSchema.items)) {
if (subSchema.items.length === 0) {
subSchema.items = subSchema.items[0];
}
else {
subSchema.items = {
oneOf: subSchema.items,
};
}
}
}
if (subSchema.format === 'int64') {
subSchema.type = 'integer';
}
if (subSchema.format) {
subSchema.type = 'string';
}
}
if (subSchema.type === 'string' && !subSchema.format && (subSchema.examples || subSchema.example)) {
const examples = asArray(subSchema.examples || subSchema.example || []);
if (examples === null || examples === void 0 ? void 0 : examples.length) {
const { format } = toJsonSchema(examples[0]);
if (format) {
subSchema.format = format;
}
}
}
if (subSchema.format === 'dateTime') {
subSchema.format = 'date-time';
}
if (subSchema.type === 'string' && subSchema.format) {
if (!JSONSchemaStringFormats.includes(subSchema.format)) {
delete subSchema.format;
}
}
if (subSchema.required) {
if (!Array.isArray(subSchema.required)) {
delete subSchema.required;
}
}
// If it is an object type but no properties given while example is available
if (((subSchema.type === 'object' && !subSchema.properties) || !subSchema.type) && subSchema.example) {
const generatedSchema = toJsonSchema(subSchema.example, {
required: false,
objects: {
additionalProperties: false,
},
strings: {
detectFormat: true,
},
arrays: {
mode: 'first',
},
});
const healedGeneratedSchema = await healJSONSchema(generatedSchema, options);
subSchema.type = asArray(healedGeneratedSchema.type)[0];
subSchema.properties = healedGeneratedSchema.properties;
// If type for properties is already given, use it
if (typeof subSchema.additionalProperties === 'object') {
for (const propertyName in subSchema.properties) {
subSchema.properties[propertyName] = subSchema.additionalProperties;
}
}
}
if (!subSchema.title && !subSchema.$ref && subSchema.type !== 'array' && !subSchema.items) {
const realPath = subSchema.$resolvedRef || path;
// Try to get definition name if missing
const splitByDefinitions = realPath.includes('/components/schemas/')
? realPath.split('/components/schemas/')
: realPath.split('/definitions/');
const maybeDefinitionBasedPath = splitByDefinitions.length > 1 ? splitByDefinitions[splitByDefinitions.length - 1] : realPath;
let pathBasedName = maybeDefinitionBasedPath
.split('~1')
.join('/')
.split('/properties')
.join('')
.split('-')
.join('_')
.split('/')
.filter(Boolean)
.join('_');
switch (subSchema.type) {
case 'string':
// If it has special pattern, use path based name because it is specific
if (subSchema.pattern || subSchema.maxLength || subSchema.minLength || subSchema.enum) {
subSchema.title = pathBasedName;
// Otherwise use the format name
}
else if (subSchema.format) {
subSchema.title = subSchema.format;
}
break;
case 'number':
case 'integer':
if (subSchema.enum) {
subSchema.title = pathBasedName;
// Otherwise use the format name
}
else if (subSchema.format) {
subSchema.title = subSchema.format;
}
break;
case 'array':
break;
default:
subSchema.title = subSchema.title || pathBasedName;
}
// If type name is reserved, add a suffix
if (reservedTypeNames.includes(pathBasedName)) {
pathBasedName += '_';
}
}
if (subSchema.type === 'object' && subSchema.properties && Object.keys(subSchema.properties).length === 0) {
delete subSchema.properties;
subSchema.additionalProperties = true;
}
}
return subSchema;
},
}, {
visitedSubschemaResultMap: new WeakMap(),
path: '',
});
}
async function referenceJSONSchema(schema) {
const initialDefinitions = {};
const { $ref: initialRef } = await visitJSONSchema(schema, {
enter: (subSchema, { path }) => {
if (typeof subSchema === 'object') {
if (crossHelpers.process.env.DEBUG) {
console.log(`Referencing ${path}`);
}
// Remove $id refs
delete subSchema.$id;
if (subSchema.$ref) {
return subSchema;
}
else if (subSchema.title) {
if (subSchema.title in initialDefinitions) {
let cnt = 2;
while (`${subSchema.title}${cnt}` in initialDefinitions) {
cnt++;
}
const definitionProp = `${subSchema.title}${cnt}`.split(' ').join('_SPACE_');
initialDefinitions[definitionProp] = subSchema;
return {
$ref: `#/definitions/${definitionProp}`,
...subSchema,
};
}
else {
const definitionProp = subSchema.title.split(' ').join('_SPACE_');
initialDefinitions[definitionProp] = subSchema;
return {
$ref: `#/definitions/${definitionProp}`,
...subSchema,
};
}
}
else if (subSchema.type === 'object') {
console.warn(`${path} cannot be referenced because it has no title`);
}
}
return subSchema;
},
});
const { definitions: finalDefinitions } = await visitJSONSchema({
definitions: initialDefinitions,
}, {
enter: subSchema => {
if (typeof subSchema === 'object') {
if (subSchema.$ref) {
return {
$ref: subSchema.$ref,
};
}
}
return subSchema;
},
});
return {
$ref: initialRef,
definitions: finalDefinitions,
};
}
exports.compareJSONSchemas = compareJSONSchemas;
exports.dereferenceObject = dereferenceObject;
exports.getAbsolutePath = getAbsolutePath;
exports.getCwd = getCwd;
exports.healJSONSchema = healJSONSchema;
exports.referenceJSONSchema = referenceJSONSchema;
exports.resolvePath = resolvePath;
exports.visitJSONSchema = visitJSONSchema;