Split out of #439. Everything real but too small for its own issue. Each is independent; none is urgent.
API shape
Cell utilities are split between the root and hucre/xlsx. hucre/xlsx exports parseCellRef, colToLetter, cellRef, rangeRef. The root additionally exports letterToCol, parseRange, isInRange, r1c1ToA1, a1ToR1C1. Someone on hucre/xlsx who needs letterToCol has to pull a second entry point for a pure string helper. The JSON surface had exactly this disagreement and it was fixed before v1 (src/index.ts:84-89 says so); this one was missed.
Ranges are A1 strings in half the API and coordinate objects in the other half.
| A1 string |
{ startRow, startCol, endRow, endCol } |
DataValidation.range, ConditionalRule.range, AutoFilter.range, TableDefinition.range, NamedRange.range, PageSetup.printArea, ReadOptions.range |
MergeRange, copyRange(sheet, source, target), SheetImage.anchor, sparkline fields |
cell-utils exports parseRange and rangeRef to convert, and both directions show up constantly in user code. There is no rule to hold in your head about which form a field wants. Accepting string | MergeRange at the boundary and normalising once would cost a few lines.
Testing infrastructure
No coverage threshold. 98.8% today, enforced by nothing — vitest.config.ts sets only exclude.
No bundle-size budget. The README compares against a competitor's ~300 KB without publishing hucre's own. Measured with rolldown: writeXlsx from the root entry is 129 KB minified / 36 KB gzipped, readXlsx 128/34, parseCsv 4.7/2.1. Worth pinning so a regression is visible.
test/parity-statement.test.ts parses src/_types.ts with a regex. Deriving beats transcribing and the intent is right, but new RegExp('export interface ' + iface + ' \\{([\\s\\S]*?)\\n\\}') breaks on any nested brace at column 0. Three other tests now use the same helper, so it is worth making sturdier — a .d.ts walk or a small TS-API script.
Correctness, minor
parseW3CDTF is new Date(value) (doc-props-reader.ts:22). hucre's own writer always emits a Z, and W3CDTF requires a zone designator on a date-time, so compliant files are fine. A non-compliant 2024-01-15T10:30:00 from another tool is parsed as local time — the #415 shape, in a third place. One Z-append guard, the way ods/reader.ts:455 already does it.
Numbers are serialised with String(value) in both the XLSX and CSV writers, so 1e21 becomes 1e+21 and 0.1 + 0.2 becomes 0.30000000000000004 (17 significant digits). Excel writes 1E+21 and caps at 15. Both are parseable; worth deciding rather than inheriting whatever V8 does.
toMarkdown escapes | but not * or _. A cell reading *not emphasis* renders as emphasis. Defensible for output the docs call terminal-only; worth a line of doc either way.
formatValue groups in threes. Since #456 the separator comes from Intl for any locale, but the grouping pattern is still fixed — so hi-IN gets 1,234,567.50 where the locale wants 12,34,567.50. Noted in the code; fixing it means reading the grouping widths from Intl too.
injectWorksheetDrawing (roundtrip.ts:1258) splices <drawing r:id="…"/> into generated XML by substring search over thirteen candidate insertion points. Safe today — the input is hucre's own output and cell text is escaped — but the worksheet writer should take a drawingRId parameter instead.
fullCalcOnLoad="1" is written unconditionally (workbook-writer.ts:147), so every workbook hucre produces forces a full recalculation and the cached formula results it just wrote are discarded. Correct given there is no formula engine; worth a line in the docs, since it makes formulaResult effectively write-only.
Diagnostics
onWarning (#462) is wired at two sites — the two measured as silent. Others could follow the same shape: an out-of-range dxfId on a conditional rule, a paper size code that is not usable, a hyperlink r:id that resolves to nothing. Each wants a test with it rather than a speculative call.
Repo
No issue or PR templates, no CODEOWNERS. Decisions about how contributions should be shaped, so left for the maintainer.
Split out of #439. Everything real but too small for its own issue. Each is independent; none is urgent.
API shape
Cell utilities are split between the root and
hucre/xlsx.hucre/xlsxexportsparseCellRef,colToLetter,cellRef,rangeRef. The root additionally exportsletterToCol,parseRange,isInRange,r1c1ToA1,a1ToR1C1. Someone onhucre/xlsxwho needsletterToColhas to pull a second entry point for a pure string helper. The JSON surface had exactly this disagreement and it was fixed before v1 (src/index.ts:84-89says so); this one was missed.Ranges are A1 strings in half the API and coordinate objects in the other half.
{ startRow, startCol, endRow, endCol }DataValidation.range,ConditionalRule.range,AutoFilter.range,TableDefinition.range,NamedRange.range,PageSetup.printArea,ReadOptions.rangeMergeRange,copyRange(sheet, source, target),SheetImage.anchor, sparkline fieldscell-utilsexportsparseRangeandrangeRefto convert, and both directions show up constantly in user code. There is no rule to hold in your head about which form a field wants. Acceptingstring | MergeRangeat the boundary and normalising once would cost a few lines.Testing infrastructure
No coverage threshold. 98.8% today, enforced by nothing —
vitest.config.tssets onlyexclude.No bundle-size budget. The README compares against a competitor's ~300 KB without publishing hucre's own. Measured with rolldown:
writeXlsxfrom the root entry is 129 KB minified / 36 KB gzipped,readXlsx128/34,parseCsv4.7/2.1. Worth pinning so a regression is visible.test/parity-statement.test.tsparsessrc/_types.tswith a regex. Deriving beats transcribing and the intent is right, butnew RegExp('export interface ' + iface + ' \\{([\\s\\S]*?)\\n\\}')breaks on any nested brace at column 0. Three other tests now use the same helper, so it is worth making sturdier — a.d.tswalk or a small TS-API script.Correctness, minor
parseW3CDTFisnew Date(value)(doc-props-reader.ts:22). hucre's own writer always emits aZ, and W3CDTF requires a zone designator on a date-time, so compliant files are fine. A non-compliant2024-01-15T10:30:00from another tool is parsed as local time — the #415 shape, in a third place. OneZ-append guard, the wayods/reader.ts:455already does it.Numbers are serialised with
String(value)in both the XLSX and CSV writers, so1e21becomes1e+21and0.1 + 0.2becomes0.30000000000000004(17 significant digits). Excel writes1E+21and caps at 15. Both are parseable; worth deciding rather than inheriting whatever V8 does.toMarkdownescapes|but not*or_. A cell reading*not emphasis*renders as emphasis. Defensible for output the docs call terminal-only; worth a line of doc either way.formatValuegroups in threes. Since #456 the separator comes fromIntlfor any locale, but the grouping pattern is still fixed — sohi-INgets1,234,567.50where the locale wants12,34,567.50. Noted in the code; fixing it means reading the grouping widths fromIntltoo.injectWorksheetDrawing(roundtrip.ts:1258) splices<drawing r:id="…"/>into generated XML by substring search over thirteen candidate insertion points. Safe today — the input is hucre's own output and cell text is escaped — but the worksheet writer should take adrawingRIdparameter instead.fullCalcOnLoad="1"is written unconditionally (workbook-writer.ts:147), so every workbook hucre produces forces a full recalculation and the cached formula results it just wrote are discarded. Correct given there is no formula engine; worth a line in the docs, since it makesformulaResulteffectively write-only.Diagnostics
onWarning(#462) is wired at two sites — the two measured as silent. Others could follow the same shape: an out-of-rangedxfIdon a conditional rule, a paper size code that is not usable, a hyperlinkr:idthat resolves to nothing. Each wants a test with it rather than a speculative call.Repo
No issue or PR templates, no
CODEOWNERS. Decisions about how contributions should be shaped, so left for the maintainer.