protected async createWorkflow()

in packages/angular/cli/models/schematic-command.ts [220:373]


  protected async createWorkflow(options: BaseSchematicSchema): Promise<workflow.BaseWorkflow> {
    if (this._workflow) {
      return this._workflow;
    }

    const { force, dryRun } = options;
    const root = this.context.root;
    const workflow = new NodeWorkflow(root, {
      force,
      dryRun,
      packageManager: await getPackageManager(root),
      packageRegistry: options.packageRegistry,
      // A schema registry is required to allow customizing addUndefinedDefaults
      registry: new schema.CoreSchemaRegistry(formats.standardFormats),
      resolvePaths: this.workspace
        ? // Workspace
          this.collectionName === this.defaultCollectionName
          ? // Favor __dirname for @schematics/angular to use the build-in version
            [__dirname, process.cwd(), root]
          : [process.cwd(), root, __dirname]
        : // Global
          [__dirname, process.cwd()],
      schemaValidation: true,
      optionTransforms: [
        // Add configuration file defaults
        async (schematic, current) => {
          const projectName =
            typeof (current as Record<string, unknown>).project === 'string'
              ? ((current as Record<string, unknown>).project as string)
              : getProjectName();

          return {
            ...(await getSchematicDefaults(schematic.collection.name, schematic.name, projectName)),
            ...current,
          };
        },
      ],
      engineHostCreator: (options) => new SchematicEngineHost(options.resolvePaths),
    });

    const getProjectName = () => {
      if (this.workspace) {
        const projectNames = getProjectsByPath(
          this.workspace,
          process.cwd(),
          this.workspace.basePath,
        );

        if (projectNames.length === 1) {
          return projectNames[0];
        } else {
          if (projectNames.length > 1) {
            this.logger.warn(tags.oneLine`
              Two or more projects are using identical roots.
              Unable to determine project using current working directory.
              Using default workspace project instead.
            `);
          }

          const defaultProjectName = this.workspace.extensions['defaultProject'];
          if (typeof defaultProjectName === 'string' && defaultProjectName) {
            return defaultProjectName;
          }
        }
      }

      return undefined;
    };

    workflow.registry.addPostTransform(schema.transforms.addUndefinedDefaults);
    workflow.registry.addSmartDefaultProvider('projectName', getProjectName);
    workflow.registry.useXDeprecatedProvider((msg) => this.logger.warn(msg));

    let shouldReportAnalytics = true;
    workflow.engineHost.registerOptionsTransform(async (_, options) => {
      if (shouldReportAnalytics) {
        shouldReportAnalytics = false;
        await this.reportAnalytics([this.description.name], options as Arguments);
      }

      return options;
    });

    if (options.interactive !== false && isTTY()) {
      workflow.registry.usePromptProvider((definitions: Array<schema.PromptDefinition>) => {
        const questions: inquirer.QuestionCollection = definitions
          .filter((definition) => !options.defaults || definition.default === undefined)
          .map((definition) => {
            const question: inquirer.Question = {
              name: definition.id,
              message: definition.message,
              default: definition.default,
            };

            const validator = definition.validator;
            if (validator) {
              question.validate = (input) => validator(input);

              // Filter allows transformation of the value prior to validation
              question.filter = async (input) => {
                for (const type of definition.propertyTypes) {
                  let value;
                  switch (type) {
                    case 'string':
                      value = String(input);
                      break;
                    case 'integer':
                    case 'number':
                      value = Number(input);
                      break;
                    default:
                      value = input;
                      break;
                  }
                  // Can be a string if validation fails
                  const isValid = (await validator(value)) === true;
                  if (isValid) {
                    return value;
                  }
                }

                return input;
              };
            }

            switch (definition.type) {
              case 'confirmation':
                question.type = 'confirm';
                break;
              case 'list':
                question.type = definition.multiselect ? 'checkbox' : 'list';
                (question as inquirer.CheckboxQuestion).choices = definition.items?.map((item) => {
                  return typeof item == 'string'
                    ? item
                    : {
                        name: item.label,
                        value: item.value,
                      };
                });
                break;
              default:
                question.type = definition.type;
                break;
            }

            return question;
          });

        return inquirer.prompt(questions);
      });
    }

    return (this._workflow = workflow);
  }