From 21291f26b05a63c3393cc03191109fdba7d35096 Mon Sep 17 00:00:00 2001 From: Max Kovalenko Date: Wed, 21 Feb 2024 23:20:51 +0200 Subject: [PATCH] feat: add flag to disable shell mode for `nest start --watch` When running on Linux/WSL, `nest start --watch` does not wait the app to shutdown on restart. That leads to two instances of application running simultaneously, especially if it has async shutdown hooks. That commit fixes that adding an option to disable shell mode when spawning child processes so then app will completely shutdown first and only then started again. Thanks @jleverenz for the investigation and solution idea in nestjs/nest-cli/1614 fixes nestjs/nest-cli/1614 --- actions/start.action.ts | 9 ++++++++- commands/start.command.ts | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/actions/start.action.ts b/actions/start.action.ts index 1c64e6a3e..c1a545ba3 100644 --- a/actions/start.action.ts +++ b/actions/start.action.ts @@ -72,12 +72,15 @@ export class StartAction extends BuildAction { commandOptions, defaultConfiguration.sourceRoot, ); + const noShellOption = commandOptions.find((option) => option.name === 'noShell') + const useShell = noShellOption ? !noShellOption.value : true; const onSuccess = this.createOnSuccessHook( entryFile, sourceRoot, debugFlag, outDir, binaryToRun, + useShell, ); await this.runBuild( @@ -103,6 +106,7 @@ export class StartAction extends BuildAction { debugFlag: boolean | string | undefined, outDirName: string, binaryToRun: string, + useShell: boolean, ) { let childProcessRef: any; process.on( @@ -120,6 +124,7 @@ export class StartAction extends BuildAction { debugFlag, outDirName, binaryToRun, + useShell, ); childProcessRef.on('exit', () => (childProcessRef = undefined)); }); @@ -133,6 +138,7 @@ export class StartAction extends BuildAction { debugFlag, outDirName, binaryToRun, + useShell, ); childProcessRef.on('exit', (code: number) => { process.exitCode = code; @@ -148,6 +154,7 @@ export class StartAction extends BuildAction { debug: boolean | string | undefined, outDirName: string, binaryToRun: string, + useShell: boolean, ) { let outputFilePath = join(outDirName, sourceRoot, entryFile); if (!fs.existsSync(outputFilePath + '.js')) { @@ -179,7 +186,7 @@ export class StartAction extends BuildAction { return spawn(binaryToRun, processArgs, { stdio: 'inherit', - shell: true, + shell: useShell, }); } } diff --git a/commands/start.command.ts b/commands/start.command.ts index 361f48fbd..690919339 100644 --- a/commands/start.command.ts +++ b/commands/start.command.ts @@ -38,6 +38,7 @@ export class StartCommand extends AbstractCommand { '--preserveWatchOutput', 'Use "preserveWatchOutput" option when using tsc watch mode.', ) + .option('--noShell', 'Spawn child processes without shell (see node\'s child_process.spawn() method docs)') .description('Run Nest application.') .action(async (app: string, command: Command) => { const options: Input[] = []; @@ -79,6 +80,10 @@ export class StartCommand extends AbstractCommand { !!command.watch && !isWebpackEnabled, }); + options.push({ + name: 'noShell', + value: !!command.noShell, + }) const availableBuilders = ['tsc', 'webpack', 'swc']; if (command.builder && !availableBuilders.includes(command.builder)) {