This commit is contained in:
2026-04-08 21:26:18 +08:00
commit 8fdc7ac0c3
401 changed files with 53093 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
{
"globals": {}
}

View File

@@ -0,0 +1,29 @@
appId: com.cslab.dcs.editor
productName: cslab-dcs-editor
directories:
output: dist
buildResources: resources
files:
- out/**/*
- '!**/*.map'
extraMetadata:
main: out/main/index.js
nsis:
oneClick: false
perMachine: false
allowToChangeInstallationDirectory: true
deleteAppDataOnUninstall: false
mac:
target: dmg
hardenedRuntime: true
gatekeeperAssess: false
win:
icon: resources/icon.ico
target:
- target: nsis
arch:
- x64
- ia32
artifactName: '${productName}_${version}_win7-setup.${ext}'
linux:
target: AppImage

View File

@@ -0,0 +1,28 @@
import { resolve } from 'node:path'
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import { createSharedViteConfig } from '../../packages/core/vite.shared'
// Core 包的根目录
const coreRoot = resolve(__dirname, '../../packages/core')
// 使用 any 断言绕过 Vite 版本差异electron: vite@5, core: vite@6
const sharedConfig = createSharedViteConfig(coreRoot) as any
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin()],
},
preload: {
plugins: [externalizeDepsPlugin()],
},
renderer: {
...sharedConfig,
root: resolve(__dirname, 'src/renderer'),
build: {
rollupOptions: {
input: {
index: resolve(__dirname, 'src/renderer/index.html'),
},
},
},
},
})

View File

@@ -0,0 +1,36 @@
{
"name": "@cslab-dcs/electron",
"version": "1.0.0",
"description": "DCS Editor Electron App",
"author": "cslab-dcs",
"main": "./out/main/index.js",
"scripts": {
"lint": "eslint .",
"typecheck": "tsc --noEmit",
"dev": "electron-vite dev",
"build": "electron-vite build",
"postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --dir",
"build:win": "npm run build && electron-builder --win",
"build:mac": "npm run build && electron-builder --mac",
"build:linux": "npm run build && electron-builder --linux"
},
"dependencies": {
"@cslab-dcs/bridge": "workspace:*",
"@cslab-dcs/core": "workspace:*",
"@electron-toolkit/preload": "2.0.0",
"@electron-toolkit/utils": "2.0.0",
"electron-updater": "6.3.9"
},
"devDependencies": {
"@vitejs/plugin-vue": "6.0.1",
"@vitejs/plugin-vue-jsx": "4.1.1",
"electron": "22.3.27",
"electron-builder": "25.1.8",
"electron-vite": "2.3.0",
"typescript": "5.9.3",
"vite": "5.4.11",
"vue": "3.5.13",
"vue-tsc": "2.2.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

1
apps/electron/src/main/env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="electron-vite/node" />

View File

@@ -0,0 +1,201 @@
/* eslint-disable node/prefer-global/process */
import fs from 'node:fs/promises'
import os from 'node:os'
import { join } from 'node:path'
import { electronApp, is, optimizer } from '@electron-toolkit/utils'
import { app, BrowserWindow, dialog, ipcMain, shell } from 'electron'
import icon from '../../resources/icon.png?asset'
function createWindow(): void {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 900,
height: 670,
show: false,
autoHideMenuBar: true,
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: true,
contextIsolation: true,
nodeIntegration: false,
},
})
mainWindow.on('ready-to-show', () => {
mainWindow.show()
})
mainWindow.webContents.setWindowOpenHandler((details) => {
if (isUrlAllowed(details.url)) {
shell.openExternal(details.url)
}
return { action: 'deny' }
})
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env.ELECTRON_RENDERER_URL) {
mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL)
}
else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
}
}
// ── 文件路径授权管理 ──
// 只允许访问经用户通过原生对话框选择的文件路径
const authorizedPaths = new Set<string>()
const MAX_AUTHORIZED_PATHS = 200
function authorizePath(filePath: string) {
if (authorizedPaths.size >= MAX_AUTHORIZED_PATHS) {
const oldest = authorizedPaths.values().next().value
if (oldest)
authorizedPaths.delete(oldest)
}
authorizedPaths.add(filePath)
}
function isPathAuthorized(filePath: string): boolean {
return authorizedPaths.has(filePath)
}
// ── URL 协议白名单 ──
const ALLOWED_URL_PROTOCOLS = new Set(['https:', 'http:'])
function isUrlAllowed(url: string): boolean {
try {
return ALLOWED_URL_PROTOCOLS.has(new URL(url).protocol)
}
catch {
return false
}
}
// IPC Handlers
function registerIpcHandlers() {
// File — 仅允许经对话框授权的路径
ipcMain.handle('file:read', async (_, path) => {
if (!isPathAuthorized(path)) {
throw new Error('文件访问被拒绝:路径未经用户选择授权')
}
return await fs.readFile(path, 'utf-8')
})
ipcMain.handle('file:exists', async (_, path) => {
if (!isPathAuthorized(path)) {
return false
}
try {
await fs.access(path)
return true
}
catch {
return false
}
})
ipcMain.handle('file:write', async (_, path, content) => {
if (!isPathAuthorized(path)) {
throw new Error('文件写入被拒绝:路径未经用户选择授权')
}
await fs.writeFile(path, content, 'utf-8')
})
// Dialog — 记录用户选择的路径
ipcMain.handle('dialog:open', async (_, options) => {
const { filePaths } = await dialog.showOpenDialog({
title: options?.title,
defaultPath: options?.defaultPath,
filters: options?.filters,
properties: [
options?.multiple ? 'multiSelections' : 'openFile',
options?.directory ? 'openDirectory' : 'openFile',
],
})
filePaths.forEach(p => authorizePath(p))
return options?.multiple ? filePaths : filePaths[0] || null
})
ipcMain.handle('dialog:save', async (_, options) => {
const { filePath } = await dialog.showSaveDialog({
title: options?.title,
defaultPath: options?.defaultPath,
filters: options?.filters,
})
if (filePath)
authorizePath(filePath)
return filePath || null
})
ipcMain.handle('dialog:message', async (_, options) => {
await dialog.showMessageBox({
title: options?.title,
message: options?.message,
type: options?.type || 'info',
})
})
ipcMain.handle('dialog:confirm', async (_, options) => {
const { response } = await dialog.showMessageBox({
title: options?.title,
message: options?.message,
type: options?.type || 'info',
buttons: [options?.okLabel || 'Yes', options?.cancelLabel || 'No'],
defaultId: 0,
cancelId: 1,
})
return response === 0
})
// System
ipcMain.handle('app:version', () => app.getVersion())
ipcMain.handle('app:info', () => ({
name: 'electron',
version: process.versions.electron,
os: process.platform,
osVersion: os.release(),
arch: process.arch,
}))
ipcMain.handle('shell:open', (_, url) => {
if (!isUrlAllowed(url)) {
throw new Error('不允许打开此类型的链接,仅支持 HTTP/HTTPS')
}
return shell.openExternal(url)
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
// Set app user model id for windows
electronApp.setAppUserModelId('com.cslab.dcs')
// Default open or close DevTools by F12 in development
// and ignore CommandOrControl + R in production.
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
registerIpcHandlers()
createWindow()
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0)
createWindow()
})
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})

