@shbernal/ts-xlsx 2.0.0 → 3.0.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.
Files changed (147) hide show
  1. package/README.md +20 -10
  2. package/dist/bytes.d.ts +24 -0
  3. package/dist/bytes.js +34 -0
  4. package/dist/core/address.d.ts +26 -4
  5. package/dist/core/address.js +21 -9
  6. package/dist/core/autofilter.d.ts +12 -1
  7. package/dist/core/autofilter.js +35 -12
  8. package/dist/core/axis-handle.d.ts +8 -0
  9. package/dist/core/axis-handle.js +14 -0
  10. package/dist/core/cell.d.ts +24 -0
  11. package/dist/core/cell.js +9 -1
  12. package/dist/core/color-resolution.d.ts +1 -1
  13. package/dist/core/color-resolution.js +7 -8
  14. package/dist/core/column.d.ts +4 -1
  15. package/dist/core/column.js +31 -35
  16. package/dist/core/conditional-formatting-overlay.d.ts +5 -0
  17. package/dist/core/conditional-formatting-overlay.js +11 -0
  18. package/dist/core/conditional-formatting.d.ts +45 -5
  19. package/dist/core/conditional-formatting.js +74 -0
  20. package/dist/core/data-validation-overlay.d.ts +11 -0
  21. package/dist/core/data-validation-overlay.js +21 -2
  22. package/dist/core/data-validation.d.ts +9 -2
  23. package/dist/core/data-validation.js +26 -0
  24. package/dist/core/grid-edits.d.ts +16 -0
  25. package/dist/core/grid-edits.js +26 -8
  26. package/dist/core/grid-shift.d.ts +19 -0
  27. package/dist/core/grid-shift.js +8 -0
  28. package/dist/core/image.d.ts +2 -0
  29. package/dist/core/image.js +2 -0
  30. package/dist/core/merge-index.d.ts +31 -0
  31. package/dist/core/merge-index.js +60 -0
  32. package/dist/core/merge.d.ts +12 -11
  33. package/dist/core/merge.js +36 -10
  34. package/dist/core/page-setup.d.ts +86 -2
  35. package/dist/core/page-setup.js +34 -1
  36. package/dist/core/pivot-table.d.ts +2 -0
  37. package/dist/core/pivot-table.js +23 -18
  38. package/dist/core/protection.d.ts +3 -0
  39. package/dist/core/protection.js +20 -14
  40. package/dist/core/row-input.d.ts +7 -0
  41. package/dist/core/row-input.js +10 -8
  42. package/dist/core/row.d.ts +4 -1
  43. package/dist/core/row.js +19 -23
  44. package/dist/core/style.d.ts +78 -7
  45. package/dist/core/style.js +90 -80
  46. package/dist/core/table.d.ts +26 -12
  47. package/dist/core/table.js +67 -33
  48. package/dist/core/theme.d.ts +3 -1
  49. package/dist/core/theme.js +4 -0
  50. package/dist/core/used-extent.d.ts +44 -0
  51. package/dist/core/used-extent.js +102 -0
  52. package/dist/core/workbook.d.ts +2 -2
  53. package/dist/core/worksheet-comments.d.ts +8 -0
  54. package/dist/core/worksheet-comments.js +18 -0
  55. package/dist/core/worksheet.d.ts +31 -7
  56. package/dist/core/worksheet.js +72 -86
  57. package/dist/customui/ribbon.js +23 -24
  58. package/dist/entries/core.d.ts +3 -3
  59. package/dist/entries/csv.d.ts +1 -1
  60. package/dist/entries/node-unavailable.d.ts +15 -0
  61. package/dist/entries/node-unavailable.js +20 -0
  62. package/dist/entries/node.d.ts +1 -0
  63. package/dist/entries/node.js +1 -0
  64. package/dist/entries/xlsx.d.ts +0 -1
  65. package/dist/entries/xlsx.js +0 -1
  66. package/dist/io/csv/read.js +1 -1
  67. package/dist/io/csv/write.d.ts +12 -2
  68. package/dist/io/csv/write.js +17 -2
  69. package/dist/io/opc/inflate.js +3 -13
  70. package/dist/io/opc/part-paths.d.ts +1 -0
  71. package/dist/io/opc/part-paths.js +15 -0
  72. package/dist/io/opc/read-opc.d.ts +22 -3
  73. package/dist/io/opc/read-opc.js +11 -39
  74. package/dist/io/opc/rels.d.ts +12 -6
  75. package/dist/io/opc/rels.js +3 -5
  76. package/dist/io/style/xf-style.d.ts +33 -4
  77. package/dist/io/style/xf-style.js +12 -1
  78. package/dist/io/xlsb/read-styles.js +6 -3
  79. package/dist/io/xlsb/read.d.ts +1 -1
  80. package/dist/io/xlsb/read.js +5 -8
  81. package/dist/io/xlsx/cell-accumulator.d.ts +2 -4
  82. package/dist/io/xlsx/cell-accumulator.js +20 -58
  83. package/dist/io/xlsx/cell-value.d.ts +1 -1
  84. package/dist/io/xlsx/cell-value.js +7 -4
  85. package/dist/io/xlsx/color-xml.d.ts +1 -1
  86. package/dist/io/xlsx/color-xml.js +8 -7
  87. package/dist/io/xlsx/comments.js +6 -3
  88. package/dist/io/xlsx/conditional-formatting.d.ts +4 -3
  89. package/dist/io/xlsx/conditional-formatting.js +81 -65
  90. package/dist/io/xlsx/data-validation.d.ts +10 -8
  91. package/dist/io/xlsx/data-validation.js +44 -41
  92. package/dist/io/xlsx/edit-vba.js +4 -11
  93. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  94. package/dist/io/xlsx/hyperlinks.js +26 -31
  95. package/dist/io/xlsx/images.js +33 -35
  96. package/dist/io/xlsx/package-plan.d.ts +9 -1
  97. package/dist/io/xlsx/package-plan.js +29 -16
  98. package/dist/io/xlsx/part-names.d.ts +41 -0
  99. package/dist/io/xlsx/part-names.js +46 -0
  100. package/dist/io/xlsx/read-pivot.js +4 -9
  101. package/dist/io/xlsx/read-rows.js +12 -13
  102. package/dist/io/xlsx/read-shared-strings.js +9 -47
  103. package/dist/io/xlsx/read-styles.d.ts +1 -1
  104. package/dist/io/xlsx/read-styles.js +65 -43
  105. package/dist/io/xlsx/read-worksheet.d.ts +10 -1
  106. package/dist/io/xlsx/read-worksheet.js +48 -63
  107. package/dist/io/xlsx/read.d.ts +3 -4
  108. package/dist/io/xlsx/read.js +124 -146
  109. package/dist/io/xlsx/rich-runs.d.ts +41 -7
  110. package/dist/io/xlsx/rich-runs.js +95 -30
  111. package/dist/io/xlsx/sheet-properties.d.ts +1 -1
  112. package/dist/io/xlsx/sheet-properties.js +23 -26
  113. package/dist/io/xlsx/styles.d.ts +3 -15
  114. package/dist/io/xlsx/styles.js +83 -105
  115. package/dist/io/xlsx/tables.js +14 -13
  116. package/dist/io/xlsx/theme-xml.js +8 -5
  117. package/dist/io/xlsx/threaded-comments.js +20 -19
  118. package/dist/io/xlsx/workbook-xml.js +43 -36
  119. package/dist/io/xlsx/worksheet-xml.d.ts +1 -1
  120. package/dist/io/xlsx/worksheet-xml.js +47 -52
  121. package/dist/io/xlsx/write-stream.d.ts +14 -1
  122. package/dist/io/xlsx/write-stream.js +40 -33
  123. package/dist/io/xlsx/write.d.ts +5 -0
  124. package/dist/io/xlsx/write.js +155 -131
  125. package/dist/sha512.d.ts +2 -0
  126. package/dist/sha512.js +146 -0
  127. package/dist/token-set.d.ts +15 -0
  128. package/dist/token-set.js +4 -0
  129. package/dist/vba/bytes.d.ts +1 -2
  130. package/dist/vba/bytes.js +1 -12
  131. package/dist/vba/cfb-format.d.ts +24 -0
  132. package/dist/vba/cfb-format.js +12 -0
  133. package/dist/vba/cfb-writer.js +17 -17
  134. package/dist/vba/cfb.js +1 -7
  135. package/dist/vba/dir-records.d.ts +53 -0
  136. package/dist/vba/dir-records.js +28 -0
  137. package/dist/vba/ms-ovba.js +32 -11
  138. package/dist/vba/project-editor.js +5 -34
  139. package/dist/vba/project.js +15 -31
  140. package/dist/vba/vba-encoding.js +1 -1
  141. package/dist/xml/xml-read.d.ts +117 -72
  142. package/dist/xml/xml-read.js +123 -169
  143. package/dist/xml/xml-scan.d.ts +100 -0
  144. package/dist/xml/xml-scan.js +208 -0
  145. package/dist/xml/xml.d.ts +44 -2
  146. package/dist/xml/xml.js +17 -5
  147. package/package.json +22 -6
