修改前端架构到Electron ,增加llm_api页面,增加chat SSE渲染,修改若安按钮,卡片名称修改等小功能,增加agent-core,增加SQlite,增加llm UDS Pipe websocke

This commit is contained in:
mark
2026-09-27 14:18:57 +08:00
parent b064c0cbca
commit c800a503da
3427 changed files with 1290962 additions and 32816 deletions
+24
View File
@@ -0,0 +1,24 @@
import colors from 'picocolors';
import { createLogger } from 'vite';
import { s as startElectron } from './lib-BmEkZIgk.mjs';
import { build } from './lib-D1n7TA6K.mjs';
import 'node:path';
import 'node:fs';
import 'node:url';
import 'node:module';
import 'esbuild';
import 'node:child_process';
import 'node:crypto';
import 'node:fs/promises';
import 'magic-string';
async function preview(inlineConfig = {}, options) {
if (!options.skipBuild) {
await build(inlineConfig);
}
const logger = createLogger(inlineConfig.logLevel);
startElectron(inlineConfig.root);
logger.info(colors.green(`\nstart electron app...\n`));
}
export { preview };
+112
View File
@@ -0,0 +1,112 @@
'use strict';
var vite = require('vite');
var colors = require('picocolors');
var config = require('./lib-CMs-qhOt.cjs');
require('node:path');
require('node:fs');
require('node:url');
require('node:module');
require('esbuild');
require('node:child_process');
require('node:crypto');
require('node:fs/promises');
require('magic-string');
async function createServer(inlineConfig = {}, options) {
process.env.NODE_ENV_ELECTRON_VITE = 'development';
const config$1 = await config.resolveConfig(inlineConfig, 'serve', 'development');
if (config$1.config) {
const logger = vite.createLogger(inlineConfig.logLevel);
let server;
let ps;
const errorHook = (e) => {
logger.error(`${colors.bgRed(colors.white(' ERROR '))} ${colors.red(e.message)}`);
};
const mainViteConfig = config$1.config?.main;
if (mainViteConfig && !options.rendererOnly) {
const watchHook = () => {
logger.info(colors.green(`\nrebuild the electron main process successfully`));
if (ps) {
logger.info(colors.cyan(`\n waiting for electron to exit...`));
ps.removeAllListeners();
ps.kill();
ps = config.startElectron(inlineConfig.root);
logger.info(colors.green(`\nrestart electron app...`));
}
};
await doBuild(mainViteConfig, watchHook, errorHook);
logger.info(colors.green(`\nbuild the electron main process successfully`));
}
const preloadViteConfig = config$1.config?.preload;
if (preloadViteConfig && !options.rendererOnly) {
logger.info(colors.gray(`\n-----\n`));
const watchHook = () => {
logger.info(colors.green(`\nrebuild the electron preload files successfully`));
if (server) {
logger.info(colors.cyan(`\n trigger renderer reload`));
server.ws.send({ type: 'full-reload' });
}
};
await doBuild(preloadViteConfig, watchHook, errorHook);
logger.info(colors.green(`\nbuild the electron preload files successfully`));
}
if (options.rendererOnly) {
logger.warn(`\n${colors.yellow(colors.bold('warn'))}:${colors.yellow(' you have skipped the main process and preload scripts building')}`);
}
const rendererViteConfig = config$1.config?.renderer;
if (rendererViteConfig) {
logger.info(colors.gray(`\n-----\n`));
server = await vite.createServer(rendererViteConfig);
if (!server.httpServer) {
throw new Error('HTTP server not available');
}
await server.listen();
const conf = server.config.server;
const protocol = conf.https ? 'https:' : 'http:';
const host = config.resolveHostname(conf.host);
const port = conf.port;
process.env.ELECTRON_RENDERER_URL = `${protocol}//${host}:${port}`;
const slogger = server.config.logger;
slogger.info(colors.green(`dev server running for the electron renderer process at:\n`), {
clear: !slogger.hasWarned && !options.rendererOnly
});
server.printUrls();
}
ps = config.startElectron(inlineConfig.root);
logger.info(colors.green(`\nstart electron app...\n`));
}
}
async function doBuild(config, watchHook, errorHook) {
return new Promise(resolve => {
if (config.build?.watch) {
let firstBundle = true;
const closeBundle = () => {
if (firstBundle) {
firstBundle = false;
resolve();
}
else {
watchHook();
}
};
config = vite.mergeConfig(config, {
plugins: [
{
name: 'vite:electron-watcher',
closeBundle
}
]
});
}
vite.build(config)
.then(() => {
if (!config.build?.watch) {
resolve();
}
})
.catch(e => errorHook(e));
});
}
exports.createServer = createServer;
+47
View File
@@ -0,0 +1,47 @@
'use strict';
var vite = require('vite');
var config = require('./lib-CMs-qhOt.cjs');
require('node:path');
require('node:fs');
require('node:url');
require('node:module');
require('picocolors');
require('esbuild');
require('node:child_process');
require('node:crypto');
require('node:fs/promises');
require('magic-string');
/**
* Bundles the electron app for production.
*/
async function build(inlineConfig = {}) {
process.env.NODE_ENV_ELECTRON_VITE = 'production';
const config$1 = await config.resolveConfig(inlineConfig, 'build', 'production');
if (config$1.config) {
const mainViteConfig = config$1.config?.main;
if (mainViteConfig) {
if (mainViteConfig.build?.watch) {
mainViteConfig.build.watch = null;
}
await vite.build(mainViteConfig);
}
const preloadViteConfig = config$1.config?.preload;
if (preloadViteConfig) {
if (preloadViteConfig.build?.watch) {
preloadViteConfig.build.watch = null;
}
await vite.build(preloadViteConfig);
}
const rendererViteConfig = config$1.config?.renderer;
if (rendererViteConfig) {
if (rendererViteConfig.build?.watch) {
rendererViteConfig.build.watch = null;
}
await vite.build(rendererViteConfig);
}
}
}
exports.build = build;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
import { build as build$1 } from 'vite';
import { r as resolveConfig } from './lib-BmEkZIgk.mjs';
import 'node:path';
import 'node:fs';
import 'node:url';
import 'node:module';
import 'picocolors';
import 'esbuild';
import 'node:child_process';
import 'node:crypto';
import 'node:fs/promises';
import 'magic-string';
/**
* Bundles the electron app for production.
*/
async function build(inlineConfig = {}) {
process.env.NODE_ENV_ELECTRON_VITE = 'production';
const config = await resolveConfig(inlineConfig, 'build', 'production');
if (config.config) {
const mainViteConfig = config.config?.main;
if (mainViteConfig) {
if (mainViteConfig.build?.watch) {
mainViteConfig.build.watch = null;
}
await build$1(mainViteConfig);
}
const preloadViteConfig = config.config?.preload;
if (preloadViteConfig) {
if (preloadViteConfig.build?.watch) {
preloadViteConfig.build.watch = null;
}
await build$1(preloadViteConfig);
}
const rendererViteConfig = config.config?.renderer;
if (rendererViteConfig) {
if (rendererViteConfig.build?.watch) {
rendererViteConfig.build.watch = null;
}
await build$1(rendererViteConfig);
}
}
}
export { build };
+110
View File
@@ -0,0 +1,110 @@
import { createLogger, createServer as createServer$1, mergeConfig, build } from 'vite';
import colors from 'picocolors';
import { r as resolveConfig, c as resolveHostname, s as startElectron } from './lib-BmEkZIgk.mjs';
import 'node:path';
import 'node:fs';
import 'node:url';
import 'node:module';
import 'esbuild';
import 'node:child_process';
import 'node:crypto';
import 'node:fs/promises';
import 'magic-string';
async function createServer(inlineConfig = {}, options) {
process.env.NODE_ENV_ELECTRON_VITE = 'development';
const config = await resolveConfig(inlineConfig, 'serve', 'development');
if (config.config) {
const logger = createLogger(inlineConfig.logLevel);
let server;
let ps;
const errorHook = (e) => {
logger.error(`${colors.bgRed(colors.white(' ERROR '))} ${colors.red(e.message)}`);
};
const mainViteConfig = config.config?.main;
if (mainViteConfig && !options.rendererOnly) {
const watchHook = () => {
logger.info(colors.green(`\nrebuild the electron main process successfully`));
if (ps) {
logger.info(colors.cyan(`\n waiting for electron to exit...`));
ps.removeAllListeners();
ps.kill();
ps = startElectron(inlineConfig.root);
logger.info(colors.green(`\nrestart electron app...`));
}
};
await doBuild(mainViteConfig, watchHook, errorHook);
logger.info(colors.green(`\nbuild the electron main process successfully`));
}
const preloadViteConfig = config.config?.preload;
if (preloadViteConfig && !options.rendererOnly) {
logger.info(colors.gray(`\n-----\n`));
const watchHook = () => {
logger.info(colors.green(`\nrebuild the electron preload files successfully`));
if (server) {
logger.info(colors.cyan(`\n trigger renderer reload`));
server.ws.send({ type: 'full-reload' });
}
};
await doBuild(preloadViteConfig, watchHook, errorHook);
logger.info(colors.green(`\nbuild the electron preload files successfully`));
}
if (options.rendererOnly) {
logger.warn(`\n${colors.yellow(colors.bold('warn'))}:${colors.yellow(' you have skipped the main process and preload scripts building')}`);
}
const rendererViteConfig = config.config?.renderer;
if (rendererViteConfig) {
logger.info(colors.gray(`\n-----\n`));
server = await createServer$1(rendererViteConfig);
if (!server.httpServer) {
throw new Error('HTTP server not available');
}
await server.listen();
const conf = server.config.server;
const protocol = conf.https ? 'https:' : 'http:';
const host = resolveHostname(conf.host);
const port = conf.port;
process.env.ELECTRON_RENDERER_URL = `${protocol}//${host}:${port}`;
const slogger = server.config.logger;
slogger.info(colors.green(`dev server running for the electron renderer process at:\n`), {
clear: !slogger.hasWarned && !options.rendererOnly
});
server.printUrls();
}
ps = startElectron(inlineConfig.root);
logger.info(colors.green(`\nstart electron app...\n`));
}
}
async function doBuild(config, watchHook, errorHook) {
return new Promise(resolve => {
if (config.build?.watch) {
let firstBundle = true;
const closeBundle = () => {
if (firstBundle) {
firstBundle = false;
resolve();
}
else {
watchHook();
}
};
config = mergeConfig(config, {
plugins: [
{
name: 'vite:electron-watcher',
closeBundle
}
]
});
}
build(config)
.then(() => {
if (!config.build?.watch) {
resolve();
}
})
.catch(e => errorHook(e));
});
}
export { createServer };
+26
View File
@@ -0,0 +1,26 @@
'use strict';
var colors = require('picocolors');
var vite = require('vite');
var config = require('./lib-CMs-qhOt.cjs');
var build = require('./lib-BVIIv3aq.cjs');
require('node:path');
require('node:fs');
require('node:url');
require('node:module');
require('esbuild');
require('node:child_process');
require('node:crypto');
require('node:fs/promises');
require('magic-string');
async function preview(inlineConfig = {}, options) {
if (!options.skipBuild) {
await build.build(inlineConfig);
}
const logger = vite.createLogger(inlineConfig.logLevel);
config.startElectron(inlineConfig.root);
logger.info(colors.green(`\nstart electron app...\n`));
}
exports.preview = preview;
+121
View File
@@ -0,0 +1,121 @@
'use strict';
var cac = require('cac');
var colors = require('picocolors');
var vite = require('vite');
var version = "2.3.0";
const cli = cac.cac('electron-vite');
function createInlineConfig(root, options) {
return {
root,
mode: options.mode,
configFile: options.config,
logLevel: options.logLevel,
clearScreen: options.clearScreen,
ignoreConfigWarning: options.ignoreConfigWarning,
build: {
sourcemap: options.sourcemap,
outDir: options.outDir,
...(options.w || options.watch ? { watch: {} } : null)
}
};
}
cli
.option('-c, --config <file>', `[string] use specified config file`)
.option('-l, --logLevel <level>', `[string] info | warn | error | silent`)
.option('--clearScreen', `[boolean] allow/disable clear screen when logging`)
.option('-d, --debug [feat]', `[string | boolean] show debug logs`)
.option('-f, --filter <filter>', `[string] filter debug logs`)
.option('-m, --mode <mode>', `[string] set env mode`)
.option('--ignoreConfigWarning', `[boolean] ignore config warning`)
.option('--sourcemap', `[boolean] output source maps for debug (default: false)`)
.option('--outDir <dir>', `[string] output directory (default: out)`)
.option('--entry <file>', `[string] specify electron entry file`);
// dev
cli
.command('[root]', 'start dev server and electron app')
.alias('serve')
.alias('dev')
.option('-w, --watch', `[boolean] rebuilds when main process or preload script modules have changed on disk`)
.option('--inspect [port]', `[boolean | number] enable V8 inspector on the specified port`)
.option('--inspectBrk [port]', `[boolean | number] enable V8 inspector on the specified port`)
.option('--remoteDebuggingPort <port>', `[string] port for remote debugging`)
.option('--noSandbox', `[boolean] forces renderer process to run un-sandboxed`)
.option('--rendererOnly', `[boolean] only dev server for the renderer`)
.action(async (root, options) => {
if (options.remoteDebuggingPort) {
process.env.REMOTE_DEBUGGING_PORT = options.remoteDebuggingPort;
}
if (options.inspect) {
process.env.V8_INSPECTOR_PORT = typeof options.inspect === 'number' ? `${options.inspect}` : '5858';
}
if (options.inspectBrk) {
process.env.V8_INSPECTOR_BRK_PORT = typeof options.inspectBrk === 'number' ? `${options.inspectBrk}` : '5858';
}
if (options.noSandbox) {
process.env.NO_SANDBOX = '1';
}
if (options['--']) {
process.env.ELECTRON_CLI_ARGS = JSON.stringify(options['--']);
}
if (options.entry) {
process.env.ELECTRON_ENTRY = options.entry;
}
const { createServer } = await Promise.resolve().then(function () { return require('./chunks/lib-BSnPjVZB.cjs'); });
const inlineConfig = createInlineConfig(root, options);
try {
await createServer(inlineConfig, { rendererOnly: options.rendererOnly });
}
catch (e) {
const error = e;
vite.createLogger(options.logLevel).error(colors.red(`error during start dev server and electron app:\n${error.stack}`), { error });
process.exit(1);
}
});
// build
cli.command('build [root]', 'build for production').action(async (root, options) => {
const { build } = await Promise.resolve().then(function () { return require('./chunks/lib-BVIIv3aq.cjs'); });
const inlineConfig = createInlineConfig(root, options);
if (options.entry) {
process.env.ELECTRON_ENTRY = options.entry;
}
try {
await build(inlineConfig);
}
catch (e) {
const error = e;
vite.createLogger(options.logLevel).error(colors.red(`error during build:\n${error.stack}`), { error });
process.exit(1);
}
});
// preview
cli
.command('preview [root]', 'start electron app to preview production build')
.option('--noSandbox', `[boolean] forces renderer process to run un-sandboxed`)
.option('--skipBuild', `[boolean] skip build`)
.action(async (root, options) => {
const { preview } = await Promise.resolve().then(function () { return require('./chunks/lib-yyd5PWBM.cjs'); });
const inlineConfig = createInlineConfig(root, options);
if (options.noSandbox) {
process.env.NO_SANDBOX = '1';
}
if (options.entry) {
process.env.ELECTRON_ENTRY = options.entry;
}
if (options['--']) {
process.env.ELECTRON_CLI_ARGS = JSON.stringify(options['--']);
}
try {
await preview(inlineConfig, { skipBuild: options.skipBuild });
}
catch (e) {
const error = e;
vite.createLogger(options.logLevel).error(colors.red(`error during preview electron app:\n${error.stack}`), { error });
process.exit(1);
}
});
cli.help();
cli.version(version);
cli.parse();
+119
View File
@@ -0,0 +1,119 @@
import { cac } from 'cac';
import colors from 'picocolors';
import { createLogger } from 'vite';
var version = "2.3.0";
const cli = cac('electron-vite');
function createInlineConfig(root, options) {
return {
root,
mode: options.mode,
configFile: options.config,
logLevel: options.logLevel,
clearScreen: options.clearScreen,
ignoreConfigWarning: options.ignoreConfigWarning,
build: {
sourcemap: options.sourcemap,
outDir: options.outDir,
...(options.w || options.watch ? { watch: {} } : null)
}
};
}
cli
.option('-c, --config <file>', `[string] use specified config file`)
.option('-l, --logLevel <level>', `[string] info | warn | error | silent`)
.option('--clearScreen', `[boolean] allow/disable clear screen when logging`)
.option('-d, --debug [feat]', `[string | boolean] show debug logs`)
.option('-f, --filter <filter>', `[string] filter debug logs`)
.option('-m, --mode <mode>', `[string] set env mode`)
.option('--ignoreConfigWarning', `[boolean] ignore config warning`)
.option('--sourcemap', `[boolean] output source maps for debug (default: false)`)
.option('--outDir <dir>', `[string] output directory (default: out)`)
.option('--entry <file>', `[string] specify electron entry file`);
// dev
cli
.command('[root]', 'start dev server and electron app')
.alias('serve')
.alias('dev')
.option('-w, --watch', `[boolean] rebuilds when main process or preload script modules have changed on disk`)
.option('--inspect [port]', `[boolean | number] enable V8 inspector on the specified port`)
.option('--inspectBrk [port]', `[boolean | number] enable V8 inspector on the specified port`)
.option('--remoteDebuggingPort <port>', `[string] port for remote debugging`)
.option('--noSandbox', `[boolean] forces renderer process to run un-sandboxed`)
.option('--rendererOnly', `[boolean] only dev server for the renderer`)
.action(async (root, options) => {
if (options.remoteDebuggingPort) {
process.env.REMOTE_DEBUGGING_PORT = options.remoteDebuggingPort;
}
if (options.inspect) {
process.env.V8_INSPECTOR_PORT = typeof options.inspect === 'number' ? `${options.inspect}` : '5858';
}
if (options.inspectBrk) {
process.env.V8_INSPECTOR_BRK_PORT = typeof options.inspectBrk === 'number' ? `${options.inspectBrk}` : '5858';
}
if (options.noSandbox) {
process.env.NO_SANDBOX = '1';
}
if (options['--']) {
process.env.ELECTRON_CLI_ARGS = JSON.stringify(options['--']);
}
if (options.entry) {
process.env.ELECTRON_ENTRY = options.entry;
}
const { createServer } = await import('./chunks/lib-t2ExBjL5.mjs');
const inlineConfig = createInlineConfig(root, options);
try {
await createServer(inlineConfig, { rendererOnly: options.rendererOnly });
}
catch (e) {
const error = e;
createLogger(options.logLevel).error(colors.red(`error during start dev server and electron app:\n${error.stack}`), { error });
process.exit(1);
}
});
// build
cli.command('build [root]', 'build for production').action(async (root, options) => {
const { build } = await import('./chunks/lib-D1n7TA6K.mjs');
const inlineConfig = createInlineConfig(root, options);
if (options.entry) {
process.env.ELECTRON_ENTRY = options.entry;
}
try {
await build(inlineConfig);
}
catch (e) {
const error = e;
createLogger(options.logLevel).error(colors.red(`error during build:\n${error.stack}`), { error });
process.exit(1);
}
});
// preview
cli
.command('preview [root]', 'start electron app to preview production build')
.option('--noSandbox', `[boolean] forces renderer process to run un-sandboxed`)
.option('--skipBuild', `[boolean] skip build`)
.action(async (root, options) => {
const { preview } = await import('./chunks/lib-AgEz08--.mjs');
const inlineConfig = createInlineConfig(root, options);
if (options.noSandbox) {
process.env.NO_SANDBOX = '1';
}
if (options.entry) {
process.env.ELECTRON_ENTRY = options.entry;
}
if (options['--']) {
process.env.ELECTRON_CLI_ARGS = JSON.stringify(options['--']);
}
try {
await preview(inlineConfig, { skipBuild: options.skipBuild });
}
catch (e) {
const error = e;
createLogger(options.logLevel).error(colors.red(`error during preview electron app:\n${error.stack}`), { error });
process.exit(1);
}
});
cli.help();
cli.version(version);
cli.parse();
+513
View File
@@ -0,0 +1,513 @@
'use strict';
var vite = require('vite');
var config = require('./chunks/lib-CMs-qhOt.cjs');
var server = require('./chunks/lib-BSnPjVZB.cjs');
var build = require('./chunks/lib-BVIIv3aq.cjs');
var preview = require('./chunks/lib-yyd5PWBM.cjs');
var path = require('node:path');
var fs = require('node:fs');
var node_child_process = require('node:child_process');
var node_module = require('node:module');
var colors = require('picocolors');
var babel = require('@babel/core');
var MagicString = require('magic-string');
require('node:url');
require('esbuild');
require('node:crypto');
require('node:fs/promises');
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var babel__namespace = /*#__PURE__*/_interopNamespaceDefault(babel);
// Inspired by https://github.com/bytenode/bytenode
const _require = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
function getBytecodeCompilerPath() {
return path.join(path.dirname(_require.resolve('electron-vite/package.json')), 'bin', 'electron-bytecode.cjs');
}
function compileToBytecode(code) {
return new Promise((resolve, reject) => {
let data = Buffer.from([]);
const electronPath = config.getElectronPath();
const bytecodePath = getBytecodeCompilerPath();
const proc = node_child_process.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);`,
`};`
];
/**
* Compile to v8 bytecode to protect source code.
*/
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 filter = vite.createFilter(/\.(m?[jt]s|[jt]sx)$/);
const escapeRegExpString = (str) => {
return str
.replace(/\\/g, '\\\\\\\\')
.replace(/[|{}()[\]^$+*?.]/g, '\\$&')
.replace(/-/g, '\\u002d');
};
const transformAllChunks = _chunkAlias.length === 0;
const isBytecodeChunk = (chunkName) => {
return transformAllChunks || _chunkAlias.some(alias => alias === chunkName);
};
const _transform = (code) => {
const re = babel__namespace.transform(code, {
plugins: ['@babel/plugin-transform-arrow-functions']
});
return re.code || '';
};
const useStrict = '"use strict";';
const bytecodeModuleLoader = 'bytecode-loader.cjs';
let config$1;
let useInRenderer = false;
let bytecodeRequired = false;
let bytecodeFiles = [];
return {
name: 'vite:bytecode',
apply: 'build',
enforce: 'post',
configResolved(resolvedConfig) {
config$1 = resolvedConfig;
useInRenderer = config$1.plugins.some(p => p.name === 'vite:electron-renderer-preset-config');
if (useInRenderer) {
config$1.logger.warn(colors.yellow('bytecodePlugin does not support renderer.'));
}
if (resolvedConfig.build.minify && protectedStrings.length > 0) {
config$1.logger.warn(colors.yellow('Strings cannot be protected when minification is enabled.'));
}
},
transform(code, id) {
if (config$1.build.minify || protectedStrings.length === 0 || !filter(id))
return;
let match;
let s;
protectedStrings.forEach(str => {
const escapedStr = escapeRegExpString(str);
const re = new RegExp(`\\u0027${escapedStr}\\u0027|\\u0022${escapedStr}\\u0022`, 'g');
const charCodes = Array.from(str).map(s => s.charCodeAt(0));
const replacement = `String.fromCharCode(${charCodes.toString()})`;
while ((match = re.exec(code))) {
s ||= new MagicString(code);
const [full] = match;
s.overwrite(match.index, match.index + full.length, replacement, {
contentOnly: true
});
}
});
if (s) {
return {
code: s.toString(),
map: config$1.build.sourcemap ? s.generateMap({ hires: 'boundary' }) : null
};
}
},
renderChunk(code, chunk, options) {
if (options.format === 'es') {
config$1.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".'));
return null;
}
if (useInRenderer) {
return null;
}
if (chunk.type === 'chunk' && isBytecodeChunk(chunk.name)) {
bytecodeRequired = true;
if (transformArrowFunctions) {
return {
code: _transform(code)
};
}
}
return null;
},
generateBundle(options) {
if (options.format !== 'es' && !useInRenderer && bytecodeRequired) {
this.emitFile({
type: 'asset',
source: bytecodeModuleLoaderCode.join('\n') + '\n',
name: 'Bytecode Loader File',
fileName: bytecodeModuleLoader
});
}
},
async writeBundle(options, output) {
if (options.format === 'es' || useInRenderer || !bytecodeRequired) {
return;
}
const outDir = options.dir;
bytecodeFiles = [];
const bundles = Object.keys(output);
const chunks = Object.values(output).filter(chunk => chunk.type === 'chunk' && isBytecodeChunk(chunk.name) && chunk.fileName !== bytecodeModuleLoader);
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 keepBundle = (chunkFileName) => {
const newFileName = path.resolve(path.dirname(chunkFileName), `_${path.basename(chunkFileName)}`);
fs.renameSync(chunkFileName, newFileName);
};
const getBytecodeLoaderBlock = (chunkFileName) => {
return `require("${config.toRelativePath(bytecodeModuleLoader, vite.normalizePath(chunkFileName))}");`;
};
await Promise.all(bundles.map(async (name) => {
const chunk = output[name];
if (chunk.type === 'chunk') {
let _code = chunk.code;
if (bytecodeRE && _code.match(bytecodeRE)) {
let match;
const s = new MagicString(_code);
while ((match = bytecodeRE.exec(_code))) {
const [prefix, chunkName] = match;
const len = prefix.length + chunkName.length;
s.overwrite(match.index, match.index + len, prefix + chunkName + 'c', {
contentOnly: true
});
}
_code = s.toString();
}
const chunkFileName = path.resolve(outDir, name);
if (bytecodeChunks.includes(name)) {
const bytecodeBuffer = await compileToBytecode(_code);
fs.writeFileSync(path.resolve(outDir, name + 'c'), bytecodeBuffer);
if (chunk.isEntry) {
if (!removeBundleJS) {
keepBundle(chunkFileName);
}
const bytecodeLoaderBlock = getBytecodeLoaderBlock(chunk.fileName);
const bytecodeModuleBlock = `require("./${path.basename(name) + 'c'}");`;
const code = `${useStrict}\n${bytecodeLoaderBlock}\n${bytecodeModuleBlock}\n`;
fs.writeFileSync(chunkFileName, code);
}
else {
if (removeBundleJS) {
fs.unlinkSync(chunkFileName);
}
else {
keepBundle(chunkFileName);
}
}
bytecodeFiles.push({ name: name + 'c', size: bytecodeBuffer.length });
}
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);
}
}
const bytecodeLoaderBlock = getBytecodeLoaderBlock(chunk.fileName);
_code = hasBytecodeMoudle ? _code.replace(useStrict, `${useStrict}\n${bytecodeLoaderBlock}`) : _code;
}
fs.writeFileSync(chunkFileName, _code);
}
}
}));
},
closeBundle() {
if (!useInRenderer) {
const chunkLimit = config$1.build.chunkSizeWarningLimit;
const outDir = vite.normalizePath(path.relative(config$1.root, path.resolve(config$1.root, config$1.build.outDir))) + '/';
config$1.logger.info(`${colors.green(`✓`)} ${bytecodeFiles.length} bundles compiled into bytecode.`);
let longest = 0;
bytecodeFiles.forEach(file => {
const len = file.name.length;
if (len > longest)
longest = len;
});
bytecodeFiles.forEach(file => {
const kbs = file.size / 1000;
config$1.logger.info(`${colors.gray(colors.white(colors.dim(outDir)))}${colors.green(file.name.padEnd(longest + 2))} ${kbs > chunkLimit ? colors.yellow(`${kbs.toFixed(2)} kB`) : colors.dim(`${kbs.toFixed(2)} kB`)}`);
});
bytecodeFiles = [];
}
}
};
}
/**
* Automatically externalize dependencies
*/
function externalizeDepsPlugin(options = {}) {
const { exclude = [], include = [] } = options;
const pkg = config.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 = vite.mergeConfig(defaultConfig.build, config.build || {});
config.build = buildConfig;
}
};
}
async function transformWithSWC(code, id, options) {
const { sourcemap = false, minify = false } = options;
delete options.sourcemap;
delete options.minify;
const isTs = /\.tsx?$/.test(id);
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
let swc;
try {
swc = require$1('@swc/core');
}
catch {
throw new Error('swc plugin require @swc/core, you need to install it.');
}
const jsc = {
parser: {
syntax: isTs ? 'typescript' : 'ecmascript',
decorators: true
},
transform: {
legacyDecorator: true,
decoratorMetadata: true,
...options
},
keepClassNames: true,
target: 'es2022',
minify: {
format: {
comments: false
}
}
};
const result = await swc.transform(code, {
jsc,
sourceMaps: sourcemap,
minify,
configFile: false,
swcrc: false
});
const map = sourcemap && result.map ? JSON.parse(result.map) : { mappings: '' };
return {
code: result.code,
map
};
}
/**
* Use SWC to support for emitting type metadata for decorators.
* When using `swcPlugin`, you need to install `@swc/core`.
*/
function swcPlugin(options = {}) {
const filter = vite.createFilter(options.include || /\.(m?ts|[jt]sx)$/, options.exclude || /\.js$/);
let sourcemap = false;
let minify = false;
return {
name: 'vite:swc',
config() {
return {
esbuild: false
};
},
async configResolved(resolvedConfig) {
sourcemap = resolvedConfig.build?.sourcemap === 'inline' ? 'inline' : !!resolvedConfig.build?.sourcemap;
minify = resolvedConfig.build?.minify;
},
async transform(code, id) {
if (filter(id)) {
const result = await transformWithSWC(code, id, { sourcemap, ...(options.transformOptions || {}) });
return {
code: result.code,
map: result.map
};
}
},
async renderChunk(code, chunk) {
if (!minify || minify === 'terser') {
return null;
}
const result = await transformWithSWC(code, chunk.fileName, {
sourcemap,
minify: true,
...(options.transformOptions || {})
});
return {
code: result.code,
map: result.map
};
}
};
}
Object.defineProperty(exports, "createLogger", {
enumerable: true,
get: function () { return vite.createLogger; }
});
Object.defineProperty(exports, "defineViteConfig", {
enumerable: true,
get: function () { return vite.defineConfig; }
});
Object.defineProperty(exports, "mergeConfig", {
enumerable: true,
get: function () { return vite.mergeConfig; }
});
Object.defineProperty(exports, "splitVendorChunk", {
enumerable: true,
get: function () { return vite.splitVendorChunk; }
});
Object.defineProperty(exports, "splitVendorChunkPlugin", {
enumerable: true,
get: function () { return vite.splitVendorChunkPlugin; }
});
exports.defineConfig = config.defineConfig;
exports.loadConfigFromFile = config.loadConfigFromFile;
exports.loadEnv = config.loadEnv;
exports.resolveConfig = config.resolveConfig;
exports.createServer = server.createServer;
exports.build = build.build;
exports.preview = preview.preview;
exports.bytecodePlugin = bytecodePlugin;
exports.externalizeDepsPlugin = externalizeDepsPlugin;
exports.swcPlugin = swcPlugin;
+134
View File
@@ -0,0 +1,134 @@
/// <reference types="node" />
import { UserConfig as UserConfig$1, UserConfigExport, ConfigEnv, LogLevel, Plugin, FilterPattern } from 'vite';
export { LogLevel, createLogger, defineConfig as defineViteConfig, mergeConfig, splitVendorChunk, splitVendorChunkPlugin } from 'vite';
import { TransformConfig } from '@swc/core';
interface UserConfig {
/**
* Vite config options for electron main process
*
* https://vitejs.dev/config/
*/
main?: UserConfig$1 & {
configFile?: string | false;
};
/**
* Vite config options for electron renderer process
*
* https://vitejs.dev/config/
*/
renderer?: UserConfig$1 & {
configFile?: string | false;
};
/**
* Vite config options for electron preload files
*
* https://vitejs.dev/config/
*/
preload?: UserConfig$1 & {
configFile?: string | false;
};
}
interface ElectronViteConfig {
/**
* Vite config options for electron main process
*
* https://vitejs.dev/config/
*/
main?: UserConfigExport;
/**
* Vite config options for electron renderer process
*
* https://vitejs.dev/config/
*/
renderer?: UserConfigExport;
/**
* Vite config options for electron preload files
*
* https://vitejs.dev/config/
*/
preload?: UserConfigExport;
}
type InlineConfig = Omit<UserConfig$1, 'base'> & {
configFile?: string | false;
envFile?: false;
ignoreConfigWarning?: boolean;
};
type ElectronViteConfigFnObject = (env: ConfigEnv) => ElectronViteConfig;
type ElectronViteConfigFnPromise = (env: ConfigEnv) => Promise<ElectronViteConfig>;
type ElectronViteConfigFn = (env: ConfigEnv) => ElectronViteConfig | Promise<ElectronViteConfig>;
type ElectronViteConfigExport = ElectronViteConfig | Promise<ElectronViteConfig> | ElectronViteConfigFnObject | ElectronViteConfigFnPromise | ElectronViteConfigFn;
/**
* Type helper to make it easier to use `electron.vite.config.*`
* accepts a direct {@link ElectronViteConfig} object, or a function that returns it.
* The function receives a object that exposes two properties:
* `command` (either `'build'` or `'serve'`), and `mode`.
*/
declare function defineConfig(config: ElectronViteConfig): ElectronViteConfig;
declare function defineConfig(config: Promise<ElectronViteConfig>): Promise<ElectronViteConfig>;
declare function defineConfig(config: ElectronViteConfigFnObject): ElectronViteConfigFnObject;
declare function defineConfig(config: ElectronViteConfigExport): ElectronViteConfigExport;
interface ResolvedConfig {
config?: UserConfig;
configFile?: string;
configFileDependencies: string[];
}
declare function resolveConfig(inlineConfig: InlineConfig, command: 'build' | 'serve', defaultMode?: string): Promise<ResolvedConfig>;
declare function loadConfigFromFile(configEnv: ConfigEnv, configFile?: string, configRoot?: string, logLevel?: LogLevel, ignoreConfigWarning?: boolean): Promise<{
path: string;
config: UserConfig;
dependencies: string[];
}>;
declare function createServer(inlineConfig: InlineConfig | undefined, options: {
rendererOnly?: boolean;
}): Promise<void>;
/**
* Bundles the electron app for production.
*/
declare function build(inlineConfig?: InlineConfig): Promise<void>;
declare function preview(inlineConfig: InlineConfig | undefined, options: {
skipBuild?: boolean;
}): Promise<void>;
/**
* 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.
*/
declare function loadEnv(mode: string, envDir?: string, prefixes?: string | string[]): Record<string, string>;
interface BytecodeOptions {
chunkAlias?: string | string[];
transformArrowFunctions?: boolean;
removeBundleJS?: boolean;
protectedStrings?: string[];
}
/**
* Compile to v8 bytecode to protect source code.
*/
declare function bytecodePlugin(options?: BytecodeOptions): Plugin | null;
interface ExternalOptions {
exclude?: string[];
include?: string[];
}
/**
* Automatically externalize dependencies
*/
declare function externalizeDepsPlugin(options?: ExternalOptions): Plugin | null;
type SwcOptions = {
include?: FilterPattern;
exclude?: FilterPattern;
transformOptions?: TransformConfig;
};
/**
* Use SWC to support for emitting type metadata for decorators.
* When using `swcPlugin`, you need to install `@swc/core`.
*/
declare function swcPlugin(options?: SwcOptions): Plugin;
export { type BytecodeOptions, type ElectronViteConfig, type ElectronViteConfigExport, type ElectronViteConfigFn, type ElectronViteConfigFnObject, type ElectronViteConfigFnPromise, type ExternalOptions, type InlineConfig, type ResolvedConfig, type SwcOptions, type UserConfig, build, bytecodePlugin, createServer, defineConfig, externalizeDepsPlugin, loadConfigFromFile, loadEnv, preview, resolveConfig, swcPlugin };
+464
View File
@@ -0,0 +1,464 @@
import { createFilter, normalizePath, mergeConfig } from 'vite';
export { createLogger, defineConfig as defineViteConfig, mergeConfig, splitVendorChunk, splitVendorChunkPlugin } from 'vite';
import { g as getElectronPath, t as toRelativePath, l as loadPackageData } from './chunks/lib-BmEkZIgk.mjs';
export { d as defineConfig, b as loadConfigFromFile, a as loadEnv, r as resolveConfig } from './chunks/lib-BmEkZIgk.mjs';
export { createServer } from './chunks/lib-t2ExBjL5.mjs';
export { build } from './chunks/lib-D1n7TA6K.mjs';
export { preview } from './chunks/lib-AgEz08--.mjs';
import path from 'node:path';
import fs from 'node:fs';
import { spawn } from 'node:child_process';
import { createRequire } from 'node:module';
import colors from 'picocolors';
import * as babel from '@babel/core';
import MagicString from 'magic-string';
import 'node:url';
import 'esbuild';
import 'node:crypto';
import 'node:fs/promises';
// Inspired by https://github.com/bytenode/bytenode
const _require = createRequire(import.meta.url);
function getBytecodeCompilerPath() {
return path.join(path.dirname(_require.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);`,
`};`
];
/**
* Compile to v8 bytecode to protect source code.
*/
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 filter = createFilter(/\.(m?[jt]s|[jt]sx)$/);
const escapeRegExpString = (str) => {
return str
.replace(/\\/g, '\\\\\\\\')
.replace(/[|{}()[\]^$+*?.]/g, '\\$&')
.replace(/-/g, '\\u002d');
};
const transformAllChunks = _chunkAlias.length === 0;
const isBytecodeChunk = (chunkName) => {
return transformAllChunks || _chunkAlias.some(alias => alias === chunkName);
};
const _transform = (code) => {
const re = babel.transform(code, {
plugins: ['@babel/plugin-transform-arrow-functions']
});
return re.code || '';
};
const useStrict = '"use strict";';
const bytecodeModuleLoader = 'bytecode-loader.cjs';
let config;
let useInRenderer = false;
let bytecodeRequired = false;
let bytecodeFiles = [];
return {
name: 'vite:bytecode',
apply: 'build',
enforce: 'post',
configResolved(resolvedConfig) {
config = resolvedConfig;
useInRenderer = config.plugins.some(p => p.name === 'vite:electron-renderer-preset-config');
if (useInRenderer) {
config.logger.warn(colors.yellow('bytecodePlugin does not support renderer.'));
}
if (resolvedConfig.build.minify && protectedStrings.length > 0) {
config.logger.warn(colors.yellow('Strings cannot be protected when minification is enabled.'));
}
},
transform(code, id) {
if (config.build.minify || protectedStrings.length === 0 || !filter(id))
return;
let match;
let s;
protectedStrings.forEach(str => {
const escapedStr = escapeRegExpString(str);
const re = new RegExp(`\\u0027${escapedStr}\\u0027|\\u0022${escapedStr}\\u0022`, 'g');
const charCodes = Array.from(str).map(s => s.charCodeAt(0));
const replacement = `String.fromCharCode(${charCodes.toString()})`;
while ((match = re.exec(code))) {
s ||= new MagicString(code);
const [full] = match;
s.overwrite(match.index, match.index + full.length, replacement, {
contentOnly: true
});
}
});
if (s) {
return {
code: s.toString(),
map: config.build.sourcemap ? s.generateMap({ hires: 'boundary' }) : null
};
}
},
renderChunk(code, chunk, options) {
if (options.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".'));
return null;
}
if (useInRenderer) {
return null;
}
if (chunk.type === 'chunk' && isBytecodeChunk(chunk.name)) {
bytecodeRequired = true;
if (transformArrowFunctions) {
return {
code: _transform(code)
};
}
}
return null;
},
generateBundle(options) {
if (options.format !== 'es' && !useInRenderer && bytecodeRequired) {
this.emitFile({
type: 'asset',
source: bytecodeModuleLoaderCode.join('\n') + '\n',
name: 'Bytecode Loader File',
fileName: bytecodeModuleLoader
});
}
},
async writeBundle(options, output) {
if (options.format === 'es' || useInRenderer || !bytecodeRequired) {
return;
}
const outDir = options.dir;
bytecodeFiles = [];
const bundles = Object.keys(output);
const chunks = Object.values(output).filter(chunk => chunk.type === 'chunk' && isBytecodeChunk(chunk.name) && chunk.fileName !== bytecodeModuleLoader);
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 keepBundle = (chunkFileName) => {
const newFileName = path.resolve(path.dirname(chunkFileName), `_${path.basename(chunkFileName)}`);
fs.renameSync(chunkFileName, newFileName);
};
const getBytecodeLoaderBlock = (chunkFileName) => {
return `require("${toRelativePath(bytecodeModuleLoader, normalizePath(chunkFileName))}");`;
};
await Promise.all(bundles.map(async (name) => {
const chunk = output[name];
if (chunk.type === 'chunk') {
let _code = chunk.code;
if (bytecodeRE && _code.match(bytecodeRE)) {
let match;
const s = new MagicString(_code);
while ((match = bytecodeRE.exec(_code))) {
const [prefix, chunkName] = match;
const len = prefix.length + chunkName.length;
s.overwrite(match.index, match.index + len, prefix + chunkName + 'c', {
contentOnly: true
});
}
_code = s.toString();
}
const chunkFileName = path.resolve(outDir, name);
if (bytecodeChunks.includes(name)) {
const bytecodeBuffer = await compileToBytecode(_code);
fs.writeFileSync(path.resolve(outDir, name + 'c'), bytecodeBuffer);
if (chunk.isEntry) {
if (!removeBundleJS) {
keepBundle(chunkFileName);
}
const bytecodeLoaderBlock = getBytecodeLoaderBlock(chunk.fileName);
const bytecodeModuleBlock = `require("./${path.basename(name) + 'c'}");`;
const code = `${useStrict}\n${bytecodeLoaderBlock}\n${bytecodeModuleBlock}\n`;
fs.writeFileSync(chunkFileName, code);
}
else {
if (removeBundleJS) {
fs.unlinkSync(chunkFileName);
}
else {
keepBundle(chunkFileName);
}
}
bytecodeFiles.push({ name: name + 'c', size: bytecodeBuffer.length });
}
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);
}
}
const bytecodeLoaderBlock = getBytecodeLoaderBlock(chunk.fileName);
_code = hasBytecodeMoudle ? _code.replace(useStrict, `${useStrict}\n${bytecodeLoaderBlock}`) : _code;
}
fs.writeFileSync(chunkFileName, _code);
}
}
}));
},
closeBundle() {
if (!useInRenderer) {
const chunkLimit = config.build.chunkSizeWarningLimit;
const outDir = normalizePath(path.relative(config.root, path.resolve(config.root, config.build.outDir))) + '/';
config.logger.info(`${colors.green(`✓`)} ${bytecodeFiles.length} bundles compiled into bytecode.`);
let longest = 0;
bytecodeFiles.forEach(file => {
const len = file.name.length;
if (len > longest)
longest = len;
});
bytecodeFiles.forEach(file => {
const kbs = file.size / 1000;
config.logger.info(`${colors.gray(colors.white(colors.dim(outDir)))}${colors.green(file.name.padEnd(longest + 2))} ${kbs > chunkLimit ? colors.yellow(`${kbs.toFixed(2)} kB`) : colors.dim(`${kbs.toFixed(2)} kB`)}`);
});
bytecodeFiles = [];
}
}
};
}
/**
* Automatically externalize dependencies
*/
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;
}
};
}
async function transformWithSWC(code, id, options) {
const { sourcemap = false, minify = false } = options;
delete options.sourcemap;
delete options.minify;
const isTs = /\.tsx?$/.test(id);
const require = createRequire(import.meta.url);
let swc;
try {
swc = require('@swc/core');
}
catch {
throw new Error('swc plugin require @swc/core, you need to install it.');
}
const jsc = {
parser: {
syntax: isTs ? 'typescript' : 'ecmascript',
decorators: true
},
transform: {
legacyDecorator: true,
decoratorMetadata: true,
...options
},
keepClassNames: true,
target: 'es2022',
minify: {
format: {
comments: false
}
}
};
const result = await swc.transform(code, {
jsc,
sourceMaps: sourcemap,
minify,
configFile: false,
swcrc: false
});
const map = sourcemap && result.map ? JSON.parse(result.map) : { mappings: '' };
return {
code: result.code,
map
};
}
/**
* Use SWC to support for emitting type metadata for decorators.
* When using `swcPlugin`, you need to install `@swc/core`.
*/
function swcPlugin(options = {}) {
const filter = createFilter(options.include || /\.(m?ts|[jt]sx)$/, options.exclude || /\.js$/);
let sourcemap = false;
let minify = false;
return {
name: 'vite:swc',
config() {
return {
esbuild: false
};
},
async configResolved(resolvedConfig) {
sourcemap = resolvedConfig.build?.sourcemap === 'inline' ? 'inline' : !!resolvedConfig.build?.sourcemap;
minify = resolvedConfig.build?.minify;
},
async transform(code, id) {
if (filter(id)) {
const result = await transformWithSWC(code, id, { sourcemap, ...(options.transformOptions || {}) });
return {
code: result.code,
map: result.map
};
}
},
async renderChunk(code, chunk) {
if (!minify || minify === 'terser') {
return null;
}
const result = await transformWithSWC(code, chunk.fileName, {
sourcemap,
minify: true,
...(options.transformOptions || {})
});
return {
code: result.code,
map: result.map
};
}
};
}
export { bytecodePlugin, externalizeDepsPlugin, swcPlugin };