@shbernal/ts-xlsx 1.3.1 → 2.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.
Files changed (186) 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 +72 -3
  5. package/dist/core/address.js +50 -10
  6. package/dist/core/autofilter.d.ts +13 -2
  7. package/dist/core/autofilter.js +35 -10
  8. package/dist/core/cell.d.ts +17 -16
  9. package/dist/core/cell.js +3 -7
  10. package/dist/core/color-resolution.d.ts +8 -8
  11. package/dist/core/color-resolution.js +7 -8
  12. package/dist/core/column.d.ts +8 -9
  13. package/dist/core/column.js +2 -4
  14. package/dist/core/comment-thread.d.ts +32 -23
  15. package/dist/core/comment-thread.js +4 -4
  16. package/dist/core/conditional-formatting-overlay.d.ts +6 -1
  17. package/dist/core/conditional-formatting-overlay.js +11 -0
  18. package/dist/core/conditional-formatting.d.ts +48 -8
  19. package/dist/core/conditional-formatting.js +88 -0
  20. package/dist/core/data-validation-overlay.d.ts +12 -1
  21. package/dist/core/data-validation-overlay.js +21 -2
  22. package/dist/core/data-validation.d.ts +12 -5
  23. package/dist/core/data-validation.js +34 -0
  24. package/dist/core/date.d.ts +3 -3
  25. package/dist/core/formula.d.ts +6 -6
  26. package/dist/core/grid-edits.d.ts +16 -0
  27. package/dist/core/grid-edits.js +29 -11
  28. package/dist/core/grid-shift.d.ts +19 -0
  29. package/dist/core/grid-shift.js +8 -0
  30. package/dist/core/image.d.ts +39 -2
  31. package/dist/core/image.js +10 -0
  32. package/dist/core/internal.d.ts +9 -1
  33. package/dist/core/limits.d.ts +4 -4
  34. package/dist/core/merge.d.ts +17 -12
  35. package/dist/core/merge.js +36 -5
  36. package/dist/core/page-setup.d.ts +20 -6
  37. package/dist/core/page-setup.js +8 -1
  38. package/dist/core/pivot-table.d.ts +4 -4
  39. package/dist/core/pivot-table.js +29 -20
  40. package/dist/core/preserved.d.ts +3 -3
  41. package/dist/core/protection.d.ts +7 -4
  42. package/dist/core/protection.js +20 -14
  43. package/dist/core/range.d.ts +16 -14
  44. package/dist/core/range.js +5 -5
  45. package/dist/core/row-input.d.ts +9 -8
  46. package/dist/core/row-input.js +12 -10
  47. package/dist/core/row.d.ts +7 -8
  48. package/dist/core/row.js +2 -4
  49. package/dist/core/style.d.ts +41 -14
  50. package/dist/core/style.js +82 -67
  51. package/dist/core/table-style.d.ts +9 -9
  52. package/dist/core/table-style.js +1 -1
  53. package/dist/core/table.d.ts +53 -51
  54. package/dist/core/table.js +83 -41
  55. package/dist/core/theme.d.ts +16 -44
  56. package/dist/core/theme.js +6 -148
  57. package/dist/core/value.d.ts +7 -7
  58. package/dist/core/workbook-protection.d.ts +2 -2
  59. package/dist/core/workbook-styles.d.ts +26 -0
  60. package/dist/core/workbook-styles.js +48 -0
  61. package/dist/core/workbook-theme.d.ts +25 -0
  62. package/dist/core/workbook-theme.js +49 -0
  63. package/dist/core/workbook-vba.d.ts +17 -0
  64. package/dist/core/workbook-vba.js +79 -0
  65. package/dist/core/workbook.d.ts +133 -87
  66. package/dist/core/workbook.js +74 -133
  67. package/dist/core/worksheet-comments.d.ts +17 -0
  68. package/dist/core/worksheet-comments.js +70 -0
  69. package/dist/core/worksheet-model.d.ts +4 -4
  70. package/dist/core/worksheet-model.js +1 -0
  71. package/dist/core/worksheet-pictures.d.ts +29 -0
  72. package/dist/core/worksheet-pictures.js +42 -0
  73. package/dist/core/worksheet.d.ts +100 -79
  74. package/dist/core/worksheet.js +63 -142
  75. package/dist/customui/errors.d.ts +1 -1
  76. package/dist/customui/ribbon.d.ts +3 -3
  77. package/dist/customui/ribbon.js +24 -23
  78. package/dist/entries/core.d.ts +7 -7
  79. package/dist/entries/core.js +1 -1
  80. package/dist/entries/csv.d.ts +1 -1
  81. package/dist/entries/node-unavailable.d.ts +15 -0
  82. package/dist/entries/node-unavailable.js +20 -0
  83. package/dist/entries/node.d.ts +1 -0
  84. package/dist/entries/node.js +1 -0
  85. package/dist/entries/vba.d.ts +1 -1
  86. package/dist/entries/vba.js +1 -1
  87. package/dist/entries/xlsx.d.ts +1 -1
  88. package/dist/entries/xlsx.js +1 -1
  89. package/dist/errors.d.ts +12 -12
  90. package/dist/io/csv/read.js +2 -3
  91. package/dist/io/csv/write.d.ts +19 -4
  92. package/dist/io/csv/write.js +34 -4
  93. package/dist/io/opc/errors.d.ts +6 -6
  94. package/dist/io/opc/inflate.d.ts +1 -1
  95. package/dist/io/opc/inflate.js +2 -12
  96. package/dist/io/opc/namespaces.d.ts +1 -1
  97. package/dist/io/opc/read-opc.d.ts +19 -3
  98. package/dist/io/opc/read-opc.js +14 -8
  99. package/dist/io/opc/read-options.d.ts +2 -2
  100. package/dist/io/opc/rels.d.ts +11 -0
  101. package/dist/io/opc/rels.js +3 -2
  102. package/dist/io/opc/sniff-format.d.ts +2 -2
  103. package/dist/io/style/xf-style.d.ts +44 -7
  104. package/dist/io/style/xf-style.js +27 -0
  105. package/dist/io/xlsb/errors.d.ts +1 -1
  106. package/dist/io/xlsb/formula.d.ts +4 -4
  107. package/dist/io/xlsb/primitives.d.ts +8 -8
  108. package/dist/io/xlsb/ptg-functions.d.ts +1 -1
  109. package/dist/io/xlsb/read-styles.js +2 -20
  110. package/dist/io/xlsb/read.d.ts +2 -2
  111. package/dist/io/xlsx/cell-accumulator.d.ts +26 -9
  112. package/dist/io/xlsx/cell-accumulator.js +89 -46
  113. package/dist/io/xlsx/cell-value.d.ts +3 -3
  114. package/dist/io/xlsx/cell-value.js +9 -6
  115. package/dist/io/xlsx/color-xml.d.ts +1 -1
  116. package/dist/io/xlsx/color-xml.js +18 -23
  117. package/dist/io/xlsx/comments.d.ts +5 -5
  118. package/dist/io/xlsx/comments.js +17 -8
  119. package/dist/io/xlsx/conditional-formatting.d.ts +4 -4
  120. package/dist/io/xlsx/conditional-formatting.js +64 -58
  121. package/dist/io/xlsx/data-validation.d.ts +3 -3
  122. package/dist/io/xlsx/data-validation.js +35 -33
  123. package/dist/io/xlsx/errors.d.ts +3 -3
  124. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  125. package/dist/io/xlsx/hyperlinks.js +10 -18
  126. package/dist/io/xlsx/images.d.ts +1 -1
  127. package/dist/io/xlsx/images.js +25 -28
  128. package/dist/io/xlsx/namespaces.d.ts +2 -2
  129. package/dist/io/xlsx/package-plan.js +5 -2
  130. package/dist/io/xlsx/read-pivot.js +4 -13
  131. package/dist/io/xlsx/read-rows.d.ts +13 -13
  132. package/dist/io/xlsx/read-rows.js +19 -55
  133. package/dist/io/xlsx/read-shared-strings.js +7 -44
  134. package/dist/io/xlsx/read-styles.d.ts +3 -3
  135. package/dist/io/xlsx/read-styles.js +42 -66
  136. package/dist/io/xlsx/read-worksheet.js +73 -129
  137. package/dist/io/xlsx/read.d.ts +4 -6
  138. package/dist/io/xlsx/read.js +104 -142
  139. package/dist/io/xlsx/rich-runs.d.ts +41 -7
  140. package/dist/io/xlsx/rich-runs.js +94 -30
  141. package/dist/io/xlsx/rich-text.d.ts +1 -1
  142. package/dist/io/xlsx/shared-strings.d.ts +2 -2
  143. package/dist/io/xlsx/sheet-properties.d.ts +1 -1
  144. package/dist/io/xlsx/sheet-properties.js +14 -10
  145. package/dist/io/xlsx/styles.d.ts +9 -22
  146. package/dist/io/xlsx/styles.js +65 -89
  147. package/dist/io/xlsx/tables.d.ts +2 -2
  148. package/dist/io/xlsx/tables.js +20 -25
  149. package/dist/io/xlsx/theme-xml.d.ts +37 -0
  150. package/dist/io/xlsx/theme-xml.js +145 -0
  151. package/dist/io/xlsx/threaded-comments.d.ts +18 -27
  152. package/dist/io/xlsx/threaded-comments.js +26 -38
  153. package/dist/io/xlsx/workbook-xml.js +14 -8
  154. package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
  155. package/dist/io/xlsx/worksheet-xml.js +40 -48
  156. package/dist/io/xlsx/write-stream.d.ts +23 -16
  157. package/dist/io/xlsx/write-stream.js +13 -14
  158. package/dist/io/xlsx/write.d.ts +8 -7
  159. package/dist/io/xlsx/write.js +6 -3
  160. package/dist/io/xlsx/x14-ext.d.ts +1 -1
  161. package/dist/sha512.d.ts +2 -0
  162. package/dist/sha512.js +146 -0
  163. package/dist/vba/bytes.d.ts +11 -0
  164. package/dist/vba/bytes.js +29 -0
  165. package/dist/vba/cfb-writer.d.ts +1 -1
  166. package/dist/vba/cfb.d.ts +1 -1
  167. package/dist/vba/cfb.js +15 -51
  168. package/dist/vba/codepage.d.ts +2 -2
  169. package/dist/vba/dir-records.d.ts +53 -0
  170. package/dist/vba/dir-records.js +28 -0
  171. package/dist/vba/errors.d.ts +2 -2
  172. package/dist/vba/index.d.ts +2 -2
  173. package/dist/vba/index.js +2 -2
  174. package/dist/vba/ms-ovba.d.ts +1 -1
  175. package/dist/vba/ms-ovba.js +1 -3
  176. package/dist/vba/project-editor.d.ts +9 -9
  177. package/dist/vba/project-editor.js +6 -44
  178. package/dist/vba/project.d.ts +5 -5
  179. package/dist/vba/project.js +3 -28
  180. package/dist/vba/vba-encoding.d.ts +1 -1
  181. package/dist/xml/errors.d.ts +1 -1
  182. package/dist/xml/xml-read.d.ts +98 -9
  183. package/dist/xml/xml-read.js +70 -0
  184. package/dist/xml/xml.d.ts +74 -10
  185. package/dist/xml/xml.js +39 -7
  186. package/package.json +59 -36
