Browse Source

feat: Update build scripts and enhance environment configuration

- Refactored build scripts in package.json for better clarity and functionality.
- Added new environment variables and paths for production and development setups.
- Improved chat component with dynamic message handling and error management.
- Updated dependencies in package.json for enhanced performance and compatibility.
mono
谢亚昕 2 months ago
parent
commit
848257759b
  1. BIN
      bun.lockb
  2. 4
      internal/helper/src/env.ts
  3. 25
      internal/helper/src/path.ts
  4. 9
      package.json
  5. 90
      packages/client/src/components/ChatBox/index.vue
  6. 4
      packages/client/src/components/ChatBox/prompt.txt
  7. 11
      packages/client/src/composables/useChat/index.ts
  8. 6
      packages/client/src/vue.d.ts
  9. 2
      packages/client/vite.config.ts
  10. 15
      packages/core/src/SsrMiddleWare.ts
  11. 4
      packages/server/package.json
  12. 14
      packages/server/src/jobs/index.ts
  13. 10
      packages/server/src/logger.ts
  14. 6
      packages/server/src/middleware/install.ts
  15. 65
      scripts/build.js
  16. 20
      tsup.config.ts
  17. 18
      tsup.jobs.config.ts
  18. 18
      tsup.modules.config.ts

BIN
bun.lockb

Binary file not shown.

4
internal/helper/src/env.ts

@ -2,12 +2,10 @@
const isProduction = process.env.NODE_ENV === 'production' const isProduction = process.env.NODE_ENV === 'production'
const port = process.env.PORT || 5173 const port = process.env.PORT || 5173
const base = process.env.BASE || '/' const base = process.env.BASE || '/'
const LOG_DIR = process.env.LOG_DIR || 'logs'
export const Env = { export const Env = {
isProduction, isProduction,
port: Number(port), port: Number(port),
base, base
LOG_DIR,
} }

25
internal/helper/src/path.ts

@ -1,23 +1,18 @@
import path from "node:path" import path from "node:path"
import fs from "node:fs/promises" import fs from "node:fs/promises"
const isProduction = process.env.NODE_ENV === 'production' export const isProduction = process.env.NODE_ENV === 'production'
export function getPathByRoot(...argus: string[]) { export function getPathByRoot(...argus: string[]) {
return path.resolve(import.meta.dir, '../../..', ...argus) return path.resolve(import.meta.dir, '../../..', ...argus)
} }
const templateHtml = isProduction // 生产环境路径配置
? await fs.readFile(getPathByRoot('packages', 'client/index.html'), 'utf-8') export const TemplateHtml = isProduction ? await fs.readFile('./client/index.html', 'utf-8') : ""
: '' export const serverPublic = path.resolve("./public")
export const serverModules = path.resolve("./modules")
export function getDevPathFromClient(...argus: string[]) { export const jobsDir = path.resolve("./jobs/jobs")
return getPathByRoot('packages', 'client', ...argus) export const clientRoot = path.resolve("./client")
} export const ssrManifest = path.resolve('./client/.vite/ssr-manifest.json')
export function getDevPathFromServer(...argus: string[]) { export const entryServer = path.resolve('./server/entry-server.js')
return getPathByRoot('packages', 'server', ...argus) export const logDir = path.resolve('./logs')
}
export function getProdPath(...argus: string[]) {
return getPathByRoot('dist', ...argus)
}

9
package.json

