Skip to content

♻️ refactor: share LiteXML command identities across bundles via a pure module#168

Merged
arvinxx merged 1 commit into
masterfrom
fix/litexml-command-shared-identity
Jun 6, 2026
Merged

♻️ refactor: share LiteXML command identities across bundles via a pure module#168
arvinxx merged 1 commit into
masterfrom
fix/litexml-command-shared-identity

Conversation

@arvinxx
Copy link
Copy Markdown
Member

@arvinxx arvinxx commented Jun 6, 2026

💻 Change Type

  • ♻️ refactor
  • 🐛 fix

🔀 Description of Change

This package ships two independently-bundled entries — the browser build (index / react / renderer) and the node build (headless). Each one previously inlined its own createCommand(...) call for the LiteXML commands.

Because Lexical's dispatchCommand matches listeners by object reference, not by the string label, a command obtained from one entry and dispatched onto an editor registered by the other silently no-ops — same label, different object.

Concretely, before this PR (v4.16.0 build output):

  • es/plugins/litexml/command/index.js holds createCommand("LITEXML_MODIFY_COMMAND") (used by the browser bundles)
  • es/headless.js inlines a second createCommand("LITEXML_MODIFY_COMMAND")

→ two distinct objects. Dispatching the browser command onto a HeadlessEditor kernel does nothing.

🛠 How it's fixed

  1. Move every LiteXML command identity (+ the DiffAction enum) into a new side-effect-free module src/plugins/litexml/command/symbols.ts (it only imports createCommand from lexical). The existing command/index.ts and command/diffCommand.ts re-export from it, so no other source changes.
  2. Add it as a dedicated shared chunk entry in the node build (tsdown.config.ts) so headless.js references ./plugins/litexml/command/symbols.js instead of inlining a copy. The unbundled browser build already resolves to the same emitted file.
  3. Expose it through a new subpath export @lobehub/editor/litexml-commands, pointing at that same symbols.js.

Result:

  • Both bundles import the same emitted symbols.js → a single runtime identity for each command.
  • The commands can be imported on the server (@lobehub/editor/litexml-commands) without pulling the DOM-dependent editor bundle.
  • Consumers can now dispatch LITEXML_*_COMMAND directly on a HeadlessEditor kernel instead of routing through wrapper methods.

🧪 How to Test

pnpm run ci + pnpm run test are green (301 tests). Verified against the built output:

import { createHeadlessEditor } from '@lobehub/editor/headless';
import { LITEXML_APPLY_COMMAND } from '@lobehub/editor/litexml-commands';

resetRandomKey();
const h = createHeadlessEditor();
h.hydrate({ type: 'markdown', content: '# This is a title \nThis is <ins>underline</ins> and this is <ins>underline2</ins>\n\n' });
h.kernel.dispatchCommand(LITEXML_APPLY_COMMAND, {
  litexml: ['<span id="lqqe">ModifiedText</span>', '<span id="m1v0">THIS IS </span>'],
});
// → exported markdown becomes "# ModifiedText\n\nTHIS IS <ins>underline</ins> ..."

The subpath command (imported from a DOM-free path) mutates a HeadlessEditor exactly as the in-bundle command would — proving the kernel and the subpath share one identity.

📝 Additional Information

  • No public API removed; existing LITEXML_* exports from ., ./headless, and ./plugins/litexml are unchanged.
  • Backstory: downstream (@lobechat/editor-runtime) currently works around the duplicate identity with a server-side LiteXML "adapter". With this subpath, that workaround can be removed.

🤖 Generated with Claude Code

…re module

The package ships two independently-bundled entries — the browser build
(`index`/`react`/`renderer`) and the node build (`headless`). Each previously
inlined its own `createCommand(...)` call for the LiteXML commands, so a command
obtained from one entry and dispatched onto an editor registered by the other
silently no-ops (Lexical matches command listeners by object reference, not by
the string label).

Isolate every LiteXML command identity (+ the `DiffAction` enum) into a new
side-effect-free `plugins/litexml/command/symbols.ts`, re-exported from the
existing command modules so nothing else changes. Emit it as a dedicated chunk
in the node build and expose it through a new `@lobehub/editor/litexml-commands`
subpath, so:

- both bundles import the same emitted `symbols.js` → one runtime identity;
- the commands can be imported on the server without pulling the DOM-dependent
  editor bundle (the module only imports `createCommand` from `lexical`).

This lets consumers dispatch `LITEXML_*_COMMAND` directly on a `HeadlessEditor`
kernel instead of routing through wrapper methods.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 6, 2026

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

Project Deployment Actions Updated (UTC)
lobe-editor Ready Ready Preview, Comment Jun 6, 2026 5:33pm

Request Review

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

We've reviewed this pull request using the Sourcery rules engine

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 6, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@lobehub/editor@168

commit: 275611d

@arvinxx arvinxx merged commit f7d70c1 into master Jun 6, 2026
7 of 8 checks passed
@arvinxx arvinxx deleted the fix/litexml-command-shared-identity branch June 6, 2026 17:56
github-actions Bot pushed a commit that referenced this pull request Jun 6, 2026
### [Version&nbsp;4.16.1](v4.16.0...v4.16.1)
<sup>Released on **2026-06-06**</sup>

#### ♻ Code Refactoring

- **misc**: Share LiteXML command identities across bundles via a pure module.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Share LiteXML command identities across bundles via a pure module, closes [#168](#168) ([f7d70c1](f7d70c1))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
@lobehubbot
Copy link
Copy Markdown
Member

🎉 This PR is included in version 4.16.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants