Skip to content

Fix: repair genuinely-invalid blocks in canonicalize() - #1

Open
saarnilauri wants to merge 1 commit into
humanmade:mainfrom
saarnilauri:fix/canonicalize-repairs-invalid-blocks
Open

Fix: repair genuinely-invalid blocks in canonicalize()#1
saarnilauri wants to merge 1 commit into
humanmade:mainfrom
saarnilauri:fix/canonicalize-repairs-invalid-blocks

Conversation

@saarnilauri

@saarnilauri saarnilauri commented Aug 24, 2026

Copy link
Copy Markdown

Problem

canonicalize() (the fix command) produces its output with
serialize(parse(markup)). This canonicalizes near-misses attribute order,
whitespace, generated classes, but it does not repair a block whose stored
HTML actually fails validation. WordPress's serializer re-emits a block's
originalContent verbatim when isValid === false, so a genuinely-invalid
block round-trips unchanged and fix just reports it invalid.

Fix

Before serializing, rebuild invalid registered blocks from their parsed
attributes via createBlock(), so each block's save() re-runs and emits valid
markup. Valid blocks keep their parsed form (with any repaired descendants), and
unregistered blocks (core/missing, custom blocks) pass through untouched. This
is the same technique the token-repair path already uses in tokens/apply.ts
after it mutates attributes, wrapped in withMutedWordPressConsole for the same
reason.

Scope note

Blocks whose invalidity is covered by a deprecation (e.g. a heading missing
wp-block-heading) are already repaired by parse()'s migration and are
unaffected. This targets the rest, near-miss structures with no matching
deprecation, such as an image without its <figure> wrapper or a button
without wp-block-button__link. Rebuilding regenerates a block's inner markup
from save(), so it's applied only to blocks that were already invalid.

Tests

  • repairs a genuinely-invalid block by rebuilding from parsed attributes
    an image without <figure> becomes valid (wp-block-image, src preserved).
  • leaves unregistered blocks untouched while repairing invalid core blocks
    a custom block round-trips verbatim while an invalid core image is repaired.

Both fail on main and pass with this change. Full suite green (198 tests),
typecheck clean.

Use of AI

The code changes were created with AI agent, the changes reviewed by me.

canonicalize() (the `fix` command) round-trips markup through
serialize(parse()). That canonicalizes near-misses, but the WP
serializer re-emits `originalContent` verbatim for a block whose
stored HTML fails validation (isValid === false), so a genuinely
broken block round-trips still broken and `fix` reports it invalid
rather than repairing it.

Rebuild invalid *registered* blocks from their parsed attributes via
createBlock() before serializing, so save() re-runs and produces valid
markup. Valid blocks keep their parsed form (with repaired
descendants); unregistered blocks (core/missing, custom) pass through
untouched. This mirrors what the token-repair path already does after
mutating attributes.

Note: cases covered by a block deprecation (e.g. a heading missing
wp-block-heading) are already repaired by parse-time migration; this
handles the rest (an image without its <figure> wrapper, a button
without wp-block-button__link, …).

Adds gate tests covering repair of an invalid image block and
pass-through of an unregistered block alongside a repaired core block.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant