mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-15 12:55:51 +00:00
chore: 添加 highlight.js 包
This commit is contained in:
59
README.md
59
README.md
@@ -14,28 +14,28 @@
|
||||
|
||||
[文档在这里, 支持投稿 PR](https://ccb.agent-aura.top/) | [留影文档在这里](./Friends.md) | [Discord 群组](https://discord.gg/uApuzJWGKX)
|
||||
|
||||
| 特性 | 说明 | 文档 |
|
||||
|------|------|------|
|
||||
| **Claude 群控技术** | Pipe IPC 多实例协作:同机 main/sub 自动编排 + LAN 跨机器零配置发现与通讯,`/pipes` 选择面板 + `Shift+↓` 交互 + 消息广播路由 | [Pipe IPC](https://ccb.agent-aura.top/docs/features/pipes-and-lan) / [LAN](https://ccb.agent-aura.top/docs/features/lan-pipes) |
|
||||
| **ACP 协议一等一支持** | 支持接入 Zed、Cursor 等 IDE,支持会话恢复、Skills、权限桥接 | [文档](https://ccb.agent-aura.top/docs/features/acp-zed) |
|
||||
| **Remote Control 私有部署** | Docker 自托管远程界面, 可以手机上看 CC | [文档](https://ccb.agent-aura.top/docs/features/remote-control-self-hosting) |
|
||||
| **Langfuse 监控** | 企业级 Agent 监控, 可以清晰看到每次 agent loop 细节, 可以一键转化为数据集 | [文档](https://ccb.agent-aura.top/docs/features/langfuse-monitoring) |
|
||||
| **Web Search** | 内置网页搜索工具, 支持 bing 和 brave 搜索 | [文档](https://ccb.agent-aura.top/docs/features/web-browser-tool) |
|
||||
| **Poor Mode** | 穷鬼模式,关闭记忆提取和键入建议,大幅度减少并发请求 | /poor 可以开关 |
|
||||
| **Channels 频道通知** | MCP 服务器推送外部消息到会话(飞书/Slack/Discord/微信等),`--channels plugin:name@marketplace` 启用 | [文档](https://ccb.agent-aura.top/docs/features/channels) |
|
||||
| **自定义模型供应商** | OpenAI/Anthropic/Gemini/Grok 兼容 | [文档](https://ccb.agent-aura.top/docs/features/custom-platform-login) |
|
||||
| Voice Mode | Push-to-Talk 语音输入 | [文档](https://ccb.agent-aura.top/docs/features/voice-mode) |
|
||||
| Computer Use | 屏幕截图、键鼠控制 | [文档](https://ccb.agent-aura.top/docs/features/computer-use) |
|
||||
| Chrome Use | 浏览器自动化、表单填写、数据抓取 | [自托管](https://ccb.agent-aura.top/docs/features/chrome-use-mcp) [原生版](https://ccb.agent-aura.top/docs/features/claude-in-chrome-mcp) |
|
||||
| Sentry | 企业级错误追踪 | [文档](https://ccb.agent-aura.top/docs/internals/sentry-setup) |
|
||||
| GrowthBook | 企业级特性开关 | [文档](https://ccb.agent-aura.top/docs/internals/growthbook-adapter) |
|
||||
| /dream 记忆整理 | 自动整理和优化记忆文件 | [文档](https://ccb.agent-aura.top/docs/features/auto-dream) |
|
||||
|
||||
| 特性 | 说明 | 文档 |
|
||||
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Claude 群控技术** | Pipe IPC 多实例协作:同机 main/sub 自动编排 + LAN 跨机器零配置发现与通讯,`/pipes` 选择面板 + `Shift+↓` 交互 + 消息广播路由 | [Pipe IPC](https://ccb.agent-aura.top/docs/features/pipes-and-lan) / [LAN](https://ccb.agent-aura.top/docs/features/lan-pipes) |
|
||||
| **ACP 协议一等一支持** | 支持接入 Zed、Cursor 等 IDE,支持会话恢复、Skills、权限桥接 | [文档](https://ccb.agent-aura.top/docs/features/acp-zed) |
|
||||
| **Remote Control 私有部署** | Docker 自托管远程界面, 可以手机上看 CC | [文档](https://ccb.agent-aura.top/docs/features/remote-control-self-hosting) |
|
||||
| **Langfuse 监控** | 企业级 Agent 监控, 可以清晰看到每次 agent loop 细节, 可以一键转化为数据集 | [文档](https://ccb.agent-aura.top/docs/features/langfuse-monitoring) |
|
||||
| **Web Search** | 内置网页搜索工具, 支持 bing 和 brave 搜索 | [文档](https://ccb.agent-aura.top/docs/features/web-browser-tool) |
|
||||
| **Poor Mode** | 穷鬼模式,关闭记忆提取和键入建议,大幅度减少并发请求 | /poor 可以开关 |
|
||||
| **Channels 频道通知** | MCP 服务器推送外部消息到会话(飞书/Slack/Discord/微信等),`--channels plugin:name@marketplace` 启用 | [文档](https://ccb.agent-aura.top/docs/features/channels) |
|
||||
| **自定义模型供应商** | OpenAI/Anthropic/Gemini/Grok 兼容 | [文档](https://ccb.agent-aura.top/docs/features/custom-platform-login) |
|
||||
| Voice Mode | Push-to-Talk 语音输入 | [文档](https://ccb.agent-aura.top/docs/features/voice-mode) |
|
||||
| Computer Use | 屏幕截图、键鼠控制 | [文档](https://ccb.agent-aura.top/docs/features/computer-use) |
|
||||
| Chrome Use | 浏览器自动化、表单填写、数据抓取 | [自托管](https://ccb.agent-aura.top/docs/features/chrome-use-mcp) [原生版](https://ccb.agent-aura.top/docs/features/claude-in-chrome-mcp) |
|
||||
| Sentry | 企业级错误追踪 | [文档](https://ccb.agent-aura.top/docs/internals/sentry-setup) |
|
||||
| GrowthBook | 企业级特性开关 | [文档](https://ccb.agent-aura.top/docs/internals/growthbook-adapter) |
|
||||
| /dream 记忆整理 | 自动整理和优化记忆文件 | [文档](https://ccb.agent-aura.top/docs/features/auto-dream) |
|
||||
|
||||
- 🚀 [想要启动项目](#快速开始源码版)
|
||||
- 🐛 [想要调试项目](#vs-code-调试)
|
||||
- 📖 [想要学习项目](#teach-me-学习项目)
|
||||
|
||||
|
||||
## ⚡ 快速开始(安装版)
|
||||
|
||||
不用克隆仓库, 从 NPM 下载后, 直接使用
|
||||
@@ -45,7 +45,7 @@ npm i -g claude-code-best
|
||||
|
||||
# bun 安装比较多问题, 推荐 npm 装
|
||||
# bun i -g claude-code-best
|
||||
# bun pm -g trust claude-code-best
|
||||
# bun pm -g trust claude-code-best @claude-code-best/mcp-chrome-bridge
|
||||
|
||||
ccb # 以 nodejs 打开 claude code
|
||||
ccb-bun # 以 bun 形态打开
|
||||
@@ -91,17 +91,17 @@ bun run build
|
||||
|
||||
需要填写的字段:
|
||||
|
||||
| 📌 字段 | 📝 说明 | 💡 示例 |
|
||||
|------|------|------|
|
||||
| Base URL | API 服务地址 | `https://api.example.com/v1` |
|
||||
| API Key | 认证密钥 | `sk-xxx` |
|
||||
| Haiku Model | 快速模型 ID | `claude-haiku-4-5-20251001` |
|
||||
| Sonnet Model | 均衡模型 ID | `claude-sonnet-4-6` |
|
||||
| Opus Model | 高性能模型 ID | `claude-opus-4-6` |
|
||||
|
||||
| 📌 字段 | 📝 说明 | 💡 示例 |
|
||||
| ------------ | ------------- | ---------------------------- |
|
||||
| Base URL | API 服务地址 | `https://api.example.com/v1` |
|
||||
| API Key | 认证密钥 | `sk-xxx` |
|
||||
| Haiku Model | 快速模型 ID | `claude-haiku-4-5-20251001` |
|
||||
| Sonnet Model | 均衡模型 ID | `claude-sonnet-4-6` |
|
||||
| Opus Model | 高性能模型 ID | `claude-opus-4-6` |
|
||||
|
||||
- ⌨️ **Tab / Shift+Tab** 切换字段,**Enter** 确认并跳到下一个,最后一个字段按 Enter 保存
|
||||
|
||||
|
||||
> ℹ️ 支持所有 Anthropic API 兼容服务(如 OpenRouter、AWS Bedrock 代理等),只要接口兼容 Messages API 即可。
|
||||
|
||||
## Feature Flags
|
||||
@@ -121,16 +121,17 @@ TUI (REPL) 模式需要真实终端,无法直接通过 VS Code launch 启动
|
||||
### 步骤
|
||||
|
||||
1. **终端启动 inspect 服务**:
|
||||
|
||||
```bash
|
||||
bun run dev:inspect
|
||||
```
|
||||
会输出类似 `ws://localhost:8888/xxxxxxxx` 的地址。
|
||||
|
||||
会输出类似 `ws://localhost:8888/xxxxxxxx` 的地址。
|
||||
2. **VS Code 附着调试器**:
|
||||
|
||||
- 在 `src/` 文件中打断点
|
||||
- F5 → 选择 **"Attach to Bun (TUI debug)"**
|
||||
|
||||
|
||||
## Teach Me 学习项目
|
||||
|
||||
我们新加了一个 teach-me skills, 通过问答式引导帮你理解这个项目的任何模块。(调整 [sigma skill 而来](https://github.com/sanyuan0704/sanyuan-skills))
|
||||
@@ -157,7 +158,7 @@ TUI (REPL) 模式需要真实终端,无法直接通过 VS Code launch 启动
|
||||
## 相关文档及网站
|
||||
|
||||
- **在线文档(Mintlify)**: [ccb.agent-aura.top](https://ccb.agent-aura.top/) — 文档源码位于 [`docs/`](docs/) 目录,欢迎投稿 PR
|
||||
- **DeepWiki**: <https://deepwiki.com/claude-code-best/claude-code>
|
||||
- **DeepWiki**: [https://deepwiki.com/claude-code-best/claude-code](https://deepwiki.com/claude-code-best/claude-code)
|
||||
|
||||
## Contributors
|
||||
|
||||
|
||||
1
bun.lock
1
bun.lock
@@ -7,6 +7,7 @@
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.19.0",
|
||||
"@claude-code-best/mcp-chrome-bridge": "^3.0.1",
|
||||
"highlight.js": "^11.11.1",
|
||||
"ws": "^8.20.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
"format": "biome format --write src/",
|
||||
"prepare": "git config core.hooksPath .githooks",
|
||||
"test": "bun test",
|
||||
"check:bundle": "bun run scripts/check-bundle-integrity.ts",
|
||||
"check:unused": "knip-bun",
|
||||
"health": "bun run scripts/health-check.ts",
|
||||
"postinstall": "node scripts/run-parallel.mjs scripts/postinstall.cjs scripts/setup-chrome-mcp.mjs",
|
||||
@@ -64,6 +65,7 @@
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.19.0",
|
||||
"@claude-code-best/mcp-chrome-bridge": "^3.0.1",
|
||||
"highlight.js": "^11.11.1",
|
||||
"ws": "^8.20.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
336
scripts/check-bundle-integrity.ts
Normal file
336
scripts/check-bundle-integrity.ts
Normal file
@@ -0,0 +1,336 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* 构建产物完整性检查脚本
|
||||
*
|
||||
* 检查 Bun.build({ splitting: true }) 输出的 dist/ 目录中是否存在:
|
||||
* 1. 引用了不存在的 chunk 文件(断链)
|
||||
* 2. 通过 __require() 或 import() 引用的第三方模块(非 Node.js 内置),在生产环境中会找不到
|
||||
* 3. 缺失的静态 import 依赖(跨 chunk 引用目标不存在)
|
||||
*
|
||||
* 用法:
|
||||
* bun scripts/check-bundle-integrity.ts # 检查当前 dist/
|
||||
* bun scripts/check-bundle-integrity.ts ./dist # 指定目录
|
||||
*/
|
||||
|
||||
import { readdir, readFile } from "fs/promises"
|
||||
import { join, resolve, dirname } from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
|
||||
// ─── 从 package.json 读取 dependencies 作为白名单 ────────────────
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
const pkg = JSON.parse(await readFile(join(__dirname, '..', 'package.json'), 'utf-8'))
|
||||
const PKG_DEPS = new Set(Object.keys(pkg.dependencies ?? {}))
|
||||
|
||||
// ─── Node.js 内置模块白名单 ────────────────────────────────────────
|
||||
const NODE_BUILTINS = new Set([
|
||||
"assert",
|
||||
"async_hooks",
|
||||
"buffer",
|
||||
"child_process",
|
||||
"cluster",
|
||||
"console",
|
||||
"constants",
|
||||
"crypto",
|
||||
"dgram",
|
||||
"diagnostics_channel",
|
||||
"dns",
|
||||
"domain",
|
||||
"events",
|
||||
"fs",
|
||||
"fs/promises",
|
||||
"http",
|
||||
"http2",
|
||||
"https",
|
||||
"inspector",
|
||||
"module",
|
||||
"net",
|
||||
"os",
|
||||
"path",
|
||||
"perf_hooks",
|
||||
"process",
|
||||
"punycode",
|
||||
"querystring",
|
||||
"readline",
|
||||
"repl",
|
||||
"stream",
|
||||
"string_decoder",
|
||||
"sys",
|
||||
"timers",
|
||||
"tls",
|
||||
"tty",
|
||||
"url",
|
||||
"util",
|
||||
"v8",
|
||||
"vm",
|
||||
"worker_threads",
|
||||
"zlib",
|
||||
"node:test",
|
||||
])
|
||||
|
||||
// Node 18+ 内置但不在传统列表中的模块
|
||||
const NODE_18_PLUS_BUILTINS = new Set(["undici"])
|
||||
|
||||
// Bun 专用模块(仅在 Bun 运行时可用,Node.js 环境会失败)
|
||||
const BUN_MODULES = new Set(["bun", "bun:ffi", "bun:test", "bun:sqlite"])
|
||||
|
||||
// macOS JXA / native 框架(通过 ObjC.import,非真正的 require)
|
||||
const NATIVE_FRAMEWORKS = new Set(["AppKit", "CoreGraphics", "Foundation", "UIKit"])
|
||||
|
||||
// ─── 模式 ──────────────────────────────────────────────────────────
|
||||
// 匹配 import { ... } from "./chunk-xxxxx.js" 或 import"./chunk-xxxxx.js"
|
||||
const STATIC_IMPORT_RE = /(?:from\s+|import\s+)"(\.\/[^"]+\.js)"/g
|
||||
// 匹配 __require("xxx")
|
||||
const REQUIRE_RE = /__require\("([^"]+)"\)/g
|
||||
// 匹配动态 import("xxx"),排除 ./chunk-xxx.js 的内部引用
|
||||
const DYNAMIC_IMPORT_RE = /import\("([^"]+)"\)/g
|
||||
// 匹配 nodeRequire("xxx")(createRequire 创建的 require 别名)
|
||||
const NODE_REQUIRE_RE = /nodeRequire\("([^"]+)"\)/g
|
||||
|
||||
interface Finding {
|
||||
type: "broken-chunk-ref" | "third-party-require" | "third-party-import" | "third-party-node-require" | "bun-runtime-only"
|
||||
severity: "error" | "warning"
|
||||
file: string
|
||||
line: number
|
||||
module: string
|
||||
snippet: string
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const distDir = resolve(process.argv[2] || "./dist")
|
||||
|
||||
console.log(`\n🔍 检查构建产物完整性: ${distDir}\n`)
|
||||
|
||||
// 1. 列出所有 chunk 文件
|
||||
let files: string[]
|
||||
try {
|
||||
files = (await readdir(distDir)).filter((f) => f.endsWith(".js"))
|
||||
} catch {
|
||||
console.error(`❌ 无法读取目录: ${distDir}`)
|
||||
console.error(" 请先运行 bun run build")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const fileSet = new Set(files)
|
||||
console.log(`📦 找到 ${files.length} 个 JS 文件\n`)
|
||||
|
||||
const findings: Finding[] = []
|
||||
|
||||
// 2. 逐文件扫描
|
||||
for (const file of files) {
|
||||
const filePath = join(distDir, file)
|
||||
const content = await readFile(filePath, "utf-8")
|
||||
const lines = content.split("\n")
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i]
|
||||
const lineNum = i + 1
|
||||
|
||||
// 2a. 检查静态 chunk 引用是否断链
|
||||
const staticImportMatches = line.matchAll(STATIC_IMPORT_RE)
|
||||
for (const m of staticImportMatches) {
|
||||
const ref = m[1]
|
||||
// 提取文件名部分(去掉 ./)
|
||||
const refFile = ref.replace(/^\.\//, "")
|
||||
if (!fileSet.has(refFile)) {
|
||||
findings.push({
|
||||
type: "broken-chunk-ref",
|
||||
severity: "error",
|
||||
file,
|
||||
line: lineNum,
|
||||
module: ref,
|
||||
snippet: line.trim().slice(0, 120),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 2b. 检查 __require 中的第三方模块
|
||||
const requireMatches = line.matchAll(REQUIRE_RE)
|
||||
for (const m of requireMatches) {
|
||||
const mod = m[1]
|
||||
// 跳过 ObjC.import(JXA 语法,不是真正的 require)
|
||||
if (NATIVE_FRAMEWORKS.has(mod)) continue
|
||||
if (NODE_BUILTINS.has(mod) || NODE_18_PLUS_BUILTINS.has(mod) || PKG_DEPS.has(mod) || mod.startsWith("node:")) continue
|
||||
if (BUN_MODULES.has(mod)) {
|
||||
findings.push({
|
||||
type: "bun-runtime-only",
|
||||
severity: "warning",
|
||||
file,
|
||||
line: lineNum,
|
||||
module: mod,
|
||||
snippet: line.trim().slice(0, 120),
|
||||
})
|
||||
continue
|
||||
}
|
||||
// 第三方模块 — 在生产环境(全局 npm install)中找不到
|
||||
findings.push({
|
||||
type: "third-party-require",
|
||||
severity: "error",
|
||||
file,
|
||||
line: lineNum,
|
||||
module: mod,
|
||||
snippet: line.trim().slice(0, 120),
|
||||
})
|
||||
}
|
||||
|
||||
// 2c. 检查动态 import() 中的第三方模块
|
||||
const dynImportMatches = line.matchAll(DYNAMIC_IMPORT_RE)
|
||||
for (const m of dynImportMatches) {
|
||||
const mod = m[1]
|
||||
// 跳过内部 chunk 引用和相对路径
|
||||
if (mod.startsWith("./") || mod.startsWith("../")) continue
|
||||
// 跳过 ObjC.import
|
||||
if (NATIVE_FRAMEWORKS.has(mod)) continue
|
||||
if (NODE_BUILTINS.has(mod) || NODE_18_PLUS_BUILTINS.has(mod) || PKG_DEPS.has(mod) || mod.startsWith("node:")) continue
|
||||
if (BUN_MODULES.has(mod)) {
|
||||
// bun:test 等只在 Bun 运行时可用,Node.js 运行时会失败
|
||||
findings.push({
|
||||
type: "bun-runtime-only",
|
||||
severity: "warning",
|
||||
file,
|
||||
line: lineNum,
|
||||
module: mod,
|
||||
snippet: line.trim().slice(0, 120),
|
||||
})
|
||||
continue
|
||||
}
|
||||
// 第三方动态 import
|
||||
findings.push({
|
||||
type: "third-party-import",
|
||||
severity: "error",
|
||||
file,
|
||||
line: lineNum,
|
||||
module: mod,
|
||||
snippet: line.trim().slice(0, 120),
|
||||
})
|
||||
}
|
||||
|
||||
// 2d. 检查 nodeRequire("xxx") 中的第三方模块(createRequire 别名)
|
||||
const nodeRequireMatches = line.matchAll(NODE_REQUIRE_RE)
|
||||
for (const m of nodeRequireMatches) {
|
||||
const mod = m[1]
|
||||
if (NATIVE_FRAMEWORKS.has(mod)) continue
|
||||
if (NODE_BUILTINS.has(mod) || NODE_18_PLUS_BUILTINS.has(mod) || PKG_DEPS.has(mod) || mod.startsWith("node:")) continue
|
||||
if (BUN_MODULES.has(mod)) {
|
||||
findings.push({
|
||||
type: "bun-runtime-only",
|
||||
severity: "warning",
|
||||
file,
|
||||
line: lineNum,
|
||||
module: mod,
|
||||
snippet: line.trim().slice(0, 120),
|
||||
})
|
||||
continue
|
||||
}
|
||||
findings.push({
|
||||
type: "third-party-node-require",
|
||||
severity: "error",
|
||||
file,
|
||||
line: lineNum,
|
||||
module: mod,
|
||||
snippet: line.trim().slice(0, 120),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 汇总报告
|
||||
const errors = findings.filter((f) => f.severity === "error")
|
||||
const warnings = findings.filter((f) => f.severity === "warning")
|
||||
|
||||
// 按 type 分组
|
||||
const brokenRefs = errors.filter((f) => f.type === "broken-chunk-ref")
|
||||
const thirdPartyRequires = errors.filter((f) => f.type === "third-party-require")
|
||||
const thirdPartyImports = errors.filter((f) => f.type === "third-party-import")
|
||||
const thirdPartyNodeRequires = errors.filter((f) => f.type === "third-party-node-require")
|
||||
const bunRuntimeOnly = warnings.filter((f) => f.type === "bun-runtime-only")
|
||||
|
||||
if (brokenRefs.length > 0) {
|
||||
console.log("❌ 断裂的 chunk 引用(引用了不存在的文件):")
|
||||
for (const f of brokenRefs) {
|
||||
console.log(` ${f.file}:${f.line} → ${f.module}`)
|
||||
}
|
||||
console.log()
|
||||
}
|
||||
|
||||
if (thirdPartyRequires.length > 0) {
|
||||
console.log("❌ 通过 __require() 引用的第三方模块(生产环境会找不到):")
|
||||
const grouped = groupByModule(thirdPartyRequires)
|
||||
for (const [mod, items] of grouped) {
|
||||
console.log(` "${mod}" — 出现 ${items.length} 次:`)
|
||||
for (const f of items.slice(0, 5)) {
|
||||
console.log(` ${f.file}:${f.line}`)
|
||||
}
|
||||
if (items.length > 5) console.log(` ... 还有 ${items.length - 5} 处`)
|
||||
}
|
||||
console.log()
|
||||
}
|
||||
|
||||
if (thirdPartyImports.length > 0) {
|
||||
console.log("❌ 通过 import() 动态引用的第三方模块(生产环境会找不到):")
|
||||
const grouped = groupByModule(thirdPartyImports)
|
||||
for (const [mod, items] of grouped) {
|
||||
console.log(` "${mod}" — 出现 ${items.length} 次:`)
|
||||
for (const f of items.slice(0, 5)) {
|
||||
console.log(` ${f.file}:${f.line}`)
|
||||
}
|
||||
if (items.length > 5) console.log(` ... 还有 ${items.length - 5} 处`)
|
||||
}
|
||||
console.log()
|
||||
}
|
||||
|
||||
if (thirdPartyNodeRequires.length > 0) {
|
||||
console.log("❌ 通过 nodeRequire() 引用的第三方模块(绕过打包,生产环境会找不到):")
|
||||
const grouped = groupByModule(thirdPartyNodeRequires)
|
||||
for (const [mod, items] of grouped) {
|
||||
console.log(` "${mod}" — 出现 ${items.length} 次:`)
|
||||
for (const f of items.slice(0, 5)) {
|
||||
console.log(` ${f.file}:${f.line}`)
|
||||
}
|
||||
if (items.length > 5) console.log(` ... 还有 ${items.length - 5} 处`)
|
||||
}
|
||||
console.log()
|
||||
}
|
||||
|
||||
if (bunRuntimeOnly.length > 0) {
|
||||
console.log("⚠️ Bun 运行时专用模块(Node.js 环境会失败):")
|
||||
const grouped = groupByModule(bunRuntimeOnly)
|
||||
for (const [mod, items] of grouped) {
|
||||
console.log(` "${mod}" — 出现 ${items.length} 次`)
|
||||
}
|
||||
console.log()
|
||||
}
|
||||
|
||||
// 4. 总结
|
||||
console.log("─".repeat(50))
|
||||
if (errors.length === 0 && warnings.length === 0) {
|
||||
console.log("✅ 构建产物完整性检查通过,未发现问题。")
|
||||
} else {
|
||||
console.log(`📊 总计: ${errors.length} 个错误, ${warnings.length} 个警告`)
|
||||
if (errors.length > 0) {
|
||||
console.log(
|
||||
`\n💡 修复建议:
|
||||
- 第三方模块问题:在 build.ts 中通过 external 选项排除,或确保它们被正确打包到 chunk 中
|
||||
- 断链问题:检查 build 时是否有文件被意外删除或构建不完整
|
||||
- Bun 专用模块:确保运行时使用 bun 而非 node`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(errors.length > 0 ? 1 : 0)
|
||||
}
|
||||
|
||||
function groupByModule(items: Finding[]): Map<string, Finding[]> {
|
||||
const map = new Map<string, Finding[]>()
|
||||
for (const item of items) {
|
||||
const list = map.get(item.module) || []
|
||||
list.push(item)
|
||||
map.set(item.module, list)
|
||||
}
|
||||
// 按出现次数降序
|
||||
return new Map([...map.entries()].sort((a, b) => b[1].length - a[1].length))
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error("Fatal error:", err)
|
||||
process.exit(2)
|
||||
})
|
||||
Reference in New Issue
Block a user