@@ -1,23 +1,26 @@
1
1
  import { AuthoringError } from '../errors.js';
2
- import { decodeAddress, decodeRange, encodeAddress } from './address.js';
2
+ import { decodeCellRef, decodeRange, encodeAddress, rectsOverlap, tryDecodeCellRef, } from './address.js';
3
3
  import { canonicalizeAutoFilter } from './autofilter.js';
4
4
  import { applyCellStyle, Cell, copyCellContent } from './cell.js';
5
5
  import { Column } from './column.js';
6
- import { commentThreadGuid, commentThreadOffset } from './comment-thread.js';
7
6
  import { ConditionalFormattingOverlay } from './conditional-formatting-overlay.js';
8
- import { replaceContents } from './containers.js';
9
7
  import { DataValidationOverlay } from './data-validation-overlay.js';
10
8
  import { GridEdits } from './grid-edits.js';
11
- import { PX_TO_EMU, resolveAnchorPoint, } from './image.js';
12
9
  import { INTERNAL } from './internal.js';
13
- import { clearCoveredValues, masterOf, rectsOverlap } from './merge.js';
10
+ import { clearCoveredValues, masterOf } from './merge.js';
14
11
  import { PivotTable } from './pivot-table.js';
15
12
  import { deriveCredential, } from './protection.js';
