From 49b46c44a96e33d8769e3e904e4ef302d934347c Mon Sep 17 00:00:00 2001 From: Xc Date: Thu, 16 Oct 2025 15:11:48 +0800 Subject: [PATCH 01/13] 4.0.29-dev --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index a1d0d1f..d435926 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xuchangzju/oak-cli", - "version": "4.0.28", + "version": "4.0.29", "description": "client for oak framework", "main": "lib/index.js", "scripts": { @@ -112,9 +112,9 @@ "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.5.3", "node-watch": "^0.7.4", - "oak-backend-base": "^4.1.23", - "oak-domain": "^5.1.28", - "oak-frontend-base": "^5.3.38", + "oak-backend-base": "file:../oak-backend-base", + "oak-domain": "file:../oak-domain", + "oak-frontend-base": "file:../oak-frontend-base", "parse-asn1": "5.1.6", "postcss": "^8.4.4", "postcss-flexbugs-fixes": "^5.0.2", From 9a50ce03d3c64b08c08fc32e5bff94d1aee3a3c1 Mon Sep 17 00:00:00 2001 From: qcqcqc <1220204124@zust.edu.cn> Date: Tue, 4 Nov 2025 10:01:56 +0800 Subject: [PATCH 02/13] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=9C=A8?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E6=A8=A1=E5=9D=97=E6=97=B6=E6=9B=B4=E6=96=B0?= =?UTF-8?q?configuration/compiler.js=E4=BC=9A=E5=87=BA=E7=8E=B0=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/create.js | 6 ++++-- lib/file-handle.d.ts | 2 +- lib/template.js | 7 ++++++- src/create.ts | 6 ++++-- src/template.ts | 9 +++++++-- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/create.js b/lib/create.js index b30d4c1..f1a5b1b 100644 --- a/lib/create.js +++ b/lib/create.js @@ -234,8 +234,10 @@ async function create(dirName, cmd) { (0, file_handle_1.checkFileExistsAndCreate)(tsConfigMpJsonPath, tsConfigMpJson, enum_1.checkFileExistsAndCreateType.FILE); // 创建tsconfig.web.json (0, file_handle_1.checkFileExistsAndCreate)(tsConfigWebJsonPath, tsConfigWebJson, enum_1.checkFileExistsAndCreateType.FILE); - // 更新configuration/compiler.js - (0, template_1.updateCompilerJsContent)(rootPath, deps); + // 更新configuration/compiler.js (仅在非模块化模式下) + if (!isModule) { + (0, template_1.updateCompilerJsContent)(rootPath, deps); + } (0, tip_style_1.Success)(`${(0, tip_style_1.success)(`Successfully created project ${(0, tip_style_1.primary)(name)}, directory name is ${(0, tip_style_1.primary)(dirName)}`)}`); shelljs_1.default.cd(dirName); if (deps.length > 0) { diff --git a/lib/file-handle.d.ts b/lib/file-handle.d.ts index c3136cd..9f0f136 100644 --- a/lib/file-handle.d.ts +++ b/lib/file-handle.d.ts @@ -37,7 +37,7 @@ export declare function writeFile(path: string | PathLike, data: any): void; export declare function readFile(path: string | PathLike, options?: { encoding?: null | undefined; flag?: string | undefined; -} | null): Buffer | undefined; +} | null): NonSharedBuffer | undefined; /** * @name 拷贝文件夹 * @export diff --git a/lib/template.js b/lib/template.js index e1e16cc..cb0e8e3 100644 --- a/lib/template.js +++ b/lib/template.js @@ -636,8 +636,13 @@ function oakConfigContentWithWeb() { } function updateCompilerJsContent(directory, deps) { const compilerJsPath = (0, path_1.join)(directory, 'configuration', 'compiler.js'); - (0, assert_1.default)((0, fs_1.existsSync)(compilerJsPath)); + // 只有在有依赖项时才需要修改 compiler.js if (deps.length > 0) { + // 检查文件是否存在 + if (!(0, fs_1.existsSync)(compilerJsPath)) { + console.warn(`Warning: ${compilerJsPath} does not exist, skipping compiler.js update`); + return; + } const { ast } = (0, core_1.transformFileSync)(compilerJsPath, { ast: true }); const { program } = ast; const { body } = program; diff --git a/src/create.ts b/src/create.ts index 8efbdd8..d26c1ef 100644 --- a/src/create.ts +++ b/src/create.ts @@ -356,8 +356,10 @@ export async function create(dirName: string, cmd: any) { tsConfigWebJson, checkFileExistsAndCreateType.FILE ); - // 更新configuration/compiler.js - updateCompilerJsContent(rootPath, deps); + // 更新configuration/compiler.js (仅在非模块化模式下) + if (!isModule) { + updateCompilerJsContent(rootPath, deps); + } Success( `${success( `Successfully created project ${primary( diff --git a/src/template.ts b/src/template.ts index fa75c07..f76fa05 100644 --- a/src/template.ts +++ b/src/template.ts @@ -663,9 +663,14 @@ export function oakConfigContentWithWeb() { export function updateCompilerJsContent(directory: string, deps: string[]) { const compilerJsPath = join(directory, 'configuration', 'compiler.js'); - assert(existsSync(compilerJsPath)); - + + // 只有在有依赖项时才需要修改 compiler.js if (deps.length > 0) { + // 检查文件是否存在 + if (!existsSync(compilerJsPath)) { + console.warn(`Warning: ${compilerJsPath} does not exist, skipping compiler.js update`); + return; + } const { ast } = transformFileSync(compilerJsPath, { ast: true })!; const { program } = ast!; const { body } = program!; From e46c41812dca8ea525f98b4672b03483ec9b3b8b Mon Sep 17 00:00:00 2001 From: qcqcqc <1220204124@zust.edu.cn> Date: Tue, 4 Nov 2025 10:10:20 +0800 Subject: [PATCH 03/13] =?UTF-8?q?fix:=20=E5=A4=8D=E5=88=B6=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E6=96=87=E4=BB=B6=E6=97=B6=E6=9C=AA=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E7=AD=89=E5=BE=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/file-handle.js | 5 ++--- src/file-handle.ts | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/file-handle.js b/lib/file-handle.js index 3e5b805..c7d88bd 100644 --- a/lib/file-handle.js +++ b/lib/file-handle.js @@ -149,9 +149,8 @@ function copyFolder(currentDir, targetDir, overwrite = false) { } else { if (file.isFile()) { - const readStream = (0, fs_1.createReadStream)(copyCurrentFileInfo); - const writeStream = (0, fs_1.createWriteStream)(copyTargetFileInfo); - readStream.pipe(writeStream); + // 使用同步复制确保文件完全写入 + (0, fs_1.copyFileSync)(copyCurrentFileInfo, copyTargetFileInfo); // console.log(`复制文件: ${copyCurrentFileInfo} -> ${copyTargetFileInfo}`); } else { diff --git a/src/file-handle.ts b/src/file-handle.ts index d5d2492..0f985aa 100644 --- a/src/file-handle.ts +++ b/src/file-handle.ts @@ -1,4 +1,4 @@ -import { readdirSync, statSync, writeFileSync, PathLike, existsSync, unlinkSync, mkdirSync, rmdirSync, createReadStream, accessSync, createWriteStream, constants, readFileSync } from 'fs' +import { readdirSync, statSync, writeFileSync, PathLike, existsSync, unlinkSync, mkdirSync, rmdirSync, createReadStream, accessSync, createWriteStream, constants, readFileSync, copyFileSync } from 'fs' import { join } from 'path' import { checkFileExistsAndCreateType } from './enum' import { Error, error, Warn, warn } from './tip-style' @@ -141,9 +141,8 @@ export function copyFolder(currentDir: PathLike, targetDir: PathLike, overwrite: } else { if (file.isFile()) { - const readStream = createReadStream(copyCurrentFileInfo); - const writeStream = createWriteStream(copyTargetFileInfo); - readStream.pipe(writeStream); + // 使用同步复制确保文件完全写入 + copyFileSync(copyCurrentFileInfo, copyTargetFileInfo); // console.log(`复制文件: ${copyCurrentFileInfo} -> ${copyTargetFileInfo}`); } else { try { From 00dab2c4549adfc4cce5d3f159f224e83f58b0b3 Mon Sep 17 00:00:00 2001 From: qcqcqc <1220204124@zust.edu.cn> Date: Tue, 4 Nov 2025 10:28:15 +0800 Subject: [PATCH 04/13] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=8C=96=E9=A1=B9=E7=9B=AE=E5=88=9B=E5=BB=BA=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E6=A8=A1=E6=9D=BF=E6=96=87=E4=BB=B6=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BB=A5=E5=8F=8A=E9=87=8D=E5=91=BD=E5=90=8D=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=90=8D=E7=A7=B0=E9=97=AE=E9=A2=98=20fix:=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E9=A1=B9=E7=9B=AE=E7=9B=AE=E5=BD=95=E4=B8=AD=E4=B8=8D?= =?UTF-8?q?=E5=AD=98=E5=9C=A8entities=E7=9A=84=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/create.js | 35 +++++++++++++++++++++++++++++---- scripts/make-app-domain.js | 8 +++++++- src/create.ts | 40 +++++++++++++++++++++++++++++++++----- 3 files changed, 73 insertions(+), 10 deletions(-) diff --git a/lib/create.js b/lib/create.js index f1a5b1b..350a1a7 100644 --- a/lib/create.js +++ b/lib/create.js @@ -191,8 +191,22 @@ async function create(dirName, cmd) { (0, file_handle_1.checkFileExistsAndCreate)(rootPath); // 复制项目文件 if (isModule) { - // 模块化的项目,只拷贝src和typings目录 - (0, file_handle_1.copyFolder)((0, path_1.join)(emptyTemplatePath, 'src'), (0, path_1.join)(rootPath, 'src')); + // 模块化的项目,只拷贝 src 下的内容,但跳过 pages 目录;同时拷贝 typings + const templateSrc = (0, path_1.join)(emptyTemplatePath, 'src'); + const destSrc = (0, path_1.join)(rootPath, 'src'); + // 确保目标 src 目录存在 + if (!(0, fs_1.existsSync)(destSrc)) { + (0, fs_1.mkdirSync)(destSrc, { recursive: true }); + } + const entries = (0, fs_1.readdirSync)(templateSrc, { withFileTypes: true }); + for (const entry of entries) { + if (entry.name === 'pages') { + continue; // 模块模式下跳过 pages + } + const from = (0, path_1.join)(templateSrc, entry.name); + const to = (0, path_1.join)(destSrc, entry.name); + (0, file_handle_1.copyFolder)(from, to); + } (0, file_handle_1.copyFolder)((0, path_1.join)(emptyTemplatePath, 'typings'), (0, path_1.join)(rootPath, 'typings')); } else { @@ -256,8 +270,21 @@ async function create(dirName, cmd) { }); // 创建package.json (0, file_handle_1.checkFileExistsAndCreate)(packageJsonPath, packageJson, enum_1.checkFileExistsAndCreateType.FILE); - (0, rename_1.renameProject)(rootPath, name, title, DEFAULT_PROJECT_NAME, DEFAULT_PROJECT_TITLE); - if (example) { + // 只在非模块化模式下重命名整个项目(包括web、wechatMp等) + if (!isModule) { + (0, rename_1.renameProject)(rootPath, name, title, DEFAULT_PROJECT_NAME, DEFAULT_PROJECT_TITLE); + } + else { + // 模块化模式下只更新 package.json 的 name + const packageJsonFilePath = (0, path_1.join)(rootPath, 'package.json'); + const packageJsonContent = (0, fs_1.readFileSync)(packageJsonFilePath, 'utf-8'); + const packageJsonJson = JSON.parse(packageJsonContent); + packageJsonJson.name = name; + const newPackageJsonContent = JSON.stringify(packageJsonJson, undefined, 4); + (0, fs_1.writeFileSync)(packageJsonFilePath, newPackageJsonContent); + (0, tip_style_1.Success)(`${(0, tip_style_1.success)(`Change project name to ${(0, tip_style_1.primary)(name)}`)}`); + } + if (example && !isModule) { // todo: copy template example files (0, file_handle_1.copyFolder)(examplePath, rootPath, true); } diff --git a/scripts/make-app-domain.js b/scripts/make-app-domain.js index aa274a9..115ac69 100644 --- a/scripts/make-app-domain.js +++ b/scripts/make-app-domain.js @@ -19,6 +19,12 @@ dependencies.forEach( } ); -analyzeEntities(join(process.cwd(), 'src', 'entities')); +const projectEntitiesPath = join(process.cwd(), 'src', 'entities'); +if (existsSync(projectEntitiesPath)) { + analyzeEntities(projectEntitiesPath, 'src/entities'); +} else { + console.warn('no project entities found'); +} + removeSync(join(process.cwd(), 'src', 'oak-app-domain')); buildSchema(join(process.cwd(), 'src', 'oak-app-domain')); \ No newline at end of file diff --git a/src/create.ts b/src/create.ts index d26c1ef..6e6c817 100644 --- a/src/create.ts +++ b/src/create.ts @@ -1,5 +1,5 @@ import * as ts from 'typescript'; -import { writeFileSync } from 'fs'; +import { writeFileSync, readFileSync, readdirSync, existsSync, mkdirSync } from 'fs'; const { factory } = ts; import { @@ -279,8 +279,22 @@ export async function create(dirName: string, cmd: any) { checkFileExistsAndCreate(rootPath); // 复制项目文件 if (isModule) { - // 模块化的项目,只拷贝src和typings目录 - copyFolder(join(emptyTemplatePath, 'src'), join(rootPath, 'src')); + // 模块化的项目,只拷贝 src 下的内容,但跳过 pages 目录;同时拷贝 typings + const templateSrc = join(emptyTemplatePath, 'src'); + const destSrc = join(rootPath, 'src'); + // 确保目标 src 目录存在 + if (!existsSync(destSrc)) { + mkdirSync(destSrc, { recursive: true }); + } + const entries = readdirSync(templateSrc, { withFileTypes: true }); + for (const entry of entries) { + if (entry.name === 'pages') { + continue; // 模块模式下跳过 pages + } + const from = join(templateSrc, entry.name); + const to = join(destSrc, entry.name); + copyFolder(from, to); + } copyFolder(join(emptyTemplatePath, 'typings'), join(rootPath, 'typings')); } else { @@ -392,9 +406,25 @@ export async function create(dirName: string, cmd: any) { checkFileExistsAndCreateType.FILE ); - renameProject(rootPath, name, title, DEFAULT_PROJECT_NAME, DEFAULT_PROJECT_TITLE); + // 只在非模块化模式下重命名整个项目(包括web、wechatMp等) + if (!isModule) { + renameProject(rootPath, name, title, DEFAULT_PROJECT_NAME, DEFAULT_PROJECT_TITLE); + } else { + // 模块化模式下只更新 package.json 的 name + const packageJsonFilePath = join(rootPath, 'package.json'); + const packageJsonContent = readFileSync(packageJsonFilePath, 'utf-8'); + const packageJsonJson = JSON.parse(packageJsonContent); + packageJsonJson.name = name; + const newPackageJsonContent = JSON.stringify(packageJsonJson, undefined, 4); + writeFileSync(packageJsonFilePath, newPackageJsonContent); + Success( + `${success( + `Change project name to ${primary(name)}` + )}` + ); + } - if (example) { + if (example && !isModule) { // todo: copy template example files copyFolder(examplePath, rootPath, true); } From 19393583679e5dca70a77a52517783b1ded4f46b Mon Sep 17 00:00:00 2001 From: qcqcqc <1220204124@zust.edu.cn> Date: Tue, 4 Nov 2025 13:45:29 +0800 Subject: [PATCH 05/13] =?UTF-8?q?fix:=20=E5=A4=8D=E5=88=B6=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E6=97=B6=E8=A6=81=E5=B8=A6=E6=9C=89.gitignore?= =?UTF-8?q?=E5=92=8Coak.config.json=E4=B8=A4=E4=B8=AA=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/create.js | 6 ++++++ src/create.ts | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/lib/create.js b/lib/create.js index 350a1a7..401e23b 100644 --- a/lib/create.js +++ b/lib/create.js @@ -248,6 +248,12 @@ async function create(dirName, cmd) { (0, file_handle_1.checkFileExistsAndCreate)(tsConfigMpJsonPath, tsConfigMpJson, enum_1.checkFileExistsAndCreateType.FILE); // 创建tsconfig.web.json (0, file_handle_1.checkFileExistsAndCreate)(tsConfigWebJsonPath, tsConfigWebJson, enum_1.checkFileExistsAndCreateType.FILE); + // 复制.gitignore + const gitignoreContent = (0, file_handle_1.readFile)((0, path_1.join)(__dirname, '..', 'template', '.gitignore')); + (0, file_handle_1.checkFileExistsAndCreate)((0, path_1.join)(rootPath, '.gitignore'), gitignoreContent, enum_1.checkFileExistsAndCreateType.FILE); + // 复制oak.config.json + const oakConfigContent = (0, file_handle_1.readFile)((0, path_1.join)(__dirname, '..', 'template', config_1.USER_CONFIG_FILE_NAME)); + (0, file_handle_1.checkFileExistsAndCreate)((0, path_1.join)(rootPath, config_1.USER_CONFIG_FILE_NAME), oakConfigContent, enum_1.checkFileExistsAndCreateType.FILE); // 更新configuration/compiler.js (仅在非模块化模式下) if (!isModule) { (0, template_1.updateCompilerJsContent)(rootPath, deps); diff --git a/src/create.ts b/src/create.ts index 6e6c817..593f035 100644 --- a/src/create.ts +++ b/src/create.ts @@ -370,6 +370,20 @@ export async function create(dirName: string, cmd: any) { tsConfigWebJson, checkFileExistsAndCreateType.FILE ); + // 复制.gitignore + const gitignoreContent = readFile(join(__dirname, '..', 'template', '.gitignore')); + checkFileExistsAndCreate( + join(rootPath, '.gitignore'), + gitignoreContent, + checkFileExistsAndCreateType.FILE + ); + // 复制oak.config.json + const oakConfigContent = readFile(join(__dirname, '..', 'template', USER_CONFIG_FILE_NAME)); + checkFileExistsAndCreate( + join(rootPath, USER_CONFIG_FILE_NAME), + oakConfigContent, + checkFileExistsAndCreateType.FILE + ); // 更新configuration/compiler.js (仅在非模块化模式下) if (!isModule) { updateCompilerJsContent(rootPath, deps); From 23865e639d5d43664477ece2d8458604b143eb68 Mon Sep 17 00:00:00 2001 From: qcqcqc <1220204124@zust.edu.cn> Date: Thu, 6 Nov 2025 15:34:20 +0800 Subject: [PATCH 06/13] =?UTF-8?q?feat:=20=E7=8E=B0=E5=B0=86=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E5=A4=84=E7=90=86=E5=99=A8=E6=B3=A8=E5=86=8C=E5=88=B0?= =?UTF-8?q?appLoader=EF=BC=8C=E4=B8=8D=E5=86=8D=E9=87=8D=E8=BD=BDconsole.e?= =?UTF-8?q?rror?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/server/start.js | 21 ++++++++++++--------- lib/types/index.d.ts | 10 ++++++---- src/server/start.ts | 25 ++++++++++++++----------- src/types/index.ts | 10 ++++++---- 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/lib/server/start.js b/lib/server/start.js index 51e772a..ba0ea3a 100644 --- a/lib/server/start.js +++ b/lib/server/start.js @@ -173,15 +173,18 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) { return result; } if (errorHandler && typeof errorHandler === 'function') { - (0, polyfill_1.polyfillConsole)("startup", true, (props) => { - if (props.level === "error") { - appLoader.execRoutine(async (ctx) => { - await errorHandler(props.caller, props.args, ctx); - }).catch((err) => { - console.warn('执行全局错误处理失败:', err); - }); - } - return props.args; + // polyfillConsole("startup", true, (props) => { + // if (props.level === "error") { + // appLoader.execRoutine(async (ctx) => { + // await errorHandler(props.caller, props.args, ctx); + // }).catch((err) => { + // console.warn('执行全局错误处理失败:', err); + // }); + // } + // return props.args; + // }); + appLoader.registerInternalErrorHandler(async (ctx, type, msg, err, oakErr) => { + await errorHandler(ctx, type, msg, err, oakErr); }); } // 否则启动服务器模式 diff --git a/lib/types/index.d.ts b/lib/types/index.d.ts index 6ae2aa9..7ab4e10 100644 --- a/lib/types/index.d.ts +++ b/lib/types/index.d.ts @@ -1,4 +1,6 @@ -import { EntityDict } from "oak-domain/lib/types"; -import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; -import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore'; -export type ErrorHandler = (caller: NodeJS.CallSite | null, args: any[], ctx: AsyncContext) => Promise; +import { BaseEntityDict } from "oak-domain"; +import { AsyncContext } from "oak-domain/lib/store/AsyncRowStore"; +import { EntityDict, OakException } from "oak-domain/lib/types"; +export type InternalErrorType = 'aspect' | 'trigger' | 'watcher' | 'timer' | 'checkpoint'; +export type InternalErrorHandler> = (ctx: Cxt, type: InternalErrorType, message: string, err: Error, oakException?: OakException) => Promise; +export type ExceptionPublisher = (type: string, message: string, err: any) => Promise; diff --git a/src/server/start.ts b/src/server/start.ts index 80485f8..43f4087 100644 --- a/src/server/start.ts +++ b/src/server/start.ts @@ -26,7 +26,7 @@ import chalk from 'chalk'; import { checkNodeVersion, randomString } from '../utils'; import bcrypt from 'bcryptjs'; import { LogFormatter, polyfillConsole, removePolyfill } from './polyfill'; -import { ErrorHandler } from '../types'; +import { InternalErrorHandler } from '../types'; checkNodeVersion() @@ -57,7 +57,7 @@ export async function startup) => Promise, ): Promise<(() => Promise) | any> { - let errorHandler: ErrorHandler | undefined = undefined; + let errorHandler: InternalErrorHandler | undefined = undefined; try { errorHandler = require(join( @@ -221,15 +221,18 @@ export async function startup { - if (props.level === "error") { - appLoader.execRoutine(async (ctx) => { - await errorHandler(props.caller, props.args, ctx); - }).catch((err) => { - console.warn('执行全局错误处理失败:', err); - }); - } - return props.args; + // polyfillConsole("startup", true, (props) => { + // if (props.level === "error") { + // appLoader.execRoutine(async (ctx) => { + // await errorHandler(props.caller, props.args, ctx); + // }).catch((err) => { + // console.warn('执行全局错误处理失败:', err); + // }); + // } + // return props.args; + // }); + appLoader.registerInternalErrorHandler(async (ctx, type, msg, err, oakErr) => { + await errorHandler(ctx, type, msg, err, oakErr); }); } diff --git a/src/types/index.ts b/src/types/index.ts index 0b41650..f51d2fc 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,7 @@ -import { EntityDict } from "oak-domain/lib/types"; -import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; -import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore'; +import { BaseEntityDict } from "oak-domain"; +import { AsyncContext } from "oak-domain/lib/store/AsyncRowStore"; +import { EntityDict, OakException } from "oak-domain/lib/types"; -export type ErrorHandler = ( caller: NodeJS.CallSite | null, args: any[], ctx: AsyncContext) => Promise; \ No newline at end of file +export type InternalErrorType = 'aspect' | 'trigger' | 'watcher' | 'timer' | 'checkpoint' +export type InternalErrorHandler> = (ctx: Cxt, type: InternalErrorType, message:string, err: Error, oakException?: OakException, ) => Promise; +export type ExceptionPublisher = (type: string, message: string, err: any) => Promise; \ No newline at end of file From 2d8690abb6aa8088b9ebed5356e1abf26a4fcb27 Mon Sep 17 00:00:00 2001 From: qcqcqc <1220204124@zust.edu.cn> Date: Fri, 7 Nov 2025 10:01:49 +0800 Subject: [PATCH 07/13] =?UTF-8?q?feat:=20=E5=BC=82=E5=B8=B8=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=99=A8=E6=94=B9=E4=B8=BA=E4=BB=8Elib/configuration/?= =?UTF-8?q?exception=E5=AF=BC=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/server/start.js | 4 ++-- src/server/start.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/server/start.js b/lib/server/start.js index ba0ea3a..f75ab8a 100644 --- a/lib/server/start.js +++ b/lib/server/start.js @@ -41,10 +41,10 @@ function concat(...paths) { async function startup(path, connector, omitWatchers, omitTimers, routine) { let errorHandler = undefined; try { - errorHandler = require((0, path_1.join)(path, 'lib', 'configuration', 'errors')).default; + errorHandler = require((0, path_1.join)(path, 'lib', 'configuration', 'exception')).default; } catch (err) { - // 不存在errors配置 + // 不存在exception配置 } const serverConfiguration = require((0, path_1.join)(path, 'lib', 'configuration', 'server')).default; // 拿到package.json,用作项目的唯一标识,否则无法区分不同项目的Redis+socketIO连接 diff --git a/src/server/start.ts b/src/server/start.ts index 43f4087..ed10cc0 100644 --- a/src/server/start.ts +++ b/src/server/start.ts @@ -64,10 +64,10 @@ export async function startup Date: Tue, 11 Nov 2025 11:03:39 +0800 Subject: [PATCH 08/13] =?UTF-8?q?feat:=20=E6=94=B9=E4=B8=BA=E4=BB=8EappLoa?= =?UTF-8?q?der=E5=8A=A0=E8=BD=BDExceptionHandler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/server/start.js | 49 ++++++++++++++++++++++------------------ lib/types/index.d.ts | 4 ++-- src/server/start.ts | 54 ++++++++++++++++++++++---------------------- src/types/index.ts | 7 ------ 4 files changed, 56 insertions(+), 58 deletions(-) delete mode 100644 src/types/index.ts diff --git a/lib/server/start.js b/lib/server/start.js index f75ab8a..6719b22 100644 --- a/lib/server/start.js +++ b/lib/server/start.js @@ -39,13 +39,17 @@ function concat(...paths) { }); } async function startup(path, connector, omitWatchers, omitTimers, routine) { - let errorHandler = undefined; - try { - errorHandler = require((0, path_1.join)(path, 'lib', 'configuration', 'exception')).default; - } - catch (err) { - // 不存在exception配置 - } + // let errorHandler: InternalErrorHandler | undefined = undefined; + // try { + // errorHandler = require(join( + // path, + // 'lib', + // 'configuration', + // 'exception' + // )).default; + // } catch (err) { + // // 不存在exception配置 + // } const serverConfiguration = require((0, path_1.join)(path, 'lib', 'configuration', 'server')).default; // 拿到package.json,用作项目的唯一标识,否则无法区分不同项目的Redis+socketIO连接 const packageJson = require((0, path_1.join)(path, 'package.json')); @@ -172,21 +176,22 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) { await appLoader.unmount(); return result; } - if (errorHandler && typeof errorHandler === 'function') { - // polyfillConsole("startup", true, (props) => { - // if (props.level === "error") { - // appLoader.execRoutine(async (ctx) => { - // await errorHandler(props.caller, props.args, ctx); - // }).catch((err) => { - // console.warn('执行全局错误处理失败:', err); - // }); - // } - // return props.args; - // }); - appLoader.registerInternalErrorHandler(async (ctx, type, msg, err, oakErr) => { - await errorHandler(ctx, type, msg, err, oakErr); - }); - } + // if (errorHandler && typeof errorHandler === 'function') { + // // polyfillConsole("startup", true, (props) => { + // // if (props.level === "error") { + // // appLoader.execRoutine(async (ctx) => { + // // await errorHandler(props.caller, props.args, ctx); + // // }).catch((err) => { + // // console.warn('执行全局错误处理失败:', err); + // // }); + // // } + // // return props.args; + // // }); + // // appLoader.registerInternalErrorHandler(async (ctx, type, msg, err) => { + // // await errorHandler(ctx, type, msg, err); + // // }); + // } + appLoader.regAllExceptionHandler(); // 否则启动服务器模式 koa.use(async (ctx, next) => { try { diff --git a/lib/types/index.d.ts b/lib/types/index.d.ts index 7ab4e10..37d97a4 100644 --- a/lib/types/index.d.ts +++ b/lib/types/index.d.ts @@ -1,6 +1,6 @@ import { BaseEntityDict } from "oak-domain"; import { AsyncContext } from "oak-domain/lib/store/AsyncRowStore"; -import { EntityDict, OakException } from "oak-domain/lib/types"; +import { EntityDict } from "oak-domain/lib/types"; export type InternalErrorType = 'aspect' | 'trigger' | 'watcher' | 'timer' | 'checkpoint'; -export type InternalErrorHandler> = (ctx: Cxt, type: InternalErrorType, message: string, err: Error, oakException?: OakException) => Promise; +export type InternalErrorHandler> = (ctx: Cxt, type: InternalErrorType, message: string, err: Error) => Promise; export type ExceptionPublisher = (type: string, message: string, err: any) => Promise; diff --git a/src/server/start.ts b/src/server/start.ts index ed10cc0..b10a6b7 100644 --- a/src/server/start.ts +++ b/src/server/start.ts @@ -26,7 +26,6 @@ import chalk from 'chalk'; import { checkNodeVersion, randomString } from '../utils'; import bcrypt from 'bcryptjs'; import { LogFormatter, polyfillConsole, removePolyfill } from './polyfill'; -import { InternalErrorHandler } from '../types'; checkNodeVersion() @@ -57,18 +56,18 @@ export async function startup) => Promise, ): Promise<(() => Promise) | any> { - let errorHandler: InternalErrorHandler | undefined = undefined; + // let errorHandler: InternalErrorHandler | undefined = undefined; - try { - errorHandler = require(join( - path, - 'lib', - 'configuration', - 'exception' - )).default; - } catch (err) { - // 不存在exception配置 - } + // try { + // errorHandler = require(join( + // path, + // 'lib', + // 'configuration', + // 'exception' + // )).default; + // } catch (err) { + // // 不存在exception配置 + // } const serverConfiguration: ServerConfiguration = require(join( path, @@ -220,21 +219,22 @@ export async function startup { - // if (props.level === "error") { - // appLoader.execRoutine(async (ctx) => { - // await errorHandler(props.caller, props.args, ctx); - // }).catch((err) => { - // console.warn('执行全局错误处理失败:', err); - // }); - // } - // return props.args; - // }); - appLoader.registerInternalErrorHandler(async (ctx, type, msg, err, oakErr) => { - await errorHandler(ctx, type, msg, err, oakErr); - }); - } + // if (errorHandler && typeof errorHandler === 'function') { + // // polyfillConsole("startup", true, (props) => { + // // if (props.level === "error") { + // // appLoader.execRoutine(async (ctx) => { + // // await errorHandler(props.caller, props.args, ctx); + // // }).catch((err) => { + // // console.warn('执行全局错误处理失败:', err); + // // }); + // // } + // // return props.args; + // // }); + // // appLoader.registerInternalErrorHandler(async (ctx, type, msg, err) => { + // // await errorHandler(ctx, type, msg, err); + // // }); + // } + appLoader.regAllExceptionHandler() // 否则启动服务器模式 koa.use(async (ctx, next) => { diff --git a/src/types/index.ts b/src/types/index.ts deleted file mode 100644 index f51d2fc..0000000 --- a/src/types/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { BaseEntityDict } from "oak-domain"; -import { AsyncContext } from "oak-domain/lib/store/AsyncRowStore"; -import { EntityDict, OakException } from "oak-domain/lib/types"; - -export type InternalErrorType = 'aspect' | 'trigger' | 'watcher' | 'timer' | 'checkpoint' -export type InternalErrorHandler> = (ctx: Cxt, type: InternalErrorType, message:string, err: Error, oakException?: OakException, ) => Promise; -export type ExceptionPublisher = (type: string, message: string, err: any) => Promise; \ No newline at end of file From 896d53561e367e344e9e92fb0976f4cdc6cd0989 Mon Sep 17 00:00:00 2001 From: qcqcqc <1220204124@zust.edu.cn> Date: Tue, 11 Nov 2025 18:02:54 +0800 Subject: [PATCH 09/13] =?UTF-8?q?feat:=20serverConfiguration=E6=94=AF?= =?UTF-8?q?=E6=8C=81middleware=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/server/start.js | 21 +++++++++++++++++++-- src/server/start.ts | 23 +++++++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/lib/server/start.js b/lib/server/start.js index 6719b22..db8553b 100644 --- a/lib/server/start.js +++ b/lib/server/start.js @@ -9,7 +9,7 @@ const path_1 = require("path"); const koa_1 = tslib_1.__importDefault(require("koa")); const koa_router_1 = tslib_1.__importDefault(require("koa-router")); const koa_body_1 = tslib_1.__importDefault(require("koa-body")); -const koa_logger_1 = tslib_1.__importDefault(require("koa-logger")); +// import logger from 'koa-logger'; const oak_backend_base_1 = require("oak-backend-base"); const types_1 = require("oak-domain/lib/types"); const cluster_adapter_1 = require("@socket.io/cluster-adapter"); @@ -63,7 +63,24 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) { const corsMethods = ['PUT', 'POST', 'GET', 'DELETE', 'OPTIONS']; const koa = new koa_1.default(); // 使用 koa-logger 中间件打印日志 - koa.use((0, koa_logger_1.default)()); + // koa.use(logger()); + // 注册自定义中间件 + if (serverConfiguration.middleware) { + if (Array.isArray(serverConfiguration.middleware)) { + serverConfiguration.middleware.forEach((mw) => { + koa.use(mw); + }); + } + else if (typeof serverConfiguration.middleware === 'function') { + const mws = serverConfiguration.middleware(koa); + if (!Array.isArray(mws)) { + throw new Error('middleware 配置函数必须返回 Koa.Middleware 数组'); + } + mws.forEach((mw) => { + koa.use(mw); + }); + } + } // socket const httpServer = (0, http_1.createServer)(koa.callback()); const socketPath = connector.getSocketPath(); diff --git a/src/server/start.ts b/src/server/start.ts index b10a6b7..e9ebb97 100644 --- a/src/server/start.ts +++ b/src/server/start.ts @@ -5,7 +5,7 @@ import PathLib, { join } from 'path'; import Koa from 'koa'; import KoaRouter from 'koa-router'; import KoaBody from 'koa-body'; -import logger from 'koa-logger'; +// import logger from 'koa-logger'; import { AppLoader, getClusterInfo, ClusterAppLoader } from 'oak-backend-base'; import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext'; @@ -88,7 +88,26 @@ export async function startup { + koa.use(mw); + }); + } + else if (typeof serverConfiguration.middleware === 'function') { + const mws = serverConfiguration.middleware(koa); + if (!Array.isArray(mws)) { + throw new Error('middleware 配置函数必须返回 Koa.Middleware 数组'); + } + mws.forEach((mw) => { + koa.use(mw); + }); + } + } + // socket const httpServer = createServer(koa.callback()); const socketPath = connector.getSocketPath(); From 1440147f3e63be06c8194bf412681eacc0b22b1c Mon Sep 17 00:00:00 2001 From: qcqcqc <1220204124@zust.edu.cn> Date: Tue, 11 Nov 2025 18:13:07 +0800 Subject: [PATCH 10/13] =?UTF-8?q?fix:=20=E5=B0=86=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E4=B8=AD=E9=97=B4=E4=BB=B6=E7=A7=BB=E5=8A=A8=E5=88=B0?= =?UTF-8?q?koa-body=E4=B9=8B=E5=90=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/server/start.js | 34 +++++++++++++++++----------------- src/server/start.ts | 35 +++++++++++++++++------------------ 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/lib/server/start.js b/lib/server/start.js index db8553b..e6d9e6f 100644 --- a/lib/server/start.js +++ b/lib/server/start.js @@ -64,23 +64,6 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) { const koa = new koa_1.default(); // 使用 koa-logger 中间件打印日志 // koa.use(logger()); - // 注册自定义中间件 - if (serverConfiguration.middleware) { - if (Array.isArray(serverConfiguration.middleware)) { - serverConfiguration.middleware.forEach((mw) => { - koa.use(mw); - }); - } - else if (typeof serverConfiguration.middleware === 'function') { - const mws = serverConfiguration.middleware(koa); - if (!Array.isArray(mws)) { - throw new Error('middleware 配置函数必须返回 Koa.Middleware 数组'); - } - mws.forEach((mw) => { - koa.use(mw); - }); - } - } // socket const httpServer = (0, http_1.createServer)(koa.callback()); const socketPath = connector.getSocketPath(); @@ -229,6 +212,23 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) { koa.use((0, koa_body_1.default)(Object.assign({ multipart: true, }, serverConfiguration.koaBody))); + // 注册自定义中间件 + if (serverConfiguration.middleware) { + if (Array.isArray(serverConfiguration.middleware)) { + serverConfiguration.middleware.forEach((mw) => { + koa.use(mw); + }); + } + else if (typeof serverConfiguration.middleware === 'function') { + const mws = serverConfiguration.middleware(koa); + if (!Array.isArray(mws)) { + throw new Error('middleware 配置函数必须返回 Koa.Middleware 数组'); + } + mws.forEach((mw) => { + koa.use(mw); + }); + } + } const router = new koa_router_1.default(); // 如果是开发环境,允许options if (['development', 'staging'].includes(process.env.NODE_ENV)) { diff --git a/src/server/start.ts b/src/server/start.ts index e9ebb97..598ba37 100644 --- a/src/server/start.ts +++ b/src/server/start.ts @@ -90,24 +90,6 @@ export async function startup { - koa.use(mw); - }); - } - else if (typeof serverConfiguration.middleware === 'function') { - const mws = serverConfiguration.middleware(koa); - if (!Array.isArray(mws)) { - throw new Error('middleware 配置函数必须返回 Koa.Middleware 数组'); - } - mws.forEach((mw) => { - koa.use(mw); - }); - } - } - // socket const httpServer = createServer(koa.callback()); const socketPath = connector.getSocketPath(); @@ -283,6 +265,23 @@ export async function startup { + koa.use(mw); + }); + } + else if (typeof serverConfiguration.middleware === 'function') { + const mws = serverConfiguration.middleware(koa); + if (!Array.isArray(mws)) { + throw new Error('middleware 配置函数必须返回 Koa.Middleware 数组'); + } + mws.forEach((mw) => { + koa.use(mw); + }); + } + } const router = new KoaRouter(); // 如果是开发环境,允许options From ef0d846f0a7d04cae1272bf6e390e101cb3bf402 Mon Sep 17 00:00:00 2001 From: qcqcqc <1220204124@zust.edu.cn> Date: Wed, 26 Nov 2025 17:09:10 +0800 Subject: [PATCH 11/13] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84watch=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E5=99=A8=EF=BC=8C=E7=8E=B0=E7=BC=96=E8=AF=91=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E4=B9=8B=E5=90=8E=E4=BC=9A=E8=87=AA=E5=8A=A8=E8=BF=9B?= =?UTF-8?q?=E8=A1=8Calias=E6=9B=BF=E6=8D=A2=EF=BC=8C=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=97=B6=E9=97=AE=E9=A2=98=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E4=BF=AE=E5=A4=8Dserver=E5=90=AF=E5=8A=A8=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E6=95=B4=E4=BD=93=E8=BF=9B=E7=A8=8B=E9=80=80?= =?UTF-8?q?=E5=87=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/server/watch.js | 163 +++++++++++++++++++++++++---------------- package.json | 1 + src/server/watch.ts | 171 ++++++++++++++++++++++++++++---------------- 3 files changed, 211 insertions(+), 124 deletions(-) diff --git a/lib/server/watch.js b/lib/server/watch.js index d93019a..8019e88 100644 --- a/lib/server/watch.js +++ b/lib/server/watch.js @@ -7,8 +7,8 @@ const typescript_1 = tslib_1.__importDefault(require("typescript")); const path_1 = tslib_1.__importDefault(require("path")); const dayjs_1 = tslib_1.__importDefault(require("dayjs")); const fs_1 = tslib_1.__importDefault(require("fs")); -const lodash_1 = require("lodash"); const polyfill_1 = require("./polyfill"); +const tsc_alias_1 = require("tsc-alias"); // 创建事件发射器 const createEventEmitter = () => { const listeners = new Map(); @@ -227,12 +227,19 @@ const createServerManager = (projectPath, eventEmitter, config) => { // eslint-disable-next-line @typescript-eslint/no-var-requires const simpleConnector = require(path_1.default.join(projectPath, "lib/config/connector")).default; console.warn("----> Starting service......"); - // 这里注意要在require之前,因为require会触发编译 - const { startup } = require('./start'); - shutdown = await startup(pwd, simpleConnector).then((shutdown) => { - config.lifecycle.onServerStart(config); - return shutdown; - }); + try { + // 这里注意要在require之前,因为require会触发编译 + const { startup } = require('./start'); + shutdown = await startup(pwd, simpleConnector).then((shutdown) => { + config.lifecycle.onServerStart(config); + return shutdown; + }); + } + catch (error) { + console.error("----> Failed to start service:", error); + isRestarting = false; + return; + } isRestarting = false; await eventEmitter.emit("server-restarted", {}); }; @@ -248,7 +255,7 @@ const createServerManager = (projectPath, eventEmitter, config) => { }; }; // 创建编译器 -const createCompiler = (projectPath, options, projectReferences, aliasConfig, config) => { +const createCompiler = async (projectPath, options, projectReferences, treatFile, config) => { const createProgramAndSourceFile = (path) => { const program = typescript_1.default.createProgram({ rootNames: [path], @@ -375,6 +382,8 @@ const createCompiler = (projectPath, options, projectReferences, aliasConfig, co else { console.log(`Emit succeeded for ${filePath}.`); config.lifecycle.onAfterCompile(config); + const jsFilePath = libPath; + treatFile(jsFilePath); return { taskId: task.id, success: true, @@ -445,40 +454,39 @@ const createFileWatcher = (projectPath, eventEmitter) => { const generateTaskId = () => { return `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; }; -/** - * 根据 alias 配置表将路径中的别名替换为真实路径 - * @param path - 输入的路径字符串,例如 "@project/file" 或 "@oak-app-domain/some-module" - * @param aliasConfig - alias 配置表,key 为别名,value 为对应的真实路径或路径数组 - * @returns 替换后的路径,如果没有匹配到 alias,则返回原始路径 - */ -const replaceAliasWithPath = (path, aliasConfig) => { - for (const [alias, targets] of Object.entries(aliasConfig)) { - // If alias ends with "*", handle it as a dynamic alias. - if (alias.endsWith('*')) { - // Create a regex pattern that matches paths starting with the alias, followed by any characters - const aliasPattern = new RegExp(`^${alias.replace(/\*$/, "")}(.*)`); // e.g., '@project/*' becomes '@project/(.*)' - const match = path.match(aliasPattern); - if (match) { - // Replace the alias with the target path, appending the matched part from the original path - const target = Array.isArray(targets) ? targets[0] : targets; // Take the first target (if it's an array) - // Ensure that the target path ends with a slash if it's not already - const replacedPath = target.replace(/\/\*$/, "/") + match[1]; - return replacedPath; - } - } - else { - // Handle static alias without "*" by directly matching the path - if (path.startsWith(alias)) { - const target = Array.isArray(targets) ? targets[0] : targets; // Take the first target (if it's an array) - // Replace the alias part with the target path - return path.replace(alias, target); - } - } - } - // If no alias matches, return the original path - return path; -}; -const watch = (projectPath, config) => { +// /** +// * 根据 alias 配置表将路径中的别名替换为真实路径 +// * @param path - 输入的路径字符串,例如 "@project/file" 或 "@oak-app-domain/some-module" +// * @param aliasConfig - alias 配置表,key 为别名,value 为对应的真实路径或路径数组 +// * @returns 替换后的路径,如果没有匹配到 alias,则返回原始路径 +// */ +// const replaceAliasWithPath = (path: string, aliasConfig: Record): string => { +// for (const [alias, targets] of Object.entries(aliasConfig)) { +// // If alias ends with "*", handle it as a dynamic alias. +// if (alias.endsWith('*')) { +// // Create a regex pattern that matches paths starting with the alias, followed by any characters +// const aliasPattern = new RegExp(`^${alias.replace(/\*$/, "")}(.*)`); // e.g., '@project/*' becomes '@project/(.*)' +// const match = path.match(aliasPattern); +// if (match) { +// // Replace the alias with the target path, appending the matched part from the original path +// const target = Array.isArray(targets) ? targets[0] : targets; // Take the first target (if it's an array) +// // Ensure that the target path ends with a slash if it's not already +// const replacedPath = target.replace(/\/\*$/, "/") + match[1]; +// return replacedPath; +// } +// } else { +// // Handle static alias without "*" by directly matching the path +// if (path.startsWith(alias)) { +// const target = Array.isArray(targets) ? targets[0] : targets; // Take the first target (if it's an array) +// // Replace the alias part with the target path +// return path.replace(alias, target); +// } +// } +// } +// // If no alias matches, return the original path +// return path; +// }; +const watch = async (projectPath, config) => { const realConfig = getOverrideConfig(config); const enableTrace = !!process.env.ENABLE_TRACE; // 查找配置文件 @@ -486,20 +494,30 @@ const watch = (projectPath, config) => { if (!configFileName) { throw new Error("Could not find a valid 'tsconfig.build.json'."); } - // 读取配置文件 - const configFile = typescript_1.default.readConfigFile(configFileName, typescript_1.default.sys.readFile); - // 解析配置文件内容 - const { options, projectReferences } = typescript_1.default.parseJsonConfigFileContent(configFile.config, typescript_1.default.sys, path_1.default.dirname(configFileName)); - const aliasConfig = (0, lodash_1.cloneDeep)(options.paths) || {}; - // 输出原始配置 - // console.log("[DEBUG] Original alias config:", aliasConfig); - Object.keys(aliasConfig).forEach((key) => { - const value = aliasConfig[key]; - // 替换src - aliasConfig[key] = typeof value === "string" ? value.replace("src", "lib") : value.map((v) => v.replace("src", "lib")); + const runFile = await (0, tsc_alias_1.prepareSingleFileReplaceTscAliasPaths)({ + configFile: path_1.default.join(projectPath, "tsconfig.build.json"), + resolveFullPaths: true, }); + function treatFile(filePath) { + const fileContents = fs_1.default.readFileSync(filePath, 'utf8'); + const newContents = runFile({ fileContents, filePath }); + // do stuff with newContents + fs_1.default.writeFileSync(filePath, newContents, 'utf8'); + } + // // 读取配置文件 + const configFile = typescript_1.default.readConfigFile(configFileName, typescript_1.default.sys.readFile); + // // 解析配置文件内容 + const { options, projectReferences } = typescript_1.default.parseJsonConfigFileContent(configFile.config, typescript_1.default.sys, path_1.default.dirname(configFileName)); + // const aliasConfig: AliasConfig = cloneDeep(options.paths) || {}; + // // 输出原始配置 + // // console.log("[DEBUG] Original alias config:", aliasConfig); + // Object.keys(aliasConfig).forEach((key) => { + // const value = aliasConfig[key]; + // // 替换src + // aliasConfig[key] = typeof value === "string" ? value.replace("src", "lib") : value.map((v: string) => v.replace("src", "lib")); + // }); // 输出真实的alias配置 - console.debug("[DEBUG] Running Alias config:", aliasConfig); + // console.debug("[DEBUG] Running Alias config:", aliasConfig); // 初始化polyfill const polyfillLoader = () => { const BuiltinModule = require("module"); @@ -530,6 +548,9 @@ const watch = (projectPath, config) => { console.error(`[resolve] Emit skipped for: ${tsPath}`); throw new Error("TypeScript emit skipped"); } + else { + treatFile(jsPath); + } console.log(`[resolve] Successfully compiled: ${tsPath}`); return jsPath; } @@ -559,11 +580,11 @@ const watch = (projectPath, config) => { rFoptions // 解析选项 ) { let resolvedRequest = request; - const replacedPath = replaceAliasWithPath(request, aliasConfig); - if (replacedPath !== request) { - console.log(`[resolve] Alias resolved: ${request} -> ${replacedPath}`); - resolvedRequest = path_1.default.join(projectPath, replacedPath); - } + // const replacedPath = replaceAliasWithPath(request, aliasConfig); + // if (replacedPath !== request) { + // console.log(`[resolve] Alias resolved: ${request} -> ${replacedPath}`); + // resolvedRequest = pathLib.join(projectPath, replacedPath); + // } try { return oldResolveFilename.call(this, resolvedRequest, parent, isMain, rFoptions); } @@ -631,14 +652,34 @@ const watch = (projectPath, config) => { "src/ports/index.ts", ]; compileFiles.forEach(tryCompile); + // 最后替换lib目录下所有的路径别名 + console.log(`[watch] Replacing path aliases in lib directory......`); + const libDir = path_1.default.join(projectPath, "lib"); + const walkDir = (dir) => { + const files = fs_1.default.readdirSync(dir); + files.forEach((file) => { + const fullPath = path_1.default.join(dir, file); + const stat = fs_1.default.statSync(fullPath); + if (stat.isDirectory()) { + walkDir(fullPath); + } + else if (stat.isFile() && fullPath.endsWith(".js")) { + const fileContents = fs_1.default.readFileSync(fullPath, 'utf8'); + const newContents = runFile({ fileContents, filePath: fullPath }); + fs_1.default.writeFileSync(fullPath, newContents, 'utf8'); + } + }); + }; + walkDir(libDir); + console.log(`[watch] Path alias replacement completed.`); } }; - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { // 创建事件系统 const eventEmitter = createEventEmitter(); // 创建各个组件 const compileQueue = createCompileQueue(eventEmitter); - const compiler = createCompiler(projectPath, options, projectReferences, aliasConfig, realConfig); + const compiler = await createCompiler(projectPath, options, projectReferences, treatFile, realConfig); const serverManager = createServerManager(projectPath, eventEmitter, realConfig); const fileWatcher = createFileWatcher(projectPath, eventEmitter); // 设置编译器处理器 diff --git a/package.json b/package.json index d435926..4ff5ca3 100644 --- a/package.json +++ b/package.json @@ -145,6 +145,7 @@ "stylelint-webpack-plugin": "^3.2.0", "tailwindcss": "^3.0.2", "terser-webpack-plugin": "^5.2.5", + "tsc-alias": "^1.8.16", "tslib": "^2.4.0", "ui-extract-webpack-plugin": "^1.0.0", "uuid": "^8.3.2", diff --git a/src/server/watch.ts b/src/server/watch.ts index abad55b..61ad4a0 100644 --- a/src/server/watch.ts +++ b/src/server/watch.ts @@ -3,9 +3,10 @@ import ts, { CompilerOptions, ProjectReference } from "typescript"; import pathLib from "path"; import dayjs from "dayjs"; import fs from "fs"; -import { cloneDeep } from "lodash"; +// import { cloneDeep } from "lodash"; import { AsyncContext } from "oak-domain/lib/store/AsyncRowStore"; import { LogFormatter, LogFormatterProp, polyfillConsole } from "./polyfill"; +import { prepareSingleFileReplaceTscAliasPaths, SingleFileReplacer } from 'tsc-alias'; /* * 工作流程 @@ -397,15 +398,22 @@ const createServerManager = ( console.warn("----> Starting service......"); - // 这里注意要在require之前,因为require会触发编译 - const { startup } = require('./start') as { - startup: (pwd: string, connector: any) => Promise<() => Promise>; - } + try { + // 这里注意要在require之前,因为require会触发编译 + const { startup } = require('./start') as { + startup: (pwd: string, connector: any) => Promise<() => Promise>; + } - shutdown = await startup(pwd, simpleConnector).then((shutdown) => { - config.lifecycle.onServerStart(config); - return shutdown; - }); + shutdown = await startup(pwd, simpleConnector).then((shutdown) => { + config.lifecycle.onServerStart(config); + return shutdown; + }); + + } catch (error) { + console.error("----> Failed to start service:", error); + isRestarting = false; + return; + } isRestarting = false; await eventEmitter.emit("server-restarted", {}); @@ -425,13 +433,14 @@ const createServerManager = ( }; // 创建编译器 -const createCompiler = ( +const createCompiler = async ( projectPath: string, options: CompilerOptions, projectReferences: readonly ProjectReference[] | undefined, - aliasConfig: Record, + treatFile: (filePath: string) => void, config: RealWatchConfig ) => { + const createProgramAndSourceFile = (path: string) => { const program = ts.createProgram({ rootNames: [path], @@ -578,6 +587,8 @@ const createCompiler = ( } else { console.log(`Emit succeeded for ${filePath}.`); config.lifecycle.onAfterCompile(config); + const jsFilePath = libPath; + treatFile(jsFilePath); return { taskId: task.id, success: true, @@ -666,42 +677,42 @@ const generateTaskId = (): string => { return `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; }; -/** - * 根据 alias 配置表将路径中的别名替换为真实路径 - * @param path - 输入的路径字符串,例如 "@project/file" 或 "@oak-app-domain/some-module" - * @param aliasConfig - alias 配置表,key 为别名,value 为对应的真实路径或路径数组 - * @returns 替换后的路径,如果没有匹配到 alias,则返回原始路径 - */ -const replaceAliasWithPath = (path: string, aliasConfig: Record): string => { - for (const [alias, targets] of Object.entries(aliasConfig)) { - // If alias ends with "*", handle it as a dynamic alias. - if (alias.endsWith('*')) { - // Create a regex pattern that matches paths starting with the alias, followed by any characters - const aliasPattern = new RegExp(`^${alias.replace(/\*$/, "")}(.*)`); // e.g., '@project/*' becomes '@project/(.*)' +// /** +// * 根据 alias 配置表将路径中的别名替换为真实路径 +// * @param path - 输入的路径字符串,例如 "@project/file" 或 "@oak-app-domain/some-module" +// * @param aliasConfig - alias 配置表,key 为别名,value 为对应的真实路径或路径数组 +// * @returns 替换后的路径,如果没有匹配到 alias,则返回原始路径 +// */ +// const replaceAliasWithPath = (path: string, aliasConfig: Record): string => { +// for (const [alias, targets] of Object.entries(aliasConfig)) { +// // If alias ends with "*", handle it as a dynamic alias. +// if (alias.endsWith('*')) { +// // Create a regex pattern that matches paths starting with the alias, followed by any characters +// const aliasPattern = new RegExp(`^${alias.replace(/\*$/, "")}(.*)`); // e.g., '@project/*' becomes '@project/(.*)' - const match = path.match(aliasPattern); - if (match) { - // Replace the alias with the target path, appending the matched part from the original path - const target = Array.isArray(targets) ? targets[0] : targets; // Take the first target (if it's an array) - // Ensure that the target path ends with a slash if it's not already - const replacedPath = target.replace(/\/\*$/, "/") + match[1]; - return replacedPath; - } - } else { - // Handle static alias without "*" by directly matching the path - if (path.startsWith(alias)) { - const target = Array.isArray(targets) ? targets[0] : targets; // Take the first target (if it's an array) - // Replace the alias part with the target path - return path.replace(alias, target); - } - } - } +// const match = path.match(aliasPattern); +// if (match) { +// // Replace the alias with the target path, appending the matched part from the original path +// const target = Array.isArray(targets) ? targets[0] : targets; // Take the first target (if it's an array) +// // Ensure that the target path ends with a slash if it's not already +// const replacedPath = target.replace(/\/\*$/, "/") + match[1]; +// return replacedPath; +// } +// } else { +// // Handle static alias without "*" by directly matching the path +// if (path.startsWith(alias)) { +// const target = Array.isArray(targets) ? targets[0] : targets; // Take the first target (if it's an array) +// // Replace the alias part with the target path +// return path.replace(alias, target); +// } +// } +// } - // If no alias matches, return the original path - return path; -}; +// // If no alias matches, return the original path +// return path; +// }; -export const watch = ( +export const watch = async ( projectPath: string, config?: WatchConfig ): Promise<() => Promise> => { @@ -719,29 +730,42 @@ export const watch = ( throw new Error("Could not find a valid 'tsconfig.build.json'."); } - // 读取配置文件 + const runFile: SingleFileReplacer = await prepareSingleFileReplaceTscAliasPaths({ + configFile: pathLib.join(projectPath, "tsconfig.build.json"), + resolveFullPaths: true, + }); + + function treatFile(filePath: string) { + console.log(`Processing file for alias replacement: ${filePath}`); + const fileContents = fs.readFileSync(filePath, 'utf8'); + const newContents = runFile({ fileContents, filePath }); + // do stuff with newContents + fs.writeFileSync(filePath, newContents, 'utf8'); + } + + // // 读取配置文件 const configFile = ts.readConfigFile(configFileName, ts.sys.readFile); - // 解析配置文件内容 + // // 解析配置文件内容 const { options, projectReferences } = ts.parseJsonConfigFileContent( configFile.config, ts.sys, pathLib.dirname(configFileName) ); - const aliasConfig: AliasConfig = cloneDeep(options.paths) || {}; + // const aliasConfig: AliasConfig = cloneDeep(options.paths) || {}; - // 输出原始配置 - // console.log("[DEBUG] Original alias config:", aliasConfig); + // // 输出原始配置 + // // console.log("[DEBUG] Original alias config:", aliasConfig); - Object.keys(aliasConfig).forEach((key) => { - const value = aliasConfig[key]; - // 替换src - aliasConfig[key] = typeof value === "string" ? value.replace("src", "lib") : value.map((v: string) => v.replace("src", "lib")); - }); + // Object.keys(aliasConfig).forEach((key) => { + // const value = aliasConfig[key]; + // // 替换src + // aliasConfig[key] = typeof value === "string" ? value.replace("src", "lib") : value.map((v: string) => v.replace("src", "lib")); + // }); // 输出真实的alias配置 - console.debug("[DEBUG] Running Alias config:", aliasConfig); + // console.debug("[DEBUG] Running Alias config:", aliasConfig); // 初始化polyfill const polyfillLoader = () => { @@ -781,6 +805,8 @@ export const watch = ( if (emitResult.emitSkipped) { console.error(`[resolve] Emit skipped for: ${tsPath}`); throw new Error("TypeScript emit skipped"); + } else { + treatFile(jsPath) } console.log(`[resolve] Successfully compiled: ${tsPath}`); @@ -823,13 +849,12 @@ export const watch = ( rFoptions: object | undefined // 解析选项 ) { let resolvedRequest = request; - const replacedPath = replaceAliasWithPath(request, aliasConfig); - - if (replacedPath !== request) { - console.log(`[resolve] Alias resolved: ${request} -> ${replacedPath}`); - resolvedRequest = pathLib.join(projectPath, replacedPath); - } + // const replacedPath = replaceAliasWithPath(request, aliasConfig); + // if (replacedPath !== request) { + // console.log(`[resolve] Alias resolved: ${request} -> ${replacedPath}`); + // resolvedRequest = pathLib.join(projectPath, replacedPath); + // } try { return oldResolveFilename.call(this, resolvedRequest, parent, isMain, rFoptions); } catch (error: any) { @@ -908,16 +933,36 @@ export const watch = ( "src/ports/index.ts", ]; compileFiles.forEach(tryCompile); + // 最后替换lib目录下所有的路径别名 + console.log(`[watch] Replacing path aliases in lib directory......`); + const libDir = pathLib.join(projectPath, "lib"); + const walkDir = (dir: string) => { + const files = fs.readdirSync(dir); + files.forEach((file) => { + const fullPath = pathLib.join(dir, file); + const stat = fs.statSync(fullPath); + if (stat.isDirectory()) { + walkDir(fullPath); + } + else if (stat.isFile() && fullPath.endsWith(".js")) { + const fileContents = fs.readFileSync(fullPath, 'utf8'); + const newContents = runFile({ fileContents, filePath: fullPath }); + fs.writeFileSync(fullPath, newContents, 'utf8'); + } + }); + }; + walkDir(libDir); + console.log(`[watch] Path alias replacement completed.`); } }; - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { // 创建事件系统 const eventEmitter = createEventEmitter(); // 创建各个组件 const compileQueue = createCompileQueue(eventEmitter); - const compiler = createCompiler(projectPath, options, projectReferences, aliasConfig, realConfig); + const compiler = await createCompiler(projectPath, options, projectReferences, treatFile, realConfig); const serverManager = createServerManager(projectPath, eventEmitter, realConfig); const fileWatcher = createFileWatcher(projectPath, eventEmitter); From 4140595e0ec1421eaa1652864aa4deb5bdcae261 Mon Sep 17 00:00:00 2001 From: Xc Date: Fri, 28 Nov 2025 10:43:10 +0800 Subject: [PATCH 12/13] =?UTF-8?q?template=E4=B8=AD=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E7=96=8F=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/template.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/template.ts b/src/template.ts index fa75c07..80c6675 100644 --- a/src/template.ts +++ b/src/template.ts @@ -436,7 +436,7 @@ export function tsConfigPathsJsonContent(deps: string[]) { compilerOptions: { baseUrl: "./", paths, - typeRoots: ["./typings"] + typeRoots: ["./typings", "node_modules/@types"] } }, null, '\t'); } From 2b0ffbffaf80ed2d654634f839db5bf1a29ed002 Mon Sep 17 00:00:00 2001 From: Xc Date: Tue, 2 Dec 2025 10:53:20 +0800 Subject: [PATCH 13/13] 4.0.29-pub --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 4ff5ca3..602c8ba 100644 --- a/package.json +++ b/package.json @@ -112,9 +112,9 @@ "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.5.3", "node-watch": "^0.7.4", - "oak-backend-base": "file:../oak-backend-base", - "oak-domain": "file:../oak-domain", - "oak-frontend-base": "file:../oak-frontend-base", + "oak-backend-base": "^4.1.24", + "oak-domain": "^5.1.30", + "oak-frontend-base": "^5.3.43", "parse-asn1": "5.1.6", "postcss": "^8.4.4", "postcss-flexbugs-fixes": "^5.0.2",