简体   繁体   English

依次运行 angular 自定义构建器

[英]Run angular custom builders in sequence

I have an Angular 13 project with some custom made builders.我有一个 Angular 13 项目和一些定制的构建器。 One of these generates a file with a $templateCache module.其中之一会生成一个带有 $templateCache 模块的文件。 This can take a few seconds to complete.这可能需要几秒钟才能完成。

The issue is that the main Angular builder starts before the $templateCache builder has completed.问题是主 Angular 构建器在 $templateCache 构建器完成之前启动。 This causes the module file to not be included in the build and the application crashes on page load or the build fails with an error saying the module cannot be resolved.这会导致模块文件不包含在构建中,并且应用程序在页面加载时崩溃,或者构建失败并显示无法解析模块的错误。

How can I ensure the $templateCache builder has completed before starting the main build?在开始主构建之前,如何确保 $templateCache 构建器已完成?

This is my builder that I am using to run the other custom ones and the Angular builder:这是我用来运行其他自定义构建器和 Angular 构建器的构建器:

import { CustomScheduleBuilderSchema } from './schema';
import { BuilderContext, BuilderOutput, BuilderRun, createBuilder, ScheduleOptions } from '@angular-devkit/architect';
import { Observable, of, from } from 'rxjs';
import { catchError, finalize, first, mergeMap } from 'rxjs/operators';
import { json, JsonObject } from '@angular-devkit/core';

function scheduleNgBuildTarget(
  options: JsonObject & CustomScheduleBuilderSchema,
  context: BuilderContext,
): Promise<BuilderRun> {
  return context.scheduleTarget({
    target: options.target,
    project: context.target?.project ?? ''
  });
}

function scheduleConcatenateLanguagesBuilder(
  context: BuilderContext
): Promise<BuilderRun> {
  return context.scheduleTarget({
    target: 'concat-lang',
    project: context.target?.project ?? ''
  },
  {
    watchModeEnabled: true
  });
}

function scheduleTemplatesBuilder(
  context: BuilderContext
): Promise<BuilderRun> {
  return context.scheduleTarget({
    target: 'templates',
    project: context.target?.project ?? ''
  },
  {
    watchModeEnabled: true
  });
}

function scheduleVersionBuilder(
  context: BuilderContext
): Promise<BuilderRun> {
  return context.scheduleTarget({
    target: 'version',
    project: context.target?.project ?? ''
  });
}

export function runCustomScheduleBuilder (
  options: JsonObject & CustomScheduleBuilderSchema,
  context: BuilderContext
): Observable<BuilderOutput> {

  const scheduleLogger = context.logger.createChild('CustomSchedule');
  scheduleLogger.info('Starting build');


  return of({ success: true }).pipe(
    first(),
    mergeMap(_ => from(scheduleConcatenateLanguagesBuilder(context))),
    mergeMap(_ => from(scheduleTemplatesBuilder(context))),
    mergeMap(_ => from(scheduleVersionBuilder(context))),
    mergeMap(_ => from(scheduleNgBuildTarget(options, context))), // This should run after the other builders have completed.
    mergeMap(target =>
      target.output.pipe(
        finalize(() => {
          scheduleLogger.info('Shutting down build');
        }),
      ),
    ),
    catchError(e => {
      scheduleLogger.error(JSON.stringify(e));
      return of({ success: false });
    }),
  );
}

export default createBuilder<json.JsonObject & CustomScheduleBuilderSchema>(runCustomScheduleBuilder);

This is the builder that generates the file with the $templateCache module:这是使用 $templateCache 模块生成文件的构建器:

import { TemplatesBuilderSchema } from './schema';
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
import { json, normalize } from '@angular-devkit/core';
import { Observable, of, noop, from, fromEvent } from 'rxjs';
import { catchError, map, finalize, take, tap, mergeMap, first } from 'rxjs/operators';
import { join } from 'path';
import html2jsProcessor from 'angular-template-cache';
import defaults from 'angular-template-cache/lib/defaults';
import { watch } from 'chokidar';