@ -8,8 +8,10 @@
"scripts": { "scripts": {
"postinstall": "node scripts/fix-type-router.js", "postinstall": "node scripts/fix-type-router.js",
"dev": "bun run --hot packages/server/src/booststap.ts", "dev": "bun run --hot packages/server/src/booststap.ts",
"build": "bun run --filter client build", "build:client": "bun run --filter client build",
"preview": "cross-env NODE_ENV=production bun run packages/server/src/booststap.ts", "start": "cd dist && cross-env NODE_ENV=production bun run booststap.js",
"build:all": "tsup --config tsup.config.ts",
"build": "rimraf dist && bun run build:client && bun run build:all && node scripts/build.js",
"tsc:booststap": "tsc packages/booststap/src/server.ts --outDir dist --module es2022 --target es2022 --lib es2022,dom --moduleResolution bundler --esModuleInterop --skipLibCheck --forceConsistentCasingInFileNames --noEmit false --incremental false", "tsc:booststap": "tsc packages/booststap/src/server.ts --outDir dist --module es2022 --target es2022 --lib es2022,dom --moduleResolution bundler --esModuleInterop --skipLibCheck --forceConsistentCasingInFileNames --noEmit false --incremental false",
"tsc:server": "tsc packages/server/src/**/*.ts --outDir dist/server --module es2022 --target es2022 --lib es2022,dom --moduleResolution bundler --esModuleInterop --skipLibCheck --forceConsistentCasingInFileNames --noEmit false --incremental false" "tsc:server": "tsc packages/server/src/**/*.ts --outDir dist/server --module es2022 --target es2022 --lib es2022,dom --moduleResolution bundler --esModuleInterop --skipLibCheck --forceConsistentCasingInFileNames --noEmit false --incremental false"
}, },
@ -19,8 +21,11 @@
"client": "workspace:*", "client": "workspace:*",
"core": "workspace:*", "core": "workspace:*",
"cross-env": "^10.1.0", "cross-env": "^10.1.0",
"fast-glob": "^3.3.3",
"helper": "workspace:*", "helper": "workspace:*",
"rimraf": "^6.0.1",
"server": "workspace:*", "server": "workspace:*",
"tsup": "^8.5.0",
"unplugin-vue-components": "^29.1.0", "unplugin-vue-components": "^29.1.0",
"vite-plugin-devtools-json": "^1.0.0", "vite-plugin-devtools-json": "^1.0.0",
"x": "workspace:*" "x": "workspace:*"

90
packages/client/src/components/ChatBox/index.vue

