diff --git a/packages/node/src/executors/node/node.impl.ts b/packages/node/src/executors/node/node.impl.ts index 729d00a9a5ae6..eabcedd193cef 100644 --- a/packages/node/src/executors/node/node.impl.ts +++ b/packages/node/src/executors/node/node.impl.ts @@ -5,12 +5,12 @@ import { parseTargetString, runExecutor, } from '@nrwl/devkit'; +import { readCachedProjectGraph } from '@nrwl/workspace/src/core/project-graph'; +import { calculateProjectDependencies } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; import { ChildProcess, fork } from 'child_process'; import * as treeKill from 'tree-kill'; import { promisify } from 'util'; import { InspectType, NodeExecutorOptions } from './schema'; -import { readCachedProjectGraph } from '@nrwl/workspace/src/core/project-graph'; -import { calculateProjectDependencies } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; let subProcess: ChildProcess = null; @@ -24,15 +24,15 @@ export async function* nodeExecutor( context: ExecutorContext ) { process.on('SIGTERM', async () => { - await killProcess(); + await killCurrentProcess(); process.exit(128 + 15); }); process.on('SIGINT', async () => { - await killProcess(); + await killCurrentProcess(); process.exit(128 + 2); }); process.on('SIGHUP', async () => { - await killProcess(); + await killCurrentProcess(); process.exit(128 + 1); }); @@ -80,7 +80,7 @@ function calculateResolveMappings( }, {}); } -function runProcess( +async function runProcess( event: ExecutorEvent, options: NodeExecutorOptions, mappings: { [project: string]: string } @@ -98,6 +98,18 @@ function runProcess( }, } ); + + if (!options.watch) { + return new Promise((resolve, reject) => { + subProcess.on('exit', (code) => { + if (code === 0) { + resolve(undefined); + } else { + reject(); + } + }); + }); + } } function getExecArgv(options: NodeExecutorOptions) { @@ -123,17 +135,22 @@ async function handleBuildEvent( options: NodeExecutorOptions, mappings: { [project: string]: string } ) { - if ((!event.success || options.watch) && subProcess) { - await killProcess(); + // Don't kill previous run unless new build is successful. + if (options.watch && event.success) { + await killCurrentProcess(); } + if (event.success) { - runProcess(event, options, mappings); + await runProcess(event, options, mappings); } } -async function killProcess() { - const promisifiedTreeKill: (pid: number, signal: string) => Promise = - promisify(treeKill); +const promisifiedTreeKill: (pid: number, signal: string) => Promise = + promisify(treeKill); + +async function killCurrentProcess() { + if (!subProcess) return; + try { await promisifiedTreeKill(subProcess.pid, 'SIGTERM'); } catch (err) { diff --git a/packages/node/src/utils/config.ts b/packages/node/src/utils/config.ts index f231d413805e7..c4fb06a72bd30 100644 --- a/packages/node/src/utils/config.ts +++ b/packages/node/src/utils/config.ts @@ -95,6 +95,8 @@ export function getBaseWebpackPartial( }, plugins: [ new ForkTsCheckerWebpackPlugin({ + // For watch mode, type errors should result in failure. + async: false, typescript: { enabled: true, configFile: options.tsConfig, @@ -104,6 +106,9 @@ export function getBaseWebpackPartial( ], watch: options.watch, watchOptions: { + // Delay the next rebuild from first file change, otherwise can lead to + // two builds on a single file change. + aggregateTimeout: 200, poll: options.poll, }, stats: getStatsConfig(options),