From 3dba3a07f077d11051631f0744cc3570a66a6461 Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Sun, 8 Sep 2024 16:26:12 -0700 Subject: [PATCH] Generate preload cache from live app --- app/config.ts | 1 + app/main.ts | 3 + package.json | 2 +- preload.wrapper.ts | 13 +++- ts/scripts/generate-preload-cache.html | 12 ---- ts/scripts/generate-preload-cache.preload.ts | 29 --------- ts/scripts/generate-preload-cache.ts | 66 ++++++++++++-------- 7 files changed, 57 insertions(+), 69 deletions(-) delete mode 100644 ts/scripts/generate-preload-cache.html delete mode 100644 ts/scripts/generate-preload-cache.preload.ts diff --git a/app/config.ts b/app/config.ts index f38515e1c..512f1b981 100644 --- a/app/config.ts +++ b/app/config.ts @@ -38,6 +38,7 @@ if (getEnvironment() === Environment.PackagedApp) { process.env.NODE_TLS_REJECT_UNAUTHORIZED = ''; process.env.SIGNAL_ENABLE_HTTP = ''; process.env.SIGNAL_CI_CONFIG = ''; + process.env.GENERATE_PRELOAD_CACHE = ''; } // We load config after we've made our modifications to NODE_ENV diff --git a/app/main.ts b/app/main.ts index 587fd87c9..67d649188 100644 --- a/app/main.ts +++ b/app/main.ts @@ -2567,6 +2567,9 @@ ipc.on('restart', () => { app.quit(); }); ipc.on('shutdown', () => { + if (process.env.GENERATE_PRELOAD_CACHE) { + windowState.markReadyForShutdown(); + } app.quit(); }); diff --git a/package.json b/package.json index 540cc597a..b3448225c 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "build:esbuild:prod": "node scripts/esbuild.js --prod", "build:electron": "electron-builder --config.extraMetadata.environment=$SIGNAL_ENV", "build:release": "cross-env SIGNAL_ENV=production npm run build:electron -- --config.directories.output=release", - "build:preload-cache": "electron --js-args=\"--predictable --random-seed 1\" ts/scripts/generate-preload-cache.js", + "build:preload-cache": "node ts/scripts/generate-preload-cache.js", "verify": "run-p --print-label verify:*", "verify:ts": "tsc --noEmit", "electron:install-app-deps": "electron-builder install-app-deps" diff --git a/preload.wrapper.ts b/preload.wrapper.ts index 7773a54ce..d7e83ea9e 100644 --- a/preload.wrapper.ts +++ b/preload.wrapper.ts @@ -5,9 +5,10 @@ // https://github.com/zertosh/v8-compile-cache/blob/b6bc035d337fbda0e6e3ec7936499048fc9deafc/v8-compile-cache.js import { Module } from 'node:module'; -import { readFileSync } from 'node:fs'; +import { readFileSync, writeFileSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { Script } from 'node:vm'; +import { ipcRenderer } from 'electron'; const srcPath = join(__dirname, 'preload.bundle.js'); const cachePath = join(__dirname, 'preload.bundle.cache'); @@ -22,6 +23,8 @@ try { } } +let script: Script | undefined; + function compile( filename: string, content: string @@ -31,7 +34,7 @@ function compile( // create wrapper function const wrapper = Module.wrap(content); - const script = new Script(wrapper, { + script = new Script(wrapper, { filename, lineOffset: 0, cachedData, @@ -124,3 +127,9 @@ ModuleInternals.prototype._compile = function _compile( // eslint-disable-next-line import/no-dynamic-require require(srcPath); + +// See `ts/scripts/generate-preload-cache.ts` +if (script && process.env.GENERATE_PRELOAD_CACHE) { + writeFileSync(cachePath, script.createCachedData()); + ipcRenderer.send('shutdown'); +} diff --git a/ts/scripts/generate-preload-cache.html b/ts/scripts/generate-preload-cache.html deleted file mode 100644 index 9e17b3325..000000000 --- a/ts/scripts/generate-preload-cache.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - Generating cache... - - - Generating cache... - - diff --git a/ts/scripts/generate-preload-cache.preload.ts b/ts/scripts/generate-preload-cache.preload.ts deleted file mode 100644 index 51122245f..000000000 --- a/ts/scripts/generate-preload-cache.preload.ts +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2024 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -import { Module } from 'node:module'; -import { readFile, writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; -import { Script } from 'node:vm'; -import { ipcRenderer } from 'electron'; - -ipcRenderer.on('compile', async () => { - try { - const sourceFile = join(__dirname, '..', '..', 'preload.bundle.js'); - const outFile = sourceFile.replace(/\.js$/, ''); - - const source = await readFile(sourceFile, 'utf8'); - const script = new Script(Module.wrap(source), { - filename: 'preload.bundle.js', - produceCachedData: true, - }); - if (!script.cachedDataProduced || !script.cachedData) { - throw new Error('Cached data not produced'); - } - - await writeFile(`${outFile}.cache`, script.cachedData); - await ipcRenderer.invoke('done'); - } catch (error) { - await ipcRenderer.invoke('error', error); - } -}); diff --git a/ts/scripts/generate-preload-cache.ts b/ts/scripts/generate-preload-cache.ts index fdd25fde9..93adb6b66 100644 --- a/ts/scripts/generate-preload-cache.ts +++ b/ts/scripts/generate-preload-cache.ts @@ -1,35 +1,51 @@ // Copyright 2024 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import { pathToFileURL } from 'node:url'; +import { spawnSync } from 'node:child_process'; import { join } from 'node:path'; -import { app, BrowserWindow, ipcMain } from 'electron'; +import { tmpdir } from 'node:os'; +import { mkdtemp, rm } from 'node:fs/promises'; -app.on('ready', async () => { - ipcMain.handle('done', () => { - app.quit(); - }); +const ROOT_DIR = join(__dirname, '..', '..'); - ipcMain.handle('error', (_event, err) => { - console.error(err); - process.exit(1); - }); +const ELECTRON = join( + ROOT_DIR, + 'node_modules', + '.bin', + process.platform === 'win32' ? 'electron.cmd' : 'electron' +); - const window = new BrowserWindow({ - show: false, - webPreferences: { - devTools: true, - nodeIntegration: false, - sandbox: false, - contextIsolation: true, - preload: join(__dirname, 'generate-preload-cache.preload.js'), - }, - }); +async function main(): Promise { + const storagePath = await mkdtemp(join(tmpdir(), 'signal-preload-cache-')); - await window.loadURL( - pathToFileURL(join(__dirname, 'generate-preload-cache.html')).toString() - ); + let status: number | null; + try { + ({ status } = spawnSync( + ELECTRON, + ['--js-args="--predictable --random-seed 1"', 'ci.js'], + { + cwd: ROOT_DIR, + env: { + ...process.env, + GENERATE_PRELOAD_CACHE: 'on', + SIGNAL_CI_CONFIG: JSON.stringify({ + storagePath, + }), + }, + // Since we run `.cmd` file on Windows - use shell + shell: process.platform === 'win32', + } + )); + } finally { + await rm(storagePath, { recursive: true }); + } - window.webContents.openDevTools(); - window.webContents.send('compile', process.argv[2], process.argv[3]); + if (status !== 0) { + throw new Error(`Exit code: ${status}`); + } +} + +main().catch(error => { + console.error(error); + process.exit(1); });