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.
This commit is contained in:
手瓜一十雪 2026-01-29 15:27:46 +08:00
parent b958e9e803
commit 542036f46e
7 changed files with 115 additions and 47 deletions

View File

@ -1,4 +1,3 @@
/// <reference path="./external-shims.d.ts" />
// 聚合导出核心库的所有内容(包括枚举、类和类型)
export * from '../napcat-core/index';

View File

@ -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": {

View File

@ -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": {

View File

@ -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')
);

View File

@ -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<T>': 'any',
// inversify
'Container': 'any',
// async-mutex
'Mutex': 'any',
'Semaphore': 'any',
// napcat-protobuf
'NapProtoDecodeStructType': 'any',
'NapProtoEncodeStructType': 'any',
'NapProtoDecodeStructType<T>': 'any',
'NapProtoEncodeStructType<T>': '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,
// <Type>
/<(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...');

View File

@ -39,9 +39,6 @@
"../napcat-onebot/**/*.ts",
"../napcat-common/**/*.ts"
],
"files": [
"./external-shims.d.ts"
],
"exclude": [
"node_modules",
"dist"

View File

@ -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:*