Skip to content

fix(paragraph): guard listRendering destructure against null#2896

Merged
caio-pizzol merged 5 commits intosuperdoc-dev:mainfrom
kendaller:fix/paragraph-null-list-rendering
Apr 22, 2026
Merged

fix(paragraph): guard listRendering destructure against null#2896
caio-pizzol merged 5 commits intosuperdoc-dev:mainfrom
kendaller:fix/paragraph-null-list-rendering

Conversation

@kendaller
Copy link
Copy Markdown
Contributor

Summary

Fixes a TypeError: Cannot destructure property 'suffix' of 'this.node.attrs.listRendering' as it is null thrown from ParagraphNodeView.

Reproduction

When a paragraph node carries listRendering: null (as an explicit null, not undefined) and the post-transaction list-styles re-pass runs, two call sites crash:

  1. #updateListStyles (line 235) destructures { suffix, justification } directly.
  2. #initList (lines 286-287) accesses listRendering.markerText and .suffix.

In my case this surfaced when dispatching a ProseMirror transaction that combined tr.setDocAttribute('bodySectPr', …) with a paragraph delete — the re-pass walks paragraphs and blows up on the first one whose listRendering is null.

Fix

One-line guards at each call site — ?? {} on the destructure, optional chaining on the property access. Existing defaults (suffix ?? 'tab' and the suffix == null branch in #createSeparator) absorb the null case cleanly.

Test plan

  • Added a regression test (does not throw when listRendering is null) that calls .update() with a listRendering: null node and asserts no throw.
  • All 14 ParagraphNodeView tests pass: pnpm vitest run src/editors/v1/extensions/paragraph/ParagraphNodeView.test.js.
  • pnpm run lint — 0 errors (warnings are pre-existing any usage elsewhere).
  • pnpm run format:check — clean.

ParagraphNodeView destructured `{ suffix, justification }` from
`this.node.attrs.listRendering` without a null-check in #updateListStyles,
and accessed `listRendering.markerText`/`.suffix` directly in #initList.

When a paragraph node carried `listRendering: null` — which can happen
after certain editor mutations (e.g. dispatching a transaction that
combines `setDocAttribute('bodySectPr', …)` with a paragraph delete) —
the post-transaction list-styles re-pass threw:

    TypeError: Cannot destructure property 'suffix' of
    'this.node.attrs.listRendering' as it is null

Use `?? {}` and optional chaining so a null value falls back through
the existing defaults (`suffix ?? 'tab'` and the `suffix == null` branch
in #createSeparator).

Adds a regression test.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 655c60a846

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/super-editor/src/editors/v1/extensions/paragraph/ParagraphNodeView.js Outdated
Previously the null-guarded path fell back to `suffix = 'tab'` and still
invoked `#calculateMarkerStyle`/`#calculateTabSeparatorStyle`. Reviewer
(codex-connector) flagged that in mixed-suffix updates — where a queued
RAF callback runs after a node transitions from `suffix: 'space'` to
`listRendering: null` — the separator may still be a Text node. Writing
`this.separator.style.cssText` on a Text node throws.

Change #updateListStyles and #initList to return early when
`listRendering` is null, leaving the existing marker/separator untouched.
Future updates (when the node gets a real listRendering or isList()
returns false) will clean up as before.

Adds a regression test covering the space→null transition.
@kendaller
Copy link
Copy Markdown
Contributor Author

Good catch — addressed in 0307343. Changed the null-guarded path from "force suffix='tab'" to "early return, leave the existing marker/separator untouched" in both #updateListStyles and #initList. Future updates (when listRendering gets a real value or isList() flips to false) will do the cleanup.

Added a second regression test (does not try to style a text-node separator when switching to null listRendering) that asserts the space→null transition doesn't replace or style the pre-existing Text node separator.

@caio-pizzol caio-pizzol self-assigned this Apr 22, 2026
Copy link
Copy Markdown
Contributor

@caio-pizzol caio-pizzol left a comment

Choose a reason for hiding this comment

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

@kendaller thanks for jumping in - the fix is right, reproduced the crash locally. one out-of-diff thing: the branch at lines 86-88 that handles non-list paragraphs doesn't cancel a scheduled re-render, so the list marker can reappear after the paragraph stops being a list. left three inline - happy to push the cancel fix as a follow-up if easier.

Comment thread packages/super-editor/src/editors/v1/extensions/paragraph/ParagraphNodeView.js Outdated
Addresses review feedback on superdoc-dev#2896:

- Update #initList JSDoc `@param` to include `| null`, matching the
  no-op-on-null behavior added in the previous commit.
- Add a test that mounts a ParagraphNodeView with `listRendering: null`
  (the constructor path, not just update()), confirming the null guards
  in #initList and #updateListStyles cover first-render too.
- Add a test for the space→null→tab transition that verifies the
  separator swaps from a text node back to a span when listRendering
  returns with a different suffix.
@kendaller
Copy link
Copy Markdown
Contributor Author

kendaller commented Apr 22, 2026

@caio-pizzol thanks for the review! JSDoc + both tests pushed in 86da5c8 (rebased onto your main-merge). On the out-of-diff #removeList not cancelling the scheduled RAF — agreed that's a real bug but going to leave it for a follow-up PR to keep this one scoped to the null destructure.

Copy link
Copy Markdown
Contributor

@caio-pizzol caio-pizzol left a comment

Choose a reason for hiding this comment

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

hey @kendaller! thanks for addressing last round review.

all three points landed as suggested: jsdoc now matches the nullable contract, and both the mount-null and space → null → tab recovery tests are in.

lgtm.

@caio-pizzol caio-pizzol enabled auto-merge (squash) April 22, 2026 21:39
@caio-pizzol caio-pizzol merged commit 2c38373 into superdoc-dev:main Apr 22, 2026
52 checks passed
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 22, 2026

🎉 This PR is included in @superdoc-dev/react v1.2.0-next.43

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 22, 2026

🎉 This PR is included in vscode-ext v2.3.0-next.45

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 22, 2026

🎉 This PR is included in template-builder v1.6.0-next.8

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 22, 2026

🎉 This PR is included in esign v2.3.0-next.45

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 22, 2026

🎉 This PR is included in superdoc v1.28.0-next.8

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 22, 2026

🎉 This PR is included in superdoc-cli v0.8.0-next.8

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented Apr 22, 2026

🎉 This PR is included in superdoc-sdk v1.6.0-next.44

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants