取决于完成多个并行 gulp 任务

Depending on completion of multiple parallel gulp tasks

我有一个复杂的 NodeJS 应用程序,由多个纱线工作区包组成。以下 gulpconfig.ts 文件为整个包集定义了一个 gulp 构建任务:

import { series, parallel, TaskFunction, src, dest } from 'gulp';
import gulpTslint from 'gulp-tslint';
import { createProject } from 'gulp-typescript';
import tslint, { Linter } from 'tslint';
import { resolve } from 'path';
import { DestOptions } from 'vinyl-fs';
import { exec } from 'child_process';
import { promisify } from 'util';

const execPromise = promisify(exec);

interface GulpTasks {
  [key: string]: TaskFunction;
}

type TaskFunctionCallback = (error?: any) => void;

class Project {
  public projectFiles: NodeJS.ReadWriteStream;
  public constructor(
    public readonly projectDirectory: string,
    public readonly sourcemaps: boolean = false,
  ) {
    this.projectFiles = src('./src/**/*.ts', { cwd: projectDirectory, sourcemaps });
  }

  public lint(): this {
    this.projectFiles = this.projectFiles.pipe(
      gulpTslint({
        tslint,
        configuration: resolve(__dirname, 'tslint.json'),
        program: Linter.createProgram(
          resolve(this.projectDirectory, 'tsconfig.json'),
          this.projectDirectory,
        ),
      }),
    );

    return this;
  }

  public build(): this {
    const opts: DestOptions = {};
    if (this.sourcemaps) {
      opts.sourcemaps = true;
    }
    this.projectFiles = this.projectFiles
      .pipe(createProject(resolve(this.projectDirectory, 'tsconfig.build.json'))())
      .pipe(dest(resolve(this.projectDirectory, 'dist'), opts));

    return this;
  }
}

const buildDatabase = (cb: TaskFunctionCallback): NodeJS.ReadWriteStream =>
  new Project(resolve(__dirname, 'server/database')).build().projectFiles;

const buildSharedCommon = (cb: TaskFunctionCallback): NodeJS.ReadWriteStream =>
  new Project(resolve(__dirname, 'shared/common')).lint().build().projectFiles;
const buildSharedFrontoffice = (cb: TaskFunctionCallback): NodeJS.ReadWriteStream =>
  new Project(resolve(__dirname, 'shared/frontoffice')).lint().build().projectFiles;
const buildSharedBackoffice = (cb: TaskFunctionCallback): NodeJS.ReadWriteStream =>
  new Project(resolve(__dirname, 'shared/backoffice')).lint().build().projectFiles;

const buildClientCommon = (cb: TaskFunctionCallback): NodeJS.ReadWriteStream =>
  new Project(resolve(__dirname, 'client/common')).lint().build().projectFiles;
const buildClientFrontoffice = async (cb: TaskFunctionCallback): Promise<unknown> =>
  execPromise('yarn run build', { cwd: 'client/frontoffice' });
const buildClientBackoffice = async (cb: TaskFunctionCallback): Promise<unknown> =>
  execPromise('yarn run build', { cwd: 'client/backoffice' });

const buildServerCommon = (cb: TaskFunctionCallback): NodeJS.ReadWriteStream =>
  new Project(resolve(__dirname, 'server/common')).lint().build().projectFiles;
const buildServerFrontoffice = (cb: TaskFunctionCallback): NodeJS.ReadWriteStream =>
  new Project(resolve(__dirname, 'server/frontoffice')).lint().build().projectFiles;
const buildServerBackoffice = (cb: TaskFunctionCallback): NodeJS.ReadWriteStream =>
  new Project(resolve(__dirname, 'server/backoffice')).lint().build().projectFiles;

const tasks: GulpTasks = {
  build: parallel(
    buildDatabase,
    series(
      buildSharedCommon,
      parallel(buildSharedBackoffice, buildSharedFrontoffice),
      parallel(
        series(buildClientCommon, parallel(buildClientFrontoffice, buildClientBackoffice)),
        series(buildServerCommon, parallel(buildServerFrontoffice, buildServerBackoffice)),
      ),
    ),
  ),
};

export = tasks;

由于我的应用程序的结构,buildServerCommon 依赖于 buildDatabase。然而,buildDatabase 不依赖于任何东西,并且需要很长时间,这就是为什么我将它与其余部分并行启动的原因。

尽管采用上述设置,buildDatabase 可能(并且确实)比 buildServerCommon 启动晚完成。

如何让 buildServerCommon 仅在 buildDatabase 和 buildSharedCommon 之后启动,但仍然让其他一切尽早启动?

基本上,在我的依赖关系树中:

我尝试使用

const tasks: GulpTasks = {
  build: series(
    parallel(buildSharedCommon, buildDatabase),
    parallel(buildServerCommon, buildSharedFrontoffice, buildSharedBackoffice),
    parallel(
      series(buildClientCommon, parallel(buildClientFrontoffice, buildClientBackoffice)),
      parallel(buildServerFrontoffice, buildServerBackoffice),
    ),
  ),
};

但这不是最优的,因为 buildSharedFrontoffice 和 buildSharedBackoffice 在 buildDatabase 完成之前不会被编译,并且从那里客户端也被不必要地延迟。

如果您使用 Gulp 的早期版本,请注意 serial() 的参数顺序与使用 task() 的早期版本不同。特别是 serial() 的参数按照它们列出的顺序一个接一个地执行。

因此,如果您需要 buildDatabasebuildServerCommon 之前完成 ,它们需要以预期的顺序出现在相同的 serial 中函数调用。我想你需要更多这样的东西:

const tasks: GulpTasks = {
build: parallel(
    series( buildDatabase, buildServerCommon /* ... others? */ ),
    buildSharedCommon,
    buildSharedFrontoffice,
    // ... others?
    )
}; 

这当然假设 buildSharedCommon 和 buildSharedFrontoffice 对 serial() 块中的消息没有依赖性。

我是这样看的,在 gulp 3 中,gulp 任务致力于从 task 声明的所有定义中找出正确的构建顺序,而在 gulp 4,您必须明确使用新的 seriesparallel 结构。从这个意义上说,gulp 变得比声明更具程序性。

我认为最佳解决方案是等待构建所有共享包,然后为客户端和服务器分叉 2 个并行分支。

const tasks: GulpTasks = {
    build: series(
        series(buildSharedCommon, parallel(buildSharedFrontoffice, buildSharedBackoffice)),
        parallel(
            series(buildClientCommon, parallel(buildClientFrontoffice, buildClientBackoffice)),
            series(buildDatabase, buildServerCommon, parallel(buildServerFrontoffice, buildServerBackoffice))
        )
    )
  };

Fastest/ideal 执行路径是并行旋转 3 个任务,然后等待 3 个任务中的 2 个完成,用于两个不同的延续(客户端和服务器)。 Gulp 开箱即用,需要您的代码来处理同步。