@shbernal/ts-xlsx 2.1.0 → 3.1.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/README.md +4 -1
- package/dist/bytes.d.ts +7 -0
- package/dist/bytes.js +22 -0
- package/dist/core/address.d.ts +60 -2
- package/dist/core/address.js +40 -24
- package/dist/core/autofilter.d.ts +5 -4
- package/dist/core/autofilter.js +34 -35
- package/dist/core/axis-handle.d.ts +10 -0
- package/dist/core/axis-handle.js +17 -0
- package/dist/core/cell.d.ts +25 -0
- package/dist/core/cell.js +9 -3
- package/dist/core/clone.d.ts +29 -0
- package/dist/core/clone.js +24 -0
- package/dist/core/color-resolution.js +2 -4
- package/dist/core/column.d.ts +5 -1
- package/dist/core/column.js +34 -35
- package/dist/core/comment-thread.js +2 -1
- package/dist/core/conditional-formatting-overlay.d.ts +2 -1
- package/dist/core/conditional-formatting-overlay.js +2 -2
- package/dist/core/conditional-formatting.d.ts +14 -6
- package/dist/core/conditional-formatting.js +45 -43
- package/dist/core/containers.d.ts +12 -0
- package/dist/core/containers.js +5 -0
- package/dist/core/data-validation-overlay.d.ts +2 -1
- package/dist/core/data-validation-overlay.js +4 -4
- package/dist/core/data-validation.d.ts +9 -3
- package/dist/core/data-validation.js +21 -18
- package/dist/core/date-format.d.ts +20 -0
- package/dist/core/date-format.js +160 -0
- package/dist/core/date.d.ts +43 -8
- package/dist/core/date.js +18 -2
- package/dist/core/formula.d.ts +26 -0
- package/dist/core/formula.js +20 -4
- package/dist/core/grid-edits.d.ts +2 -3
- package/dist/core/grid-edits.js +69 -70
- package/dist/core/grid-shift.d.ts +58 -9
- package/dist/core/grid-shift.js +26 -4
- package/dist/core/image.d.ts +14 -10
- package/dist/core/image.js +9 -9
- package/dist/core/internal.d.ts +8 -2
- package/dist/core/limits.d.ts +23 -0
- package/dist/core/limits.js +4 -0
- package/dist/core/merge-index.d.ts +31 -0
- package/dist/core/merge-index.js +60 -0
- package/dist/core/merge.d.ts +5 -14
- package/dist/core/merge.js +18 -27
- package/dist/core/page-setup.d.ts +72 -2
- package/dist/core/page-setup.js +34 -8
- package/dist/core/pivot-table.d.ts +1 -1
- package/dist/core/pivot-table.js +67 -53
- package/dist/core/protection.js +2 -11
- package/dist/core/range.js +34 -30
- package/dist/core/row-input.js +2 -2
- package/dist/core/row.d.ts +5 -1
- package/dist/core/row.js +22 -23
- package/dist/core/style.d.ts +134 -75
- package/dist/core/style.js +112 -53
- package/dist/core/table-style.d.ts +1 -1
- package/dist/core/table-style.js +4 -4
- package/dist/core/table.d.ts +28 -10
- package/dist/core/table.js +54 -39
- package/dist/core/theme.js +6 -5
- package/dist/core/used-extent.d.ts +44 -0
- package/dist/core/used-extent.js +102 -0
- package/dist/core/value.d.ts +9 -0
- package/dist/core/value.js +36 -34
- package/dist/core/workbook-media.d.ts +34 -0
- package/dist/core/workbook-media.js +44 -0
- package/dist/core/workbook-styles.d.ts +67 -1
- package/dist/core/workbook-vba.js +3 -2
- package/dist/core/workbook.d.ts +23 -2
- package/dist/core/workbook.js +24 -40
- package/dist/core/worksheet-comments.d.ts +2 -1
- package/dist/core/worksheet-comments.js +20 -13
- package/dist/core/worksheet-merges.d.ts +44 -0
- package/dist/core/worksheet-merges.js +59 -0
- package/dist/core/worksheet-model.d.ts +9 -1
- package/dist/core/worksheet-model.js +24 -9
- package/dist/core/worksheet.d.ts +72 -12
- package/dist/core/worksheet.js +68 -70
- package/dist/customui/ribbon.js +6 -7
- package/dist/entries/core.d.ts +18 -13
- package/dist/entries/core.js +12 -6
- package/dist/entries/xlsx.d.ts +1 -1
- package/dist/errors.d.ts +64 -0
- package/dist/errors.js +22 -0
- package/dist/hex.d.ts +8 -0
- package/dist/hex.js +3 -0
- package/dist/io/csv/delimiter.d.ts +9 -0
- package/dist/io/csv/delimiter.js +6 -0
- package/dist/io/csv/read.js +11 -4
- package/dist/io/csv/write.d.ts +9 -2
- package/dist/io/csv/write.js +9 -38
- package/dist/io/opc/inflate.js +15 -1
- package/dist/io/opc/namespaces.d.ts +16 -0
- package/dist/io/opc/namespaces.js +4 -0
- package/dist/io/opc/part-paths.d.ts +10 -0
- package/dist/io/opc/part-paths.js +16 -1
- package/dist/io/opc/read-opc.d.ts +69 -10
- package/dist/io/opc/read-opc.js +49 -55
- package/dist/io/opc/read-options.d.ts +5 -1
- package/dist/io/opc/rels.d.ts +1 -6
- package/dist/io/opc/rels.js +1 -4
- package/dist/io/opc/sniff-format.d.ts +3 -3
- package/dist/io/opc/sniff-format.js +2 -2
- package/dist/io/style/xf-style.d.ts +48 -2
- package/dist/io/style/xf-style.js +14 -0
- package/dist/io/xlsb/formula.d.ts +2 -2
- package/dist/io/xlsb/formula.js +49 -34
- package/dist/io/xlsb/primitives.js +8 -14
- package/dist/io/xlsb/ptg-functions.d.ts +19 -10
- package/dist/io/xlsb/ptg-functions.js +385 -596
- package/dist/io/xlsb/read-styles.js +50 -83
- package/dist/io/xlsb/read-worksheet.d.ts +20 -1
- package/dist/io/xlsb/read-worksheet.js +73 -89
- package/dist/io/xlsb/read.d.ts +6 -3
- package/dist/io/xlsb/read.js +47 -29
- package/dist/io/xlsb/record-stream.d.ts +30 -0
- package/dist/io/xlsb/record-stream.js +20 -0
- package/dist/io/xlsb/record-types.d.ts +1 -5
- package/dist/io/xlsb/record-types.js +1 -5
- package/dist/io/xlsx/cell-accumulator.d.ts +9 -2
- package/dist/io/xlsx/cell-accumulator.js +32 -9
- package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
- package/dist/io/xlsx/cell-style-resolution.js +29 -0
- package/dist/io/xlsx/cell-value.d.ts +5 -3
- package/dist/io/xlsx/cell-value.js +13 -15
- package/dist/io/xlsx/color-xml.js +3 -2
- package/dist/io/xlsx/column-budget.d.ts +26 -0
- package/dist/io/xlsx/column-budget.js +22 -0
- package/dist/io/xlsx/comments.d.ts +6 -2
- package/dist/io/xlsx/comments.js +36 -31
- package/dist/io/xlsx/conditional-formatting.d.ts +9 -5
- package/dist/io/xlsx/conditional-formatting.js +51 -46
- package/dist/io/xlsx/data-validation.d.ts +9 -7
- package/dist/io/xlsx/data-validation.js +38 -35
- package/dist/io/xlsx/edit-vba.d.ts +7 -2
- package/dist/io/xlsx/edit-vba.js +15 -29
- package/dist/io/xlsx/font-xml.d.ts +7 -0
- package/dist/io/xlsx/font-xml.js +104 -0
- package/dist/io/xlsx/hyperlinks.d.ts +41 -7
- package/dist/io/xlsx/hyperlinks.js +40 -28
- package/dist/io/xlsx/images.js +24 -25
- package/dist/io/xlsx/namespaces.d.ts +16 -0
- package/dist/io/xlsx/namespaces.js +7 -0
- package/dist/io/xlsx/package-plan.d.ts +23 -3
- package/dist/io/xlsx/package-plan.js +66 -25
- package/dist/io/xlsx/part-names.d.ts +41 -0
- package/dist/io/xlsx/part-names.js +46 -0
- package/dist/io/xlsx/pivot.js +8 -7
- package/dist/io/xlsx/read-parts.d.ts +59 -0
- package/dist/io/xlsx/read-parts.js +252 -0
- package/dist/io/xlsx/read-pivot.js +3 -1
- package/dist/io/xlsx/read-repair.d.ts +26 -0
- package/dist/io/xlsx/read-repair.js +39 -0
- package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +1 -1
- package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +7 -3
- package/dist/io/xlsx/read-rows.d.ts +18 -5
- package/dist/io/xlsx/read-rows.js +50 -37
- package/dist/io/xlsx/read-shared-strings.js +5 -3
- package/dist/io/xlsx/read-styles.d.ts +1 -6
- package/dist/io/xlsx/read-styles.js +65 -109
- package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
- package/dist/io/xlsx/read-workbook-xml.js +170 -0
- package/dist/io/xlsx/read-worksheet.d.ts +11 -1
- package/dist/io/xlsx/read-worksheet.js +51 -193
- package/dist/io/xlsx/read.d.ts +5 -13
- package/dist/io/xlsx/read.js +111 -436
- package/dist/io/xlsx/rich-text.js +1 -1
- package/dist/io/xlsx/row-position.d.ts +18 -0
- package/dist/io/xlsx/row-position.js +11 -0
- package/dist/io/xlsx/row-xml.d.ts +128 -0
- package/dist/io/xlsx/row-xml.js +203 -0
- package/dist/io/xlsx/shared-formulas.js +2 -2
- package/dist/io/xlsx/sheet-properties.d.ts +10 -1
- package/dist/io/xlsx/sheet-properties.js +166 -37
- package/dist/io/xlsx/style-elements.d.ts +43 -0
- package/dist/io/xlsx/style-elements.js +156 -0
- package/dist/io/xlsx/styles.d.ts +12 -3
- package/dist/io/xlsx/styles.js +5 -204
- package/dist/io/xlsx/tables.js +11 -14
- package/dist/io/xlsx/theme-xml.d.ts +26 -4
- package/dist/io/xlsx/theme-xml.js +107 -69
- package/dist/io/xlsx/threaded-comments.d.ts +8 -10
- package/dist/io/xlsx/threaded-comments.js +3 -1
- package/dist/io/xlsx/workbook-xml.d.ts +54 -4
- package/dist/io/xlsx/workbook-xml.js +84 -57
- package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
- package/dist/io/xlsx/worksheet-xml.js +59 -242
- package/dist/io/xlsx/write-stream.d.ts +71 -16
- package/dist/io/xlsx/write-stream.js +79 -41
- package/dist/io/xlsx/write.d.ts +6 -2
- package/dist/io/xlsx/write.js +217 -153
- package/dist/rel-type.d.ts +16 -0
- package/dist/rel-type.js +6 -0
- package/dist/token-set.d.ts +28 -0
- package/dist/token-set.js +7 -0
- package/dist/vba/bytes.d.ts +14 -5
- package/dist/vba/bytes.js +16 -6
- package/dist/vba/cfb-format.d.ts +32 -0
- package/dist/vba/cfb-format.js +13 -0
- package/dist/vba/cfb-writer.d.ts +9 -0
- package/dist/vba/cfb-writer.js +28 -25
- package/dist/vba/cfb.d.ts +13 -1
- package/dist/vba/cfb.js +73 -57
- package/dist/vba/codepage.js +2 -1
- package/dist/vba/errors.d.ts +7 -0
- package/dist/vba/ms-ovba.js +79 -23
- package/dist/vba/project-editor.js +35 -40
- package/dist/vba/project.d.ts +11 -1
- package/dist/vba/project.js +40 -21
- package/dist/vba/vba-encoding.js +5 -6
- package/dist/xml/xml-attrs.d.ts +37 -0
- package/dist/xml/xml-attrs.js +30 -0
- package/dist/xml/xml-chars.d.ts +40 -0
- package/dist/xml/xml-chars.js +12 -0
- package/dist/xml/xml-namespaces.d.ts +31 -0
- package/dist/xml/xml-namespaces.js +50 -0
- package/dist/xml/xml-read.d.ts +128 -81
- package/dist/xml/xml-read.js +161 -178
- package/dist/xml/xml-scan.d.ts +78 -0
- package/dist/xml/xml-scan.js +180 -0
- package/dist/xml/xml.d.ts +29 -28
- package/dist/xml/xml.js +24 -24
- package/package.json +13 -9
|
@@ -3,13 +3,15 @@ import type { AutoFilter } from '../../core/autofilter.ts';
|
|
|
3
3
|
import type { Cell } from '../../core/cell.ts';
|
|
4
4
|
import type { ConditionalFormatting } from '../../core/conditional-formatting.ts';
|
|
5
5
|
import type { DataValidation } from '../../core/data-validation.ts';
|
|
6
|
+
import type { DateEpoch } from '../../core/date.ts';
|
|
6
7
|
import type { AnchorPoint } from '../../core/image.ts';
|
|
8
|
+
import { INTERNAL } from '../../core/internal.ts';
|
|
7
9
|
import type { SheetProtectionOptions } from '../../core/protection.ts';
|
|
8
10
|
import { type CellValue } from '../../core/value.ts';
|
|
9
11
|
import { type AddImageOptions, type AddWorksheetOptions, Workbook } from '../../core/workbook.ts';
|
|
10
12
|
import type { Worksheet } from '../../core/worksheet.ts';
|
|
13
|
+
import { type FlushedSheet } from './row-xml.ts';
|
|
11
14
|
import type { StyleRegistry } from './styles.ts';
|
|
12
|
-
import { type FlushedSheet } from './write.ts';
|
|
13
15
|
/** Calculation settings applied to the streamed workbook. Mirrors the {@link Workbook} flags. */
|
|
14
16
|
export interface CalcProperties {
|
|
15
17
|
/** Ask the consumer to recalculate every formula on open: the OOXML `fullCalcOnLoad` flag. */
|
|
@@ -45,6 +47,13 @@ export type WorkbookStreamWriterOptions = SinkOptions & {
|
|
|
45
47
|
* same {@link WriteOptions.useSharedStrings} the buffered writer exposes. Off by default.
|
|
46
48
|
*/
|
|
47
49
|
readonly useSharedStrings?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Which date system this workbook's serials count in ({@link Workbook.dateEpoch}). A construction
|
|
52
|
+
* option rather than a settable property, unlike on the buffered writer: an eager writer serialises
|
|
53
|
+
* each row as it is committed, so a system changed part-way through would leave the rows before the
|
|
54
|
+
* change counting from a different day than the rows after it.
|
|
55
|
+
*/
|
|
56
|
+
readonly dateEpoch?: DateEpoch;
|
|
48
57
|
};
|
|
49
58
|
/**
|
|
50
59
|
* A row appended to a {@link WorksheetStreamWriter}. Style its cells through {@link cells}, then call
|
|
@@ -68,7 +77,13 @@ export declare class StreamedRow {
|
|
|
68
77
|
*/
|
|
69
78
|
export declare class WorksheetStreamWriter {
|
|
70
79
|
#private;
|
|
71
|
-
constructor(
|
|
80
|
+
private constructor();
|
|
81
|
+
/**
|
|
82
|
+
* The workbook writer's construction channel. A static member keyed by the same symbol as the
|
|
83
|
+
* instance one below, so both halves of this class's internal wiring are reached the one way; see
|
|
84
|
+
* `core/internal.ts`.
|
|
85
|
+
*/
|
|
86
|
+
static readonly [INTERNAL]: WorksheetStreamWriterFactory;
|
|
72
87
|
/** The sheet's name. */
|
|
73
88
|
get name(): string;
|
|
74
89
|
/** The number of rows written so far. Spans gaps and formatted-only rows, like the model, and
|
|
@@ -78,16 +93,6 @@ export declare class WorksheetStreamWriter {
|
|
|
78
93
|
addRow(values: CellValue[]): StreamedRow;
|
|
79
94
|
/** Append a batch of rows in one call, each landing directly below the previous. */
|
|
80
95
|
addRows(rows: CellValue[][]): StreamedRow[];
|
|
81
|
-
/**
|
|
82
|
-
* Serialise an eagerly-committed row and release its cells from the model. Called by
|
|
83
|
-
* {@link StreamedRow.commit}; the row's `<row>` XML is retained (interned into the workbook's live
|
|
84
|
-
* style registry so its ids stay valid) and the cell graph is dropped, bounding peak memory.
|
|
85
|
-
*
|
|
86
|
-
* @throws {AuthoringError} if the row carries a shared-formula cell: a finished row cannot join the
|
|
87
|
-
* whole-sheet formula planning, so shared formulas must be authored through {@link getCell}.
|
|
88
|
-
*/
|
|
89
|
-
flushRow(number: number, cells: readonly Cell[]): void;
|
|
90
|
-
flushedSheet(): FlushedSheet | undefined;
|
|
91
96
|
/**
|
|
92
97
|
* Address a cell by its A1 reference to read or style it before the sheet is committed. The row it
|
|
93
98
|
* names must still be live: a row whose {@link StreamedRow.commit} has run is finished.
|
|
@@ -139,6 +144,41 @@ export declare class WorksheetStreamWriter {
|
|
|
139
144
|
/** Whether the sheet has been committed. */
|
|
140
145
|
get committed(): boolean;
|
|
141
146
|
get model(): Worksheet;
|
|
147
|
+
readonly [INTERNAL]: WorksheetStreamWriterInternals;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* How a {@link WorksheetStreamWriter} comes into being. Its parameters are the writer's own plumbing
|
|
151
|
+
* -- the interning table its rows' style ids are assigned from, the epoch its dates are serialised
|
|
152
|
+
* against -- and a consumer supplies neither, because a consumer receives the sheet writer from
|
|
153
|
+
* {@link WorkbookStreamWriter.addWorksheet}. Reached as `WorksheetStreamWriter[INTERNAL]`.
|
|
154
|
+
*/
|
|
155
|
+
export interface WorksheetStreamWriterFactory {
|
|
156
|
+
create(sheet: Worksheet, eager: boolean, styles: StyleRegistry, dateEpoch: DateEpoch): WorksheetStreamWriter;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* What the streaming writer's own machinery may do to a sheet writer that a caller may not, the same
|
|
160
|
+
* boundary `sheet[INTERNAL]` draws around the model. Reached as `writer[INTERNAL]`; see
|
|
161
|
+
* `core/internal.ts`.
|
|
162
|
+
*
|
|
163
|
+
* Both members were public until they were not. Neither is callable by anyone but this module, and
|
|
164
|
+
* between them they named seven writer-internal types -- `StyleRegistry`, `FlushedSheet` and the
|
|
165
|
+
* shapes those reach -- on a published class's signature, so a consumer could hold values whose types
|
|
166
|
+
* the package would not export. They carried `@unpublished` to record that, which is a note rather
|
|
167
|
+
* than a boundary; this is the boundary.
|
|
168
|
+
*/
|
|
169
|
+
export interface WorksheetStreamWriterInternals {
|
|
170
|
+
/**
|
|
171
|
+
* Serialise an eagerly-committed row and release its cells from the model. Called by
|
|
172
|
+
* {@link StreamedRow.commit}; the row's `<row>` XML is retained (interned into the workbook's live
|
|
173
|
+
* style registry so its ids stay valid) and the cell graph is dropped, bounding peak memory.
|
|
174
|
+
*
|
|
175
|
+
* @throws {AuthoringError} if the row carries a shared-formula cell: a finished row cannot join the
|
|
176
|
+
* whole-sheet formula planning, so shared formulas must be authored through
|
|
177
|
+
* {@link WorksheetStreamWriter.getCell}.
|
|
178
|
+
*/
|
|
179
|
+
flushRow(number: number, cells: readonly Cell[]): void;
|
|
180
|
+
/** The rows this writer flushed, or undefined if none. Handed to `buildPackageParts` at commit. */
|
|
181
|
+
flushedSheet(): FlushedSheet | undefined;
|
|
142
182
|
}
|
|
143
183
|
/**
|
|
144
184
|
* A workbook written incrementally to a Node stream. Add worksheets, append their rows, commit each
|
|
@@ -169,10 +209,25 @@ export declare class WorkbookStreamWriter {
|
|
|
169
209
|
/** Create a worksheet and append it to the workbook. */
|
|
170
210
|
addWorksheet(name: string, options?: AddWorksheetOptions): WorksheetStreamWriter;
|
|
171
211
|
/**
|
|
172
|
-
* Assemble the workbook into its package
|
|
173
|
-
*
|
|
174
|
-
*
|
|
212
|
+
* Assemble the workbook into its package and stream the bytes out. Every sheet is frozen first, so
|
|
213
|
+
* a row added after this rejects legibly. Idempotent only in that a second call throws rather than
|
|
214
|
+
* re-emitting.
|
|
215
|
+
*
|
|
216
|
+
* **What it resolves with, and why it can be `undefined`.** The archive is handed back only when
|
|
217
|
+
* somebody asked for it: when no sink was supplied, or when {@link stream} was touched, since a
|
|
218
|
+
* `PassThrough` nobody drained would otherwise be the only copy. A caller who passed `{filename}`
|
|
219
|
+
* or their own `stream` and never reached for {@link stream} gets `undefined`, and the package is
|
|
220
|
+
* never materialised as a whole: retaining every chunk to concatenate them at the end costs a
|
|
221
|
+
* second full-size buffer on top of the part map and the archive itself, which is exactly the
|
|
222
|
+
* memory the caller passed a sink to avoid. The type says so rather than the prose alone, because
|
|
223
|
+
* a promise that resolves with a value the caller was told to ignore is a promise they will use.
|
|
224
|
+
*
|
|
225
|
+
* If assembling or zipping the package fails, the returned promise rejects *and* every stream this
|
|
226
|
+
* writer was given or handed out is destroyed with that error. A caller piping {@link stream}, or
|
|
227
|
+
* awaiting its own sink, therefore sees an `error` rather than waiting on a stream that will never
|
|
228
|
+
* end; and it is an `error` rather than a clean `end` because the bytes written so far are a
|
|
229
|
+
* truncated package that must not be read as a whole one.
|
|
175
230
|
*/
|
|
176
|
-
commit(): Promise<Uint8Array>;
|
|
231
|
+
commit(): Promise<Uint8Array | undefined>;
|
|
177
232
|
}
|
|
178
233
|
export {};
|
|
@@ -6,9 +6,12 @@ import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
|
|
|
6
6
|
import { INTERNAL } from '../../core/internal.js';
|
|
7
7
|
import { isSharedFormulaValue } from '../../core/value.js';
|
|
8
8
|
import { Workbook } from '../../core/workbook.js';
|
|
9
|
-
import { AuthoringError } from '../../errors.js';
|
|
9
|
+
import { AuthoringError, quoted } from '../../errors.js';
|
|
10
10
|
import { FIXED_ENTRY_MTIME } from '../opc/zip-mtime.js';
|
|
11
|
-
import {
|
|
11
|
+
import { collectNotes } from './comments.js';
|
|
12
|
+
import { collectHyperlinks } from './hyperlinks.js';
|
|
13
|
+
import { buildColumnDefaults, Extent, renderRow, } from './row-xml.js';
|
|
14
|
+
import { buildPackageParts, createStyleRegistry } from './write.js';
|
|
12
15
|
export class StreamedRow {
|
|
13
16
|
#cells;
|
|
14
17
|
#sheet;
|
|
@@ -26,7 +29,7 @@ export class StreamedRow {
|
|
|
26
29
|
if (this.#committed)
|
|
27
30
|
return;
|
|
28
31
|
this.#committed = true;
|
|
29
|
-
this.#sheet?.flushRow(this.#number, this.#cells);
|
|
32
|
+
this.#sheet?.[INTERNAL].flushRow(this.#number, this.#cells);
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
35
|
export class WorksheetStreamWriter {
|
|
@@ -39,12 +42,21 @@ export class WorksheetStreamWriter {
|
|
|
39
42
|
#flushedRows = [];
|
|
40
43
|
#flushedNumbers = new Set();
|
|
41
44
|
#extent = new Extent();
|
|
42
|
-
#
|
|
43
|
-
|
|
45
|
+
#rowOutline = new Map();
|
|
46
|
+
#hyperlinks = [];
|
|
47
|
+
#notes = [];
|
|
48
|
+
#dateEpoch;
|
|
49
|
+
constructor(sheet, eager, styles, dateEpoch) {
|
|
44
50
|
this.#sheet = sheet;
|
|
45
51
|
this.#eager = eager;
|
|
46
52
|
this.#styles = styles;
|
|
53
|
+
this.#dateEpoch = dateEpoch;
|
|
47
54
|
}
|
|
55
|
+
static [INTERNAL] = {
|
|
56
|
+
create(sheet, eager, styles, dateEpoch) {
|
|
57
|
+
return new WorksheetStreamWriter(sheet, eager, styles, dateEpoch);
|
|
58
|
+
},
|
|
59
|
+
};
|
|
48
60
|
get name() {
|
|
49
61
|
return this.#sheet.name;
|
|
50
62
|
}
|
|
@@ -77,45 +89,53 @@ export class WorksheetStreamWriter {
|
|
|
77
89
|
});
|
|
78
90
|
return cells;
|
|
79
91
|
}
|
|
80
|
-
flushRow(number, cells) {
|
|
92
|
+
#flushRow(number, cells) {
|
|
81
93
|
for (const cell of cells) {
|
|
82
94
|
if (isSharedFormulaValue(cell.value)) {
|
|
83
|
-
throw new AuthoringError(`row ${number} of streamed sheet
|
|
95
|
+
throw new AuthoringError(`row ${number} of streamed sheet ${quoted(this.#sheet.name)} carries a shared-formula cell; a ` +
|
|
84
96
|
'committed row is finalised before the sheet is planned, so author shared formulas through ' +
|
|
85
97
|
'getCell (leaving the row uncommitted) instead');
|
|
86
98
|
}
|
|
87
99
|
}
|
|
88
100
|
this.#columnDefaults ??= buildColumnDefaults(this.#sheet);
|
|
89
101
|
const properties = this.#sheet.getRow(number).properties;
|
|
90
|
-
this.#
|
|
91
|
-
|
|
102
|
+
this.#rowOutline.set(number, {
|
|
103
|
+
outlineLevel: properties?.outlineLevel ?? 0,
|
|
104
|
+
hidden: properties?.hidden ?? false,
|
|
105
|
+
});
|
|
106
|
+
this.#hyperlinks.push(...collectHyperlinks(cells));
|
|
107
|
+
this.#notes.push(...collectNotes(cells));
|
|
108
|
+
const { xml, attrs, minCol, maxCol } = renderRow({ number, cells, properties }, {
|
|
92
109
|
columnDefaults: this.#columnDefaults,
|
|
93
110
|
styles: this.#styles,
|
|
94
111
|
sharedStrings: null,
|
|
95
112
|
sharedRoles: new Map(),
|
|
96
113
|
collapsedSummaries: new Set(),
|
|
114
|
+
dateEpoch: this.#dateEpoch,
|
|
97
115
|
});
|
|
98
116
|
if (xml !== '') {
|
|
99
|
-
this.#flushedRows.push({ number, xml });
|
|
117
|
+
this.#flushedRows.push({ number, xml, attrs });
|
|
100
118
|
this.#extent.add(number, minCol, maxCol);
|
|
101
119
|
}
|
|
102
120
|
this.#flushedNumbers.add(number);
|
|
103
121
|
this.#sheet[INTERNAL].evictRow(number);
|
|
104
122
|
}
|
|
105
|
-
flushedSheet() {
|
|
123
|
+
#flushedSheet() {
|
|
106
124
|
if (this.#flushedRows.length === 0)
|
|
107
125
|
return undefined;
|
|
108
126
|
return {
|
|
109
127
|
rows: this.#flushedRows,
|
|
110
128
|
extent: this.#extent,
|
|
111
|
-
|
|
129
|
+
rowOutline: this.#rowOutline,
|
|
130
|
+
hyperlinks: this.#hyperlinks,
|
|
131
|
+
notes: this.#notes,
|
|
112
132
|
};
|
|
113
133
|
}
|
|
114
134
|
getCell(reference) {
|
|
115
135
|
this.#assertOpen();
|
|
116
136
|
const target = tryDecodeCellRef(reference);
|
|
117
137
|
if (target !== undefined && this.#flushedNumbers.has(target.row)) {
|
|
118
|
-
throw new AuthoringError(`cell ${reference} of streamed sheet
|
|
138
|
+
throw new AuthoringError(`cell ${reference} of streamed sheet ${quoted(this.#sheet.name)} is in row ${target.row}, which is ` +
|
|
119
139
|
'already committed: its bytes are written and its cells released, so this value could only ' +
|
|
120
140
|
'be emitted as a second row with that number. Style a row before committing it.');
|
|
121
141
|
}
|
|
@@ -153,9 +173,15 @@ export class WorksheetStreamWriter {
|
|
|
153
173
|
get model() {
|
|
154
174
|
return this.#sheet;
|
|
155
175
|
}
|
|
176
|
+
[INTERNAL] = {
|
|
177
|
+
flushRow: (number, cells) => {
|
|
178
|
+
this.#flushRow(number, cells);
|
|
179
|
+
},
|
|
180
|
+
flushedSheet: () => this.#flushedSheet(),
|
|
181
|
+
};
|
|
156
182
|
#assertOpen() {
|
|
157
183
|
if (this.#committed) {
|
|
158
|
-
throw new AuthoringError(`worksheet
|
|
184
|
+
throw new AuthoringError(`worksheet ${quoted(this.#sheet.name)} is already committed: its rows are finalised and no more can be added`);
|
|
159
185
|
}
|
|
160
186
|
}
|
|
161
187
|
}
|
|
@@ -172,6 +198,8 @@ export class WorkbookStreamWriter {
|
|
|
172
198
|
constructor(options = {}) {
|
|
173
199
|
this.#writeOptions = { useSharedStrings: options.useSharedStrings ?? false };
|
|
174
200
|
this.#eager = !this.#writeOptions.useSharedStrings;
|
|
201
|
+
if (options.dateEpoch !== undefined)
|
|
202
|
+
this.#workbook.dateEpoch = options.dateEpoch;
|
|
175
203
|
this.#styles = createStyleRegistry(this.#workbook);
|
|
176
204
|
if (options.stream && options.filename) {
|
|
177
205
|
throw new AuthoringError('provide either a stream or a filename to the streaming writer, not both');
|
|
@@ -187,23 +215,17 @@ export class WorkbookStreamWriter {
|
|
|
187
215
|
return this.#stream;
|
|
188
216
|
}
|
|
189
217
|
addImage(options) {
|
|
190
|
-
|
|
191
|
-
throw new AuthoringError('the workbook is already committed: no more images can be registered');
|
|
192
|
-
}
|
|
218
|
+
this.#assertOpen('no more images can be registered');
|
|
193
219
|
return this.#workbook.addImage(options);
|
|
194
220
|
}
|
|
195
221
|
addWorksheet(name, options = {}) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
const sheet = new WorksheetStreamWriter(this.#workbook.addWorksheet(name, options), this.#eager, this.#styles);
|
|
222
|
+
this.#assertOpen('no more worksheets can be added');
|
|
223
|
+
const sheet = WorksheetStreamWriter[INTERNAL].create(this.#workbook.addWorksheet(name, options), this.#eager, this.#styles, this.#workbook.dateEpoch);
|
|
200
224
|
this.#sheets.push(sheet);
|
|
201
225
|
return sheet;
|
|
202
226
|
}
|
|
203
227
|
async commit() {
|
|
204
|
-
|
|
205
|
-
throw new AuthoringError('the workbook is already committed');
|
|
206
|
-
}
|
|
228
|
+
this.#assertOpen('its package is already assembled and cannot be re-emitted');
|
|
207
229
|
this.#committed = true;
|
|
208
230
|
for (const sheet of this.#sheets)
|
|
209
231
|
sheet.commit();
|
|
@@ -211,26 +233,41 @@ export class WorkbookStreamWriter {
|
|
|
211
233
|
this.#workbook.fullCalcOnLoad = true;
|
|
212
234
|
const flushed = new Map();
|
|
213
235
|
for (const sheet of this.#sheets) {
|
|
214
|
-
const sheetFlushed = sheet.flushedSheet();
|
|
236
|
+
const sheetFlushed = sheet[INTERNAL].flushedSheet();
|
|
215
237
|
if (sheetFlushed)
|
|
216
238
|
flushed.set(sheet.model, sheetFlushed);
|
|
217
239
|
}
|
|
218
|
-
const parts = buildPackageParts(this.#workbook, {
|
|
219
|
-
...this.#writeOptions,
|
|
220
|
-
styles: this.#styles,
|
|
221
|
-
flushed,
|
|
222
|
-
});
|
|
223
240
|
const owned = this.#stream;
|
|
224
241
|
const sink = this.#sink;
|
|
242
|
+
const collect = sink === undefined || owned !== undefined;
|
|
225
243
|
const sinkSettled = sink ? settleOnFinish(sink) : undefined;
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
244
|
+
try {
|
|
245
|
+
const parts = buildPackageParts(this.#workbook, {
|
|
246
|
+
...this.#writeOptions,
|
|
247
|
+
styles: this.#styles,
|
|
248
|
+
flushed,
|
|
249
|
+
});
|
|
250
|
+
const bytes = await streamZipPackage(parts, (chunk) => {
|
|
251
|
+
owned?.write(chunk);
|
|
252
|
+
sink?.write(chunk);
|
|
253
|
+
}, collect);
|
|
254
|
+
owned?.end();
|
|
255
|
+
sink?.end();
|
|
256
|
+
await sinkSettled;
|
|
257
|
+
return bytes;
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
261
|
+
sinkSettled?.catch(() => { });
|
|
262
|
+
owned?.destroy(failure);
|
|
263
|
+
sink?.destroy(failure);
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
#assertOpen(what) {
|
|
268
|
+
if (this.#committed) {
|
|
269
|
+
throw new AuthoringError(`the workbook is already committed: ${what}`);
|
|
270
|
+
}
|
|
234
271
|
}
|
|
235
272
|
}
|
|
236
273
|
function settleOnFinish(sink) {
|
|
@@ -239,7 +276,7 @@ function settleOnFinish(sink) {
|
|
|
239
276
|
sink.once('error', reject);
|
|
240
277
|
});
|
|
241
278
|
}
|
|
242
|
-
function streamZipPackage(parts, onChunk) {
|
|
279
|
+
function streamZipPackage(parts, onChunk, collect) {
|
|
243
280
|
return new Promise((resolve, reject) => {
|
|
244
281
|
const collected = [];
|
|
245
282
|
const zip = new Zip((err, chunk, final) => {
|
|
@@ -247,10 +284,11 @@ function streamZipPackage(parts, onChunk) {
|
|
|
247
284
|
reject(err);
|
|
248
285
|
return;
|
|
249
286
|
}
|
|
250
|
-
|
|
287
|
+
if (collect)
|
|
288
|
+
collected.push(chunk);
|
|
251
289
|
onChunk(chunk);
|
|
252
290
|
if (final)
|
|
253
|
-
resolve(concat(collected));
|
|
291
|
+
resolve(collect ? concat(collected) : undefined);
|
|
254
292
|
});
|
|
255
293
|
for (const [name, data] of Object.entries(parts)) {
|
|
256
294
|
const entry = new ZipDeflate(name, { level: 6 });
|
package/dist/io/xlsx/write.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { Workbook } from '../../core/workbook.ts';
|
|
2
2
|
import type { Worksheet } from '../../core/worksheet.ts';
|
|
3
|
+
import type { FlushedSheet } from './row-xml.ts';
|
|
3
4
|
import { StyleRegistry } from './styles.ts';
|
|
4
|
-
import { type FlushedSheet } from './worksheet-xml.ts';
|
|
5
|
-
export { buildColumnDefaults, Extent, type FlushedSheet, type RowRenderContext, renderRow, } from './worksheet-xml.ts';
|
|
6
5
|
/** Options controlling how {@link writeXlsx} serialises a workbook. */
|
|
7
6
|
export interface WriteOptions {
|
|
8
7
|
/**
|
|
@@ -81,6 +80,11 @@ export declare function createStyleRegistry(workbook: Workbook): StyleRegistry;
|
|
|
81
80
|
* streaming writer can drive the identical parts through a streamed zip container rather than
|
|
82
81
|
* `zipSync`. Neither writer duplicates a byte of serialisation.
|
|
83
82
|
*
|
|
83
|
+
* Three steps, and their order is load-bearing. The package is planned in full before anything is
|
|
84
|
+
* serialised, so a drawing's embed and a pivot's cache can target a part number that already exists.
|
|
85
|
+
* The sheets are serialised before the parts are emitted, because interning a style is a side effect
|
|
86
|
+
* of that pass and `xl/styles.xml` is one of the parts emitted in the third step.
|
|
87
|
+
*
|
|
84
88
|
* @throws {AuthoringError} if the workbook has no worksheets, or holds a value the writer cannot represent.
|
|
85
89
|
*/
|
|
86
90
|
export declare function buildPackageParts(workbook: Workbook, options?: InternalWriteOptions): Record<string, Uint8Array>;
|