json-machete
Version:
189 lines (188 loc) • 7.83 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolvePath = void 0;
exports.getAbsolutePath = getAbsolutePath;
exports.getCwd = getCwd;
exports.dereferenceObject = dereferenceObject;
const tslib_1 = require("tslib");
const json_pointer_1 = tslib_1.__importDefault(require("json-pointer"));
const url_join_1 = tslib_1.__importDefault(require("url-join"));
const healUntitledDefinitions_js_1 = require("./healUntitledDefinitions.js");
const resolvePath = (path, root) => {
try {
return json_pointer_1.default.get(root, path);
}
catch (e) {
if (e.message?.startsWith('Invalid reference')) {
return undefined;
}
throw e;
}
};
exports.resolvePath = resolvePath;
function isUrl(str) {
return /^https?:\/\//.test(str);
}
function isRefObject(obj) {
return typeof obj === 'object' && typeof obj.$ref === 'string';
}
const getAbsolute$Ref = (given$ref, baseFilePath) => {
const [givenExternalFileRelativePath, givenRefPath] = given$ref.split('#');
if (givenExternalFileRelativePath) {
const cwd = getCwd(baseFilePath);
const givenExternalFilePath = getAbsolutePath(givenExternalFileRelativePath, cwd);
if (givenRefPath) {
return `${givenExternalFilePath}#${givenRefPath}`;
}
return givenExternalFilePath;
}
return `${baseFilePath}#${givenRefPath}`;
};
function normalizeUrl(url) {
return new URL(url).toString();
}
function getAbsolutePath(path, cwd) {
if (isUrl(path)) {
return path;
}
if (isUrl(cwd)) {
return normalizeUrl((0, url_join_1.default)(cwd, path));
}
if (path.startsWith('/') || path.substring(1).startsWith(':\\')) {
return path;
}
return cwd + '/' + path;
}
function getCwd(path) {
const pathParts = path.split('/');
pathParts.pop();
return pathParts.join('/');
}
async function dereferenceObject(obj, { cwd = globalThis?.process.cwd(), fetch, externalFileCache = new Map(), refMap = new Map(), root = obj, debugLogFn, readFileOrUrl, resolvedObjects = new WeakSet(), }) {
if (obj != null && typeof obj === 'object') {
if (isRefObject(obj)) {
const $ref = obj.$ref;
if (refMap.has($ref)) {
return refMap.get($ref);
}
else {
debugLogFn?.(`Resolving ${$ref}`);
const [externalRelativeFilePath, refPath] = $ref.split('#');
if (externalRelativeFilePath) {
const externalFilePath = getAbsolutePath(externalRelativeFilePath, cwd);
const newCwd = getCwd(externalFilePath);
let externalFile = externalFileCache.get(externalFilePath);
if (!externalFile) {
try {
externalFile = await readFileOrUrl(externalFilePath, { cwd, fetch });
}
catch (e) {
console.error(e);
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
(0, healUntitledDefinitions_js_1.handleUntitledDefinitions)(externalFile);
}
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());
},
}),
debugLogFn,
readFileOrUrl,
root: externalFile,
resolvedObjects,
fetch,
});
refMap.set($ref, result);
resolvedObjects.add(result);
if (result && !result.$resolvedRef) {
result.$resolvedRef = refPath;
}
if (obj.title && !result.title) {
result.title = obj.title;
}
return result;
}
else {
const resolvedObj = (0, exports.resolvePath)(refPath, root);
if (resolvedObjects.has(resolvedObj)) {
refMap.set($ref, resolvedObj);
return resolvedObj;
}
/*
if (resolvedObj && !resolvedObj.$resolvedRef) {
resolvedObj.$resolvedRef = refPath;
}
*/
const result = await dereferenceObject(resolvedObj, {
cwd,
fetch,
externalFileCache,
refMap,
root,
debugLogFn,
readFileOrUrl,
resolvedObjects,
});
if (!result) {
return obj;
}
resolvedObjects.add(result);
refMap.set($ref, result);
if (!result.$resolvedRef) {
result.$resolvedRef = refPath;
}
return result;
}
}
}
else {
if (!resolvedObjects.has(obj)) {
resolvedObjects.add(obj);
for (const key in obj) {
const val = obj[key];
if (typeof val === 'object') {
obj[key] = await dereferenceObject(val, {
cwd,
externalFileCache,
refMap,
root,
debugLogFn,
readFileOrUrl,
resolvedObjects,
fetch,
});
}
}
}
}
}
return obj;
}