@expo/cli
Version:
150 lines (149 loc) • 5.87 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "jsonSchemaDeref", {
enumerable: true,
get: function() {
return jsonSchemaDeref;
}
});
/** Return JSON schema ref if input is of `NodeRef` type */ const getRef = (node)=>node != null && typeof node === 'object' && '$ref' in node && typeof node.$ref === 'string' ? node.$ref : undefined;
/** Parse a JSON schema ref into a path array, or return undefined */ const parseRefMaybe = (ref)=>{
if (ref[0] !== '#') {
return undefined;
}
const props = [];
let startIndex = 1;
let index = 1;
let char;
while(index < ref.length){
while((char = ref.charCodeAt(index++)) && char !== 47 /*'/'*/ );
const prop = ref.slice(startIndex, index - 1);
startIndex = index;
if (prop) props.push(prop);
}
return props.length ? props : undefined;
};
const NOT_FOUND_SYMBOL = Symbol();
/** Get value at given JSON schema path or return `NOT_FOUND_SYMBOL` */ const getValueAtPath = (input, ref)=>{
let node = input;
for(let index = 0; index < ref.length; index++){
const part = ref[index];
if (node != null && typeof node === 'object' && part in node) {
node = node[part];
} else {
node = NOT_FOUND_SYMBOL;
break;
}
}
return node;
};
/** Find all JSON schema refs recursively and add them to `refs` Map */ const findRefsRec = (node, refs, path)=>{
if (node == null || typeof node !== 'object') {} else if (Array.isArray(node)) {
for(let index = 0, l = node.length; index < l; index++){
const value = node[index];
const ref = getRef(value);
if (ref) {
const targetRef = parseRefMaybe(ref);
if (targetRef) refs.set([
...path,
index
], targetRef);
} else if (value != null && typeof value === 'object') {
path.push(index);
findRefsRec(value, refs, path);
path.pop();
}
}
} else {
const record = node;
for(const key in record){
const value = record[key];
const ref = getRef(value);
if (ref) {
const targetRef = parseRefMaybe(ref);
if (targetRef) refs.set([
...path,
key
], targetRef);
} else if (value != null && typeof value === 'object') {
path.push(key);
findRefsRec(value, refs, path);
path.pop();
}
}
}
};
/** Detect whether target (where we set the source value) is a nested path inside the source path */ const isSelfReferencingRefEntry = (target, source)=>{
for(let index = 0; index < source.length; index++){
if (source[index] !== target[index]) return false;
}
return true;
};
/** Return sorted refs entries. Longest target paths will be returned first */ const getSortedRefEntries = (refs)=>{
const entries = [
...refs.entries()
].sort((a, b)=>b[1].length - a[1].length);
// Filter out self-referenceing paths. If we set nested targets to source values, we'd
// create unserializable circular references
return entries.filter((entry)=>!isSelfReferencingRefEntry(entry[0], entry[1]));
};
function jsonSchemaDeref(input) {
// Find all JSON schema refs paths
const refs = new Map();
findRefsRec(input, refs, []);
// Shallow copy output
const output = {
...input
};
// Process all ref entries with deepest targets first
nextRef: for (const [target, source] of getSortedRefEntries(refs)){
let inputNode = input;
let outputNode = output;
let targetIndex = 0;
// For each path part on the target, traverse the output and clone the input
// to not pollute it
for(; targetIndex < target.length - 1; targetIndex++){
const part = target[targetIndex];
if (inputNode == null || typeof inputNode !== 'object' || !(part in inputNode)) {
break;
} else if (outputNode[part] === inputNode[part]) {
// Copy the input on the output if references are equal
outputNode[part] = Array.isArray(inputNode[part]) ? [
...inputNode[part]
] : {
...inputNode[part]
};
inputNode = inputNode[part];
outputNode = outputNode[part];
} else {
break;
}
}
// For each remaining part on the target, continue traversing the output
for(; targetIndex < target.length - 1; targetIndex++){
const part = target[targetIndex];
if (outputNode == null || typeof outputNode !== 'object' || !(part in outputNode)) {
continue nextRef;
} else {
outputNode = outputNode[part];
}
}
// Get value from output
let sourceValue = getValueAtPath(output, source);
if (sourceValue === NOT_FOUND_SYMBOL) {
// If no value was found, try to get a value from the input instead
sourceValue = getValueAtPath(input, source);
// Otherwise, skip this ref
if (sourceValue === NOT_FOUND_SYMBOL) continue;
}
// Set the source value on the target path
// The for-loops prior have made sure that the output has already been deeply
// cloned and traversed for the entire path
outputNode[target[target.length - 1]] = sourceValue;
}
// Return the output with resolved refs
return output;
}
//# sourceMappingURL=jsonSchemaDeref.js.map
;