87 lines
3.9 KiB
JavaScript
87 lines
3.9 KiB
JavaScript
![]() |
/* eslint max-statements-per-line: ["error", { "max": 2 }] */
|
||
|
import { eachSchema } from "../eachSchema";
|
||
|
import joinScope from "./joinScope";
|
||
|
import getRef from "./getRef";
|
||
|
import { get } from "@sagold/json-pointer";
|
||
|
const COMPILED = "__compiled";
|
||
|
const COMPILED_REF = "__ref";
|
||
|
const GET_REF = "getRef";
|
||
|
const GET_ROOT = "getRoot";
|
||
|
const suffixes = /(#|\/)+$/g;
|
||
|
/**
|
||
|
* compiles the input root schema for `$ref` resolution and returns it again
|
||
|
* @attention this modifies input schema but maintains data-structure and thus returns
|
||
|
* the same object with JSON.stringify
|
||
|
*
|
||
|
* for a compiled json-schema you can call getRef on any contained schema (location of type).
|
||
|
* this resolves a $ref target to a valid schema (for a valid $ref)
|
||
|
*
|
||
|
* @param draft
|
||
|
* @param schemaToCompile - json-schema to compile
|
||
|
* @param [rootSchema] - compiled root json-schema to use for definitions resolution
|
||
|
* @param [force] = false - force compile json-schema
|
||
|
* @return compiled input json-schema
|
||
|
*/
|
||
|
export default function compileSchema(draft, schemaToCompile, rootSchema = schemaToCompile, force = false) {
|
||
|
if (!schemaToCompile || schemaToCompile[COMPILED] !== undefined) {
|
||
|
return schemaToCompile;
|
||
|
}
|
||
|
const context = { ids: {}, remotes: draft.remotes };
|
||
|
const rootSchemaAsString = JSON.stringify(schemaToCompile);
|
||
|
const compiledSchema = JSON.parse(rootSchemaAsString);
|
||
|
// flag this schema as compiled
|
||
|
Object.defineProperty(compiledSchema, COMPILED, { enumerable: false, value: true });
|
||
|
// add getRef-helper to this object
|
||
|
Object.defineProperty(compiledSchema, GET_REF, {
|
||
|
enumerable: false,
|
||
|
value: getRef.bind(null, context, compiledSchema)
|
||
|
});
|
||
|
// bail early, when no $refs are defined
|
||
|
if (force === false && rootSchemaAsString.includes("$ref") === false) {
|
||
|
return compiledSchema;
|
||
|
}
|
||
|
// compile this schema under rootSchema, making definitions available to $ref-resolution
|
||
|
if (schemaToCompile !== rootSchema) {
|
||
|
Object.defineProperty(compiledSchema, "definitions", {
|
||
|
enumerable: false,
|
||
|
value: Object.assign({}, rootSchema.definitions, rootSchema.$defs, schemaToCompile.definitions, schemaToCompile.$defs)
|
||
|
});
|
||
|
}
|
||
|
const scopes = {};
|
||
|
const getRoot = () => compiledSchema;
|
||
|
eachSchema(compiledSchema, (schema, pointer) => {
|
||
|
var _a;
|
||
|
if (schema.id) {
|
||
|
// if this is a schema being merged on root object, we cannot override
|
||
|
// parents locations, but must reuse it
|
||
|
if (schema.id.startsWith("http") && /(allOf|anyOf|oneOf)\/\d+$/.test(pointer)) {
|
||
|
const parentPointer = pointer.replace(/\/(allOf|anyOf|oneOf)\/\d+$/, "");
|
||
|
const parentSchema = get(compiledSchema, parentPointer);
|
||
|
schema.id = (_a = parentSchema.id) !== null && _a !== void 0 ? _a : schema.id;
|
||
|
}
|
||
|
context.ids[schema.id.replace(suffixes, "")] = pointer;
|
||
|
}
|
||
|
// build up scopes and add them to $ref-resolution map
|
||
|
pointer = `#${pointer}`.replace(/##+/, "#");
|
||
|
const previousPointer = pointer.replace(/\/[^/]+$/, "");
|
||
|
const parentPointer = pointer.replace(/\/[^/]+\/[^/]+$/, "");
|
||
|
const previousScope = scopes[previousPointer] || scopes[parentPointer];
|
||
|
const scope = joinScope(previousScope, schema.id);
|
||
|
scopes[pointer] = scope;
|
||
|
if (context.ids[scope] == null) {
|
||
|
context.ids[scope] = pointer;
|
||
|
}
|
||
|
if (schema.$ref && !schema[COMPILED_REF]) {
|
||
|
Object.defineProperty(schema, COMPILED_REF, {
|
||
|
enumerable: false,
|
||
|
value: joinScope(scope, schema.$ref)
|
||
|
});
|
||
|
// @todo currently not used:
|
||
|
Object.defineProperty(schema, GET_ROOT, { enumerable: false, value: getRoot });
|
||
|
// console.log("compiled ref", scope, schema.$ref, "=>", joinScope(scope, schema.$ref));
|
||
|
}
|
||
|
});
|
||
|
// console.log(JSON.stringify(context.ids, null, 2));
|
||
|
return compiledSchema;
|
||
|
}
|