Skip to content

Commit

Permalink
Split custom electron plugin to separate files.
Browse files Browse the repository at this point in the history
  • Loading branch information
webfiltered committed Dec 20, 2024
1 parent 71e7598 commit dcae8aa
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 91 deletions.
58 changes: 58 additions & 0 deletions infrastructure/ElectronViteApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { ViteDevServer } from 'vite';
import { spawn, type ChildProcess } from 'node:child_process';
import electronPath from 'electron';

export class ViteElectronApp {
current!: ChildProcess;

busy = false;
attemptedStartWhenBusy = false;

constructor(public server: ViteDevServer) {
this.start();
}

/** Attempts to kill the current instance, then starts a new instace of the electron app. */
start() {
if (this.busy) {
this.attemptedStartWhenBusy = true;
return;
}
this.busy = true;
this.close();

// Spawn new electron process
// eslint-disable-next-line @typescript-eslint/no-base-to-string
this.current = spawn(String(electronPath), ['--inspect=9223', '.'], { stdio: 'inherit' });

// Stops the watch script when the application has been quit
this.current.addListener('exit', () => {
this.server.close().catch(console.error);
});

this.busy = false;
this.restartIfRequired();
}

/** Kills the current instance */
close(restart = false) {
if (!this.current) return;

this.busy = true;

this.current.removeAllListeners('exit');
this.current.on('exit', () => {
this.busy = false;
if (restart) this.start();
});
this.current.kill('SIGINT');
}

restartIfRequired() {
if (!this.attemptedStartWhenBusy) return;

console.warn('... restarting because required...');
this.attemptedStartWhenBusy = false;
this.close(true);
}
}
38 changes: 38 additions & 0 deletions infrastructure/viteElectronAppPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { PluginOption } from 'vite';
import { ViteElectronApp } from './ElectronViteApp';
import path from 'node:path';
import { readFile } from 'node:fs/promises';

export function viteElectronAppPlugin(): PluginOption {
let electronApp: ViteElectronApp;

return {
name: 'Electron App reload on save',
apply: 'serve',
async hotUpdate({ file }) {
if (this.environment.name !== 'client') return;
console.debug('hotUpdate');

// Reload app on file change
const relative = path.relative('.', file);

// Invalidate modules manually
const json = await readFile(path.join(process.cwd(), 'tsconfig.build.json'));
const parsed: unknown = JSON.parse(json.toString());

if (typeof parsed === 'object' && parsed && 'include' in parsed && Array.isArray(parsed.include)) {
for (const include of parsed.include) {
console.log(include);
if (typeof include === 'string' && path.matchesGlob(relative, include)) electronApp.start();
}
}
return [];
},
configureServer(server) {
electronApp = new ViteElectronApp(server);
},
buildEnd() {
electronApp.close();
},
};
}
9 changes: 8 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,12 @@
},
"exclude": [".history", ".vite", "assets", "dist", "node_modules", "out"],
// Include JS files so they are covered by projectService (ESLint)
"include": ["src/**/*", "*.ts", "*.js", "scripts/**/*", "tests/**/*"]
"include": [
"src/**/*",
"*.ts",
"*.js",
"infrastructure/**/*",
"scripts/**/*",
"tests/**/*"
]
}
94 changes: 4 additions & 90 deletions vite.main.config.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,13 @@
/// <reference types="vitest/config" />
import type { UserConfig, ViteDevServer } from 'vite';
import { UserConfig } from 'vite';
import { defineConfig, mergeConfig } from 'vite';
import { getBuildConfig, external, pluginHotRestart } from './vite.base.config';
import { viteElectronAppPlugin } from './infrastructure/viteElectronAppPlugin';
import { sentryVitePlugin } from '@sentry/vite-plugin';
import { version } from './package.json';
import { ChildProcess, spawn } from 'node:child_process';
import electronPath from 'electron';
import { readFile } from 'node:fs/promises';
import path from 'node:path';

class SingletonElectronApp {
current!: ChildProcess;

busy = false;
triedToStartWhilstBusy = false;

constructor(public server: ViteDevServer) {
this.start();
}

/** Attempts to kill the current instance, then starts a new instace of the electron app. */
start() {
if (this.busy) {
this.triedToStartWhilstBusy = true;
return;
}
this.busy = true;
this.close();

// Spawn new electron process
// eslint-disable-next-line @typescript-eslint/no-base-to-string
this.current = spawn(String(electronPath), ['--inspect=9223', '.'], { stdio: 'inherit' });

// Stops the watch script when the application has been quit
this.current.addListener('exit', () => {
this.server.close().catch(console.error);
});

this.busy = false;
this.restartIfRequired();
}

/** Kills the current instance */
close(restart = false) {
if (!this.current) return;

this.busy = true;

this.current.removeAllListeners('exit');
this.current.on('exit', () => {
this.busy = false;
if (restart) this.start();
});
this.current.kill('SIGINT');
}

restartIfRequired() {
if (!this.triedToStartWhilstBusy) return;

console.warn('... restarting because required...');
this.triedToStartWhilstBusy = false;
this.close(true);
}
}

// https://vitejs.dev/config
export default defineConfig((env) => {
let electronApp: SingletonElectronApp;

const config: UserConfig = {
build: {
outDir: '.vite/build',
Expand All @@ -88,34 +28,8 @@ export default defineConfig((env) => {
},
},
plugins: [
{
name: 'hotUpdate Plugin',
apply: 'serve',
async hotUpdate({ file }) {
if (this.environment.name !== 'client') return;
console.warn('hotUpdate');

// Reload app on file change
const relative = path.relative('.', file);

// Invalidate modules manually
const json = await readFile(path.join(process.cwd(), 'tsconfig.build.json'));
const parsed: unknown = JSON.parse(json.toString());
if (typeof parsed === 'object' && parsed && 'include' in parsed && Array.isArray(parsed.include)) {
for (const include of parsed.include) {
console.log(include);
if (typeof include === 'string' && path.matchesGlob(relative, include)) electronApp.start();
}
}
return [];
},
configureServer(server) {
electronApp = new SingletonElectronApp(server);
},
buildEnd() {
electronApp.close();
},
},
// Custom hot reload solution for vite 6
viteElectronAppPlugin(),
pluginHotRestart('restart'),
process.env.NODE_ENV === 'production'
? sentryVitePlugin({
Expand Down

0 comments on commit dcae8aa

Please sign in to comment.