8
apps/electron/src/preload/index.d.ts vendored Normal file
View File

@@ -0,0 +1,8 @@
import type { ElectronAPI } from '@electron-toolkit/preload'
declare global {
interface Window {
electron: ElectronAPI
api: Record<string, unknown>
}
}

View File

@@ -0,0 +1,22 @@
/* eslint-disable node/prefer-global/process */
import { electronAPI } from '@electron-toolkit/preload'
import { contextBridge } from 'electron'
// Custom APIs for renderer
const api = {}
// Use `contextBridge` APIs to expose IPC renderer to the renderer process.
// Read more at https://www.electronjs.org/docs/latest/tutorial/context-isolation
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld('electron', electronAPI)
contextBridge.exposeInMainWorld('api', api)
}
catch (error) {
console.error(error)
}
}
else {
window.electron = electronAPI
window.api = api
}

View File

@@ -0,0 +1,12 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>DCS Editor (Electron)</title>
<!-- <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"> -->
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

17
apps/electron/src/renderer/src/env.d.ts vendored Normal file
View File

@@ -0,0 +1,17 @@
/// <reference types="vite/client" />
/// <reference types="element-plus/global" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<object, object, any>
export default component
}
interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}

View File

@@ -0,0 +1,5 @@
import { bridge } from '@cslab-dcs/bridge'
import { createDCSApp } from '@cslab-dcs/core'
const app = createDCSApp(bridge)
app.mount('#app')

View File

@@ -0,0 +1,8 @@
{
"references": [
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.preload.json" },
{ "path": "./tsconfig.web.json" }
],
"files": []
}

View File

@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"types": ["electron-vite/node"],
"outDir": "out"
},
"include": ["src/main/**/*"],
"exclude": ["node_modules", "out", "dist"]
}

View File

@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"types": ["electron-vite/node"],
"outDir": "out"
},
"include": ["src/preload/**/*"],
"exclude": ["node_modules", "out", "dist"]
}

View File