@@ -15,9 +15,19 @@ import { Row } from './row.ts';
15
15
  import type { CellStyle, Color, Fill } from './style.ts';
16
16
  import { Table, type TableOptions } from './table.ts';
17
17
  import type { CellValue } from './value.ts';
18
+ /**
19
+ * Whether a thing Excel can hide is showing: a sheet's tab, or the document window itself.
20
+ *
21
+ * One type for two schema enumerations. `ST_SheetState` and `ST_Visibility` are declared separately
22
+ * in ECMA-376 and carry the same three tokens with the same meanings, and `veryHidden` means the same
23
+ * thing in both: hidden, and not offered in the unhide list.
24
+ */
25
+ export type Visibility = 'visible' | 'hidden' | 'veryHidden';
26
+ /** Narrow a raw `<sheet state>` or `<workbookView visibility>` token to a known {@link Visibility}. */
27
+ export declare const isVisibility: (value: string) => value is Visibility;
18
28
  export interface WorksheetState {
19
29
  /** Sheet visibility, as Excel models it. Defaults to `visible`. */
20
- readonly state: 'visible' | 'hidden' | 'veryHidden';
30
+ readonly state: Visibility;
21
31
  }
22
32
  /** Format defaults applied to every row/column that carries no explicit override. */
23
33
  export interface WorksheetProperties {
@@ -266,14 +276,21 @@ export declare class Worksheet {
266
276
  * or 0 for an empty sheet. Spans gaps: a value in row 5 makes this 5 even if rows 2–4
267
277
  * are empty. This is the used-range extent, not a populated-row tally (see
268
278
  * {@link actualRowCount}).
279
+ *
280
+ * *Carrying anything* is deliberately wide: a value, a cell's own style facet, quote-prefix flag
281
+ * or named-style link, a note, a row height or outline level, or a merge reaching down. Styling a
282
+ * band of empty rows is how a template is laid out, so those rows are used and {@link addRow}
283
+ * appends past them. A cell merely materialised by {@link getCell} and left untouched carries
284
+ * nothing, so reading a far address never grows the sheet.
269
285
  */
270
286
  get rowCount(): number;
271
287
  /** The number of rows that hold at least one non-empty cell, ignoring gaps and formatting-only rows. */
272
288
  get actualRowCount(): number;
273
289
  /**
274
- * The 1-based index of the last column carrying anything (a non-empty cell or its own format
290
+ * The 1-based index of the last column carrying anything (a used cell or the column's own format
275
291
  * properties), or 0 for an empty sheet. The used-range width, mirroring {@link rowCount} for the
276
- * other axis: a value in column E makes this 5 even if columns B–D are empty.
292
+ * other axis, down to what *carrying anything* means: a value in column E makes this 5 even if
293
+ * columns B–D are empty, and a column holding nothing but a styled empty cell is still used.
277
294
  */
278
295
  get columnCount(): number;
279
296
  /**
@@ -538,9 +555,13 @@ export declare class Worksheet {
538
555
  /**
539
556
  * Copy the row at the 1-based `start`, `options.count` times (default 1). With `options.insert`
540
557
  * (the default) the copies are inserted directly after the source, shifting the rows below, and
541
- * any merged range there, down by `count`; otherwise the copies overwrite the rows immediately
542
- * below without shifting. Each copy is a faithful duplicate of the source's values and per-cell
543
- * styles, and carries no merge of its own, so a range can be merged onto a duplicated row afterwards.
558
+ * any merged range there, down by `count`; otherwise the copies *replace* the rows immediately
559
+ * below without shifting. A destination row is not overlaid but wholly re-made, so a cell it held
560
+ * in a column the source leaves empty is dropped, exactly as it would be with a shifting insert.
561
+ *
562
+ * Each copy is a faithful duplicate of the source: its cell values, its per-cell styles, and its
563
+ * row properties (height, hidden, outline level, row fill). It carries no merge of its own, so a
564
+ * range can be merged onto a duplicated row afterwards.
544
565
  *
545
566
  * @throws {RangeError} if `start` is not a positive integer or `count` is negative.
546
567
  */
@@ -574,7 +595,10 @@ export declare class Worksheet {
574
595
  * never disturbs merges or the columns to its left.
575
596
  *
576
597
  * `values` is an array indexed by row (index 0 → row 1); a hole or an explicit `undefined` leaves
577
- * that row untouched, mirroring {@link addRow}'s positional-array shape.
598
+ * that row untouched, mirroring {@link addRow}'s positional-array shape. That is the only shape a
599
+ * column takes: the other {@link RowInput} form addresses columns by their
600
+ * {@link ColumnProperties.key}, and a column's values are indexed by *row*, which carries no key,
601
+ * so there is nothing on this axis for a keyed object to name.
578
602
  */
579
603
  addColumn(values: CellValue[]): Cell[];
580
604
  /**
@@ -1,5 +1,6 @@
1
1
  import { AuthoringError } from '../errors.js';
2
- import { decodeCellRef, decodeRange, encodeAddress, rectsOverlap, tryDecodeCellRef, } from './address.js';
2
+ import { tokenSet } from '../token-set.js';
3
+ import { decodeCellRef, decodeRange, encodeAddress, tryDecodeCellRef } from './address.js';
3
4
  import { canonicalizeAutoFilter } from './autofilter.js';
4
5
  import { applyCellStyle, Cell, copyCellContent } from './cell.js';
5
6
  import { Column } from './column.js';
@@ -7,16 +8,19 @@ import { ConditionalFormattingOverlay } from './conditional-formatting-overlay.j
7
8
  import { DataValidationOverlay } from './data-validation-overlay.js';
8
9
  import { GridEdits } from './grid-edits.js';
9
10
  import { INTERNAL } from './internal.js';
10
- import { clearCoveredValues, masterOf } from './merge.js';
11
+ import { MergeIndex } from './merge-index.js';
12
+ import { clearCoveredValues } from './merge.js';
11
13
  import { PivotTable } from './pivot-table.js';
12
14
  import { deriveCredential, } from './protection.js';
13
15
  import { Range, rangeFrom } from './range.js';
14
- import { buildRowCells, rowPlacements } from './row-input.js';
16
+ import { buildRowCells, positionalPlacements, rowPlacements } from './row-input.js';
15
17
  import { Row } from './row.js';
16
- import { Table, TOTALS_ROW_SUBTOTAL_CODE } from './table.js';
18
+ import { Table } from './table.js';
19
+ import { UsedExtent } from './used-extent.js';
17
20
  import { WorksheetComments } from './worksheet-comments.js';
18
21
  import { WORKSHEET_MODEL_FACETS } from './worksheet-model.js';
19
22
  import { WorksheetPictures } from './worksheet-pictures.js';
23
+ export const isVisibility = tokenSet({ visible: true, hidden: true, veryHidden: true });
20
24
  export class Worksheet {
21
25
  name;
22
26
  id;
@@ -50,10 +54,19 @@ export class Worksheet {
50
54
  #protection;
51
55
  #autoFilter;
52
56
  #edits;
57
+ #extent;
58
+ #mergeIndex;
53
59
  constructor(name, id, state = 'visible') {
54
60
  this.name = name;
55
61
  this.id = id;
56
62
  this.state = state;
63
+ this.#mergeIndex = new MergeIndex(this.#mergeRects);
64
+ this.#extent = new UsedExtent({
65
+ rows: this.#rows,
66
+ rowProperties: this.#rowProperties,
67
+ columns: this.#columns,
68
+ mergeRects: this.#mergeRects,
69
+ });
57
70
  this.#edits = new GridEdits({
58
71
  rows: this.#rows,
59
72
  rowProperties: this.#rowProperties,
@@ -62,11 +75,20 @@ export class Worksheet {
62
75
  mergeRects: this.#mergeRects,
63
76
  tables: this.#tables,
64
77
  images: this.#images.anchors,
78
+ dataValidations: this.#dataValidations,
79
+ conditionalFormattings: this.#conditionalFormattings,
80
+ comments: this.#comments,
81
+ autoFilter: {
82
+ get: () => this.#autoFilter,
83
+ set: (next) => {
84
+ this.#autoFilter = next;
85
+ },
86
+ },
65
87
  });
66
88
  }
67
89
  getCell(reference) {
68
90
  const { col, row } = decodeCellRef(reference);
69
- const master = masterOf(this.#mergeRects, row, col);
91
+ const master = this.#mergeIndex.masterOf(row, col);
70
92
  return this.#cellAt(master.row, master.col);
71
93
  }
72
94
  hasCell(row, col) {
@@ -84,30 +106,17 @@ export class Worksheet {
84
106
  return new Range(this, referenceOrTop, left ?? 0, bottom ?? 0, right ?? 0);
85
107
  }
86
108
  get rowCount() {
87
- let last = 0;
88
- for (const [number, cols] of this.#rows) {
89
- if (number > last && this.#rowHasContent(cols))
90
- last = number;
91
- }
92
- for (const number of this.#rowProperties.keys()) {
93
- if (number > last)
94
- last = number;
95
- }
96
- for (const rect of this.#mergeRects) {
97
- if (rect.bottom > last)
98
- last = rect.bottom;
99
- }
100
- return last;
109
+ return this.#extent.lastRow;
101
110
  }
102
111
  get actualRowCount() {
103
112
  let count = 0;
104
113
  for (const cols of this.#rows.values()) {
105
- if (this.#rowHasContent(cols))
114
+ if (this.#rowIsPopulated(cols))
106
115
  count++;
107
116
  }
108
117
  return count;
109
118
  }
110
- #rowHasContent(cols) {
119
+ #rowIsPopulated(cols) {
111
120
  for (const cell of cols.values()) {
112
121
  if (cell.value !== null)
113
122
  return true;
@@ -115,22 +124,7 @@ export class Worksheet {
115
124
  return false;
116
125
  }
117
126
  get columnCount() {
118
- let last = 0;
119
- for (const cols of this.#rows.values()) {
120
- for (const [col, cell] of cols) {
121
- if (cell.value !== null && col > last)
122
- last = col;
123
- }
124
- }
125
- for (const index of this.#columns.keys()) {
126
- if (index > last)
127
- last = index;
128
- }
129
- for (const rect of this.#mergeRects) {
130
- if (rect.right > last)
131
- last = rect.right;
132
- }
133
- return last;
127
+ return this.#extent.lastColumn;
134
128
  }
135
129
  get usedRange() {
136
130
  const bottom = this.rowCount;
@@ -151,46 +145,17 @@ export class Worksheet {
151
145
  }
152
146
  }
153
147
  addTable(options) {
154
- const table = new Table(options, (row, col, value, style) => {
155
- const cell = this.#cellAt(row, col);
156
- cell.value = value;
157
- if (style !== undefined)
158
- applyCellStyle(cell, style);
159
- }, (row) => this.spliceRows(row, 0, []));
148
+ const table = new Table(options, {
149
+ holdsValue: (row, col) => this.hasCell(row, col) && this.#cellAt(row, col).value != null,
150
+ writeCell: (row, col, value, style) => {
151
+ const cell = this.#cellAt(row, col);
152
+ cell.value = value;
153
+ if (style !== undefined)
154
+ applyCellStyle(cell, style);
155
+ },
156
+ insertRow: (row) => this.spliceRows(row, 0, []),
157
+ });
160
158
  this.#tables.push(table);
161
- if (table.headerRow) {
162
- const { top, left } = table.region;
163
- table.columns.forEach((column, index) => {
164
- const col = left + index;
165
- if (this.hasCell(top, col) && this.#cellAt(top, col).value != null)
166
- return;
167
- this.#cellAt(top, col).value = column.name;
168
- });
169
- }
170
- if (table.totalsRow) {
171
- const { left, bottom } = table.region;
172
- table.columns.forEach((column, index) => {
173
- const col = left + index;
174
- if (this.hasCell(bottom, col) && this.#cellAt(bottom, col).value != null)
175
- return;
176
- if (column.totalsRowLabel !== undefined) {
177
- this.#cellAt(bottom, col).value = column.totalsRowLabel;
178
- return;
179
- }
180
- if (column.totalsRowFunction !== undefined) {
181
- const code = TOTALS_ROW_SUBTOTAL_CODE[column.totalsRowFunction];
182
- if (code !== undefined) {
183
- this.#cellAt(bottom, col).value = {
184
- formula: `SUBTOTAL(${code},${table.name}[${column.name}])`,
185
- };
186
- }
187
- else if (column.totalsRowFunction === 'custom' &&
188
- column.totalsRowFormula !== undefined) {
189
- this.#cellAt(bottom, col).value = { formula: column.totalsRowFormula };
190
- }
191
- }
192
- });
193
- }
194
159
  return table;
195
160
  }
196
161
  get tables() {
@@ -247,11 +212,12 @@ export class Worksheet {
247
212
  const { top, left, bottom, right } = decodeRange(range);
248
213
  if (top !== undefined && left !== undefined && bottom !== undefined && right !== undefined) {
249
214
  const rect = { top, left, bottom, right };
250
- const clash = this.#mergeRects.find((existing) => rectsOverlap(existing, rect));
251
- if (clash) {
215
+ if (this.#mergeIndex.overlapping(rect) !== undefined) {
252
216
  throw new AuthoringError(`merged range "${range}" overlaps an existing merged region`);
253
217
  }
254
218
  this.#mergeRects.push(rect);
219
+ this.#mergeIndex.note(rect);
220
+ this.#extent.noteMerge(rect);
255
221
  clearCoveredValues(this.#rows, rect);
256
222
  }
257
223
  this.#merges.push(range);
@@ -273,8 +239,11 @@ export class Worksheet {
273
239
  const { top, left, bottom, right } = decodeRange(range);
274
240
  if (top !== undefined && left !== undefined && bottom !== undefined && right !== undefined) {
275
241
  const rectIndex = this.#mergeRects.findIndex((r) => r.top === top && r.left === left && r.bottom === bottom && r.right === right);
276
- if (rectIndex !== -1)
242
+ if (rectIndex !== -1) {
277
243
  this.#mergeRects.splice(rectIndex, 1);
244
+ this.#mergeIndex.invalidate();
245
+ this.#extent.invalidate();
246
+ }
278
247
  }
279
248
  return true;
280
249
  }
@@ -298,6 +267,8 @@ export class Worksheet {
298
267
  assertStartAndCount('splice', 'row', start, count);
299
268
  const inserted = inserts.map((values, i) => buildRowCells(start + i, values, this.#columns));
300
269
  this.#edits.spliceRows(start, count, inserted);
270
+ this.#mergeIndex.invalidate();
271
+ this.#extent.invalidate();
301
272
  }
302
273
  insertRow(pos, values) {
303
274
  this.spliceRows(pos, 0, values);
@@ -339,6 +310,7 @@ export class Worksheet {
339
310
  const { count = 1, insert = true } = options;
340
311
  assertStartAndCount('duplicate', 'row', start, count);
341
312
  const source = this.#rows.get(start);
313
+ const sourceProperties = this.#rowProperties.get(start);
342
314
  const snapshot = (destRow) => {
343
315
  const row = new Map();
344
316
  if (source) {
@@ -350,18 +322,30 @@ export class Worksheet {
350
322
  }
351
323
  return row;
352
324
  };
325
+ const copyProperties = (destRow) => {
326
+ if (sourceProperties === undefined)
327
+ this.#rowProperties.delete(destRow);
328
+ else
329
+ this.#rowProperties.set(destRow, { ...sourceProperties });
330
+ };
353
331
  if (insert) {
354
332
  const copies = Array.from({ length: count }, () => snapshot(start));
355
333
  this.#edits.spliceRows(start + 1, 0, copies);
334
+ this.#mergeIndex.invalidate();
356
335
  }
357
336
  else {
358
337
  for (let i = 1; i <= count; i++)
359
338
  this.#rows.set(start + i, snapshot(start + i));
360
339
  }
340
+ for (let i = 1; i <= count; i++)
341
+ copyProperties(start + i);
342
+ this.#extent.invalidate();
361
343
  }
362
344
  spliceColumns(start, count, ...inserts) {
363
345
  assertStartAndCount('splice', 'column', start, count);
364
346
  this.#edits.spliceColumns(start, count, inserts);
347
+ this.#mergeIndex.invalidate();
348
+ this.#extent.invalidate();
365
349
  }
366
350
  insertColumn(pos, values) {
367
351
  this.spliceColumns(pos, 0, values);
@@ -373,15 +357,11 @@ export class Worksheet {
373
357
  let index = this.columnCount;
374
358
  return columns.map((values) => {
375
359
  index += 1;
376
- const cells = [];
377
- values.forEach((value, i) => {
378
- if (value === undefined)
379
- return;
380
- const cell = this.#cellAt(i + 1, index);
360
+ return positionalPlacements(values).map(([row, value]) => {
361
+ const cell = this.#cellAt(row, index);
381
362
  cell.value = value;
382
- cells.push(cell);
363
+ return cell;
383
364
  });
384
- return cells;
385
365
  });
386
366
  }
387
367
  get model() {
@@ -399,6 +379,8 @@ export class Worksheet {
399
379
  this.#dataValidations.clear();
400
380
  this.#conditionalFormattings.clear();
401
381
  this.#tables.length = 0;
382
+ this.#mergeIndex.invalidate();
383
+ this.#extent.reset();
402
384
  }
403
385
  set model(model) {
404
386
  this.#resetContent();
@@ -432,12 +414,14 @@ export class Worksheet {
432
414
  cell = new Cell(row, col);
433
415
  cols.set(col, cell);
434
416
  }
417
+ this.#extent.noteCell(row, col);
435
418
  return cell;
436
419
  }
437
420
  [INTERNAL] = {
438
421
  evictRow: (number) => {
439
422
  this.#rows.delete(number);
440
423
  this.#rowProperties.delete(number);
424
+ this.#extent.invalidate();
441
425
  },
442
426
  addLoadedPivotTable: (pivot) => {
443
427
  this.#loadedPivotTables.push(pivot);
@@ -459,6 +443,7 @@ export class Worksheet {
459
443
  properties = {};
460
444
  this.#rowProperties.set(number, properties);
461
445
  }
446
+ this.#extent.noteDeclaredRow(number);
462
447
  return properties;
463
448
  },
464
449
  rowCells: (number) => {
@@ -472,6 +457,7 @@ export class Worksheet {
472
457
  properties = {};
473
458
  this.#columns.set(index, properties);
474
459
  }
460
+ this.#extent.noteDeclaredColumn(index);
475
461
  return properties;
476
462
  },
477
463
  columnCells: (index) => {
@@ -1,5 +1,6 @@
1
1
  import { strFromU8 } from 'fflate';
2
- import { boolStrict, localName, xmlEvents } from '../xml/xml-read.js';
2
+ import { tokenSet } from '../token-set.js';
3
+ import { boolStrict, localName, xmlEvents } from '../xml/xml-scan.js';
3
4
  import { CustomUiParseError } from './errors.js';
4
5
  export const CUSTOMUI_2006_NAMESPACE = 'http://schemas.microsoft.com/office/2006/01/customui';
5
6
  export const CUSTOMUI_2009_NAMESPACE = 'http://schemas.microsoft.com/office/2009/07/customui';
@@ -9,26 +10,26 @@ export function isCustomUiRelType(type) {
9
10
  return type.endsWith('/ui/extensibility');
10
11
  }
11
12
  const MAX_DEPTH = 256;
12
- const KNOWN_KINDS = new Set([
13
- 'button',
14
- 'toggleButton',
15
- 'checkBox',
16
- 'editBox',
17
- 'dropDown',
18
- 'comboBox',
19
- 'gallery',
20
- 'menu',
21
- 'dynamicMenu',
22
- 'splitButton',
23
- 'buttonGroup',
24
- 'box',
25
- 'labelControl',
26
- 'separator',
27
- 'menuSeparator',
28
- 'dialogBoxLauncher',
29
- 'control',
30
- 'item',
31
- ]);
13
+ const isKnownControlKind = tokenSet({
14
+ button: true,
15
+ toggleButton: true,
16
+ checkBox: true,
17
+ editBox: true,
18
+ dropDown: true,
19
+ comboBox: true,
20
+ gallery: true,
21
+ menu: true,
22
+ dynamicMenu: true,
23
+ splitButton: true,
24
+ buttonGroup: true,
25
+ box: true,
26
+ labelControl: true,
27
+ separator: true,
28
+ menuSeparator: true,
29
+ dialogBoxLauncher: true,
30
+ control: true,
31
+ item: true,
32
+ });
32
33
  export function parseCustomUi(input) {
33
34
  const xml = typeof input === 'string' ? input : strFromU8(input);
34
35
  let root;
@@ -79,9 +80,7 @@ function toGroup(groupEl) {
79
80
  };
80
81
  }
81
82
  function toControl(el) {
82
- const kind = KNOWN_KINDS.has(el.local)
83
- ? el.local
84
- : 'unknown';
83
+ const kind = isKnownControlKind(el.local) ? el.local : 'unknown';
85
84
  return {
86
85
  kind,
87
86
  ...identity(el.attrs),
@@ -4,11 +4,11 @@ export { Cell } from '../core/cell.ts';
4
4
  export { applyTint, type ColorResolutionContext, DEFAULT_INDEXED_COLORS, resolveColor, SYSTEM_INDEXED_COLORS, } from '../core/color-resolution.ts';
5
5
  export { Column } from '../core/column.ts';
6
6
  export type { Comment, CommentThread, Mention, MentionRef, Person } from '../core/comment-thread.ts';
7
- export type { CfValueObject, ConditionalFormatting, ConditionalFormattingRule, } from '../core/conditional-formatting.ts';
7
+ export type { CfTimePeriod, CfValueObject, CfValueObjectType, ConditionalFormatting, ConditionalFormattingOperator, ConditionalFormattingRule, ConditionalFormattingType, IconSetType, } from '../core/conditional-formatting.ts';
8
8
  export type { DataValidation, DataValidationEntry, DataValidationErrorStyle, DataValidationOperator, DataValidationType, } from '../core/data-validation.ts';
9
9
  export { type AnchoredImage, type AnchorPoint, type Extent, type ImageAnchor, type ImageEditAs, isOneCellAnchor, type OneCellAnchor, type PortableImage, PX_TO_EMU, type TwoCellAnchor, type WorkbookImage, type WorksheetImages, } from '../core/image.ts';
10
10
  export { MAX_COLUMN_WIDTH, MAX_ROW_HEIGHT } from '../core/limits.ts';
11
- export type { HeaderFooter, PageBreak, PageMargins, PageSetup, PrintOptions, } from '../core/page-setup.ts';
11
+ export type { HeaderFooter, PageBreak, PageMargins, PageOrder, PageOrientation, PageSetup, PrintOptions, } from '../core/page-setup.ts';
12
12
  export { type ParsedPivotField, type ParsedPivotSource, type ParsedPivotTable, type PivotCacheField, type PivotItem, type PivotMetric, type PivotNumericSummary, type PivotRecordCell, type PivotSourceKind, PivotTable, type PivotTableOptions, } from '../core/pivot-table.ts';
13
13
  export type { PreservedPart, PreservedRelationship, PreservedRootReference, PreservedWorksheetReference, } from '../core/preserved.ts';
14
14
  export type { SheetProtection, SheetProtectionCredential, SheetProtectionFlags, SheetProtectionOptions, } from '../core/protection.ts';
@@ -22,4 +22,4 @@ export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, THEME_COLOR_SLOTS, typ
22
22
  export { type CellValue, cellValueToText, coerceCellValue, type DataTableFormulaValue, detectValueType, ERROR_CODES, type ErrorCode, type ErrorValue, type FormulaResult, type FormulaValue, type HyperlinkValue, isDataTableFormulaValue, isErrorCode, isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, type RichTextRun, type RichTextValue, richTextToPlain, type SharedFormulaValue, ValueType, } from '../core/value.ts';
23
23
  export { type AddImageOptions, type AddWorksheetOptions, DEFAULT_WORKBOOK_VIEW, type DefinedName, type PreservedWorkbookReference, Workbook, type WorkbookProperties, type WorkbookView, } from '../core/workbook.ts';
24
24
  export type { WorkbookProtection, WorkbookProtectionCredentialAttr, } from '../core/workbook-protection.ts';
25
- export { type CellModel, type ColumnProperties, type OutlineProperties, type RowInput, type RowProperties, type SheetView, Worksheet, type WorksheetModel, type WorksheetProperties, type WorksheetState, } from '../core/worksheet.ts';
25
+ export { type CellModel, type ColumnProperties, type OutlineProperties, type RowInput, type RowProperties, type SheetView, type Visibility, Worksheet, type WorksheetModel, type WorksheetProperties, type WorksheetState, } from '../core/worksheet.ts';
@@ -1,2 +1,2 @@
1
1
  export { type CsvReadOptions, readCsv } from '../io/csv/read.ts';
2
- export { type CsvWriteOptions, writeCsv, writeCsvText } from '../io/csv/write.ts';
2
+ export { type CsvEncoding, type CsvWriteOptions, writeCsv, writeCsvText } from '../io/csv/write.ts';
@@ -0,0 +1,15 @@
1
+ /** Not available outside Node; see {@link WorkbookStreamWriter}. */
2
+ export declare class StreamedRow {
3
+ constructor();
4
+ }
5
+ /** Not available outside Node; see {@link WorkbookStreamWriter}. */
6
+ export declare class WorksheetStreamWriter {
7
+ constructor();
8
+ }
9
+ /**
10
+ * Not available outside Node. The streaming writer needs `node:fs` and `node:stream`; in a browser,
11
+ * build the workbook in memory and call `writeXlsx`.
12
+ */
13
+ export declare class WorkbookStreamWriter {
14
+ constructor();
15
+ }
@@ -0,0 +1,20 @@
1
+ const WHY = 'the streaming writer opens files and pipes Node streams, which a browser has neither of. ' +
2
+ 'Use writeXlsx (or writeXlsxAsync), which produce the same package as bytes.';
3
+ function unavailable(name) {
4
+ throw new Error(`ts-xlsx: ${name} is not available in this environment: ${WHY}`);
5
+ }
6
+ export class StreamedRow {
7
+ constructor() {
8
+ unavailable('StreamedRow');
9
+ }
10
+ }
11
+ export class WorksheetStreamWriter {
12
+ constructor() {
13
+ unavailable('WorksheetStreamWriter');
14
+ }
15
+ }
16
+ export class WorkbookStreamWriter {
17
+ constructor() {
18
+ unavailable('WorkbookStreamWriter');
19
+ }
20
+ }
@@ -0,0 +1 @@
1
+ export { type CalcProperties, StreamedRow, WorkbookStreamWriter, type WorkbookStreamWriterOptions, WorksheetStreamWriter, } from '../io/xlsx/write-stream.ts';
@@ -0,0 +1 @@
1
+ export { StreamedRow, WorkbookStreamWriter, WorksheetStreamWriter, } from '../io/xlsx/write-stream.js';
@@ -3,4 +3,3 @@ export { type ReadXlsxOptions, readXlsx } from '../io/xlsx/read.ts';
3
3
  export { type ReadSheetRowsOptions, readSheetRows, readWorkbookStream, } from '../io/xlsx/read-rows.ts';
4
4
  export { DEFAULT_THEME_XML, parseThemeColorScheme, parseThemeFontScheme, } from '../io/xlsx/theme-xml.ts';
5
5
  export { type WriteOptions, writeXlsx, writeXlsxAsync } from '../io/xlsx/write.ts';
6
- export { type CalcProperties, StreamedRow, WorkbookStreamWriter, type WorkbookStreamWriterOptions, WorksheetStreamWriter, } from '../io/xlsx/write-stream.ts';
@@ -3,4 +3,3 @@ export { readXlsx } from '../io/xlsx/read.js';
3
3
  export { readSheetRows, readWorkbookStream, } from '../io/xlsx/read-rows.js';
4
4
  export { DEFAULT_THEME_XML, parseThemeColorScheme, parseThemeFontScheme, } from '../io/xlsx/theme-xml.js';
5
5
  export { writeXlsx, writeXlsxAsync } from '../io/xlsx/write.js';
6
- export { StreamedRow, WorkbookStreamWriter, WorksheetStreamWriter, } from '../io/xlsx/write-stream.js';
@@ -1,6 +1,6 @@
1
1
  import { Workbook } from '../../core/workbook.js';
2
2
  export function readCsv(input, options = {}) {
3
- const text = stripBom(typeof input === 'string' ? input : Buffer.from(input).toString('utf8'));
3
+ const text = stripBom(typeof input === 'string' ? input : new TextDecoder('utf-8', { ignoreBOM: true }).decode(input));
4
4
  const delimiter = options.delimiter ?? ',';
5
5
  if (delimiter.length !== 1) {
6
6
  throw new RangeError(`CSV delimiter must be a single character, got ${JSON.stringify(delimiter)}`);
@@ -1,5 +1,15 @@
1
1
  import { type CellValue } from '../../core/value.ts';
2
2
  import type { Workbook } from '../../core/workbook.ts';
3
+ /**
4
+ * A byte encoding {@link writeCsv} can produce, spelled the way Node's `Buffer` spells it and
5
+ * meaning the same bytes.
6
+ *
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 surface depend on
9
+ * Node's types, and it offered `base64` and `hex` as if they were output encodings for a text
10
+ * format. The list is what a CSV consumer actually asks for.
11
+ */
12
+ export type CsvEncoding = 'ascii' | 'latin1' | 'ucs-2' | 'ucs2' | 'utf-8' | 'utf-16le' | 'utf16le' | 'utf8';
3
13
  export interface CsvWriteOptions {
4
14
  /** Which worksheet to write; defaults to the first. A name matching no sheet throws rather than
5
15
  * silently emitting an empty file. */
@@ -14,7 +24,7 @@ export interface CsvWriteOptions {
14
24
  /** Render Date cells in UTC rather than the runner's local time. */
15
25
  readonly dateUTC?: boolean;
16
26
  /** Byte encoding for {@link writeCsv}; defaults to `"utf8"`. */
17
- readonly encoding?: BufferEncoding;
27
+ readonly encoding?: CsvEncoding;
18
28
  /** Prepend a UTF-8 byte-order mark (applies only to UTF-8); defaults to `true` for UTF-8. */
19
29
  readonly bom?: boolean;
20
30
  /** Per-field transform replacing the default value rendering; receives the cell's value (`null`
@@ -28,6 +38,6 @@ export declare function writeCsvText(workbook: Workbook, options?: CsvWriteOptio
28
38
  * The CSV bytes of one worksheet in the requested encoding, with a UTF-8 BOM by default.
29
39
  *
30
40
  * @throws {AuthoringError} if a field holds an unpaired surrogate and the encoding is UTF-8, which
31
- * cannot represent one. The alternative is `Buffer.from`'s silent U+FFFD substitution.
41
+ * cannot represent one. The alternative is a silent U+FFFD substitution.
32
42
  */
33
43
  export declare function writeCsv(workbook: Workbook, options?: CsvWriteOptions): Uint8Array;
@@ -30,10 +30,10 @@ export function writeCsv(workbook, options = {}) {
30
30
  const utf8 = isUtf8(encoding);
31
31
  if (utf8)
32
32
  assertEncodable(text);
33
- const body = Buffer.from(text, encoding);
33
+ const body = encode(text, encoding);
34
34
  const wantBom = options.bom ?? utf8;
35
35
  if (!wantBom || !utf8)
36
- return Uint8Array.from(body);
36
+ return body;
37
37
  const out = new Uint8Array(UTF8_BOM.length + body.length);
38
38
  out.set(UTF8_BOM, 0);
39
39
  out.set(body, UTF8_BOM.length);
@@ -42,6 +42,21 @@ export function writeCsv(workbook, options = {}) {
42
42
  function isUtf8(encoding) {
43
43
  return encoding === 'utf8' || encoding === 'utf-8';
44
44
  }
45
+ function encode(text, encoding) {
46
+ if (isUtf8(encoding))
47
+ return new TextEncoder().encode(text);
48
+ if (encoding === 'ascii' || encoding === 'latin1') {
49
+ const bytes = new Uint8Array(text.length);
50
+ for (let i = 0; i < text.length; i++)
51
+ bytes[i] = text.charCodeAt(i) & 0xff;
52
+ return bytes;
53
+ }
54
+ const bytes = new Uint8Array(text.length * 2);
55
+ const view = new DataView(bytes.buffer);
56
+ for (let i = 0; i < text.length; i++)
57
+ view.setUint16(i * 2, text.charCodeAt(i), true);
58
+ return bytes;
59
+ }
45
60
  function assertEncodable(text) {
46
61
  const found = LONE_SURROGATE.exec(text);
47
62
  if (found === null)
@@ -1,8 +1,9 @@
1
1
  import { Unzip, UnzipInflate } from 'fflate';
2
+ import { concat } from '../../bytes.js';
2
3
  import { PackageReadError } from './errors.js';
3
4
  const INPUT_SLICE = 1 << 14;
4
5
  export function inflatePackage(data, cap) {
5
- const files = {};
6
+ const files = Object.create(null);
6
7
  let total = 0;
7
8
  let failure;
8
9
  const unzip = new Unzip((file) => {
@@ -23,7 +24,7 @@ export function inflatePackage(data, cap) {
23
24
  chunks.push(chunk);
24
25
  size += chunk.length;
25
26
  if (final)
26
- files[file.name] = join(chunks, size);
27
+ files[file.name] = concat(chunks, size);
27
28
  };
28
29
  try {
29
30
  file.start();
@@ -43,14 +44,3 @@ export function inflatePackage(data, cap) {
43
44
  throw failure;
44
45
  return files;
45
46
  }
46
- function join(chunks, size) {
47
- if (chunks.length === 1)
48
- return chunks[0];
49
- const out = new Uint8Array(size);
50
- let at = 0;
51
- for (const chunk of chunks) {
52
- out.set(chunk, at);
53
- at += chunk.length;
54
- }
55
- return out;
56
- }