mirror of
https://github.com/Darkatse/TauriTavern.git
synced 2026-10-19 07:35:14 +00:00
build: migrate frontend bundling from webpack to Rspack
This commit is contained in:
parent
451a0e7897
commit
bfbb1bc5b7
15 changed files with 283 additions and 989 deletions
|
|
@ -48,8 +48,8 @@ src/
|
|||
├── tauri-main.js # 轻量引导入口
|
||||
├── init.js # 启动编排器
|
||||
├── lib.js # 库门面(ESM 导出)
|
||||
├── dist/lib.core.bundle.js # webpack 构建的核心依赖包(启动必需)
|
||||
├── dist/lib.optional.bundle.js # webpack 构建的可选依赖包(按需加载)
|
||||
├── dist/lib.core.bundle.js # Rspack 构建的核心依赖包(启动必需)
|
||||
├── dist/lib.optional.bundle.js # Rspack 构建的可选依赖包(按需加载)
|
||||
└── tauri/main/
|
||||
├── bootstrap.js # 组合根(composition root)
|
||||
├── context.js # 兼容 shim(re-export `context/index`)
|
||||
|
|
@ -75,7 +75,7 @@ src/
|
|||
前置要求:
|
||||
|
||||
- Rust stable
|
||||
- Node.js 18+
|
||||
- Node.js 22.12+
|
||||
- pnpm
|
||||
- Tauri CLI
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ pnpm install
|
|||
|
||||
```bash
|
||||
pnpm run check # guardrails + host kernel 类型检查(推荐每次改动后先跑)
|
||||
pnpm run web:build # 构建前端资源包(webpack)
|
||||
pnpm run web:build # 构建前端资源包(Rspack)
|
||||
pnpm run dev # 桌面开发模式(等价 tauri:dev)
|
||||
pnpm run tauri:dev # 桌面开发模式
|
||||
pnpm run tauri:build # 构建桌面发行包
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ src/
|
|||
├── tauri-main.js # thin bootstrap entry
|
||||
├── init.js # startup orchestrator
|
||||
├── lib.js # library facade (ESM exports)
|
||||
├── dist/lib.core.bundle.js # webpack-built core vendor bundle (startup-critical)
|
||||
├── dist/lib.optional.bundle.js # webpack-built optional vendor bundle (on-demand)
|
||||
├── dist/lib.core.bundle.js # Rspack-built core vendor bundle (startup-critical)
|
||||
├── dist/lib.optional.bundle.js # Rspack-built optional vendor bundle (on-demand)
|
||||
└── tauri/main/
|
||||
├── bootstrap.js # composition root
|
||||
├── context.js # compatibility shim (re-export `context/index`)
|
||||
|
|
@ -73,7 +73,7 @@ src/
|
|||
Prerequisites:
|
||||
|
||||
- Rust stable
|
||||
- Node.js 18+
|
||||
- Node.js 22.12+
|
||||
- pnpm
|
||||
- Tauri CLI
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ Common commands:
|
|||
|
||||
```bash
|
||||
pnpm run check # guardrails + host-kernel type checks (recommended)
|
||||
pnpm run web:build # build frontend bundles (webpack)
|
||||
pnpm run web:build # build frontend bundles (Rspack)
|
||||
pnpm run dev # desktop dev mode (alias of tauri:dev)
|
||||
pnpm run tauri:dev # desktop dev mode
|
||||
pnpm run tauri:build # build desktop installers
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@
|
|||
|
||||
### 6.1 产物与入口
|
||||
|
||||
- webpack 入口:`webpack.config.js`
|
||||
- Rspack 入口:`rspack.config.js`
|
||||
- `lib.core` → `src/lib-bundle-core.js` → `src/dist/lib.core.bundle.js`
|
||||
- `lib.optional` → `src/lib-bundle-optional.js` → `src/dist/lib.optional.bundle.js`
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
- **框架**: 保留SillyTavern原有的前端代码
|
||||
- **核心技术**: HTML, CSS, JavaScript (jQuery)
|
||||
- **构建工具**: Webpack (用于库打包)
|
||||
- **构建工具**: Rspack (用于前端资源打包)
|
||||
|
||||
## 2. 后端技术栈
|
||||
|
||||
|
|
@ -78,8 +78,7 @@
|
|||
|
||||
### 3.2 前端构建工具
|
||||
|
||||
- **Webpack**: 用于打包第三方库
|
||||
- **Babel**: JavaScript转译
|
||||
- **Rspack**: 用于打包第三方库与 TauriTavern 前端入口
|
||||
|
||||
### 3.3 前端与后端通信
|
||||
|
||||
|
|
@ -160,7 +159,7 @@
|
|||
- **资源加载**: 优化资源加载顺序
|
||||
- **库注入链路**: `lib.js` 静态导入 `src/dist/lib.core.bundle.js`;重库通过 `getHljs()/getReadability()` 动态加载 `src/dist/lib.optional.bundle.js`
|
||||
- **延迟加载**: 非关键资源延迟加载
|
||||
- **构建缓存**: webpack filesystem cache 提升增量构建速度
|
||||
- **构建缓存**: Rspack persistent cache 提升增量构建速度
|
||||
|
||||
## 8. 兼容性和迁移
|
||||
|
||||
|
|
|
|||
14
package.json
14
package.json
|
|
@ -16,8 +16,8 @@
|
|||
"check:contracts": "pnpm run test:contracts",
|
||||
"test": "pnpm run test:contracts",
|
||||
"test:contracts": "node --test --test-concurrency=1 \"tests/**/*.test.mjs\"",
|
||||
"web:build": "webpack --config webpack.config.js",
|
||||
"web:dev": "webpack --config webpack.config.js --watch",
|
||||
"web:build": "rspack build --config rspack.config.js",
|
||||
"web:dev": "rspack build --config rspack.config.js --watch",
|
||||
"tauri:dev": "tauri dev",
|
||||
"tauri:build": "tauri build",
|
||||
"tauri:build:portable": "node scripts/build-portable.mjs",
|
||||
|
|
@ -32,19 +32,19 @@
|
|||
"fastools:run": "cargo run --manifest-path fastools/Cargo.toml"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rspack/cli": "^2.0.8",
|
||||
"@rspack/core": "^2.0.8",
|
||||
"@tauri-apps/cli": "^2.10.0",
|
||||
"@types/jquery": "^3.5.33",
|
||||
"@types/jquery-cropper": "^1.0.4",
|
||||
"@types/jquery.transit": "^0.9.33",
|
||||
"@types/jqueryui": "^1.12.24",
|
||||
"@types/lodash": "^4.17.24",
|
||||
"@types/node": "^18.19.84",
|
||||
"@types/node": "^20.19.43",
|
||||
"@types/select2": "^4.0.63",
|
||||
"@types/toastr": "^2.1.43",
|
||||
"es-module-lexer": "^2.0.0",
|
||||
"typescript": "^5.9.3",
|
||||
"webpack": "^5.105.4",
|
||||
"webpack-cli": "^5.1.4"
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@adobe/css-tools": "^4.4.4",
|
||||
|
|
@ -70,6 +70,6 @@
|
|||
"vue": "^3.5.33"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1126
pnpm-lock.yaml
generated
1126
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,7 @@ import crypto from 'crypto';
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import webpack from 'webpack';
|
||||
import * as rspack from '@rspack/core';
|
||||
|
||||
// Get the directory name of the current module
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
|
|
@ -11,7 +11,7 @@ const __dirname = path.dirname(__filename);
|
|||
const cacheEnvironment = `${process.platform}-${process.arch}-node${process.versions.node.split('.')[0]}`;
|
||||
|
||||
const commonCacheInputs = [
|
||||
'webpack.config.js',
|
||||
'rspack.config.js',
|
||||
'package.json',
|
||||
'pnpm-lock.yaml',
|
||||
];
|
||||
|
|
@ -71,7 +71,7 @@ function buildCacheVersion(name, inputFiles) {
|
|||
hash.update(`platform=${process.platform}\n`);
|
||||
hash.update(`arch=${process.arch}\n`);
|
||||
hash.update(`node=${process.versions.node}\n`);
|
||||
hash.update(`webpack=${webpack.version}\n`);
|
||||
hash.update(`rspack=${rspack.rspackVersion}\n`);
|
||||
|
||||
for (const file of inputFiles) {
|
||||
hash.update(`file=${file}\n`);
|
||||
|
|
@ -82,15 +82,14 @@ function buildCacheVersion(name, inputFiles) {
|
|||
return hash.digest('hex');
|
||||
}
|
||||
|
||||
function createFilesystemCache(name, inputFiles) {
|
||||
function createPersistentCache(name, inputFiles) {
|
||||
return {
|
||||
type: 'filesystem',
|
||||
name,
|
||||
cacheDirectory: path.resolve(__dirname, '.cache/webpack', cacheEnvironment, name),
|
||||
type: 'persistent',
|
||||
version: buildCacheVersion(name, inputFiles),
|
||||
buildDependencies: {
|
||||
config: [__filename],
|
||||
inputs: inputFiles.map(resolveRepoPath),
|
||||
buildDependencies: commonCacheInputs.map(resolveRepoPath),
|
||||
storage: {
|
||||
type: 'filesystem',
|
||||
directory: path.resolve(__dirname, '.cache/rspack', cacheEnvironment, name),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -128,8 +127,26 @@ const sharedPerformance = {
|
|||
maxAssetSize: 5120000
|
||||
};
|
||||
|
||||
const sharedStats = {
|
||||
preset: 'normal',
|
||||
assets: true,
|
||||
chunks: true,
|
||||
modules: true,
|
||||
entrypoints: true,
|
||||
timings: true,
|
||||
builtAt: true,
|
||||
logging: 'warn',
|
||||
cachedAssets: false,
|
||||
cachedModules: false,
|
||||
chunkModules: false,
|
||||
assetsSort: '!size',
|
||||
modulesSort: '!size',
|
||||
assetsSpace: 20,
|
||||
modulesSpace: 20,
|
||||
};
|
||||
|
||||
function createVueDefinePlugin() {
|
||||
return new webpack.DefinePlugin({
|
||||
return new rspack.DefinePlugin({
|
||||
__VUE_OPTIONS_API__: JSON.stringify(true),
|
||||
__VUE_PROD_DEVTOOLS__: JSON.stringify(false),
|
||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: JSON.stringify(false),
|
||||
|
|
@ -139,8 +156,9 @@ function createVueDefinePlugin() {
|
|||
const coreConfig = {
|
||||
name: 'vendor-libs',
|
||||
mode: 'production',
|
||||
bail: true,
|
||||
target: ['web', 'es2020'],
|
||||
cache: createFilesystemCache('vendor-libs', libraryCacheInputs),
|
||||
cache: createPersistentCache('vendor-libs', libraryCacheInputs),
|
||||
entry: {
|
||||
'lib.core': './src/lib-bundle-core.js',
|
||||
'lib.optional': './src/lib-bundle-optional.js',
|
||||
|
|
@ -148,41 +166,40 @@ const coreConfig = {
|
|||
output: {
|
||||
filename: '[name].bundle.js',
|
||||
path: path.resolve(__dirname, 'src/dist'),
|
||||
module: true,
|
||||
library: {
|
||||
type: 'module'
|
||||
}
|
||||
},
|
||||
experiments: {
|
||||
outputModule: true,
|
||||
},
|
||||
resolve: sharedResolve,
|
||||
optimization: sharedOptimization,
|
||||
performance: sharedPerformance,
|
||||
stats: sharedStats,
|
||||
};
|
||||
|
||||
const agentSystemConfig = {
|
||||
name: 'agent-system',
|
||||
dependencies: ['vendor-libs'],
|
||||
mode: 'production',
|
||||
bail: true,
|
||||
target: ['web', 'es2020'],
|
||||
cache: createFilesystemCache('agent-system', agentSystemCacheInputs),
|
||||
cache: createPersistentCache('agent-system', agentSystemCacheInputs),
|
||||
entry: {
|
||||
index: './src/scripts/extensions/agent-system/src/index.js',
|
||||
},
|
||||
output: {
|
||||
filename: '[name].bundle.js',
|
||||
path: path.resolve(__dirname, 'src/scripts/extensions/agent-system/dist'),
|
||||
module: true,
|
||||
library: {
|
||||
type: 'module'
|
||||
},
|
||||
clean: true,
|
||||
},
|
||||
experiments: {
|
||||
outputModule: true,
|
||||
},
|
||||
resolve: sharedResolve,
|
||||
optimization: sharedOptimization,
|
||||
performance: sharedPerformance,
|
||||
stats: sharedStats,
|
||||
plugins: [
|
||||
createVueDefinePlugin(),
|
||||
],
|
||||
|
|
@ -192,8 +209,9 @@ const tauriTavernSettingsConfig = {
|
|||
name: 'tauritavern-settings',
|
||||
dependencies: ['vendor-libs'],
|
||||
mode: 'production',
|
||||
bail: true,
|
||||
target: ['web', 'es2020'],
|
||||
cache: createFilesystemCache('tauritavern-settings', tauriSettingUiCacheInputs),
|
||||
cache: createPersistentCache('tauritavern-settings', tauriSettingUiCacheInputs),
|
||||
entry: {
|
||||
settings: './src/scripts/tauri/setting/settings-app/index.js',
|
||||
'dev-logs': './src/scripts/tauri/setting/dev-logs-app/index.js',
|
||||
|
|
@ -202,17 +220,16 @@ const tauriTavernSettingsConfig = {
|
|||
output: {
|
||||
filename: '[name].bundle.js',
|
||||
path: path.resolve(__dirname, 'src/scripts/tauri/setting/dist'),
|
||||
module: true,
|
||||
library: {
|
||||
type: 'module'
|
||||
},
|
||||
clean: true,
|
||||
},
|
||||
experiments: {
|
||||
outputModule: true,
|
||||
},
|
||||
resolve: sharedResolve,
|
||||
optimization: sharedOptimization,
|
||||
performance: sharedPerformance,
|
||||
stats: sharedStats,
|
||||
plugins: [
|
||||
createVueDefinePlugin(),
|
||||
],
|
||||
|
|
@ -14,7 +14,7 @@ function printHelp() {
|
|||
Options:
|
||||
--target <triple> Rust target triple, e.g. x86_64-pc-windows-msvc
|
||||
--output-dir <path> Portable output directory (default: release)
|
||||
--skip-web-build Skip frontend webpack build
|
||||
--skip-web-build Skip frontend bundle build
|
||||
--help Show this help message
|
||||
`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Library module facade for TauriTavern.
|
||||
*
|
||||
* We load a small core webpack bundle (`src/dist/lib.core.bundle.js`) and
|
||||
* We load a small core Rspack bundle (`src/dist/lib.core.bundle.js`) and
|
||||
* re-export the libraries expected by SillyTavern frontend modules.
|
||||
*
|
||||
* Heavy / feature-specific libraries live in `src/dist/lib.optional.bundle.js`
|
||||
|
|
|
|||
|
|
@ -64,7 +64,8 @@ async function loadRollbackScript() {
|
|||
if (rollbackScriptOverride) {
|
||||
return rollbackScriptOverride;
|
||||
}
|
||||
return import(/* webpackIgnore: true */ '/script.js');
|
||||
// Rspack supports webpackIgnore and leaves this host runtime import native.
|
||||
return import('/script.js' /* webpackIgnore: true */);
|
||||
}
|
||||
|
||||
export function __setAgentRunRollbackScriptForTests(script) {
|
||||
|
|
|
|||
|
|
@ -69,10 +69,11 @@ function assertRetryRuntime(runtime) {
|
|||
}
|
||||
|
||||
async function defaultRetryRuntime() {
|
||||
// Rspack supports webpackIgnore and leaves these host runtime imports native.
|
||||
const [script, groupChats, router] = await Promise.all([
|
||||
import(/* webpackIgnore: true */ '/script.js'),
|
||||
import(/* webpackIgnore: true */ '/scripts/group-chats.js'),
|
||||
import(/* webpackIgnore: true */ '/scripts/tauritavern/agent/agent-generation-router.js'),
|
||||
import('/script.js' /* webpackIgnore: true */),
|
||||
import('/scripts/group-chats.js' /* webpackIgnore: true */),
|
||||
import('/scripts/tauritavern/agent/agent-generation-router.js' /* webpackIgnore: true */),
|
||||
]);
|
||||
return {
|
||||
Generate: script.Generate,
|
||||
|
|
|
|||
|
|
@ -1720,6 +1720,22 @@ test('Agent System profile panel does not statically bundle main app modules', a
|
|||
assert.match(modelTargetConnectionSource, /requireSillyTavernContext/);
|
||||
});
|
||||
|
||||
test('Agent run helpers keep main runtime imports native for the extension bundle', async () => {
|
||||
const controllerSource = await readFile(path.join(
|
||||
REPO_ROOT,
|
||||
'src/scripts/tauritavern/agent/agent-run-controller.js',
|
||||
), 'utf8');
|
||||
const retrySource = await readFile(path.join(
|
||||
REPO_ROOT,
|
||||
'src/scripts/tauritavern/agent/agent-run-retry.js',
|
||||
), 'utf8');
|
||||
|
||||
assert.match(controllerSource, /import\(['"]\/script\.js['"]\s*\/\*\s*webpackIgnore:\s*true\s*\*\/\)/);
|
||||
assert.match(retrySource, /import\(['"]\/script\.js['"]\s*\/\*\s*webpackIgnore:\s*true\s*\*\/\)/);
|
||||
assert.match(retrySource, /import\(['"]\/scripts\/group-chats\.js['"]\s*\/\*\s*webpackIgnore:\s*true\s*\*\/\)/);
|
||||
assert.match(retrySource, /import\(['"]\/scripts\/tauritavern\/agent\/agent-generation-router\.js['"]\s*\/\*\s*webpackIgnore:\s*true\s*\*\/\)/);
|
||||
});
|
||||
|
||||
test('Skill extension resolves active scoped sections from SillyTavern context', async () => {
|
||||
const hostWindow = installWindow({});
|
||||
hostWindow.SillyTavern = {
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ test('TauriTavern Dev Logs wrapper owns the host ABI boundary', async () => {
|
|||
assert.doesNotMatch(source, /devlog_/);
|
||||
});
|
||||
|
||||
test('webpack exposes a dedicated TauriTavern Dev Logs Vue entry', async () => {
|
||||
const source = await readRepoFile('webpack.config.js');
|
||||
test('Rspack exposes a dedicated TauriTavern Dev Logs Vue entry', async () => {
|
||||
const source = await readRepoFile('rspack.config.js');
|
||||
|
||||
assert.match(source, /['"]dev-logs['"]:\s*['"]\.\/src\/scripts\/tauri\/setting\/dev-logs-app\/index\.js['"]/);
|
||||
assert.match(source, /listJavaScriptFiles\(['"]src\/scripts\/tauri\/setting\/dev-logs-app['"]\)/);
|
||||
|
|
|
|||
|
|
@ -51,13 +51,13 @@ test('TauriTavern Settings wallpaper options use the no-render background refres
|
|||
assert.doesNotMatch(source, /getBackgrounds/);
|
||||
});
|
||||
|
||||
test('webpack exposes a dedicated TauriTavern Settings Vue entry', async () => {
|
||||
const source = await readRepoFile('webpack.config.js');
|
||||
test('Rspack exposes a dedicated TauriTavern Settings Vue entry', async () => {
|
||||
const source = await readRepoFile('rspack.config.js');
|
||||
|
||||
assert.match(source, /name:\s*['"]tauritavern-settings['"]/);
|
||||
assert.match(source, /settings:\s*['"]\.\/src\/scripts\/tauri\/setting\/settings-app\/index\.js['"]/);
|
||||
assert.match(source, /src\/scripts\/tauri\/setting\/dist/);
|
||||
assert.match(source, /createFilesystemCache\(['"]tauritavern-settings['"],\s*tauriSettingUiCacheInputs\)/);
|
||||
assert.match(source, /createPersistentCache\(['"]tauritavern-settings['"],\s*tauriSettingUiCacheInputs\)/);
|
||||
assert.match(source, /createVueDefinePlugin\(\)/);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ test('TauriTavern Sync listeners keep event contract while delegating progress U
|
|||
assert.doesNotMatch(source, /from\s+['"]vue(?:\/|['"])/);
|
||||
});
|
||||
|
||||
test('webpack exposes a dedicated TauriTavern Sync Vue entry', async () => {
|
||||
const source = await readRepoFile('webpack.config.js');
|
||||
test('Rspack exposes a dedicated TauriTavern Sync Vue entry', async () => {
|
||||
const source = await readRepoFile('rspack.config.js');
|
||||
|
||||
assert.match(source, /sync:\s*['"]\.\/src\/scripts\/tauri\/setting\/sync-app\/index\.js['"]/);
|
||||
assert.match(source, /listJavaScriptFiles\(['"]src\/scripts\/tauri\/setting\/sync-app['"]\)/);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue