#!/usr/bin/env node const {createServer, build, createLogger} = require('vite'); const electronPath = require('electron'); const {spawn} = require('child_process'); const node = 'node' + (process.platform === 'win32' ? '.exe' : ''); const server = !process.argv.includes('--noserver'); /** @type 'production' | 'development'' */ const mode = process.env.MODE = process.env.MODE || 'development'; /** @type {import('vite').LogLevel} */ const logLevel = 'info'; /** Messages on stderr that match any of the contained patterns will be stripped from output */ const stderrFilterPatterns = [ /** * warning about devtools extension * @see https://github.com/cawa-93/vite-electron-builder/issues/492 * @see https://github.com/MarshallOfSound/electron-devtools-installer/issues/143 */ /ExtensionLoadWarning/, ]; const setupServerPackageWatcher = () => { const logger = createLogger(logLevel, { prefix: '[srvr]', }); let spawnProcess = null; const processDied = () => { logger.error('Server has died.', {timestamp: true}); spawnProcess = null; }; return build({ mode, logLevel, build: { watch: {}, }, configFile: 'packages/server/vite.config.js', plugins: [{ name: 'reload-server-on-server-package-change', writeBundle() { if (spawnProcess !== null) { spawnProcess.off('exit', processDied); spawnProcess.kill('SIGINT'); spawnProcess = null; } spawnProcess = spawn(node, ['./index.cjs'], { cwd: './packages/server/dist', }); /** Proxy all logs */ spawnProcess.stdout.on('data', d => d.toString().trim() && d.toString().trim().split('\n').forEach(str => logger.info(str, {timestamp: true}))); /** Proxy error logs but stripe some noisy messages. See {@link stderrFilterPatterns} */ spawnProcess.stderr.on('data', d => { const data = d.toString().trim(); if (!data) return; const mayIgnore = stderrFilterPatterns.some((r) => r.test(data)); if (mayIgnore) return; logger.error(data, {timestamp: true}); }); /** Stops the watch script when the application has been quit */ spawnProcess.on('exit', processDied); }, }], }); }; /** * Setup watcher for `main` package * On file changed it totally re-launch electron app. * @param {import('vite').ViteDevServer} watchServer Renderer watch server instance. * Needs to set up `VITE_DEV_SERVER_URL` environment variable from {@link import('vite').ViteDevServer.resolvedUrls} */ const setupMainPackageWatcher = ({resolvedUrls}) => { process.env.VITE_DEV_SERVER_URL = resolvedUrls.local[0]; const logger = createLogger(logLevel, { prefix: '[main]', }); /** @type {ChildProcessWithoutNullStreams | null} */ let spawnProcess = null; return build({ mode, logLevel, build: { /** * Set to {} to enable rollup watcher * @see https://vitejs.dev/config/build-options.html#build-watch */ watch: {}, }, configFile: 'packages/main/vite.config.js', plugins: [{ name: 'reload-app-on-main-package-change', writeBundle() { /** Kill electron ff process already exist */ if (spawnProcess !== null) { spawnProcess.off('exit', process.exit); spawnProcess.kill('SIGINT'); spawnProcess = null; } /** Spawn new electron process */ spawnProcess = spawn(String(electronPath), ['.']); /** Proxy all logs */ spawnProcess.stdout.on('data', d => d.toString().trim() && logger.warn(d.toString(), {timestamp: true})); /** Proxy error logs but stripe some noisy messages. See {@link stderrFilterPatterns} */ spawnProcess.stderr.on('data', d => { const data = d.toString().trim(); if (!data) return; const mayIgnore = stderrFilterPatterns.some((r) => r.test(data)); if (mayIgnore) return; logger.error(data, {timestamp: true}); }); /** Stops the watch script when the application has been quit */ spawnProcess.on('exit', process.exit); }, }], }); }; /** * Setup watcher for `preload` package * On file changed it reload web page. * @param {import('vite').ViteDevServer} watchServer Renderer watch server instance. * Required to access the web socket of the page. By sending the `full-reload` command to the socket, it reloads the web page. */ const setupPreloadPackageWatcher = ({ws}) => build({ mode, logLevel, build: { /** * Set to {} to enable rollup watcher * @see https://vitejs.dev/config/build-options.html#build-watch */ watch: {}, }, configFile: 'packages/preload/vite.config.js', plugins: [{ name: 'reload-page-on-preload-package-change', writeBundle() { ws.send({ type: 'full-reload', }); }, }], }); (async () => { try { /** * Renderer watcher * This must be the first, * because the {@link setupMainPackageWatcher} and {@link setupPreloadPackageWatcher} depend on the renderer params */ const rendererWatchServer = await createServer({ mode, logLevel, configFile: 'packages/renderer/vite.config.js', }); /** * Should launch watch server before create other watchers */ await rendererWatchServer.listen(); /** * See {@link setupPreloadPackageWatcher} JSDoc */ await setupPreloadPackageWatcher(rendererWatchServer); /** * See {@link setupMainPackageWatcher} JSDoc */ await setupMainPackageWatcher(rendererWatchServer); if(server) await setupServerPackageWatcher(); } catch (e) { console.error(e); process.exit(1); } })();