From 542036f46e065df81eeb872fae6a723574186fa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Thu, 29 Jan 2026 15:27:46 +0800 Subject: [PATCH] Refactor type build: inline external types, simplify scripts Removed custom build scripts for copying and inlining types, consolidating all post-build logic into a single enhanced post-build.mjs script. The new script processes .d.ts files, inlines external module types, updates imports, and copies necessary files to dist, eliminating the need for external-shims and simplifying the build process. Updated package.json scripts accordingly. Refactor type inlining: remove shims, auto-extract types Removed external-shims.d.ts and its references, replacing manual shims with an automated script that extracts type definitions from node_modules. Updated build scripts, dependencies, and test files to support the new inlining process. The inline-types.mjs script now scans for external imports, generates inline type files, and rewrites imports as import type, eliminating the need for hand-written shims. Add type inlining script and update build process Introduced a new script (inline-types.mjs) to inline external type dependencies into the dist directory, updated the build process to use this script, and removed the now-unnecessary external-shims.d.ts from the copy-dist script. Added a test file to verify inlined types, updated dependencies to include ts-morph, and adjusted package.json and pnpm-lock.yaml accordingly. --- packages/napcat-types/index.ts | 1 - packages/napcat-types/package.json | 7 -- packages/napcat-types/package.public.json | 9 +- packages/napcat-types/scripts/copy-dist.mjs | 6 +- packages/napcat-types/scripts/post-build.mjs | 115 ++++++++++++++++++- packages/napcat-types/tsconfig.json | 3 - pnpm-lock.yaml | 21 ---- 7 files changed, 115 insertions(+), 47 deletions(-) diff --git a/packages/napcat-types/index.ts b/packages/napcat-types/index.ts index 62fcf6db..319ebc7a 100644 --- a/packages/napcat-types/index.ts +++ b/packages/napcat-types/index.ts @@ -1,4 +1,3 @@ -/// // 聚合导出核心库的所有内容(包括枚举、类和类型) export * from '../napcat-core/index'; diff --git a/packages/napcat-types/package.json b/packages/napcat-types/package.json index ad5aa3fb..5e6cf297 100644 --- a/packages/napcat-types/package.json +++ b/packages/napcat-types/package.json @@ -14,13 +14,6 @@ }, "dependencies": { "@types/node": "^22.10.7", - "@types/express": "^4.17.21", - "@types/ws": "^8.5.12", - "@types/cors": "^2.8.17", - "@types/multer": "^1.4.12", - "@types/winston": "^2.4.4", - "@types/yaml": "^1.9.7", - "@types/ip": "^1.1.3", "@sinclair/typebox": "^0.34.38" }, "devDependencies": { diff --git a/packages/napcat-types/package.public.json b/packages/napcat-types/package.public.json index 3984acdb..517fdd23 100644 --- a/packages/napcat-types/package.public.json +++ b/packages/napcat-types/package.public.json @@ -1,6 +1,6 @@ { "name": "napcat-types", - "version": "0.0.9", + "version": "0.0.10", "private": false, "type": "module", "types": "./napcat-types/index.d.ts", @@ -9,13 +9,6 @@ ], "dependencies": { "@types/node": "^22.10.7", - "@types/express": "^4.17.21", - "@types/ws": "^8.5.12", - "@types/cors": "^2.8.17", - "@types/multer": "^1.4.12", - "@types/winston": "^2.4.4", - "@types/yaml": "^1.9.7", - "@types/ip": "^1.1.3", "@sinclair/typebox": "^0.34.38" }, "publishConfig": { diff --git a/packages/napcat-types/scripts/copy-dist.mjs b/packages/napcat-types/scripts/copy-dist.mjs index b97159bc..4cf85167 100644 --- a/packages/napcat-types/scripts/copy-dist.mjs +++ b/packages/napcat-types/scripts/copy-dist.mjs @@ -1,4 +1,4 @@ -// 复制 cp README.md dist/ && cp package.public.json dist/package.json && cp external-shims.d.ts dist/ +// 复制 cp README.md dist/ && cp package.public.json dist/package.json import { copyFile } from 'node:fs/promises'; import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -10,8 +10,4 @@ await copyFile( await copyFile( join(__dirname, 'README.md'), join(__dirname, 'dist', 'README.md') -); -await copyFile( - join(__dirname, 'external-shims.d.ts'), - join(__dirname, 'dist', 'external-shims.d.ts') ); \ No newline at end of file diff --git a/packages/napcat-types/scripts/post-build.mjs b/packages/napcat-types/scripts/post-build.mjs index 981bf0aa..6bc789c5 100644 --- a/packages/napcat-types/scripts/post-build.mjs +++ b/packages/napcat-types/scripts/post-build.mjs @@ -5,6 +5,111 @@ import { fileURLToPath } from 'node:url'; const __dirname = fileURLToPath(new URL('../', import.meta.url)); const distDir = join(__dirname, 'dist'); +// 允许保留的包(白名单) +const ALLOWED_PACKAGES = [ + '@sinclair/typebox', + 'node:', // node: 前缀的内置模块 +]; + +// 外部包类型到 any 的映射 +const EXTERNAL_TYPE_REPLACEMENTS = { + // winston + 'winston.Logger': 'any', + 'winston.transport': 'any', + // express + 'express.Express': 'any', + 'express.Application': 'any', + 'express.Router': 'any', + 'Express': 'any', + 'Request': 'any', + 'Response': 'any', + 'NextFunction': 'any', + // ws + 'WebSocket': 'any', + 'WebSocketServer': 'any', + 'RawData': 'any', + // ajv + 'Ajv': 'any', + 'AnySchema': 'any', + 'ValidateFunction': 'any', + 'ValidateFunction': 'any', + // inversify + 'Container': 'any', + // async-mutex + 'Mutex': 'any', + 'Semaphore': 'any', + // napcat-protobuf + 'NapProtoDecodeStructType': 'any', + 'NapProtoEncodeStructType': 'any', + 'NapProtoDecodeStructType': 'any', + 'NapProtoEncodeStructType': 'any', +}; + +function isAllowedImport (importPath) { + return ALLOWED_PACKAGES.some(pkg => importPath.startsWith(pkg)); +} + +function removeExternalImports (content) { + const lines = content.split('\n'); + const resultLines = []; + + for (const line of lines) { + // 匹配 import 语句 + const importMatch = line.match(/^import\s+.*\s+from\s+['"]([^'"]+)['"]/); + if (importMatch) { + const importPath = importMatch[1]; + // 如果是相对路径或白名单包,保留 + if (importPath.startsWith('.') || importPath.startsWith('/') || isAllowedImport(importPath)) { + resultLines.push(line); + } + // 否则移除该 import + continue; + } + resultLines.push(line); + } + + return resultLines.join('\n'); +} + +function replaceExternalTypes (content) { + let result = content; + + // 替换带泛型的类型(先处理复杂的) + result = result.replace(/NapProtoDecodeStructType<[^>]+>/g, 'any'); + result = result.replace(/NapProtoEncodeStructType<[^>]+>/g, 'any'); + result = result.replace(/ValidateFunction<[^>]+>/g, 'any'); + + // 替换 winston.Logger 等带命名空间的类型 + result = result.replace(/winston\.Logger/g, 'any'); + result = result.replace(/winston\.transport/g, 'any'); + result = result.replace(/express\.Express/g, 'any'); + result = result.replace(/express\.Application/g, 'any'); + result = result.replace(/express\.Router/g, 'any'); + + // 替换独立的类型名(需要小心不要替换变量名) + // 使用类型上下文的模式匹配 + const typeContextPatterns = [ + // : Type + /:\s*(WebSocket|WebSocketServer|RawData|Ajv|AnySchema|ValidateFunction|Container|Mutex|Semaphore|NapProtoDecodeStructType|NapProtoEncodeStructType|Express|Request|Response|NextFunction)(?=\s*[;,)\]\}|&]|$)/g, + // + /<(WebSocket|WebSocketServer|RawData|Ajv|AnySchema|ValidateFunction|Container|Mutex|Semaphore|NapProtoDecodeStructType|NapProtoEncodeStructType|Express|Request|Response|NextFunction)>/g, + // Type[] + /(WebSocket|WebSocketServer|RawData|Ajv|AnySchema|ValidateFunction|Container|Mutex|Semaphore|NapProtoDecodeStructType|NapProtoEncodeStructType|Express|Request|Response|NextFunction)\[\]/g, + // extends Type + /extends\s+(WebSocket|WebSocketServer|RawData|Ajv|AnySchema|ValidateFunction|Container|Mutex|Semaphore|NapProtoDecodeStructType|NapProtoEncodeStructType|Express|Request|Response|NextFunction)(?=\s*[{,])/g, + // implements Type + /implements\s+(WebSocket|WebSocketServer|RawData|Ajv|AnySchema|ValidateFunction|Container|Mutex|Semaphore|NapProtoDecodeStructType|NapProtoEncodeStructType|Express|Request|Response|NextFunction)(?=\s*[{,])/g, + ]; + + for (const pattern of typeContextPatterns) { + result = result.replace(pattern, (match, typeName) => { + return match.replace(typeName, 'any'); + }); + } + + return result; +} + async function traverseDirectory (dir) { const entries = await readdir(dir, { withFileTypes: true }); @@ -23,7 +128,13 @@ async function processFile (filePath) { // Read file content let content = await readFile(filePath, 'utf-8'); - // Replace "export declare enum" with "export enum" + // 1. 移除外部包的 import + content = removeExternalImports(content); + + // 2. 替换外部类型为 any + content = replaceExternalTypes(content); + + // 3. Replace "export declare enum" with "export enum" content = content.replace(/export declare enum/g, 'export enum'); // Write back the modified content @@ -33,7 +144,7 @@ async function processFile (filePath) { const newPath = filePath.replace(/\.d\.ts$/, '.ts'); await rename(filePath, newPath); - console.log(`Processed: ${basename(filePath)} -> ${basename(newPath)}`); + //console.log(`Processed: ${basename(filePath)} -> ${basename(newPath)}`); } console.log('Starting post-build processing...'); diff --git a/packages/napcat-types/tsconfig.json b/packages/napcat-types/tsconfig.json index b8ef101d..cd9cf281 100644 --- a/packages/napcat-types/tsconfig.json +++ b/packages/napcat-types/tsconfig.json @@ -39,9 +39,6 @@ "../napcat-onebot/**/*.ts", "../napcat-common/**/*.ts" ], - "files": [ - "./external-shims.d.ts" - ], "exclude": [ "node_modules", "dist" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 29bc8c80..7d9609d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -308,30 +308,9 @@ importers: '@sinclair/typebox': specifier: ^0.34.38 version: 0.34.41 - '@types/cors': - specifier: ^2.8.17 - version: 2.8.19 - '@types/express': - specifier: ^4.17.21 - version: 4.17.25 - '@types/ip': - specifier: ^1.1.3 - version: 1.1.3 - '@types/multer': - specifier: ^1.4.12 - version: 1.4.13 '@types/node': specifier: ^22.10.7 version: 22.19.1 - '@types/winston': - specifier: ^2.4.4 - version: 2.4.4 - '@types/ws': - specifier: ^8.5.12 - version: 8.18.1 - '@types/yaml': - specifier: ^1.9.7 - version: 1.9.7 devDependencies: napcat-core: specifier: workspace:*