async function populateTemplateCache(
  options: any,
  templatesLogger: any
) {
  await html2jsProcessor(options);
  templatesLogger.info(`Completed templates file: ${options.output}`)
}

export function runTemplatesBuilder(
  { outputPath, watchModeEnabled }: TemplatesBuilderSchema,
  { workspaceRoot, logger }: BuilderContext
): Observable<BuilderOutput> {
  const templatesLogger = logger.createChild('Templates');
  templatesLogger.info('Creating templates file to populate $templateCache');

  const options = {
    ...defaults,
    strict: false,
    filesGlob: 'src/app/**/*.html',
    moduleName: 'myApp.templates',
    output: join(workspaceRoot, normalize(outputPath)),
  };

  // Setup file watcher
  const watcher = watch('src/app/**/*.html', { ignoreInitial: true });

  watcher.on('add', () => {
    populateTemplateCache(options, templatesLogger);
  })
  .on('change', () => {
    populateTemplateCache(options, templatesLogger);
  })
  .on('unlink', () => {
    populateTemplateCache(options, templatesLogger);
  });

  return fromEvent(watcher, 'ready').pipe(
    tap(() => {
      templatesLogger.info('templates watcher ready...');
    }),
    first(),
    mergeMap(_ => from(populateTemplateCache(options, templatesLogger))),
    map(() => ({ success: true })),
    finalize(() => {
      templatesLogger.info('Shutting down templates file watcher');
      watcher.close();
    }),
    catchError(e => {
      templatesLogger.error(`Failed to create templates file: ${options.output}, Error: ${JSON.stringify(e)}`);
      return of({ success: false });
    }),
    watchModeEnabled ? tap(noop) : take(1),
  );  
}

export default createBuilder<json.JsonObject & TemplatesBuilderSchema>(runTemplatesBuilder);

It seems the scheduleTarget function returns a resolved Promise without waiting for the builder target to complete.似乎scheduleTarget function 返回已解决的 Promise 而无需等待构建器目标完成。

By creating a child instance of a logger and passing it to the builder I can listen for log messages and wait to return the resolved promise until the expected 'Completed templates' message is detected.通过创建记录器的子实例并将其传递给构建器,我可以侦听日志消息并等待返回已解析的 promise,直到检测到预期的“已完成模板”消息。

async function scheduleTemplatesBuilder(
  context: BuilderContext
): Promise<BuilderRun> {
  const templatesLogger = context.logger.createChild('templates');
  const builderRun = context.scheduleTarget({
    target: 'templates',
    project: context.target?.project ?? ''
  },
  {
    watchModeEnabled: true
  },
  {
    logger: templatesLogger
  });

  await templatesLogger.pipe(
    takeWhile(logEntry => !logEntry.message.includes('Completed templates')),
  ).toPromise();

  return builderRun;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用多个自定义 angular 构建器 - Using multiple custom angular builders 将自定义环境参数传递给@angular-builders/custom-webpack - Pass custom environment parameters to @angular-builders/custom-webpack 找不到 builder @angular-builders/jest:run 的实现 - Could not find the implementation for builder @angular-builders/jest:run Angular 项目在 scss 文件上使用 @angular-builders/custom-webpack 编译失败 - Angular Project fails compilation with @angular-builders/custom-webpack on scss files Angular 使用@angular-builders/custom-webpack 将 v13 迁移到 v14 - Angular migration v13 to v14 using @angular-builders/custom-webpack 如何使用带有 angular-builder 的自定义 webpack 将整个目录复制到 dist/ 中? - How do I copy an entire directory into dist/ using custom webpack with angular-builders? 如何使jQuery在Angular 2中以正确的顺序运行 - How to get jQuery to run in right sequence in Angular 2 Ionic And Angular 5表单构建器和组未读取值 - Ionic And Angular 5 form builders and groups is not reading values 错误:Package“@ionic/angular-toolkit”没有定义构建器 - Error: Package "@ionic/angular-toolkit" has no builders defined 无法在Angular 7应用中运行自定义Angular 7元素包 - Cannot run custom Angular 7 elements bundle in Angular 7 app
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM