Skip to content

feat(ods): carry named ranges, which the grammar unblocked - #557

Merged
productdevbook merged 1 commit into
mainfrom
feat/ods-named-ranges
Aug 13, 2026
Merged

feat(ods): carry named ranges, which the grammar unblocked#557
productdevbook merged 1 commit into
mainfrom
feat/ods-named-ranges

Conversation

@productdevbook

Copy link
Copy Markdown
Owner

SPEC-COVERAGE.md had table:named-expressions as an open item — in the ODF grammar, in a LibreOffice document, and nowhere in src/. PARITY.md listed named ranges among the things ODS models in neither direction.

Why it waited, and what unblocked it

The LibreOffice fixture writes <table:named-expressions/> empty, so the corpus had no example of the populated form. Implementing against the spec alone would have left the round trip checking itself — the closed loop the corpus exists to break.

What changed is #552: the output can now be validated against the OASIS grammar, which is the authority the corpus was standing in for.

That was not a formality

The round trip went green and the document was invalid.

ODF spells a sheet name containing a space as $'My Sheet'.$A$1. hucre wrote $My Sheet.$A$1. The grammar's own regular expression for cell-range-address allows a run with no dot, space or apostrophe, or a quoted string — nothing else:

error: value of attribute "table:cell-range-address" is invalid; must be
a string matching the regular expression "($?([^\. ']+|'([^']|'')+'))?\.…"

This reader accepted the bare form happily, so only jing could say. That is the whole argument for the check, demonstrated on the first feature written after it existed.

What landed

Written into the epilogue of <office:spreadsheet>, after every <table:table> — where the sequence puts it.

Deliberate limits, all recorded in PARITY.md:

  • Workbook-level only. ODF scopes a name to a sheet by putting the block inside that <table:table>, which NamedRange.scope could drive and does not yet.
  • <table:named-expression> — a formula rather than a range — has no field in NamedRange and is skipped rather than half-read.
  • A range that will not parse is dropped rather than written malformed: table:cell-range-address is required, so a half-built one would invalidate the document for a name nobody can resolve.

The document with a quoted sheet name is now part of what validate-odf.mjs checks, so the case only the schema could catch stays caught.

pnpm test green — 10,578 tests, 233 files.

🤖 Generated with Claude Code

`SPEC-COVERAGE.md` had `table:named-expressions` as an open item — in the
ODF grammar, in a LibreOffice document, and nowhere in `src/`. PARITY
listed named ranges among the things ODS models in neither direction.

The reason it waited is worth recording. The LibreOffice fixture writes
`<table:named-expressions/>` *empty*, so the corpus had no example of the
populated form, and implementing against the spec alone would have left
the round trip checking itself — the closed loop the corpus exists to
break. What changed is #552: the output can now be validated against the
OASIS grammar, which is the authority the corpus was standing in for.

That was not a formality. The round trip went green and the document was
invalid: ODF spells a sheet name with a space `$'My Sheet'.$A$1`, and
hucre wrote `$My Sheet.$A$1`. The grammar's own regular expression for
`cell-range-address` allows a run with no dot, space or apostrophe, or a
quoted string, and nothing else. This reader accepted the bare form
happily, so only jing could say.

Written into the epilogue of `<office:spreadsheet>`, after every
`<table:table>`, which is where the sequence puts it.

Workbook-level only. ODF scopes a name to a sheet by putting the block
inside that `<table:table>`, which `NamedRange.scope` could drive and
does not yet; PARITY says so. A `<table:named-expression>` — a formula
rather than a range — has no field in `NamedRange` and is skipped rather
than half-read, and a range that will not parse is dropped rather than
written malformed, because the attribute is required.

The document with a quoted sheet name is now part of what
`validate-odf.mjs` checks, so the case that only the schema could catch
stays caught.

The `readOds` bundle grows 27.6 -> 27.7 KB minified (9.7 KB gzipped
either way, at that rounding), and `everything` with it. Those two
budgets are raised. `--update` also wanted to raise `readXlsx` and
`read+write`, which grew 0.8% from earlier merges and still fit their
existing budgets — re-baselining those here would absorb someone else's
growth into an unrelated PR, so they are left where they were.

pnpm test green — 10,578 tests, 233 files.
@productdevbook
productdevbook merged commit 140dedd into main Aug 13, 2026
6 checks passed
@productdevbook
productdevbook deleted the feat/ods-named-ranges branch August 13, 2026 15:46
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