修改前端架构到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
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022, Alex Wei
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+105
View File
@@ -0,0 +1,105 @@
<p align="center">
<img src="https://alex8088.github.io/assets/electron-vite.svg" width="150px" height="150px">
</p>
<div align="center">
<h1>electron-vite</h1>
</div>
<p align="center">Next generation Electron build tooling based on Vite</p>
<p align="center">
<img src="https://img.shields.io/npm/v/electron-vite?color=6988e6&label=version">
<img src="https://img.shields.io/github/license/alex8088/electron-vite?color=blue" alt="license" />
</p>
<p align="center">
<a href="https://electron-vite.org">Documentation</a> |
<a href="https://electron-vite.org/guide">Getting Started</a> |
<a href="https://github.com/alex8088/quick-start/tree/master/packages/create-electron">create-electron</a>
</p>
<p align="center">
<a href="https://cn.electron-vite.org">中文文档</a>
</p>
<br />
<br />
## Features
- ⚡️ [Vite](https://vitejs.dev) powered and use the same way.
- 🛠 Pre-configured for Electron, don't worry about configuration.
- 💡 Optimize asset handling (Node.js addons, WebAssembly, Worker Thread, etc).
- 🚀 Fast HMR for renderer processes.
- 🔥 Hot reloading for main process and preload scripts.
- 🔌 Easy to debug in IDEs like VSCode or WebStorm.
- 🔒 Compile to v8 bytecode to protect source code.
- 🏷️ Support for TypeScript decorators.
- 📦 Out-of-the-box support for TypeScript, Vue, React, Svelte, SolidJS and more.
## Usage
### Install
```sh
npm i electron-vite -D
```
### Development & Build
In a project where `electron-vite` is installed, you can use `electron-vite` binary directly with `npx electron-vite` or add the npm scripts to your `package.json` file like this:
```json
{
"scripts": {
"start": "electron-vite preview",
"dev": "electron-vite dev",
"prebuild": "electron-vite build"
}
}
```
### Configuration
When running `electron-vite` from the command line, electron-vite will automatically try to resolve a config file named `electron.vite.config.js` inside project root. The most basic config file looks like this:
```js
// electron.vite.config.js
export default {
main: {
// vite config options
},
preload: {
// vite config options
},
renderer: {
// vite config options
}
}
```
### Getting Started
Clone the [electron-vite-boilerplate](https://github.com/alex8088/electron-vite-boilerplate) or use the [create-electron](https://github.com/alex8088/quick-start/tree/master/packages/create-electron) tool to scaffold your project.
```bash
npm create @quick-start/electron
```
Currently supported template presets include:
| JavaScript | TypeScript |
| :--------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------: |
| [vanilla](https://github.com/alex8088/quick-start/tree/master/packages/create-electron/playground/vanilla) | [vanilla-ts](https://github.com/alex8088/quick-start/tree/master/packages/create-electron/playground/vanilla-ts) |
| [vue](https://github.com/alex8088/quick-start/tree/master/packages/create-electron/playground/vue) | [vue-ts](https://github.com/alex8088/quick-start/tree/master/packages/create-electron/playground/vue-ts) |
| [react](https://github.com/alex8088/quick-start/tree/master/packages/create-electron/playground/react) | [react-ts](https://github.com/alex8088/quick-start/tree/master/packages/create-electron/playground/react-ts) |
| [svelte](https://github.com/alex8088/quick-start/tree/master/packages/create-electron/playground/svelte) | [svelte-ts](https://github.com/alex8088/quick-start/tree/master/packages/create-electron/playground/svelte-ts) |
| [solid](https://github.com/alex8088/quick-start/tree/master/packages/create-electron/playground/solid) | [solid-ts](https://github.com/alex8088/quick-start/tree/master/packages/create-electron/playground/solid-ts) |
## Contribution
See [Contributing Guide](CONTRIBUTING.md).
## License
[MIT](./LICENSE) © alex.wei
+32
View File
@@ -0,0 +1,32 @@
const vm = require('vm')
const v8 = require('v8')
const wrap = require('module').wrap
v8.setFlagsFromString('--no-lazy')
v8.setFlagsFromString('--no-flush-bytecode')
let code = ''
process.stdin.setEncoding('utf-8')
process.stdin.on('readable', () => {
const data = process.stdin.read()
if (data !== null) {
code += data
}
})
process.stdin.on('end', () => {
try {
if (typeof code !== 'string') {
throw new Error(`javascript code must be string. ${typeof code} was given.`)
}
const script = new vm.Script(wrap(code), { produceCachedData: true })
const bytecodeBuffer = script.createCachedData()
process.stdout.write(bytecodeBuffer)
} catch (error) {
console.error(error)
}
})
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env node
const debugIndex = process.argv.findIndex(arg => /^(?:-d|--debug)$/.test(arg))
const filterIndex = process.argv.findIndex(arg => /^(?:-f|--filter)$/.test(arg))
if (debugIndex > 0) {
let value = process.argv[debugIndex + 1]
if (!value || value.startsWith('-')) {
value = 'vite:*'
} else {
value = value
.split(',')
.map(v => `vite:${v}`)
.join(',')
}
process.env.DEBUG = `${process.env.DEBUG ? process.env.DEBUG + ',' : ''}${value}`
if (filterIndex > 0) {
const filter = process.argv[filterIndex + 1]
if (filter && !filter.startsWith('-')) {
process.env.VITE_DEBUG_FILTER = filter
}
}
}
function run() {
import('../dist/cli.mjs')
}
run()
+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 };
+112
View File
@@ -0,0 +1,112 @@
// node worker
declare module '*?nodeWorker' {
import { Worker, WorkerOptions } from 'node:worker_threads'
export default function (options: WorkerOptions): Worker
}
// module path
declare module '*?modulePath' {
const src: string
export default src
}
// node asset
declare module '*?asset' {
const src: string
export default src
}
declare module '*?asset&asarUnpack' {
const src: string
export default src
}
declare module '*.json?commonjs-external&asset' {
const src: string
export default src
}
// native node module
declare module '*.node' {
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const node: any
export default node
}
// node wasm
declare module '*.wasm?loader' {
const loadWasm: (options?: WebAssembly.Imports) => Promise<WebAssembly.Instance>
export default loadWasm
}
// build-in process env
declare namespace NodeJS {
interface ProcessEnv {
/**
* Vite's dev server address for Electron renderers.
*/
readonly ELECTRON_RENDERER_URL?: string
}
}
// Refer to Vite's ImportMeta type declarations
// <https://github.com/vitejs/vite/blob/main/packages/vite/types/importMeta.d.ts>
interface ImportMetaEnv {
MODE: string
DEV: boolean
PROD: boolean
}
interface ImportGlobOptions<Eager extends boolean, AsType extends string> {
/**
* Import type for the import url.
*/
as?: AsType
/**
* Import as static or dynamic
*
* @default false
*/
eager?: Eager
/**
* Import only the specific named export. Set to `default` to import the default export.
*/
import?: string
/**
* Custom queries
*/
query?: string | Record<string, string | number | boolean>
/**
* Search files also inside `node_modules/` and hidden directories (e.g. `.git/`). This might have impact on performance.
*
* @default false
*/
exhaustive?: boolean
}
interface KnownAsTypeMap {
raw: string
url: string
worker: Worker
}
interface ImportGlobFunction {
/**
* Import a list of files with a glob pattern.
*
* https://vitejs.dev/guide/features.html#glob-import
*/
<Eager extends boolean, As extends string, T = As extends keyof KnownAsTypeMap ? KnownAsTypeMap[As] : unknown>(
glob: string | string[],
options?: ImportGlobOptions<Eager, As>
): (Eager extends true ? true : false) extends true ? Record<string, T> : Record<string, () => Promise<T>>
<M>(glob: string | string[], options?: ImportGlobOptions<false, string>): Record<string, () => Promise<M>>
<M>(glob: string | string[], options: ImportGlobOptions<true, string>): Record<string, M>
}
interface ImportMeta {
url: string
readonly env: ImportMetaEnv
glob: ImportGlobFunction
}
+105
View File
@@ -0,0 +1,105 @@
{
"name": "electron-vite",
"version": "2.3.0",
"description": "Electron build tooling based on Vite",
"type": "module",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./node": {
"types": "./node.d.ts"
},
"./package.json": "./package.json"
},
"bin": {
"electron-vite": "bin/electron-vite.js"
},
"files": [
"bin",
"dist",
"node.d.ts"
],
"engines": {
"node": "^18.0.0 || >=20.0.0"
},
"packageManager": "pnpm@8.6.10",
"author": "Alex Wei<https://github.com/alex8088>",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/alex8088/electron-vite.git"
},
"bugs": {
"url": "https://github.com/alex8088/electron-vite/issues"
},
"homepage": "https://electron-vite.org",
"keywords": [
"electron",
"vite",
"cli",
"plugin"
],
"scripts": {
"format": "prettier --write .",
"lint": "eslint --ext .ts src/**",
"typecheck": "tsc --noEmit",
"build": "pnpm run lint && rollup -c rollup.config.ts --configPlugin typescript"
},
"simple-git-hooks": {
"pre-commit": "npx lint-staged",
"commit-msg": "node scripts/verifyCommit.js $1"
},
"lint-staged": {
"*.js": [
"prettier --write"
],
"*.ts?(x)": [
"eslint",
"prettier --parser=typescript --write"
]
},
"peerDependencies": {
"@swc/core": "^1.0.0",
"vite": "^4.0.0 || ^5.0.0"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
}
},
"devDependencies": {
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.6",
"@swc/core": "^1.6.5",
"@types/node": "^18.19.39",
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"lint-staged": "^15.2.7",
"prettier": "^3.3.2",
"rollup": "^4.18.0",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-rm": "^1.0.2",
"simple-git-hooks": "^2.11.1",
"tslib": "^2.6.3",
"typescript": "^5.3.3",
"vite": "^5.3.1"
},
"dependencies": {
"@babel/core": "^7.24.7",
"@babel/plugin-transform-arrow-functions": "^7.24.7",
"cac": "^6.7.14",
"esbuild": "^0.21.5",
"magic-string": "^0.30.10",
"picocolors": "^1.0.1"
}
}