Improve forge configs

This commit is contained in:
Hakan Shehu
2024-11-16 23:20:30 +01:00
parent a65cb839b6
commit 2c6029d92f
6 changed files with 848 additions and 1041 deletions

View File

@@ -1,4 +1,35 @@
/// <reference types="@electron-forge/plugin-vite/forge-vite-env" /> export {}; // Make this a module
declare const MAIN_WINDOW_VITE_DEV_SERVER_URL: string; declare global {
declare const MAIN_WINDOW_VITE_NAME: string; // This allows TypeScript to pick up the magic constants that's auto-generated by Forge's Vite
// plugin that tells the Electron app where to look for the Vite-bundled app code (depending on
// whether you're running in development or production).
const MAIN_WINDOW_VITE_DEV_SERVER_URL: string;
const MAIN_WINDOW_VITE_NAME: string;
namespace NodeJS {
interface Process {
// Used for hot reload after preload scripts.
viteDevServers: Record<string, import('vite').ViteDevServer>;
}
}
type VitePluginConfig = ConstructorParameters<
typeof import('@electron-forge/plugin-vite').VitePlugin
>[0];
interface VitePluginRuntimeKeys {
VITE_DEV_SERVER_URL: `${string}_VITE_DEV_SERVER_URL`;
VITE_NAME: `${string}_VITE_NAME`;
}
}
declare module 'vite' {
interface ConfigEnv<
K extends keyof VitePluginConfig = keyof VitePluginConfig,
> {
root: string;
forgeConfig: VitePluginConfig;
forgeConfigSelf: VitePluginConfig[K][number];
}
}

View File

@@ -0,0 +1,116 @@
import { builtinModules } from 'node:module';
import type { AddressInfo } from 'node:net';
import type { ConfigEnv, Plugin, UserConfig } from 'vite';
import pkg from './package.json';
import path from 'path';
export const builtins = [
'electron',
...builtinModules.map((m) => [m, `node:${m}`]).flat(),
];
export const external = [
...builtins,
...Object.keys(
'dependencies' in pkg ? (pkg.dependencies as Record<string, unknown>) : {}
).filter((key) => !key.startsWith('@colanode')),
];
export function getBuildConfig(env: ConfigEnv<'build'>): UserConfig {
const { root, mode, command } = env;
return {
root,
mode,
build: {
// Prevent multiple builds from interfering with each other.
emptyOutDir: false,
// 🚧 Multiple builds may conflict.
outDir: '.vite/build',
watch: command === 'serve' ? {} : null,
minify: command === 'build',
},
clearScreen: false,
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
};
}
export function getDefineKeys(names: string[]) {
const define: { [name: string]: VitePluginRuntimeKeys } = {};
return names.reduce((acc, name) => {
const NAME = name.toUpperCase();
const keys: VitePluginRuntimeKeys = {
VITE_DEV_SERVER_URL: `${NAME}_VITE_DEV_SERVER_URL`,
VITE_NAME: `${NAME}_VITE_NAME`,
};
return { ...acc, [name]: keys };
}, define);
}
export function getBuildDefine(env: ConfigEnv<'build'>) {
const { command, forgeConfig } = env;
const names = forgeConfig.renderer
.filter(({ name }) => name != null)
.map(({ name }) => name!);
const defineKeys = getDefineKeys(names);
const define = Object.entries(defineKeys).reduce(
(acc, [name, keys]) => {
const { VITE_DEV_SERVER_URL, VITE_NAME } = keys;
const def = {
[VITE_DEV_SERVER_URL]:
command === 'serve'
? JSON.stringify(process.env[VITE_DEV_SERVER_URL])
: undefined,
[VITE_NAME]: JSON.stringify(name),
};
return { ...acc, ...def };
},
{} as Record<string, any>
);
return define;
}
export function pluginExposeRenderer(name: string): Plugin {
const { VITE_DEV_SERVER_URL } = getDefineKeys([name])[name];
return {
name: '@electron-forge/plugin-vite:expose-renderer',
configureServer(server) {
process.viteDevServers ??= {};
// Expose server for preload scripts hot reload.
process.viteDevServers[name] = server;
server.httpServer?.once('listening', () => {
const addressInfo = server.httpServer!.address() as AddressInfo;
// Expose env constant for main process use.
process.env[VITE_DEV_SERVER_URL] =
`http://localhost:${addressInfo?.port}`;
});
},
};
}
export function pluginHotRestart(command: 'reload' | 'restart'): Plugin {
return {
name: '@electron-forge/plugin-vite:hot-restart',
closeBundle() {
if (command === 'reload') {
for (const server of Object.values(process.viteDevServers)) {
// Preload scripts hot reload.
server.ws.send({ type: 'full-reload' });
}
} else {
// Main process hot restart.
// https://github.com/electron/forge/blob/v7.2.0/packages/api/core/src/api/start.ts#L216-L223
process.stdin.emit('data', 'rs');
}
},
};
}

