import chokidar from 'chokidar'; import esbuild from 'esbuild'; import { $ } from 'execa'; import path from 'path'; import { fileURLToPath } from 'url'; import { excludeVendorFromSourceMap } from './plugins.mjs'; process.env['NODE_ENV'] = 'development'; process.env['VITE_HOST'] = '0.0.0.0'; process.env['VITE_PORT'] = 6969; /** * This script does several things: * 1. Runs the vite server in dev mode `yarn vite` (unless --no-renderer is passed) * 2. Runs a file watcher for the main processes * 3. Builds the main process on file changes * 4. Runs electron which loads renderer using vite server url */ /** * @type {null | Function} global function used to stop dev */ const dirname = path.dirname(fileURLToPath(import.meta.url)); const root = path.join(dirname, '..', '..'); const $$ = $({ stdio: 'inherit' }); let isReload = false; /** * @type {null | import('execa').ExecaChildProcess} */ let electronProcess = null; /** * @type {null | import('execa').ExecaChildProcess} */ let viteProcess = null; console.log(`running Frappe Books in dev mode\nroot: ${root}`); if (!process.argv.includes('--no-renderer')) { viteProcess = $$`yarn vite`; } /** * Create esbuild context that is used * to [re]build the main process code */ const ctx = await esbuild.context({ entryPoints: [path.join(root, 'main.ts')], bundle: true, sourcemap: true, sourcesContent: false, platform: 'node', target: 'node16', outfile: path.join(root, 'dist', 'dev', 'main.js'), external: ['knex', 'electron', 'better-sqlite3'], plugins: [excludeVendorFromSourceMap], write: true, }); /** * Create a file watcher so that rebuild * can be triggered everytime a main process * file changes. */ const fswatcher = chokidar.watch([ path.join(root, 'main.ts'), path.join(root, 'main'), ]); /** * Callback function to cleanly shut file watching * and rebuilding objects. * * Called on CTRL+C and kill */ const terminate = async () => { await fswatcher.close(); await ctx.dispose(); if (electronProcess) { electronProcess.kill(); } if (viteProcess) { viteProcess.kill(); } process.exit(); }; process.on('SIGINT', terminate); process.on('SIGTERM', terminate); if (viteProcess) { viteProcess.on('close', terminate); } /** * Build once and run electron before setting file watcher */ await handleResult(await ctx.rebuild()); electronProcess = runElectron(); /** * On main process source files change * - rebuild main process * - restart electron */ fswatcher.on('change', async (path) => { console.log(`change detected:\n\t${path}`); const result = await ctx.rebuild(); await handleResult(result); console.log(`main process source rebuilt\nrestarting electron`); if (electronProcess) { isReload = true; electronProcess.kill(); electronProcess = runElectron(); } }); /** * @param {esbuild.BuildResult} result */ async function handleResult(result) { if (!result.errors.length) { return; } console.log('error on build'); for (const error of result.errors) { console.log(error); } await terminate(); } function runElectron() { const electronProcess = $$`npx electron --inspect=5858 ${path.join( root, 'dist', 'dev', 'main.js' )}`; electronProcess.on('close', async () => { if (isReload) { return; } await terminate(); }); return electronProcess; }