Skip to content

[lexical-code-prism] Production client builds throw Prism is not defined #9857

Open

Description

@nbozidarevic

Description

Apps bundling @lexical/code-prism for production with Rolldown/Vite throw Prism is not defined at startup when a route that highlights code loads.

packages/lexical-code-prism/src/FacadePrism.ts loads the core with a bare import 'prismjs' (CommonJS) followed by the language components (plain scripts that read the bare Prism global). The bare import gives bundlers nothing to order on, so the CommonJS init can sink past the component scripts: they evaluate with no global Prism. The module then reads the result through globalThis.Prism || window.Prism, which is likewise unset.

Minimal shape that throws (Vite/Rolldown production build, single route chunk):

facade.js:

import 'prismjs';
import './lang-clike.js'; // plain script: Prism.languages.clike = {...}
export const P = globalThis.Prism || window.Prism;

The shared-chunk variant (language scripts in an earlier-evaluating chunk than the core's require call) throws unconditionally. Reordering so the core initializes first, plus reading it through the module namespace so bundlers must initialize it ahead of the module body, fixes both shapes.

I have a fix incoming as a PR: namespace-import the core first and read the export from the namespace (keeping the pre-existing-global adoption behavior).

Activity

  1. added a commit that references this issue on Nov 10, 2026
    3ebf324
  2. nbozidarevic commented on Nov 11, 2026

    @nbozidarevic
    ContributorAuthor

    Found the failing layout, with a repro. Posting it here so the follow-up has something to point its test at.

    The layout that throws on main

    A Vite/Rolldown production build with aggressive tree-shaking — treeshake.moduleSideEffects: false — using the single-chunk facade shape from this issue (bare import 'prismjs', plain-script language components, result read through globalThis.Prism).

    • Bare import: the entire core and all language components are shaken out. The output bundle is 0.87 kB vs 20.18 kB — all that remains is a dead globalThis.Prism; read, so Prism is unset and clike is missing. The import 'prismjs' is eliminated because nothing in the graph uses its value.
    • Namespace import ([lexical-code-prism] Chore: read the Prism core through its module namespace #9858): globalThis.Prism ?? PrismCore reads the binding, so the core is a used value import and survives. Full core + components, evaluates clean.

    Reproduced both with raw rolldown 1.3.1 (treeshake: {moduleSideEffects: false}) and with Vite 8.3.7 (same flag via build.rolldownOptions.treeshake), prismjs 1.30.0, single route chunk. This also explains why the pinned-bundler matrix in #9858 saw nothing: under default tree-shaking the bare import is kept, and both forms evaluate identically.

    What #9858 does not cover

    The shared-chunk shape (a component-only chunk evaluating before the core chunk) throws identically before and after — verified on Vite 8.3.7, esbuild 0.28.2, and rolldown 1.3.1. A namespace import can't pull the core init into an earlier chunk, so that shape needs app-level ordering (core imported in the entry/shared chunk), not a FacadePrism change. Saying this plainly so the follow-up isn't expected to cover it.

    Proposed follow-up

    Keep-both, as suggested on the revert: the bare import 'prismjs' first (preserves the eager order that #9877 restored for loaders that defer named requires — Metro inlineRequires, Babel lazy, and similar setups), then import * as PrismCore from 'prismjs' to read the value (survives the DCE layout above — I verified keep-both passes it too). With a regression test built on this layout: build the facade with moduleSideEffects: false and assert the clike grammar is present. Happy to put that PR up.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions