From e8d7866b8be73e9b929231993b2b5c4b81c213e7 Mon Sep 17 00:00:00 2001 From: dash <1549469775@qq.com> Date: Fri, 17 Oct 2025 01:00:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=A0=E9=99=A4=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84=20bunfig.toml=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=EF=BC=8C=E6=9B=B4=E6=96=B0=E7=9B=B8=E5=85=B3=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E5=92=8C=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 26 ++++++ bun.lockb | Bin 226837 -> 260615 bytes bunfig.toml | 16 ---- internal/x/components/ClientOnly.vue | 9 +- package.json | 10 +-- packages/client/package.json | 23 ++--- packages/core/package.json | 9 -- packages/core/src/SsrMiddleWare.ts | 97 ---------------------- packages/server/drizzle.config.ts | 10 +++ packages/server/drizzle/0000_thick_may_parker.sql | 8 ++ packages/server/drizzle/meta/0000_snapshot.json | 70 ++++++++++++++++ packages/server/drizzle/meta/_journal.json | 13 +++ packages/server/package.json | 16 +++- packages/server/src/db/index.ts | 4 + packages/server/src/db/schema.ts | 8 ++ packages/server/src/middleware/install.ts | 2 +- packages/server/src/ssr/index.ts | 97 ++++++++++++++++++++++ scripts/build.js | 4 +- scripts/fix-type-router.js | 16 ++-- tsup.config.ts | 3 +- tsup.jobs.config.ts | 18 ---- tsup.modules.config.ts | 18 ---- 22 files changed, 284 insertions(+), 193 deletions(-) create mode 100644 .env.example delete mode 100644 bunfig.toml delete mode 100644 packages/core/package.json delete mode 100644 packages/core/src/SsrMiddleWare.ts create mode 100644 packages/server/drizzle.config.ts create mode 100644 packages/server/drizzle/0000_thick_may_parker.sql create mode 100644 packages/server/drizzle/meta/0000_snapshot.json create mode 100644 packages/server/drizzle/meta/_journal.json create mode 100644 packages/server/src/db/index.ts create mode 100644 packages/server/src/db/schema.ts create mode 100644 packages/server/src/ssr/index.ts delete mode 100644 tsup.jobs.config.ts delete mode 100644 tsup.modules.config.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7c41b2a --- /dev/null +++ b/.env.example @@ -0,0 +1,26 @@ +# 环境变量配置文件 + +# 服务器配置 +NODE_ENV=development +PORT=3000 +HOST=localhost + +# 安全配置 +AI_APIKEY=ai-apikey +SESSION_SECRET=your-session-secret-key-here,another-secret-key +JWT_SECRET=your-jwt-secret-key-must-be-at-least-32-characters-long + +# 数据库配置 +DB_PATH=./data/database.db + +# 日志配置 +LOG_LEVEL=info +LOG_FILE=./logs/app.log + +# 缓存配置 +REDIS_HOST=localhost +REDIS_PORT=6379 + +# 任务调度配置 +JOBS_ENABLED=true +TZ=Asia/Shanghai \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index 7d0a184bc69a2ccd1e07437f89d923a4df8de471..359f9ccaba234661621fbc735b90b3900052a344 100644 GIT binary patch delta 68021 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 diff --git a/bunfig.toml b/bunfig.toml deleted file mode 100644 index 1e13871..0000000 --- a/bunfig.toml +++ /dev/null @@ -1,16 +0,0 @@ -# bun 编译配置:将 dist 下的所有资源一并内嵌到可执行文件中 -# 注意:命令行中的 --compile/--outfile 参数依然可以覆盖这里的设置 - -[compile] -# 入口文件(与 package.json 的 bundle:exe 一致) -entrypoint = "dist/booststap.js" - -# 需要内嵌到可执行文件中的静态资源(包含服务端/客户端产物) -assets = [ - "dist/**" -] - -# 输出可执行文件路径 -outfile = "dist/koa-ssr.exe" - - diff --git a/internal/x/components/ClientOnly.vue b/internal/x/components/ClientOnly.vue index 479fe11..e90b937 100644 --- a/internal/x/components/ClientOnly.vue +++ b/internal/x/components/ClientOnly.vue @@ -36,10 +36,11 @@ export default defineComponent({ onMounted(() => { mounted.value = true; }); - const vm = getCurrentInstance(); - if (vm) { - vm._nuxtClientOnly = true; - } + // 这里没什么用,先去掉 + // const vm = getCurrentInstance(); + // if (vm) { + // vm._nuxtClientOnly = true; + // } provide(clientOnlySymbol, true); return () => { if (mounted.value) { diff --git a/package.json b/package.json index 78168fb..5052d41 100644 --- a/package.json +++ b/package.json @@ -8,20 +8,17 @@ "scripts": { "postinstall": "node scripts/fix-type-router.js", "dev": "bun run --hot packages/server/src/booststap.ts", + "db": "bun run --env-file=.env --filter server db ", "build:client": "bun run --filter client build", "start": "cd dist && cross-env NODE_ENV=production bun run booststap.js", "build:all": "cross-env NODE_ENV=production tsup --config tsup.config.ts", "build": "rimraf dist && bun run build:client && bun run build:all && node scripts/build.js", - "bundle:exe": "bun build --compile ./dist/booststap.js ./dist/**/* --minify --outfile ./dist/koa-ssr.exe", - "build:exe": "bun run build && bun run bundle:exe", "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" }, "devDependencies": { "@types/bun": "latest", - "@types/koa-compose": "^3.2.8", "client": "workspace:*", - "core": "workspace:*", "cross-env": "^10.1.0", "fast-glob": "^3.3.3", "helper": "workspace:*", @@ -29,6 +26,7 @@ "server": "workspace:*", "tsup": "^8.5.0", "unplugin-vue-components": "^29.1.0", + "vite": "^7.1.7", "vite-plugin-devtools-json": "^1.0.0", "x": "workspace:*" }, @@ -36,8 +34,6 @@ "typescript": "^5.9.3" }, "dependencies": { - "koa-compose": "^4.1.0", - "pinia": "^3.0.3", - "vite": "^7.1.7" + "vue": "^3.5.22" } } diff --git a/packages/client/package.json b/packages/client/package.json index 115d4ee..bb8d3ca 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -11,13 +11,18 @@ "@types/jsdom": "^27.0.0", "sass-embedded": "^1.93.2", "unplugin-vue-components": "^29.1.0", - "vue-tsc": "^3.1.0" + "vue-tsc": "^3.1.0", + "@vitejs/plugin-vue": "^6.0.1", + "unplugin-auto-import": "^20.2.0", + "vite-plugin-vue-layouts": "^0.11.0" }, "dependencies": { - "@maz-ui/icons": "^4.1.3", - "@maz-ui/themes": "^4.1.5", "@unhead/vue": "^2.0.17", - "@vitejs/plugin-vue": "^6.0.1", + "ofetch": "^1.4.1", + "pinia": "^3.0.3", + "unplugin-vue-router": "^0.15.0", + "vue-final-modal": "^4.5.5", + "vue-router": "^4.5.1", "ant-design-x-vue": "^1.3.2", "dompurify": "^3.2.7", "eventsource-parser": "^3.0.6", @@ -25,13 +30,9 @@ "jsdom": "^27.0.0", "marked": "^16.3.0", "maz-ui": "^4.1.6", - "ofetch": "^1.4.1", "quill": "^2.0.3", - "unplugin-auto-import": "^20.2.0", - "unplugin-vue-router": "^0.15.0", - "vite-plugin-vue-layouts": "^0.11.0", - "vue": "^3.5.22", - "vue-final-modal": "^4.5.5", - "vue-router": "^4.5.1" + "@maz-ui/icons": "^4.1.3", + "@maz-ui/themes": "^4.1.5", + "@maz-ui/translations": "^4.1.7" } } diff --git a/packages/core/package.json b/packages/core/package.json deleted file mode 100644 index 30dabbb..0000000 --- a/packages/core/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "core", - "exports": { - "./*": { - "import": "./src/*.ts", - "require": "./src/*.ts" - } - } -} diff --git a/packages/core/src/SsrMiddleWare.ts b/packages/core/src/SsrMiddleWare.ts deleted file mode 100644 index 9b3cc08..0000000 --- a/packages/core/src/SsrMiddleWare.ts +++ /dev/null @@ -1,97 +0,0 @@ -import fs from 'node:fs/promises' -import { TemplateHtml, getPathByRoot, clientRoot, ssrManifest, entryServer } from "helper/path" -import { parseCookieHeader } from "helper/cookie" -import { Env } from "helper/env" -import type { ViteDevServer } from 'vite' -import Send from 'koa-send' -import type Koa from 'koa' -import c2k from 'koa-connect' -import { transformHtmlTemplate } from "unhead/server"; - -const base = Env.base - -export async function SsrMiddleWare(app: Koa, options?: { onDevViteClose?: Function }) { - let vite: ViteDevServer - if (process.env.NODE_ENV !== 'production') { - // Dev mode: create Vite server in middleware mode. - const { createServer } = await import('vite') - vite = await createServer({ - server: { middlewareMode: true }, - configFile: getPathByRoot('packages', 'client/vite.config.ts'), - root: getPathByRoot('packages', 'client'), - appType: 'custom', - base, - }) - app.use(c2k(vite.middlewares)) - vite.httpServer?.on("close", () => { - vite.close() - options?.onDevViteClose?.() - }) - } else { - // Production mode: serve pre-built static assets. - app.use(async (ctx, next) => { - try { - await Send(ctx, ctx.path, { root: clientRoot, index: false }); - if (ctx.status === 404) { - await next() - } - } catch (error) { - if (ctx.status === 404) { - await next() - } else { - throw error - } - } - }) - } - - // Handle every other route with SSR. - app.use(async (ctx, next) => { - if (!ctx.originalUrl.startsWith(base)) return await next() - - try { - const url = ctx.originalUrl.replace(base, '') - let template - let render - let manifest - if (process.env.NODE_ENV !== 'production') { - // Always read fresh template in development - template = await fs.readFile(getPathByRoot('packages', 'client/index.html'), 'utf-8') - template = await vite.transformIndexHtml(url, template) - manifest = {} - render = (await vite.ssrLoadModule(getPathByRoot('packages', 'client/src/entry-server.ts'))).render - } else { - manifest = await fs.readFile(ssrManifest, 'utf-8') - template = TemplateHtml - // @ts-ignore - render = (await import(entryServer)).render - } - - const cookies = parseCookieHeader(ctx.request.headers['cookie'] as string) - - const rendered = await render(url, manifest, { cookies }) - - const html = await transformHtmlTemplate( - rendered.unHead, - template - .replace(``, rendered.head ?? '') - .replace(``, rendered.html ?? '') - ) - ctx.status = 200 - ctx.set({ 'Content-Type': 'text/html' }) - ctx.body = html - - // 设置服务端渲染期间收集到的 Set-Cookie - const setCookies: string[] = (rendered as any).setCookies || [] - if (setCookies.length > 0) { - ctx.set('Set-Cookie', setCookies) - } - } catch (e: Error | any) { - vite?.ssrFixStacktrace(e) - ctx.status = 500 - console.error(e.stack) - ctx.body = e.stack - } - await next() - }) -} \ No newline at end of file diff --git a/packages/server/drizzle.config.ts b/packages/server/drizzle.config.ts new file mode 100644 index 0000000..35b7459 --- /dev/null +++ b/packages/server/drizzle.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'drizzle-kit'; + +export default defineConfig({ + out: './drizzle', + schema: './src/db/schema.ts', + dialect: 'mysql', + dbCredentials: { + url: process.env.DATABASE_URL!, + }, +}); \ No newline at end of file diff --git a/packages/server/drizzle/0000_thick_may_parker.sql b/packages/server/drizzle/0000_thick_may_parker.sql new file mode 100644 index 0000000..64a13c0 --- /dev/null +++ b/packages/server/drizzle/0000_thick_may_parker.sql @@ -0,0 +1,8 @@ +CREATE TABLE `users_table` ( + `id` serial AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL, + `age` int NOT NULL, + `email` varchar(255) NOT NULL, + CONSTRAINT `users_table_id` PRIMARY KEY(`id`), + CONSTRAINT `users_table_email_unique` UNIQUE(`email`) +); diff --git a/packages/server/drizzle/meta/0000_snapshot.json b/packages/server/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..cc53403 --- /dev/null +++ b/packages/server/drizzle/meta/0000_snapshot.json @@ -0,0 +1,70 @@ +{ + "version": "5", + "dialect": "mysql", + "id": "3770df0e-b3f2-4223-8e66-2880f634d8b0", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "users_table": { + "name": "users_table", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "age": { + "name": "age", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "users_table_id": { + "name": "users_table_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "users_table_email_unique": { + "name": "users_table_email_unique", + "columns": [ + "email" + ] + } + }, + "checkConstraint": {} + } + }, + "views": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "tables": {}, + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/server/drizzle/meta/_journal.json b/packages/server/drizzle/meta/_journal.json new file mode 100644 index 0000000..3ced3d6 --- /dev/null +++ b/packages/server/drizzle/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "mysql", + "entries": [ + { + "idx": 0, + "version": "5", + "when": 1760625865036, + "tag": "0000_thick_may_parker", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/packages/server/package.json b/packages/server/package.json index e996d54..366e5c3 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -7,27 +7,37 @@ "types": "./src/*.d.ts" } }, - "scripts": {}, + "scripts": { + "db": "bunx --bun drizzle-kit " + }, "devDependencies": { "@types/formidable": "^3.4.5", "@types/koa": "^3.0.0", "@types/koa-bodyparser": "^4.3.12", + "@types/koa-compose": "^3.2.8", "@types/koa-send": "^4.1.6", - "@types/path-is-absolute": "^1.0.2" + "@types/path-is-absolute": "^1.0.2", + "drizzle-kit": "^0.31.5" }, "dependencies": { "@types/jsonwebtoken": "^9.0.10", + "assert": "^2.1.0", + "drizzle-orm": "^0.44.6", "formidable": "^3.5.4", + "http-errors": "^2.0.0", "jsonwebtoken": "^9.0.2", "koa": "^3.0.1", "koa-bodyparser": "^4.4.1", + "koa-compose": "^4.1.0", "koa-connect": "^2.1.0", "koa-send": "^5.0.1", "koa-session": "^7.0.2", "log4js": "^6.9.1", "minimatch": "^10.0.3", + "mysql2": "^3.15.2", "node-cron": "^4.2.1", "path-is-absolute": "^2.0.0", - "path-to-regexp": "^8.3.0" + "path-to-regexp": "^8.3.0", + "unhead": "^2.0.19" } } diff --git a/packages/server/src/db/index.ts b/packages/server/src/db/index.ts new file mode 100644 index 0000000..ce84de8 --- /dev/null +++ b/packages/server/src/db/index.ts @@ -0,0 +1,4 @@ +import { drizzle } from "drizzle-orm/mysql2"; + +// You can specify any property from the mysql2 connection options +const db = drizzle({ connection: { uri: process.env.DATABASE_URL } }); diff --git a/packages/server/src/db/schema.ts b/packages/server/src/db/schema.ts new file mode 100644 index 0000000..3ad5b0f --- /dev/null +++ b/packages/server/src/db/schema.ts @@ -0,0 +1,8 @@ +import { int, mysqlTable, serial, varchar } from 'drizzle-orm/mysql-core'; + +export const usersTable = mysqlTable('users_table', { + id: serial().primaryKey(), + name: varchar({ length: 255 }).notNull(), + age: int().notNull(), + email: varchar({ length: 255 }).notNull().unique(), +}); diff --git a/packages/server/src/middleware/install.ts b/packages/server/src/middleware/install.ts index aa9811d..f0ca653 100644 --- a/packages/server/src/middleware/install.ts +++ b/packages/server/src/middleware/install.ts @@ -1,5 +1,5 @@ -import { SsrMiddleWare } from "core/SsrMiddleWare" +import { SsrMiddleWare } from "../ssr" import bodyParser from "koa-bodyparser" import app from "@/app" import ResponseTime from "./ResponseTime" diff --git a/packages/server/src/ssr/index.ts b/packages/server/src/ssr/index.ts new file mode 100644 index 0000000..9b65b91 --- /dev/null +++ b/packages/server/src/ssr/index.ts @@ -0,0 +1,97 @@ +import fs from 'node:fs/promises' +import { TemplateHtml, getPathByRoot, clientRoot, ssrManifest, entryServer } from "helper/path" +import { parseCookieHeader } from "helper/cookie" +import { Env } from "helper/env" +import type { ViteDevServer } from 'vite' +import Send from 'koa-send' +import type Koa from 'koa' +import c2k from 'koa-connect' +import { transformHtmlTemplate } from "unhead/server"; + +const base = Env.base + +export async function SsrMiddleWare(app: Koa, options?: { onDevViteClose?: Function }) { + let vite: ViteDevServer + if (process.env.NODE_ENV !== 'production') { + // Dev mode: create Vite server in middleware mode. + const { createServer } = await import('vite') + vite = await createServer({ + server: { middlewareMode: true }, + configFile: getPathByRoot('packages', 'client/vite.config.ts'), + root: getPathByRoot('packages', 'client'), + appType: 'custom', + base, + }) + app.use(c2k(vite.middlewares)) + vite.httpServer?.on("close", () => { + vite.close() + options?.onDevViteClose?.() + }) + } else { + // Production mode: serve pre-built static assets. + app.use(async (ctx, next) => { + try { + await Send(ctx, ctx.path, { root: clientRoot, index: false }); + if (ctx.status === 404) { + await next() + } + } catch (error) { + if (ctx.status === 404) { + await next() + } else { + throw error + } + } + }) + } + + // Handle every other route with SSR. + app.use(async (ctx, next) => { + if (!ctx.originalUrl.startsWith(base)) return await next() + + try { + const url = ctx.originalUrl.replace(base, '') + let template + let render + let manifest + if (process.env.NODE_ENV !== 'production') { + // Always read fresh template in development + template = await fs.readFile(getPathByRoot('packages', 'client/index.html'), 'utf-8') + template = await vite.transformIndexHtml(url, template) + manifest = {} + render = (await vite.ssrLoadModule(getPathByRoot('packages', 'client/src/entry-server.ts'))).render + } else { + manifest = await fs.readFile(ssrManifest, 'utf-8') + template = TemplateHtml + // @ts-ignore + render = (await import(entryServer)).render + } + + const cookies = parseCookieHeader(ctx.request.headers['cookie'] as string) + + const rendered = await render(url, manifest, { cookies }) + + const html = await transformHtmlTemplate( + rendered.unHead, + template + .replace(``, rendered.head ?? '') + .replace(``, rendered.html ?? '') + ) + ctx.status = 200 + ctx.set({ 'Content-Type': 'text/html' }) + ctx.body = html + + // 设置服务端渲染期间收集到的 Set-Cookie + const setCookies: string[] = (rendered as any).setCookies || [] + if (setCookies.length > 0) { + ctx.set('Set-Cookie', setCookies) + } + } catch (e: Error | any) { + vite?.ssrFixStacktrace(e) + ctx.status = 500 + console.error(e) + ctx.body = e.stack + } + await next() + }) +} \ No newline at end of file diff --git a/scripts/build.js b/scripts/build.js index 7ddeeb8..5bf2954 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -15,12 +15,14 @@ try { 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')); + const clientPackageJson = JSON.parse(readFileSync(join(projectRoot, 'packages/client/package.json'), 'utf8')); // 2. 合并依赖 console.log('🔗 合并依赖...'); const mergedDependencies = { ...rootPackageJson.dependencies, - ...serverPackageJson.dependencies + ...serverPackageJson.dependencies, + ...clientPackageJson.dependencies }; // 3. 创建生产环境的package.json diff --git a/scripts/fix-type-router.js b/scripts/fix-type-router.js index 9cf6e91..f23d275 100644 --- a/scripts/fix-type-router.js +++ b/scripts/fix-type-router.js @@ -6,13 +6,15 @@ const dtsPath = path.resolve( "../node_modules/vue-router/dist/vue-router.d.ts" ); -const content = fs.readFileSync(dtsPath, "utf8"); +if (fs.existsSync(dtsPath)) { + const content = fs.readFileSync(dtsPath, "utf8"); -const fixedContent = content.replace( - /declare module ['"]vue['"]/g, - "declare module '@vue/runtime-core'" -); + const fixedContent = content.replace( + /declare module ['"]vue['"]/g, + "declare module '@vue/runtime-core'" + ); -fs.writeFileSync(dtsPath, fixedContent, "utf8"); + fs.writeFileSync(dtsPath, fixedContent, "utf8"); -console.log("Fixed vue-router.d.ts module declaration."); + console.log("Fixed vue-router.d.ts module declaration."); +} diff --git a/tsup.config.ts b/tsup.config.ts index 909642e..fe50bdd 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -6,9 +6,10 @@ import fg from "fast-glob" const jobsEntries = await fg(["packages/server/src/jobs/**/*.ts"], { }); const modulesEntries = await fg(["packages/server/src/modules/**/*.ts"], { }); +console.log(process.env.NODE_ENV); export default defineConfig({ - entry: ["packages/server/src/booststap.ts", ...jobsEntries, ...modulesEntries], + entry: [...jobsEntries, ...modulesEntries, "packages/server/src/booststap.ts"], format: "esm", treeshake: true, sourcemap: false, diff --git a/tsup.jobs.config.ts b/tsup.jobs.config.ts deleted file mode 100644 index 8a1a0e0..0000000 --- a/tsup.jobs.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -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), - ] -}) \ No newline at end of file diff --git a/tsup.modules.config.ts b/tsup.modules.config.ts deleted file mode 100644 index 76c6978..0000000 --- a/tsup.modules.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -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), - ] -}) \ No newline at end of file