16
13
  import { Range, rangeFrom } from './range.js';
14
+ import { buildRowCells, positionalPlacements, rowPlacements } from './row-input.js';
17
15
  import { Row } from './row.js';
18
- import { buildRowCells, rowPlacements } from './row-input.js';
19
- import { Table, TOTALS_ROW_SUBTOTAL_CODE } from './table.js';
16
+ import { Table } from './table.js';
17
+ import { WorksheetComments } from './worksheet-comments.js';
20
18
  import { WORKSHEET_MODEL_FACETS } from './worksheet-model.js';
19
+ import { WorksheetPictures } from './worksheet-pictures.js';
20
+ const VISIBILITIES = { visible: true, hidden: true, veryHidden: true };
21
+ export function isVisibility(value) {
22
+ return Object.hasOwn(VISIBILITIES, value);
23
+ }
21
24
  export class Worksheet {
22
25
  name;
23
26
  id;
@@ -38,10 +41,12 @@ export class Worksheet {
38
41
  #tables = [];
39
42
  #pivotTables = [];
40
43
  #loadedPivotTables = [];
41
- #commentThreads = [];
44
+ #comments = new WorksheetComments(() => this.name);
42
45
  #merges = [];
43
- #images = [];
44
- #backgroundImageId;
46
+ #images = new WorksheetPictures({
47
+ columnWidth: (col) => this.#columns.get(col + 1)?.width ?? this.properties.defaultColWidth,
48
+ rowHeight: (row) => this.#rowProperties.get(row + 1)?.height ?? this.properties.defaultRowHeight,
49
+ });
45
50
  #preservedReferences = [];
46
51
  #mergeRects = [];
47
52
  #dataValidations = new DataValidationOverlay();
@@ -60,14 +65,20 @@ export class Worksheet {
60
65
  merges: this.#merges,
61
66
  mergeRects: this.#mergeRects,
62
67
  tables: this.#tables,
63
- images: this.#images,
68
+ images: this.#images.anchors,
69
+ dataValidations: this.#dataValidations,
70
+ conditionalFormattings: this.#conditionalFormattings,
71
+ comments: this.#comments,
72
+ autoFilter: {
73
+ get: () => this.#autoFilter,
74
+ set: (next) => {
75
+ this.#autoFilter = next;
76
+ },
77
+ },
64
78
  });
65
79
  }
66
80
  getCell(reference) {
67
- const { col, row } = decodeAddress(reference);
68
- if (col === undefined || row === undefined) {
69
- throw new SyntaxError(`"${reference}" is not a single-cell reference — it omits a column or row`);
70
- }
81
+ const { col, row } = decodeCellRef(reference);
71
82
  const master = masterOf(this.#mergeRects, row, col);
72
83
  return this.#cellAt(master.row, master.col);
73
84
  }
@@ -153,46 +164,17 @@ export class Worksheet {
153
164
  }
154
165
  }
