perf(electron): compile the main-process bundle from a V8 code cache (#1696)

main.js is now a small entry that enables Node's on-disk V8 compile cache under userData/v8-compile-cache and then requires the application bundle main.app.js. Warm launches reach app.whenReady about 13 ms sooner at the median; IPTVNATOR_DISABLE_COMPILE_CACHE=1 turns it off and IPTVNATOR_COMPILE_CACHE_DIR relocates it.

Packaging now ships main.app.js explicitly and verify:package-layout requires the main-process entry files in app.asar, because nx-electron copies the backend through an allowlist. The Xtream benchmark build identity hashes both the launcher and the bundle.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5.1 authored and GitHub committed 2026-09-26 19:19:30 +02:00
1 parent 0f1cad4b16
commit 587e19541f
12 files changed
+587 -20

No files matched your search

@@ -62,13 +62,23 @@ export const BUILD_IDENTITY: XtreamBenchmarkBuildIdentity = {
'database-worker-source-map'
),
},
main: {
launcher: {
javascript: buildFile(
'dist/apps/electron-backend/main.js',
'main-javascript'
'launcher-javascript'
),
sourceMap: buildFile(
'dist/apps/electron-backend/main.js.map',
'launcher-source-map'
),
},
main: {
javascript: buildFile(
'dist/apps/electron-backend/main.app.js',
'main-javascript'
),
sourceMap: buildFile(
'dist/apps/electron-backend/main.app.js.map',
'main-source-map'
),
},
@@ -11,6 +11,7 @@ const RENDERER_FILE =
const BUILD_KEYS = ['electron', 'renderer'] as const;
const ELECTRON_KEYS = [
'databaseWorker',
'launcher',
'main',
'playlistRefreshWorker',
'preload',
@@ -26,7 +27,8 @@ const RENDERER_KEYS = [
] as const;
const ELECTRON_PATHS = {
databaseWorker: 'dist/apps/electron-backend/workers/database.worker.js',
main: 'dist/apps/electron-backend/main.js',
launcher: 'dist/apps/electron-backend/main.js',
main: 'dist/apps/electron-backend/main.app.js',
playlistRefreshWorker:
'dist/apps/electron-backend/workers/playlist-refresh.worker.js',
preload: 'dist/apps/electron-backend/main.preload.js',
@@ -44,6 +46,7 @@ export function parseXtreamBenchmarkBuildIdentity(
electronInput['databaseWorker'],
ELECTRON_PATHS.databaseWorker
),
launcher: pair(electronInput['launcher'], ELECTRON_PATHS.launcher),
main: pair(electronInput['main'], ELECTRON_PATHS.main),
playlistRefreshWorker: pair(
electronInput['playlistRefreshWorker'],
@@ -38,9 +38,14 @@ describe('Xtream benchmark build identity', () => {
assert.deepEqual(identity.electron.main.javascript, {
bytes: 4,
path: 'dist/apps/electron-backend/main.js',
path: 'dist/apps/electron-backend/main.app.js',
sha256: sha256('main'),
});
assert.deepEqual(identity.electron.launcher.javascript, {
bytes: 8,
path: 'dist/apps/electron-backend/main.js',
sha256: sha256('launcher'),
});
assert.deepEqual(identity.electron.preload.sourceMap, {
bytes: 11,
path: 'dist/apps/electron-backend/main.preload.js.map',
@@ -112,7 +117,7 @@ describe('Xtream benchmark build identity', () => {
const symlinkRoot = await buildFixture();
const mainPath = join(
symlinkRoot,
'dist/apps/electron-backend/main.js'
'dist/apps/electron-backend/main.app.js'
);
const external = join(symlinkRoot, 'external-main.js');
await writeFile(external, 'main');
@@ -219,8 +224,10 @@ async function buildFixture(): Promise<string> {
mkdir(join(renderer, 'assets'), { recursive: true }),
]);
await Promise.all([
writeFile(join(backend, 'main.js'), 'main'),
writeFile(join(backend, 'main.js.map'), 'main-map'),
writeFile(join(backend, 'main.js'), 'launcher'),
writeFile(join(backend, 'main.js.map'), 'launcher-map'),
writeFile(join(backend, 'main.app.js'), 'main'),
writeFile(join(backend, 'main.app.js.map'), 'main-map'),
writeFile(join(backend, 'main.preload.js'), 'preload'),
writeFile(join(backend, 'main.preload.js.map'), 'preload-map'),
writeFile(join(workers, 'database.worker.js'), 'database'),
@@ -18,6 +18,7 @@ export interface XtreamBuildPairIdentity {
export interface XtreamBenchmarkBuildIdentity {
readonly electron: {
readonly databaseWorker: XtreamBuildPairIdentity;
readonly launcher: XtreamBuildPairIdentity;
readonly main: XtreamBuildPairIdentity;
readonly playlistRefreshWorker: XtreamBuildPairIdentity;
readonly preload: XtreamBuildPairIdentity;
@@ -35,7 +36,10 @@ const BACKEND_ROOT = 'dist/apps/electron-backend';
const RENDERER_ROOT = 'dist/apps/web';
const ELECTRON_PATHS = {
databaseWorker: `${BACKEND_ROOT}/workers/database.worker.js`,
main: `${BACKEND_ROOT}/main.js`,
// main.js only enables the compile cache and requires main.app.js, but
// it decides startup behavior, so both belong to the identity.
launcher: `${BACKEND_ROOT}/main.js`,
main: `${BACKEND_ROOT}/main.app.js`,
playlistRefreshWorker: `${BACKEND_ROOT}/workers/playlist-refresh.worker.js`,
preload: `${BACKEND_ROOT}/main.preload.js`,
} as const;
@@ -45,17 +49,25 @@ export async function captureXtreamBuildIdentity(
): Promise<XtreamBenchmarkBuildIdentity> {
try {
if (!isAbsolute(workspaceRoot)) invalid();
const [databaseWorker, main, playlistRefreshWorker, preload, renderer] =
await Promise.all([
readPair(workspaceRoot, ELECTRON_PATHS.databaseWorker),
readPair(workspaceRoot, ELECTRON_PATHS.main),
readPair(workspaceRoot, ELECTRON_PATHS.playlistRefreshWorker),
readPair(workspaceRoot, ELECTRON_PATHS.preload),
readRenderer(workspaceRoot),
]);
const [
databaseWorker,
launcher,
main,
playlistRefreshWorker,
preload,
renderer,
] = await Promise.all([
readPair(workspaceRoot, ELECTRON_PATHS.databaseWorker),
readPair(workspaceRoot, ELECTRON_PATHS.launcher),
readPair(workspaceRoot, ELECTRON_PATHS.main),
readPair(workspaceRoot, ELECTRON_PATHS.playlistRefreshWorker),
readPair(workspaceRoot, ELECTRON_PATHS.preload),
readRenderer(workspaceRoot),
]);
return Object.freeze({
electron: Object.freeze({
databaseWorker,
launcher,
main,
playlistRefreshWorker,
preload,