Skip to content

[lexical-code][lexical-markdown] Extract Prism; make code fences opt-in#8197

Closed
gurkerl83 wants to merge 7 commits intofacebook:mainfrom
gurkerl83:prism-extract-markdown-composition
Closed

[lexical-code][lexical-markdown] Extract Prism; make code fences opt-in#8197
gurkerl83 wants to merge 7 commits intofacebook:mainfrom
gurkerl83:prism-extract-markdown-composition

Conversation

@gurkerl83
Copy link
Copy Markdown

Summary

This PR removes the Prism-driven coupling between markdown and code packages by doing two coordinated changes:

  1. Extract Prism highlighting ownership from @lexical/code into @lexical/code-prism.
  2. Make markdown code-fence support explicit composition (instead of implicit default behavior in @lexical/markdown).

Context

A previous attempt (#8196) proposed a lightweight @lexical/code/node subpath to avoid importing the Prism-heavy @lexical/code root from markdown internals.

Based on maintainer feedback (comment), this PR follows the structural direction instead:

  • move Prism out to a dedicated package,
  • avoid introducing an interim awkward subpath.

Problem

@lexical/markdown and @lexical/code were coupled such that optional code/highlighting paths affected markdown-only use cases. This made modular composition weaker than intended.

Changes

1) Prism extraction

  • Added @lexical/code-prism package.
  • Moved Prism-specific runtime/highlighting logic there.
  • Updated internal consumers (playground/integration fixtures) to import Prism APIs from @lexical/code-prism.

2) @lexical/code slim-down

  • @lexical/code keeps code-node/core behavior.
  • Prism ownership is no longer in @lexical/code root.

3) Markdown composition change

  • @lexical/markdown no longer has a direct runtime dependency on @lexical/code.
  • TRANSFORMERS no longer includes code-fence behavior by default.
  • Added createMarkdownCodeBlockTransformer(...) so code-fence behavior can be composed explicitly by consumers.
  • Added multiline transformer isNode predicate support so shortcut skip behavior remains correct for composed node types.

4) Docs/tests updates

  • Updated markdown docs and React plugin docs to show explicit code-fence composition.
  • Added/updated unit tests for:
    • default core transformers (no code-fence -> CodeNode),
    • explicit composed code-fence behavior.

Semver-visible behavior change

@lexical/markdown default TRANSFORMERS is now code-agnostic.
Consumers that want fenced code block conversion to CodeNode must compose it explicitly.

Migration

import {
  TRANSFORMERS,
  createMarkdownCodeBlockTransformer,
} from '@lexical/markdown';
import {$createCodeNode, $isCodeNode, CodeNode} from '@lexical/code';

const CODE = createMarkdownCodeBlockTransformer({
  $createCodeNode,
  $isCodeNode,
  dependencies: [CodeNode],
});

const transformers = [...TRANSFORMERS, CODE];

Validation

  • pnpm exec vitest run packages/lexical-markdown/src/__tests__/unit/LexicalMarkdown.test.ts packages/lexical-markdown/src/__tests__/unit/MarkdownTransformers.test.ts
  • pnpm exec vitest run packages/lexical-markdown/src/__tests__/unit/LexicalMarkdownCodeComposition.test.ts
  • pnpm exec vitest run scripts/__tests__/unit/build.test.ts
  • pnpm run flow
  • pnpm run tsc

Issue links

Fixes #5381
Related to #4550 #5424 #6575

Move Prism implementation sources into `packages/lexical-code-prism/src`:

- `CodeHighlighterPrism.ts`
- `FacadePrism.ts`

This commit is relocation-only:
- no import rewiring
- no behavior changes
- no package/export wiring changes yet

Follow-up commits will wire imports/exports and switch consumers.
Create initial @lexical/code-prism package scaffold.

Adds:
- package manifest
- CJS package entry shim
- source index entrypoint

No runtime rewiring or API ownership changes yet.
Move Prism runtime API ownership from @lexical/code to @lexical/code-prism.

- Rewire Prism implementation sources to depend on @lexical/code node APIs
- Update @lexical/code exports/deps to remove Prism API surface
- Update playground and integration fixtures to import Prism APIs from @lexical/code-prism
- Add TS path mappings for @lexical/code-prism

No markdown transformer composition changes in this commit.
Align Flow public type declarations with Prism extraction.

- Add Flow declarations for @lexical/code-prism Prism API surface
- Remove Prism-related declarations from @lexical/code Flow surface

No runtime behavior changes in this commit.
- Remove markdown’s direct runtime dependency on @lexical/code.
- Replace built-in CODE export with createMarkdownCodeBlockTransformer.
- Add multiline transformer node predicates to preserve shortcut skip behavior.
- Keep default TRANSFORMERS code-agnostic.
- Update Flow/TS surfaces for the new composition API.
- Rewire playground markdown transformer composition to use the factory.
- Regenerate pnpm-lock.yaml with pnpm install --lockfile-only.
- Add a dedicated markdown composition test file for fenced code behavior.
- Verify default TRANSFORMERS import fenced blocks as paragraphs.
- Verify composed transformers ([...TRANSFORMERS, CODE]) import fenced blocks as CodeNode.
- Keep broad LexicalMarkdown.test.ts focused on general markdown behavior.
- Align test setup with createMarkdownCodeBlockTransformer composition usage.
- Add markdown README guidance for composing code fences with createMarkdownCodeBlockTransformer.
- Update React plugin docs to show composed transformers usage with MarkdownShortcutPlugin.
- Clarify that code-block shortcuts are opt-in via composition.
- Update @lexical/code README to describe node/util ownership after Prism extraction.
- Add @lexical/code-prism README describing Prism highlighting ownership.
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lexical Ready Ready Preview, Comment Mar 7, 2026 9:55pm
lexical-playground Ready Ready Preview, Comment Mar 7, 2026 9:55pm

Request Review

@etrepum
Copy link
Copy Markdown
Collaborator

etrepum commented Mar 7, 2026

This is a better direction but it breaks compatibility without any sort of real deprecation cycle. For a few releases @lexical/code is going to have to work as-is. The problem you want solved is not fixable in a single pass or in a single release, but you should be able to work around it by configuring your bundler to not include the prism stuff in the meantime in situations where you aren’t going to use it.

@etrepum etrepum added the extended-tests Run extended e2e tests on a PR label Mar 7, 2026
@gurkerl83
Copy link
Copy Markdown
Author

Makes sense, and thanks for the guidance.
I agree this is not something to flip in one pass without a deprecation path. Even if the change is technically small, it changes expected default behavior for existing users.

A major release could be a better place for this kind of split. I also tried bundler-level mitigation in our app, but it was brittle in practice and didn’t reliably keep the Prism/code path out in all cases.

For now, the compatibility requirement for @lexical/code as-is is clear, so I’ll leave this PR as-is. It may still be the wrong shape (especially around adding a compose layer), but it was intended as one exploration of stronger separation.

@etrepum
Copy link
Copy Markdown
Collaborator

etrepum commented Mar 7, 2026

Moving prism code to the new package makes sense, that part is directionally correct, but there should be deprecated re-exports from the main code package so that people can upgrade without changing the usage in lockstep.

In the first pass, no behavior should change other than the addition of deprecation warnings and new packages/exports for the upgrade path. The playground and other sibling packages can be upgraded to use the new path to avoid deprecation warnings. No types or exports in @lexical/code should change. No behavior change to markdown should happen at all. No tests should need to change (other than imports, but only to avoid deprecation warnings) and all existing tests should pass.

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

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. extended-tests Run extended e2e tests on a PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: prismjs is included as a dependency of @lexical/markdown

2 participants