From 0ba9ae04a66b534cc91a397563943d079ec2b682 Mon Sep 17 00:00:00 2001 From: digua Date: Mon, 25 May 2026 20:56:56 +0800 Subject: [PATCH] chore: migrate workspace packages to ESM - Add "type": "module" to all workspace package.json files - Replace __dirname with import.meta.url-based resolveCliPath() helper - Convert stream-json/stream-chain CJS named imports to default + destructure for ESM compatibility - Use createRequire() for @node-rs/jieba native module loading in ESM context Co-authored-by: Cursor --- apps/cli/src/cli.ts | 7 ++++--- apps/cli/src/http/index.ts | 4 ++-- apps/cli/src/http/routes/web/helpers.ts | 4 ++-- apps/cli/src/mcp.ts | 4 ++-- apps/cli/src/paths.ts | 8 ++++++++ apps/cli/src/sync/adapters.ts | 3 ++- apps/cli/src/version.ts | 4 ++-- packages/config/package.json | 1 + packages/core/package.json | 1 + packages/mcp-server/package.json | 1 + packages/node-runtime/src/nlp/segmenter.ts | 3 ++- packages/parser/package.json | 1 + packages/parser/src/formats/chatlab.ts | 13 +++++++++---- packages/parser/src/formats/shuakami-qq-exporter.ts | 13 +++++++++---- .../parser/src/formats/shuakami-qq-preprocessor.ts | 13 +++++++++---- .../parser/src/formats/telegram-native-single.ts | 10 +++++++--- packages/parser/src/formats/telegram-native.ts | 13 +++++++++---- .../parser/src/formats/tyrrrz-discord-exporter.ts | 13 +++++++++---- packages/parser/src/formats/weflow.ts | 13 +++++++++---- packages/shared-types/package.json | 1 + packages/sync/package.json | 1 + packages/tools/package.json | 1 + 22 files changed, 92 insertions(+), 40 deletions(-) create mode 100644 apps/cli/src/paths.ts diff --git a/apps/cli/src/cli.ts b/apps/cli/src/cli.ts index 48fcb80d..8322f52b 100644 --- a/apps/cli/src/cli.ts +++ b/apps/cli/src/cli.ts @@ -24,6 +24,7 @@ import { executeReadonlySql, } from '@openchatlab/core' import { getVersion } from './version' +import { resolveCliPath } from './paths' const program = new Command() @@ -291,7 +292,7 @@ program let webRoot: string | undefined if (options.web !== undefined) { - const webDir = typeof options.web === 'string' ? options.web : path.resolve(__dirname, '../dist-web') + const webDir = typeof options.web === 'string' ? options.web : resolveCliPath('dist-web') if (fs.existsSync(webDir)) { webRoot = path.resolve(webDir) } else { @@ -349,7 +350,7 @@ program .action(async (options) => { const { startHttpServer } = await import('./http') const port = parseInt(options.port, 10) - const webDir = path.resolve(__dirname, '../dist-web') + const webDir = resolveCliPath('dist-web') let webRoot: string | undefined if (fs.existsSync(webDir)) { @@ -434,7 +435,7 @@ function openBrowser(url: string): void { */ function resolveNativeBinding(): string | undefined { if (process.versions.electron) return undefined - const nativePath = path.resolve(__dirname, '../native/better_sqlite3.node') + const nativePath = resolveCliPath('native/better_sqlite3.node') if (fs.existsSync(nativePath)) return nativePath return undefined } diff --git a/apps/cli/src/http/index.ts b/apps/cli/src/http/index.ts index 8bbcf4a1..61100dbc 100644 --- a/apps/cli/src/http/index.ts +++ b/apps/cli/src/http/index.ts @@ -6,7 +6,6 @@ */ import * as fs from 'fs' -import * as path from 'path' import * as crypto from 'crypto' import type { FastifyInstance } from 'fastify' import { loadConfig, writeConfigField, MigrationRunner, ALL_MIGRATIONS } from '@openchatlab/config' @@ -28,6 +27,7 @@ import { registerAiRoutes } from './routes/ai' import { registerPreferencesRoutes } from './routes/preferences' import { initServerAiLogger, closeServerAiLogger } from '../ai/logger' import { initSync, cleanupSync } from '../sync' +import { resolveCliPath } from '../paths' let server: FastifyInstance | null = null let dbManager: DatabaseManager | null = null @@ -43,7 +43,7 @@ export interface HttpServerOptions { function resolveNativeBinding(): string | undefined { if (process.versions.electron) return undefined - const nativePath = path.resolve(__dirname, '../../native/better_sqlite3.node') + const nativePath = resolveCliPath('native/better_sqlite3.node') if (fs.existsSync(nativePath)) return nativePath return undefined } diff --git a/apps/cli/src/http/routes/web/helpers.ts b/apps/cli/src/http/routes/web/helpers.ts index f8cfcb68..5c97832a 100644 --- a/apps/cli/src/http/routes/web/helpers.ts +++ b/apps/cli/src/http/routes/web/helpers.ts @@ -3,9 +3,9 @@ */ import * as fs from 'fs' -import * as path from 'path' import type { TimeFilter } from '@openchatlab/shared-types' import type { DatabaseManager } from '@openchatlab/node-runtime' +import { resolveCliPath } from '../../../paths' export function parseTimeFilter(query: Record): TimeFilter | undefined { const { startTs, endTs, memberId } = query @@ -19,7 +19,7 @@ export function parseTimeFilter(query: Record): Time export function resolveNativeBinding(): string | undefined { if (process.versions.electron) return undefined - const nativePath = path.resolve(__dirname, '../../../../native/better_sqlite3.node') + const nativePath = resolveCliPath('native/better_sqlite3.node') if (fs.existsSync(nativePath)) return nativePath return undefined } diff --git a/apps/cli/src/mcp.ts b/apps/cli/src/mcp.ts index 9515b9e8..ba5ca19e 100644 --- a/apps/cli/src/mcp.ts +++ b/apps/cli/src/mcp.ts @@ -5,7 +5,6 @@ */ import fs from 'fs' -import path from 'path' import { loadConfig } from '@openchatlab/config' import { NodePathProvider, @@ -15,10 +14,11 @@ import { } from '@openchatlab/node-runtime' import { startMcpServer } from 'chatlab-mcp' import { getVersion } from './version' +import { resolveCliPath } from './paths' function resolveNativeBinding(): string | undefined { if (process.versions.electron) return undefined - const nativePath = path.resolve(__dirname, '../native/better_sqlite3.node') + const nativePath = resolveCliPath('native/better_sqlite3.node') if (fs.existsSync(nativePath)) return nativePath return undefined } diff --git a/apps/cli/src/paths.ts b/apps/cli/src/paths.ts new file mode 100644 index 00000000..ddc1c537 --- /dev/null +++ b/apps/cli/src/paths.ts @@ -0,0 +1,8 @@ +import path from 'path' +import { fileURLToPath } from 'url' + +export function resolveCliPath(...segments: string[]): string { + const moduleDir = path.dirname(fileURLToPath(import.meta.url)) + const packageRoot = path.basename(moduleDir) === 'dist' ? path.dirname(moduleDir) : path.resolve(moduleDir, '..') + return path.resolve(packageRoot, ...segments) +} diff --git a/apps/cli/src/sync/adapters.ts b/apps/cli/src/sync/adapters.ts index fb5e95cc..c17a1391 100644 --- a/apps/cli/src/sync/adapters.ts +++ b/apps/cli/src/sync/adapters.ts @@ -17,6 +17,7 @@ import type { DatabaseManager } from '@openchatlab/node-runtime' import { openBetterSqliteDatabase } from '@openchatlab/node-runtime' import { parseFile } from '../import/chatlab-reader' import { importData } from '../import/importer' +import { resolveCliPath } from '../paths' function getTempFilePath(ext: string): string { const id = crypto.randomBytes(8).toString('hex') @@ -52,7 +53,7 @@ export class NodeFetcher implements HttpFetcher { function resolveNativeBinding(): string | undefined { if (process.versions.electron) return undefined - const nativePath = path.resolve(__dirname, '../../native/better_sqlite3.node') + const nativePath = resolveCliPath('native/better_sqlite3.node') if (fs.existsSync(nativePath)) return nativePath return undefined } diff --git a/apps/cli/src/version.ts b/apps/cli/src/version.ts index 25caf638..a0e118bf 100644 --- a/apps/cli/src/version.ts +++ b/apps/cli/src/version.ts @@ -1,5 +1,5 @@ import { readFileSync } from 'fs' -import { resolve } from 'path' +import { resolveCliPath } from './paths' let cached: string | undefined @@ -10,7 +10,7 @@ let cached: string | undefined export function getVersion(): string { if (cached) return cached try { - const pkgPath = resolve(__dirname, '../package.json') + const pkgPath = resolveCliPath('package.json') cached = JSON.parse(readFileSync(pkgPath, 'utf-8')).version } catch { cached = '0.0.0-dev' diff --git a/packages/config/package.json b/packages/config/package.json index e7f9b5ba..332f0673 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "description": "ChatLab 配置读取层:TOML/JSON 文件 + 环境变量 + Zod 校验", + "type": "module", "main": "./src/index.ts", "types": "./src/index.ts", "dependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index 556a9757..05271a6a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "description": "ChatLab 平台无关的共享核心:抽象接口、查询逻辑、分析算法", + "type": "module", "main": "./src/index.ts", "types": "./src/index.ts" } diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 164529bf..b35d6fdb 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -2,6 +2,7 @@ "name": "chatlab-mcp", "version": "0.1.0", "description": "ChatLab MCP Server — shared core for CLI and Desktop, also usable standalone via npx", + "type": "module", "main": "./dist/index.mjs", "bin": { "chatlab-mcp": "bin/chatlab-mcp.js" diff --git a/packages/node-runtime/src/nlp/segmenter.ts b/packages/node-runtime/src/nlp/segmenter.ts index 33d5e65e..0167843e 100644 --- a/packages/node-runtime/src/nlp/segmenter.ts +++ b/packages/node-runtime/src/nlp/segmenter.ts @@ -7,6 +7,7 @@ import * as fs from 'fs' import * as path from 'path' +import { createRequire } from 'module' import type { SupportedLocale, PosFilterMode, @@ -24,6 +25,7 @@ interface JiebaInstance { let _nlpDir: string | null = null const jiebaInstances = new Map() +const require = createRequire(import.meta.url) /** * 设置自定义词库目录路径(由应用初始化时调用) @@ -65,7 +67,6 @@ export function getJieba(dictType: DictType = 'default'): JiebaInstance { } try { - // eslint-disable-next-line @typescript-eslint/no-require-imports const { Jieba } = require('@node-rs/jieba') let instance: JiebaInstance const diskDict = tryLoadDictFromDisk(effectiveType) diff --git a/packages/parser/package.json b/packages/parser/package.json index 46bd2037..8cf73896 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "description": "ChatLab 多格式聊天记录解析器(14+ 格式)", + "type": "module", "main": "./src/index.ts", "types": "./src/index.ts", "dependencies": { diff --git a/packages/parser/src/formats/chatlab.ts b/packages/parser/src/formats/chatlab.ts index 29ce816f..65ad03b2 100644 --- a/packages/parser/src/formats/chatlab.ts +++ b/packages/parser/src/formats/chatlab.ts @@ -9,10 +9,10 @@ */ import * as fs from 'fs' -import { parser } from 'stream-json' -import { pick } from 'stream-json/filters/Pick' -import { streamValues } from 'stream-json/streamers/StreamValues' -import { chain } from 'stream-chain' +import streamJson from 'stream-json' +import pickModule from 'stream-json/filters/Pick.js' +import streamValuesModule from 'stream-json/streamers/StreamValues.js' +import streamChain from 'stream-chain' import { KNOWN_PLATFORMS, ChatType } from '@openchatlab/shared-types' import type { FormatFeature, @@ -27,6 +27,11 @@ import type { import { getFileSize, createProgress, readFileHeadBytes } from '../utils' import * as path from 'path' +const { parser } = streamJson +const { pick } = pickModule +const { streamValues } = streamValuesModule +const { chain } = streamChain + // ==================== 辅助函数 ==================== /** diff --git a/packages/parser/src/formats/shuakami-qq-exporter.ts b/packages/parser/src/formats/shuakami-qq-exporter.ts index 7809fd6f..6e8ce183 100644 --- a/packages/parser/src/formats/shuakami-qq-exporter.ts +++ b/packages/parser/src/formats/shuakami-qq-exporter.ts @@ -19,10 +19,10 @@ import * as fs from 'fs' import * as path from 'path' -import { parser } from 'stream-json' -import { pick } from 'stream-json/filters/Pick' -import { streamValues } from 'stream-json/streamers/StreamValues' -import { chain } from 'stream-chain' +import streamJson from 'stream-json' +import pickModule from 'stream-json/filters/Pick.js' +import streamValuesModule from 'stream-json/streamers/StreamValues.js' +import streamChain from 'stream-chain' import { KNOWN_PLATFORMS, ChatType, MessageType } from '@openchatlab/shared-types' import type { FormatFeature, @@ -36,6 +36,11 @@ import type { } from '../types' import { getFileSize, createProgress, readFileHeadBytes, parseTimestamp, isValidYear } from '../utils' +const { parser } = streamJson +const { pick } = pickModule +const { streamValues } = streamValuesModule +const { chain } = streamChain + // ==================== 特征定义 ==================== export const feature: FormatFeature = { diff --git a/packages/parser/src/formats/shuakami-qq-preprocessor.ts b/packages/parser/src/formats/shuakami-qq-preprocessor.ts index ea893b07..394ec229 100644 --- a/packages/parser/src/formats/shuakami-qq-preprocessor.ts +++ b/packages/parser/src/formats/shuakami-qq-preprocessor.ts @@ -9,13 +9,18 @@ import * as fs from 'fs' import * as path from 'path' import * as os from 'os' -import { parser } from 'stream-json' -import { pick } from 'stream-json/filters/Pick' -import { streamValues } from 'stream-json/streamers/StreamValues' -import { chain } from 'stream-chain' +import streamJson from 'stream-json' +import pickModule from 'stream-json/filters/Pick.js' +import streamValuesModule from 'stream-json/streamers/StreamValues.js' +import streamChain from 'stream-chain' import type { ParseProgress, Preprocessor } from '../types' import { getFileSize, createProgress } from '../utils' +const { parser } = streamJson +const { pick } = pickModule +const { streamValues } = streamValuesModule +const { chain } = streamChain + /** 预处理阈值:50MB */ const PREPROCESS_THRESHOLD = 50 * 1024 * 1024 diff --git a/packages/parser/src/formats/telegram-native-single.ts b/packages/parser/src/formats/telegram-native-single.ts index 73815c6e..5ac9a9fe 100644 --- a/packages/parser/src/formats/telegram-native-single.ts +++ b/packages/parser/src/formats/telegram-native-single.ts @@ -15,9 +15,9 @@ */ import * as fs from 'fs' -import { chain } from 'stream-chain' -import { parser } from 'stream-json' -import { streamValues } from 'stream-json/streamers/StreamValues' +import streamChain from 'stream-chain' +import streamJson from 'stream-json' +import streamValuesModule from 'stream-json/streamers/StreamValues.js' import { KNOWN_PLATFORMS, ChatType } from '@openchatlab/shared-types' import type { @@ -34,6 +34,10 @@ import { getFileSize, createProgress } from '../utils' import { mapChatType, extractPlatformId, detectMessageType, buildContent } from './utils/telegram-utils' import type { TelegramChat } from './utils/telegram-utils' +const { chain } = streamChain +const { parser } = streamJson +const { streamValues } = streamValuesModule + // ==================== 特征定义 ==================== export const feature: FormatFeature = { diff --git a/packages/parser/src/formats/telegram-native.ts b/packages/parser/src/formats/telegram-native.ts index 9466f4f4..327d7ada 100644 --- a/packages/parser/src/formats/telegram-native.ts +++ b/packages/parser/src/formats/telegram-native.ts @@ -16,10 +16,10 @@ */ import * as fs from 'fs' -import { chain } from 'stream-chain' -import { parser } from 'stream-json' -import { pick } from 'stream-json/filters/Pick' -import { streamValues } from 'stream-json/streamers/StreamValues' +import streamChain from 'stream-chain' +import streamJson from 'stream-json' +import pickModule from 'stream-json/filters/Pick.js' +import streamValuesModule from 'stream-json/streamers/StreamValues.js' import { KNOWN_PLATFORMS, ChatType } from '@openchatlab/shared-types' import type { @@ -36,6 +36,11 @@ import { getFileSize, createProgress } from '../utils' import { mapChatType, extractPlatformId, detectMessageType, buildContent } from './utils/telegram-utils' import type { TelegramChat } from './utils/telegram-utils' +const { chain } = streamChain +const { parser } = streamJson +const { pick } = pickModule +const { streamValues } = streamValuesModule + // ==================== 类型定义 ==================== /** Telegram 聊天信息(扫描结果) */ diff --git a/packages/parser/src/formats/tyrrrz-discord-exporter.ts b/packages/parser/src/formats/tyrrrz-discord-exporter.ts index 406d1775..a0fee082 100644 --- a/packages/parser/src/formats/tyrrrz-discord-exporter.ts +++ b/packages/parser/src/formats/tyrrrz-discord-exporter.ts @@ -9,10 +9,10 @@ */ import * as fs from 'fs' -import { parser } from 'stream-json' -import { pick } from 'stream-json/filters/Pick' -import { streamValues } from 'stream-json/streamers/StreamValues' -import { chain } from 'stream-chain' +import streamJson from 'stream-json' +import pickModule from 'stream-json/filters/Pick.js' +import streamValuesModule from 'stream-json/streamers/StreamValues.js' +import streamChain from 'stream-chain' import { KNOWN_PLATFORMS, ChatType, MessageType, type MemberRole } from '@openchatlab/shared-types' import type { FormatFeature, @@ -26,6 +26,11 @@ import type { } from '../types' import { getFileSize, createProgress, readFileHeadBytes } from '../utils' +const { parser } = streamJson +const { pick } = pickModule +const { streamValues } = streamValuesModule +const { chain } = streamChain + // ==================== Discord 数据结构定义 ==================== interface DiscordGuild { diff --git a/packages/parser/src/formats/weflow.ts b/packages/parser/src/formats/weflow.ts index e7d23230..ef863f0d 100644 --- a/packages/parser/src/formats/weflow.ts +++ b/packages/parser/src/formats/weflow.ts @@ -17,10 +17,10 @@ import * as fs from 'fs' import * as path from 'path' -import { parser } from 'stream-json' -import { pick } from 'stream-json/filters/Pick' -import { streamValues } from 'stream-json/streamers/StreamValues' -import { chain } from 'stream-chain' +import streamJson from 'stream-json' +import pickModule from 'stream-json/filters/Pick.js' +import streamValuesModule from 'stream-json/streamers/StreamValues.js' +import streamChain from 'stream-chain' import { KNOWN_PLATFORMS, ChatType, MessageType } from '@openchatlab/shared-types' import type { FormatFeature, @@ -34,6 +34,11 @@ import type { } from '../types' import { getFileSize, createProgress, readFileHeadBytes } from '../utils' +const { parser } = streamJson +const { pick } = pickModule +const { streamValues } = streamValuesModule +const { chain } = streamChain + // ==================== 辅助函数 ==================== /** diff --git a/packages/shared-types/package.json b/packages/shared-types/package.json index 01a02aa6..b18697ab 100644 --- a/packages/shared-types/package.json +++ b/packages/shared-types/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "description": "ChatLab 跨平台共享类型定义", + "type": "module", "main": "./index.ts", "types": "./index.ts" } diff --git a/packages/sync/package.json b/packages/sync/package.json index 8a94ebf6..5d2e6c40 100644 --- a/packages/sync/package.json +++ b/packages/sync/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "description": "ChatLab platform-agnostic data sync engine: config, data source, pull scheduler", + "type": "module", "main": "./src/index.ts", "types": "./src/index.ts" } diff --git a/packages/tools/package.json b/packages/tools/package.json index 8ccb71ef..17f500ee 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "description": "ChatLab AI 工具链:工具定义 + 平台无关 handler,服务于 MCP/HTTP/Electron", + "type": "module", "main": "./src/index.ts", "types": "./src/index.ts", "dependencies": {