in packages/angular_devkit/core/src/json/schema/registry.ts [284:394]
private async _compile(
schema: JsonSchema,
): Promise<
(data: JsonValue, options?: SchemaValidatorOptions) => Promise<SchemaValidatorResult>
> {
if (typeof schema === 'boolean') {
return async (data) => ({ success: schema, data });
}
const schemaInfo: SchemaInfo = {
smartDefaultRecord: new Map<string, JsonObject>(),
promptDefinitions: [],
};
this._ajv.removeSchema(schema);
let validator: ValidateFunction;
try {
this._currentCompilationSchemaInfo = schemaInfo;
validator = this._ajv.compile(schema);
} catch (e) {
// This should eventually be refactored so that we we handle race condition where the same schema is validated at the same time.
if (!(e instanceof Ajv.MissingRefError)) {
throw e;
}
validator = await this._ajv.compileAsync(schema);
} finally {
this._currentCompilationSchemaInfo = undefined;
}
return async (data: JsonValue, options?: SchemaValidatorOptions) => {
const validationOptions: SchemaValidatorOptions = {
withPrompts: true,
applyPostTransforms: true,
applyPreTransforms: true,
...options,
};
const validationContext = {
promptFieldsWithValue: new Set<string>(),
};
// Apply pre-validation transforms
if (validationOptions.applyPreTransforms) {
for (const visitor of this._pre.values()) {
data = await visitJson(
data,
visitor,
schema,
this._resolver.bind(this),
validator,
).toPromise();
}
}
// Apply smart defaults
await this._applySmartDefaults(data, schemaInfo.smartDefaultRecord);
// Apply prompts
if (validationOptions.withPrompts) {
const visitor: JsonVisitor = (value, pointer) => {
if (value !== undefined) {
validationContext.promptFieldsWithValue.add(pointer);
}
return value;
};
if (typeof schema === 'object') {
await visitJson(data, visitor, schema, this._resolver.bind(this), validator).toPromise();
}
const definitions = schemaInfo.promptDefinitions.filter(
(def) => !validationContext.promptFieldsWithValue.has(def.id),
);
if (definitions.length > 0) {
await this._applyPrompts(data, definitions);
}
}
// Validate using ajv
try {
const success = await validator.call(validationContext, data);
if (!success) {
return { data, success, errors: validator.errors ?? [] };
}
} catch (error) {
if (error instanceof Ajv.ValidationError) {
return { data, success: false, errors: error.errors };
}
throw error;
}
// Apply post-validation transforms
if (validationOptions.applyPostTransforms) {
for (const visitor of this._post.values()) {
data = await visitJson(
data,
visitor,
schema,
this._resolver.bind(this),
validator,
).toPromise();
}
}
return { data, success: true };
};
}