electron-vite
Version:
Electron build tooling based on Vite
1,800 lines • 72.4 kB
JavaScript
import path from 'node:path';
import fs from 'node:fs';
import { pathToFileURL } from 'node:url';
import { createRequire, builtinModules } from 'node:module';
import colors from 'picocolors';
import { loadEnv as loadEnv$1, mergeConfig, normalizePath, build, createLogger } from 'vite';
import { build as build$1 } from 'esbuild';
import { spawn } from 'node:child_process';
import { createHash } from 'node:crypto';
import fs$1 from 'node:fs/promises';
import MagicString from 'magic-string';
import * as babel from '@babel/core';
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-function-type */
function isObject(value) {
return Object.prototype.toString.call(value) === '[object Object]';
}
const wildcardHosts = new Set(['0.0.0.0', '::', '0000:0000:0000:0000:0000:0000:0000:0000']);
function resolveHostname(optionsHost) {
return typeof optionsHost === 'string' && !wildcardHosts.has(optionsHost) ? optionsHost : 'localhost';
}
const queryRE = /\?.*$/s;
const hashRE = /#.*$/s;
const cleanUrl = (url) => url.replace(hashRE, '').replace(queryRE, '');
function getHash(text) {
return createHash('sha256')
.update(text)
.digest('hex')
.substring(0, 8);
}
function toRelativePath(filename, importer) {
const relPath = path.posix.relative(path.dirname(importer), filename);
return relPath.startsWith('.') ? relPath : `./${relPath}`;
}
/**
* Load `.env` files within the `envDir` (default: `process.cwd()`) .
* By default, only env variables prefixed with `VITE_`, `MAIN_VITE_`, `PRELOAD_VITE_` and
* `RENDERER_VITE_` are loaded, unless `prefixes` is changed.
*/
function loadEnv(mode, envDir = process.cwd(), prefixes = ['VITE_', 'MAIN_VITE_', 'PRELOAD_VITE_', 'RENDERER_VITE_']) {
return loadEnv$1(mode, envDir, prefixes);
}
let packageCached = null;
function loadPackageData(root = process.cwd()) {
if (packageCached)
return packageCached;
const pkg = path.join(root, 'package.json');
if (fs.existsSync(pkg)) {
const _require = createRequire(import.meta.url);
const data = _require(pkg);
packageCached = {
main: data.main,
type: data.type,
dependencies: data.dependencies
};
return packageCached;
}
return null;
}
function isFilePathESM(filePath) {
if (/\.m[jt]s$/.test(filePath) || filePath.endsWith('.ts')) {
return true;
}
else if (/\.c[jt]s$/.test(filePath)) {
return false;
}
else {
const pkg = loadPackageData();
return pkg?.type === 'module';
}
}
function deepClone(value) {
if (Array.isArray(value)) {
return value.map(v => deepClone(v));
}
if (isObject(value)) {
const cloned = {};
for (const key in value) {
cloned[key] = deepClone(value[key]);
}
return cloned;
}
if (typeof value === 'function') {
return value;
}
if (value instanceof RegExp) {
return new RegExp(value);
}
if (typeof value === 'object' && value != null) {
throw new Error('Cannot deep clone non-plain object');
}
return value;
}
async function asyncFlatten(arr) {
do {
arr = (await Promise.all(arr)).flat(Infinity);
} while (arr.some((v) => v?.then));
return arr;
}
const _require$2 = createRequire(import.meta.url);
const ensureElectronEntryFile = (root = process.cwd()) => {
if (process.env.ELECTRON_ENTRY)
return;
const pkg = loadPackageData();
if (pkg) {
if (!pkg.main) {
throw new Error('No entry point found for electron app, please add a "main" field to package.json');
}
else {
const entryPath = path.resolve(root, pkg.main);
if (!fs.existsSync(entryPath)) {
throw new Error(`No electron app entry file found: ${entryPath}`);
}
}
}
else {
throw new Error('Not found: package.json');
}
};
const getElectronMajorVer = () => {
let majorVer = process.env.ELECTRON_MAJOR_VER || '';
if (!majorVer) {
const pkg = _require$2.resolve('electron/package.json');
if (fs.existsSync(pkg)) {
const version = _require$2(pkg).version;
majorVer = version.split('.')[0];
process.env.ELECTRON_MAJOR_VER = majorVer;
}
}
return majorVer;
};
function supportESM() {
const majorVer = getElectronMajorVer();
return parseInt(majorVer) >= 28;
}
function supportImportMetaPaths() {
const majorVer = getElectronMajorVer();
return parseInt(majorVer) >= 30;
}
function getElectronPath() {
let electronExecPath = process.env.ELECTRON_EXEC_PATH || '';
if (!electronExecPath) {
const electronModulePath = path.dirname(_require$2.resolve('electron'));
const pathFile = path.join(electronModulePath, 'path.txt');
let executablePath;
if (fs.existsSync(pathFile)) {
executablePath = fs.readFileSync(pathFile, 'utf-8');
}
if (executablePath) {
electronExecPath = path.join(electronModulePath, 'dist', executablePath);
process.env.ELECTRON_EXEC_PATH = electronExecPath;
}
else {
throw new Error('Electron uninstall');
}
}
return electronExecPath;
}
function getElectronNodeTarget() {
const electronVer = getElectronMajorVer();
const nodeVer = {
'39': '22.20',
'38': '22.19',
'37': '22.16',
'36': '22.14',
'35': '22.14',
'34': '20.18',
'33': '20.18',
'32': '20.16',
'31': '20.14',
'30': '20.11',
'29': '20.9',
'28': '18.18',
'27': '18.17',
'26': '18.16',
'25': '18.15',
'24': '18.14',
'23': '18.12',
'22': '16.17'
};
if (electronVer && parseInt(electronVer) > 10) {
let target = nodeVer[electronVer];
if (!target)
target = Object.values(nodeVer).reverse()[0];
return 'node' + target;
}
return '';
}
function getElectronChromeTarget() {
const electronVer = getElectronMajorVer();
const chromeVer = {
'39': '142',
'38': '140',
'37': '138',
'36': '136',
'35': '134',
'34': '132',
'33': '130',
'32': '128',
'31': '126',
'30': '124',
'29': '122',
'28': '120',
'27': '118',
'26': '116',
'25': '114',
'24': '112',
'23': '110',
'22': '108'
};
if (electronVer && parseInt(electronVer) > 10) {
let target = chromeVer[electronVer];
if (!target)
target = Object.values(chromeVer).reverse()[0];
return 'chrome' + target;
}
return '';
}
function startElectron(root) {
ensureElectronEntryFile(root);
const electronPath = getElectronPath();
const isDev = process.env.NODE_ENV_ELECTRON_VITE === 'development';
const args = process.env.ELECTRON_CLI_ARGS ? JSON.parse(process.env.ELECTRON_CLI_ARGS) : [];
if (!!process.env.REMOTE_DEBUGGING_PORT && isDev) {
args.push(`--remote-debugging-port=${process.env.REMOTE_DEBUGGING_PORT}`);
}
if (!!process.env.V8_INSPECTOR_PORT && isDev) {
args.push(`--inspect=${process.env.V8_INSPECTOR_PORT}`);
}
if (!!process.env.V8_INSPECTOR_BRK_PORT && isDev) {
args.push(`--inspect-brk=${process.env.V8_INSPECTOR_BRK_PORT}`);
}
if (process.env.NO_SANDBOX === '1') {
args.push('--no-sandbox');
}
const entry = process.env.ELECTRON_ENTRY || '.';
const ps = spawn(electronPath, [entry].concat(args), { stdio: 'inherit' });
ps.on('close', process.exit);
return ps;
}
function findLibEntry(root, scope) {
for (const name of ['index', scope]) {
for (const ext of ['js', 'ts', 'mjs', 'cjs']) {
const entryFile = path.resolve(root, 'src', scope, `${name}.${ext}`);
if (fs.existsSync(entryFile)) {
return entryFile;
}
}
}
return undefined;
}
function findInput(root, scope = 'renderer') {
const rendererDir = path.resolve(root, 'src', scope, 'index.html');
if (fs.existsSync(rendererDir)) {
return rendererDir;
}
return '';
}
function processEnvDefine() {
return {
'process.env': `process.env`,
'global.process.env': `global.process.env`,
'globalThis.process.env': `globalThis.process.env`
};
}
function resolveBuildOutputs$1(outputs, libOptions) {
if (libOptions && !Array.isArray(outputs)) {
const libFormats = libOptions.formats || [];
return libFormats.map(format => ({ ...outputs, format }));
}
return outputs;
}
function electronMainConfigPresetPlugin(options) {
return {
name: 'vite:electron-main-config-preset',
apply: 'build',
enforce: 'pre',
config(config) {
const root = options?.root || process.cwd();
const nodeTarget = getElectronNodeTarget();
const pkg = loadPackageData() || { type: 'commonjs' };
const format = pkg.type && pkg.type === 'module' && supportESM() ? 'es' : 'cjs';
const defaultConfig = {
resolve: {
browserField: false,
mainFields: ['module', 'jsnext:main', 'jsnext'],
conditions: ['node']
},
build: {
outDir: path.resolve(root, 'out', 'main'),
target: nodeTarget,
assetsDir: 'chunks',
rollupOptions: {
external: ['electron', /^electron\/.+/, ...builtinModules.flatMap(m => [m, `node:${m}`])],
output: {}
},
reportCompressedSize: false,
minify: false
}
};
const build = config.build || {};
const rollupOptions = build.rollupOptions || {};
if (!rollupOptions.input) {
const libOptions = build.lib;
const outputOptions = rollupOptions.output;
defaultConfig.build['lib'] = {
entry: findLibEntry(root, 'main'),
formats: libOptions && libOptions.formats && libOptions.formats.length > 0
? []
: [outputOptions && !Array.isArray(outputOptions) && outputOptions.format ? outputOptions.format : format]
};
}
else {
defaultConfig.build.rollupOptions.output['format'] = format;
}
defaultConfig.build.rollupOptions.output['assetFileNames'] = path.posix.join(build.assetsDir || defaultConfig.build.assetsDir, '[name]-[hash].[ext]');
const buildConfig = mergeConfig(defaultConfig.build, build);
config.build = buildConfig;
config.resolve = mergeConfig(defaultConfig.resolve, config.resolve || {});
config.define = config.define || {};
config.define = { ...processEnvDefine(), ...config.define };
config.envPrefix = config.envPrefix || ['MAIN_VITE_', 'VITE_'];
config.publicDir = config.publicDir || 'resources';
// do not copy public dir
config.build.copyPublicDir = false;
// module preload polyfill does not apply to nodejs (main process)
config.build.modulePreload = false;
// enable ssr build
config.build.ssr = true;
config.build.ssrEmitAssets = true;
config.ssr = { ...config.ssr, ...{ noExternal: true } };
}
};
}
function electronMainConfigValidatorPlugin() {
return {
name: 'vite:electron-main-config-validator',
apply: 'build',
enforce: 'post',
configResolved(config) {
const build = config.build;
if (!build.target) {
throw new Error('build.target option is required in the electron vite main config.');
}
else {
const targets = Array.isArray(build.target) ? build.target : [build.target];
if (targets.some(t => !t.startsWith('node'))) {
throw new Error('The electron vite main config build.target option must be "node?".');
}
}
const libOptions = build.lib;
const rollupOptions = build.rollupOptions;
if (!(libOptions && libOptions.entry) && !rollupOptions?.input) {
throw new Error('An entry point is required in the electron vite main config, ' +
'which can be specified using "build.lib.entry" or "build.rollupOptions.input".');
}
const resolvedOutputs = resolveBuildOutputs$1(rollupOptions.output, libOptions);
if (resolvedOutputs) {
const outputs = Array.isArray(resolvedOutputs) ? resolvedOutputs : [resolvedOutputs];
if (outputs.length > 1) {
throw new Error('The electron vite main config does not support multiple outputs.');
}
else {
const outpout = outputs[0];
if (['es', 'cjs'].includes(outpout.format || '')) {
if (outpout.format === 'es' && !supportESM()) {
throw new Error('The electron vite main config output format does not support "es", ' +
'you can upgrade electron to the latest version or switch to "cjs" format.');
}
}
else {
throw new Error(`The electron vite main config output format must be "cjs"${supportESM() ? ' or "es"' : ''}.`);
}
}
}
}
};
}
function electronPreloadConfigPresetPlugin(options) {
return {
name: 'vite:electron-preload-config-preset',
apply: 'build',
enforce: 'pre',
config(config) {
const root = options?.root || process.cwd();
const nodeTarget = getElectronNodeTarget();
const pkg = loadPackageData() || { type: 'commonjs' };
const format = pkg.type && pkg.type === 'module' && supportESM() ? 'es' : 'cjs';
const defaultConfig = {
ssr: {
resolve: {
conditions: ['module', 'browser', 'development|production'],
mainFields: ['browser', 'module', 'jsnext:main', 'jsnext']
}
},
build: {
outDir: path.resolve(root, 'out', 'preload'),
target: nodeTarget,
assetsDir: 'chunks',
rollupOptions: {
external: ['electron', /^electron\/.+/, ...builtinModules.flatMap(m => [m, `node:${m}`])],
output: {}
},
reportCompressedSize: false,
minify: false
}
};
const build = config.build || {};
const rollupOptions = build.rollupOptions || {};
if (!rollupOptions.input) {
const libOptions = build.lib;
const outputOptions = rollupOptions.output;
defaultConfig.build['lib'] = {
entry: findLibEntry(root, 'preload'),
formats: libOptions && libOptions.formats && libOptions.formats.length > 0
? []
: [outputOptions && !Array.isArray(outputOptions) && outputOptions.format ? outputOptions.format : format]
};
}
else {
defaultConfig.build.rollupOptions.output['format'] = format;
}
defaultConfig.build.rollupOptions.output['assetFileNames'] = path.posix.join(build.assetsDir || defaultConfig.build.assetsDir, '[name]-[hash].[ext]');
const buildConfig = mergeConfig(defaultConfig.build, build);
config.build = buildConfig;
const resolvedOutputs = resolveBuildOutputs$1(config.build.rollupOptions.output, config.build.lib || false);
if (resolvedOutputs) {
const outputs = Array.isArray(resolvedOutputs) ? resolvedOutputs : [resolvedOutputs];
if (outputs.find(({ format }) => format === 'es')) {
if (Array.isArray(config.build.rollupOptions.output)) {
config.build.rollupOptions.output.forEach(output => {
if (output.format === 'es') {
output['entryFileNames'] = '[name].mjs';
output['chunkFileNames'] = '[name]-[hash].mjs';
}
});
}
else {
config.build.rollupOptions.output['entryFileNames'] = '[name].mjs';
config.build.rollupOptions.output['chunkFileNames'] = '[name]-[hash].mjs';
}
}
}
config.define = config.define || {};
config.define = { ...processEnvDefine(), ...config.define };
config.envPrefix = config.envPrefix || ['PRELOAD_VITE_', 'VITE_'];
config.publicDir = config.publicDir || 'resources';
// do not copy public dir
config.build.copyPublicDir = false;
// module preload polyfill does not apply to nodejs (preload scripts)
config.build.modulePreload = false;
// enable ssr build
config.build.ssr = true;
config.build.ssrEmitAssets = true;
config.ssr = mergeConfig(defaultConfig.ssr, config.ssr || {});
config.ssr.noExternal = true;
}
};
}
function electronPreloadConfigValidatorPlugin() {
return {
name: 'vite:electron-preload-config-validator',
apply: 'build',
enforce: 'post',
configResolved(config) {
const build = config.build;
if (!build.target) {
throw new Error('build.target option is required in the electron vite preload config.');
}
else {
const targets = Array.isArray(build.target) ? build.target : [build.target];
if (targets.some(t => !t.startsWith('node'))) {
throw new Error('The electron vite preload config build.target must be "node?".');
}
}
const libOptions = build.lib;
const rollupOptions = build.rollupOptions;
if (!(libOptions && libOptions.entry) && !rollupOptions?.input) {
throw new Error('An entry point is required in the electron vite preload config, ' +
'which can be specified using "build.lib.entry" or "build.rollupOptions.input".');
}
const resolvedOutputs = resolveBuildOutputs$1(rollupOptions.output, libOptions);
if (resolvedOutputs) {
const outputs = Array.isArray(resolvedOutputs) ? resolvedOutputs : [resolvedOutputs];
if (outputs.length > 1) {
throw new Error('The electron vite preload config does not support multiple outputs.');
}
else {
const outpout = outputs[0];
if (['es', 'cjs'].includes(outpout.format || '')) {
if (outpout.format === 'es' && !supportESM()) {
throw new Error('The electron vite preload config output format does not support "es", ' +
'you can upgrade electron to the latest version or switch to "cjs" format.');
}
}
else {
throw new Error(`The electron vite preload config output format must be "cjs"${supportESM() ? ' or "es"' : ''}.`);
}
}
}
}
};
}
function electronRendererConfigPresetPlugin(options) {
return {
name: 'vite:electron-renderer-config-preset',
enforce: 'pre',
config(config) {
const root = options?.root || process.cwd();
config.base =
config.mode === 'production' || process.env.NODE_ENV_ELECTRON_VITE === 'production' ? './' : config.base;
config.root = config.root || './src/renderer';
const chromeTarget = getElectronChromeTarget();
const emptyOutDir = () => {
let outDir = config.build?.outDir;
if (outDir) {
if (!path.isAbsolute(outDir)) {
outDir = path.resolve(root, outDir);
}
const resolvedRoot = normalizePath(path.resolve(root));
return normalizePath(outDir).startsWith(resolvedRoot + '/');
}
return true;
};
const defaultConfig = {
build: {
outDir: path.resolve(root, 'out', 'renderer'),
target: chromeTarget,
modulePreload: { polyfill: false },
rollupOptions: {
input: findInput(root)
},
reportCompressedSize: false,
minify: false,
emptyOutDir: emptyOutDir()
}
};
if (config.build?.outDir) {
config.build.outDir = path.resolve(root, config.build.outDir);
}
const buildConfig = mergeConfig(defaultConfig.build, config.build || {});
config.build = buildConfig;
config.envDir = config.envDir || path.resolve(root);
config.envPrefix = config.envPrefix || ['RENDERER_VITE_', 'VITE_'];
}
};
}
function electronRendererConfigValidatorPlugin() {
return {
name: 'vite:electron-renderer-config-validator',
enforce: 'post',
configResolved(config) {
if (config.base !== './' && config.base !== '/') {
config.logger.warn(colors.yellow('(!) Should not set "base" option for the electron vite renderer config.'));
}
const build = config.build;
if (!build.target) {
throw new Error('build.target option is required in the electron vite renderer config.');
}
else {
const targets = Array.isArray(build.target) ? build.target : [build.target];
if (targets.some(t => !t.startsWith('chrome') && !/^es((202\d{1})|next)$/.test(t))) {
config.logger.warn('The electron vite renderer config build.target is not "chrome?" or "es?". This could be a mistake.');
}
}
const rollupOptions = build.rollupOptions;
if (!rollupOptions.input) {
config.logger.warn(colors.yellow(`index.html file is not found in ${colors.dim('/src/renderer')} directory.`));
throw new Error('build.rollupOptions.input option is required in the electron vite renderer config.');
}
}
};
}
const nodeAssetRE = /__VITE_NODE_ASSET__([\w$]+)__/g;
const nodePublicAssetRE = /__VITE_NODE_PUBLIC_ASSET__([a-z\d]{8})__/g;
const assetImportRE = /(?:[?|&]asset(?:&|$)|\.wasm\?loader$|\.node$)/;
const assetRE = /[?|&]asset(?:&|$)/;
const assetUnpackRE = /[?|&]asset&asarUnpack$/;
const wasmHelperId = '\0__electron-vite-wasm-helper';
const wasmHelperCode = `
import { join } from 'path'
import { readFile } from 'fs/promises'
export default async function loadWasm(file, importObject = {}) {
const wasmBuffer = await readFile(join(__dirname, file))
const result = await WebAssembly.instantiate(wasmBuffer, importObject)
return result.instance
}
`;
function assetPlugin() {
let publicDir = '';
const publicAssetPathCache = new Map();
const assetCache = new Map();
const isImportMetaPathSupported = supportImportMetaPaths();
return {
name: 'vite:node-asset',
apply: 'build',
enforce: 'pre',
buildStart() {
publicAssetPathCache.clear();
assetCache.clear();
},
configResolved(config) {
publicDir = config.publicDir;
},
resolveId(id) {
if (id === wasmHelperId) {
return id;
}
},
async load(id) {
if (id === wasmHelperId) {
return wasmHelperCode;
}
if (id.startsWith('\0') || !assetImportRE.test(id)) {
return;
}
let referenceId;
const file = cleanUrl(id);
if (publicDir && file.startsWith(publicDir)) {
const hash = getHash(file);
if (!publicAssetPathCache.get(hash)) {
publicAssetPathCache.set(hash, file);
}
referenceId = `__VITE_NODE_PUBLIC_ASSET__${hash}__`;
}
else {
const cached = assetCache.get(file);
if (cached) {
referenceId = cached;
}
else {
const source = await fs$1.readFile(file);
const hash = this.emitFile({
type: 'asset',
name: path.basename(file),
source: source
});
referenceId = `__VITE_NODE_ASSET__${hash}__`;
assetCache.set(file, referenceId);
}
}
if (assetRE.test(id)) {
const dirnameExpr = isImportMetaPathSupported ? 'import.meta.dirname' : '__dirname';
if (assetUnpackRE.test(id)) {
return `
import { join } from 'path'
export default join(${dirnameExpr}, ${referenceId}).replace('app.asar', 'app.asar.unpacked')`;
}
else {
return `
import { join } from 'path'
export default join(${dirnameExpr}, ${referenceId})`;
}
}
if (id.endsWith('.node')) {
return `export default require(${referenceId})`;
}
if (id.endsWith('.wasm?loader')) {
return `
import loadWasm from ${JSON.stringify(wasmHelperId)}
export default importObject => loadWasm(${referenceId}, importObject)`;
}
},
renderChunk(code, chunk, { sourcemap, dir }) {
let match;
let s;
nodeAssetRE.lastIndex = 0;
while ((match = nodeAssetRE.exec(code))) {
s ||= new MagicString(code);
const [full, hash] = match;
const filename = this.getFileName(hash);
const outputFilepath = toRelativePath(filename, chunk.fileName);
const replacement = JSON.stringify(outputFilepath);
s.overwrite(match.index, match.index + full.length, replacement, {
contentOnly: true
});
}
nodePublicAssetRE.lastIndex = 0;
while ((match = nodePublicAssetRE.exec(code))) {
s ||= new MagicString(code);
const [full, hash] = match;
const filename = publicAssetPathCache.get(hash);
const outputFilepath = toRelativePath(filename, normalizePath(path.join(dir, chunk.fileName)));
const replacement = JSON.stringify(outputFilepath);
s.overwrite(match.index, match.index + full.length, replacement, {
contentOnly: true
});
}
if (s) {
return {
code: s.toString(),
map: sourcemap ? s.generateMap({ hires: 'boundary' }) : null
};
}
return null;
}
};
}
const nodeWorkerAssetUrlRE = /__VITE_NODE_WORKER_ASSET__([\w$]+)__/g;
const nodeWorkerRE = /\?nodeWorker(?:&|$)/;
const nodeWorkerImporterRE = /(?:\?)nodeWorker&importer=([^&]+)(?:&|$)/;
/**
* Resolve `?nodeWorker` import and automatically generate `Worker` wrapper.
*/
function workerPlugin() {
return {
name: 'vite:node-worker',
apply: 'build',
enforce: 'pre',
resolveId(id, importer) {
if (id.endsWith('?nodeWorker')) {
return id + `&importer=${importer}`;
}
},
load(id) {
if (nodeWorkerRE.test(id)) {
const match = nodeWorkerImporterRE.exec(id);
if (match) {
const hash = this.emitFile({
type: 'chunk',
id: cleanUrl(id),
importer: match[1]
});
const assetRefId = `__VITE_NODE_WORKER_ASSET__${hash}__`;
return `
import { Worker } from 'node:worker_threads';
export default function (options) { return new Worker(new URL(${assetRefId}, import.meta.url), options); }`;
}
}
},
renderChunk(code, chunk, { sourcemap }) {
let match;
let s;
nodeWorkerAssetUrlRE.lastIndex = 0;
while ((match = nodeWorkerAssetUrlRE.exec(code))) {
s ||= new MagicString(code);
const [full, hash] = match;
const filename = this.getFileName(hash);
const outputFilepath = toRelativePath(filename, chunk.fileName);
const replacement = JSON.stringify(outputFilepath);
s.overwrite(match.index, match.index + full.length, replacement, {
contentOnly: true
});
}
if (s) {
return {
code: s.toString(),
map: sourcemap ? s.generateMap({ hires: 'boundary' }) : null
};
}
return null;
}
};
}
function importMetaPlugin() {
return {
name: 'vite:import-meta',
apply: 'build',
enforce: 'pre',
resolveImportMeta(property, { format }) {
if (property === 'url' && format === 'cjs') {
return `require("url").pathToFileURL(__filename).href`;
}
if (property === 'filename' && format === 'cjs') {
return `__filename`;
}
if (property === 'dirname' && format === 'cjs') {
return `__dirname`;
}
return null;
}
};
}
/*
* The core of this plugin was conceived by pi0 and is taken from the following repository:
* https://github.com/unjs/unbuild/blob/main/src/builder/plugins/cjs.ts
* license: https://github.com/unjs/unbuild/blob/main/LICENSE
*/
const CJSyntaxRe = /__filename|__dirname|require\(|require\.resolve\(/;
const CJSShim_normal = `
// -- CommonJS Shims --
import __cjs_url__ from 'node:url';
import __cjs_path__ from 'node:path';
import __cjs_mod__ from 'node:module';
const __filename = __cjs_url__.fileURLToPath(import.meta.url);
const __dirname = __cjs_path__.dirname(__filename);
const require = __cjs_mod__.createRequire(import.meta.url);
`;
const CJSShim_node_20_11 = `
// -- CommonJS Shims --
import __cjs_mod__ from 'node:module';
const __filename = import.meta.filename;
const __dirname = import.meta.dirname;
const require = __cjs_mod__.createRequire(import.meta.url);
`;
const ESMStaticImportRe = /(?<=\s|^|;)import\s*([\s"']*(?<imports>[\p{L}\p{M}\w\t\n\r $*,/{}@.]+)from\s*)?["']\s*(?<specifier>(?<="\s*)[^"]*[^\s"](?=\s*")|(?<='\s*)[^']*[^\s'](?=\s*'))\s*["'][\s;]*/gmu;
function findStaticImports(code) {
const matches = [];
for (const match of code.matchAll(ESMStaticImportRe)) {
matches.push({ end: (match.index || 0) + match[0].length });
}
return matches;
}
function esmShimPlugin() {
const CJSShim = supportImportMetaPaths() ? CJSShim_node_20_11 : CJSShim_normal;
return {
name: 'vite:esm-shim',
apply: 'build',
enforce: 'post',
renderChunk(code, _chunk, { format, sourcemap }) {
if (format === 'es') {
if (code.includes(CJSShim) || !CJSyntaxRe.test(code)) {
return null;
}
const lastESMImport = findStaticImports(code).pop();
const indexToAppend = lastESMImport ? lastESMImport.end : 0;
const s = new MagicString(code);
s.appendRight(indexToAppend, CJSShim);
return {
code: s.toString(),
map: sourcemap ? s.generateMap({ hires: 'boundary' }) : null
};
}
return null;
}
};
}
function buildReporterPlugin() {
const moduleIds = [];
return {
name: 'vite:build-reporter',
buildEnd() {
const allModuleIds = Array.from(this.getModuleIds());
const sourceFiles = allModuleIds.filter(id => {
if (id.includes('node_modules')) {
return false;
}
const info = this.getModuleInfo(id);
return info && !info.isExternal;
});
moduleIds.push(...sourceFiles);
},
api: {
getWatchFiles() {
return moduleIds;
}
}
};
}
const modulePathRE = /__VITE_MODULE_PATH__([\w$]+)__/g;
/**
* Resolve `?modulePath` import and return the module bundle path.
*/
function modulePathPlugin(config) {
const isImportMetaPathSupported = supportImportMetaPaths();
const assetCache = new Set();
return {
name: 'vite:module-path',
apply: 'build',
enforce: 'pre',
buildStart() {
assetCache.clear();
},
async load(id) {
if (id.endsWith('?modulePath')) {
// id resolved by Vite resolve plugin
const re = await bundleEntryFile$1(cleanUrl(id), config, this.meta.watchMode);
const [outputChunk, ...outputChunks] = re.bundles.output;
const hash = this.emitFile({
type: 'asset',
fileName: outputChunk.fileName,
source: outputChunk.code
});
for (const chunk of outputChunks) {
if (assetCache.has(chunk.fileName)) {
continue;
}
this.emitFile({
type: 'asset',
fileName: chunk.fileName,
source: chunk.type === 'chunk' ? chunk.code : chunk.source
});
assetCache.add(chunk.fileName);
}
for (const id of re.watchFiles) {
this.addWatchFile(id);
}
const refId = `__VITE_MODULE_PATH__${hash}__`;
const dirnameExpr = isImportMetaPathSupported ? 'import.meta.dirname' : '__dirname';
return `
import { join } from 'path'
export default join(${dirnameExpr}, ${refId})`;
}
},
renderChunk(code, chunk, { sourcemap }) {
let match;
let s;
modulePathRE.lastIndex = 0;
while ((match = modulePathRE.exec(code))) {
s ||= new MagicString(code);
const [full, hash] = match;
const filename = this.getFileName(hash);
const outputFilepath = toRelativePath(filename, chunk.fileName);
const replacement = JSON.stringify(outputFilepath);
s.overwrite(match.index, match.index + full.length, replacement, {
contentOnly: true
});
}
if (s) {
return {
code: s.toString(),
map: sourcemap ? s.generateMap({ hires: 'boundary' }) : null
};
}
return null;
}
};
}
async function bundleEntryFile$1(input, config, watch) {
const reporter = watch ? buildReporterPlugin() : undefined;
const viteConfig = mergeConfig(config, {
build: {
write: false,
watch: false
},
plugins: [
{
name: 'vite:entry-file-name',
outputOptions(output) {
if (typeof output.entryFileNames !== 'function' && output.entryFileNames) {
output.entryFileNames = '[name]-[hash]' + path.extname(output.entryFileNames);
}
return output;
}
},
reporter
],
logLevel: 'warn',
configFile: false
});
// rewrite the input instead of merging
const buildOptions = viteConfig.build;
buildOptions.rollupOptions = {
...buildOptions.rollupOptions,
input
};
const bundles = await build(viteConfig);
return {
bundles: bundles,
watchFiles: reporter?.api?.getWatchFiles() || []
};
}
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-function-type */
const VIRTUAL_ENTRY_ID = '\0virtual:isolate-entries';
const LogLevels = {
silent: 0,
error: 1,
warn: 2,
info: 3
};
function isolateEntriesPlugin(userConfig) {
let logger;
let entries;
let transformedCount = 0;
const assetCache = new Set();
return {
name: 'vite:isolate-entries',
apply: 'build',
configResolved(config) {
logger = config.logger;
},
options(opts) {
const { input } = opts;
if (input && typeof input === 'object') {
if ((Array.isArray(input) && input.length > 0) || Object.keys(input).length > 1) {
opts.input = VIRTUAL_ENTRY_ID;
entries = Array.isArray(input) ? input : Object.entries(input).map(([key, value]) => ({ [key]: value }));
return opts;
}
}
},
buildStart() {
transformedCount = 0;
assetCache.clear();
},
resolveId(id) {
if (id === VIRTUAL_ENTRY_ID) {
return id;
}
return null;
},
async load(id) {
if (id === VIRTUAL_ENTRY_ID) {
const shouldLog = LogLevels[userConfig.logLevel || 'info'] >= LogLevels.info;
const shouldWatch = this.meta.watchMode;
const watchFiles = new Set();
for (const entry of entries) {
const re = await bundleEntryFile(entry, userConfig, shouldWatch, shouldLog, transformedCount);
const outputChunks = re.bundles.output;
for (const chunk of outputChunks) {
if (assetCache.has(chunk.fileName)) {
continue;
}
this.emitFile({
type: 'asset',
fileName: chunk.fileName,
source: chunk.type === 'chunk' ? chunk.code : chunk.source
});
assetCache.add(chunk.fileName);
}
for (const id of re.watchFiles) {
watchFiles.add(id);
}
transformedCount += re.transformedCount;
}
for (const id of watchFiles) {
this.addWatchFile(id);
}
return `
// This is the virtual entry file
console.log(1)`;
}
},
renderStart() {
clearLine(-1);
logger.info(`${colors.green(`✓`)} ${transformedCount} modules transformed.`);
},
generateBundle(_, bundle) {
for (const chunkName in bundle) {
if (chunkName.includes('virtual_isolate-entries')) {
delete bundle[chunkName];
}
}
}
};
}
async function bundleEntryFile(input, config, watch, shouldLog, preTransformedCount) {
const transformReporter = transformReporterPlugin(preTransformedCount, shouldLog);
const buildReporter = watch ? buildReporterPlugin() : undefined;
const viteConfig = mergeConfig(config, {
build: {
write: false,
watch: false
},
plugins: [transformReporter, buildReporter],
logLevel: 'warn',
configFile: false
});
// rewrite the input instead of merging
viteConfig.build.rollupOptions.input = input;
const bundles = await build(viteConfig);
return {
bundles: bundles,
watchFiles: buildReporter?.api?.getWatchFiles() || [],
transformedCount: transformReporter?.api?.getTransformedCount() || 0
};
}
function transformReporterPlugin(preTransformedCount = 0, shouldLog = true) {
let transformedCount = 0;
let root;
const log = throttle(id => {
writeLine(`transforming (${preTransformedCount + transformedCount}) ${colors.dim(path.relative(root, id))}`);
});
return {
name: 'vite:transform-reporter',
configResolved(config) {
root = config.root;
},
transform(_, id) {
transformedCount++;
if (!shouldLog)
return;
if (id.includes('?'))
return;
log(id);
},
api: {
getTransformedCount() {
return transformedCount;
}
}
};
}
function writeLine(output) {
clearLine();
if (output.length < process.stdout.columns) {
process.stdout.write(output);
}
else {
process.stdout.write(output.substring(0, process.stdout.columns - 1));
}
}
function clearLine(move = 0) {
if (move < 0) {
process.stdout.moveCursor(0, move);
}
process.stdout.clearLine(0);
process.stdout.cursorTo(0);
}
function throttle(fn) {
let timerHandle = null;
return (...args) => {
if (timerHandle)
return;
fn(...args);
timerHandle = setTimeout(() => {
timerHandle = null;
}, 100);
};
}
/**
* Automatically externalize dependencies.
*
* @deprecated use `build.externalizeDeps` config option instead
*/
function externalizeDepsPlugin(options = {}) {
const { exclude = [], include = [] } = options;
const pkg = loadPackageData() || {};
let deps = Object.keys(pkg.dependencies || {});
if (include.length) {
deps = deps.concat(include.filter(dep => dep.trim() !== ''));
}
if (exclude.length) {
deps = deps.filter(dep => !exclude.includes(dep));
}
deps = [...new Set(deps)];
return {
name: 'vite:externalize-deps',
enforce: 'pre',
config(config) {
const defaultConfig = {
build: {
rollupOptions: {
external: deps.length > 0 ? [...deps, new RegExp(`^(${deps.join('|')})/.+`)] : []
}
}
};
const buildConfig = mergeConfig(defaultConfig.build, config.build || {});
config.build = buildConfig;
}
};
}
// Inspired by https://github.com/bytenode/bytenode
const _require$1 = createRequire(import.meta.url);
function getBytecodeCompilerPath() {
return path.join(path.dirname(_require$1.resolve('electron-vite/package.json')), 'bin', 'electron-bytecode.cjs');
}
function compileToBytecode(code) {
return new Promise((resolve, reject) => {
let data = Buffer.from([]);
const electronPath = getElectronPath();
const bytecodePath = getBytecodeCompilerPath();
const proc = spawn(electronPath, [bytecodePath], {
env: { ELECTRON_RUN_AS_NODE: '1' },
stdio: ['pipe', 'pipe', 'pipe', 'ipc']
});
if (proc.stdin) {
proc.stdin.write(code);
proc.stdin.end();
}
if (proc.stdout) {
proc.stdout.on('data', chunk => {
data = Buffer.concat([data, chunk]);
});
proc.stdout.on('error', err => {
console.error(err);
});
proc.stdout.on('end', () => {
resolve(data);
});
}
if (proc.stderr) {
proc.stderr.on('data', chunk => {
console.error('Error: ', chunk.toString());
});
proc.stderr.on('error', err => {
console.error('Error: ', err);
});
}
proc.addListener('message', message => console.log(message));
proc.addListener('error', err => console.error(err));
proc.on('error', err => reject(err));
proc.on('exit', () => {
resolve(data);
});
});
}
const bytecodeModuleLoaderCode = [
`"use strict";`,
`const fs = require("fs");`,
`const path = require("path");`,
`const vm = require("vm");`,
`const v8 = require("v8");`,
`const Module = require("module");`,
`v8.setFlagsFromString("--no-lazy");`,
`v8.setFlagsFromString("--no-flush-bytecode");`,
`const FLAG_HASH_OFFSET = 12;`,
`const SOURCE_HASH_OFFSET = 8;`,
`let dummyBytecode;`,
`function setFlagHashHeader(bytecodeBuffer) {`,
` if (!dummyBytecode) {`,
` const script = new vm.Script("", {`,
` produceCachedData: true`,
` });`,
` dummyBytecode = script.createCachedData();`,
` }`,
` dummyBytecode.slice(FLAG_HASH_OFFSET, FLAG_HASH_OFFSET + 4).copy(bytecodeBuffer, FLAG_HASH_OFFSET);`,
`};`,
`function getSourceHashHeader(bytecodeBuffer) {`,
` return bytecodeBuffer.slice(SOURCE_HASH_OFFSET, SOURCE_HASH_OFFSET + 4);`,
`};`,
`function buffer2Number(buffer) {`,
` let ret = 0;`,
` ret |= buffer[3] << 24;`,
` ret |= buffer[2] << 16;`,
` ret |= buffer[1] << 8;`,
` ret |= buffer[0];`,
` return ret;`,
`};`,
`Module._extensions[".jsc"] = Module._extensions[".cjsc"] = function (module, filename) {`,
` const bytecodeBuffer = fs.readFileSync(filename);`,
` if (!Buffer.isBuffer(bytecodeBuffer)) {`,
` throw new Error("BytecodeBuffer must be a buffer object.");`,
` }`,
` setFlagHashHeader(bytecodeBuffer);`,
` const length = buffer2Number(getSourceHashHeader(bytecodeBuffer));`,
` let dummyCode = "";`,
` if (length > 1) {`,
` dummyCode = "\\"" + "\\u200b".repeat(length - 2) + "\\"";`,
` }`,
` const script = new vm.Script(dummyCode, {`,
` filename: filename,`,
` lineOffset: 0,`,
` displayErrors: true,`,
` cachedData: bytecodeBuffer`,
` });`,
` if (script.cachedDataRejected) {`,
` throw new Error("Invalid or incompatible cached data (cachedDataRejected)");`,
` }`,
` const require = function (id) {`,
` return module.require(id);`,
` };`,
` require.resolve = function (request, options) {`,
` return Module._resolveFilename(request, module, false, options);`,
` };`,
` if (process.mainModule) {`,
` require.main = process.mainModule;`,
` }`,
` require.extensions = Module._extensions;`,
` require.cache = Module._cache;`,
` const compiledWrapper = script.runInThisContext({`,
` filename: filename,`,
` lineOffset: 0,`,
` columnOffset: 0,`,
` displayErrors: true`,
` });`,
` const dirname = path.dirname(filename);`,
` const args = [module.exports, require, module, filename, dirname, process, global];`,
` return compiledWrapper.apply(module.exports, args);`,
`};`
];
const bytecodeChunkExtensionRE = /.(jsc|cjsc)$/;
/**
* Compile source code to v8 bytecode.
*
* @deprecated use `build.bytecode` config option instead
*/
function bytecodePlugin(options = {}) {
if (process.env.NODE_ENV_ELECTRON_VITE !== 'production') {
return null;
}
const { chunkAlias = [], transformArrowFunctions = true, removeBundleJS = true, protectedStrings = [] } = options;
const _chunkAlias = Array.isArray(chunkAlias) ? chunkAlias : [chunkAlias];
const transformAllChunks = _chunkAlias.length === 0;
const isBytecodeChunk = (chunkName) => {
return transformAllChunks || _chunkAlias.some(alias => alias === chunkName);
};
const plugins = [];
if (transformArrowFunctions) {
plugins.push('@babel/plugin-transform-arrow-functions');
}
if (protectedStrings.length > 0) {
plugins.push([protectStringsPlugin, { protectedStrings: new Set(protectedStrings) }]);
}
const shouldTransformBytecodeChunk = plugins.length !== 0;
const _transform = (code, sourceMaps = false) => {
const re = babel.transform(code, { plugins, sourceMaps });
return re ? { code: re.code || '', map: re.map } : null;
};
const useStrict = '"use strict";';
const bytecodeModuleLoader = 'bytecode-loader.cjs';
let logger;
let supported = false;
return {
name: 'vite:bytecode',
apply: 'build',
enforce: 'post',
configResolved(config) {
if (supported) {
return;
}
logger = config.logger;
const useInRenderer = config.plugins.some(p => p.name === 'vite:electron-renderer-preset-config');
if (useInRenderer) {
config.logger.warn(colors.yellow('bytecodePlugin does not support renderer.'));
return;
}
const build = config.build;
const resolvedOutputs = resolveBuildOutputs(build.rollupOptions.output, build.lib);
if (resolvedOutputs) {
const outputs = Array.isArray(resolvedOutputs) ? resolvedOutputs : [resolvedOutputs];
const output = outputs[0];
if (output.format === 'es') {
config.logger.warn(colors.yellow('bytecodePlugin does not support ES module, please remove "type": "module" ' +
'in package.json or set the "build.rollupOptions.output.format" option to "cjs".'));
}
supported = output.format === 'cjs' && !useInRenderer;
}
},
renderChunk(code, chunk, { sourcemap }) {
if (supported && isBytecodeChunk(chunk.name) && shouldTransformBytecodeChunk) {
return _transform(code, !!sourcemap);
}
return null;
},
async generateBundle(_, output) {
if (!supported) {
return;
}
const _chunks = Object.values(output);
const chunks = _chunks.filter(chunk => chunk.type === 'chunk' && isBytecodeChunk(chunk.name));
if (chunks.length === 0) {
return;
}
const bytecodeChunks = chunks.map(chunk => chunk.fileName);
const nonEntryChunks = chunks.filter(chunk => !chunk.isEntry).map(chunk => path.basename(chunk.fileName));
const pattern = nonEntryChunks.map(chunk => `(${chunk})`).join('|');
const bytecodeRE = pattern ? new RegExp(`require\\(\\S*(?=(${pattern})\\S*\\))`, 'g') : null;
const getBytecodeLoaderBlock = (chunkFileName) => {
return `require("${toRelativePath(bytecodeModuleLoader, normalizePath(chunkFileName))}");`;
};
let bytecodeChunkCount = 0;
const bundles = Object.keys(output);
await Promise.all(bundles.map(async (name) => {
const chunk = output[name];
if (chunk.type === 'chunk') {
let _code = chunk.code;
if (bytecodeRE) {
let match;
let s;
while ((match = bytecodeRE.exec(_code))) {
s ||= new MagicString(_code);
const [prefix, chunkName] = match;
const len = prefix.length + chunkName.length;
s.overwrite(match.index, match.index + len, prefix + chunkName + 'c', {
contentOnly: true
});
}
if (s) {
_code = s.toString();
}
}
if (bytecodeChunks.includes(name)) {
const bytecodeBuffer = await compileToBytecode(_code);
this.emitFile({
type: 'asset',
fileName: name + 'c',
source: bytecodeBuffer
});
if (!removeBundleJS) {
this.emitFile({
type: 'asset',
fileName: '_' + chunk.fileName,
source: chunk.code
});
}
if (chunk.isEntry) {
const bytecodeLoaderBlock = getBytecodeLoaderBlock(chunk.fileName);
const bytecodeModuleBlock = `require("./${path.basename(name) + 'c'}");`;
const code = `${useStrict}\n${bytecodeLoaderBlock}\n${bytecodeModuleBlock}\n`;
chunk.code = code;
}
else {
delete output[chunk.fileName];
}
bytecodeChunkCount += 1;
}
else {
if (chunk.isEntry) {
let hasBytecodeMoudle = false;
const idsToHandle = new Set([...chunk.imports, ...chunk.dynamicImports]);
for (const moduleId of idsToHandle) {
if (bytecodeChunks.includes(moduleId)) {
hasBytecodeMoudle = true;
break;
}
const moduleInfo = this.getModuleInfo(moduleId);
if (moduleInfo && !moduleInfo.isExternal) {
const { importers, dynamicImporters } = moduleInfo;
for (const importerId of importers)
idsToHandle.add(importerId);
for (const importerId of dynamicImporters)
idsToHandle.add(importerId);
}
}
_code = hasBytecodeMoudle
? _code.replace(/("use strict";)|('use strict';)/, `${useStrict}\n${getBytecodeLoaderBlock(chunk.fileName)}`)
: _code;
}
chunk.code = _code;
}
}
}));
if (bytecodeChunkCount && !_chunks.some(ass => ass.type === 'asset' && ass.fileName === bytecodeModuleLoader)) {
this.emitFile({
type: 'asset',
source: bytecodeModuleLoaderCode.join('\n') + '\n',
name: 'Bytecode Loader File',
fileName: bytecodeModuleLoader
});
}
},
writeBundle(_, output) {
if (supported) {
const bytecodeChunkCount = Object.keys(output).filter(chunk => bytecodeChunkExtensionRE.test(chunk)).length;
logger.info(`${colors.green(`✓`)} ${bytecodeChunkCount} chunks compiled into bytecode.`);
}
}
};
}
function resolveBuildOutputs(outputs, libOptions) {
if (libOptions && !Array.isArray(outputs)) {
const libFormats = libOptions.formats || [];
return libFormats.map(format => ({ ...outputs, format }));
}
return outputs;
}
function protectStringsPlugin(api) {
const { types: t } = api;
function createFromCharCodeFunction(value) {
const charCodes = Array.from(value).map(s => s.charCodeAt(0));
const charCodeLiterals = charCodes.map(code => t.numericLiteral(code));
// String.fromCharCode
const memberExpression = t.memberExpression(t.identifier('String'), t.identifier('fromCharCode'));
// String.fromCharCode(...arr)
const callExpression = t.callExpression(memberExpression, [t.spreadElement(t.identifier('arr'))]);
// return String.fromCharCode(...arr)
const returnStatement = t.returnStatement(callExpression);
// function (arr) { return ... }
const functionExpression = t.functionExpression(null, [t.identifier('arr')], t.blockStatement([returnStatement]));
// (function(...) { ... })([x, x, x])
return t.callExpression(functionExpression, [t.arrayExpression(charCodeLiterals)]);
}
return {
name: 'protect-strings-plugin',
visitor: {
StringLiteral(path, state) {
// obj['property']
if (path.parentPath.isMemberExpression({ property: path.node, computed: true })) {
return;
}
// { 'key': value }
if (path.parentPath.isObjectProperty({ key: path.node, computed: false })) {
return;
}
// require('fs')
if (path.parentPath.isCallExpression() &&
t.isIdentifier(path.parentPath.node.callee) &&
path.parentPath.node.callee.name === 'require' &&
path.parentPath.node.arguments[0] === path.node) {
return;
}
// Only CommonJS is supported, import declaration and export declaration checks are ignored
const { value } = path.node;
if (state.opts.protectedStrings.has(value)) {
path.replaceWith(createFromCharCodeFunction(value));
}
},
TemplateLiteral(path, state) {
// Must be a pure static template literal
// expressions must be empty (no ${variables})
// quasis must have only one element (meaning the entire string is a single static part).
if (path.node.expressions.length > 0 || path.node.quasis.length !== 1) {
return;
}
// Extract the raw value of the template literal
// path.node.quasis[0].value.raw is used to get the raw string, including escape sequences
// path.node.quasis[0].value.cooked is used to get the processed/cooked string (with escape sequences handled)
const value = path.node.quasis[0].value.cooked;
if (value && state.opts.protectedStrings.has(value)) {
path.replaceWith(createFromCharCodeFunction(value));
}
}
}
};
}
function defineConfig(config) {
return config;
}
async function resolveConfig(inlineConfig, command, defaultMode = 'development') {
const config = inlineConfig;
const mode = inlineConfig.mode || defaultMode;
process.env.NODE_ENV = defaultMode;
let userConfig;
let configFileDependencies = [];
let { configFile } = config;
if (configFile !== false) {
const configEnv = {
mode,
command
};
const loadResult = await loadConfigFromFile(configEnv, configFile, config.root, config.logLevel, config.ignoreConfigWarning);
if (loadResult) {
const root = config.root;
delete config.root;
delete config.configFile;
config.configFile = false;
const outDir = config.build?.outDir;
if (loadResult.config.main) {
const mainViteConfig = mergeConfig(loadResult.config.main, deepClone(config));
mainViteConfig.mode = inlineConfig.mode || mainViteConfig.mode || defaultMode;
if (outDir) {
resetOutDir(mainViteConfig, outDir, 'main');
}
const configDrivenPlugins = await resolveConfigDrivenPlugins(mainViteConfig);
const builtInMainPlugins = [
electronMainConfigPresetPlugin({ root }),
electronMainConfigValidatorPlugin(),
assetPlugin(),
workerPlugin(),
modulePathPlugin(mergeConfig({
plugins: [
electronMainConfigPresetPlugin({ root }),
assetPlugin(),
importMetaPlugin(),
esmShimPlugin(),
...configDrivenPlugins
]
}, mainViteConfig)),
importMetaPlugin(),
esmShimPlugin(),
...configDrivenPlugins
];
mainViteConfig.plugins = builtInMainPlugins.concat(mainViteConfig.plugins || []);
loadResult.config.main = mainViteConfig;
}
if (loadResult.config.preload) {
const preloadViteConfig = mergeConfig(loadResult.config.preload, deepClone(config));
preloadViteConfig.mode = inlineConfig.mode || preloadViteConfig.mode || defaultMode;
if (outDir) {
resetOutDir(preloadViteConfig, outDir, 'preload');
}
const configDrivenPlugins = await resolveConfigDrivenPlugins(preloadViteConfig);
const builtInPreloadPlugins = [
electronPreloadConfigPresetPlugin({ root }),
electronPreloadConfigValidatorPlugin(),
assetPlugin(),
importMetaPlugin(),
esmShimPlugin(),
...configDrivenPlugins
];
if (preloadViteConfig.build?.isolatedEntries) {
builtInPreloadPlugins.push(isolateEntriesPlugin(mergeConfig({
plugins: [
electronPreloadConfigPresetPlugin({ root }),
assetPlugin(),
importMetaPlugin(),
esmShimPlugin(),
...configDrivenPlugins
]
}, preloadViteConfig)));
}
preloadViteConfig.plugins = builtInPreloadPlugins.concat(preloadViteConfig.plugins);
loadResult.config.preload = preloadViteConfig;
}
if (loadResult.config.renderer) {
const rendererViteConfig = mergeConfig(loadResult.config.renderer, deepClone(config));
rendererViteConfig.mode = inlineConfig.mode || rendererViteConfig.mode || defaultMode;
if (outDir) {
resetOutDir(rendererViteConfig, outDir, 'renderer');
}
const builtInRendererPlugins = [
electronRendererConfigPresetPlugin({ root }),
electronRendererConfigValidatorPlugin()
];
if (rendererViteConfig.build?.isolatedEntries) {
builtInRendererPlugins.push(isolateEntriesPlugin(mergeConfig({
plugins: [electronRendererConfigPresetPlugin({ root })]
}, rendererViteConfig)));
}
rendererViteConfig.plugins = builtInRendererPlugins.concat(rendererViteConfig.plugins || []);
loadResult.config.renderer = rendererViteConfig;
}
userConfig = loadResult.config;
configFile = loadResult.path;
configFileDependencies = loadResult.dependencies;
}
}
const resolved = {
config: userConfig,
configFile: configFile ? normalizePath(configFile) : undefined,
configFileDependencies
};
return resolved;
}
function resetOutDir(config, outDir, subOutDir) {
let userOutDir = config.build?.outDir;
if (outDir === userOutDir) {
userOutDir = path.resolve(config.root || process.cwd(), outDir, subOutDir);
if (config.build) {
config.build.outDir = userOutDir;
}
else {
config.build = { outDir: userOutDir };
}
}
}
async function resolveConfigDrivenPlugins(config) {
const userPlugins = (await asyncFlatten(config.plugins || [])).filter(Boolean);
const configDrivenPlugins = [];
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) {
return typeof value === 'object' && value !== null;
}
const CONFIG_FILE_NAME = 'electron.vite.config';
async function loadConfigFromFile(configEnv, configFile, configRoot = process.cwd(), logLevel, ignoreConfigWarning = false) {
if (configFile && /^vite.config.(js|ts|mjs|cjs|mts|cts)$/.test(configFile)) {
throw new Error(`config file cannot be named ${configFile}.`);
}
const resolvedPath = configFile
? path.resolve(configFile)
: findConfigFile(configRoot, ['js', 'ts', 'mjs', 'cjs', 'mts', 'cts']);
if (!resolvedPath) {
return {
path: '',
config: { main: {}, preload: {}, renderer: {} },
dependencies: []
};
}
const isESM = isFilePathESM(resolvedPath);
try {
const { code, dependencies } = await bundleConfigFile(resolvedPath, isESM);
const configExport = await loadConfigFormBundledFile(configRoot, resolvedPath, code, isESM);
const config = await (typeof configExport === 'function' ? configExport(configEnv) : configExport);
if (!isObject(config)) {
throw new Error(`config must export or return an object`);
}
if (!ignoreConfigWarning) {
const missingFields = ['main', 'renderer', 'preload'].filter(field => !config[field]);
if (missingFields.length > 0) {
createLogger(logLevel).warn(`${colors.yellow(colors.bold('(!)'))} ${colors.yellow(`${missingFields.join(' and ')} config is missing`)}\n`);
}
}
return {
path: normalizePath(resolvedPath),
config,
dependencies
};
}
catch (e) {
createLogger(logLevel).error(colors.red(`failed to load config from ${resolvedPath}`), { error: e });
throw e;
}
}
function findConfigFile(configRoot, extensions) {
for (const ext of extensions) {
const configFile = path.resolve(configRoot, `${CONFIG_FILE_NAME}.${ext}`);
if (fs.existsSync(configFile)) {
return configFile;
}
}
return '';
}
async function bundleConfigFile(fileName, isESM) {
const dirnameVarName = '__electron_vite_injected_dirname';
const filenameVarName = '__electron_vite_injected_filename';
const importMetaUrlVarName = '__electron_vite_injected_import_meta_url';
const result = await build$1({
absWorkingDir: process.cwd(),
entryPoints: [fileName],
write: false,
target: ['node20'],
platform: 'node',
bundle: true,
format: isESM ? 'esm' : 'cjs',
sourcemap: false,
metafile: true,
define: {
__dirname: dirnameVarName,
__filename: filenameVarName,
'import.meta.url': importMetaUrlVarName
},
plugins: [
{
name: 'externalize-deps',
setup(build) {
build.onResolve({ filter: /.*/ }, args => {
const id = args.path;
if (id[0] !== '.' && !path.isAbsolute(id)) {
return {
external: true
};
}
return null;
});
}
},
{
name: 'replace-import-meta',
setup(build) {
build.onLoad({ filter: /\.[cm]?[jt]s$/ }, async (args) => {
const contents = await fs.promises.readFile(args.path, 'utf8');
const injectValues = `const ${dirnameVarName} = ${JSON.stringify(path.dirname(args.path))};` +
`const ${filenameVarName} = ${JSON.stringify(args.path)};` +
`const ${importMetaUrlVarName} = ${JSON.stringify(pathToFileURL(args.path).href)};`;
return {
loader: args.path.endsWith('ts') ? 'ts' : 'js',
contents: injectValues + contents
};
});
}
}
]
});
const { text } = result.outputFiles[0];
return {
code: text,
dependencies: result.metafile ? Object.keys(result.metafile.inputs) : []
};
}
const _require = createRequire(import.meta.url);
async function loadConfigFormBundledFile(configRoot, configFile, bundledCode, isESM) {
if (isESM) {
const fileNameTmp = path.resolve(configRoot, `${CONFIG_FILE_NAME}.${Date.now()}.mjs`);
fs.writeFileSync(fileNameTmp, bundledCode);
const fileUrl = pathToFileURL(fileNameTmp);
try {
return (await import(fileUrl.href)).default;
}
finally {
try {
fs.unlinkSync(fileNameTmp);
}
catch { }
}
}
else {
const extension = path.extname(configFile);
const realFileName = fs.realpathSync(configFile);
const loaderExt = extension in _require.extensions ? extension : '.js';
const defaultLoader = _require.extensions[loaderExt];
_require.extensions[loaderExt] = (module, filename) => {
if (filename === realFileName) {
module._compile(bundledCode, filename);
}
else {
defaultLoader(module, filename);
}
};
delete _require.cache[_require.resolve(configFile)];
const raw = _require(configFile);
_require.extensions[loaderExt] = defaultLoader;
return raw.__esModule ? raw.default : raw;
}
}
export { loadConfigFromFile as a, bytecodePlugin as b, resolveHostname as c, defineConfig as d, externalizeDepsPlugin as e, loadEnv as l, resolveConfig as r, startElectron as s };