private async _compile()

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 };
    };
  }