chore: simplify bytecode compiler initialization

This commit is contained in:
alex8088 2026-10-10 02:03:25 +08:00
parent fa199eb372
commit 6b0f85a2a7
5 changed files with 22 additions and 40 deletions

View File

@ -1,6 +1,7 @@
import { build as viteBuild, mergeConfig } from 'vite' import { build as viteBuild, mergeConfig } from 'vite'
import { type InlineConfig, type MainViteConfig, type PreloadViteConfig, resolveConfig } from './config' import { type InlineConfig, type MainViteConfig, type PreloadViteConfig, resolveConfig } from './config'
import { BytecodeBuild, bytecodeBuildPluginName } from './bytecode/build' import { BytecodeCompiler } from './bytecodeCompiler'
import { BYTECODE_BUILD_PLUGIN_NAME } from './constants'
import { asyncFlatten } from './utils' import { asyncFlatten } from './utils'
async function useBytecode(config?: MainViteConfig | PreloadViteConfig): Promise<boolean> { async function useBytecode(config?: MainViteConfig | PreloadViteConfig): Promise<boolean> {
@ -15,10 +16,10 @@ async function useBytecode(config?: MainViteConfig | PreloadViteConfig): Promise
export async function build(inlineConfig: InlineConfig = {}): Promise<void> { export async function build(inlineConfig: InlineConfig = {}): Promise<void> {
process.env.NODE_ENV_ELECTRON_VITE = 'production' process.env.NODE_ENV_ELECTRON_VITE = 'production'
const bytecodeBuild = new BytecodeBuild() const bytecodeCompiler = new BytecodeCompiler()
const config = await resolveConfig( const config = await resolveConfig(
mergeConfig(inlineConfig, { mergeConfig(inlineConfig, {
plugins: [{ name: bytecodeBuildPluginName, api: { build: bytecodeBuild } }] plugins: [{ name: BYTECODE_BUILD_PLUGIN_NAME, api: { compiler: bytecodeCompiler } }]
}), }),
'build', 'build',
'production' 'production'
@ -49,9 +50,9 @@ export async function build(inlineConfig: InlineConfig = {}): Promise<void> {
if (!hasBytecode) return buildTargets() if (!hasBytecode) return buildTargets()
try { try {
await bytecodeBuild.start() await bytecodeCompiler.start()
await buildTargets() await buildTargets()
} finally { } finally {
await bytecodeBuild.stop() await bytecodeCompiler.stop()
} }
} }

View File

@ -1,25 +0,0 @@
import { type Plugin } from 'vite'
import { BytecodeCompiler, type BytecodeCompileResult, type BytecodeTarget } from './compiler'
export const bytecodeBuildPluginName = 'vite:electron-bytecode-build'
export class BytecodeBuild {
private readonly compiler = new BytecodeCompiler()
start(): Promise<void> {
return this.compiler.start()
}
compile(target: BytecodeTarget, code: string): Promise<BytecodeCompileResult> {
return this.compiler.compile(target, code)
}
stop(): Promise<void> {
return this.compiler.stop()
}
}
export function getBytecodeBuild(plugins: readonly Plugin[]): BytecodeBuild | undefined {
const plugin = plugins.find(plugin => plugin.name === bytecodeBuildPluginName)
return (plugin?.api as { build?: BytecodeBuild } | undefined)?.build
}

View File

@ -1,7 +1,7 @@
import { type ChildProcess, spawn } from 'node:child_process' import { type ChildProcess, spawn } from 'node:child_process'
import path from 'node:path' import path from 'node:path'
import { createRequire } from 'node:module' import { createRequire } from 'node:module'
import { getElectronPath } from '../electron' import { getElectronPath } from './electron'
const require = createRequire(import.meta.url) const require = createRequire(import.meta.url)
const RPC_TIMEOUT = 30_000 const RPC_TIMEOUT = 30_000

1
src/constants.ts Normal file
View File

@ -0,0 +1 @@
export const BYTECODE_BUILD_PLUGIN_NAME = 'vite:electron-bytecode-build'

View File

@ -5,18 +5,23 @@ import { type Plugin, type LibraryOptions, type Rolldown, normalizePath } from '
import * as babel from '@babel/core' import * as babel from '@babel/core'
import MagicString from 'magic-string' import MagicString from 'magic-string'
import { toRelativePath } from '../utils' import { toRelativePath } from '../utils'
import { type BytecodeBuild, getBytecodeBuild } from '../bytecode/build' import type { BytecodeCompiler, BytecodeTarget } from '../bytecodeCompiler'
import { type BytecodeTarget } from '../bytecode/compiler' import { BYTECODE_BUILD_PLUGIN_NAME } from '../constants'
// Inspired by https://github.com/bytenode/bytenode // Inspired by https://github.com/bytenode/bytenode
function getBytecodeCompiler(plugins: readonly Plugin[]): BytecodeCompiler | undefined {
const plugin = plugins.find(plugin => plugin.name === BYTECODE_BUILD_PLUGIN_NAME)
return (plugin?.api as { compiler?: BytecodeCompiler } | undefined)?.compiler
}
// Versioned envelope: magic/version (8), UTF-16 source length (4), module ID (16), V8 cache. // Versioned envelope: magic/version (8), UTF-16 source length (4), module ID (16), V8 cache.
// Keep our metadata separate from the private V8 cache header. // Keep our metadata separate from the private V8 cache header.
async function compileToBytecode(build: BytecodeBuild, target: BytecodeTarget, code: string): Promise<Buffer> { async function compileToBytecode(compiler: BytecodeCompiler, target: BytecodeTarget, code: string): Promise<Buffer> {
const id = randomBytes(16) const id = randomBytes(16)
// Reserve enough source space for an untruncated, per-artifact placeholder identity. // Reserve enough source space for an untruncated, per-artifact placeholder identity.
const source = code + '\n/*' + id.toString('hex') + '*/' const source = code + '\n/*' + id.toString('hex') + '*/'
const result = await build.compile(target, source) const result = await compiler.compile(target, source)
const header = Buffer.alloc(28) const header = Buffer.alloc(28)
header.write('EVBC0001', 0, 'ascii') header.write('EVBC0001', 0, 'ascii')
header.writeUInt32LE(result.sourceLength, 8) header.writeUInt32LE(result.sourceLength, 8)
@ -118,14 +123,14 @@ export function bytecodePlugin(options: BytecodeOptions = {}): Plugin | null {
let supported = false let supported = false
let isPreload = false let isPreload = false
let bytecodeBuild: BytecodeBuild | undefined let bytecodeCompiler: BytecodeCompiler | undefined
return { return {
name: 'vite:bytecode', name: 'vite:bytecode',
apply: 'build', apply: 'build',
enforce: 'post', enforce: 'post',
configResolved(config): void { configResolved(config): void {
bytecodeBuild = getBytecodeBuild(config.plugins) bytecodeCompiler = getBytecodeCompiler(config.plugins)
if (supported) { if (supported) {
return return
} }
@ -184,8 +189,8 @@ export function bytecodePlugin(options: BytecodeOptions = {}): Plugin | null {
const bundles = Object.keys(output) const bundles = Object.keys(output)
if (!bytecodeBuild) { if (!bytecodeCompiler) {
throw new Error('Bytecode compilation requires an electron-vite build session') throw new Error('Bytecode compilation requires an electron-vite build')
} }
for (const name of bundles) { for (const name of bundles) {
const chunk = output[name] const chunk = output[name]
@ -207,7 +212,7 @@ export function bytecodePlugin(options: BytecodeOptions = {}): Plugin | null {
} }
} }
if (bytecodeChunks.includes(name)) { if (bytecodeChunks.includes(name)) {
const bytecodeBuffer = await compileToBytecode(bytecodeBuild, isPreload ? 'preload' : 'main', _code) const bytecodeBuffer = await compileToBytecode(bytecodeCompiler, isPreload ? 'preload' : 'main', _code)
this.emitFile({ this.emitFile({
type: 'asset', type: 'asset',
fileName: name + 'c', fileName: name + 'c',