Skip to content

Smaller consistency items left from the audit #474

Description

@productdevbook

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions