Skip to content

Commit abc3c74

Browse files
committed
fix(emcn): keep Prism grammar registrations in bundle, never throw on missing grammar
Consumers import { highlight, languages } from the @sim/emcn barrel, which re-exported Prism's highlight straight from prismjs. Bundlers resolved that passthrough directly from prismjs and skipped prism.ts's module body, dropping the side-effect grammar registrations so languages.json (etc.) were undefined at runtime. Prism then threw 'The language "json" has no grammar.', crashing the start-block file[] input format field and every other workflow-editor code highlighter. Own highlight as a local wrapper so the registrations stay in the dependency graph, and degrade to escaped plaintext when a grammar is missing instead of throwing.
1 parent 0613ceb commit abc3c74

1 file changed

Lines changed: 25 additions & 3 deletions

File tree

  • packages/emcn/src/components/code

packages/emcn/src/components/code/prism.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { highlight, languages } from 'prismjs'
1+
import { type Grammar, languages, highlight as prismHighlight } from 'prismjs'
22
import 'prismjs/components/prism-javascript'
33
import 'prismjs/components/prism-python'
44
import 'prismjs/components/prism-json'
@@ -10,10 +10,32 @@ import 'prismjs/components/prism-json'
1010
* shared `Prism.languages` registry), which marks any module that statically
1111
* imports them as having side effects and therefore non-tree-shakeable. Keeping
1212
* them here — rather than in `code.tsx` — ensures Prism only enters bundles that
13-
* actually import `highlight`/`languages`, instead of every consumer of the
14-
* shared `@sim/emcn` barrel (which re-exports `Code`).
13+
* actually import these utilities, instead of every consumer of the shared
14+
* `@sim/emcn` barrel (which re-exports `Code`).
1515
*
1616
* `code.tsx` itself never imports this module statically; it loads it lazily via
1717
* dynamic `import()` on first highlight.
18+
*
19+
* `highlight` is a local wrapper rather than a re-export of Prism's `highlight`.
20+
* A bare re-export lets bundlers resolve the binding straight from `prismjs` and
21+
* skip this module's body, dropping the grammar registrations above so
22+
* `languages.json` (etc.) become `undefined` at runtime. Owning the function
23+
* keeps the registrations in the dependency graph and lets us degrade to escaped
24+
* plaintext when a grammar is missing instead of throwing.
25+
*/
26+
27+
function escapeHtml(text: string): string {
28+
return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
29+
}
30+
31+
/**
32+
* Highlights `code` with the given Prism `grammar`, returning HTML markup.
33+
* Falls back to escaped plaintext when `grammar` is undefined so a missing or
34+
* unregistered language never throws `The language "<x>" has no grammar.`.
1835
*/
36+
function highlight(code: string, grammar: Grammar | undefined, language: string): string {
37+
if (!grammar) return escapeHtml(code)
38+
return prismHighlight(code, grammar, language)
39+
}
40+
1941
export { highlight, languages }

0 commit comments

Comments
 (0)