Files
WeFlow/vite.config.ts
ztdd88 7f4f3c2eb0 修复 silk-wasm 被错误内联导致 macOS 语音转写崩溃 (#943)
将 'silk-wasm' 加入主进程 vite external 列表。

silk-wasm 默认会被 rollup 内联进 main.js,CI 构建环境下其内部
import_meta_url 变量被错误优化为 {}.url(即 undefined),
触发 createRequire(undefined) 报错,SILK 解码失败,
语音转写功能完全不可用。

silk-wasm 本身已在 package.json 的 asarUnpack 中(line 184-185),
强制 external 后正常走 require() 从 unpacked 加载,与原意图对齐。

本地 macOS Apple Silicon 验证:
- 构建产物 main.js 已 0 内联(grep tencent silk = 0)
- require("silk-wasm") 动态保留
- 实测语音转写恢复正常
2026-05-11 18:18:41 +08:00

254 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import electron from 'vite-plugin-electron'
import renderer from 'vite-plugin-electron-renderer'
import { resolve } from 'path'
const handleElectronOnStart = (options: { reload: () => void }) => {
options.reload()
}
const exportWorkerElectronShimPlugin = () => {
const virtualId = 'virtual:weflow-export-worker-electron'
const resolvedVirtualId = `\0${virtualId}`
return {
name: 'weflow-export-worker-electron-shim',
enforce: 'pre' as const,
resolveId(id: string) {
if (id === virtualId) return resolvedVirtualId
return null
},
load(id: string) {
if (id !== resolvedVirtualId) return null
return `
import { homedir, tmpdir } from 'os'
import { join } from 'path'
const workerUserDataPath = () => String(process.env.WEFLOW_USER_DATA_PATH || process.env.WEFLOW_CONFIG_CWD || '').trim()
const appDataPath = () => {
if (process.platform === 'win32' && process.env.APPDATA) return process.env.APPDATA
if (process.platform === 'darwin') return join(homedir(), 'Library', 'Application Support')
return process.env.XDG_CONFIG_HOME || join(homedir(), '.config')
}
const getPath = (name) => {
if (name === 'userData') return workerUserDataPath() || join(appDataPath(), 'WeFlow')
if (name === 'documents') return join(homedir(), 'Documents')
if (name === 'desktop') return join(homedir(), 'Desktop')
if (name === 'downloads') return join(homedir(), 'Downloads')
if (name === 'temp') return tmpdir()
if (name === 'appData') return appDataPath()
return process.cwd()
}
export const app = {
isPackaged: Boolean(process.resourcesPath && process.env.NODE_ENV !== 'development'),
getPath,
getAppPath: () => process.cwd(),
getName: () => 'WeFlow',
getVersion: () => process.env.npm_package_version || '0.0.0'
}
export const BrowserWindow = { getAllWindows: () => [] }
export const dialog = { showMessageBox: async () => ({ response: 0, checkboxChecked: false }) }
export const shell = { openExternal: async () => false, showItemInFolder: () => {} }
export const ipcMain = { on: () => {}, handle: () => {}, removeHandler: () => {} }
export const ipcRenderer = { sendSync: () => ({}) }
export const safeStorage = {
isEncryptionAvailable: () => false,
encryptString: (value) => Buffer.from(String(value || ''), 'utf8'),
decryptString: (value) => Buffer.isBuffer(value) ? value.toString('utf8') : Buffer.from(value).toString('utf8')
}
export const Notification = class {
static isSupported() { return false }
on() { return this }
show() {}
close() {}
}
export default { app, BrowserWindow, dialog, shell, ipcMain, ipcRenderer, safeStorage, Notification }
`
},
transform(code: string, id: string) {
if (!/\.[cm]?[jt]s$/.test(id)) return null
if (!code.includes("'electron'") && !code.includes('"electron"')) return null
const next = code
.replace(/from\s+(['"])electron\1/g, `from '${virtualId}'`)
.replace(/import\s*\(\s*(['"])electron\1\s*\)/g, `import('${virtualId}')`)
.replace(/require\s*\(\s*(['"])electron\1\s*\)/g, `require('${virtualId}')`)
return next === code ? null : { code: next, map: null }
}
}
}
export default defineConfig({
base: './',
server: {
port: 3000,
strictPort: false // 如果3000被占用自动尝试下一个
},
build: {
chunkSizeWarningLimit: 900,
commonjsOptions: {
ignoreDynamicRequires: true
}
},
optimizeDeps: {
exclude: []
},
plugins: [
react(),
electron([
{
entry: 'electron/main.ts',
onstart: handleElectronOnStart,
vite: {
build: {
outDir: 'dist-electron',
rollupOptions: {
external: [
'better-sqlite3',
'koffi',
'fsevents',
'whisper-node',
'shelljs',
'exceljs',
'node-llama-cpp',
'@vscode/sudo-prompt',
'silk-wasm'
]
}
}
}
},
{
entry: 'electron/annualReportWorker.ts',
onstart: handleElectronOnStart,
vite: {
build: {
outDir: 'dist-electron',
rollupOptions: {
external: [
'koffi',
'fsevents'
],
output: {
entryFileNames: 'annualReportWorker.js',
inlineDynamicImports: true
}
}
}
}
},
{
entry: 'electron/dualReportWorker.ts',
onstart: handleElectronOnStart,
vite: {
build: {
outDir: 'dist-electron',
rollupOptions: {
external: [
'koffi',
'fsevents'
],
output: {
entryFileNames: 'dualReportWorker.js',
inlineDynamicImports: true
}
}
}
}
},
{
entry: 'electron/imageSearchWorker.ts',
onstart: handleElectronOnStart,
vite: {
build: {
outDir: 'dist-electron',
rollupOptions: {
output: {
entryFileNames: 'imageSearchWorker.js',
inlineDynamicImports: true
}
}
}
}
},
{
entry: 'electron/wcdbWorker.ts',
onstart: handleElectronOnStart,
vite: {
build: {
outDir: 'dist-electron',
rollupOptions: {
external: [
'better-sqlite3',
'koffi',
'fsevents'
],
output: {
entryFileNames: 'wcdbWorker.js',
inlineDynamicImports: true
}
}
}
}
},
{
entry: 'electron/transcribeWorker.ts',
onstart: handleElectronOnStart,
vite: {
build: {
outDir: 'dist-electron',
rollupOptions: {
external: [
'sherpa-onnx-node'
],
output: {
entryFileNames: 'transcribeWorker.js',
inlineDynamicImports: true
}
}
}
}
},
{
entry: 'electron/exportWorker.ts',
onstart: handleElectronOnStart,
vite: {
plugins: [exportWorkerElectronShimPlugin()],
build: {
outDir: 'dist-electron',
rollupOptions: {
external: [
'better-sqlite3',
'koffi',
'fsevents',
'exceljs'
],
output: {
entryFileNames: 'exportWorker.js',
inlineDynamicImports: true
}
}
}
}
},
{
entry: 'electron/preload.ts',
onstart: handleElectronOnStart,
vite: {
build: {
outDir: 'dist-electron'
}
}
}
]),
renderer()
],
resolve: {
dedupe: ['react', 'react-dom'],
alias: {
'@': resolve(__dirname, 'src')
}
}
})