@shbernal/ts-xlsx 3.2.1 → 3.2.2-next.22.g076b1ec
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/README.md +35 -35
- package/dist/core/autofilter.d.ts +1 -1
- package/dist/core/data-validation-overlay.d.ts +1 -1
- package/dist/core/workbook.d.ts +24 -20
- package/dist/core/worksheet.d.ts +2 -2
- package/dist/customui/ribbon.d.ts +1 -1
- package/dist/io/csv/write.d.ts +2 -2
- package/dist/io/style/xf-style.d.ts +1 -1
- package/dist/io/xlsx/comments.d.ts +1 -1
- package/dist/vba/project-editor.d.ts +1 -1
- package/dist/xml/xml-read.d.ts +1 -1
- package/package.json +9 -10
- package/skills/ts-xlsx-upstream/SKILL.md +5 -5
package/README.md
CHANGED
|
@@ -7,25 +7,25 @@
|
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
|
|
9
9
|
A TypeScript-first library for reading and writing spreadsheet documents
|
|
10
|
-
(`.xlsx` / OOXML, and CSV)
|
|
10
|
+
(`.xlsx` / OOXML, and CSV). It is synchronous, `Uint8Array`-native, and dependency-lean.
|
|
11
11
|
|
|
12
12
|
**[Documentation, guide and a browser playground](https://shbernal.github.io/ts-xlsx/)**
|
|
13
13
|
|
|
14
|
-
> **Status
|
|
14
|
+
> **Status: stable and published on npm.** The [changelog](CHANGELOG.md) has the current version.
|
|
15
15
|
> `ts-xlsx` began as a hard fork of [ExcelJS](https://github.com/exceljs/exceljs)
|
|
16
16
|
> and has been rebuilt from the ground up into a modern, strict-TypeScript library.
|
|
17
|
-
> **It carries no backwards-compatibility guarantee with ExcelJS
|
|
17
|
+
> **It carries no backwards-compatibility guarantee with ExcelJS**: the API below is
|
|
18
18
|
> its own, not a drop-in. From 1.0.0 onward it follows
|
|
19
19
|
> [SemVer](https://semver.org/) against _itself_; see the [changelog](CHANGELOG.md).
|
|
20
|
-
> See [`
|
|
20
|
+
> See [`AGENTS.md`](AGENTS.md) for the goals and [`docs/architecture.md`](docs/architecture.md)
|
|
21
21
|
> for the design, and [migrating from ExcelJS](docs/migrating-from-exceljs.md) if you are coming across.
|
|
22
22
|
|
|
23
23
|
## Why it exists
|
|
24
24
|
|
|
25
|
-
Upstream ExcelJS is effectively unmaintained
|
|
26
|
-
hundreds of issues and PRs
|
|
25
|
+
Upstream ExcelJS is effectively unmaintained (no release since 2023, a backlog of
|
|
26
|
+
hundreds of issues and PRs), while still serving tens of millions of downloads a month.
|
|
27
27
|
`ts-xlsx` extracts the accumulated value from that backlog (as a permanent regression
|
|
28
|
-
corpus) and discards the debt: no untyped
|
|
28
|
+
corpus) and discards the debt: no untyped APIs, no callback APIs, a single small
|
|
29
29
|
runtime dependency, and every behavior pinned by a test. This is not a compatibility
|
|
30
30
|
shim. It is a different, better library that happens to share ancestry.
|
|
31
31
|
|
|
@@ -35,9 +35,9 @@ What that buys you today:
|
|
|
35
35
|
`exactOptionalPropertyTypes`. The published `.d.ts` _is_ the documentation, and the
|
|
36
36
|
[API reference](docs/api/README.md) is generated straight from it.
|
|
37
37
|
- **Synchronous, buffer-native I/O.** `readXlsx`/`writeXlsx` take and return a
|
|
38
|
-
`Uint8Array
|
|
38
|
+
`Uint8Array`. There are no streams to await for the buffered path, no Node `Buffer` assumption,
|
|
39
39
|
so the same code runs in Node and the browser.
|
|
40
|
-
- **Streaming when you need it.** Bounded-memory row streaming both ways
|
|
40
|
+
- **Streaming when you need it.** Bounded-memory row streaming both ways: read a large
|
|
41
41
|
workbook without materializing it, or write one incrementally to a Node stream so peak
|
|
42
42
|
memory stays flat.
|
|
43
43
|
- **One runtime dependency** ([`fflate`](https://github.com/101arrowz/fflate) for zip),
|
|
@@ -77,7 +77,7 @@ const people = reopened.getWorksheet('People');
|
|
|
77
77
|
console.log(people?.getCell('A2').value); // 'Ada'
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
Persisting to disk is your platform's job, not the library's
|
|
80
|
+
Persisting to disk is your platform's job, not the library's. `writeXlsx` hands you the
|
|
81
81
|
bytes:
|
|
82
82
|
|
|
83
83
|
```ts
|
|
@@ -89,17 +89,17 @@ const wb2 = readXlsx(readFileSync('people.xlsx')); // a Buffer is a Uint8Array
|
|
|
89
89
|
|
|
90
90
|
## Core model
|
|
91
91
|
|
|
92
|
-
- **`Workbook
|
|
92
|
+
- **`Workbook`**: the document. `addWorksheet(name)`, `getWorksheet(nameOrId)`,
|
|
93
93
|
`worksheets`, defined names, images, and workbook-level properties.
|
|
94
|
-
- **`Worksheet
|
|
94
|
+
- **`Worksheet`**: a sheet. `getCell('B3')`, `addRow(values)`, `addTable(...)`,
|
|
95
95
|
`mergeCells('A1:B2')`, `getRow(n)` / `getColumn(n)`, `rows()` / `columns()`, page setup,
|
|
96
96
|
and print options.
|
|
97
|
-
- **`Row`** / **`Column
|
|
97
|
+
- **`Row`** / **`Column`**: one line of the grid. Formatting is flat (`row.height = 20`,
|
|
98
98
|
`column.width = 12`, `column.key = 'name'`), and cells are reachable from it:
|
|
99
99
|
`row.getCell('B')`, `row.cells`, `row.values`.
|
|
100
|
-
- **`Cell
|
|
100
|
+
- **`Cell`**: one cell. `cell.value` is the whole story: a `number`, `string`,
|
|
101
101
|
`boolean`, `Date`, `null` (empty), a formula (`{formula, result}`), rich text, or an
|
|
102
|
-
error
|
|
102
|
+
error, all precisely typed as [`CellValue`](docs/api/cell-values.md). A hyperlink is not
|
|
103
103
|
a value: it sits on the sheet, over any cell, through
|
|
104
104
|
`sheet.addHyperlink({ref: 'B2', target: 'https://example.com'})`.
|
|
105
105
|
|
|
@@ -115,16 +115,16 @@ for (const row of sheet.rows()) {
|
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
`Row` and `Column` are _handles_, not snapshots: they read and write straight through to the
|
|
118
|
-
sheet, so two handles on the same line always agree, and reading one creates nothing
|
|
118
|
+
sheet, so two handles on the same line always agree, and reading one creates nothing. Asking
|
|
119
119
|
about row 500 costs nothing and does not extend the used range. Position is fixed, exactly as a
|
|
120
120
|
`Cell`'s is: after a splice, `getRow(3)` still means row 3, now holding whatever moved there.
|
|
121
121
|
|
|
122
122
|
Addresses are honest: an axis a reference doesn't mention is `undefined`, never a
|
|
123
|
-
sentinel
|
|
123
|
+
sentinel. See [`decodeAddress`](docs/api/addresses-ranges.md).
|
|
124
124
|
|
|
125
125
|
Charts, vector shapes, slicers, and legacy form controls are **round-trip-only**: a
|
|
126
126
|
workbook that has them keeps them byte-faithfully through a load/edit/save, but there is
|
|
127
|
-
no API to author a new one
|
|
127
|
+
no API to author a new one. See [`docs/api/preserved.md`](docs/api/preserved.md) and
|
|
128
128
|
[ADR-0014](docs/decisions/0014-charts-shapes-slicers-are-round-trip-only-for-1-0.md).
|
|
129
129
|
|
|
130
130
|
## Reading, writing, streaming, CSV
|
|
@@ -140,10 +140,10 @@ import {
|
|
|
140
140
|
writeCsvText, // CSV as a string
|
|
141
141
|
} from '@shbernal/ts-xlsx';
|
|
142
142
|
// The streaming writer opens files and pipes Node streams, so it is the one Node-only face
|
|
143
|
-
// and lives behind its own subpath
|
|
143
|
+
// and lives behind its own subpath. That keeps everything above browser-safe:
|
|
144
144
|
import {WorkbookStreamWriter} from '@shbernal/ts-xlsx/node';
|
|
145
145
|
|
|
146
|
-
// Bounded-memory extraction
|
|
146
|
+
// Bounded-memory extraction. The whole workbook is never materialized:
|
|
147
147
|
for (const row of readSheetRows(bytes, {sheet: 'People'})) {
|
|
148
148
|
console.log(
|
|
149
149
|
row.number,
|
|
@@ -151,7 +151,7 @@ for (const row of readSheetRows(bytes, {sheet: 'People'})) {
|
|
|
151
151
|
);
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
// Bounded-memory generation
|
|
154
|
+
// Bounded-memory generation. Commit each row to serialize and free it as you go:
|
|
155
155
|
const writer = new WorkbookStreamWriter();
|
|
156
156
|
const out = writer.addWorksheet('Big');
|
|
157
157
|
for (let i = 1; i <= 1_000_000; i++) out.addRow([i, i * i]).commit();
|
|
@@ -168,7 +168,7 @@ never assembled as one object, which is what passing a sink was for.
|
|
|
168
168
|
|
|
169
169
|
## Writing without blocking the event loop
|
|
170
170
|
|
|
171
|
-
`writeXlsx` spends the whole cost of DEFLATE on the calling thread
|
|
171
|
+
`writeXlsx` spends the whole cost of DEFLATE on the calling thread: seconds, for a large
|
|
172
172
|
workbook, during which a server answers nothing. `writeXlsxAsync` produces the same package
|
|
173
173
|
with the compression handed to worker threads:
|
|
174
174
|
|
|
@@ -185,12 +185,12 @@ always and speed only when there is more than one substantial part.
|
|
|
185
185
|
|
|
186
186
|
There is deliberately **no `readXlsxAsync`**. Reading is dominated by XML parsing and model
|
|
187
187
|
building, which no worker can take off the calling thread, so it would advertise a
|
|
188
|
-
non-blocking read and then block for most of its duration
|
|
189
|
-
|
|
188
|
+
non-blocking read and then block for most of its duration. The reader also enforces its zip-bomb
|
|
189
|
+
ceiling by counting output between synchronous input slices, a guarantee that weakens the
|
|
190
190
|
moment inflation moves to a worker. If you need a non-blocking read, run the whole read in a
|
|
191
191
|
worker. See [ADR-0024](docs/decisions/0024-async-is-one-writer-not-a-mirrored-pair.md).
|
|
192
192
|
|
|
193
|
-
The reader decodes untrusted input defensively
|
|
193
|
+
The reader decodes untrusted input defensively. Entities are decoded but never
|
|
194
194
|
expanded, and inflation is bounded by a running output counter rather than any declared
|
|
195
195
|
size, so a malformed or hostile package can't exhaust memory.
|
|
196
196
|
|
|
@@ -199,7 +199,7 @@ size, so a malformed or hostile package can't exhaust memory.
|
|
|
199
199
|
The bare package name gives you everything, and with a bundler that is the right default:
|
|
200
200
|
`"sideEffects": false` is declared, so anything you don't reference is dropped. The subpaths
|
|
201
201
|
are for when you'd rather the module graph itself said which half of the library you depend
|
|
202
|
-
on
|
|
202
|
+
on, as in a Lambda with no bundler or a service that only classifies failures:
|
|
203
203
|
|
|
204
204
|
| Import from | You get | It loads |
|
|
205
205
|
| ---------------------------- | ------------------------------------------------------------------------------ | -------- |
|
|
@@ -219,18 +219,18 @@ no Node built-in at all, which is what makes them bundle for a browser with noth
|
|
|
219
219
|
configure ([ADR-0040](docs/decisions/0040-the-browser-boundary-is-an-entry-point.md)).
|
|
220
220
|
|
|
221
221
|
Every error class lives in `/errors` and nowhere else, because a container-level failure
|
|
222
|
-
belongs to no single codec
|
|
222
|
+
belongs to no single codec: `readXlsx` and `readXlsb` both raise `UnsupportedFormatError`.
|
|
223
223
|
Catching and classifying therefore costs 3 KB, not a parser.
|
|
224
224
|
|
|
225
225
|
`/xlsx` is barely cheaper than the whole package, and that is honest rather than a defect:
|
|
226
226
|
`readXlsx` sniffs the bytes and hands a binary package to the BIFF12 reader, so the `.xlsb`
|
|
227
227
|
codec is not optional on that path. The numbers above are the static-import closure of each
|
|
228
|
-
entry, measured by `pnpm run size
|
|
228
|
+
entry, measured by `pnpm run size`. A bundler can only shrink them further.
|
|
229
229
|
|
|
230
230
|
## API reference
|
|
231
231
|
|
|
232
|
-
The full reference is generated from the public types
|
|
233
|
-
compiler accepts
|
|
232
|
+
The full reference is generated from the public types, so it cannot drift from what the
|
|
233
|
+
compiler accepts. It lives in **[`docs/api/`](docs/api/README.md)**. Regenerate it with:
|
|
234
234
|
|
|
235
235
|
```shell
|
|
236
236
|
pnpm run docs
|
|
@@ -246,7 +246,7 @@ shapes are deliberately different (synchronous `Uint8Array` I/O instead of async
|
|
|
246
246
|
## Hit a bug? There is a skill for that
|
|
247
247
|
|
|
248
248
|
Most code that uses this library is written by an agent, and an agent that hits a library
|
|
249
|
-
defect will usually route around it silently
|
|
249
|
+
defect will usually route around it silently, so the defect is never reported and never
|
|
250
250
|
fixed. `ts-xlsx-upstream` is a skill that turns that moment into a filed issue with a
|
|
251
251
|
minimal reproduction, which is what the maintainers convert into a permanent regression
|
|
252
252
|
test. It ships inside the package, so it is already on disk:
|
|
@@ -262,8 +262,8 @@ copies: they are derived, `npx skills experimental_install` rebuilds them from t
|
|
|
262
262
|
`npx skills update` overwrites them in place.
|
|
263
263
|
|
|
264
264
|
It covers triage (is this bug ours or your file's?), reducing a failure to a script that
|
|
265
|
-
builds its own input, and
|
|
266
|
-
|
|
265
|
+
builds its own input, and never uploading a workbook to a public tracker, because
|
|
266
|
+
spreadsheets carry real data. Once the reproduction stands on its own, it files without
|
|
267
267
|
interrupting you, and tells you the issue number afterwards.
|
|
268
268
|
|
|
269
269
|
You do not need it to report something: <https://github.com/shbernal/ts-xlsx/issues> is
|
|
@@ -273,10 +273,10 @@ open, and errors the library knows are its own fault print that link themselves.
|
|
|
273
273
|
|
|
274
274
|
This project is optimized to be built and maintained largely by autonomous agents, with
|
|
275
275
|
a machine-checkable safety net as the primary guarantor of correctness. The rules that
|
|
276
|
-
govern every change are in [`
|
|
276
|
+
govern every change are in [`AGENTS.md`](AGENTS.md); the design and working agreements are
|
|
277
277
|
in [`docs/architecture.md`](docs/architecture.md); notable decisions are recorded under
|
|
278
278
|
[`docs/decisions/`](docs/decisions/).
|
|
279
279
|
|
|
280
280
|
## License
|
|
281
281
|
|
|
282
|
-
[MIT](LICENSE)
|
|
282
|
+
[MIT](LICENSE), inherited from ExcelJS and retained.
|
|
@@ -50,7 +50,7 @@ export declare const isCustomFilterOperator: (value: string) => value is CustomF
|
|
|
50
50
|
* is the common case (dropdowns, no criteria); an {@link AutoFilter} object carries per-column
|
|
51
51
|
* criteria too. Throws when the range is not a bounded rectangle, or a column's `colId` falls
|
|
52
52
|
* outside it, or a custom filter does not carry one or two predicates: a filter authored wrong is
|
|
53
|
-
* a bug to
|
|
53
|
+
* a bug to report, not to silently repair. (The reader sanitises hostile input before it reaches
|
|
54
54
|
* here, so load never trips these guards.)
|
|
55
55
|
*/
|
|
56
56
|
export declare function canonicalizeAutoFilter(input: string | AutoFilter): AutoFilter;
|
|
@@ -14,7 +14,7 @@ export declare class DataValidationOverlay {
|
|
|
14
14
|
* round-trip writes it back there instead of silently corrupting the cross-sheet reference.
|
|
15
15
|
*
|
|
16
16
|
* @throws {AuthoringError} when `sqref` names no area at all. A rule attached to nothing covers no
|
|
17
|
-
* cell and is written back as the same unreadable text, so it is a mistake worth
|
|
17
|
+
* cell and is written back as the same unreadable text, so it is a mistake worth reporting at the
|
|
18
18
|
* call. The reader does not reach this: it drops such an entry at its own boundary, where a foreign
|
|
19
19
|
* file's malformed attribute is supposed to be dropped.
|
|
20
20
|
*/
|
package/dist/core/workbook.d.ts
CHANGED
|
@@ -14,17 +14,21 @@ import { type NamedCellStyle, type TableStyleTable } from './workbook-styles.ts'
|
|
|
14
14
|
import { type DeclaredThemeSchemes } from './workbook-theme.ts';
|
|
15
15
|
import { type Visibility, Worksheet, type WorksheetState } from './worksheet.ts';
|
|
16
16
|
/**
|
|
17
|
-
* A workbook-level reference to package content the model does not
|
|
18
|
-
* (`pivotCacheDefinition`)
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
17
|
+
* A workbook-level reference to package content the model does not interpret: a pivot cache
|
|
18
|
+
* (`pivotCacheDefinition`), a slicer cache (`slicerCache`) or an external link. The reader keeps it
|
|
19
|
+
* verbatim so a round-trip re-emits it instead of dropping it.
|
|
20
|
+
*
|
|
21
|
+
* `relType` is the workbook relationship Type URI to re-emit, `entryPath` is the part it points at,
|
|
22
|
+
* and `parts` is the transitive closure that reference reaches, the entry included.
|
|
23
|
+
*
|
|
24
|
+
* `pivotCacheId` is the `<pivotCache cacheId>` a pivot cache is registered under in the workbook's
|
|
25
|
+
* `<pivotCaches>`. A pivot table finds its cache through that id, so the writer re-emits it too. A
|
|
26
|
+
* slicer cache has none, because the workbook lists slicer caches in an extension block instead.
|
|
27
|
+
*
|
|
24
28
|
* `externalReferenceIndex` is the 0-based position of an `externalLink` within the workbook's
|
|
25
|
-
* `<externalReferences
|
|
26
|
-
*
|
|
27
|
-
* workbook
|
|
29
|
+
* `<externalReferences>`: the `[n]` through which a formula or defined name reaches an external
|
|
30
|
+
* cell. The writer re-emits the block in its original order, so every `[n]` still points at the same
|
|
31
|
+
* linked workbook. A pivot or slicer cache has none.
|
|
28
32
|
*/
|
|
29
33
|
export interface PreservedWorkbookReference {
|
|
30
34
|
readonly relType: string;
|
|
@@ -113,7 +117,7 @@ export interface WorkbookProperties {
|
|
|
113
117
|
company?: string;
|
|
114
118
|
}
|
|
115
119
|
/**
|
|
116
|
-
* A named reference in the workbook, one of the entries Excel
|
|
120
|
+
* A named reference in the workbook, one of the entries Excel lists in its Name Manager. A name
|
|
117
121
|
* maps an identifier to a formula (`refersTo`), most often a cell range like `Sheet1!$A$1:$B$2` but
|
|
118
122
|
* possibly any formula. A name is global to the workbook unless it names a sheet in {@link scope},
|
|
119
123
|
* which restricts it to that sheet and lets another sheet reuse the same name independently.
|
|
@@ -369,8 +373,8 @@ export declare class Workbook {
|
|
|
369
373
|
* read from a package carrying no styles part: nothing was declared, and the library does not
|
|
370
374
|
* fabricate a declaration on the file's behalf.
|
|
371
375
|
*
|
|
372
|
-
* This is the
|
|
373
|
-
* which is this once anything has been authored over it.
|
|
376
|
+
* This is the value a round-trip preserves. {@link defaultFont} is what the workbook actually
|
|
377
|
+
* renders in, which is this once anything has been authored over it.
|
|
374
378
|
*/
|
|
375
379
|
get declaredDefaultFont(): Font | undefined;
|
|
376
380
|
/**
|
|
@@ -496,14 +500,14 @@ export declare class Workbook {
|
|
|
496
500
|
/**
|
|
497
501
|
* Show `images` on `sheet`, a worksheet of this workbook, registering each picture's bytes here
|
|
498
502
|
* and re-anchoring it against the id they land on. The counterpart to {@link exportImages}, and
|
|
499
|
-
* the
|
|
500
|
-
*
|
|
501
|
-
*
|
|
503
|
+
* the only way a picture can cross workbooks: an {@link AnchoredImage}'s `imageId` indexes one
|
|
504
|
+
* workbook's registry and means nothing in the next, so a raw anchor moved between workbooks
|
|
505
|
+
* points at media that does not exist there.
|
|
502
506
|
*
|
|
503
507
|
* The sheet's existing pictures are replaced, not appended to, so `importImages` is a transfer
|
|
504
|
-
* rather than an accumulation
|
|
505
|
-
*
|
|
506
|
-
*
|
|
508
|
+
* rather than an accumulation. {@link Worksheet.model} assignment works the same way, and it is why
|
|
509
|
+
* re-importing a sheet's own export leaves the sheet unchanged. For the same reason, an import with
|
|
510
|
+
* no `background` clears the destination's background.
|
|
507
511
|
*
|
|
508
512
|
* Registration is content-addressed: a picture whose bytes are already here is re-used at its
|
|
509
513
|
* existing id rather than stored twice, so importing the same sheet repeatedly, or twenty sheets
|
|
@@ -532,7 +536,7 @@ export declare class Workbook {
|
|
|
532
536
|
* {@link getWorksheet}, for a caller who knows the sheet is there: the miss throws instead of
|
|
533
537
|
* returning `undefined`, and the message names every sheet the workbook does have.
|
|
534
538
|
*
|
|
535
|
-
* The partial lookup is the right
|
|
539
|
+
* The partial lookup is the right call for asking *whether* a sheet exists, and the wrong
|
|
536
540
|
* one for reaching a sheet a template is expected to carry: `undefined` flows on into a `?.`
|
|
537
541
|
* chain and fails several steps later with nothing left to say about which name was missing.
|
|
538
542
|
* That listing is the whole point: a lookup miss is a typo, a stale template or a renamed tab,
|
package/dist/core/worksheet.d.ts
CHANGED
|
@@ -342,7 +342,7 @@ export declare class Worksheet {
|
|
|
342
342
|
/**
|
|
343
343
|
* The rows to serialise, as handles, in ascending row order: the union of rows holding cells and
|
|
344
344
|
* rows holding only metadata (a hidden or grouped row need carry no data). Mirrors how OOXML
|
|
345
|
-
* serialises (`<row>` wrapping `<c>`) and is the writer
|
|
345
|
+
* serialises (`<row>` wrapping `<c>`) and is what the writer iterates over.
|
|
346
346
|
*
|
|
347
347
|
* A handle yields its cells only when asked, so a pass that reads nothing but row attributes
|
|
348
348
|
* never assembles a cell array it will not look at.
|
|
@@ -453,7 +453,7 @@ export declare class Worksheet {
|
|
|
453
453
|
}, properties?: PictureProperties): void;
|
|
454
454
|
/**
|
|
455
455
|
* Anchor an image with a pre-built model anchor in the model's own units (EMUs). This is the
|
|
456
|
-
* low-level
|
|
456
|
+
* low-level call {@link addImage} builds on and the reader uses to re-pin an image parsed from
|
|
457
457
|
* a drawing part without a lossy pixel round-trip.
|
|
458
458
|
*/
|
|
459
459
|
addImageAnchor(imageId: number, anchor: ImageAnchor, properties?: PictureProperties): void;
|
|
@@ -11,7 +11,7 @@ export declare const CUSTOMUI_2010_REL_TYPE = "http://schemas.microsoft.com/offi
|
|
|
11
11
|
export declare function isCustomUiRelType(type: string): boolean;
|
|
12
12
|
/**
|
|
13
13
|
* A control element inside a ribbon group. `kind` is the element's local name, narrowed to the closed
|
|
14
|
-
* set of RibbonX control elements ({@link RibbonControlKind}); an element outside that set is
|
|
14
|
+
* set of RibbonX control elements ({@link RibbonControlKind}); an element outside that set is kept
|
|
15
15
|
* as `unknown` rather than dropped. The three identity attributes (`id` a document-defined control,
|
|
16
16
|
* `idQ` a qualified id, `idMso` a built-in control) and the two most-consulted display/behaviour
|
|
17
17
|
* attributes (`label`, `onAction`) are lifted out as typed conveniences; every attribute the element
|
package/dist/io/csv/write.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ import type { Workbook } from '../../core/workbook.ts';
|
|
|
5
5
|
* meaning the same bytes.
|
|
6
6
|
*
|
|
7
7
|
* Named here rather than taken from Node's `BufferEncoding`, which is what this option used to be:
|
|
8
|
-
* that type is part of `@types/node`, so it made a browser consumer's public API
|
|
9
|
-
*
|
|
8
|
+
* that type is part of `@types/node`, so it made a browser consumer's public API depend on Node's
|
|
9
|
+
* types, and it offered `base64` and `hex` as if they were output encodings for a text
|
|
10
10
|
* format. The list is what a CSV consumer actually asks for.
|
|
11
11
|
*/
|
|
12
12
|
export type CsvEncoding = 'ascii' | 'latin1' | 'ucs-2' | 'ucs2' | 'utf-8' | 'utf-16le' | 'utf16le' | 'utf8';
|
|
@@ -28,7 +28,7 @@ export interface StyleTable {
|
|
|
28
28
|
readonly namedStyles: ReadonlyArray<NamedCellStyle>;
|
|
29
29
|
/**
|
|
30
30
|
* Font id 0: the workbook's declared default font, the face every cell naming no font renders in.
|
|
31
|
-
*
|
|
31
|
+
* Kept separately from the fonts it was flattened onto because it is workbook-level state, not a
|
|
32
32
|
* cell format: a re-write must emit *this* face as font 0 rather than an assumed Calibri, or every
|
|
33
33
|
* empty cell changes face and every character-unit column width changes meaning. Absent when the
|
|
34
34
|
* file declares no font table.
|
|
@@ -38,7 +38,7 @@ export declare function collectNotes(cells: Iterable<Cell>): CommentCell[];
|
|
|
38
38
|
*
|
|
39
39
|
* Authors are laid out the way Excel lays them out: one synthetic `tc={headId}` entry per threaded
|
|
40
40
|
* conversation first, then a single anonymous author shared by every note (the model carries no note
|
|
41
|
-
* author). Each comment points at its own author by index, and a fallback
|
|
41
|
+
* author). Each comment points at its own author by index, and a fallback also carries the
|
|
42
42
|
* `xr:uid` naming its thread: the pair that keeps Excel treating the cell as threaded.
|
|
43
43
|
*/
|
|
44
44
|
export declare function commentsXml(comments: readonly CommentCell[]): string;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Only `procedural` and `class` modules can be removed this way. Removing a `document` module (e.g.
|
|
8
8
|
* `ThisWorkbook`) or a `designer` module (a UserForm) would leave the host referencing code that no
|
|
9
9
|
* longer exists, since their names are tied to a worksheet/workbook `codeName` or a designer storage
|
|
10
|
-
* this project-level
|
|
10
|
+
* this project-level edit has no visibility into. Editing such a module's code-behind is a job for
|
|
11
11
|
* the offline `tools/vba-compiler` (in-place mode), which drives the real host.
|
|
12
12
|
*
|
|
13
13
|
* @throws {VbaParseError} if `bin` is not a parseable VBA project (validated before any edit).
|
package/dist/xml/xml-read.d.ts
CHANGED
|
@@ -122,7 +122,7 @@ export declare function tagRanges(source: string): Generator<TagRange>;
|
|
|
122
122
|
* {@link XmlParseError}.
|
|
123
123
|
*/
|
|
124
124
|
export declare function elementRanges(source: string, local: string): Generator<ElementRange>;
|
|
125
|
-
/** An element start
|
|
125
|
+
/** An element start yielded by {@link openElements}: its qualified `name`, the namespace-stripped
|
|
126
126
|
* `local` name the filter matched on, and its already-decoded `attrs`. */
|
|
127
127
|
export interface OpenElement {
|
|
128
128
|
readonly name: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shbernal/ts-xlsx",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2-next.22.g076b1ec",
|
|
4
4
|
"description": "A TypeScript-first library for reading and writing xlsx (OOXML) spreadsheets.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"csv",
|
|
@@ -88,7 +88,6 @@
|
|
|
88
88
|
"site:preview": "vitepress preview www",
|
|
89
89
|
"docs": "node scripts/gen-docs.ts",
|
|
90
90
|
"docs:check": "node scripts/gen-docs.ts && git add --intent-to-add -- docs/api && git diff --exit-code -- docs/api",
|
|
91
|
-
"constitution:check": "node scripts/check-constitution.ts",
|
|
92
91
|
"layering:check": "node scripts/check-layering.ts",
|
|
93
92
|
"corpus-blind:check": "node scripts/check-corpus-blind.ts",
|
|
94
93
|
"entries:check": "node scripts/check-entries.ts",
|
|
@@ -125,18 +124,18 @@
|
|
|
125
124
|
"fflate": "^0.8.3"
|
|
126
125
|
},
|
|
127
126
|
"devDependencies": {
|
|
128
|
-
"@types/node": "^24.
|
|
127
|
+
"@types/node": "^24.19.2",
|
|
129
128
|
"charcheck": "^0.4.0",
|
|
130
|
-
"jszip": "^3.10.
|
|
131
|
-
"lefthook": "^2.1
|
|
129
|
+
"jszip": "^3.10.2",
|
|
130
|
+
"lefthook": "^2.2.1",
|
|
132
131
|
"ooxml-validate": "^0.0.3",
|
|
133
|
-
"oxfmt": "^0.
|
|
134
|
-
"oxlint": "^1.
|
|
135
|
-
"oxlint-tsgolint": "7.0.
|
|
136
|
-
"shiki": "^4.
|
|
132
|
+
"oxfmt": "^0.72.0",
|
|
133
|
+
"oxlint": "^1.87.0",
|
|
134
|
+
"oxlint-tsgolint": "7.0.2003",
|
|
135
|
+
"shiki": "^4.5.0",
|
|
137
136
|
"typescript": "^7.0.2",
|
|
138
137
|
"vitepress": "^1.6.4",
|
|
139
|
-
"vue": "^3.5.
|
|
138
|
+
"vue": "^3.5.43"
|
|
140
139
|
},
|
|
141
140
|
"engines": {
|
|
142
141
|
"node": ">=24"
|
|
@@ -17,7 +17,7 @@ builds its own input.** Everything below is in service of that.
|
|
|
17
17
|
|
|
18
18
|
## 1. Decide whether it is actually ours
|
|
19
19
|
|
|
20
|
-
Catch the error and read `code
|
|
20
|
+
Catch the error and read `code`. The taxonomy is deliberately coarse, and each answer
|
|
21
21
|
means something different about whose bug it is.
|
|
22
22
|
|
|
23
23
|
| `error.code` | Whose bug | Report it? |
|
|
@@ -45,14 +45,14 @@ node -v # runtime
|
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
Also capture, verbatim: the error `name`, its `code`, the full message, and the stack.
|
|
48
|
-
Do not paraphrase the message
|
|
48
|
+
Do not paraphrase the message: the throw site is often identifiable from its exact text.
|
|
49
49
|
|
|
50
50
|
## 3. Build a minimal reproduction
|
|
51
51
|
|
|
52
52
|
**Never attach or paste the user's workbook.** Spreadsheets in a real project carry
|
|
53
53
|
salaries, customer lists, and unreleased numbers. Treat every file in this repo as
|
|
54
54
|
confidential unless the user tells you otherwise, and never upload one to a public
|
|
55
|
-
tracker
|
|
55
|
+
tracker, including "just a screenshot of the sheet".
|
|
56
56
|
|
|
57
57
|
Instead, write a self-contained script that _constructs_ its input and fails:
|
|
58
58
|
|
|
@@ -80,7 +80,7 @@ from that description; they can never unsee an attachment.
|
|
|
80
80
|
|
|
81
81
|
If you need a file attached, build one: reproduce the _structure_ you suspect in a workbook
|
|
82
82
|
you generate, with invented values. A synthesized file is always safe to attach. A redacted
|
|
83
|
-
one is not yours to judge
|
|
83
|
+
one is not yours to judge: redaction fails quietly, and a public tracker is permanent.
|
|
84
84
|
|
|
85
85
|
## 4. Check it is not already filed
|
|
86
86
|
|
|
@@ -167,7 +167,7 @@ this project and leave a comment pointing at the issue, so the stopgap is remova
|
|
|
167
167
|
the fix ships:
|
|
168
168
|
|
|
169
169
|
```ts
|
|
170
|
-
// Workaround for ts-xlsx#<N
|
|
170
|
+
// Workaround for ts-xlsx#<N>: remove once fixed upstream.
|
|
171
171
|
```
|
|
172
172
|
|
|
173
173
|
## If `gh` is unavailable
|