@@ -0,0 +1,23 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"paths": {
"@/*": ["../../packages/core/src/*"],
"@cslab-dcs/core": ["../../packages/core/src"],
"@cslab-dcs/bridge": ["../../packages/bridge/src"],
"@cslab-dcs/schema": ["../../packages/schema/src"]
},
"types": ["element-plus/global"]
},
"include": [
"src/renderer/**/*",
"../../packages/core/src/**/*",
"../../packages/bridge/src/**/*",
"../../packages/request/src/**/*",
"../../packages/schema/src/**/*",
"../../packages/utils/src/**/*"
],
"exclude": ["node_modules", "out", "dist"]
}

View File

@@ -0,0 +1,3 @@
{
"globals": {}
}

13
apps/tauri/index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DCS Editor (Tauri)</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

30
apps/tauri/package.json Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "@cslab-dcs/tauri",
"type": "module",
"version": "1.0.0",
"private": true,
"scripts": {
"dev:web": "vite dev",
"dev": "tauri dev",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"tauri": "tauri"
},
"dependencies": {
"@cslab-dcs/bridge": "workspace:*",
"@cslab-dcs/core": "workspace:*",
"@tauri-apps/api": "2.10.1",
"@tauri-apps/plugin-dialog": "^2.2.0",
"@tauri-apps/plugin-fs": "^2.2.0",
"@tauri-apps/plugin-opener": "^2.2.5",
"vue": "^3.5.13"
},
"devDependencies": {
"@tauri-apps/cli": "2.10.0",
"@vitejs/plugin-vue": "^6.0.1",
"@vitejs/plugin-vue-jsx": "^4.1.1",
"typescript": "^5.9.3",
"vite": "^6.0.3",
"vue-tsc": "^2.2.0"
}
}

5295
apps/tauri/src-tauri/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
[package]
name = "cslab-dcs-editor"
version = "1.0.0"
description = "DCS Editor - CSLAB DCS 编辑器"
authors = [ "cslab" ]
edition = "2021"
[lib]
name = "cslab_dcs_tauri_lib"
crate-type = [
"staticlib",
"cdylib",
"rlib"
]
[build-dependencies]
tauri-build = { version = "2.5.4", features = [] }
[dependencies]
tauri = { version = "2.10.0", features = [] }
tauri-plugin-opener = "2.2.5"
tauri-plugin-dialog = "2.2.0"
tauri-plugin-fs = "2.2.0"
tauri-plugin-os = "2"
serde = { version = "1", features = [ "derive" ] }
serde_json = "1"
[features]
# this feature is used for production builds or when `devPath` points to the filesystem
# DO NOT REMOVE!!
custom-protocol = [ "tauri/custom-protocol" ]

View File

@@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}

View File

@@ -0,0 +1,13 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:default",
"dialog:default",
"opener:default",
"fs:default",
"os:default"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

View File

@@ -0,0 +1,10 @@
// #[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_os::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@@ -0,0 +1,6 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
cslab_dcs_tauri_lib::run()
}

View File

@@ -0,0 +1,40 @@
{
"identifier": "com.cslab.dcs.editor",
"build": {
"beforeDevCommand": "pnpm dev:web",
"beforeBuildCommand": "pnpm build:web",
"devUrl": "http://localhost:1420",
"frontendDist": "../dist"
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
},
"app": {
"withGlobalTauri": false,
"windows": [
{
"title": "DCS Editor",
"width": 800,
"height": 600,
"dragDropEnabled": false
}
],
"security": {
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' https:; font-src 'self' data:"
}
},
"plugins": {
"fs": {}
}
}

9
apps/tauri/src/main.ts Normal file
View File

@@ -0,0 +1,9 @@
import { WebBridge } from '@cslab-dcs/bridge'
import { TauriBridge } from '@cslab-dcs/bridge/tauri'
import { createDCSApp } from '@cslab-dcs/core'
const isTauri = !!(window as any).__TAURI_INTERNALS__ || !!(window as any).__TAURI__
const bridge = isTauri ? new TauriBridge() : new WebBridge()
const app = createDCSApp(bridge)
app.mount('#app')

23
apps/tauri/tsconfig.json Normal file
View File

@@ -0,0 +1,23 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["../../packages/core/src/*"],
"@cslab-dcs/core": ["../../packages/core/src"],
"@cslab-dcs/bridge": ["../../packages/bridge/src"],
"@cslab-dcs/schema": ["../../packages/schema/src"]
},
"types": ["element-plus/global"]
},
"include": [
"src/**/*",
"../../packages/core/src/**/*",
"../../packages/core/env.d.ts",
"../../packages/bridge/src/**/*",
"../../packages/request/src/**/*",
"../../packages/schema/src/**/*",
"../../packages/utils/src/**/*"
],
"exclude": ["node_modules", "dist", "src-tauri"]
}

36
apps/tauri/vite.config.ts Normal file
View File

