diff --git a/src/config.ts b/src/config.ts index 89c257a..33276cb 100644 --- a/src/config.ts +++ b/src/config.ts @@ -7,6 +7,8 @@ import { type UserConfig as ViteConfig, type ConfigEnv, type PluginOption, + type Plugin, + type BuildEnvironmentOptions as ViteBuildOptions, type LogLevel, createLogger, mergeConfig, @@ -28,7 +30,9 @@ import importMetaPlugin from './plugins/importMeta' import esmShimPlugin from './plugins/esmShim' import modulePathPlugin from './plugins/modulePath' import isolateEntriesPlugin from './plugins/isolateEntries' -import { isObject, isFilePathESM, deepClone } from './utils' +import { type ExternalOptions, externalizeDepsPlugin } from './plugins/externalizeDeps' +import { type BytecodeOptions, bytecodePlugin } from './plugins/bytecode' +import { isObject, isFilePathESM, deepClone, asyncFlatten } from './utils' export { defineConfig as defineViteConfig } from 'vite' @@ -46,11 +50,45 @@ interface IsolatedEntriesOption { isolatedEntries?: boolean } -export interface MainViteConfig extends ViteConfig {} +interface ExternalizeDepsMixin { + /** + * Options pass on to `externalizeDeps` plugin in electron-vite. + * + * Automatically externalize dependencies. + * + * @default true + */ + externalizeDeps?: boolean | ExternalOptions +} -export interface PreloadViteConfig extends ViteConfig, IsolatedEntriesOption {} +interface BytecodeMixin { + /** + * Options pass on to `bytecode` plugin in electron-vite. + * https://electron-vite.org/guide/source-code-protection#bytecodeplugin-options + * + * Compile source code to v8 bytecode. + */ + bytecode?: boolean | BytecodeOptions +} -export interface RendererViteConfig extends ViteConfig, IsolatedEntriesOption {} +interface MainBuildOptions extends ViteBuildOptions, ExternalizeDepsMixin, BytecodeMixin {} + +interface PreloadBuildOptions extends ViteBuildOptions, ExternalizeDepsMixin, BytecodeMixin {} + +interface RendererBuildOptions extends ViteBuildOptions {} + +interface BaseViteConfig extends Omit { + /** + * Build specific options + */ + build?: T +} + +export interface MainViteConfig extends BaseViteConfig {} + +export interface PreloadViteConfig extends BaseViteConfig, IsolatedEntriesOption {} + +export interface RendererViteConfig extends BaseViteConfig, IsolatedEntriesOption {} export interface UserConfig { /** @@ -157,6 +195,8 @@ export async function resolveConfig( resetOutDir(mainViteConfig, outDir, 'main') } + const configDrivenPlugins: PluginOption[] = await resolveConfigDrivenPlugins(mainViteConfig) + const builtInMainPlugins: PluginOption[] = [ electronMainConfigPresetPlugin({ root }), electronMainConfigValidatorPlugin(), @@ -165,13 +205,20 @@ export async function resolveConfig( modulePathPlugin( mergeConfig( { - plugins: [electronMainConfigPresetPlugin({ root }), assetPlugin(), importMetaPlugin(), esmShimPlugin()] + plugins: [ + electronMainConfigPresetPlugin({ root }), + assetPlugin(), + importMetaPlugin(), + esmShimPlugin(), + ...configDrivenPlugins + ] }, mainViteConfig ) ), importMetaPlugin(), - esmShimPlugin() + esmShimPlugin(), + ...configDrivenPlugins ] mainViteConfig.plugins = builtInMainPlugins.concat(mainViteConfig.plugins || []) @@ -188,12 +235,15 @@ export async function resolveConfig( resetOutDir(preloadViteConfig, outDir, 'preload') } + const configDrivenPlugins: PluginOption[] = await resolveConfigDrivenPlugins(preloadViteConfig) + const builtInPreloadPlugins: PluginOption[] = [ electronPreloadConfigPresetPlugin({ root }), electronPreloadConfigValidatorPlugin(), assetPlugin(), importMetaPlugin(), - esmShimPlugin() + esmShimPlugin(), + ...configDrivenPlugins ] if (preloadViteConfig.isolatedEntries) { @@ -205,7 +255,8 @@ export async function resolveConfig( electronPreloadConfigPresetPlugin({ root }), assetPlugin(), importMetaPlugin(), - esmShimPlugin() + esmShimPlugin(), + ...configDrivenPlugins ] }, preloadViteConfig @@ -278,6 +329,38 @@ function resetOutDir(config: ViteConfig, outDir: string, subOutDir: string): voi } } +async function resolveConfigDrivenPlugins(config: MainViteConfig | PreloadViteConfig): Promise { + const userPlugins = (await asyncFlatten(config.plugins || [])).filter(Boolean) as Plugin[] + + const configDrivenPlugins: PluginOption[] = [] + + const hasExternalizeDepsPlugin = userPlugins.some(p => p.name === 'vite:externalize-deps') + if (!hasExternalizeDepsPlugin) { + const externalOptions = config.build?.externalizeDeps ?? true + if (externalOptions) { + isOptions(externalOptions) + ? configDrivenPlugins.push(externalizeDepsPlugin(externalOptions)) + : configDrivenPlugins.push(externalizeDepsPlugin()) + } + } + + const hasBytecodePlugin = userPlugins.some(p => p.name === 'vite:bytecode') + if (!hasBytecodePlugin) { + const bytecodeOptions = config.build?.bytecode + if (bytecodeOptions) { + isOptions(bytecodeOptions) + ? configDrivenPlugins.push(bytecodePlugin(bytecodeOptions)) + : configDrivenPlugins.push(bytecodePlugin()) + } + } + + return configDrivenPlugins +} + +function isOptions(value: boolean | T): value is T { + return typeof value === 'object' && value !== null +} + const CONFIG_FILE_NAME = 'electron.vite.config' export async function loadConfigFromFile( diff --git a/src/utils.ts b/src/utils.ts index 75cf883..a87a0c6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -111,3 +111,12 @@ export function deepClone(value: T): DeepWritable { } return value as DeepWritable } + +type AsyncFlatten = T extends (infer U)[] ? Exclude, U[]>[] : never + +export async function asyncFlatten(arr: T): Promise> { + do { + arr = (await Promise.all(arr)).flat(Infinity) as any + } while (arr.some((v: any) => v?.then)) + return arr as unknown[] as AsyncFlatten +}