Skip to content

fix(CodeHighlighter): load Prism grammars through a bundler-analyzable loader - #2028

Open
Div627 wants to merge 1 commit into
ant-design:mainfrom
Div627:fix/code-highlighter-language-loading
Open

fix(CodeHighlighter): load Prism grammars through a bundler-analyzable loader#2028
Div627 wants to merge 1 commit into
ant-design:mainfrom
Div627:fix/code-highlighter-language-loading

Conversation

@Div627

@Div627 Div627 commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

🤔 This is a ...

  • 🐞 Bug fix

🔗 Related issue link

None — found while debugging why every code block in a Vite-bundled app renders unhighlighted.

💡 Background and solution

The bug

CodeHighlighter uses PrismLight, which is backed by refractor/core. That instance ships only plain, plaintext, text and txt:

import { refractor } from 'refractor/core'
refractor.listLanguages()
// => ["plain", "plaintext", "text", "txt"]
refractor.highlight('const a = 1', 'typescript')
// => Error: Unknown language: `typescript` is not registered

Grammars therefore have to be registered explicitly. The on-demand loader tries to do that, but it cannot work in any bundled app:

const langModule = await import(`react-syntax-highlighter/dist/esm/languages/prism/${lang}`);
SyntaxHighlighter.registerLanguage(lang, langModule.default);

A template literal over a bare specifier is not statically analyzable. Vite/Rollup leave it in the output verbatim and emit no language chunk. Here is the expression as it appears in a production build of an app on @ant-design/x@2.8.0:

await __vitePreload(() => import(`react-syntax-highlighter/dist/esm/languages/prism/${lang}`), true ? [] : void 0, import.meta.url);

Note the empty preload dependency list. At runtime the browser cannot resolve a bare specifier, so the import throws, the registerLanguage call on the next line never runs, and only console.warn('[CodeHighlighter] Failed to load language: …') is left behind.

refractor.highlight() then throws Unknown language; react-syntax-highlighter swallows that in getCodeTree and falls back to a single text node. Every code block renders as unstyled plain text, silently.

This is not fixed by upgrading. 2.8.0 had no registerLanguage call at all; 2.9.0 added one, but it sits right after the import that always fails, so it never executes.

folder/FilePreview.tsx has the same defect in a more direct form — it renders raw PrismLight and never registers anything, so file previews are never highlighted either.

The fix

Use PrismAsyncLight instead of hand-rolling the loader. It already does exactly what is needed, and does it correctly:

  • its languageLoaders map is a static object of literal import('refractor/<lang>') calls, so bundlers can code-split every grammar into a real chunk;
  • it loads and registers the grammar, then re-renders;
  • unsupported languages normalize to text rather than throwing.

folder/FilePreview.tsx is switched over too.

Two tests asserted the old console.warn behaviour for an unsupported language. PrismAsyncLight normalizes instead of warning, so those assertions are updated to check the graceful fallback (which is what they were really about). The now-dead jest.mock entries for the per-language modules are removed.

Verification

jest components/code-highlighter   40 passed
jest components/folder            134 passed

📝 Changelog

Language Changelog
🇺🇸 English Fix CodeHighlighter and Folder file preview rendering code without syntax highlighting: Prism grammars were loaded through a dynamic import that bundlers cannot analyze, so no grammar was ever registered.
🇨🇳 Chinese 修复 CodeHighlighterFolder 文件预览代码无语法高亮的问题:Prism 语法通过打包器无法分析的动态 import 加载,导致语法从未被注册。

☑️ Self-Check before Merge

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • Changelog is provided or not needed

Summary by CodeRabbit

  • 改进
    • 优化代码语法高亮的加载方式,按需加载语言支持,提升相关组件的加载效率。
    • 文件预览中的代码高亮现已支持异步加载。
    • 不受支持的语言会自动按纯文本方式正常显示,并保留代码回退渲染能力。

…e loader

`PrismLight` is backed by `refractor/core`, which only ships
plain/plaintext/text/txt. Grammars must be registered explicitly, and the
current on-demand loader cannot do that in any bundled app:

    await import(`react-syntax-highlighter/dist/esm/languages/prism/${lang}`)

