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...