From fd1831ea3c30edbfaeb7b06e55fec36acd084328 Mon Sep 17 00:00:00 2001 From: Devin Foley Date: Thu, 27 Aug 2026 09:01:51 -0700 Subject: [PATCH] Restore a single Lexical copy so the rich editor renders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cherry-pick of the master fix onto the v2026.825.0-beta.1 base, so the beta channel can ship a patched candidate without waiting for a full rebase onto master. The rich markdown editor fell back to its raw-source textarea on every field, for any content including an empty one, with the render-time error "LexicalBuilder.fromEditor: The given editor was created with LexicalBuilder 0.48.0 but this version is 0.49.0. A project should have exactly one copy of LexicalBuilder". Root pnpm overrides pinned the Lexical family to 0.49.0 while @mdxeditor/editor declares lexical ^0.48.0 — a caret on a 0.x version pins the minor, so the override forced the editor past the only line it supports (0.49 also carried breaking node-config changes). The override list was incomplete besides: @lexical/extension, @lexical/history, and @lexical/internal are reached transitively and stayed on 0.48, so the graph mixed both lines and the LexicalBuilder invariant threw. The browser bundle carried two @lexical/extension instances as a result. Drop the Lexical entries from the root overrides (react, react-dom, and rollup stay) and pin the app's own lexical and @lexical/link to 0.48.0, so the whole graph resolves through the editor's supported line. The app subclasses LinkNode for mention-aware links and registers it into the editor's instance, so node identity depends on this being one copy. Add a resolution-graph test that fails fast on a mixed graph rather than waiting for a render crash: it asserts the app and the editor resolve the same lexical copy, that @lexical/link and the transitively-reached @lexical/extension sit on that version, and that the version satisfies the editor's declared range. The lockfile is regenerated against this base rather than taken from master — the beta base predates several unrelated dependency bumps, so master's lockfile does not describe this tree. Only Lexical resolutions move; every other entry is unchanged. The conflict on ui/package.json was lucide-react version drift, resolved in favour of this base. Tested on the beta base: ui typecheck clean; the new resolution test passes (3); mention-deletion, mention-aware-link-node, and MarkdownEditor suites pass (42); the lockfile now resolves the whole Lexical family to a single 0.48.0 line; the built bundle went from two LexicalBuilder registrations to one. A probe rendering the real editor (no mock) returns no fallback for empty and plain-prose content, where both previously fell back. Prose containing bare angle brackets still falls back on this base — that is a separate editor bug, not present on master either, and out of scope here. --- package.json | 12 +- pnpm-lock.yaml | 421 +++++++++++-------------- ui/package.json | 4 +- ui/src/lib/lexical-single-copy.test.ts | 66 ++++ 4 files changed, 252 insertions(+), 251 deletions(-) create mode 100644 ui/src/lib/lexical-single-copy.test.ts diff --git a/package.json b/package.json index 51b94466634..0415d2733b7 100644 --- a/package.json +++ b/package.json @@ -88,17 +88,7 @@ "overrides": { "rollup": ">=4.59.0", "react": "^19.2.8", - "react-dom": "^19.2.8", - "lexical": "0.49.0", - "@lexical/clipboard": "0.49.0", - "@lexical/link": "0.49.0", - "@lexical/list": "0.49.0", - "@lexical/markdown": "0.49.0", - "@lexical/plain-text": "0.49.0", - "@lexical/react": "0.49.0", - "@lexical/rich-text": "0.49.0", - "@lexical/selection": "0.49.0", - "@lexical/utils": "0.49.0" + "react-dom": "^19.2.8" } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b142252c6e1..3ba1b8b0fb6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,16 +8,6 @@ overrides: rollup: '>=4.59.0' react: ^19.2.8 react-dom: ^19.2.8 - lexical: 0.49.0 - '@lexical/clipboard': 0.49.0 - '@lexical/link': 0.49.0 - '@lexical/list': 0.49.0 - '@lexical/markdown': 0.49.0 - '@lexical/plain-text': 0.49.0 - '@lexical/react': 0.49.0 - '@lexical/rich-text': 0.49.0 - '@lexical/selection': 0.49.0 - '@lexical/utils': 0.49.0 patchedDependencies: acpx@0.12.0: @@ -954,8 +944,8 @@ importers: specifier: ^3.2.2 version: 3.2.2(react@19.2.8) '@lexical/link': - specifier: 0.49.0 - version: 0.49.0(typescript@5.9.3) + specifier: 0.48.0 + version: 0.48.0(typescript@5.9.3) '@mdxeditor/editor': specifier: ^4.2.1 version: 4.2.1(@codemirror/language@6.12.4)(@lezer/highlight@1.2.3)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@5.9.3)(yjs@13.6.29) @@ -1026,8 +1016,8 @@ importers: specifier: ^26.3.6 version: 26.3.6(typescript@5.9.3) lexical: - specifier: 0.49.0 - version: 0.49.0(typescript@5.9.3) + specifier: 0.48.0 + version: 0.48.0(typescript@5.9.3) lucide-react: specifier: ^0.577.0 version: 0.577.0(react@19.2.8) @@ -2668,32 +2658,32 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@lexical/a11y@0.49.0': - resolution: {integrity: sha512-ypvO0SI9DCzEAjxkRLq+gNzRqfjg632RCkwu16Hk94PCBrEmcDhx5iUGg3SRJC7WnM2wXC/F/SB8yWDEfq7tEw==} + '@lexical/a11y@0.48.0': + resolution: {integrity: sha512-18W4ehyipkUim4YVoDZitoH63Om3j6iCN4c84zdqE9RgkWf/PE4rvI/8BHTm6Ni7NkVE14nimXgkpaP5ok15zA==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/clipboard@0.49.0': - resolution: {integrity: sha512-AVKj21xH1qU7JAFA/v0hCoafa+Yti1I7cHG+JQIgc/EqCtP8ePeJyIfTPNN/tXskoCdq++HewQoiSXRwwlocVg==} + '@lexical/clipboard@0.48.0': + resolution: {integrity: sha512-xO2trk6+yBl8XXa/VNe20kXczmPxFoWtUHjidbBLEtlGBj+mo63pJj6H5o/WlZsoMKmIOJxwxsn2ejrS8G0/7A==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/code-core@0.49.0': - resolution: {integrity: sha512-29sXI4ydcN/OyBJNlZXkGKtQXzfiIWet+YN20+wT97zkqityOvRilK6qzTRz9Cd0ungEPlJENqXQhsy6BJeXpg==} + '@lexical/code-core@0.48.0': + resolution: {integrity: sha512-+O1Ge06AuSo6+r8R2Xk6SkWG07H5/e4K/Scw9aqCM/BRjITxgvFTHTNvgQbaobCsP0uBJiwW1+ZGeWAWcBCY4w==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/devtools-core@0.49.0': - resolution: {integrity: sha512-LgjNlvRiuO/QF36aG6kfR4ptEvSw587o2+YF6SOsaOB7iw2Z3pqpsKloWSEF4QXaLFI65Li8JjMdQj+BaFMIOA==} + '@lexical/devtools-core@0.48.0': + resolution: {integrity: sha512-4kvKWW6ebgQnJNLXPLmw7dqgSChvzYIBNYtfuR6c48Sw+V/QXQTWqfIUbCIe5X4uG8EEXd5O/udXaJx7GBuP+w==} peerDependencies: react: ^19.2.8 react-dom: ^19.2.8 @@ -2702,8 +2692,8 @@ packages: typescript: optional: true - '@lexical/dragon@0.49.0': - resolution: {integrity: sha512-62/4DP5qyX/l4Yf5qRyyQrs9BV725eRU3OmLUW6g7T5xrcHXxAo7tia/NvqjqvXdpvQzyHjWgsy7dMitGITUsw==} + '@lexical/dragon@0.48.0': + resolution: {integrity: sha512-uPuu7fVca9vmL/Oz30CRZ7FIPIodwMrTgNsRmV8jE6Qd6a7RNiTW7r3+EhbhIdkLb/sjcWsYyOMyUR1TJAB0wQ==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: @@ -2718,16 +2708,8 @@ packages: typescript: optional: true - '@lexical/extension@0.49.0': - resolution: {integrity: sha512-Wv0VsuqxorbxHCK4ms1PwAu6cXIGNLtflq66auF+zwxOtBprkSFV8VzzzdKLOYD2admP0VK6EqVCeGXFK1GggA==} - peerDependencies: - typescript: '>=5.2' - peerDependenciesMeta: - typescript: - optional: true - - '@lexical/hashtag@0.49.0': - resolution: {integrity: sha512-aQTpNjrEO5apsaohWGl4ku22EgHpWUzZg5CIzvriS2A1vNmIfzLoCL+lDFpzAwk3DT1Kj1yvGHh5TvhYsb9D8w==} + '@lexical/hashtag@0.48.0': + resolution: {integrity: sha512-hPQtdnbVoNAFsmfnCGfgY7mDbvk6mIznlCRmIR7tLeQKXqz/0Tb6eH3W24EESk9hAF8wFUYNKWE1/Kb3Hl2vEQ==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: @@ -2742,16 +2724,8 @@ packages: typescript: optional: true - '@lexical/history@0.49.0': - resolution: {integrity: sha512-uQdtEd34gIJklXNSdHS2Wko1zxx1xUMVXbiodcLO6a3GeFTE5bKnx6af1zGX78KpYhUQYnHWorKuUvtdZlJPaA==} - peerDependencies: - typescript: '>=5.2' - peerDependenciesMeta: - typescript: - optional: true - - '@lexical/html@0.49.0': - resolution: {integrity: sha512-NQqAydKzRjQl7Jx+bTTyC2iuz5uhuDaQX0SSPrdfgaFDCPjqQEejcBkCt1QXnu1CkbVHutZKVGVzljx40Y6y+Q==} + '@lexical/html@0.48.0': + resolution: {integrity: sha512-uBxlgKl4YgSNEgHJSshdBqtGDzruWdx1ewop+u6faT67qHUdP3P0cUXIrG6NToDWvsL6fzCstAbN76PMER1Pnw==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: @@ -2766,64 +2740,56 @@ packages: typescript: optional: true - '@lexical/internal@0.49.0': - resolution: {integrity: sha512-s+XjPC7Qb39A/Xx9ahcz1s69CPix4ultaqyW+MDG0AXYW2quDr7m0USqReKIAiuoLIWtGN5HW8BwV2Y6t4qr6Q==} + '@lexical/link@0.48.0': + resolution: {integrity: sha512-E0UDmNLUXs/yMCnnE7hbFO0CvhWghmqa+qqPksFfzLkpMHdPpdS1yg59YEbYoNHLi/DXIu4cFRvpHIEuooxwNg==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/link@0.49.0': - resolution: {integrity: sha512-CsZVu1OfnPOn6IKWVO5DbmKwIuicGtgWSzrZMdMB8w6yPoMgFyPWeVulhPE7LNz2FhaQCWv+lVVYVAiJrUzJsg==} + '@lexical/list@0.48.0': + resolution: {integrity: sha512-9Qe/Vur44v9F9enj55SUzf79FVsijcGOQug7SpiIU8ekLr7JNzcilKYBYcZ6etGEo7bqQHsYMHXeJcSBbCI2zA==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/list@0.49.0': - resolution: {integrity: sha512-zs6wYkxakDRcJO0KmwrPPHgeLLIxzjD+P2CRu+scJCHRuA5e2iSw2iFjH5n/LlWBrlnPMSjeQse4QqsRy9nnqA==} + '@lexical/mark@0.48.0': + resolution: {integrity: sha512-DTtypWvnYSXyNUxEmsUnh4y0xXkmdk8Y72EZl8WDHcCwjqaLJlUakH7p/TuSJczs3uVSaoJzu0yh7oGkP1Vsvw==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/mark@0.49.0': - resolution: {integrity: sha512-JSYD8UYQQOMT6oDYh1KsLPK3KMGfQzU7E9fJkugKEO0/XebfsmiuNtkLCGDVIZOo8bNHlwb1YVvmswC2bjotMQ==} + '@lexical/markdown@0.48.0': + resolution: {integrity: sha512-1WasBenW4bEsa5xtnycVo8G2hcxIqyYLn3/r98yD2Y+54ZyeFuIQfOeJYhIgCh4YkKpfqyrFwkMQwAOsQDqZjA==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/markdown@0.49.0': - resolution: {integrity: sha512-n7x/3OLdi5R0ztICCn8udxRIWFZFVtmQS3a06Tjkyvm/j/dPlFQ/henKSOFjjX/IqVzQzSmiNaDhqKWxXM5npQ==} + '@lexical/overflow@0.48.0': + resolution: {integrity: sha512-1YEvMz2tW3EbwrON9mjrkjMVl/vdTcPYSn9P1j6mf5gj0LOoLDNI4TbvSD4SViy+TDghxNdG8YdCISyU2b4YKA==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/overflow@0.49.0': - resolution: {integrity: sha512-h5Qjv2SkpI76kaukvKJzvWehG9hwB06GEmAnp6aJaioOzoDcBAeWpmYBebtBUwRm0PNLFe3YICFFLpZ5Tg5VoQ==} + '@lexical/plain-text@0.48.0': + resolution: {integrity: sha512-q4f/4VZKVgCrIW2FhDFR2RII1BU0ljedPgEmJ8XQn1zc+JOFPom8Lp0lV5nyEvZaAJYwM/TfoJ9g2V7ESFKznA==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/plain-text@0.49.0': - resolution: {integrity: sha512-l7IuUj9n9CtFfz4Fz6zU6mmO+VkcnvyyebABx+lHevvTa7B6YI5PPVgABFfzdyPanyW/FGs7qpKBf59inAqbkg==} - peerDependencies: - typescript: '>=5.2' - peerDependenciesMeta: - typescript: - optional: true - - '@lexical/react@0.49.0': - resolution: {integrity: sha512-NqXF4i/IBFKVDDLDJYTwmBXE++MQuIrTQs7VTux6IPyDEpyfeFMn1rQTFb949S3f7rs33Rcu1uGtFbODQfS1Iw==} + '@lexical/react@0.48.0': + resolution: {integrity: sha512-uVh9/QSrbtjLjVbxfJ+sfiMyhUq/rv7H6uBEVDDIw1rkZJSDY1fvf/CX+dyKgwcDFjKQZ8/9i5f9UCVPeQ01hA==} peerDependencies: react: ^19.2.8 react-dom: ^19.2.8 @@ -2835,48 +2801,48 @@ packages: yjs: optional: true - '@lexical/rich-text@0.49.0': - resolution: {integrity: sha512-LM+d8ULmQvi2Av6UaW4NAIa/Bj9FOo9oh6+dglTzuoFPFQ3sogy5c38i2K9k0ul7aE+bFsXkSmHbu4LkrHIkBQ==} + '@lexical/rich-text@0.48.0': + resolution: {integrity: sha512-QMXFnwCKAQ4yzxvx5FwmANcx3K+NaBkGTxAVD8s8pOKDD/U5rzDS1iIvhH6TLWaFp7VzvLmLB+Sl1Ie/RnkaDQ==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/selection@0.49.0': - resolution: {integrity: sha512-08Vd1+VoC6YnztWOFWVsqF/Hxw5EP8qeL1c7t3+rVCV8revLzXxdQw+vbPX3tgM4fmjOBDUXk6Ws1+rTtsqjcQ==} + '@lexical/selection@0.48.0': + resolution: {integrity: sha512-Uc0wTrEtHcYK6z/aHHjkgH3vX/R4Bf8mO+qH3VbxfSAKYzNYktM0j+ZGdq5kIEL4frnw/9SulNCXlH7xpjuDgA==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/table@0.49.0': - resolution: {integrity: sha512-PG+dxSTBPbUZUY2kBZm0pJuqCEOGom4RrGrc6Jlj/7o0F37k/gGRVprFSRKuf3CqO03QIP6qRovXGDuKb+Exuw==} + '@lexical/table@0.48.0': + resolution: {integrity: sha512-t9Mz7q6ODLUz0lG5Xn9EY/5YiVpTHCqlPQP4EtFXlnBQT3DuKeDS3cC0Cn8sGSZc11YY5OLDfWpB64Frs9BL3g==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/text@0.49.0': - resolution: {integrity: sha512-mowedvbvx0HDaW+ymVdYmWVhueqgauhhqIWaCtbJj33tPk8pOu1BB0pZuJQbOB+1bDnFiZhkJV8W/CvR2M9lEA==} + '@lexical/text@0.48.0': + resolution: {integrity: sha512-ktTMRbsX4wKxdG2OpZCkrqtt8k9Vg/ZpWdukOQ0r1xPRtCuL1T+q91l7cy2ywIuCfMGYS0aoZGB4LdpUMe/H1g==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/utils@0.49.0': - resolution: {integrity: sha512-Jaa6DERBqxiFOFa49VPRV1WOb7mzRbMZ5U+v+RFagjzTmBhfxDmEkbQQ9nYTU3n3DPfdT8Hkyffextmw04etXg==} + '@lexical/utils@0.48.0': + resolution: {integrity: sha512-W4k4P+y6jmRfna8+ad4X+iMd5h8es5PC3bUw5tbi7MRApxaaFG/0w+uJiZVSwbT2Q6JnA2xhBaqzPgt/Gn6djg==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: typescript: optional: true - '@lexical/yjs@0.49.0': - resolution: {integrity: sha512-uqV3D0AOLqQ1f/Bo6ZtuDLoJYGlBm0RlucNYhNvfMAncEw0f/xHc1pTyXeWjdCI/iWgWR/MV6hgVi26TSPs+Vg==} + '@lexical/yjs@0.48.0': + resolution: {integrity: sha512-fFsE8EnPM/2KK9rMJ0z6T+Da5UW5V4P+XiAA03LoHTY5YQ/Oy8Q0i7Wcmocv/B/SpsY2o8g07euZEfudl9MVKA==} peerDependencies: typescript: '>=5.2' yjs: '>=13.5.22' @@ -6715,8 +6681,8 @@ packages: layout-base@2.0.1: resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} - lexical@0.49.0: - resolution: {integrity: sha512-9V1ZIzGpJEd8rIN+nN7veL4fW4fFWbS66Un4JNqSZB4D5t9euzN9+3+jEXy83FjNjNy0MiiUI3+DQaGCLYko0w==} + lexical@0.48.0: + resolution: {integrity: sha512-KK4Tyr/cPsleoZ7XvhGRiRmcrZidSmoFUdIXK9nPubIifoC+80Dc5THyc4xtGKtsW24S1TsHzk5gmfBU+TxmEg==} peerDependencies: typescript: '>=5.2' peerDependenciesMeta: @@ -10068,106 +10034,89 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@lexical/a11y@0.49.0(typescript@5.9.3)': + '@lexical/a11y@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/extension': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/extension': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/clipboard@0.49.0(typescript@5.9.3)': + '@lexical/clipboard@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/extension': 0.49.0(typescript@5.9.3) - '@lexical/html': 0.49.0(typescript@5.9.3) - '@lexical/internal': 0.49.0(typescript@5.9.3) - '@lexical/list': 0.49.0(typescript@5.9.3) - '@lexical/selection': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) + '@lexical/extension': 0.48.0(typescript@5.9.3) + '@lexical/html': 0.48.0(typescript@5.9.3) + '@lexical/internal': 0.48.0(typescript@5.9.3) + '@lexical/list': 0.48.0(typescript@5.9.3) + '@lexical/selection': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) '@types/trusted-types': 2.0.7 - lexical: 0.49.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/code-core@0.49.0(typescript@5.9.3)': + '@lexical/code-core@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/extension': 0.49.0(typescript@5.9.3) - '@lexical/html': 0.49.0(typescript@5.9.3) - '@lexical/internal': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/extension': 0.48.0(typescript@5.9.3) + '@lexical/html': 0.48.0(typescript@5.9.3) + '@lexical/internal': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/devtools-core@0.49.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@5.9.3)': + '@lexical/devtools-core@0.48.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@5.9.3)': dependencies: - '@lexical/html': 0.49.0(typescript@5.9.3) - '@lexical/link': 0.49.0(typescript@5.9.3) - '@lexical/mark': 0.49.0(typescript@5.9.3) - '@lexical/table': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/html': 0.48.0(typescript@5.9.3) + '@lexical/link': 0.48.0(typescript@5.9.3) + '@lexical/mark': 0.48.0(typescript@5.9.3) + '@lexical/table': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: typescript: 5.9.3 - '@lexical/dragon@0.49.0(typescript@5.9.3)': + '@lexical/dragon@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/extension': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/extension': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 '@lexical/extension@0.48.0(typescript@5.9.3)': dependencies: '@lexical/internal': 0.48.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) '@preact/signals-core': 1.14.4 - lexical: 0.49.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/extension@0.49.0(typescript@5.9.3)': + '@lexical/hashtag@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/internal': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - '@preact/signals-core': 1.14.4 - lexical: 0.49.0(typescript@5.9.3) - optionalDependencies: - typescript: 5.9.3 - - '@lexical/hashtag@0.49.0(typescript@5.9.3)': - dependencies: - '@lexical/text': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/text': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 '@lexical/history@0.48.0(typescript@5.9.3)': dependencies: '@lexical/extension': 0.48.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/history@0.49.0(typescript@5.9.3)': + '@lexical/html@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/extension': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) - optionalDependencies: - typescript: 5.9.3 - - '@lexical/html@0.49.0(typescript@5.9.3)': - dependencies: - '@lexical/extension': 0.49.0(typescript@5.9.3) - '@lexical/internal': 0.49.0(typescript@5.9.3) - '@lexical/selection': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/extension': 0.48.0(typescript@5.9.3) + '@lexical/internal': 0.48.0(typescript@5.9.3) + '@lexical/selection': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -10175,146 +10124,142 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@lexical/internal@0.49.0(typescript@5.9.3)': - optionalDependencies: - typescript: 5.9.3 - - '@lexical/link@0.49.0(typescript@5.9.3)': + '@lexical/link@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/extension': 0.49.0(typescript@5.9.3) - '@lexical/html': 0.49.0(typescript@5.9.3) - '@lexical/internal': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/extension': 0.48.0(typescript@5.9.3) + '@lexical/html': 0.48.0(typescript@5.9.3) + '@lexical/internal': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/list@0.49.0(typescript@5.9.3)': + '@lexical/list@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/extension': 0.49.0(typescript@5.9.3) - '@lexical/html': 0.49.0(typescript@5.9.3) - '@lexical/internal': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/extension': 0.48.0(typescript@5.9.3) + '@lexical/html': 0.48.0(typescript@5.9.3) + '@lexical/internal': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/mark@0.49.0(typescript@5.9.3)': + '@lexical/mark@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/markdown@0.49.0(typescript@5.9.3)': + '@lexical/markdown@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/code-core': 0.49.0(typescript@5.9.3) - '@lexical/internal': 0.49.0(typescript@5.9.3) - '@lexical/link': 0.49.0(typescript@5.9.3) - '@lexical/list': 0.49.0(typescript@5.9.3) - '@lexical/rich-text': 0.49.0(typescript@5.9.3) - '@lexical/selection': 0.49.0(typescript@5.9.3) - '@lexical/text': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/code-core': 0.48.0(typescript@5.9.3) + '@lexical/internal': 0.48.0(typescript@5.9.3) + '@lexical/link': 0.48.0(typescript@5.9.3) + '@lexical/list': 0.48.0(typescript@5.9.3) + '@lexical/rich-text': 0.48.0(typescript@5.9.3) + '@lexical/selection': 0.48.0(typescript@5.9.3) + '@lexical/text': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/overflow@0.49.0(typescript@5.9.3)': + '@lexical/overflow@0.48.0(typescript@5.9.3)': dependencies: - lexical: 0.49.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/plain-text@0.49.0(typescript@5.9.3)': + '@lexical/plain-text@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/clipboard': 0.49.0(typescript@5.9.3) - '@lexical/dragon': 0.49.0(typescript@5.9.3) - '@lexical/extension': 0.49.0(typescript@5.9.3) - '@lexical/selection': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/clipboard': 0.48.0(typescript@5.9.3) + '@lexical/dragon': 0.48.0(typescript@5.9.3) + '@lexical/extension': 0.48.0(typescript@5.9.3) + '@lexical/selection': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/react@0.49.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@5.9.3)(yjs@13.6.29)': + '@lexical/react@0.48.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@5.9.3)(yjs@13.6.29)': dependencies: '@floating-ui/react': 0.27.20(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@lexical/a11y': 0.49.0(typescript@5.9.3) - '@lexical/devtools-core': 0.49.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@5.9.3) - '@lexical/dragon': 0.49.0(typescript@5.9.3) - '@lexical/extension': 0.49.0(typescript@5.9.3) - '@lexical/hashtag': 0.49.0(typescript@5.9.3) - '@lexical/history': 0.49.0(typescript@5.9.3) - '@lexical/internal': 0.49.0(typescript@5.9.3) - '@lexical/link': 0.49.0(typescript@5.9.3) - '@lexical/list': 0.49.0(typescript@5.9.3) - '@lexical/mark': 0.49.0(typescript@5.9.3) - '@lexical/markdown': 0.49.0(typescript@5.9.3) - '@lexical/overflow': 0.49.0(typescript@5.9.3) - '@lexical/plain-text': 0.49.0(typescript@5.9.3) - '@lexical/rich-text': 0.49.0(typescript@5.9.3) - '@lexical/table': 0.49.0(typescript@5.9.3) - '@lexical/text': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - '@lexical/yjs': 0.49.0(typescript@5.9.3)(yjs@13.6.29) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/a11y': 0.48.0(typescript@5.9.3) + '@lexical/devtools-core': 0.48.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@5.9.3) + '@lexical/dragon': 0.48.0(typescript@5.9.3) + '@lexical/extension': 0.48.0(typescript@5.9.3) + '@lexical/hashtag': 0.48.0(typescript@5.9.3) + '@lexical/history': 0.48.0(typescript@5.9.3) + '@lexical/internal': 0.48.0(typescript@5.9.3) + '@lexical/link': 0.48.0(typescript@5.9.3) + '@lexical/list': 0.48.0(typescript@5.9.3) + '@lexical/mark': 0.48.0(typescript@5.9.3) + '@lexical/markdown': 0.48.0(typescript@5.9.3) + '@lexical/overflow': 0.48.0(typescript@5.9.3) + '@lexical/plain-text': 0.48.0(typescript@5.9.3) + '@lexical/rich-text': 0.48.0(typescript@5.9.3) + '@lexical/table': 0.48.0(typescript@5.9.3) + '@lexical/text': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + '@lexical/yjs': 0.48.0(typescript@5.9.3)(yjs@13.6.29) + lexical: 0.48.0(typescript@5.9.3) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: typescript: 5.9.3 yjs: 13.6.29 - '@lexical/rich-text@0.49.0(typescript@5.9.3)': + '@lexical/rich-text@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/clipboard': 0.49.0(typescript@5.9.3) - '@lexical/dragon': 0.49.0(typescript@5.9.3) - '@lexical/extension': 0.49.0(typescript@5.9.3) - '@lexical/html': 0.49.0(typescript@5.9.3) - '@lexical/selection': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/clipboard': 0.48.0(typescript@5.9.3) + '@lexical/dragon': 0.48.0(typescript@5.9.3) + '@lexical/extension': 0.48.0(typescript@5.9.3) + '@lexical/html': 0.48.0(typescript@5.9.3) + '@lexical/selection': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/selection@0.49.0(typescript@5.9.3)': + '@lexical/selection@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/internal': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/internal': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/table@0.49.0(typescript@5.9.3)': + '@lexical/table@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/clipboard': 0.49.0(typescript@5.9.3) - '@lexical/extension': 0.49.0(typescript@5.9.3) - '@lexical/html': 0.49.0(typescript@5.9.3) - '@lexical/internal': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/clipboard': 0.48.0(typescript@5.9.3) + '@lexical/extension': 0.48.0(typescript@5.9.3) + '@lexical/html': 0.48.0(typescript@5.9.3) + '@lexical/internal': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/text@0.49.0(typescript@5.9.3)': + '@lexical/text@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/internal': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/internal': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/utils@0.49.0(typescript@5.9.3)': + '@lexical/utils@0.48.0(typescript@5.9.3)': dependencies: - '@lexical/internal': 0.49.0(typescript@5.9.3) - '@lexical/selection': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/internal': 0.48.0(typescript@5.9.3) + '@lexical/selection': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 - '@lexical/yjs@0.49.0(typescript@5.9.3)(yjs@13.6.29)': + '@lexical/yjs@0.48.0(typescript@5.9.3)(yjs@13.6.29)': dependencies: - '@lexical/internal': 0.49.0(typescript@5.9.3) - '@lexical/selection': 0.49.0(typescript@5.9.3) - lexical: 0.49.0(typescript@5.9.3) + '@lexical/internal': 0.48.0(typescript@5.9.3) + '@lexical/selection': 0.48.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) yjs: 13.6.29 optionalDependencies: typescript: 5.9.3 @@ -10430,17 +10375,17 @@ snapshots: '@codemirror/merge': 6.12.2 '@codemirror/state': 6.7.1 '@codemirror/view': 6.43.9 - '@lexical/clipboard': 0.49.0(typescript@5.9.3) + '@lexical/clipboard': 0.48.0(typescript@5.9.3) '@lexical/extension': 0.48.0(typescript@5.9.3) '@lexical/history': 0.48.0(typescript@5.9.3) - '@lexical/link': 0.49.0(typescript@5.9.3) - '@lexical/list': 0.49.0(typescript@5.9.3) - '@lexical/markdown': 0.49.0(typescript@5.9.3) - '@lexical/plain-text': 0.49.0(typescript@5.9.3) - '@lexical/react': 0.49.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@5.9.3)(yjs@13.6.29) - '@lexical/rich-text': 0.49.0(typescript@5.9.3) - '@lexical/selection': 0.49.0(typescript@5.9.3) - '@lexical/utils': 0.49.0(typescript@5.9.3) + '@lexical/link': 0.48.0(typescript@5.9.3) + '@lexical/list': 0.48.0(typescript@5.9.3) + '@lexical/markdown': 0.48.0(typescript@5.9.3) + '@lexical/plain-text': 0.48.0(typescript@5.9.3) + '@lexical/react': 0.48.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@5.9.3)(yjs@13.6.29) + '@lexical/rich-text': 0.48.0(typescript@5.9.3) + '@lexical/selection': 0.48.0(typescript@5.9.3) + '@lexical/utils': 0.48.0(typescript@5.9.3) '@mdxeditor/gurx': 1.2.4(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@radix-ui/colors': 3.0.0 '@radix-ui/react-dialog': 1.1.23(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -10456,7 +10401,7 @@ snapshots: codemirror: 6.0.2 downshift: 7.6.2(react@19.2.8) js-yaml: 4.3.1 - lexical: 0.49.0(typescript@5.9.3) + lexical: 0.48.0(typescript@5.9.3) mdast-util-directive: 3.1.0 mdast-util-from-markdown: 2.0.3 mdast-util-frontmatter: 2.0.1 @@ -14230,9 +14175,9 @@ snapshots: layout-base@2.0.1: {} - lexical@0.49.0(typescript@5.9.3): + lexical@0.48.0(typescript@5.9.3): dependencies: - '@lexical/internal': 0.49.0(typescript@5.9.3) + '@lexical/internal': 0.48.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 diff --git a/ui/package.json b/ui/package.json index f6f6c206e49..38c493bfaf1 100644 --- a/ui/package.json +++ b/ui/package.json @@ -33,7 +33,7 @@ "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", - "@lexical/link": "0.49.0", + "@lexical/link": "0.48.0", "@mdxeditor/editor": "^4.2.1", "@paperclipai/adapter-claude-local": "workspace:*", "@paperclipai/adapter-codex-local": "workspace:*", @@ -57,7 +57,7 @@ "clsx": "^2.1.1", "cmdk": "^1.1.1", "i18next": "^26.3.6", - "lexical": "0.49.0", + "lexical": "0.48.0", "lucide-react": "^0.577.0", "mermaid": "^11.16.1", "motion": "^12.42.2", diff --git a/ui/src/lib/lexical-single-copy.test.ts b/ui/src/lib/lexical-single-copy.test.ts new file mode 100644 index 00000000000..5ff3eddc4db --- /dev/null +++ b/ui/src/lib/lexical-single-copy.test.ts @@ -0,0 +1,66 @@ +import { readFileSync } from "node:fs"; +import { createRequire } from "node:module"; +import { dirname, join, parse } from "node:path"; +import { describe, expect, it } from "vitest"; + +/** + * Lexical must resolve to exactly one copy across the app and the rich editor. + * + * The editor registers the app's own nodes (mention-aware links, paste + * handling) into MDXEditor's Lexical instance. If the app and MDXEditor load + * different copies — or different versions of the same package — Lexical's + * `LexicalBuilder` invariant throws during render, the editor falls back to + * its raw-source textarea, and every markdown field in the product silently + * degrades. + * + * That is not hypothetical: root `pnpm.overrides` once force-pinned the + * Lexical family past the range `@mdxeditor/editor` supports, while leaving + * `@lexical/extension` (absent from the override list) on the older line. The + * mismatch shipped and broke the editor everywhere. These assertions fail + * fast on the resolution graph instead of waiting for a render crash. + */ +describe("lexical single copy", () => { + const requireFromUi = createRequire(import.meta.url); + const mdxEditorEntry = requireFromUi.resolve("@mdxeditor/editor"); + const requireFromMdxEditor = createRequire(mdxEditorEntry); + + /** These packages block "./package.json" in exports, so read it off disk. */ + function manifestOf(specifier: string, from: NodeJS.Require): { version: string; dependencies?: Record } { + let dir = dirname(from.resolve(specifier)); + const { root } = parse(dir); + while (true) { + try { + return JSON.parse(readFileSync(join(dir, "package.json"), "utf8")); + } catch { + if (dir === root) throw new Error(`no package.json above ${specifier}`); + dir = dirname(dir); + } + } + } + + function versionOf(specifier: string, from: NodeJS.Require): string { + return manifestOf(specifier, from).version; + } + + it("resolves the same lexical copy for the app and the editor", () => { + expect(requireFromMdxEditor.resolve("lexical")).toBe(requireFromUi.resolve("lexical")); + }); + + it("keeps the app's lexical packages on the editor's version line", () => { + const core = versionOf("lexical", requireFromUi); + // @lexical/link carries the mention-aware LinkNode the app subclasses, so + // a version split here breaks node identity even with one core copy. + expect(versionOf("@lexical/link", requireFromUi)).toBe(core); + expect(versionOf("lexical", requireFromMdxEditor)).toBe(core); + // @lexical/extension owns the LexicalBuilder invariant that throws on a + // mixed graph, and it is reached transitively rather than declared. + expect(versionOf("@lexical/extension", requireFromMdxEditor)).toBe(core); + }); + + it("satisfies the editor's declared lexical range", () => { + const declared = manifestOf("@mdxeditor/editor", requireFromUi).dependencies?.lexical; + const [major, minor] = versionOf("lexical", requireFromUi).split("."); + // A caret range on a 0.x version pins the minor: ^0.48.0 means <0.49.0. + expect(declared).toBe(`^${major}.${minor}.0`); + }); +});