nx/scripts/documentation/schema-flattener.ts

72 lines
2.0 KiB
TypeScript

import { deepCopy, json, schema } from '@angular-devkit/core';
import { visitJsonSchema } from '@angular-devkit/core/src/json/schema';
import * as Ajv from 'ajv';
import * as Url from 'url';
export interface SchemaFlattener {
flatten: (schema) => json.JsonObject;
}
function _resolver(
ref: string,
validate?: Ajv.ValidateFunction
): { context?: Ajv.ValidateFunction; schema?: json.JsonObject } {
if (!validate || !ref) {
return {};
}
const schema = (validate as any).schemaEnv.root.schema;
const id = typeof schema === 'object' ? schema.$id : null;
let fullReference = ref;
if (typeof id === 'string') {
fullReference = Url.resolve(id, ref);
if (ref.startsWith('#')) {
fullReference = id + fullReference;
}
}
if (fullReference.startsWith('#')) {
fullReference = fullReference.slice(0, -1);
}
const resolvedSchema = this._ajv.getSchema(fullReference);
return {
context: resolvedSchema?.schemaEnv.validate,
schema: resolvedSchema?.schema as json.JsonObject,
};
}
export function createSchemaFlattener(
formats: schema.SchemaFormat[] = []
): SchemaFlattener {
const ajv = new Ajv({ passContext: true });
for (const format of formats) {
ajv.addFormat(format.name, format.formatter as any);
}
return {
flatten: function (schema) {
const validate = ajv.removeSchema(schema).compile(schema);
function visitor(current, pointer, parentSchema, index) {
if (
current &&
parentSchema &&
index &&
json.isJsonObject(current) &&
current.hasOwnProperty('$ref') &&
typeof current['$ref'] == 'string'
) {
const resolved = _resolver(current['$ref'], validate);
if (resolved.schema) {
parentSchema[index] = resolved.schema;
}
}
}
const schemaCopy = deepCopy(validate.schema) as json.JsonObject;
visitJsonSchema(schemaCopy, visitor);
return schemaCopy;
},
};
}