Custom angular builder get output from scheduleTarget

Discussão em 'Angular' iniciado por Øystein Amundsen, Janeiro 21, 2025.

  1. I have a custom builder where I want to schedule the serve (@angular-devkit/build-angular:dev-server) target to run, fetch the compiled output from that job and use it to populate a workbox manifest. The problem is that I have no idea of how to get the output from the scheduled target.

    The following works for the build (@angular-devkit/build-angular:application) target:

    export default createBuilder(
    async (
    opt: ApplicationExecutorSchema,
    ctx: BuilderContext,
    ): Promise<BuilderOutput> => {
    try {
    // Run the specified build
    const target = targetFromTargetString(opt.buildTarget);
    const tOpts = await ctx.getTargetOptions(target);
    const buildResult = await ctx.scheduleTarget(target);
    const result = await buildResult.result;

    if (!result.success) {
    ctx.logger.error('Error running build target.');
    return { success: false };
    }

    // Infer the manifest from the previous builder's output
    const outputPath = path.resolve(
    ctx.workspaceRoot,
    `${tOpts.outputPath}`,
    'browser',
    );
    const manifest = fs
    .readdirSync(outputPath, { recursive: true })
    .filter((file) => !`${file}`.endsWith('\\')) // Discard directories
    .map((file) => {
    return { url: `${file}`.replace('\\', '/'), revision: '' }; // TODO: Calculate hash for non-cache-busted files
    });

    // Compile the service worker file using esbuild
    const swFilePath = path.resolve(ctx.workspaceRoot, opt.serviceWorker);
    const fileName = path.basename(swFilePath, path.extname(swFilePath));
    const outputFile = path.resolve(outputPath, `${fileName}.js`);
    const swBuild = await esbuild.build({
    entryPoints: [swFilePath],
    tsconfig: path.resolve(ctx.workspaceRoot, opt.tsconfig),
    bundle: true,
    write: false,
    minify: true,
    });
    let serviceWorkerContent = swBuild.outputFiles[0].text;

    // Replace the `self.__WB_MANIFEST` string with the list of files
    serviceWorkerContent = serviceWorkerContent.replace(
    'self.__WB_MANIFEST',
    JSON.stringify(manifest),
    );

    // Write the modified service worker file back to disk
    fs.writeFileSync(outputFile, serviceWorkerContent);
    return { success: true };
    } catch (error) {
    ctx.logger.error('Error compiling service worker file:', error);
    return { success: false };
    }
    },
    );


    project.json (NX equivalent for angular.json)

    "targets": {
    "pwa": {
    "executor": "@home/builder:application",
    "options": {
    "buildTarget": "home:build",
    "serviceWorker": "apps/home/src/sw.ts",
    "tsconfig": "apps/home/tsconfig.wb.json"
    }
    },
    ....
    }


    But here I can read the output from disk. The serve target will only write to memory. And since the serve target spins up a dev-server and lives on, I cannot use scheduleTarget as this would never yield control back to my builder. I think I have to use scheduleTargetAndForget instead? Something like this?

    export default createBuilder(
    (
    opt: ApplicationExecutorSchema,
    ctx: BuilderContext,
    ): Observable<BuilderOutput> => {
    return defer(() => targetFromTargetString(opt.buildTarget)).pipe(
    switchMap(target => scheduleTargetAndForget(ctx, target)),
    switchMap((buildResult) => {
    const output = buildResult.result; // The `buildResult` contains nothing useful at all
    return of({ success: true });
    }),
    catchError((error) => {
    ctx.logger.error('Error running build target.', error);
    return of({ success: false });
    }),
    );
    },
    );


    But regardless, of which - how can I get the actual files a job outputs - either to memory or disk?

    Continue reading...

Compartilhe esta Página