@office-kit/xlsx 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +319 -0
- package/THIRD_PARTY_NOTICES.md +56 -0
- package/dist/cell/cell.d.ts +234 -0
- package/dist/cell/index.d.ts +4 -0
- package/dist/cell/rich-text.d.ts +37 -0
- package/dist/cell-D9CaNKnU.mjs +320 -0
- package/dist/cell-D9CaNKnU.mjs.map +1 -0
- package/dist/cell-style-BEDjMX1y.mjs +1579 -0
- package/dist/cell-style-BEDjMX1y.mjs.map +1 -0
- package/dist/cell.mjs +2 -0
- package/dist/chart/chart-xml.d.ts +16 -0
- package/dist/chart/chart.d.ts +735 -0
- package/dist/chart/cx/chartex-xml.d.ts +6 -0
- package/dist/chart/cx/chartex.d.ts +279 -0
- package/dist/chart/index.d.ts +6 -0
- package/dist/chart/user-shapes-xml.d.ts +4 -0
- package/dist/chart/user-shapes.d.ts +61 -0
- package/dist/chart.mjs +232 -0
- package/dist/chart.mjs.map +1 -0
- package/dist/chartsheet/chartsheet-xml.d.ts +17 -0
- package/dist/chartsheet/chartsheet.d.ts +121 -0
- package/dist/chartsheet/index.d.ts +2 -0
- package/dist/chartsheet-C3-tqkPy.mjs +23 -0
- package/dist/chartsheet-C3-tqkPy.mjs.map +1 -0
- package/dist/chartsheet.mjs +2 -0
- package/dist/colors-ovWAwnZI.mjs +67 -0
- package/dist/colors-ovWAwnZI.mjs.map +1 -0
- package/dist/compat/numbers.d.ts +14 -0
- package/dist/coordinate-96Ecci4d.mjs +276 -0
- package/dist/coordinate-96Ecci4d.mjs.map +1 -0
- package/dist/datetime-B2ySVlXt.mjs +71 -0
- package/dist/datetime-B2ySVlXt.mjs.map +1 -0
- package/dist/defined-names-CviWmtQg.mjs +89 -0
- package/dist/defined-names-CviWmtQg.mjs.map +1 -0
- package/dist/differential-D4dg-qtZ.mjs +37 -0
- package/dist/differential-D4dg-qtZ.mjs.map +1 -0
- package/dist/drawing/anchor.d.ts +63 -0
- package/dist/drawing/dml/colors.d.ts +109 -0
- package/dist/drawing/dml/dml-xml.d.ts +35 -0
- package/dist/drawing/dml/effect.d.ts +92 -0
- package/dist/drawing/dml/fill.d.ts +115 -0
- package/dist/drawing/dml/geometry.d.ts +113 -0
- package/dist/drawing/dml/line.d.ts +41 -0
- package/dist/drawing/dml/shape-properties.d.ts +33 -0
- package/dist/drawing/dml/text.d.ts +218 -0
- package/dist/drawing/drawing-xml.d.ts +5 -0
- package/dist/drawing/drawing.d.ts +117 -0
- package/dist/drawing/image.d.ts +40 -0
- package/dist/drawing/index.d.ts +14 -0
- package/dist/drawing-BxzLuryn.mjs +415 -0
- package/dist/drawing-BxzLuryn.mjs.map +1 -0
- package/dist/drawing.mjs +119 -0
- package/dist/drawing.mjs.map +1 -0
- package/dist/escape-DFTE7ZJc.mjs +51 -0
- package/dist/escape-DFTE7ZJc.mjs.map +1 -0
- package/dist/exceptions-D-CFwxgm.mjs +37 -0
- package/dist/exceptions-D-CFwxgm.mjs.map +1 -0
- package/dist/formula/tokenizer.d.ts +61 -0
- package/dist/formula/translate.d.ts +67 -0
- package/dist/inference-B3ES3KEJ.mjs +42 -0
- package/dist/inference-B3ES3KEJ.mjs.map +1 -0
- package/dist/io/browser.d.ts +41 -0
- package/dist/io/index.d.ts +7 -0
- package/dist/io/load.d.ts +46 -0
- package/dist/io/node-fs.d.ts +62 -0
- package/dist/io/node-save.d.ts +3 -0
- package/dist/io/node.d.ts +17 -0
- package/dist/io/save.d.ts +14 -0
- package/dist/io/sink.d.ts +54 -0
- package/dist/io/source.d.ts +14 -0
- package/dist/io.mjs +212 -0
- package/dist/io.mjs.map +1 -0
- package/dist/load-D5cbhoGx.mjs +1069 -0
- package/dist/load-D5cbhoGx.mjs.map +1 -0
- package/dist/manifest-Dps1-OpP.mjs +801 -0
- package/dist/manifest-Dps1-OpP.mjs.map +1 -0
- package/dist/node.d.ts +3 -0
- package/dist/node.mjs +308 -0
- package/dist/node.mjs.map +1 -0
- package/dist/packaging/core.d.ts +45 -0
- package/dist/packaging/custom.d.ts +62 -0
- package/dist/packaging/extended.d.ts +45 -0
- package/dist/packaging/index.d.ts +10 -0
- package/dist/packaging/manifest.d.ts +24 -0
- package/dist/packaging/relationships.d.ts +30 -0
- package/dist/packaging.mjs +2 -0
- package/dist/parser-DuLejQy1.mjs +156 -0
- package/dist/parser-DuLejQy1.mjs.map +1 -0
- package/dist/reader-D1fNW9k1.mjs +534 -0
- package/dist/reader-D1fNW9k1.mjs.map +1 -0
- package/dist/save-RohQtgEZ.mjs +745 -0
- package/dist/save-RohQtgEZ.mjs.map +1 -0
- package/dist/schema/core.d.ts +133 -0
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/serialize.d.ts +6 -0
- package/dist/schema.mjs +2 -0
- package/dist/serialize-55EnT30e.mjs +254 -0
- package/dist/serialize-55EnT30e.mjs.map +1 -0
- package/dist/serializer-BwbgHYJV.mjs +116 -0
- package/dist/serializer-BwbgHYJV.mjs.map +1 -0
- package/dist/streaming/index.d.ts +2 -0
- package/dist/streaming/read-only.d.ts +38 -0
- package/dist/streaming/write-only.d.ts +47 -0
- package/dist/streaming.mjs +612 -0
- package/dist/streaming.mjs.map +1 -0
- package/dist/styles/alignment.d.ts +33 -0
- package/dist/styles/alignment.schema.d.ts +3 -0
- package/dist/styles/borders.d.ts +40 -0
- package/dist/styles/borders.schema.d.ts +4 -0
- package/dist/styles/cell-style.d.ts +270 -0
- package/dist/styles/colors.d.ts +128 -0
- package/dist/styles/colors.schema.d.ts +3 -0
- package/dist/styles/differential.d.ts +41 -0
- package/dist/styles/fills.d.ts +54 -0
- package/dist/styles/fills.schema.d.ts +6 -0
- package/dist/styles/fonts.d.ts +44 -0
- package/dist/styles/fonts.schema.d.ts +3 -0
- package/dist/styles/index.d.ts +21 -0
- package/dist/styles/named-styles.d.ts +52 -0
- package/dist/styles/numbers.d.ts +39 -0
- package/dist/styles/numbers.schema.d.ts +3 -0
- package/dist/styles/protection.d.ts +9 -0
- package/dist/styles/protection.schema.d.ts +3 -0
- package/dist/styles/stylesheet-reader.d.ts +7 -0
- package/dist/styles/stylesheet-writer.d.ts +3 -0
- package/dist/styles/stylesheet.d.ts +95 -0
- package/dist/styles.mjs +4 -0
- package/dist/stylesheet-writer-C2eRmn22.mjs +8624 -0
- package/dist/stylesheet-writer-C2eRmn22.mjs.map +1 -0
- package/dist/table-DkX6UniA.mjs +113 -0
- package/dist/table-DkX6UniA.mjs.map +1 -0
- package/dist/tree-Bbs1C8Rc.mjs +192 -0
- package/dist/tree-Bbs1C8Rc.mjs.map +1 -0
- package/dist/units-rOMQqXh2.mjs +41 -0
- package/dist/units-rOMQqXh2.mjs.map +1 -0
- package/dist/user-shapes-DfmCGKB0.mjs +252 -0
- package/dist/user-shapes-DfmCGKB0.mjs.map +1 -0
- package/dist/utf8-D91g1XTG.mjs +143 -0
- package/dist/utf8-D91g1XTG.mjs.map +1 -0
- package/dist/utils/coordinate.d.ts +103 -0
- package/dist/utils/css.d.ts +18 -0
- package/dist/utils/datetime.d.ts +38 -0
- package/dist/utils/escape.d.ts +34 -0
- package/dist/utils/exceptions.d.ts +34 -0
- package/dist/utils/index.d.ts +11 -0
- package/dist/utils/inference.d.ts +24 -0
- package/dist/utils/stable-stringify.d.ts +7 -0
- package/dist/utils/units.d.ts +14 -0
- package/dist/utils/utf8.d.ts +1 -0
- package/dist/utils.mjs +39 -0
- package/dist/utils.mjs.map +1 -0
- package/dist/workbook/calc-properties.d.ts +47 -0
- package/dist/workbook/defined-names.d.ts +121 -0
- package/dist/workbook/file-recovery.d.ts +11 -0
- package/dist/workbook/file-sharing.d.ts +14 -0
- package/dist/workbook/file-version.d.ts +13 -0
- package/dist/workbook/function-groups.d.ts +10 -0
- package/dist/workbook/index.d.ts +24 -0
- package/dist/workbook/protection.d.ts +35 -0
- package/dist/workbook/shared-strings.d.ts +57 -0
- package/dist/workbook/smart-tags.d.ts +13 -0
- package/dist/workbook/views.d.ts +89 -0
- package/dist/workbook/workbook-properties.d.ts +57 -0
- package/dist/workbook/workbook.d.ts +643 -0
- package/dist/workbook-HGYNRBlV.mjs +636 -0
- package/dist/workbook-HGYNRBlV.mjs.map +1 -0
- package/dist/workbook.mjs +58 -0
- package/dist/workbook.mjs.map +1 -0
- package/dist/worksheet/auto-filter.d.ts +34 -0
- package/dist/worksheet/cell-range.d.ts +121 -0
- package/dist/worksheet/comments-xml.d.ts +24 -0
- package/dist/worksheet/comments.d.ts +13 -0
- package/dist/worksheet/conditional-formatting.d.ts +150 -0
- package/dist/worksheet/custom-sheet-views.d.ts +43 -0
- package/dist/worksheet/data-consolidate.d.ts +29 -0
- package/dist/worksheet/data-validations.d.ts +72 -0
- package/dist/worksheet/dimensions.d.ts +40 -0
- package/dist/worksheet/errors.d.ts +40 -0
- package/dist/worksheet/hyperlinks.d.ts +42 -0
- package/dist/worksheet/index.d.ts +46 -0
- package/dist/worksheet/ole-objects.d.ts +37 -0
- package/dist/worksheet/page-setup.d.ts +173 -0
- package/dist/worksheet/phonetic.d.ts +11 -0
- package/dist/worksheet/properties.d.ts +34 -0
- package/dist/worksheet/protected-ranges.d.ts +19 -0
- package/dist/worksheet/protection.d.ts +44 -0
- package/dist/worksheet/reader.d.ts +38 -0
- package/dist/worksheet/scenarios.d.ts +36 -0
- package/dist/worksheet/smart-tags.d.ts +23 -0
- package/dist/worksheet/sort-state.d.ts +28 -0
- package/dist/worksheet/table-xml.d.ts +5 -0
- package/dist/worksheet/table.d.ts +80 -0
- package/dist/worksheet/views.d.ts +47 -0
- package/dist/worksheet/web-publish.d.ts +21 -0
- package/dist/worksheet/worksheet.d.ts +935 -0
- package/dist/worksheet/writer.d.ts +72 -0
- package/dist/worksheet-CmCNoIgD.mjs +1726 -0
- package/dist/worksheet-CmCNoIgD.mjs.map +1 -0
- package/dist/worksheet.mjs +247 -0
- package/dist/worksheet.mjs.map +1 -0
- package/dist/writer-DspzfkNA.mjs +221 -0
- package/dist/writer-DspzfkNA.mjs.map +1 -0
- package/dist/xml/index.d.ts +10 -0
- package/dist/xml/iterparse.d.ts +22 -0
- package/dist/xml/namespaces.d.ts +91 -0
- package/dist/xml/parser.d.ts +7 -0
- package/dist/xml/serializer.d.ts +14 -0
- package/dist/xml/stream-writer.d.ts +39 -0
- package/dist/xml/tree.d.ts +37 -0
- package/dist/xml.mjs +140 -0
- package/dist/xml.mjs.map +1 -0
- package/dist/zip/decompression-guard.d.ts +70 -0
- package/dist/zip/index.d.ts +6 -0
- package/dist/zip/random-access-reader.d.ts +16 -0
- package/dist/zip/reader.d.ts +45 -0
- package/dist/zip/writer.d.ts +65 -0
- package/dist/zip/zip64-patch.d.ts +12 -0
- package/dist/zip.mjs +3 -0
- package/package.json +147 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yuichiro Yamashita
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="./assets/logo.png" alt="@office-kit/xlsx" width="180" height="180" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# @office-kit/xlsx
|
|
6
|
+
|
|
7
|
+
A TypeScript library for reading and writing Excel `.xlsx` workbooks
|
|
8
|
+
from Node 22+ and modern browsers, with no runtime dependencies on
|
|
9
|
+
Python or Excel. Inspired by [openpyxl](https://openpyxl.readthedocs.io/).
|
|
10
|
+
|
|
11
|
+
> **Status: pre-1.0 alpha.** The core read / write / streaming pipeline is
|
|
12
|
+
> in place and round-trips real-world fixtures (including pivot tables and
|
|
13
|
+
> macro-enabled `.xlsm`), but APIs may shift before `1.0`.
|
|
14
|
+
|
|
15
|
+
## Why @office-kit/xlsx?
|
|
16
|
+
|
|
17
|
+
The JavaScript xlsx ecosystem in 2026 is split between **commercial upsell
|
|
18
|
+
tiers** and **stalled open-source projects**. SheetJS Community Edition
|
|
19
|
+
deliberately omits styling, charts, images, pivots, conditional formatting,
|
|
20
|
+
and data validation on write — those live in [SheetJS Pro][sjs-pro], a paid
|
|
21
|
+
tier. ExcelJS is MIT but
|
|
22
|
+
[has not had a meaningful release since October 2023][exceljs-discussion]
|
|
23
|
+
and its maintainers explicitly call it inactive; the dependency footprint
|
|
24
|
+
unpacks to 21.8 MB. excel4node was [archived in 2022][e4n-archive].
|
|
25
|
+
xlsx-js-style is frozen at a 2022 SheetJS fork.
|
|
26
|
+
|
|
27
|
+
@office-kit/xlsx is the third option: an actively-developed, pure-MIT,
|
|
28
|
+
TypeScript-first library with no Pro tier and no missing features behind a
|
|
29
|
+
paywall.
|
|
30
|
+
|
|
31
|
+
[sjs-pro]: https://sheetjs.com/pro/
|
|
32
|
+
[exceljs-discussion]: https://github.com/exceljs/exceljs/discussions/2987
|
|
33
|
+
[e4n-archive]: https://github.com/natergj/excel4node
|
|
34
|
+
|
|
35
|
+
| Concern | Other libraries | @office-kit/xlsx |
|
|
36
|
+
|------------------------|----------------------------------------------------------------------------------|-----------------------------------------------------------------------|
|
|
37
|
+
| TypeScript types | hand-written `.d.ts` retrofitted (SheetJS) or community typings (xlsx-populate, excel4node) | first-party, written in TS under `exactOptionalPropertyTypes` + `noUncheckedIndexedAccess` |
|
|
38
|
+
| Bundle size | ExcelJS unpacks to 21.8 MB; xlsx ~7.5 MB | full lib ≤120 KB min+brotli (currently ~85 KB); streaming entry ~49 KB |
|
|
39
|
+
| Streaming | SheetJS docs explicitly note the zip central-directory layout prevents true streaming; ExcelJS supports both directions but the lib is heavy | both read iter and write append, with fixed-memory budget for tens of millions of rows |
|
|
40
|
+
| Charts (write) | none in ExcelJS, xlsx-js-style, SheetJS CE; gated behind SheetJS Pro | 16 legacy `c:` + 8 modern `cx:` chart kinds (Sunburst, Treemap, Waterfall, Histogram, Pareto, Funnel, BoxWhisker, RegionMap) |
|
|
41
|
+
| Pivots / VBA / OLE | ExcelJS drops pivot tables on read ([#261][exceljs-pivot]); others vary | byte-identical passthrough so Excel 365 still renders parts we don't model |
|
|
42
|
+
| Maintenance | ExcelJS stalled since 2023; excel4node archived 2022; xlsx-js-style frozen 2022; SheetJS npm artifact frozen 2022 (still distributed via private CDN) | active |
|
|
43
|
+
| License | SheetJS CE strips features for Pro upsell; SheetJS Pro pricing not published | MIT, single tier, no upsell |
|
|
44
|
+
| Conformance | none of the major libraries validate against ECMA-376 in CI | 3-tier validator (OPC structure + ECMA-376 XSD + semantic invariants) gates every CI build, including a fast-check property-based oracle |
|
|
45
|
+
| Modules | monolithic root barrel | subpath imports — `@office-kit/xlsx/io`, `/streaming`, `/cell`, `/styles`, etc., each independently tree-shakable |
|
|
46
|
+
|
|
47
|
+
[exceljs-pivot]: https://github.com/exceljs/exceljs/issues/261
|
|
48
|
+
|
|
49
|
+
### Where each existing library still wins
|
|
50
|
+
|
|
51
|
+
- **Read simple xlsx in the browser** → [`read-excel-file`][rexf] is excellent.
|
|
52
|
+
- **Write simple xlsx with images** → [`write-excel-file`][wexf] is excellent.
|
|
53
|
+
- **Template-based fidelity preservation with password protection** → `xlsx-populate`.
|
|
54
|
+
- **Non-xlsx formats** (XLS / XLSB / ODS / CSV / HTML) → SheetJS Community.
|
|
55
|
+
- **Commercial budget + long shopping list** → SheetJS Pro.
|
|
56
|
+
|
|
57
|
+
[rexf]: https://www.npmjs.com/package/read-excel-file
|
|
58
|
+
[wexf]: https://www.npmjs.com/package/write-excel-file
|
|
59
|
+
|
|
60
|
+
### @office-kit/xlsx's home turf
|
|
61
|
+
|
|
62
|
+
- You write modern TypeScript and want types that actually behave under
|
|
63
|
+
strict mode (cell values are a discriminated union, not `any`).
|
|
64
|
+
- You produce **large** xlsx files (tens of millions of cells) and care
|
|
65
|
+
about heap budget.
|
|
66
|
+
- You need **charts**, conditional formatting, data validation, defined
|
|
67
|
+
names, tables, ZIP64 (entry-count overflow; see "What's supported" for the
|
|
68
|
+
4 GiB-per-entry caveat) — and want them in MIT.
|
|
69
|
+
- You round-trip xlsx files that contain pivot tables, VBA macros,
|
|
70
|
+
threaded comments, Power Query metadata, or customXml — and need them
|
|
71
|
+
preserved byte-for-byte.
|
|
72
|
+
- You want **proof** that the bytes you emit are valid OOXML, not "Excel
|
|
73
|
+
happens to open them today."
|
|
74
|
+
|
|
75
|
+
### When NOT to use @office-kit/xlsx
|
|
76
|
+
|
|
77
|
+
Honest list:
|
|
78
|
+
|
|
79
|
+
- **Pre-1.0**: API may shift before 1.0. Pin the version for long-running
|
|
80
|
+
projects.
|
|
81
|
+
- **`.xlsx` only**: no `.xls` (BIFF), `.xlsb`, `.ods`, or `.csv`. Use
|
|
82
|
+
SheetJS for those.
|
|
83
|
+
- **Node 22+ required**: relies on built-in `Web Streams`, `Blob`, and
|
|
84
|
+
`fetch`. Node 18 / 20 (EOL) are not supported.
|
|
85
|
+
- **Browser stress-test history is shorter** than ExcelJS's. If you ship
|
|
86
|
+
to millions of browser users today, run your own benchmark first.
|
|
87
|
+
- **Visual QA in Excel 365** is on the human-verification list; the schema
|
|
88
|
+
gate proves spec compliance, not that every chart renders pixel-perfect.
|
|
89
|
+
|
|
90
|
+
### Motivation
|
|
91
|
+
|
|
92
|
+
The reasons @office-kit/xlsx exists, written down so future contributors don't
|
|
93
|
+
relitigate them:
|
|
94
|
+
|
|
95
|
+
1. **The reference implementation is in Python.** [openpyxl][openpyxl] has
|
|
96
|
+
spent 15 years collecting Excel / LibreOffice corner cases. @office-kit/xlsx
|
|
97
|
+
consumes its fixture corpus directly (`reference/openpyxl/` is a git
|
|
98
|
+
submodule), so edge cases the Python world solved years ago don't get
|
|
99
|
+
re-discovered painfully in JS.
|
|
100
|
+
2. **The 2010-era JS stack is heavy.** Most existing libraries pull in
|
|
101
|
+
`jszip`, `lodash`, `archiver`, `xmlbuilder`, `sax`. In 2026 we have
|
|
102
|
+
`fflate`, `fast-xml-parser`, and `saxes` — the toolchain is an order
|
|
103
|
+
of magnitude lighter. @office-kit/xlsx ships with three runtime dependencies.
|
|
104
|
+
3. **TypeScript-first changes the API surface.** A library authored in TS
|
|
105
|
+
under strict-mode flags from day one exposes different ergonomics than
|
|
106
|
+
`.d.ts` typings retrofitted onto an old JS codebase.
|
|
107
|
+
4. **"Schema-valid" should be a CI gate, not a vibe.** ECMA-376 is
|
|
108
|
+
downloadable; xmllint is free; vendoring the schemas costs <1 MB. There
|
|
109
|
+
is no good reason a 2026 library shouldn't validate every byte it
|
|
110
|
+
emits against the spec.
|
|
111
|
+
5. **No Pro tier.** Charts, pivots passthrough, conditional formatting,
|
|
112
|
+
ZIP64 write — all MIT. Nothing held back.
|
|
113
|
+
|
|
114
|
+
[openpyxl]: https://openpyxl.readthedocs.io/
|
|
115
|
+
|
|
116
|
+
## Install
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
pnpm add @office-kit/xlsx # or npm / yarn / bun
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Requires Node `>=22` for the built-in `Web Streams`, `Blob`, and `fetch`
|
|
123
|
+
globals.
|
|
124
|
+
|
|
125
|
+
## Subpath entries
|
|
126
|
+
|
|
127
|
+
The package has no root barrel — every export lives behind a section
|
|
128
|
+
subpath, so your editor's autocomplete only surfaces what's relevant to
|
|
129
|
+
the area you're working in. Each export has exactly one home (no
|
|
130
|
+
convenience re-exports).
|
|
131
|
+
|
|
132
|
+
| Import | Use case |
|
|
133
|
+
|------------------------|---------------------------------------------------|
|
|
134
|
+
| `@office-kit/xlsx/io` | `loadWorkbook` / `saveWorkbook` / `workbookToBytes` plus byte-level Source/Sink + browser helpers (Blob/Response/Stream) |
|
|
135
|
+
| `@office-kit/xlsx/node` | Node fs glue (`fromFile` / `toFile` / `fromBuffer` / `toBuffer` / `fromReadable` / `toWritable`) |
|
|
136
|
+
| `@office-kit/xlsx/streaming` | Read-only iter (`loadWorkbookStream`) + write-only append (`createWriteOnlyWorkbook`) |
|
|
137
|
+
| `@office-kit/xlsx/workbook` | `createWorkbook`, `addWorksheet`, defined names |
|
|
138
|
+
| `@office-kit/xlsx/worksheet` | `setCell`, `getCell`, `mergeCells`, tables, … |
|
|
139
|
+
| `@office-kit/xlsx/cell` | Cell value-model + inline rich text |
|
|
140
|
+
| `@office-kit/xlsx/styles` | Fonts, fills, borders, alignment, number formats |
|
|
141
|
+
| `@office-kit/xlsx/chart` | `c:` and `cx:` chart kinds |
|
|
142
|
+
| `@office-kit/xlsx/chartsheet` | Standalone chartsheets |
|
|
143
|
+
| `@office-kit/xlsx/drawing` | Anchors, images, chart placement |
|
|
144
|
+
|
|
145
|
+
Other subpaths: `@office-kit/xlsx/packaging`, `@office-kit/xlsx/utils`, `@office-kit/xlsx/xml`,
|
|
146
|
+
`@office-kit/xlsx/zip`, `@office-kit/xlsx/schema`. All exports are tree-shakable
|
|
147
|
+
(`"sideEffects": false`).
|
|
148
|
+
|
|
149
|
+
Bundle budgets (min + brotli):
|
|
150
|
+
|
|
151
|
+
- `@office-kit/xlsx/streaming` ≤ 80 KB (currently ~49 KB)
|
|
152
|
+
- `@office-kit/xlsx/io` ≤ 120 KB (currently ~85 KB)
|
|
153
|
+
|
|
154
|
+
## Quick examples
|
|
155
|
+
|
|
156
|
+
For a one-page lookup of task → exact functions to import and call, see
|
|
157
|
+
the [Cheatsheet](https://baseballyama.github.io/@office-kit/xlsx/docs/cheatsheet).
|
|
158
|
+
For prose-style worked examples (styling, charts, validation, streaming),
|
|
159
|
+
see the [Recipes](https://baseballyama.github.io/@office-kit/xlsx/docs/recipes).
|
|
160
|
+
|
|
161
|
+
### Read + edit + write
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { loadWorkbook, workbookToBytes } from '@office-kit/xlsx/io';
|
|
165
|
+
import { setCell } from '@office-kit/xlsx/worksheet';
|
|
166
|
+
import { fromBuffer } from '@office-kit/xlsx/node';
|
|
167
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
168
|
+
|
|
169
|
+
const wb = await loadWorkbook(fromBuffer(await readFile('input.xlsx')));
|
|
170
|
+
const sheet = wb.sheets[0];
|
|
171
|
+
if (sheet?.kind === 'worksheet') {
|
|
172
|
+
setCell(sheet.sheet, /* row */ 1, /* col */ 1, 'Hello from @office-kit/xlsx');
|
|
173
|
+
}
|
|
174
|
+
await writeFile('output.xlsx', await workbookToBytes(wb));
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Read directly from disk (Node)
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { loadWorkbook, saveWorkbook } from '@office-kit/xlsx/io';
|
|
181
|
+
import { fromFile, toFile } from '@office-kit/xlsx/node';
|
|
182
|
+
|
|
183
|
+
const wb = await loadWorkbook(fromFile('input.xlsx'));
|
|
184
|
+
// …mutate wb…
|
|
185
|
+
await saveWorkbook(wb, toFile('output.xlsx'));
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Read directly from a `fetch` response (browser)
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
import { fromResponse, loadWorkbook } from '@office-kit/xlsx/io';
|
|
192
|
+
|
|
193
|
+
const response = await fetch('/sheet.xlsx');
|
|
194
|
+
const wb = await loadWorkbook(fromResponse(response));
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Streaming write — millions of rows in a fixed memory budget
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
import { createWriteOnlyWorkbook } from '@office-kit/xlsx/streaming';
|
|
201
|
+
import { toFile } from '@office-kit/xlsx/node';
|
|
202
|
+
|
|
203
|
+
const sink = toFile('big.xlsx');
|
|
204
|
+
const wb = await createWriteOnlyWorkbook(sink);
|
|
205
|
+
const ws = await wb.addWorksheet('Data');
|
|
206
|
+
ws.setColumnWidth(1, 24); // must precede the first appendRow
|
|
207
|
+
for (let r = 0; r < 10_000_000; r++) {
|
|
208
|
+
await ws.appendRow([r, `row-${r}`, r * Math.PI]);
|
|
209
|
+
}
|
|
210
|
+
await ws.close();
|
|
211
|
+
await wb.finalize();
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The streaming writer pushes each row through deflate as it arrives, and
|
|
215
|
+
`toFile` forwards each deflated chunk to disk (honouring write-stream
|
|
216
|
+
backpressure) — peak memory stays at one pending-row buffer plus deflate
|
|
217
|
+
scratch, regardless of total archive size. The same is true of `toWritable`;
|
|
218
|
+
buffered sinks (`toBuffer` / `toBlob` / `toArrayBuffer`) instead keep the
|
|
219
|
+
full archive resident so `result()` can hand it back in one piece.
|
|
220
|
+
|
|
221
|
+
### Streaming read — iterate row-by-row without materialising the sheet
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
import { loadWorkbookStream } from '@office-kit/xlsx/streaming';
|
|
225
|
+
import { fromFile } from '@office-kit/xlsx/node';
|
|
226
|
+
|
|
227
|
+
const wb = await loadWorkbookStream(fromFile('big.xlsx'));
|
|
228
|
+
const sheet = wb.openWorksheet(wb.sheetNames[0] ?? '');
|
|
229
|
+
for await (const row of sheet.iterRows({ minRow: 1, maxRow: 100 })) {
|
|
230
|
+
console.log(row.map((c) => c.value));
|
|
231
|
+
}
|
|
232
|
+
await wb.close();
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The whole-sheet iteration path (default / `minRow <= 1`) inflates the
|
|
236
|
+
worksheet entry chunk-by-chunk straight into the SAX parser, so the inflated
|
|
237
|
+
worksheet body is never fully resident. Note: ZIP requires random access to
|
|
238
|
+
its central directory, so the **compressed archive bytes are loaded up
|
|
239
|
+
front**. A 200 MB compressed xlsx therefore needs ~200 MB resident, plus the
|
|
240
|
+
inflate window + SAX state per active iterator — not the multi-GB inflated
|
|
241
|
+
worksheet payload. Band queries (`minRow > 1`) build a row-offset index once
|
|
242
|
+
per sheet, which does materialise that sheet's inflated bytes; subsequent
|
|
243
|
+
band queries reuse the cached index.
|
|
244
|
+
|
|
245
|
+
### Migrating from openpyxl
|
|
246
|
+
|
|
247
|
+
@office-kit/xlsx is shaped after openpyxl, but a few defaults differ. The most
|
|
248
|
+
common surprise for direct ports:
|
|
249
|
+
|
|
250
|
+
- **`createWorkbook()` returns an empty workbook with no sheets.**
|
|
251
|
+
`openpyxl.Workbook()` creates a default sheet named `Sheet` that
|
|
252
|
+
callers usually remove with `wb.remove(wb.active)`. @office-kit/xlsx skips
|
|
253
|
+
that step — call `addWorksheet(wb, 'Data')` directly. Translating a
|
|
254
|
+
`remove(active)` call literally produces a no-op (or, worse, a guard
|
|
255
|
+
that hides a real bug elsewhere).
|
|
256
|
+
- **`setCell(ws, row, col, value)`** is the @office-kit/xlsx equivalent of
|
|
257
|
+
openpyxl's `ws.cell(row=r, column=c, value=v)`. Coordinates are
|
|
258
|
+
1-based on both sides.
|
|
259
|
+
- **`makeBorder({ left: makeSide({ style: 'thin' }) })`** is the
|
|
260
|
+
@office-kit/xlsx equivalent of openpyxl's
|
|
261
|
+
`Border(left=Side(style='thin'))`. Same with `makeFill`, `makeFont`,
|
|
262
|
+
etc. — every style primitive has a `make*` constructor under
|
|
263
|
+
`@office-kit/xlsx/styles`.
|
|
264
|
+
|
|
265
|
+
## What's supported
|
|
266
|
+
|
|
267
|
+
- ✅ Cell values: number, string (sharedStrings), boolean, error, formulas
|
|
268
|
+
(normal / array / shared / dataTable), inline rich text
|
|
269
|
+
- ✅ Styles: Font, Fill, Border, Alignment, Protection, NumberFormat, full
|
|
270
|
+
Stylesheet pool with dedup, named styles + DXF
|
|
271
|
+
- ✅ Worksheet rich features: mergedCells, sheetView/freezePanes, columnDims,
|
|
272
|
+
rowDims, hyperlinks, defined names, data validations, autoFilter, Tables,
|
|
273
|
+
legacy comments, conditional formatting
|
|
274
|
+
- ✅ Drawings: anchors, images (PNG/JPEG/GIF/BMP/WebP/TIFF/SVG/EMF/WMF) with
|
|
275
|
+
format + dimension auto-detection, picture frames in worksheets and charts
|
|
276
|
+
- ✅ Charts: 16 legacy `c:` chart kinds + 8 `cx:` chartex kinds (Sunburst,
|
|
277
|
+
Treemap, Waterfall, Histogram, Pareto, Funnel, BoxWhisker, RegionMap),
|
|
278
|
+
spPr / txPr / dLbls / trendline / errBars wiring, chartsheets, UserShapes
|
|
279
|
+
- ✅ Pivot tables / VBA / OLE / threaded comments / external links / Power
|
|
280
|
+
Query metadata / customXml / customUI: byte-identical passthrough so
|
|
281
|
+
Excel 365 still renders parts we don't model. The `<workbook>` body
|
|
282
|
+
extras and per-sheet rels chain are preserved end-to-end.
|
|
283
|
+
- ✅ Encrypted xlsx detection (CFB Compound Document magic): clear error
|
|
284
|
+
pointing at `msoffcrypto-tool` for decryption.
|
|
285
|
+
- ✅ ZIP64 write — partial: workbooks with > 65 535 entries get a ZIP64 EOCD
|
|
286
|
+
record + locator spliced into the final chunk. Read works too. **Limit:**
|
|
287
|
+
individual entry sizes and the central-directory offset must still fit in
|
|
288
|
+
32 bits (≤ 4 GiB each); xlsx archives never approach that in practice, but
|
|
289
|
+
if you genuinely need a single >4 GiB entry the writer will throw. Tracked
|
|
290
|
+
in [`src/zip/zip64-patch.ts`][zip64-patch].
|
|
291
|
+
|
|
292
|
+
[zip64-patch]: ./src/zip/zip64-patch.ts
|
|
293
|
+
|
|
294
|
+
## Development
|
|
295
|
+
|
|
296
|
+
The test suite reads fixtures from the `reference/openpyxl` git submodule, so
|
|
297
|
+
clone with submodules (or run `pnpm install`, which auto-inits via the
|
|
298
|
+
`prepare` script):
|
|
299
|
+
|
|
300
|
+
```sh
|
|
301
|
+
git clone --recursive https://github.com/office-kit/xlsx.git
|
|
302
|
+
# or, if you already cloned without --recursive:
|
|
303
|
+
git submodule update --init --recursive
|
|
304
|
+
|
|
305
|
+
pnpm install
|
|
306
|
+
pnpm typecheck
|
|
307
|
+
pnpm lint
|
|
308
|
+
pnpm test # vitest, ~2100 tests
|
|
309
|
+
pnpm test:perf # write-only throughput + heap-budget bench
|
|
310
|
+
pnpm build # tsdown + tsc → dist/
|
|
311
|
+
pnpm size # size-limit guards on each bundle
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
[Nix flake](flake.nix) included — `nix develop` (or [direnv](https://direnv.net/)
|
|
315
|
+
with `use flake`) gives a pinned Node 22 + pnpm 10 + Python 3 environment.
|
|
316
|
+
|
|
317
|
+
## License
|
|
318
|
+
|
|
319
|
+
MIT — see [LICENSE](LICENSE) and [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Third Party Notices
|
|
2
|
+
|
|
3
|
+
`@office-kit/xlsx` is a TypeScript port of, and incorporates work derived from, the
|
|
4
|
+
following projects.
|
|
5
|
+
|
|
6
|
+
## openpyxl (MIT)
|
|
7
|
+
|
|
8
|
+
Source: <https://foss.heptapod.net/openpyxl/openpyxl> (canonical, Mercurial),
|
|
9
|
+
mirrored at <https://github.com/quintagroup/openpyxl> (Git, used as this
|
|
10
|
+
repository's `reference/openpyxl` submodule for porting reference only).
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Copyright (c) 2010 openpyxl
|
|
14
|
+
|
|
15
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
16
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
17
|
+
in the Software without restriction, including without limitation the rights
|
|
18
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
19
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
20
|
+
furnished to do so, subject to the following conditions:
|
|
21
|
+
|
|
22
|
+
The above copyright notice and this permission notice shall be included in
|
|
23
|
+
all copies or substantial portions of the Software.
|
|
24
|
+
|
|
25
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
26
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
27
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
28
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
29
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
31
|
+
THE SOFTWARE.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Runtime dependencies
|
|
35
|
+
|
|
36
|
+
The following npm packages ship as part of `@office-kit/xlsx` at runtime. Each is
|
|
37
|
+
licensed under its own terms — links go to the upstream repository where the
|
|
38
|
+
license text lives.
|
|
39
|
+
|
|
40
|
+
- **fflate** (MIT) — <https://github.com/101arrowz/fflate>
|
|
41
|
+
ZIP / DEFLATE I/O. Used by `src/zip/reader.ts` (`unzipSync` fallback +
|
|
42
|
+
`inflateSync`) and `src/zip/writer.ts` (`Zip` + `ZipDeflate` /
|
|
43
|
+
`ZipPassThrough` streaming-deflate writer).
|
|
44
|
+
- **saxes** (ISC) — <https://github.com/lddubeau/saxes>
|
|
45
|
+
Streaming XML parser. Drives the SAX iter API in `src/xml/iterparse.ts`
|
|
46
|
+
which the streaming read-only path in `src/streaming/read-only.ts` uses.
|
|
47
|
+
- **fast-xml-parser** (MIT) —
|
|
48
|
+
<https://github.com/NaturalIntelligence/fast-xml-parser>
|
|
49
|
+
DOM-style XML parser used by `src/xml/parser.ts` for the eager
|
|
50
|
+
load path; the streaming read-only path uses saxes instead.
|
|
51
|
+
|
|
52
|
+
## Dev dependencies
|
|
53
|
+
|
|
54
|
+
Dev-only dependencies declared in `package.json` (vitest, oxlint, tsdown,
|
|
55
|
+
typescript, fast-check, size-limit, …) are subject to their upstream
|
|
56
|
+
licenses; consult `pnpm-lock.yaml` for resolved versions.
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { type RichText } from './rich-text';
|
|
2
|
+
/** Excel error tokens. */
|
|
3
|
+
export type ExcelErrorCode = '#NULL!' | '#DIV/0!' | '#VALUE!' | '#REF!' | '#NAME?' | '#NUM!' | '#N/A' | '#GETTING_DATA';
|
|
4
|
+
/** Formula sub-kind — drives the OOXML `<f t="…">` attribute. */
|
|
5
|
+
export type FormulaKind = 'normal' | 'array' | 'shared' | 'dataTable';
|
|
6
|
+
export interface FormulaValue {
|
|
7
|
+
readonly kind: 'formula';
|
|
8
|
+
readonly formula: string;
|
|
9
|
+
readonly t: FormulaKind;
|
|
10
|
+
/** Cached value Excel last computed for the cell, used when `data_only` reads it back. */
|
|
11
|
+
readonly cachedValue?: number | string | boolean;
|
|
12
|
+
/** Range string (`"A1:A10"`) for array / shared / dataTable formulas. */
|
|
13
|
+
readonly ref?: string;
|
|
14
|
+
/** Shared-formula index. */
|
|
15
|
+
readonly si?: number;
|
|
16
|
+
/** Data-table specific fields (mirrors openpyxl DataTableFormula). */
|
|
17
|
+
readonly r1?: string;
|
|
18
|
+
readonly r2?: string;
|
|
19
|
+
readonly dt2D?: boolean;
|
|
20
|
+
readonly dtr?: boolean;
|
|
21
|
+
readonly del1?: boolean;
|
|
22
|
+
readonly del2?: boolean;
|
|
23
|
+
readonly aca?: boolean;
|
|
24
|
+
readonly ca?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export type CellValue = number | string | boolean | Date | {
|
|
27
|
+
kind: 'duration';
|
|
28
|
+
ms: number;
|
|
29
|
+
} | {
|
|
30
|
+
kind: 'error';
|
|
31
|
+
code: ExcelErrorCode;
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'rich-text';
|
|
34
|
+
runs: RichText;
|
|
35
|
+
} | FormulaValue | null;
|
|
36
|
+
export interface Cell {
|
|
37
|
+
/** 1-based row index. */
|
|
38
|
+
row: number;
|
|
39
|
+
/** 1-based column index. */
|
|
40
|
+
col: number;
|
|
41
|
+
/** Effective cell value. `null` represents an empty cell. */
|
|
42
|
+
value: CellValue;
|
|
43
|
+
/** Index into Workbook.styles.cellXfs. 0 = default. */
|
|
44
|
+
styleId: number;
|
|
45
|
+
/** Optional reference to a Hyperlink registered on the worksheet. */
|
|
46
|
+
hyperlinkId?: number;
|
|
47
|
+
/** Optional reference to a Comment registered on the worksheet. */
|
|
48
|
+
commentId?: number;
|
|
49
|
+
}
|
|
50
|
+
/** Marker subtype for the placeholder cells inside a merged range (top-left holds the value). */
|
|
51
|
+
export interface MergedCell extends Cell {
|
|
52
|
+
merged: true;
|
|
53
|
+
}
|
|
54
|
+
/** Build a fresh Cell. Validates coordinates against the OOXML grid bounds. */
|
|
55
|
+
export declare function makeCell(row: number, col: number, value?: CellValue, styleId?: number): Cell;
|
|
56
|
+
/** Format a Cell's coordinate as the canonical "A1" string. */
|
|
57
|
+
export declare function getCoordinate(c: Cell): string;
|
|
58
|
+
/**
|
|
59
|
+
* Direct value setter. No type inference, no validation beyond the union — the
|
|
60
|
+
* caller is in charge. Use {@link bindValue} for the "do what I mean" path.
|
|
61
|
+
*/
|
|
62
|
+
export declare function setCellValue(c: Cell, value: CellValue): void;
|
|
63
|
+
/**
|
|
64
|
+
* "Smart" setter: infers the cell value from a JS runtime value.
|
|
65
|
+
* - `string` starting with `=` → formula
|
|
66
|
+
* - `string` matching an Excel error token → error variant
|
|
67
|
+
* - other primitives / Date / null pass through verbatim
|
|
68
|
+
*
|
|
69
|
+
* Intentionally not the default — explicit is clearer for typed code, and
|
|
70
|
+
* inferring on every write costs measurable time on the worksheet write hot
|
|
71
|
+
* path.
|
|
72
|
+
*/
|
|
73
|
+
export declare function bindValue(c: Cell, value: number | string | boolean | Date | null): void;
|
|
74
|
+
/** Plain `=A1+B1` style formula. Cached value is optional but recommended for round-trip. */
|
|
75
|
+
export declare function setFormula(c: Cell, formula: string, opts?: {
|
|
76
|
+
cachedValue?: FormulaValue['cachedValue'];
|
|
77
|
+
}): void;
|
|
78
|
+
/** Array (CSE) formula spanning a `ref` range. */
|
|
79
|
+
export declare function setArrayFormula(c: Cell, ref: string, formula: string, opts?: {
|
|
80
|
+
cachedValue?: FormulaValue['cachedValue'];
|
|
81
|
+
}): void;
|
|
82
|
+
/**
|
|
83
|
+
* Shared formula. The first cell in the group carries the formula text + ref;
|
|
84
|
+
* subsequent cells with the same `si` carry only the index and Excel
|
|
85
|
+
* reconstructs the formula via reference shifting.
|
|
86
|
+
*/
|
|
87
|
+
export declare function setSharedFormula(c: Cell, si: number, formula?: string, ref?: string, opts?: {
|
|
88
|
+
cachedValue?: FormulaValue['cachedValue'];
|
|
89
|
+
}): void;
|
|
90
|
+
/**
|
|
91
|
+
* Excel data-table formula (`<f t="dataTable">`). These appear as the "What-if
|
|
92
|
+
* Analysis → Data Table" feature output: a 1- or 2-variable sensitivity grid
|
|
93
|
+
* where the formula references one or two input cells. The wire format mirrors
|
|
94
|
+
* openpyxl's `DataTableFormula`:
|
|
95
|
+
*
|
|
96
|
+
* - `ref` — inclusive cell range the formula spans.
|
|
97
|
+
* - `r1`, `r2`— input cell coordinates ("$A$1" etc.).
|
|
98
|
+
* - `dt2D` — true for two-variable tables (uses both r1 and r2).
|
|
99
|
+
* - `dtr` — row-direction flag (true) vs column-direction (false).
|
|
100
|
+
* - `del1`/`del2` — Excel marks one of these true when the
|
|
101
|
+
* corresponding input cell has been deleted; the formula keeps round-tripping
|
|
102
|
+
* so Excel can show the warning state.
|
|
103
|
+
* - `aca`/`ca` — alwaysCalculate / calculate flags.
|
|
104
|
+
*/
|
|
105
|
+
export interface DataTableFormulaOpts {
|
|
106
|
+
ref: string;
|
|
107
|
+
r1?: string;
|
|
108
|
+
r2?: string;
|
|
109
|
+
dt2D?: boolean;
|
|
110
|
+
dtr?: boolean;
|
|
111
|
+
del1?: boolean;
|
|
112
|
+
del2?: boolean;
|
|
113
|
+
aca?: boolean;
|
|
114
|
+
ca?: boolean;
|
|
115
|
+
cachedValue?: FormulaValue['cachedValue'];
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Set a data-table formula on a cell. Preserves all the dt-specific attributes
|
|
119
|
+
* so the writer can re-emit `<f t="dataTable" r1="..." />` verbatim and Excel
|
|
120
|
+
* keeps treating the cell as a Data Table cell.
|
|
121
|
+
*/
|
|
122
|
+
export declare function setDataTableFormula(c: Cell, formula: string, opts: DataTableFormulaOpts): void;
|
|
123
|
+
/** Build a `{ kind: 'error', code }` cell value. */
|
|
124
|
+
export declare function makeErrorValue(code: ExcelErrorCode): {
|
|
125
|
+
kind: 'error';
|
|
126
|
+
code: ExcelErrorCode;
|
|
127
|
+
};
|
|
128
|
+
/** Build a `{ kind: 'duration', ms }` cell value. */
|
|
129
|
+
export declare function makeDurationValue(ms: number): {
|
|
130
|
+
kind: 'duration';
|
|
131
|
+
ms: number;
|
|
132
|
+
};
|
|
133
|
+
/** True iff `c.value` is the formula variant. */
|
|
134
|
+
export declare function isFormulaCell(c: Cell): boolean;
|
|
135
|
+
/** True iff `c.value` is the rich-text variant. */
|
|
136
|
+
export declare function isRichTextCell(c: Cell): boolean;
|
|
137
|
+
/** Returns true iff the cell has no content. */
|
|
138
|
+
export declare function isEmptyCell(c: Cell): boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Type guard for `MergedCell` — true iff the cell is a placeholder for a
|
|
141
|
+
* merged-range covered cell (the top-left of a merged range holds the value;
|
|
142
|
+
* the rest are `MergedCell`). Use this to filter merge-placeholders out of
|
|
143
|
+
* value-walking loops.
|
|
144
|
+
*/
|
|
145
|
+
export declare function isMergedCell(c: Cell): c is MergedCell;
|
|
146
|
+
/** Returns true iff the cell holds an Excel error value (`#REF!`, `#NAME?`, …). */
|
|
147
|
+
export declare function isErrorCell(c: Cell): boolean;
|
|
148
|
+
/**
|
|
149
|
+
* Get the formula text from a formula-bearing cell, or `undefined` for
|
|
150
|
+
* non-formula cells. Equivalent to: isFormulaValue(c.value) ? c.value.formula :
|
|
151
|
+
* undefined but spares callers the type-narrow + member access.
|
|
152
|
+
*/
|
|
153
|
+
export declare function getFormulaText(c: Cell): string | undefined;
|
|
154
|
+
/**
|
|
155
|
+
* Get the cached value Excel last computed for a formula cell, or `undefined`
|
|
156
|
+
* for non-formula / uncached cells. Useful for `data_only` read paths that want
|
|
157
|
+
* the displayed result without re-evaluating.
|
|
158
|
+
*/
|
|
159
|
+
export declare function getCachedFormulaValue(c: Cell): number | string | boolean | undefined;
|
|
160
|
+
/** True iff `v` is the formula variant. */
|
|
161
|
+
export declare function isFormulaValue(v: CellValue): v is FormulaValue;
|
|
162
|
+
/** True iff `v` is the rich-text variant. */
|
|
163
|
+
export declare function isRichTextValue(v: CellValue): v is {
|
|
164
|
+
kind: 'rich-text';
|
|
165
|
+
runs: RichText;
|
|
166
|
+
};
|
|
167
|
+
/** True iff `v` is the error variant. */
|
|
168
|
+
export declare function isErrorValue(v: CellValue): v is {
|
|
169
|
+
kind: 'error';
|
|
170
|
+
code: ExcelErrorCode;
|
|
171
|
+
};
|
|
172
|
+
/** True iff `v` is the duration variant. */
|
|
173
|
+
export declare function isDurationValue(v: CellValue): v is {
|
|
174
|
+
kind: 'duration';
|
|
175
|
+
ms: number;
|
|
176
|
+
};
|
|
177
|
+
export interface CellValueAsStringOptions {
|
|
178
|
+
/** Renderer for `Date` cells. Defaults to `d => d.toISOString()`. */
|
|
179
|
+
dateFormat?: (value: Date) => string;
|
|
180
|
+
/** Replacement for the `null` cell value. Defaults to `''`. */
|
|
181
|
+
emptyText?: string;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Coerce a CellValue to its plain-string display form. Numbers / booleans
|
|
185
|
+
* convert via `String`; rich text concatenates run text; formulas yield the
|
|
186
|
+
* cached value (or empty string when uncached); errors yield their Excel token;
|
|
187
|
+
* durations yield `"<ms> ms"` with no formatting; Dates yield
|
|
188
|
+
* `Date.toISOString()`; `null` yields `""`.
|
|
189
|
+
*
|
|
190
|
+
* Pass `opts.dateFormat` to override the Date renderer (e.g. a locale-specific
|
|
191
|
+
* format) and `opts.emptyText` to substitute a different placeholder for
|
|
192
|
+
* `null` cells.
|
|
193
|
+
*/
|
|
194
|
+
export declare function cellValueAsString(v: CellValue, opts?: CellValueAsStringOptions): string;
|
|
195
|
+
/**
|
|
196
|
+
* Coerce a CellValue to `boolean | undefined`. Booleans pass through; `'TRUE'`
|
|
197
|
+
* / `'true'` and `'FALSE'` / `'false'` (case-insensitive) parse to true /
|
|
198
|
+
* false; numbers yield `false` for 0 and `true` for any other finite value
|
|
199
|
+
* (matching Excel's truthy-number coercion); formula cells return their cached
|
|
200
|
+
* boolean if any. Everything else (null, Date, error, duration, rich-text,
|
|
201
|
+
* non-bool strings) yields `undefined`.
|
|
202
|
+
*/
|
|
203
|
+
export declare function cellValueAsBoolean(v: CellValue): boolean | undefined;
|
|
204
|
+
/**
|
|
205
|
+
* Coerce a CellValue to a `Date` when one is meaningful. Pass-through for
|
|
206
|
+
* `Date`-typed values; ISO-8601 strings (anything `new Date(s)` parses to a
|
|
207
|
+
* finite time) round-trip; durations are interpreted as `new Date(ms)`.
|
|
208
|
+
* Numbers, booleans, formulas, errors, rich text, and null all return
|
|
209
|
+
* `undefined` — this helper does **not** apply the Excel-serial-to-Date
|
|
210
|
+
* conversion (use `excelToDate` for that).
|
|
211
|
+
*/
|
|
212
|
+
export declare function cellValueAsDate(v: CellValue): Date | undefined;
|
|
213
|
+
/**
|
|
214
|
+
* Coerce a CellValue to a number when one is meaningful. Booleans yield 0/1;
|
|
215
|
+
* numeric strings parse via `Number(s)`; rich-text concats then parses;
|
|
216
|
+
* formulas with a numeric cached value pass through. Returns `undefined` when
|
|
217
|
+
* there's no sensible numeric reading (text strings, errors, dates, durations,
|
|
218
|
+
* null, empty).
|
|
219
|
+
*/
|
|
220
|
+
export declare function cellValueAsNumber(v: CellValue): number | undefined;
|
|
221
|
+
/**
|
|
222
|
+
* Map a CellValue to the most natural JS primitive for display / export.
|
|
223
|
+
* Unlike `cellValueAsString`/`cellValueAsNumber`/etc., which each force a
|
|
224
|
+
* single target type and return `undefined` when the value can't be coerced,
|
|
225
|
+
* this returns whatever primitive best represents the union variant:
|
|
226
|
+
*
|
|
227
|
+
* - `null` → `null`
|
|
228
|
+
* - `string` / `number` / `boolean` / `Date` → passthrough
|
|
229
|
+
* - rich text → joined run text (`string`)
|
|
230
|
+
* - formula → recursive on `cachedValue` (`null` when uncached)
|
|
231
|
+
* - error → error code (`string`)
|
|
232
|
+
* - duration → `ms` (`number`)
|
|
233
|
+
*/
|
|
234
|
+
export declare function cellValueAsPrimitive(v: CellValue): string | number | boolean | Date | null;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { Cell, CellValue, CellValueAsStringOptions, DataTableFormulaOpts, ExcelErrorCode, FormulaKind, FormulaValue, MergedCell, } from './cell';
|
|
2
|
+
export { bindValue, cellValueAsBoolean, cellValueAsDate, cellValueAsNumber, cellValueAsPrimitive, cellValueAsString, getCachedFormulaValue, getCoordinate, getFormulaText, isDurationValue, isEmptyCell, isErrorCell, isErrorValue, isFormulaCell, isFormulaValue, isMergedCell, isRichTextCell, isRichTextValue, makeCell, makeDurationValue, makeErrorValue, setArrayFormula, setCellValue, setDataTableFormula, setFormula, setSharedFormula, } from './cell';
|
|
3
|
+
export type { InlineFont, InlineUnderline, InlineVertAlign, RichText, TextRun, } from './rich-text';
|
|
4
|
+
export { makeRichText, makeTextRun, richTextToString } from './rich-text';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Color } from '../styles/colors';
|
|
2
|
+
/** Underline styles per openpyxl's cell-level NestedNoneSet. */
|
|
3
|
+
export type InlineUnderline = 'single' | 'double' | 'singleAccounting' | 'doubleAccounting';
|
|
4
|
+
export type InlineVertAlign = 'baseline' | 'superscript' | 'subscript';
|
|
5
|
+
export interface InlineFont {
|
|
6
|
+
readonly name?: string;
|
|
7
|
+
readonly sz?: number;
|
|
8
|
+
readonly b?: boolean;
|
|
9
|
+
readonly i?: boolean;
|
|
10
|
+
readonly u?: InlineUnderline;
|
|
11
|
+
readonly strike?: boolean;
|
|
12
|
+
readonly outline?: boolean;
|
|
13
|
+
readonly shadow?: boolean;
|
|
14
|
+
readonly condense?: boolean;
|
|
15
|
+
readonly extend?: boolean;
|
|
16
|
+
readonly vertAlign?: InlineVertAlign;
|
|
17
|
+
readonly color?: Color;
|
|
18
|
+
readonly family?: number;
|
|
19
|
+
readonly charset?: number;
|
|
20
|
+
readonly scheme?: 'major' | 'minor';
|
|
21
|
+
}
|
|
22
|
+
export interface TextRun {
|
|
23
|
+
readonly text: string;
|
|
24
|
+
readonly font?: InlineFont;
|
|
25
|
+
}
|
|
26
|
+
/** A frozen array of TextRuns. The shared cell value shape under `kind: 'rich-text'`. */
|
|
27
|
+
export type RichText = ReadonlyArray<TextRun>;
|
|
28
|
+
export declare function makeTextRun(text: string, font?: InlineFont): TextRun;
|
|
29
|
+
export declare function makeRichText(runs: ReadonlyArray<TextRun | {
|
|
30
|
+
text: string;
|
|
31
|
+
font?: InlineFont;
|
|
32
|
+
}>): RichText;
|
|
33
|
+
/**
|
|
34
|
+
* Concatenate the plain-text content of a rich-text value (rich-text
|
|
35
|
+
* read paths often want the raw text without formatting).
|
|
36
|
+
*/
|
|
37
|
+
export declare function richTextToString(rt: RichText): string;
|