[lexical][lexical-code-core][lexical-list][lexical-table][lexical-yjs] Refactor: make runtime style updates CSP-safe#8372
Conversation
|
Hi @holly-agyei! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This PR refactors Lexical’s runtime DOM style updates to be CSP-safe by replacing cssText / setAttribute('style', ...) usage with CSSOM property-based updates, and introduces shared utilities + tests to support the new approach.
Changes:
- Add
shared/setDOMStylehelpers to parse stored style strings and apply/remove individual properties viaCSSStyleDeclaration.setProperty/removeProperty. - Update multiple packages (core text/code/list/table/yjs/playground/utils/clipboard) to avoid runtime style string writes.
- Add unit tests covering style parsing and DOM application/update behavior.
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | Adds TS path mapping for shared/setDOMStyle. |
| tsconfig.build.json | Adds build TS path mapping for shared/setDOMStyle. |
| packages/shared/src/setDOMStyle.ts | Introduces shared CSP-safe style parsing/application helpers. |
| packages/shared/src/tests/unit/setDOMStyle.test.ts | Adds unit coverage for parsing and applying/removing styles. |
| packages/lexical/src/nodes/tests/unit/LexicalTextNode.test.tsx | Adds tests for CSP-safe style application and updates on TextNode. |
| packages/lexical/src/nodes/LexicalTextNode.ts | Replaces cssText usage with setDOMStyleFromCSS. |
| packages/lexical/src/nodes/LexicalElementNode.ts | Replaces cssText usage with per-property setProperty calls. |
| packages/lexical-yjs/src/SyncCursors.ts | Replaces cssText usage with setDOMStyleObject for cursor UI styling. |
| packages/lexical-utils/src/index.ts | Replaces cssText usage with direct style property assignments. |
| packages/lexical-table/src/LexicalTableNode.ts | Replaces cssText usage with setDOMStyleFromCSS / direct property assignment. |
| packages/lexical-playground/src/plugins/CommentPlugin/index.tsx | Replaces cssText usage with direct style property assignments for highlights. |
| packages/lexical-playground/src/nodes/MentionNode.ts | Replaces cssText usage with a direct backgroundColor assignment. |
| packages/lexical-playground/split/index.html | Replaces cssText usage with Object.assign style property updates. |
| packages/lexical-list/src/LexicalListItemNode.ts | Replaces cssText/removeAttribute('style') path with setDOMStyleFromCSS. |
| packages/lexical-devtools/tsconfig.json | Adds TS path mapping for shared/setDOMStyle. |
| packages/lexical-code-core/src/tests/unit/CodeNode.test.ts | Adds tests for CSP-safe style application/update/export on CodeNode. |
| packages/lexical-code-core/src/CodeNode.ts | Replaces setAttribute('style', ...) with setDOMStyleFromCSS in create/update/export. |
| packages/lexical-clipboard/src/clipboard.ts | Replaces cssText usage with direct style property assignments. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
f906c71 to
b3b1107
Compare
b3b1107 to
d39671e
Compare
|
Deployment failed with the following error: |
|
Deployment failed with the following error: |
3b67b7c to
24d4906
Compare
etrepum
left a comment
There was a problem hiding this comment.
It's definitely getting close but the change in the caching behavior means that a very thorough audit should be done - possibly adding a different (better) cache.
|
@etrepum |
etrepum
left a comment
There was a problem hiding this comment.
Like before I think because this changes the cache fundamentally I think it needs a very close audit. It's also not clear that 100 is a good size for this cache, or even if the cache is needed at all. There isn't any sort of easy to run benchmark that would make it easy to make that kind of decision. It's likely that the caching would be better to do closer to the nodes that the styles are for, if caching is useful, but that would also take careful consideration to avoid causing problems with collab.
Thanks, I took a closer pass on the cache behavior with performance as the primary goal and changed direction. I removed the global parser cache entirely. After auditing it more closely, I didn’t think I could justify that policy in this PR: the Instead I moved the optimization closer to the actual hot paths:
The tradeoff I chose was to avoid a broad cache-policy change and keep the parser semantics simple, while still making the important runtime paths cheaper. That seemed like the safer performance-oriented shape here, especially without a benchmark that would justify a specific cache strategy or size. |
etrepum
left a comment
There was a problem hiding this comment.
Let's optimize this parser implementation to minimize the impact of not having a cache
I think the place that might be impacted the most by not having a cache is code that uses $getSelectionStyleValueForProperty repeatedly for the same selection, but there are other ways to optimize that than a global cache.
| import { | ||
| getStyleObjectFromCSS as getStyleObjectFromCSS_, | ||
| setDOMStyleFromCSS as setDOMStyleFromCSS_, | ||
| setDOMStyleObject as setDOMStyleObject_, | ||
| } from './utils/setDOMStyle'; |
There was a problem hiding this comment.
This technique only needs to be done when deprecating re-exports, don't do it here. Put the API docs with the implementation of the function and simply export these directly.
| /** | ||
| * Parses inline CSS text into an object that is compatible with | ||
| * `CSSStyleDeclaration.setProperty()`. | ||
| * | ||
| * Property names are expected to be kebab-case, such as `font-size`, and | ||
| * values are expected to include explicit units where needed, such as `12px`. | ||
| */ | ||
| export const getStyleObjectFromCSS = getStyleObjectFromCSS_; | ||
| /** | ||
| * Applies inline CSS text to a DOM style declaration using | ||
| * `CSSStyleDeclaration.setProperty()`. | ||
| * | ||
| * Property names are expected to be kebab-case, such as `font-size`, and | ||
| * values are expected to include explicit units where needed, such as `12px`. | ||
| */ | ||
| export const setDOMStyleFromCSS = setDOMStyleFromCSS_; | ||
| /** | ||
| * Applies a style object to a DOM style declaration using | ||
| * `CSSStyleDeclaration.setProperty()`. | ||
| * | ||
| * Property names are expected to be kebab-case, such as `font-size`, and | ||
| * values are expected to include explicit units where needed, such as `12px`. | ||
| */ | ||
| export const setDOMStyleObject = setDOMStyleObject_; |
There was a problem hiding this comment.
All of this should be colocated with the definitions of these functions
…] Refactor: make runtime style updates CSP-safe (#8372)
commit 5d1bc33 Author: Sathvik Veerapaneni <98241593+Sathvik-Chowdary-Veerapaneni@users.noreply.github.com> Date: Thu Apr 23 13:12:21 2026 -0400 [lexical-list] Bug Fix: Merge nested list into parent <li> during HTML export (facebook#8313) Co-authored-by: Bob Ippolito <bob@redivi.com> commit 2c37dc2 Author: Bob Ippolito <bob@redivi.com> Date: Thu Apr 23 07:14:14 2026 -0700 [lexical-clipboard][lexical-rich-text][lexical-plain-text] Bug Fix: Drag-and-drop within the same block (facebook#8373) Co-authored-by: Claude <noreply@anthropic.com> commit ca2aa31 Author: Bob Ippolito <bob@redivi.com> Date: Thu Apr 23 07:12:43 2026 -0700 [lexical][lexical-utils][lexical-list] Bug Fix: Clean up and test $insertNodeToNearestRootAtCaret edge cases (facebook#8384) commit 207648e Author: Bob Ippolito <bob@redivi.com> Date: Thu Apr 23 07:11:52 2026 -0700 [lexical-html][lexical-playground] Feature: Implement a well-defined ordering for DOMRenderExtension overrides and add $decorateDOM (facebook#8368) commit 1ca42f1 Author: Agyei Holy <agyeiholy978@gmail.com> Date: Wed Apr 22 15:39:37 2026 -0500 [lexical][lexical-code-core][lexical-list][lexical-table][lexical-yjs] Refactor: make runtime style updates CSP-safe (facebook#8372) commit ca0ce82 Author: Bob Ippolito <bob@redivi.com> Date: Wed Apr 22 12:57:28 2026 -0700 [lexical-list] Bug Fix: Ensure that ListItemNode always has a ListItem parent (facebook#8382) commit f4c44e1 Author: Sherry <potatowagon@meta.com> Date: Thu Apr 23 00:28:54 2026 +0530 [lexical-markdown] Bug Fix: Code spans take precedence over inline formatting in shortcuts (facebook#8381) commit 4a43cb0 Author: Sergey Gorbachev <grbchv.s@gmail.com> Date: Wed Apr 22 18:31:21 2026 +0300 [lexical-playground] Feature: HTML conversion button (facebook#8379)
commit 5d1bc33 Author: Sathvik Veerapaneni <98241593+Sathvik-Chowdary-Veerapaneni@users.noreply.github.com> Date: Thu Apr 23 13:12:21 2026 -0400 [lexical-list] Bug Fix: Merge nested list into parent <li> during HTML export (facebook#8313) Co-authored-by: Bob Ippolito <bob@redivi.com> commit 2c37dc2 Author: Bob Ippolito <bob@redivi.com> Date: Thu Apr 23 07:14:14 2026 -0700 [lexical-clipboard][lexical-rich-text][lexical-plain-text] Bug Fix: Drag-and-drop within the same block (facebook#8373) Co-authored-by: Claude <noreply@anthropic.com> commit ca2aa31 Author: Bob Ippolito <bob@redivi.com> Date: Thu Apr 23 07:12:43 2026 -0700 [lexical][lexical-utils][lexical-list] Bug Fix: Clean up and test $insertNodeToNearestRootAtCaret edge cases (facebook#8384) commit 207648e Author: Bob Ippolito <bob@redivi.com> Date: Thu Apr 23 07:11:52 2026 -0700 [lexical-html][lexical-playground] Feature: Implement a well-defined ordering for DOMRenderExtension overrides and add $decorateDOM (facebook#8368) commit 1ca42f1 Author: Agyei Holy <agyeiholy978@gmail.com> Date: Wed Apr 22 15:39:37 2026 -0500 [lexical][lexical-code-core][lexical-list][lexical-table][lexical-yjs] Refactor: make runtime style updates CSP-safe (facebook#8372) commit ca0ce82 Author: Bob Ippolito <bob@redivi.com> Date: Wed Apr 22 12:57:28 2026 -0700 [lexical-list] Bug Fix: Ensure that ListItemNode always has a ListItem parent (facebook#8382) commit f4c44e1 Author: Sherry <potatowagon@meta.com> Date: Thu Apr 23 00:28:54 2026 +0530 [lexical-markdown] Bug Fix: Code spans take precedence over inline formatting in shortcuts (facebook#8381) commit 4a43cb0 Author: Sergey Gorbachev <grbchv.s@gmail.com> Date: Wed Apr 22 18:31:21 2026 +0300 [lexical-playground] Feature: HTML conversion button (facebook#8379)
Description
This replaces runtime style string writes in Lexical's DOM update paths with property-based style updates.
cssTextorsetAttribute('style', ...)Closes #8363
Test plan
Before
.style.cssTextandsetAttribute('style', ...)in editor paths.After
env COREPACK_HOME=/tmp/corepack corepack pnpm run test-unitenv COREPACK_HOME=/tmp/corepack corepack pnpm run tscenv COREPACK_HOME=/tmp/corepack corepack pnpm --filter @lexical/devtools run compileenv COREPACK_HOME=/tmp/corepack corepack pnpm run build-typesenv COREPACK_HOME=/tmp/corepack corepack pnpm run lintenv COREPACK_HOME=/tmp/corepack corepack pnpm run prettier