155
166
  addTable(options) {
156
- const table = new Table(options, (row, col, value, style) => {
157
- const cell = this.#cellAt(row, col);
158
- cell.value = value;
159
- if (style !== undefined)
160
- applyCellStyle(cell, style);
161
- }, (row) => this.spliceRows(row, 0, []));
167
+ const table = new Table(options, {
168
+ holdsValue: (row, col) => this.hasCell(row, col) && this.#cellAt(row, col).value != null,
169
+ writeCell: (row, col, value, style) => {
170
+ const cell = this.#cellAt(row, col);
171
+ cell.value = value;
172
+ if (style !== undefined)
173
+ applyCellStyle(cell, style);
174
+ },
175
+ insertRow: (row) => this.spliceRows(row, 0, []),
176
+ });
162
177
  this.#tables.push(table);
163
- if (table.headerRow) {
164
- const { top, left } = table.region;
165
- table.columns.forEach((column, index) => {
166
- const col = left + index;
167
- if (this.hasCell(top, col) && this.#cellAt(top, col).value != null)
168
- return;
169
- this.#cellAt(top, col).value = column.name;
170
- });
171
- }
172
- if (table.totalsRow) {
173
- const { left, bottom } = table.region;
174
- table.columns.forEach((column, index) => {
175
- const col = left + index;
176
- if (this.hasCell(bottom, col) && this.#cellAt(bottom, col).value != null)
177
- return;
178
- if (column.totalsRowLabel !== undefined) {
179
- this.#cellAt(bottom, col).value = column.totalsRowLabel;
180
- return;
181
- }
182
- if (column.totalsRowFunction !== undefined) {
183
- const code = TOTALS_ROW_SUBTOTAL_CODE[column.totalsRowFunction];
184
- if (code !== undefined) {
185
- this.#cellAt(bottom, col).value = {
186
- formula: `SUBTOTAL(${code},${table.name}[${column.name}])`,
187
- };
188
- }
189
- else if (column.totalsRowFunction === 'custom' &&
190
- column.totalsRowFormula !== undefined) {
191
- this.#cellAt(bottom, col).value = { formula: column.totalsRowFormula };
192
- }
193
- }
194
- });
195
- }
196
178
  return table;
197
179
  }
198
180
  get tables() {
@@ -213,82 +195,34 @@ export class Worksheet {
213
195
  return this.#loadedPivotTables;
214
196
  }
215
197
  addCommentThread(thread) {
216
- const taken = new Set(this.#commentThreads.flatMap((held) => held.comments.map((comment) => comment.id)));
217
- const comments = thread.comments.map((comment) => {
218
- const id = commentThreadGuid(comment.id, 'a comment id');
219
- if (taken.has(id)) {
220
- throw new SyntaxError(`a comment id must be unique within a sheet, but "${id}" is already used on "${this.name}" — ` +
221
- 'a reply and the legacy fallback comment both find their thread by it');
222
- }
223
- taken.add(id);
224
- return {
225
- ...comment,
226
- id,
227
- ...(comment.personId !== undefined
228
- ? { personId: commentThreadGuid(comment.personId, "a comment's author id") }
229
- : {}),
230
- mentions: comment.mentions.map((mention) => ({
231
- ...mention,
232
- personId: commentThreadGuid(mention.personId, "a mention's person id"),
233
- startIndex: commentThreadOffset(mention.startIndex, "a mention's startIndex"),
234
- length: commentThreadOffset(mention.length, "a mention's length"),
235
- ...(mention.mentionId !== undefined
236
- ? { mentionId: commentThreadGuid(mention.mentionId, 'a mention id') }
237
- : {}),
238
- })),
239
- };
240
- });
241
- this.#commentThreads.push({ ...thread, ref: this.#anchorRef(thread.ref), comments });
198
+ this.#comments.add(thread);
242
199
  }
243
200
  get commentThreads() {
244
- return this.#commentThreads;
245
- }
246
- #anchorRef(reference) {
247
- const { col, row } = decodeAddress(reference);
248
- if (col === undefined || row === undefined) {
249
- throw new SyntaxError(`"${reference}" is not a single-cell reference — it omits a column or row`);
250
- }
251
- return encodeAddress(col, row);
201
+ return this.#comments.threads;
252
202
  }
253
203
  commentThreadAt(reference) {
254
- const anchor = this.#anchorRef(reference);
255
- return this.#commentThreads.find((thread) => thread.ref === anchor);
204
+ return this.#comments.at(reference);
256
205
  }
257
206
  addImage(imageId, anchor) {
258
- const columnWidth = (col) => this.#columns.get(col + 1)?.width ?? this.properties.defaultColWidth;
259
- const rowHeight = (row) => this.#rowProperties.get(row + 1)?.height ?? this.properties.defaultRowHeight;
260
- if ('ext' in anchor) {
261
- const ext = {
262
- cx: Math.round(anchor.ext.width * PX_TO_EMU),
263
- cy: Math.round(anchor.ext.height * PX_TO_EMU),
264
- };
265
- const from = resolveAnchorPoint(anchor.tl, columnWidth, rowHeight);
266
- this.#images.push({ imageId, anchor: { from, ext } });
267
- return;
268
- }
269
- const from = resolveAnchorPoint(anchor.tl, columnWidth, rowHeight);
270
- const to = resolveAnchorPoint(anchor.br, columnWidth, rowHeight);
271
- const twoCell = anchor.editAs !== undefined ? { from, to, editAs: anchor.editAs } : { from, to };
272
- this.#images.push({ imageId, anchor: twoCell });
207
+ this.#images.add(imageId, anchor);
273
208
  }
274
209
  addImageAnchor(imageId, anchor) {
275
- this.#images.push({ imageId, anchor });
210
+ this.#images.addAnchor(imageId, anchor);
276
211
  }
277
212
  removeImage(imageId) {
278
- const kept = this.#images.filter((image) => image.imageId !== imageId);
279
- replaceContents(this.#images, kept);
213
+ this.#images.remove(imageId);
280
214
  }
281
215
  get images() {
282
- return this.#images;
216
+ return this.#images.anchors;
283
217
  }
284
218
  addBackgroundImage(imageId) {
285
- this.#backgroundImageId = imageId;
219
+ this.#images.setBackground(imageId);
286
220
  }
287
221
  removeBackgroundImage() {
288
- this.#backgroundImageId = undefined;
222
+ this.#images.setBackground(undefined);
289
223
  }
290
224
  get backgroundImageId() {
291
- return this.#backgroundImageId;
225
+ return this.#images.backgroundImageId;
292
226
  }
293
227
  get preservedReferences() {
294
228
  return this.#preservedReferences;
@@ -341,18 +275,11 @@ export class Worksheet {
341
275
  return this.#conditionalFormattings.entries;
342
276
  }
343
277
  dataValidationAt(reference) {
344
- const { col, row } = decodeAddress(reference);
345
- if (col === undefined || row === undefined)
346
- return undefined;
347
- return this.#dataValidations.at(col, row);
278
+ const cell = tryDecodeCellRef(reference);
279
+ return cell === undefined ? undefined : this.#dataValidations.at(cell.col, cell.row);
348
280
  }
349
281
  spliceRows(start, count, ...inserts) {
350
- if (!Number.isInteger(start) || start < 1) {
351
- throw new RangeError(`splice start ${start} is out of bounds — rows start at 1`);
352
- }
353
- if (!Number.isInteger(count) || count < 0) {
354
- throw new RangeError(`splice count ${count} is invalid — it must be a non-negative integer`);
355
- }
282
+ assertStartAndCount('splice', 'row', start, count);
356
283
  const inserted = inserts.map((values, i) => buildRowCells(start + i, values, this.#columns));
357
284
  this.#edits.spliceRows(start, count, inserted);
358
285
  }
@@ -394,12 +321,7 @@ export class Worksheet {
394
321
  }
395
322
  duplicateRow(start, options = {}) {
396
323
  const { count = 1, insert = true } = options;
397
- if (!Number.isInteger(start) || start < 1) {
398
- throw new RangeError(`duplicate start ${start} is out of bounds — rows start at 1`);
399
- }
400
- if (!Number.isInteger(count) || count < 0) {
401
- throw new RangeError(`duplicate count ${count} is invalid — it must be a non-negative integer`);
402
- }
324
+ assertStartAndCount('duplicate', 'row', start, count);
403
325
  const source = this.#rows.get(start);
404
326
  const snapshot = (destRow) => {
405
327
  const row = new Map();
@@ -422,12 +344,7 @@ export class Worksheet {
422
344
  }
423
345
  }
424
346
  spliceColumns(start, count, ...inserts) {
425
- if (!Number.isInteger(start) || start < 1) {
426
- throw new RangeError(`splice start ${start} is out of bounds — columns start at 1`);
427
- }
428
- if (!Number.isInteger(count) || count < 0) {
429
- throw new RangeError(`splice count ${count} is invalid — it must be a non-negative integer`);
430
- }
347
+ assertStartAndCount('splice', 'column', start, count);
431
348
  this.#edits.spliceColumns(start, count, inserts);
432
349
  }
433
350
  insertColumn(pos, values) {
@@ -440,15 +357,11 @@ export class Worksheet {
440
357
  let index = this.columnCount;
441
358
  return columns.map((values) => {
442
359
  index += 1;
443
- const cells = [];
444
- values.forEach((value, i) => {
445
- if (value === undefined)
446
- return;
447
- const cell = this.#cellAt(i + 1, index);
360
+ return positionalPlacements(values).map(([row, value]) => {
361
+ const cell = this.#cellAt(row, index);
448
362
  cell.value = value;
449
- cells.push(cell);
363
+ return cell;
450
364
  });
451
- return cells;
452
365
  });
453
366
  }
454
367
  get model() {
@@ -510,7 +423,7 @@ export class Worksheet {
510
423
  this.#loadedPivotTables.push(pivot);
511
424
  },
512
425
  restoreCommentThreads: (threads) => {
513
- replaceContents(this.#commentThreads, threads);
426
+ this.#comments.restore(threads);
514
427
  },
515
428
  addPreservedReference: (reference) => {
516
429
  this.#preservedReferences.push(reference);
@@ -552,3 +465,11 @@ export class Worksheet {
552
465
  },
553
466
  };
554
467
  }
468
+ function assertStartAndCount(verb, axis, start, count) {
469
+ if (!Number.isInteger(start) || start < 1) {
470
+ throw new RangeError(`${verb} start ${start} is out of bounds: ${axis}s start at 1`);
471
+ }
472
+ if (!Number.isInteger(count) || count < 0) {
473
+ throw new RangeError(`${verb} count ${count} is invalid: it must be a non-negative integer`);
474
+ }
475
+ }
@@ -1,7 +1,7 @@
1
1
  import { XlsxError } from '../errors.ts';
2
2
  /**
3
3
  * Thrown when a `customUI` ribbon-customisation part (`customUI/customUI.xml` or
4
- * `customUI/customUI14.xml`) is present but cannot be parsed into a {@link CustomUiDocument} malformed
4
+ * `customUI/customUI14.xml`) is present but cannot be parsed into a {@link CustomUiDocument}: malformed
5
5
  * XML, a missing or unrecognised `<customUI>` root namespace, or nesting deep enough to look hostile.
6
6
  * A workbook with no ribbon customisation never produces this: {@link Workbook.customUI} is an empty
7
7
  * array instead.
@@ -1,4 +1,4 @@
1
- /** The `customUI` schema a part is written against the read model keys off this, not the (frequently
1
+ /** The `customUI` schema a part is written against. The read model keys off this, not the (frequently
2
2
  * mis-copied) relationship type. `2007` is the original RibbonX (`customUI.xml`); `2010` is the later
3
3
  * schema (`customUI14.xml`) that also carries backstage/QAT/commands. */
4
4
  export type RibbonDialect = '2007' | '2010';
@@ -15,7 +15,7 @@ export declare function isCustomUiRelType(type: string): boolean;
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
18
- * actually carried including the many `get*` dynamic callbacks and layout hints not modelled here
18
+ * actually carried, including the many `get*` dynamic callbacks and layout hints not modelled here,
19
19
  * is preserved verbatim in {@link attributes}, so nothing is lost. Container controls (a `menu`,
20
20
  * `splitButton`, `gallery`, `dropDown`, `box`, …) carry their nested controls/items in {@link children}.
21
21
  */
@@ -29,7 +29,7 @@ export interface RibbonControl {
29
29
  readonly idMso?: string;
30
30
  /** The static label, when the element carries one (a dynamic label uses `getLabel`, in {@link attributes}). */
31
31
  readonly label?: string;
32
- /** The callback procedure name invoked on activation the macro a click runs. */
32
+ /** The callback procedure name invoked on activation: the macro a click runs. */
33
33
  readonly onAction?: string;
34
34
  /** Every attribute on the element, verbatim and entity-decoded. The typed fields above are lifted from
35
35
  * here; this map is the complete record, including attributes this model does not lift out. */
@@ -9,26 +9,29 @@ export function isCustomUiRelType(type) {
9
9
  return type.endsWith('/ui/extensibility');
10
10
  }
11
11
  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
- ]);
12
+ const KNOWN_KINDS = {
13
+ button: true,
14
+ toggleButton: true,
15
+ checkBox: true,
16
+ editBox: true,
17
+ dropDown: true,
18
+ comboBox: true,
19
+ gallery: true,
20
+ menu: true,
21
+ dynamicMenu: true,
22
+ splitButton: true,
23
+ buttonGroup: true,
24
+ box: true,
25
+ labelControl: true,
26
+ separator: true,
27
+ menuSeparator: true,
28
+ dialogBoxLauncher: true,
29
+ control: true,
30
+ item: true,
31
+ };
32
+ function isKnownControlKind(local) {
33
+ return Object.hasOwn(KNOWN_KINDS, local);
34
+ }
32
35
  export function parseCustomUi(input) {
33
36
  const xml = typeof input === 'string' ? input : strFromU8(input);
34
37
  let root;
@@ -79,9 +82,7 @@ function toGroup(groupEl) {
79
82
  };
80
83
  }
81
84
  function toControl(el) {
82
- const kind = KNOWN_KINDS.has(el.local)
83
- ? el.local
84
- : 'unknown';
85
+ const kind = isKnownControlKind(el.local) ? el.local : 'unknown';
85
86
  return {
86
87
  kind,
87
88
  ...identity(el.attrs),
@@ -1,14 +1,14 @@
1
- export { type CellAddress, columnToNumber, decodeAddress, decodeRange, encodeAddress, MAX_COLUMN, MAX_ROW, numberToColumn, type RangeAddress, } from '../core/address.ts';
1
+ export { type CellAddress, columnToNumber, decodeAddress, decodeRange, encodeAddress, type GridRect, MAX_COLUMN, MAX_ROW, numberToColumn, type RangeAddress, } from '../core/address.ts';
2
2
  export type { AutoFilter, CustomFilter, CustomFilterOperator, CustomFilterPredicate, FilterColumn, FilterCriteria, ValuesFilter, } from '../core/autofilter.ts';
3
3
  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
- export type { Comment, CommentThread, Mention, Person } from '../core/comment-thread.ts';
7
- export type { CfValueObject, ConditionalFormatting, ConditionalFormattingRule, } from '../core/conditional-formatting.ts';
6
+ export type { Comment, CommentThread, Mention, MentionRef, Person } from '../core/comment-thread.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
- export { type AnchoredImage, type AnchorPoint, type Extent, type ImageAnchor, type ImageEditAs, isOneCellAnchor, type OneCellAnchor, PX_TO_EMU, type TwoCellAnchor, type WorkbookImage, } from '../core/image.ts';
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';
@@ -18,8 +18,8 @@ export type { Alignment, Border, BorderEdge, BorderStyle, CellStyle, Color, Diff
18
18
  export { Table, type TableColumn, type TableColumnStyle, type TableOptions, type TableRegion, type TableStyleInfo, } from '../core/table.ts';
19
19
  export { isTableStyleElementType, STRIPE_ELEMENT_TYPES, TABLE_STYLE_ELEMENT_TYPES, type TableStyle, type TableStyleElement, type TableStyleElementType, } from '../core/table-style.ts';
20
20
  export { estimateWrappedLines } from '../core/text-metrics.ts';
21
- export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, parseThemeColorScheme, THEME_COLOR_SLOTS, type ThemeColorScheme, type ThemeColorSlot, type ThemeFontScheme, type ThemeOverrides, } from '../core/theme.ts';
21
+ export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, THEME_COLOR_SLOTS, type ThemeColorScheme, type ThemeColorSlot, type ThemeFontScheme, type ThemeOverrides, } from '../core/theme.ts';
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';
@@ -10,7 +10,7 @@ export { Row } from '../core/row.js';
10
10
  export { Table, } from '../core/table.js';
11
11
  export { isTableStyleElementType, STRIPE_ELEMENT_TYPES, TABLE_STYLE_ELEMENT_TYPES, } from '../core/table-style.js';
12
12
  export { estimateWrappedLines } from '../core/text-metrics.js';
13
- export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, parseThemeColorScheme, THEME_COLOR_SLOTS, } from '../core/theme.js';
13
+ export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, THEME_COLOR_SLOTS, } from '../core/theme.js';
14
14
  export { cellValueToText, coerceCellValue, detectValueType, ERROR_CODES, isDataTableFormulaValue, isErrorCode, isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, richTextToPlain, ValueType, } from '../core/value.js';
15
15
  export { DEFAULT_WORKBOOK_VIEW, Workbook, } from '../core/workbook.js';
16
16
  export { Worksheet, } from '../core/worksheet.js';
@@ -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';
@@ -1,2 +1,2 @@
1
1
  export { parseVbaProject, type VbaModule, type VbaModuleKind, type VbaProject, type VbaProjectSignature, type VbaProjectSignatureKind, } from '../vba/project.ts';
2
- export { addVbaReference, removeVbaModule, type VbaLibraryReference, } from '../vba/project-editor.ts';
2
+ export { addVbaReference, removeVbaModule, type VbaLibraryReference } from '../vba/project-editor.ts';
@@ -1,2 +1,2 @@
1
1
  export { parseVbaProject, } from '../vba/project.js';
2
- export { addVbaReference, removeVbaModule, } from '../vba/project-editor.js';
2
+ export { addVbaReference, removeVbaModule } from '../vba/project-editor.js';
@@ -1,5 +1,5 @@
1
1
  export { editXlsxVbaAddReference, editXlsxVbaRemoveModule } from '../io/xlsx/edit-vba.ts';
2
2
  export { type ReadXlsxOptions, readXlsx } from '../io/xlsx/read.ts';
3
3
  export { type ReadSheetRowsOptions, readSheetRows, readWorkbookStream, } from '../io/xlsx/read-rows.ts';
4
+ export { DEFAULT_THEME_XML, parseThemeColorScheme, parseThemeFontScheme, } from '../io/xlsx/theme-xml.ts';
4
5
  export { type WriteOptions, writeXlsx, writeXlsxAsync } from '../io/xlsx/write.ts';
5
- export { type CalcProperties, StreamedRow, WorkbookStreamWriter, type WorkbookStreamWriterOptions, WorksheetStreamWriter, } from '../io/xlsx/write-stream.ts';
@@ -1,5 +1,5 @@
1
1
  export { editXlsxVbaAddReference, editXlsxVbaRemoveModule } from '../io/xlsx/edit-vba.js';
2
2
  export { readXlsx } from '../io/xlsx/read.js';
3
3
  export { readSheetRows, readWorkbookStream, } from '../io/xlsx/read-rows.js';
4
+ export { DEFAULT_THEME_XML, parseThemeColorScheme, parseThemeFontScheme, } from '../io/xlsx/theme-xml.js';
4
5
  export { writeXlsx, writeXlsxAsync } from '../io/xlsx/write.js';
5
- export { StreamedRow, WorkbookStreamWriter, WorksheetStreamWriter, } from '../io/xlsx/write-stream.js';
package/dist/errors.d.ts CHANGED
@@ -2,22 +2,22 @@
2
2
  * What kind of failure an {@link XlsxError} reports. This is the branch most callers want, and it is
3
3
  * deliberately coarse: the four answers are the four different things a caller would *do* next.
4
4
  *
5
- * - `'unsupported-format'` the input is not a container this library reads at all (a legacy `.xls`,
5
+ * - `'unsupported-format'`: the input is not a container this library reads at all (a legacy `.xls`,
6
6
  * a blob that is not a spreadsheet). Nothing is wrong with the file; it is the wrong file *for us*.
7
- * - `'malformed-input'` a part we do read is corrupt or does not conform to its specification. The
7
+ * - `'malformed-input'`: a part we do read is corrupt or does not conform to its specification. The
8
8
  * file is broken, or hostile.
9
- * - `'authoring'` the caller described a document that cannot exist. The bug is in the calling code.
10
- * - `'internal'` an invariant this library maintains did not hold. It should be unreachable; if it
9
+ * - `'authoring'`: the caller described a document that cannot exist. The bug is in the calling code.
10
+ * - `'internal'`: an invariant this library maintains did not hold. It should be unreachable; if it
11
11
  * fires, the bug is ours.
12
12
  *
13
13
  * There is deliberately no "not implemented yet" code. Every candidate for one turned out to be an
14
- * unreachable exhaustiveness guard (so: `'internal'`), and the one genuine feature gap a binary
15
- * `.xlsb` cannot be row-streamed is already reported by {@link UnsupportedFormatError}'s `format`
14
+ * unreachable exhaustiveness guard (so: `'internal'`), and the one genuine feature gap (a binary
15
+ * `.xlsb` cannot be row-streamed) is already reported by {@link UnsupportedFormatError}'s `format`
16
16
  * branch. A code with no throw site would be a promise the library does not keep.
17
17
  *
18
18
  * **Which of these is worth reporting upstream.** `'internal'` always is, and says so at runtime.
19
19
  * `'unsupported-format'` and `'malformed-input'` are worth reporting when the file in hand opens
20
- * cleanly in Excel that combination means we are the ones who cannot read it, which is a gap, not
20
+ * cleanly in Excel: that combination means we are the ones who cannot read it, which is a gap, not
21
21
  * a corrupt input. `'authoring'` is worth reporting only if the document it refused is one a real
22
22
  * workbook can express. See `skills/ts-xlsx-upstream` for how to file one.
23
23
  */
@@ -29,7 +29,7 @@ export type XlsxErrorCode = 'unsupported-format' | 'malformed-input' | 'authorin
29
29
  *
30
30
  * Abstract on purpose: a failure always has a kind, so there is never a reason to throw the base.
31
31
  * Every subclass fixes {@link code} to a literal, which makes the class hierarchy a discriminated
32
- * union narrowing on `error.code` narrows the type.
32
+ * union, so narrowing on `error.code` narrows the type.
33
33
  *
34
34
  * The constructor is inherited from `Error`, so every subclass accepts `{cause}`; layers that wrap a
35
35
  * lower-level failure are expected to pass it rather than flatten it into the message.
@@ -43,13 +43,13 @@ export declare abstract class XlsxError extends Error {
43
43
  * worksheets. The document model, not a single argument, is what is wrong.
44
44
  *
45
45
  * **Where the line falls against native errors.** A single scalar that is out of range, unparseable,
46
- * or the wrong type stays a native `RangeError` / `SyntaxError` / `TypeError` those types exist for
46
+ * or the wrong type stays a native `RangeError` / `SyntaxError` / `TypeError`: those types exist for
47
47
  * exactly that, every caller already reads them, and wrapping them would make this taxonomy a
48
48
  * re-implementation of the language's. `AuthoringError` starts where a *composite* is internally
49
49
  * inconsistent, or contradicts something a workbook can express. `getColumn(0)` is a `RangeError`;
50
50
  * a table that names a column twice is an `AuthoringError`.
51
51
  *
52
- * It is always the calling code that is wrong, never the input file a malformed file raises a
52
+ * It is always the calling code that is wrong, never the input file; a malformed file raises a
53
53
  * `'malformed-input'` error instead.
54
54
  */
55
55
  export declare class AuthoringError extends XlsxError {
@@ -57,7 +57,7 @@ export declare class AuthoringError extends XlsxError {
57
57
  readonly code = "authoring";
58
58
  }
59
59
  /**
60
- * Thrown where an invariant the library itself maintains turns out not to hold typically an index
60
+ * Thrown where an invariant the library itself maintains turns out not to hold: typically an index
61
61
  * that a preceding pass proved to be in range, re-checked because `noUncheckedIndexedAccess` makes
62
62
  * the possibility of `undefined` explicit and casting it away would be worse.
63
63
  *
@@ -67,7 +67,7 @@ export declare class AuthoringError extends XlsxError {
67
67
  *
68
68
  * It is the one class in the taxonomy that rewrites its own message, appending {@link REPORT_NOTICE}
69
69
  * below the invariant that broke. The constructor is where that lives so a throw site added later
70
- * inherits it the alternative, a notice pasted at each of the throw sites, is one every future
70
+ * inherits it; the alternative, a notice pasted at each of the throw sites, is one every future
71
71
  * site can forget. Every other class leaves `message` exactly as given: `'malformed-input'` is a
72
72
  * routine outcome for a library that reads untrusted files, and a "report this" banner on each
73
73
  * corrupt input would train callers to ignore the one banner that always means something.
@@ -1,10 +1,9 @@
1
1
  import { Workbook } from '../../core/workbook.js';
2
- import { AuthoringError } from '../../errors.js';
3
2
  export function readCsv(input, options = {}) {
4
- 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));
5
4
  const delimiter = options.delimiter ?? ',';
6
5
  if (delimiter.length !== 1) {
7
- throw new AuthoringError(`CSV delimiter must be a single character, got ${JSON.stringify(delimiter)}`);
6
+ throw new RangeError(`CSV delimiter must be a single character, got ${JSON.stringify(delimiter)}`);
8
7
  }
9
8
  let rows = parseCsvRows(text, delimiter);
10
9
  if (options.headers)