#!/usr/bin/env node // @ts-check const { spawn } = require('child_process'); const electronPath = require('electron'); const { build, createLogger, createServer } = require('vite'); /** @type {string} */ const mode = process.env.MODE = process.env.MODE || 'development'; /** @type {import('vite').LogLevel} */ const LOG_LEVEL = 'info'; /** @type {import('vite').InlineConfig} */ const sharedConfig = { mode, build: { watch: {}, }, logLevel: LOG_LEVEL, }; /** * Messages on stderr that match any of the contained patterns will be stripped from output * * @type {RegExp[]} */ const stderrFilterPatterns = [ // warning about devtools extension // https://github.com/cawa-93/vite-electron-builder/issues/492 // https://github.com/MarshallOfSound/electron-devtools-installer/issues/143 /ExtensionLoadWarning/, // GPU sandbox error with the mesa GLSL cache // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=918433 /InitializeSandbox\(\) called with multiple threads in process gpu-process/, ]; /** * @param {{name: string; configFile: string; writeBundle?: import('rollup').OutputPlugin['writeBundle'] }} config * @returns {Promise} */ function getWatcher({ name, configFile, writeBundle }) { return build({ ...sharedConfig, configFile, plugins: [ { name, writeBundle, }, ], }); } /** * Start or restart App when source files are changed. * * @param {import('vite').ViteDevServer} viteDevServer * @returns {Promise} */ function setupMainPackageWatcher(viteDevServer) { // Write a value to an environment variable to pass it to the main process. const protocol = `http${viteDevServer.config.server.https ? 's' : ''}:`; const host = viteDevServer.config.server.host || 'localhost'; const port = viteDevServer.config.server.port; const path = '/'; process.env.VITE_DEV_SERVER_URL = `${protocol}//${host}:${port}${path}`; const logger = createLogger( LOG_LEVEL, { prefix: '[main]', }, ); /** @type {import('child_process').ChildProcessWithoutNullStreams | null} */ let spawnProcess = null; return getWatcher({ name: 'reload-app-on-main-package-change', configFile: 'packages/main/vite.config.js', writeBundle() { if (spawnProcess !== null) { spawnProcess.kill('SIGINT'); spawnProcess = null; } spawnProcess = spawn(String(electronPath), ['.']); spawnProcess.stdout.on('data', (data) => { if (data.toString().trim() !== '') { logger.warn(data.toString(), {timestamp: true}) } }); spawnProcess.stderr.on('data', (data) => { const trimmedData = data.toString().trim(); if (trimmedData === '') { return; } const mayIgnore = stderrFilterPatterns.some((r) => r.test(data)); if (mayIgnore) { return; } logger.error(data, { timestamp: true }); }); }, }); } /** * Reload App when source files are changed. * * @param {import('vite').ViteDevServer} viteDevServer * @returns {Promise} */ function setupPreloadPackageWatcher(viteDevServer) { return getWatcher({ name: 'reload-page-on-preload-package-change', configFile: 'packages/preload/vite.config.js', writeBundle() { viteDevServer.ws.send({ type: 'full-reload', }); }, }); } /** * Reload services when source files are changed. * * @param {import('vite').ViteDevServer} viteDevServer * @returns {Promise} */ function setupServicePreloadPackageWatcher(viteDevServer) { return getWatcher({ name: 'reload-services-on-service-preload-package-change', configFile: 'packages/service-preload/vite.config.js', writeBundle() { console.log('wrote'); viteDevServer.ws.send({ type: 'custom', event: 'sophie:reload-services', }); }, }); } /** * Rebuild package when source files are changed. * * @param {string} packageName The name of the package to watch. * @returns {Promise} */ function setupSharedPackageWatcher(packageName) { return getWatcher({ name: `rebuild-package-on-${packageName}-package-change`, configFile: `packages/${packageName}/vite.config.js`, }); } /** * @returns {Promise} */ async function setupDevEnvironment() { await setupSharedPackageWatcher('shared'); const viteDevServer = await createServer({ ...sharedConfig, configFile: 'packages/renderer/vite.config.js', }); await viteDevServer.listen(); await Promise.all([ setupPreloadPackageWatcher(viteDevServer), setupServicePreloadPackageWatcher(viteDevServer), setupSharedPackageWatcher('shared'), ]); return setupMainPackageWatcher(viteDevServer); } setupDevEnvironment().catch((err) => { console.error(err); process.exit(1); });