View File

@@ -1,35 +1,40 @@
import { defineConfig } from 'vite'; import type { ConfigEnv, UserConfig } from 'vite';
import { defineConfig, mergeConfig } from 'vite';
import {
getBuildConfig,
getBuildDefine,
external,
pluginHotRestart,
} from './vite.base.config';
import path from 'path';
// https://vitejs.dev/config // https://vitejs.dev/config
export default defineConfig(async () => { export default defineConfig((env) => {
const { viteStaticCopy } = await import('vite-plugin-static-copy'); const forgeEnv = env as ConfigEnv<'build'>;
return { const { forgeConfigSelf } = forgeEnv;
resolve: { const define = getBuildDefine(forgeEnv);
alias: { const config: UserConfig = {
'@': '/src',
},
},
build: { build: {
lib: {
entry: forgeConfigSelf.entry!,
fileName: () => '[name].js',
formats: ['cjs'],
},
rollupOptions: { rollupOptions: {
external: [ external,
'better-sqlite3', },
'kysely', },
'unzipper', plugins: [pluginHotRestart('restart')],
'mime-types', define,
'node-gyp-build', resolve: {
'asynckit', // Load the Node.js entry.
], mainFields: ['module', 'jsnext:main', 'jsnext'],
alias: {
'@': path.resolve(__dirname, './src'),
node_modules: path.resolve(__dirname, '../../node_modules'),
}, },
}, },
plugins: [
viteStaticCopy({
targets: [
{
src: 'assets/**/*',
dest: 'assets',
},
],
}),
],
}; };
return mergeConfig(getBuildConfig(forgeEnv), config);
}); });

View File

@@ -1,10 +1,35 @@
import { defineConfig } from 'vite'; import type { ConfigEnv, UserConfig } from 'vite';
import { defineConfig, mergeConfig } from 'vite';
import { getBuildConfig, external, pluginHotRestart } from './vite.base.config';
import path from 'path';
// https://vitejs.dev/config // https://vitejs.dev/config
export default defineConfig({ export default defineConfig((env) => {
resolve: { const forgeEnv = env as ConfigEnv<'build'>;
alias: { const { forgeConfigSelf } = forgeEnv;
'@': '/src', const config: UserConfig = {
build: {
rollupOptions: {
external,
// Preload scripts may contain Web assets, so use the `build.rollupOptions.input` instead `build.lib.entry`.
input: forgeConfigSelf.entry!,
output: {
format: 'cjs',
// It should not be split chunks.
inlineDynamicImports: true,
entryFileNames: '[name].js',
chunkFileNames: '[name].js',
assetFileNames: '[name].[ext]',
},
},
}, },
}, plugins: [pluginHotRestart('reload')],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
};
return mergeConfig(getBuildConfig(forgeEnv), config);
}); });

View File

@@ -1,10 +1,28 @@
import type { ConfigEnv, UserConfig } from 'vite';
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import { pluginExposeRenderer } from './vite.base.config';
import path from 'path';
// https://vitejs.dev/config // https://vitejs.dev/config
export default defineConfig({ export default defineConfig((env) => {
resolve: { const forgeEnv = env as ConfigEnv<'renderer'>;
alias: { const { root, mode, forgeConfigSelf } = forgeEnv;
'@': '/src', const name = forgeConfigSelf.name ?? '';
return {
root,
mode,
base: './',
build: {
outDir: `.vite/renderer/${name}`,
}, },
}, plugins: [pluginExposeRenderer(name)],
resolve: {
preserveSymlinks: true,
alias: {
'@': path.resolve(__dirname, './src'),
},
},
clearScreen: false,
} as UserConfig;
}); });

1612
package-lock.json generated

File diff suppressed because it is too large Load Diff