@ -11,8 +11,13 @@ const { scrollToBottom } = useScroll({
}); });
// import sseDataModule from "./_/sseData.ts"; // import sseDataModule from "./_/sseData.ts";
interface IMsg <T = "user">{
const msgList = ref<{ role: "user" | "assistant" | "system", content: string, reasoning_content?: string }[]>([ role: "user" | "assistant" | "system",
content: T extends "user" ? any : string,
reasoning_content?: string,
isHidden?: boolean
}
const msgList = ref<IMsg[]>([
{ {
role: "system", role: "system",
content: PromptText content: PromptText
@ -26,7 +31,21 @@ enum STATUS {
} }
const status = ref(STATUS.WAITING); const status = ref(STATUS.WAITING);
const { sendStream, getConfig, updateConfig } = useChat(); const { sendStream, getConfig, updateConfig } = useChat(Chat.ModelProvider.OpenAI, {
// DeepSeek
// model: "deepseek-chat",
// apiKey: process.env.AI_APIKEY,
// baseUrl: "https://api.deepseek.com",
// temperature: 0.8,
// siliconflow
model: "Qwen/Qwen3-8B", // tools
// model: "deepseek-ai/deepseek-vl2",
apiKey: process.env.AI_APIKEY,
baseUrl: "https://api.siliconflow.cn/v1",
temperature: 0.8,
});
const openaiConfig = reactive({ const openaiConfig = reactive({
model: getConfig().model, model: getConfig().model,
apiKey: getConfig().apiKey, apiKey: getConfig().apiKey,
@ -39,22 +58,15 @@ watch(openaiConfig, () => {
}, { deep: true }) }, { deep: true })
const inputEl = useTemplateRef("inputEl") const inputEl = useTemplateRef("inputEl")
onMounted(() => { async function sendMsg(msg: any, isHidden?: boolean) {
inputMsg.value = "列举你能的事情,标记顺序,简洁回答,并提供友好的问候。" const newMsg = `检查回答是否符合要求!!!
handleSubmit() ---
inputEl.value?.focus() ${msg}`
})
function handleSubmit() {
if(status.value === STATUS.SENDING) return
msgList.value.push({ msgList.value.push({
role: "user", role: "user",
content: inputMsg.value, content: newMsg,
isHidden: isHidden,
}); });
inputMsg.value = "";
nextTick(() => {
scrollToBottom();
});
status.value = STATUS.SENDING; status.value = STATUS.SENDING;
let contents = JSON.parse(JSON.stringify(unref(msgList))).map((v: any) => { let contents = JSON.parse(JSON.stringify(unref(msgList))).map((v: any) => {
return { return {
@ -67,23 +79,35 @@ function handleSubmit() {
content: "", content: "",
reasoning_content: "", reasoning_content: "",
}); });
sendStream(contents as any, (msg: any) => { try {
await sendStream(contents as any, (msg: any) => {
msgList.value[msgList.value.length - 1].reasoning_content = msg.reasoning_content; msgList.value[msgList.value.length - 1].reasoning_content = msg.reasoning_content;
msgList.value[msgList.value.length - 1].content = msg.content; msgList.value[msgList.value.length - 1].content = msg.content;
if (msg.isComplete) { if (msg.isComplete) {
status.value = STATUS.WAITING; status.value = STATUS.WAITING;
} }
}); });
} catch (error: any) {
// let cursor = 0 try {
// const interval = setInterval(() => { const text = await error.response.text()
// msgList.value[msgList.value.length - 1].msg += sseDataModule[cursor].answer; msgList.value[msgList.value.length - 1].content = text;
// cursor++; } catch (err) {
// if (cursor >= sseDataModule.length) { msgList.value[msgList.value.length - 1].content = error.message;
// clearInterval(interval); }
// status.value = STATUS.WAITING; status.value = STATUS.WAITING;
// } }
// }, 300); }
onMounted(() => {
sendMsg("列举你能的事情,标记顺序,简洁回答。", true)
inputEl.value?.focus()
})
function handleSubmit() {
if (status.value === STATUS.SENDING) return
sendMsg(inputMsg.value)
inputMsg.value = "";
nextTick(() => {
scrollToBottom();
});
} }
function handleDelete(item: { role: string, content: string, reasoning_content?: string }, index: number) { function handleDelete(item: { role: string, content: string, reasoning_content?: string }, index: number) {
@ -110,17 +134,20 @@ function handleDelete(item: { role: string, content: string, reasoning_content?:
<div class="chatbox-container" ref="chatboxContainer"> <div class="chatbox-container" ref="chatboxContainer">
<div class="chatbox-content" ref="chatboxContent"> <div class="chatbox-content" ref="chatboxContent">
<template v-for="(data, index) in msgList" :key="index"> <template v-for="(data, index) in msgList" :key="index">
<div class="system-msg" v-if="data.role === 'system'"> <div class="system-msg" v-if="data.role === 'system' && !data.isHidden">
<textarea rows="10" cols="50" v-model="data.content"></textarea> <textarea rows="10" cols="50" v-model="data.content"></textarea>
</div> </div>
<div v-else class="chat-item" :class="{ left: data.role === 'assistant', right: data.role === 'user' }"> <div v-else-if="!data.isHidden" class="chat-item"
:class="{ left: data.role === 'assistant', right: data.role === 'user' }">
<div v-if="data.role === 'assistant'" style="display: flex; flex-direction: column; gap: 2px;"> <div v-if="data.role === 'assistant'" style="display: flex; flex-direction: column; gap: 2px;">
<div style="display: flex; gap: 10px;"> <div style="display: flex; gap: 10px;">
<div style="width: 50px; height: 50px;flex-shrink: 0;"> <div style="width: 50px; height: 50px;flex-shrink: 0;">
<img style="width: 100%; height: 100%;" src="/deepseek.svg" alt="avatar"></img> <img style="width: 100%; height: 100%;" src="/deepseek.svg" alt="avatar"></img>
</div> </div>
<div style="padding-top: 5px;"> <div style="padding-top: 5px;">
<div v-if="data.reasoning_content"> <div v-if="data.reasoning_content"
style="color: var(--color-fg-muted);padding: 20px;">
<h2 style="font-size: 20px;margin-bottom: 10px;">推理中</h2>
<Msg :msg="data.reasoning_content"></Msg> <Msg :msg="data.reasoning_content"></Msg>
</div> </div>
<div> <div>
@ -150,8 +177,7 @@ function handleDelete(item: { role: string, content: string, reasoning_content?:
</div> </div>
</div> </div>
<form class="chat-input" @submit.native.prevent="handleSubmit"> <form class="chat-input" @submit.native.prevent="handleSubmit">
<input ref="inputEl" type="text" v-model="inputMsg" placeholder="请输入内容" <input ref="inputEl" type="text" v-model="inputMsg" placeholder="请输入内容" class="chat-input-input">
class="chat-input-input">
</form> </form>
</div> </div>
</template> </template>

4
packages/client/src/components/ChatBox/prompt.txt

@ -1,6 +1,8 @@
每次回答前必须遵循以下规则!!!
知识库截断: 2024-06 知识库截断: 2024-06
你是AI代理人, 由DeepSeek提供技术支持. 你为你的主人服务,为USER提供信息与服务。 重要信息:你是AI代理人, 由Qwen/Qwen3-8B大模型提供技术支持. 你为你的主人服务,为USER提供信息与服务。
你可以帮主人代为处理一些任务。每次USER发送一条信息,你应该主动带入你的身份。 你可以帮主人代为处理一些任务。每次USER发送一条信息,你应该主动带入你的身份。

11
packages/client/src/composables/useChat/index.ts

@ -1,7 +1,7 @@
import { Chat } from "./Chat"; import { Chat } from "./Chat";
import { OpenAIModelConfig } from "./provider/Openai"; import { OpenAIModelConfig } from "./provider/Openai";
export function useChat() { export function useChat(provider: any, config: any) {
if (import.meta.env.SSR) { if (import.meta.env.SSR) {
return { return {
sendStream: () => {}, sendStream: () => {},
@ -18,14 +18,9 @@ export function useChat() {
} }
const chat = new Chat(); const chat = new Chat();
let curProvider: ReturnType<typeof chat.setProvider>; let curProvider: ReturnType<typeof chat.setProvider>;
curProvider = chat.setProvider(Chat.ModelProvider.OpenAI, { curProvider = chat.setProvider(provider, config)
model: "deepseek-chat",
apiKey: __DEEPSEAK_APIKEY__,
baseUrl: "https://api.deepseek.com",
temperature: 0.8,
});
function getConfig() { function getConfig() {
return curProvider.getConfig(); return curProvider!.getConfig();
} }
function updateConfig(config: OpenAIModelConfig) { function updateConfig(config: OpenAIModelConfig) {
chat.updateConfig({ chat.updateConfig({

6
packages/client/src/vue.d.ts

@ -19,5 +19,9 @@ declare module '@vue/runtime-core' {
} }
} }
declare global { declare global {
const __DEEPSEAK_APIKEY__: string const process: {
env: {
AI_APIKEY: string
}
}
} }

2
packages/client/vite.config.ts

@ -25,7 +25,7 @@ export default defineConfig({
}, },
}, },
define: { define: {
__DEEPSEAK_APIKEY__: `"${process.env.DEEPSEAK_APIKEY}"`, "process.env.AI_APIKEY": `"${process.env.AI_APIKEY}"`,
}, },
build: { build: {
emptyOutDir: true, emptyOutDir: true,

15
packages/core/src/SsrMiddleWare.ts

@ -1,5 +1,5 @@
import fs from 'node:fs/promises' import fs from 'node:fs/promises'
import { getPathByRoot } from "helper/path" import { isProduction, TemplateHtml, getPathByRoot, clientRoot, ssrManifest, entryServer } from "helper/path"
import { parseCookieHeader } from "helper/cookie" import { parseCookieHeader } from "helper/cookie"
import { Env } from "helper/env" import { Env } from "helper/env"
import { ViteDevServer } from 'vite' import { ViteDevServer } from 'vite'
@ -8,13 +8,8 @@ import type Koa from 'koa'
import c2k from 'koa-connect' import c2k from 'koa-connect'
import { transformHtmlTemplate } from "unhead/server"; import { transformHtmlTemplate } from "unhead/server";
const isProduction = Env.isProduction
const base = Env.base const base = Env.base
const templateHtml = isProduction
? await fs.readFile(getPathByRoot('dist', 'client/index.html'), 'utf-8')
: ''
export async function SsrMiddleWare(app: Koa, options?: { onDevViteClose?: Function }) { export async function SsrMiddleWare(app: Koa, options?: { onDevViteClose?: Function }) {
let vite: ViteDevServer let vite: ViteDevServer
if (!isProduction) { if (!isProduction) {
@ -36,7 +31,7 @@ export async function SsrMiddleWare(app: Koa, options?: { onDevViteClose?: Funct
// Production mode: serve pre-built static assets. // Production mode: serve pre-built static assets.
app.use(async (ctx, next) => { app.use(async (ctx, next) => {
try { try {
await Send(ctx, ctx.path, { root: getPathByRoot('dist/client'), index: false }); await Send(ctx, ctx.path, { root: clientRoot, index: false });
if (ctx.status === 404) { if (ctx.status === 404) {
await next() await next()
} }
@ -66,10 +61,10 @@ export async function SsrMiddleWare(app: Koa, options?: { onDevViteClose?: Funct
manifest = {} manifest = {}
render = (await vite.ssrLoadModule(getPathByRoot('packages', 'client/src/entry-server.ts'))).render render = (await vite.ssrLoadModule(getPathByRoot('packages', 'client/src/entry-server.ts'))).render
} else { } else {
manifest = await fs.readFile(getPathByRoot('dist', 'client/.vite/ssr-manifest.json'), 'utf-8') manifest = await fs.readFile(ssrManifest, 'utf-8')
template = templateHtml template = TemplateHtml
// @ts-ignore // @ts-ignore
render = (await import(getPathByRoot('dist', 'server/entry-server.js'))).render render = (await import(entryServer)).render
} }
const cookies = parseCookieHeader(ctx.request.headers['cookie'] as string) const cookies = parseCookieHeader(ctx.request.headers['cookie'] as string)

4
packages/server/package.json

@ -13,12 +13,12 @@
"@types/koa": "^3.0.0", "@types/koa": "^3.0.0",
"@types/koa-bodyparser": "^4.3.12", "@types/koa-bodyparser": "^4.3.12",
"@types/koa-send": "^4.1.6", "@types/koa-send": "^4.1.6",
"@types/path-is-absolute": "^1.0.2", "@types/path-is-absolute": "^1.0.2"
"jsonwebtoken": "^9.0.2"
}, },
"dependencies": { "dependencies": {
"@types/jsonwebtoken": "^9.0.10", "@types/jsonwebtoken": "^9.0.10",
"formidable": "^3.5.4", "formidable": "^3.5.4",
"jsonwebtoken": "^9.0.2",
"koa": "^3.0.1", "koa": "^3.0.1",
"koa-bodyparser": "^4.4.1", "koa-bodyparser": "^4.4.1",
"koa-connect": "^2.1.0", "koa-connect": "^2.1.0",

14
packages/server/src/jobs/index.ts

@ -2,6 +2,8 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import scheduler from './scheduler'; import scheduler from './scheduler';
import { TaskOptions } from 'node-cron'; import { TaskOptions } from 'node-cron';
import { isProduction, jobsDir } from 'helper/path';
import { logger } from '@/logger';
interface OneJob { interface OneJob {
id: string id: string
@ -16,12 +18,12 @@ export function defineJob(job: OneJob) {
return job; return job;
} }
const jobsDir = path.join(__dirname, 'jobs'); const _jobsDir = isProduction ? jobsDir : path.join(__dirname, 'jobs');
const jobModules: Record<string, OneJob> = {}; const jobModules: Record<string, OneJob> = {};
fs.readdirSync(jobsDir).forEach(file => { fs.readdirSync(_jobsDir).forEach(async file => {
if (!file.endsWith('Job.ts')) return; if (!file.endsWith(isProduction ? 'Job.js' : 'Job.ts')) return;
const jobModule = require(path.join(jobsDir, file)); const jobModule = await import(path.join(_jobsDir, file));
const job = jobModule.default || jobModule; const job = jobModule.default || jobModule;
if (job && job.id && job.cronTime && typeof job.task === 'function') { if (job && job.id && job.cronTime && typeof job.task === 'function') {
jobModules[job.id] = job; jobModules[job.id] = job;
@ -30,13 +32,15 @@ fs.readdirSync(jobsDir).forEach(file => {
} }
}); });
logger.info(`[Jobs] 加载了 ${Object.keys(jobModules).length} 个任务`);
function callHook(id: string, hookName: string) { function callHook(id: string, hookName: string) {
const job = jobModules[id]; const job = jobModules[id];
if (job && typeof job[hookName] === 'function') { if (job && typeof job[hookName] === 'function') {
try { try {
job[hookName](); job[hookName]();
} catch (e) { } catch (e) {
console.error(`[Job:${id}] ${hookName} 执行异常:`, e); logger.error(`[Job:${id}] ${hookName} 执行异常:`, e);
} }
} }
} }

10
packages/server/src/logger.ts

@ -1,14 +1,12 @@
import log4js from "log4js"; import log4js from "log4js";
import { Env } from 'helper/env'; import { logDir } from 'helper/path';
const { LOG_DIR } = Env;
log4js.configure({ log4js.configure({
appenders: { appenders: {
all: { all: {
type: "file", type: "file",
filename: `${LOG_DIR}/all.log`, filename: `${logDir}/all.log`,
maxLogSize: 102400, maxLogSize: 102400,
pattern: "-yyyy-MM-dd.log", pattern: "-yyyy-MM-dd.log",
alwaysIncludePattern: true, alwaysIncludePattern: true,
@ -20,7 +18,7 @@ log4js.configure({
}, },
error: { error: {
type: "file", type: "file",
filename: `${LOG_DIR}/error.log`, filename: `${logDir}/error.log`,
maxLogSize: 102400, maxLogSize: 102400,
pattern: "-yyyy-MM-dd.log", pattern: "-yyyy-MM-dd.log",
alwaysIncludePattern: true, alwaysIncludePattern: true,
@ -32,7 +30,7 @@ log4js.configure({
}, },
jobs: { jobs: {
type: "file", type: "file",
filename: `${LOG_DIR}/jobs.log`, filename: `${logDir}/jobs.log`,
maxLogSize: 102400, maxLogSize: 102400,
pattern: "-yyyy-MM-dd.log", pattern: "-yyyy-MM-dd.log",
alwaysIncludePattern: true, alwaysIncludePattern: true,

6
packages/server/src/middleware/install.ts

@ -12,7 +12,7 @@ import { DefaultContext, Next, ParameterizedContext } from "koa"
import { AuthMiddleware } from "./Auth" import { AuthMiddleware } from "./Auth"
import Session from "./Session" import Session from "./Session"
import Send from "./Send" import Send from "./Send"
import { getPathByRoot } from "helper/path" import { getPathByRoot, isProduction, serverModules, serverPublic } from "helper/path"
type App = typeof app type App = typeof app
@ -31,7 +31,7 @@ export default async (app: App) => {
}) })
const publicPath = getPathByRoot("public") const publicPath = isProduction ? serverPublic : getPathByRoot("public")
app.use(async (ctx, next) => { app.use(async (ctx, next) => {
if (!ctx.path.startsWith("/public")) return await next() if (!ctx.path.startsWith("/public")) return await next()
if (ctx.method.toLowerCase() === "get") { if (ctx.method.toLowerCase() === "get") {
@ -65,7 +65,7 @@ export default async (app: App) => {
app.use(bodyParser()) app.use(bodyParser())
app.use( app.use(
await Controller({ await Controller({
root: path.resolve(__dirname, "../modules"), root: isProduction ? serverModules : path.resolve(__dirname, "../modules"),
handleBeforeEachRequest: (options: any) => { handleBeforeEachRequest: (options: any) => {
const { auth = true } = options || {} const { auth = true } = options || {}
return async (ctx: ParameterizedContext, next: Next) => { return async (ctx: ParameterizedContext, next: Next) => {

65
scripts/build.js

@ -0,0 +1,65 @@
#!/usr/bin/env node
import { readFileSync, writeFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
// 获取当前文件目录
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const projectRoot = join(__dirname, '..');
console.log('🚀 开始构建项目...');
try {
// 1. 读取package.json文件
console.log('📋 读取package.json文件...');
const rootPackageJson = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8'));
const serverPackageJson = JSON.parse(readFileSync(join(projectRoot, 'packages/server/package.json'), 'utf8'));
// 2. 合并依赖
console.log('🔗 合并依赖...');
const mergedDependencies = {
...rootPackageJson.dependencies,
...serverPackageJson.dependencies
};
// 3. 创建生产环境的package.json
const productionPackageJson = {
name: rootPackageJson.name,
type: 'module',
version: rootPackageJson.version || '1.0.0',
description: 'Koa SSR 生产环境应用',
main: 'booststap.js',
scripts: {
start: 'bun run booststap.js'
},
dependencies: mergedDependencies,
engines: {
bun: '>=1.0.0'
},
keywords: ['koa', 'ssr', 'vue', 'bun'],
author: '',
license: 'MIT'
};
// 4. 写入新的package.json到dist目录
console.log('💾 生成生产环境package.json...');
const distPackageJsonPath = join(projectRoot, 'dist', 'package.json');
writeFileSync(distPackageJsonPath, JSON.stringify(productionPackageJson, null, 2));
console.log('✅ package.json 已生成到 dist/package.json');
// 5. 显示合并后的依赖信息
console.log('\n📊 依赖合并结果:');
console.log(`- 根目录依赖: ${Object.keys(rootPackageJson.dependencies || {}).length} 个`);
console.log(`- Server依赖: ${Object.keys(serverPackageJson.dependencies || {}).length} 个`);
console.log(`- 合并后总依赖: ${Object.keys(mergedDependencies).length}`);
console.log('\n🎉 构建脚本执行完成!');
console.log('📁 生产环境文件已生成到 dist/ 目录');
console.log('💡 可以使用以下命令启动生产环境:');
console.log(' cd dist && bun install && bun run start');
} catch (error) {
console.error('❌ 构建过程中发生错误:', error.message);
process.exit(1);
}

20
tsup.config.ts

@ -0,0 +1,20 @@
import { defineConfig } from "tsup";
import pkg from "./package.json";
import spkg from "./packages/server/package.json";
import fg from "fast-glob"
const jobsEntries = await fg(["packages/server/src/jobs/**/*.ts"], { });
const modulesEntries = await fg(["packages/server/src/modules/**/*.ts"], { });
export default defineConfig({
entry: ["packages/server/src/booststap.ts", ...jobsEntries, ...modulesEntries],
format: "esm",
sourcemap: false,
clean: false,
cjsInterop: true,
external: [
...Object.keys(pkg.dependencies),
...Object.keys(spkg.dependencies),
]
});

18
tsup.jobs.config.ts

@ -0,0 +1,18 @@
import { defineConfig } from 'tsup'
import pkg from "./package.json";
import spkg from "./packages/server/package.json";
import fg from "fast-glob"
const entries = await fg(["packages/server/src/jobs/**/*.ts"], { });
export default defineConfig({
entry: entries,
format: 'esm',
sourcemap: false,
clean: false,
outDir: "dist/jobs",
external: [
...Object.keys(pkg.dependencies),
...Object.keys(spkg.dependencies),
]
})

18
tsup.modules.config.ts

@ -0,0 +1,18 @@
import { defineConfig } from 'tsup'
import pkg from "./package.json";
import spkg from "./packages/server/package.json";
import fg from "fast-glob"
const entries = await fg(["packages/server/src/modules/**/*.ts"], { });
export default defineConfig({
entry: entries,
format: 'esm',
sourcemap: false,
clean: false,
outDir: "dist/modules",
external: [
...Object.keys(pkg.dependencies),
...Object.keys(spkg.dependencies),
]
})
Loading…
Cancel
Save