Skip to content

Commit 0d38ea2

Browse files
author
Bot
committed
fix: 将 highlight.js 改为静态导入以兼容 Bun --compile 模式
动态 import 在 bun --compile 模式下模块解析指向内部 bunfs 路径, 导致无法找到 highlight.js 模块。
1 parent fb1fae3 commit 0d38ea2

2 files changed

Lines changed: 19 additions & 31 deletions

File tree

packages/color-diff-napi/src/index.ts

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,21 @@
1717
* getSyntaxTheme always returns the default for the given Claude theme.
1818
*/
1919

20-
import { createRequire } from 'node:module'
2120
import { diffArrays } from 'diff'
22-
import type * as hljsNamespace from 'highlight.js'
21+
import hljs from 'highlight.js'
2322
import { basename, extname } from 'path'
2423

25-
// createRequire works in both Bun and Node.js ESM contexts.
26-
// Needed because this package is "type": "module" but uses require() for
27-
// lazy loading — bare require is not available in Node.js ESM.
28-
const nodeRequire = createRequire(import.meta.url)
29-
30-
// Lazy: defers loading highlight.js until first render. The full bundle
31-
// registers 190+ language grammars at require time (~50MB, 100-200ms on
32-
// macOS, several× that on Windows). With a top-level import, any caller
33-
// chunk that reaches this module — including test/preload.ts via
34-
// StructuredDiff.tsx → colorDiff.ts — pays that cost at module-eval time
35-
// and carries the heap for the rest of the process. On Windows CI this
36-
// pushed later tests in the same shard into GC-pause territory and a
37-
// beforeEach/afterEach hook timeout (officialRegistry.test.ts, PR #24150).
38-
// Same lazy pattern the NAPI wrapper used for dlopen.
39-
type HLJSApi = typeof hljsNamespace.default
24+
// Static import — createRequire(import.meta.url) fails in Bun --compile mode
25+
// because the resolved path points to the internal bunfs binary path where
26+
// node_modules cannot be found. A top-level import ensures the module is
27+
// bundled and accessible at runtime.
28+
type HLJSApi = typeof hljs
4029
let cachedHljs: HLJSApi | null = null
41-
function hljs(): HLJSApi {
30+
function hljsApi(): HLJSApi {
4231
if (cachedHljs) return cachedHljs
43-
const mod = nodeRequire('highlight.js')
4432
// highlight.js uses `export =` (CJS). Under bun/ESM the interop wraps it
4533
// in .default; under node CJS the module IS the API. Check at runtime.
34+
const mod = hljs as HLJSApi & { default?: HLJSApi }
4635
cachedHljs = 'default' in mod && mod.default ? mod.default : mod
4736
return cachedHljs!
4837
}
@@ -441,9 +430,9 @@ function detectLanguage(
441430
// Filename-based lookup (handles Dockerfile, Makefile, CMakeLists.txt, etc.)
442431
const stem = base.split('.')[0] ?? ''
443432
const byName = FILENAME_LANGS[base] ?? FILENAME_LANGS[stem]
444-
if (byName && hljs().getLanguage(byName)) return byName
433+
if (byName && hljsApi().getLanguage(byName)) return byName
445434
if (ext) {
446-
const lang = hljs().getLanguage(ext)
435+
const lang = hljsApi().getLanguage(ext)
447436
if (lang) return ext
448437
}
449438
// Shebang / first-line detection (strip UTF-8 BOM)
@@ -525,7 +514,7 @@ function highlightLine(
525514
}
526515
let result
527516
try {
528-
result = hljs().highlight(code, {
517+
result = hljsApi().highlight(code, {
529518
language: state.lang,
530519
ignoreIllegals: true,
531520
})

src/utils/cliHighlight.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,30 @@
11
// highlight.js's type defs carry `/// <reference lib="dom" />`. SSETransport,
22
// mcp/client, ssh, dumpPrompts use DOM types (TextDecodeOptions, RequestInfo)
3-
// that only typecheck because this file's `typeof import('highlight.js')` pulls
4-
// lib.dom in. tsconfig has lib: ["ESNext"] only — fixing the actual DOM-type
5-
// deps is a separate sweep; this ref preserves the status quo.
3+
// that only typecheck because the hljs import below pulls lib.dom in.
4+
// tsconfig has lib: ["ESNext"] only — this ref preserves the status quo.
65
/// <reference lib="dom" />
76

87
import { extname } from 'path'
8+
// Static import — dynamic import('highlight.js') fails in Bun --compile mode
9+
// because module resolution points to the internal bunfs binary path.
10+
import hljs from 'highlight.js'
911

1012
export type CliHighlight = {
1113
highlight: typeof import('cli-highlight').highlight
1214
supportsLanguage: typeof import('cli-highlight').supportsLanguage
1315
}
1416

1517
// One promise shared by Fallback.tsx, markdown.ts, events.ts, getLanguageName.
16-
// The highlight.js import piggybacks: cli-highlight has already pulled it into
17-
// the module cache, so the second import() is a cache hit — no extra bytes
18-
// faulted in.
1918
let cliHighlightPromise: Promise<CliHighlight | null> | undefined
2019

2120
let loadedGetLanguage: ((name: string) => { name: string } | undefined) | undefined
2221

2322
async function loadCliHighlight(): Promise<CliHighlight | null> {
2423
try {
2524
const cliHighlight = await import('cli-highlight')
26-
// cache hit — cli-highlight already loaded highlight.js
27-
const highlightJs = await import('highlight.js')
28-
loadedGetLanguage = (highlightJs as { getLanguage?: typeof loadedGetLanguage }).getLanguage
25+
// highlight.js CJS interop: `export =` wraps in .default under ESM
26+
const hljsMod = hljs as { getLanguage?: typeof loadedGetLanguage; default?: typeof hljs }
27+
loadedGetLanguage = hljsMod.getLanguage ?? hljsMod.default?.getLanguage
2928
return {
3029
highlight: cliHighlight.highlight,
3130
supportsLanguage: cliHighlight.supportsLanguage,

0 commit comments

Comments
 (0)