From 8fa3fba11ff423d566224655ccf9a9c6f8f0e71f Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Fri, 3 Mar 2023 20:24:49 +0100 Subject: [PATCH] refactor(nuxi): hard restart with communication channel (#19423) --- packages/nuxi/src/cli-run.ts | 1 + packages/nuxi/src/cli-wrapper.ts | 59 +++++++++++++++---------------- packages/nuxi/src/cli.ts | 2 +- packages/nuxi/src/commands/dev.ts | 24 +++++++------ packages/nuxi/src/constants.ts | 11 ------ packages/nuxi/src/index.ts | 1 - 6 files changed, 44 insertions(+), 54 deletions(-) delete mode 100644 packages/nuxi/src/constants.ts diff --git a/packages/nuxi/src/cli-run.ts b/packages/nuxi/src/cli-run.ts index 56c55f22b4..07aef4afac 100644 --- a/packages/nuxi/src/cli-run.ts +++ b/packages/nuxi/src/cli-run.ts @@ -1,4 +1,5 @@ // @ts-ignore process._startTime = Date.now() + // @ts-ignore import('./cli').then(r => (r.default || r).main()) diff --git a/packages/nuxi/src/cli-wrapper.ts b/packages/nuxi/src/cli-wrapper.ts index 8a20e6ffd5..66891d694d 100644 --- a/packages/nuxi/src/cli-wrapper.ts +++ b/packages/nuxi/src/cli-wrapper.ts @@ -2,39 +2,38 @@ * This file is used to wrap the CLI entrypoint in a restartable process. */ import { fileURLToPath } from 'node:url' -import { execa } from 'execa' -import { EXIT_CODE_RESTART } from './constants' +import { fork } from 'node:child_process' +import type { ChildProcess } from 'node:child_process' const cliEntry = fileURLToPath(new URL('../dist/cli-run.mjs', import.meta.url)) -async function startSubprocess (preArgs: string[], postArgs: string[]) { - const child = await execa( - 'node', - [ - ...preArgs, - cliEntry, - ...postArgs - ], - { - reject: false, - stdio: 'inherit', - env: { - ...process.env, - NUXI_CLI_WRAPPER: 'true' - } - } - ) - if (child.exitCode === EXIT_CODE_RESTART) { - await startSubprocess(preArgs, postArgs) - } else { - process.exit(child.exitCode) - } -} - -const args = process.argv.slice(2) -// only enable wrapper in dev command -if (args[0] === 'dev') { - startSubprocess([], args) +// Only enable wrapper for nuxt dev command +if (process.argv[2] === 'dev') { + process.env.__CLI_ARGV__ = JSON.stringify(process.argv) + startSubprocess() } else { import(cliEntry) } + +function startSubprocess () { + let childProc: ChildProcess + + process.on('exit', () => { + if (childProc) { + childProc.kill() + } + }) + + start() + + function start () { + childProc = fork(cliEntry) + childProc.on('close', (code) => { if (code) { process.exit(code) } }) + childProc.on('message', (message) => { + if ((message as { type: string })?.type === 'nuxt:restart') { + childProc.kill() + startSubprocess() + } + }) + } +} diff --git a/packages/nuxi/src/cli.ts b/packages/nuxi/src/cli.ts index 5f4023eeac..71a1dfc6e3 100755 --- a/packages/nuxi/src/cli.ts +++ b/packages/nuxi/src/cli.ts @@ -9,7 +9,7 @@ import { showHelp } from './utils/help' import { showBanner } from './utils/banner' async function _main () { - const _argv = process.argv.slice(2) + const _argv = (process.env.__CLI_ARGV__ ? JSON.parse(process.env.__CLI_ARGV__) : process.argv).slice(2) const args = mri(_argv, { boolean: [ 'no-clear' diff --git a/packages/nuxi/src/commands/dev.ts b/packages/nuxi/src/commands/dev.ts index 52cb53fa39..26abe0cbf3 100644 --- a/packages/nuxi/src/commands/dev.ts +++ b/packages/nuxi/src/commands/dev.ts @@ -14,7 +14,6 @@ import { loadKit } from '../utils/kit' import { importModule } from '../utils/cjs' import { overrideEnv } from '../utils/env' import { writeNuxtManifest, loadNuxtManifest, cleanupNuxtDirs } from '../utils/nuxt' -import { EXIT_CODE_RESTART } from '../constants' import { defineNuxtCommand } from './index' export default defineNuxtCommand({ @@ -90,15 +89,18 @@ export default defineNuxtCommand({ } currentNuxt = await loadNuxt({ rootDir, dev: true, ready: false }) - // Hard restart - if (process.env.NUXI_CLI_WRAPPER) { - currentNuxt.hooks.hook('restart', (options) => { - if (options?.hard) { - process.exit(EXIT_CODE_RESTART) - } - }) - } - currentNuxt.hooks.hookOnce('restart', () => load(true)) + + currentNuxt.hooks.hookOnce('restart', async (options) => { + if (options?.hard && process.send) { + await listener.close().catch(() => {}) + await currentNuxt.close().catch(() => {}) + await watcher.close().catch(() => {}) + await distWatcher.close().catch(() => {}) + process.send({ type: 'nuxt:restart' }) + } else { + await load(true) + } + }) if (!isRestart) { showURL() @@ -121,7 +123,7 @@ export default defineNuxtCommand({ await currentNuxt.ready() await currentNuxt.hooks.callHook('listen', listener.server, listener) - const address = listener.server.address() as AddressInfo + const address = (listener.server.address() || {}) as AddressInfo currentNuxt.options.devServer.url = listener.url currentNuxt.options.devServer.port = address.port currentNuxt.options.devServer.host = address.address diff --git a/packages/nuxi/src/constants.ts b/packages/nuxi/src/constants.ts deleted file mode 100644 index 36ebe5dfd8..0000000000 --- a/packages/nuxi/src/constants.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Special exit code to restart the process - * - * Usage: - * ```ts - * if (process.env.NUXI_CLI_WRAPPER) { - * process.exit(EXIT_CODE_RESTART) - * } - * ``` - */ -export const EXIT_CODE_RESTART = 85 diff --git a/packages/nuxi/src/index.ts b/packages/nuxi/src/index.ts index 119ab45c2a..b8492c1b01 100755 --- a/packages/nuxi/src/index.ts +++ b/packages/nuxi/src/index.ts @@ -1,2 +1 @@ export * from './run' -export * from './constants'