@@ -0,0 +1,36 @@
import type { UserConfig } from 'vite'
import { resolve } from 'node:path'
import process from 'node:process'
import { defineConfig } from 'vite'
import { createSharedViteConfig } from '../../packages/core/vite.shared'
const coreRoot = resolve(__dirname, '../../packages/core')
const host = process.env.VITE_DEV_SERVER_HOST
// 使用类型断言绕过 pnpm 导致的 Vite 依赖重复安装类型不兼容问题
export default defineConfig({
...createSharedViteConfig(coreRoot) as UserConfig,
root: __dirname,
clearScreen: false,
server: {
port: 1420,
strictPort: true,
host: host || false,
hmr: host
? {
protocol: 'ws',
host,
port: 1421,
}
: undefined,
watch: {
// 3. tell vite to ignore watching `src-tauri`
ignored: ['**/src-tauri/**'],
},
},
build: {
outDir: 'dist',
emptyOutDir: true,
},
})

3
apps/web/.env Normal file
View File

@@ -0,0 +1,3 @@
# 默认环境变量配置(会被 .env.development 和 .env.production 覆盖)
# VITE_API_BASE_URL=https://cslab.oberyun.com
VITE_API_BASE_URL=http://192.168.1.110:8001

View File

@@ -0,0 +1,2 @@
# 开发环境配置
VITE_API_BASE_URL=http://192.168.1.110:8001

2
apps/web/.env.production Normal file
View File

@@ -0,0 +1,2 @@
# 生产环境配置
VITE_API_BASE_URL=https://cslab.oberyun.com

View File

@@ -0,0 +1,3 @@
{
"globals": {}
}

13
apps/web/index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DCS Editor (Web)</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

24
apps/web/package.json Normal file
View File

@@ -0,0 +1,24 @@
{
"name": "@cslab-dcs/web",
"type": "module",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "vite --host",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"typecheck": "vue-tsc --noEmit"
},
"dependencies": {
"@cslab-dcs/bridge": "workspace:*",
"@cslab-dcs/core": "workspace:*",
"vue": "^3.5.13"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.1",
"@vitejs/plugin-vue-jsx": "^4.1.1",
"typescript": "^5.9.3",
"vite": "^6.0.3",
"vue-tsc": "^2.2.0"
}
}

16
apps/web/src/env.d.ts vendored Normal file
View File

@@ -0,0 +1,16 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<object, object, any>
export default component
}
interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}

5
apps/web/src/main.ts Normal file
View File

@@ -0,0 +1,5 @@
import { bridge } from '@cslab-dcs/bridge'
import { createDCSApp } from '@cslab-dcs/core'
const app = createDCSApp(bridge)
app.mount('#app')

24
apps/web/tsconfig.json Normal file
View File

@@ -0,0 +1,24 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["../../packages/core/src/*"],
"@cslab-dcs/core": ["../../packages/core/src"],
"@cslab-dcs/bridge": ["../../packages/bridge/src"],
"@cslab-dcs/schema": ["../../packages/schema/src"]
},
"types": ["element-plus/global"]
},
"include": [
"src/**/*",
"../../packages/core/src/**/*",
"../../packages/core/src/auto-imports.d.ts",
"../../packages/core/env.d.ts",
"../../packages/bridge/src/**/*",
"../../packages/request/src/**/*",
"../../packages/schema/src/**/*",
"../../packages/utils/src/**/*"
],
"exclude": ["node_modules", "dist"]
}

31
apps/web/unocss.config.ts Normal file
View File

@@ -0,0 +1,31 @@
import {
defineConfig,
presetAttributify,
presetIcons,
presetTypography,
presetUno,
transformerDirectives,
transformerVariantGroup,
} from 'unocss'
import presetChinese from 'unocss-preset-chinese'
import presetEase from 'unocss-preset-ease'
export default defineConfig({
presets: [
presetUno(),
presetAttributify(),
presetChinese(),
presetEase(),
presetTypography(),
presetIcons({
scale: 1.2,
warn: true,
}),
],
shortcuts: [
['flex-center', 'flex items-center justify-center'],
['flex-between', 'flex items-center justify-between'],
['flex-end', 'flex items-end justify-between'],
],
transformers: [transformerDirectives(), transformerVariantGroup()],
})

25
apps/web/vite.config.ts Normal file
View File

@@ -0,0 +1,25 @@
import type { UserConfig } from 'vite'
import { resolve } from 'node:path'
import { defineConfig, loadEnv, mergeConfig } from 'vite'
import { createSharedViteConfig } from '../../packages/core/vite.shared'
// Core 包的根目录
const coreRoot = resolve(__dirname, '../../packages/core')
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, coreRoot, 'VITE_')
const sharedConfig = createSharedViteConfig(coreRoot, env) as UserConfig
return mergeConfig(sharedConfig, {
root: __dirname,
base: env.VITE_BASE_URL, // 确保相对路径,方便部署
build: {
outDir: 'dist',
emptyOutDir: true,
},
server: {
port: 5173,
},
})
})