Back to Plate

Lexical normalization harvest rows need selection query boundaries

docs/solutions/best-practices/2026-05-09-lexical-normalization-harvest-rows-need-selection-query-boundaries.md

53.0.62.8 KB
Original Source

Lexical normalization harvest rows need selection query boundaries

Problem

Lexical's LexicalNormalization.test.tsx looks like tree-normalization coverage by filename, but the file tests $normalizeSelection: element-backed selection endpoints are converted to concrete text endpoints when possible.

Symptoms

  • The source rows are named under LexicalNormalization, but every test builds a RangeSelection and calls $normalizeSelection.
  • Some rows are portable text-edge behavior; others depend on Lexical decorator and element-point semantics.
  • Routing the whole file to Slate normalization would invite a broad tree-repair test where Slate only needs a query/location contract.

What Didn't Work

  • Treating the file as generic tree normalization would reopen the wrong owner.
  • Copying Lexical element-point assertions would be fake parity because Slate public Point values target text nodes.
  • Treating reversed rows as a separate browser behavior would overclaim; the accepted behavior is package-level location resolution.

Solution

Route the portable part to Slate's public query APIs:

  • element path -> Editor.range(editor, path);
  • element path start/end -> Editor.edges(editor, path);
  • nested element path -> text child points through Editor.range and Editor.edges;
  • backward ranges -> Editor.point(editor, range) and { edge: 'end' }.

The compact proof belongs in .tmp/slate-v2/packages/slate/test/query-contract.ts, not in normalization-contract.ts.

Why This Works

Slate's public selection model stores text points. Lexical's normalization helper is useful only as a reminder to prove that callers can pass element paths and still get stable text endpoints through the public query layer.

Decorator and element-point rows are intentionally not generic Slate behavior. They should only come back under a real decorator, void, or browser-selection owner.

Prevention

  • Do not trust upstream filenames when harvesting tests; read the operation under test.
  • If a source "normalization" row only converts selection endpoints, check the query/location owner first.
  • Keep tree-repair normalization rows in normalization-contract.ts; keep path/range/point resolution rows in query-contract.ts.
  • Reject decorator endpoint semantics unless a Slate owner exposes the same public behavior.