From 18421f6f3dc3fe2a973caff0bdb1ed7727547789 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Mon, 16 Jan 2023 19:28:44 -0800 Subject: [PATCH] feat(vite): enable watch mode for preview --- .../packages/vite/executors/build.json | 5 + .../vite/src/executors/build/build.impl.ts | 51 ++++++++- packages/vite/src/executors/build/schema.d.ts | 1 + packages/vite/src/executors/build/schema.json | 5 + .../preview-server/preview-server.impl.ts | 100 +++++++++++------- packages/vite/src/utils/options-utils.ts | 1 + 6 files changed, 119 insertions(+), 44 deletions(-) diff --git a/docs/generated/packages/vite/executors/build.json b/docs/generated/packages/vite/executors/build.json index c6d7b5734f88f..df2e66627b95b 100644 --- a/docs/generated/packages/vite/executors/build.json +++ b/docs/generated/packages/vite/executors/build.json @@ -78,6 +78,11 @@ "force": { "description": "Force the optimizer to ignore the cache and re-bundle", "type": "boolean" + }, + "watch": { + "description": "Enable re-building when files change.", + "type": "object", + "default": null } }, "definitions": {}, diff --git a/packages/vite/src/executors/build/build.impl.ts b/packages/vite/src/executors/build/build.impl.ts index 9bbdcd88d593b..17526637786cc 100644 --- a/packages/vite/src/executors/build/build.impl.ts +++ b/packages/vite/src/executors/build/build.impl.ts @@ -10,7 +10,7 @@ import { copyAssets } from '@nrwl/js'; import { existsSync } from 'fs'; import { resolve } from 'path'; -export default async function viteBuildExecutor( +export default async function* viteBuildExecutor( options: ViteBuildExecutorOptions, context: ExecutorContext ) { @@ -24,7 +24,7 @@ export default async function viteBuildExecutor( } ); - await runInstance(buildConfig); + const watcherOrOutput = await runInstance(buildConfig); const libraryPackageJson = resolve(projectRoot, 'package.json'); const rootPackageJson = resolve(context.root, 'package.json'); @@ -49,7 +49,27 @@ export default async function viteBuildExecutor( ); } - return { success: true }; + if ('on' in watcherOrOutput) { + // watcherOrOutput is a RollupWatcher. + // event is a RollupWatcherEvent. + const emitter = makeEmitter(); + let success = true; + watcherOrOutput.on('event', (event: any) => { + if (event.code === 'START') { + success = true; + } else if (event.code === 'ERROR') { + success = false; + } else if (event.code === 'END') { + emitter.push({ success }); + } + // result must be closed when present. + // see https://rollupjs.org/guide/en/#rollupwatch + event.result?.close(); + }); + yield* emitter; + } else { + yield { success: true }; + } } function runInstance(options: InlineConfig) { @@ -57,3 +77,28 @@ function runInstance(options: InlineConfig) { ...options, }); } + +/** + * Helper to create an async iterator. + * Calling push on the returned object emits the value. + */ +function makeEmitter() { + const events = []; + let resolve: (value: unknown) => void | null; + + return { + push: (event) => { + events.push(event); + resolve?.(event); + resolve = null; + }, + [Symbol.asyncIterator]: () => ({ + next: async () => { + if (events.length == 0) { + await new Promise((r) => (resolve = r)); + } + return { value: events.shift(), done: false }; + }, + }), + }; +} diff --git a/packages/vite/src/executors/build/schema.d.ts b/packages/vite/src/executors/build/schema.d.ts index 06ef9ad1a204b..e4a29d3abe789 100644 --- a/packages/vite/src/executors/build/schema.d.ts +++ b/packages/vite/src/executors/build/schema.d.ts @@ -12,4 +12,5 @@ export interface ViteBuildExecutorOptions { logLevel?: 'info' | 'warn' | 'error' | 'silent'; mode?: string; ssr?: boolean | string; + watch?: object | null; } diff --git a/packages/vite/src/executors/build/schema.json b/packages/vite/src/executors/build/schema.json index 1f0acffccefe2..e8c9d8d6e7811 100644 --- a/packages/vite/src/executors/build/schema.json +++ b/packages/vite/src/executors/build/schema.json @@ -118,6 +118,11 @@ "force": { "description": "Force the optimizer to ignore the cache and re-bundle", "type": "boolean" + }, + "watch": { + "description": "Enable re-building when files change.", + "type": "object", + "default": null } }, "definitions": {}, diff --git a/packages/vite/src/executors/preview-server/preview-server.impl.ts b/packages/vite/src/executors/preview-server/preview-server.impl.ts index 8c50426f78ab7..20bb2851244d1 100644 --- a/packages/vite/src/executors/preview-server/preview-server.impl.ts +++ b/packages/vite/src/executors/preview-server/preview-server.impl.ts @@ -1,5 +1,5 @@ import { ExecutorContext, parseTargetString, runExecutor } from '@nrwl/devkit'; -import { InlineConfig, mergeConfig, preview } from 'vite'; +import { InlineConfig, mergeConfig, preview, PreviewServer } from 'vite'; import { getNxTargetOptions, getViteSharedConfig, @@ -22,20 +22,12 @@ export default async function* vitePreviewServerExecutor( // Merge the options from the build and preview-serve targets. // The latter takes precedence. const mergedOptions = { + ...{ watch: {} }, ...buildTargetOptions, ...options, }; - // Launch the build target. - const target = parseTargetString(options.buildTarget, context.projectGraph); - const build = await runExecutor(target, mergedOptions, context); - for await (const result of build) { - if (!result.success) { - return result; - } - } - - // Launch the server. + // Retrieve the server configuration. const serverConfig: InlineConfig = mergeConfig( getViteSharedConfig(mergedOptions, options.clearScreen, context), { @@ -48,41 +40,67 @@ export default async function* vitePreviewServerExecutor( console.warn('WARNING: preview is not meant to be run in production!'); } - try { - const server = await preview(serverConfig); - server.printUrls(); + let server: PreviewServer | undefined; - const processOnExit = async () => { - const { httpServer } = server; - // closeAllConnections was added in Node v18.2.0 - httpServer.closeAllConnections && httpServer.closeAllConnections(); - httpServer.close(() => { - process.off('SIGINT', processOnExit); - process.off('SIGTERM', processOnExit); - process.off('exit', processOnExit); - }); - }; + const processOnExit = async () => { + await closeServer(server); + process.off('SIGINT', processOnExit); + process.off('SIGTERM', processOnExit); + process.off('exit', processOnExit); + }; + + process.on('SIGINT', processOnExit); + process.on('SIGTERM', processOnExit); + process.on('exit', processOnExit); - process.on('SIGINT', processOnExit); - process.on('SIGTERM', processOnExit); - process.on('exit', processOnExit); + // Launch the build target. + const target = parseTargetString(options.buildTarget, context.projectGraph); + const build = await runExecutor(target, mergedOptions, context); - const resolvedUrls = [ - ...server.resolvedUrls.local, - ...server.resolvedUrls.network, - ]; + for await (const result of build) { + if (result.success) { + try { + if (!server) { + server = await preview(serverConfig); + } + server.printUrls(); + + const resolvedUrls = [ + ...server.resolvedUrls.local, + ...server.resolvedUrls.network, + ]; - yield { - success: true, - baseUrl: resolvedUrls[0] ?? '', - }; - } catch (e) { - console.error(e); - yield { - success: false, - baseUrl: '', - }; + yield { + success: true, + baseUrl: resolvedUrls[0] ?? '', + }; + } catch (e) { + console.error(e); + yield { + success: false, + baseUrl: '', + }; + } + } else { + yield { + success: false, + baseUrl: '', + }; + } } await new Promise(() => {}); } + +function closeServer(server?: PreviewServer): Promise { + return new Promise((resolve) => { + if (!server) { + resolve(); + } else { + const { httpServer } = server; + // closeAllConnections was added in Node v18.2.0 + httpServer.closeAllConnections && httpServer.closeAllConnections(); + httpServer.close(() => resolve()); + } + }); +} diff --git a/packages/vite/src/utils/options-utils.ts b/packages/vite/src/utils/options-utils.ts index 729161fefe96f..5a7b292e76bed 100644 --- a/packages/vite/src/utils/options-utils.ts +++ b/packages/vite/src/utils/options-utils.ts @@ -142,6 +142,7 @@ export function getViteBuildOptions( manifest: options.manifest, ssrManifest: options.ssrManifest, ssr: options.ssr, + watch: options.watch, }; }