A template literal over a bare specifier is not statically analyzable, so
Vite/Rollup emit it verbatim and no language chunk is ever produced. At runtime
the browser cannot resolve a bare specifier, the import throws, and the
`registerLanguage` call right after it never runs. `refractor.highlight()` then
throws `Unknown language`, react-syntax-highlighter swallows it and falls back to
a single text node — every code block renders unhighlighted, silently.

Use `PrismAsyncLight` instead: it ships a static loader map, so bundlers can
split every grammar into a real chunk, and it loads *and registers* the grammar
before re-rendering. Unsupported languages normalize to `text` rather than
warning, so the two tests asserting the old warning are updated.

`folder/FilePreview` had the same defect (raw `PrismLight`, no registration at
all) and is switched over too.
@dosubot dosubot Bot added bug Something isn't working javascript Pull requests that update Javascript code labels Aug 24, 2026
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ba2929aa-0046-42f6-bba1-1876c2d902a5

📥 Commits

Reviewing files that changed from the base of the PR and between 25aad7b and 4cbfc41.

📒 Files selected for processing (3)
  • packages/x/components/code-highlighter/CodeHighlighter.tsx
  • packages/x/components/code-highlighter/__tests__/index.test.tsx
  • packages/x/components/folder/FilePreview.tsx

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

代码高亮组件改用 PrismAsyncLight,并删除按语言缓存和动态语言加载逻辑。完整模式继续使用 Prism。文件预览同步切换实现。测试改为验证不受支持语言回退为 text

Changes

异步语法高亮

Layer / File(s) Summary
高亮器运行时切换
packages/x/components/code-highlighter/CodeHighlighter.tsx
轻量模式直接使用 PrismAsyncLight。代码删除按语言缓存和动态加载器。完整模式继续使用 Prism
预览集成与语言回退
packages/x/components/folder/FilePreview.tsx, packages/x/components/code-highlighter/__tests__/index.test.tsx
文件预览改用 PrismAsyncLight。测试验证不受支持的语言会规范化为 text 并正常渲染,同时移除语言导入失败警告断言。

Estimated code review effort: 2 (简单) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 4cbfc

The PR replaces the bundler-incompatible grammar loading path with an async loader for code highlighting and file previews, with targeted tests passing. No actionable merge-blocking risk remains.

Suggested reviewers: kimteayon

Poem

兔子蹦蹦换高亮,
异步 Prism 载语法。
不识语言回到 text,
预览仍把代码画。
缓存旧路轻轻删,
测试伴我啃胡萝卜。

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了通过可被打包器分析的加载器加载 Prism 语法的核心修复,与代码变更和 PR 目标一致。
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Bundle Report

Changes will decrease total bundle size by 41 bytes (-0.0%) ⬇️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
antdx-array-push 2.11MB -41 bytes (-0.0%) ⬇️

Affected Assets, Files, and Routes:

view changes for bundle: antdx-array-push

Assets Changed:

Asset Name Size Change Total Size Change (%)
antdx.min.js -41 bytes 2.11MB -0.0%

@nrps9909 nrps9909 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The statically analyzable loader is the right direction, but exact head 4cbfc41f2ae031549df8b92b98e34ca54afa93d4 still leaves the primary Folder path unhighlighted for common file extensions.

PrismAsyncLight checks the requested string against its loader-map keys before loading. In the installed react-syntax-highlighter@16.1.1, supportedLanguages contains canonical keys such as markup, javascript, typescript, bash, python, and markdown, but not their common aliases html, js, ts, sh, py, or md. I verified this directly from PrismAsyncLight.supportedLanguages.

FilePreview.tsx passes the raw lowercased file extension as language, so .html, .js, .ts, .sh, .py, and .md files all normalize to text and remain unhighlighted. The public CodeHighlighter demo itself also passes lang="html", which now follows that fallback. This means the PR fixes the bundler failure but does not deliver syntax highlighting for many of the main consumer inputs it claims to fix.

Please add a shared alias-to-canonical normalization before both highlighters (at minimum html -> markup, js -> javascript, ts -> typescript, sh -> bash, py -> python, and md -> markdown, plus any aliases already documented/supported by the component contract). Add regression assertions that wait for actual Prism token markup for representative CodeHighlighter and Folder extension cases; checking only that <pre> exists cannot distinguish highlighting from the silent text fallback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working javascript Pull requests that update Javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants