@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.
Files changed (225) hide show
  1. package/README.md +4 -1
  2. package/dist/bytes.d.ts +7 -0
  3. package/dist/bytes.js +22 -0
  4. package/dist/core/address.d.ts +60 -2
  5. package/dist/core/address.js +40 -24
  6. package/dist/core/autofilter.d.ts +5 -4
  7. package/dist/core/autofilter.js +34 -35
  8. package/dist/core/axis-handle.d.ts +10 -0
  9. package/dist/core/axis-handle.js +17 -0
  10. package/dist/core/cell.d.ts +25 -0
  11. package/dist/core/cell.js +9 -3
  12. package/dist/core/clone.d.ts +29 -0
  13. package/dist/core/clone.js +24 -0
  14. package/dist/core/color-resolution.js +2 -4
  15. package/dist/core/column.d.ts +5 -1
  16. package/dist/core/column.js +34 -35
  17. package/dist/core/comment-thread.js +2 -1
  18. package/dist/core/conditional-formatting-overlay.d.ts +2 -1
  19. package/dist/core/conditional-formatting-overlay.js +2 -2
  20. package/dist/core/conditional-formatting.d.ts +14 -6
  21. package/dist/core/conditional-formatting.js +45 -43
  22. package/dist/core/containers.d.ts +12 -0
  23. package/dist/core/containers.js +5 -0
  24. package/dist/core/data-validation-overlay.d.ts +2 -1
  25. package/dist/core/data-validation-overlay.js +4 -4
  26. package/dist/core/data-validation.d.ts +9 -3
  27. package/dist/core/data-validation.js +21 -18
  28. package/dist/core/date-format.d.ts +20 -0
  29. package/dist/core/date-format.js +160 -0
  30. package/dist/core/date.d.ts +43 -8
  31. package/dist/core/date.js +18 -2
  32. package/dist/core/formula.d.ts +26 -0
  33. package/dist/core/formula.js +20 -4
  34. package/dist/core/grid-edits.d.ts +2 -3
  35. package/dist/core/grid-edits.js +69 -70
  36. package/dist/core/grid-shift.d.ts +58 -9
  37. package/dist/core/grid-shift.js +26 -4
  38. package/dist/core/image.d.ts +14 -10
  39. package/dist/core/image.js +9 -9
  40. package/dist/core/internal.d.ts +8 -2
  41. package/dist/core/limits.d.ts +23 -0
  42. package/dist/core/limits.js +4 -0
  43. package/dist/core/merge-index.d.ts +31 -0
  44. package/dist/core/merge-index.js +60 -0
  45. package/dist/core/merge.d.ts +5 -14
  46. package/dist/core/merge.js +18 -27
  47. package/dist/core/page-setup.d.ts +72 -2
  48. package/dist/core/page-setup.js +34 -8
  49. package/dist/core/pivot-table.d.ts +1 -1
  50. package/dist/core/pivot-table.js +67 -53
  51. package/dist/core/protection.js +2 -11
  52. package/dist/core/range.js +34 -30
  53. package/dist/core/row-input.js +2 -2
  54. package/dist/core/row.d.ts +5 -1
  55. package/dist/core/row.js +22 -23
  56. package/dist/core/style.d.ts +134 -75
  57. package/dist/core/style.js +112 -53
  58. package/dist/core/table-style.d.ts +1 -1
  59. package/dist/core/table-style.js +4 -4
  60. package/dist/core/table.d.ts +28 -10
  61. package/dist/core/table.js +54 -39
  62. package/dist/core/theme.js +6 -5
  63. package/dist/core/used-extent.d.ts +44 -0
  64. package/dist/core/used-extent.js +102 -0
  65. package/dist/core/value.d.ts +9 -0
  66. package/dist/core/value.js +36 -34
  67. package/dist/core/workbook-media.d.ts +34 -0
  68. package/dist/core/workbook-media.js +44 -0
  69. package/dist/core/workbook-styles.d.ts +67 -1
  70. package/dist/core/workbook-vba.js +3 -2
  71. package/dist/core/workbook.d.ts +23 -2
  72. package/dist/core/workbook.js +24 -40
  73. package/dist/core/worksheet-comments.d.ts +2 -1
  74. package/dist/core/worksheet-comments.js +20 -13
  75. package/dist/core/worksheet-merges.d.ts +44 -0
  76. package/dist/core/worksheet-merges.js +59 -0
  77. package/dist/core/worksheet-model.d.ts +9 -1
  78. package/dist/core/worksheet-model.js +24 -9
  79. package/dist/core/worksheet.d.ts +72 -12
  80. package/dist/core/worksheet.js +68 -70
  81. package/dist/customui/ribbon.js +6 -7
  82. package/dist/entries/core.d.ts +18 -13
  83. package/dist/entries/core.js +12 -6
  84. package/dist/entries/xlsx.d.ts +1 -1
  85. package/dist/errors.d.ts +64 -0
  86. package/dist/errors.js +22 -0
  87. package/dist/hex.d.ts +8 -0
  88. package/dist/hex.js +3 -0
  89. package/dist/io/csv/delimiter.d.ts +9 -0
  90. package/dist/io/csv/delimiter.js +6 -0
  91. package/dist/io/csv/read.js +11 -4
  92. package/dist/io/csv/write.d.ts +9 -2
  93. package/dist/io/csv/write.js +9 -38
  94. package/dist/io/opc/inflate.js +15 -1
  95. package/dist/io/opc/namespaces.d.ts +16 -0
  96. package/dist/io/opc/namespaces.js +4 -0
  97. package/dist/io/opc/part-paths.d.ts +10 -0
  98. package/dist/io/opc/part-paths.js +16 -1
  99. package/dist/io/opc/read-opc.d.ts +69 -10
  100. package/dist/io/opc/read-opc.js +49 -55
  101. package/dist/io/opc/read-options.d.ts +5 -1
  102. package/dist/io/opc/rels.d.ts +1 -6
  103. package/dist/io/opc/rels.js +1 -4
  104. package/dist/io/opc/sniff-format.d.ts +3 -3
  105. package/dist/io/opc/sniff-format.js +2 -2
  106. package/dist/io/style/xf-style.d.ts +48 -2
  107. package/dist/io/style/xf-style.js +14 -0
  108. package/dist/io/xlsb/formula.d.ts +2 -2
  109. package/dist/io/xlsb/formula.js +49 -34
  110. package/dist/io/xlsb/primitives.js +8 -14
  111. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  112. package/dist/io/xlsb/ptg-functions.js +385 -596
  113. package/dist/io/xlsb/read-styles.js +50 -83
  114. package/dist/io/xlsb/read-worksheet.d.ts +20 -1
  115. package/dist/io/xlsb/read-worksheet.js +73 -89
  116. package/dist/io/xlsb/read.d.ts +6 -3
  117. package/dist/io/xlsb/read.js +47 -29
  118. package/dist/io/xlsb/record-stream.d.ts +30 -0
  119. package/dist/io/xlsb/record-stream.js +20 -0
  120. package/dist/io/xlsb/record-types.d.ts +1 -5
  121. package/dist/io/xlsb/record-types.js +1 -5
  122. package/dist/io/xlsx/cell-accumulator.d.ts +9 -2
  123. package/dist/io/xlsx/cell-accumulator.js +32 -9
  124. package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
  125. package/dist/io/xlsx/cell-style-resolution.js +29 -0
  126. package/dist/io/xlsx/cell-value.d.ts +5 -3
  127. package/dist/io/xlsx/cell-value.js +13 -15
  128. package/dist/io/xlsx/color-xml.js +3 -2
  129. package/dist/io/xlsx/column-budget.d.ts +26 -0
  130. package/dist/io/xlsx/column-budget.js +22 -0
  131. package/dist/io/xlsx/comments.d.ts +6 -2
  132. package/dist/io/xlsx/comments.js +36 -31
  133. package/dist/io/xlsx/conditional-formatting.d.ts +9 -5
  134. package/dist/io/xlsx/conditional-formatting.js +51 -46
  135. package/dist/io/xlsx/data-validation.d.ts +9 -7
  136. package/dist/io/xlsx/data-validation.js +38 -35
  137. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  138. package/dist/io/xlsx/edit-vba.js +15 -29
  139. package/dist/io/xlsx/font-xml.d.ts +7 -0
  140. package/dist/io/xlsx/font-xml.js +104 -0
  141. package/dist/io/xlsx/hyperlinks.d.ts +41 -7
  142. package/dist/io/xlsx/hyperlinks.js +40 -28
  143. package/dist/io/xlsx/images.js +24 -25
  144. package/dist/io/xlsx/namespaces.d.ts +16 -0
  145. package/dist/io/xlsx/namespaces.js +7 -0
  146. package/dist/io/xlsx/package-plan.d.ts +23 -3
  147. package/dist/io/xlsx/package-plan.js +66 -25
  148. package/dist/io/xlsx/part-names.d.ts +41 -0
  149. package/dist/io/xlsx/part-names.js +46 -0
  150. package/dist/io/xlsx/pivot.js +8 -7
  151. package/dist/io/xlsx/read-parts.d.ts +59 -0
  152. package/dist/io/xlsx/read-parts.js +252 -0
  153. package/dist/io/xlsx/read-pivot.js +3 -1
  154. package/dist/io/xlsx/read-repair.d.ts +26 -0
  155. package/dist/io/xlsx/read-repair.js +39 -0
  156. package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +1 -1
  157. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +7 -3
  158. package/dist/io/xlsx/read-rows.d.ts +18 -5
  159. package/dist/io/xlsx/read-rows.js +50 -37
  160. package/dist/io/xlsx/read-shared-strings.js +5 -3
  161. package/dist/io/xlsx/read-styles.d.ts +1 -6
  162. package/dist/io/xlsx/read-styles.js +65 -109
  163. package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
  164. package/dist/io/xlsx/read-workbook-xml.js +170 -0
  165. package/dist/io/xlsx/read-worksheet.d.ts +11 -1
  166. package/dist/io/xlsx/read-worksheet.js +51 -193
  167. package/dist/io/xlsx/read.d.ts +5 -13
  168. package/dist/io/xlsx/read.js +111 -436
  169. package/dist/io/xlsx/rich-text.js +1 -1
  170. package/dist/io/xlsx/row-position.d.ts +18 -0
  171. package/dist/io/xlsx/row-position.js +11 -0
  172. package/dist/io/xlsx/row-xml.d.ts +128 -0
  173. package/dist/io/xlsx/row-xml.js +203 -0
  174. package/dist/io/xlsx/shared-formulas.js +2 -2
  175. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  176. package/dist/io/xlsx/sheet-properties.js +166 -37
  177. package/dist/io/xlsx/style-elements.d.ts +43 -0
  178. package/dist/io/xlsx/style-elements.js +156 -0
  179. package/dist/io/xlsx/styles.d.ts +12 -3
  180. package/dist/io/xlsx/styles.js +5 -204
  181. package/dist/io/xlsx/tables.js +11 -14
  182. package/dist/io/xlsx/theme-xml.d.ts +26 -4
  183. package/dist/io/xlsx/theme-xml.js +107 -69
  184. package/dist/io/xlsx/threaded-comments.d.ts +8 -10
  185. package/dist/io/xlsx/threaded-comments.js +3 -1
  186. package/dist/io/xlsx/workbook-xml.d.ts +54 -4
  187. package/dist/io/xlsx/workbook-xml.js +84 -57
  188. package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
  189. package/dist/io/xlsx/worksheet-xml.js +59 -242
  190. package/dist/io/xlsx/write-stream.d.ts +71 -16
  191. package/dist/io/xlsx/write-stream.js +79 -41
  192. package/dist/io/xlsx/write.d.ts +6 -2
  193. package/dist/io/xlsx/write.js +217 -153
  194. package/dist/rel-type.d.ts +16 -0
  195. package/dist/rel-type.js +6 -0
  196. package/dist/token-set.d.ts +28 -0
  197. package/dist/token-set.js +7 -0
  198. package/dist/vba/bytes.d.ts +14 -5
  199. package/dist/vba/bytes.js +16 -6
  200. package/dist/vba/cfb-format.d.ts +32 -0
  201. package/dist/vba/cfb-format.js +13 -0
  202. package/dist/vba/cfb-writer.d.ts +9 -0
  203. package/dist/vba/cfb-writer.js +28 -25
  204. package/dist/vba/cfb.d.ts +13 -1
  205. package/dist/vba/cfb.js +73 -57
  206. package/dist/vba/codepage.js +2 -1
  207. package/dist/vba/errors.d.ts +7 -0
  208. package/dist/vba/ms-ovba.js +79 -23
  209. package/dist/vba/project-editor.js +35 -40
  210. package/dist/vba/project.d.ts +11 -1
  211. package/dist/vba/project.js +40 -21
  212. package/dist/vba/vba-encoding.js +5 -6
  213. package/dist/xml/xml-attrs.d.ts +37 -0
  214. package/dist/xml/xml-attrs.js +30 -0
  215. package/dist/xml/xml-chars.d.ts +40 -0
  216. package/dist/xml/xml-chars.js +12 -0
  217. package/dist/xml/xml-namespaces.d.ts +31 -0
  218. package/dist/xml/xml-namespaces.js +50 -0
  219. package/dist/xml/xml-read.d.ts +128 -81
  220. package/dist/xml/xml-read.js +161 -178
  221. package/dist/xml/xml-scan.d.ts +78 -0
  222. package/dist/xml/xml-scan.js +180 -0
  223. package/dist/xml/xml.d.ts +29 -28
  224. package/dist/xml/xml.js +24 -24
  225. package/package.json +13 -9
@@ -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 { assertAxisInBounds, decodeCellRef, 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,25 +8,25 @@ 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 { clearCoveredValues } from './merge.js';
11
12
  import { PivotTable } from './pivot-table.js';
12
13
  import { deriveCredential, } from './protection.js';
13
14
  import { Range, rangeFrom } from './range.js';
14
15
  import { buildRowCells, positionalPlacements, rowPlacements } from './row-input.js';
15
16
  import { Row } from './row.js';
16
17
  import { Table } from './table.js';
18
+ import { UsedExtent } from './used-extent.js';
17
19
  import { WorksheetComments } from './worksheet-comments.js';
20
+ import { WorksheetMerges } from './worksheet-merges.js';
18
21
  import { WORKSHEET_MODEL_FACETS } from './worksheet-model.js';
19
22
  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
- }
23
+ export const isVisibility = tokenSet({ visible: true, hidden: true, veryHidden: true });
24
24
  export class Worksheet {
25
25
  name;
26
26
  id;
27
27
  state;
28
28
  tabColor;
29
+ codeName;
29
30
  properties = {};
30
31
  outline = {};
31
32
  view = {};
@@ -42,28 +43,33 @@ export class Worksheet {
42
43
  #pivotTables = [];
43
44
  #loadedPivotTables = [];
44
45
  #comments = new WorksheetComments(() => this.name);
45
- #merges = [];
46
+ #merges = new WorksheetMerges();
46
47
  #images = new WorksheetPictures({
47
48
  columnWidth: (col) => this.#columns.get(col + 1)?.width ?? this.properties.defaultColWidth,
48
49
  rowHeight: (row) => this.#rowProperties.get(row + 1)?.height ?? this.properties.defaultRowHeight,
49
50
  });
50
51
  #preservedReferences = [];
51
- #mergeRects = [];
52
52
  #dataValidations = new DataValidationOverlay();
53
53
  #conditionalFormattings = new ConditionalFormattingOverlay();
54
54
  #protection;
55
55
  #autoFilter;
56
56
  #edits;
57
+ #extent;
57
58
  constructor(name, id, state = 'visible') {
58
59
  this.name = name;
59
60
  this.id = id;
60
61
  this.state = state;
62
+ this.#extent = new UsedExtent({
63
+ rows: this.#rows,
64
+ rowProperties: this.#rowProperties,
65
+ columns: this.#columns,
66
+ mergeRects: this.#merges.rects,
67
+ });
61
68
  this.#edits = new GridEdits({
62
69
  rows: this.#rows,
63
70
  rowProperties: this.#rowProperties,
64
71
  columns: this.#columns,
65
72
  merges: this.#merges,
66
- mergeRects: this.#mergeRects,
67
73
  tables: this.#tables,
68
74
  images: this.#images.anchors,
69
75
  dataValidations: this.#dataValidations,
@@ -79,8 +85,7 @@ export class Worksheet {
79
85
  }
80
86
  getCell(reference) {
81
87
  const { col, row } = decodeCellRef(reference);
82
- const master = masterOf(this.#mergeRects, row, col);
83
- return this.#cellAt(master.row, master.col);
88
+ return this[INTERNAL].masterAt(row, col);
84
89
  }
85
90
  hasCell(row, col) {
86
91
  return this.#rows.get(row)?.has(col) ?? false;
@@ -94,33 +99,24 @@ export class Worksheet {
94
99
  getRange(referenceOrTop, left, bottom, right) {
95
100
  if (typeof referenceOrTop === 'string')
96
101
  return rangeFrom(this, referenceOrTop);
97
- return new Range(this, referenceOrTop, left ?? 0, bottom ?? 0, right ?? 0);
102
+ const corners = [left, bottom, right].filter((corner) => corner !== undefined).length + 1;
103
+ if (left === undefined || bottom === undefined || right === undefined) {
104
+ throw new AuthoringError(`getRange(top, left, bottom, right) needs all four corners; got ${corners}`);
105
+ }
106
+ return new Range(this, referenceOrTop, left, bottom, right);
98
107
  }
99
108
  get rowCount() {
100
- let last = 0;
101
- for (const [number, cols] of this.#rows) {
102
- if (number > last && this.#rowHasContent(cols))
103
- last = number;
104
- }
105
- for (const number of this.#rowProperties.keys()) {
106
- if (number > last)
107
- last = number;
108
- }
109
- for (const rect of this.#mergeRects) {
110
- if (rect.bottom > last)
111
- last = rect.bottom;
112
- }
113
- return last;
109
+ return this.#extent.lastRow;
114
110
  }
115
111
  get actualRowCount() {
116
112
  let count = 0;
117
113
  for (const cols of this.#rows.values()) {
118
- if (this.#rowHasContent(cols))
114
+ if (this.#rowIsPopulated(cols))
119
115
  count++;
120
116
  }
121
117
  return count;
122
118
  }
123
- #rowHasContent(cols) {
119
+ #rowIsPopulated(cols) {
124
120
  for (const cell of cols.values()) {
125
121
  if (cell.value !== null)
126
122
  return true;
@@ -128,22 +124,7 @@ export class Worksheet {
128
124
  return false;
129
125
  }
130
126
  get columnCount() {
131
- let last = 0;
132
- for (const cols of this.#rows.values()) {
133
- for (const [col, cell] of cols) {
134
- if (cell.value !== null && col > last)
135
- last = col;
136
- }
137
- }
138
- for (const index of this.#columns.keys()) {
139
- if (index > last)
140
- last = index;
141
- }
142
- for (const rect of this.#mergeRects) {
143
- if (rect.right > last)
144
- last = rect.right;
145
- }
146
- return last;
127
+ return this.#extent.lastColumn;
147
128
  }
148
129
  get usedRange() {
149
130
  const bottom = this.rowCount;
@@ -228,20 +209,14 @@ export class Worksheet {
228
209
  return this.#preservedReferences;
229
210
  }
230
211
  mergeCells(range) {
231
- const { top, left, bottom, right } = decodeRange(range);
232
- if (top !== undefined && left !== undefined && bottom !== undefined && right !== undefined) {
233
- const rect = { top, left, bottom, right };
234
- const clash = this.#mergeRects.find((existing) => rectsOverlap(existing, rect));
235
- if (clash) {
236
- throw new AuthoringError(`merged range "${range}" overlaps an existing merged region`);
237
- }
238
- this.#mergeRects.push(rect);
212
+ const rect = this.#merges.add(range);
213
+ if (rect !== undefined) {
214
+ this.#extent.noteMerge(rect);
239
215
  clearCoveredValues(this.#rows, rect);
240
216
  }
241
- this.#merges.push(range);
242
217
  }
243
218
  get merges() {
244
- return this.#merges;
219
+ return this.#merges.ranges;
245
220
  }
246
221
  get autoFilter() {
247
222
  return this.#autoFilter;
@@ -250,17 +225,10 @@ export class Worksheet {
250
225
  this.#autoFilter = filter === undefined ? undefined : canonicalizeAutoFilter(filter);
251
226
  }
252
227
  unmergeCells(range) {
253
- const index = this.#merges.indexOf(range);
254
- if (index === -1)
255
- return false;
256
- this.#merges.splice(index, 1);
257
- const { top, left, bottom, right } = decodeRange(range);
258
- if (top !== undefined && left !== undefined && bottom !== undefined && right !== undefined) {
259
- const rectIndex = this.#mergeRects.findIndex((r) => r.top === top && r.left === left && r.bottom === bottom && r.right === right);
260
- if (rectIndex !== -1)
261
- this.#mergeRects.splice(rectIndex, 1);
262
- }
263
- return true;
228
+ const { existed, rectsChanged } = this.#merges.remove(range);
229
+ if (rectsChanged)
230
+ this.#extent.invalidate();
231
+ return existed;
264
232
  }
265
233
  addDataValidation(sqref, rule, options = {}) {
266
234
  this.#dataValidations.add(sqref, rule, options);
@@ -282,6 +250,8 @@ export class Worksheet {
282
250
  assertStartAndCount('splice', 'row', start, count);
283
251
  const inserted = inserts.map((values, i) => buildRowCells(start + i, values, this.#columns));
284
252
  this.#edits.spliceRows(start, count, inserted);
253
+ this.#merges.invalidate();
254
+ this.#extent.invalidate();
285
255
  }
286
256
  insertRow(pos, values) {
287
257
  this.spliceRows(pos, 0, values);
@@ -323,6 +293,7 @@ export class Worksheet {
323
293
  const { count = 1, insert = true } = options;
324
294
  assertStartAndCount('duplicate', 'row', start, count);
325
295
  const source = this.#rows.get(start);
296
+ const sourceProperties = this.#rowProperties.get(start);
326
297
  const snapshot = (destRow) => {
327
298
  const row = new Map();
328
299
  if (source) {
@@ -334,18 +305,30 @@ export class Worksheet {
334
305
  }
335
306
  return row;
336
307
  };
308
+ const copyProperties = (destRow) => {
309
+ if (sourceProperties === undefined)
310
+ this.#rowProperties.delete(destRow);
311
+ else
312
+ this.#rowProperties.set(destRow, { ...sourceProperties });
313
+ };
337
314
  if (insert) {
338
315
  const copies = Array.from({ length: count }, () => snapshot(start));
339
316
  this.#edits.spliceRows(start + 1, 0, copies);
317
+ this.#merges.invalidate();
340
318
  }
341
319
  else {
342
320
  for (let i = 1; i <= count; i++)
343
321
  this.#rows.set(start + i, snapshot(start + i));
344
322
  }
323
+ for (let i = 1; i <= count; i++)
324
+ copyProperties(start + i);
325
+ this.#extent.invalidate();
345
326
  }
346
327
  spliceColumns(start, count, ...inserts) {
347
328
  assertStartAndCount('splice', 'column', start, count);
348
329
  this.#edits.spliceColumns(start, count, inserts);
330
+ this.#merges.invalidate();
331
+ this.#extent.invalidate();
349
332
  }
350
333
  insertColumn(pos, values) {
351
334
  this.spliceColumns(pos, 0, values);
@@ -374,11 +357,11 @@ export class Worksheet {
374
357
  this.#rows.clear();
375
358
  this.#columns.clear();
376
359
  this.#rowProperties.clear();
377
- this.#merges.length = 0;
378
- this.#mergeRects.length = 0;
360
+ this.#merges.clear();
379
361
  this.#dataValidations.clear();
380
362
  this.#conditionalFormattings.clear();
381
363
  this.#tables.length = 0;
364
+ this.#extent.reset();
382
365
  }
383
366
  set model(model) {
384
367
  this.#resetContent();
@@ -412,12 +395,14 @@ export class Worksheet {
412
395
  cell = new Cell(row, col);
413
396
  cols.set(col, cell);
414
397
  }
398
+ this.#extent.noteCell(row, col);
415
399
  return cell;
416
400
  }
417
401
  [INTERNAL] = {
418
402
  evictRow: (number) => {
419
403
  this.#rows.delete(number);
420
404
  this.#rowProperties.delete(number);
405
+ this.#extent.invalidate();
421
406
  },
422
407
  addLoadedPivotTable: (pivot) => {
423
408
  this.#loadedPivotTables.push(pivot);
@@ -432,6 +417,11 @@ export class Worksheet {
432
417
  this.#protection = protection;
433
418
  },
434
419
  cellAt: (row, col) => this.#cellAt(row, col),
420
+ masterAt: (row, col) => {
421
+ const master = this.#merges.masterOf(row, col);
422
+ return this.#cellAt(master.row, master.col);
423
+ },
424
+ peekCell: (row, col) => this.#rows.get(row)?.get(col),
435
425
  rowPropertiesOf: (number) => this.#rowProperties.get(number),
436
426
  ensureRowProperties: (number) => {
437
427
  let properties = this.#rowProperties.get(number);
@@ -439,8 +429,13 @@ export class Worksheet {
439
429
  properties = {};
440
430
  this.#rowProperties.set(number, properties);
441
431
  }
432
+ this.#extent.noteDeclaredRow(number);
442
433
  return properties;
443
434
  },
435
+ dropRowProperties: (number) => {
436
+ if (this.#rowProperties.delete(number))
437
+ this.#extent.invalidate();
438
+ },
444
439
  rowCells: (number) => {
445
440
  const cols = this.#rows.get(number);
446
441
  return cols ? [...cols].sort(([a], [b]) => a - b).map(([, cell]) => cell) : [];
@@ -452,8 +447,13 @@ export class Worksheet {
452
447
  properties = {};
453
448
  this.#columns.set(index, properties);
454
449
  }
450
+ this.#extent.noteDeclaredColumn(index);
455
451
  return properties;
456
452
  },
453
+ dropColumnProperties: (index) => {
454
+ if (this.#columns.delete(index))
455
+ this.#extent.invalidate();
456
+ },
457
457
  columnCells: (index) => {
458
458
  const cells = [];
459
459
  for (const number of [...this.#rows.keys()].sort((a, b) => a - b)) {
@@ -466,9 +466,7 @@ export class Worksheet {
466
466
  };
467
467
  }
468
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
- }
469
+ assertAxisInBounds(axis, start);
472
470
  if (!Number.isInteger(count) || count < 0) {
473
471
  throw new RangeError(`${verb} count ${count} is invalid: it must be a non-negative integer`);
474
472
  }
@@ -1,15 +1,17 @@
1
1
  import { strFromU8 } from 'fflate';
2
- import { boolStrict, localName, xmlEvents } from '../xml/xml-read.js';
2
+ import { isRelType } from '../rel-type.js';
3
+ import { tokenSet } from '../token-set.js';
4
+ import { boolStrict, localName, xmlEvents } from '../xml/xml-scan.js';
3
5
  import { CustomUiParseError } from './errors.js';
4
6
  export const CUSTOMUI_2006_NAMESPACE = 'http://schemas.microsoft.com/office/2006/01/customui';
5
7
  export const CUSTOMUI_2009_NAMESPACE = 'http://schemas.microsoft.com/office/2009/07/customui';
6
8
  export const CUSTOMUI_2007_REL_TYPE = 'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility';
7
9
  export const CUSTOMUI_2010_REL_TYPE = 'http://schemas.microsoft.com/office/2007/relationships/ui/extensibility';
8
10
  export function isCustomUiRelType(type) {
9
- return type.endsWith('/ui/extensibility');
11
+ return isRelType(type, 'ui/extensibility');
10
12
  }
11
13
  const MAX_DEPTH = 256;
12
- const KNOWN_KINDS = {
14
+ const isKnownControlKind = tokenSet({
13
15
  button: true,
14
16
  toggleButton: true,
15
17
  checkBox: true,
@@ -28,10 +30,7 @@ const KNOWN_KINDS = {
28
30
  dialogBoxLauncher: true,
29
31
  control: true,
30
32
  item: true,
31
- };
32
- function isKnownControlKind(local) {
33
- return Object.hasOwn(KNOWN_KINDS, local);
34
- }
33
+ });
35
34
  export function parseCustomUi(input) {
36
35
  const xml = typeof input === 'string' ? input : strFromU8(input);
37
36
  let root;
@@ -1,25 +1,30 @@
1
- export { type CellAddress, columnToNumber, decodeAddress, decodeRange, encodeAddress, type GridRect, MAX_COLUMN, MAX_ROW, numberToColumn, type RangeAddress, } from '../core/address.ts';
2
- export type { AutoFilter, CustomFilter, CustomFilterOperator, CustomFilterPredicate, FilterColumn, FilterCriteria, ValuesFilter, } from '../core/autofilter.ts';
1
+ export { type CellAddress, type CellPosition, columnToNumber, decodeAddress, decodeRange, encodeAddress, type GridRect, MAX_COLUMN, MAX_ROW, numberToColumn, type RangeAddress, } from '../core/address.ts';
2
+ export { type AutoFilter, type CustomFilter, type CustomFilterOperator, type CustomFilterPredicate, type FilterColumn, type FilterCriteria, isCustomFilterOperator, type 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
+ export { AxisHandle } from '../core/axis-handle.ts';
5
6
  export { Column } from '../core/column.ts';
7
+ export type { DateEpoch } from '../core/date.ts';
6
8
  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
- 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, type PortableImage, PX_TO_EMU, type TwoCellAnchor, type WorkbookImage, type WorksheetImages, } from '../core/image.ts';
10
- export { MAX_COLUMN_WIDTH, MAX_ROW_HEIGHT } from '../core/limits.ts';
11
- export type { HeaderFooter, PageBreak, PageMargins, PageOrder, PageOrientation, PageSetup, PrintOptions, } from '../core/page-setup.ts';
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';
9
+ export { type CfTimePeriod, type CfValueObject, type CfValueObjectType, type ConditionalFormatting, type ConditionalFormattingOperator, type ConditionalFormattingRule, type ConditionalFormattingType, type IconSetType, isCfTimePeriod, isCfValueObjectType, isConditionalFormattingOperator, isConditionalFormattingType, isIconSetType, } from '../core/conditional-formatting.ts';
10
+ export { type DataValidation, type DataValidationEntry, type DataValidationErrorStyle, type DataValidationOperator, type DataValidationType, isDataValidationErrorStyle, isDataValidationOperator, isDataValidationType, } from '../core/data-validation.ts';
11
+ export type { AxisSplice } from '../core/grid-shift.ts';
12
+ export { type AnchoredImage, type AnchorPoint, type Extent, type ImageAnchor, type ImageEditAs, isImageEditAs, isOneCellAnchor, type OneCellAnchor, type PortableImage, PX_TO_EMU, type TwoCellAnchor, type WorkbookImage, type WorksheetImages, } from '../core/image.ts';
13
+ export type { ReadPackageOptions } from '../io/opc/read-options.ts';
14
+ export { INVALID_SHEET_NAME_CHARS, MAX_COLUMN_WIDTH, MAX_ROW_HEIGHT, MAX_SHEET_NAME_LENGTH, MAX_TABLE_NAME_LENGTH, TABLE_NAME_PATTERN, } from '../core/limits.ts';
15
+ export { type HeaderFooter, isPageOrder, isPageOrientation, type PageBreak, type PageMargins, type PageOrder, type PageOrientation, type PageSetup, type PrintOptions, } from '../core/page-setup.ts';
16
+ export { type ParsedPivotField, type ParsedPivotSource, type ParsedPivotTable, type PivotCacheField, type PivotItem, type PivotMetric, type PivotNumericSummary, isDeclarablePivotSourceKind, type PivotRecordCell, type PivotSourceKind, PivotTable, type PivotTableOptions, } from '../core/pivot-table.ts';
13
17
  export type { PreservedPart, PreservedRelationship, PreservedRootReference, PreservedWorksheetReference, } from '../core/preserved.ts';
14
18
  export type { SheetProtection, SheetProtectionCredential, SheetProtectionFlags, SheetProtectionOptions, } from '../core/protection.ts';
15
19
  export { Range } from '../core/range.ts';
16
20
  export { Row } from '../core/row.ts';
17
- export type { Alignment, Border, BorderEdge, BorderStyle, CellStyle, Color, DifferentialStyle, Fill, FillPatternType, Font, FontScheme, FontVerticalAlignment, GradientFill, GradientStop, HorizontalAlignment, NamedCellStyle, PatternFill, Protection, TableStyleNamespace, TableStyleTable, UnderlineStyle, VerticalAlignment, } from '../core/style.ts';
18
- export { Table, type TableColumn, type TableColumnStyle, type TableOptions, type TableRegion, type TableStyleInfo, } from '../core/table.ts';
21
+ export { type Alignment, type Border, type BorderEdge, type BorderStyle, type CellContent, type CellStyle, type Color, type Fill, type FillPatternType, type Font, type FontScheme, type FontVerticalAlignment, type GradientFill, type GradientStop, type HorizontalAlignment, isBorderStyle, isFillPatternType, isFontScheme, isFontVerticalAlignment, isHorizontalAlignment, isNamedUnderlineStyle, isVerticalAlignment, type PatternFill, type Protection, type UnderlineStyle, type VerticalAlignment, } from '../core/style.ts';
22
+ export { Table, type TableColumn, type TableColumnStyle, isTotalsRowFunction, type TableGrid, type TableOptions, type TableRegion, type TableStyleInfo, type TotalsRowFunction, } from '../core/table.ts';
23
+ export type { DifferentialStyle, NamedCellStyle, TableStyleNamespace, TableStyleTable, } from '../core/workbook-styles.ts';
19
24
  export { isTableStyleElementType, STRIPE_ELEMENT_TYPES, TABLE_STYLE_ELEMENT_TYPES, type TableStyle, type TableStyleElement, type TableStyleElementType, } from '../core/table-style.ts';
20
25
  export { estimateWrappedLines } from '../core/text-metrics.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';
26
+ export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, isThemeColorSlot, THEME_COLOR_SLOTS, type ThemeColorScheme, type ThemeColorSlot, type ThemeFontScheme, type ThemeOverrides, } from '../core/theme.ts';
22
27
  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
- export { type AddImageOptions, type AddWorksheetOptions, DEFAULT_WORKBOOK_VIEW, type DefinedName, type PreservedWorkbookReference, Workbook, type WorkbookProperties, type WorkbookView, } from '../core/workbook.ts';
28
+ export { type AddImageOptions, type AddWorksheetOptions, DEFAULT_WORKBOOK_VIEW, type DefinedName, type PreservedTheme, type PreservedWorkbookReference, Workbook, type WorkbookProperties, type WorkbookView, } from '../core/workbook.ts';
24
29
  export type { WorkbookProtection, WorkbookProtectionCredentialAttr, } from '../core/workbook-protection.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';
30
+ export { type CellModel, type ColumnProperties, type OutlineProperties, type RowInput, type RowProperties, type SheetView, isVisibility, type Visibility, Worksheet, type WorksheetModel, type WorksheetProperties, type WorksheetState, } from '../core/worksheet.ts';
@@ -1,16 +1,22 @@
1
1
  export { columnToNumber, decodeAddress, decodeRange, encodeAddress, MAX_COLUMN, MAX_ROW, numberToColumn, } from '../core/address.js';
2
+ export { isCustomFilterOperator, } from '../core/autofilter.js';
2
3
  export { Cell } from '../core/cell.js';
3
4
  export { applyTint, DEFAULT_INDEXED_COLORS, resolveColor, SYSTEM_INDEXED_COLORS, } from '../core/color-resolution.js';
5
+ export { AxisHandle } from '../core/axis-handle.js';
4
6
  export { Column } from '../core/column.js';
5
- export { isOneCellAnchor, PX_TO_EMU, } from '../core/image.js';
6
- export { MAX_COLUMN_WIDTH, MAX_ROW_HEIGHT } from '../core/limits.js';
7
- export { PivotTable, } from '../core/pivot-table.js';
7
+ export { isCfTimePeriod, isCfValueObjectType, isConditionalFormattingOperator, isConditionalFormattingType, isIconSetType, } from '../core/conditional-formatting.js';
8
+ export { isDataValidationErrorStyle, isDataValidationOperator, isDataValidationType, } from '../core/data-validation.js';
9
+ export { isImageEditAs, isOneCellAnchor, PX_TO_EMU, } from '../core/image.js';
10
+ export { INVALID_SHEET_NAME_CHARS, MAX_COLUMN_WIDTH, MAX_ROW_HEIGHT, MAX_SHEET_NAME_LENGTH, MAX_TABLE_NAME_LENGTH, TABLE_NAME_PATTERN, } from '../core/limits.js';
11
+ export { isPageOrder, isPageOrientation, } from '../core/page-setup.js';
12
+ export { isDeclarablePivotSourceKind, PivotTable, } from '../core/pivot-table.js';
8
13
  export { Range } from '../core/range.js';
9
14
  export { Row } from '../core/row.js';
10
- export { Table, } from '../core/table.js';
15
+ export { isBorderStyle, isFillPatternType, isFontScheme, isFontVerticalAlignment, isHorizontalAlignment, isNamedUnderlineStyle, isVerticalAlignment, } from '../core/style.js';
16
+ export { Table, isTotalsRowFunction, } from '../core/table.js';
11
17
  export { isTableStyleElementType, STRIPE_ELEMENT_TYPES, TABLE_STYLE_ELEMENT_TYPES, } from '../core/table-style.js';
12
18
  export { estimateWrappedLines } from '../core/text-metrics.js';
13
- export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, THEME_COLOR_SLOTS, } from '../core/theme.js';
19
+ export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, isThemeColorSlot, THEME_COLOR_SLOTS, } from '../core/theme.js';
14
20
  export { cellValueToText, coerceCellValue, detectValueType, ERROR_CODES, isDataTableFormulaValue, isErrorCode, isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, richTextToPlain, ValueType, } from '../core/value.js';
15
21
  export { DEFAULT_WORKBOOK_VIEW, Workbook, } from '../core/workbook.js';
16
- export { Worksheet, } from '../core/worksheet.js';
22
+ export { isVisibility, Worksheet, } from '../core/worksheet.js';
@@ -1,5 +1,5 @@
1
1
  export { editXlsxVbaAddReference, editXlsxVbaRemoveModule } from '../io/xlsx/edit-vba.ts';
2
- export { type ReadXlsxOptions, readXlsx } from '../io/xlsx/read.ts';
2
+ export { 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';
package/dist/errors.d.ts CHANGED
@@ -77,3 +77,67 @@ export declare class InternalError extends XlsxError {
77
77
  readonly code = "internal";
78
78
  constructor(message?: string, options?: ErrorOptions);
79
79
  }
80
+ /**
81
+ * A name as it should appear inside an error message: quoted, and unambiguous whatever it contains.
82
+ *
83
+ * Almost every message this library throws names something the caller or the file chose - a sheet, a
84
+ * table, a defined name, a module stream, a part path - and the tree had grown three ways of setting
85
+ * that name off from the prose, split by directory rather than by intent: `"..."`, `'...'`, and
86
+ * `JSON.stringify`. Only the third survives a name that itself contains a quote, which is exactly the
87
+ * class of name a spreadsheet permits: a sheet called `Q1 "draft"` renders under either literal
88
+ * spelling as a message whose reader cannot tell where the name ends. It also renders a name
89
+ * containing a newline or a zero-width character as one that looks identical to a name that does not,
90
+ * which for a library whose input is untrusted is the difference between a diagnostic and a decoy.
91
+ *
92
+ * So: one spelling, and the one that escapes. `JSON.stringify` is the whole implementation - the
93
+ * point is not the algorithm but that every throw site reaches the same one.
94
+ *
95
+ * Not exported from the `/errors` entry: this is how messages are written, not part of the taxonomy
96
+ * a caller catches.
97
+ */
98
+ export declare function quoted(name: string): string;
99
+ /**
100
+ * The one message for "that token is not in the enumeration", owned here because both sides of a
101
+ * layering boundary throw it.
102
+ *
103
+ * `checkedToken` in `src/xml/xml.ts` is the general form, and `core/` may not import a serialisation
104
+ * to reach it. The response to that constraint had been to transcribe the sentence into
105
+ * `core/table-style.ts` character for character, which is a copy that no gate can see is a copy. This
106
+ * module is the one place below both of them, so the template lives here and neither side owns it.
107
+ */
108
+ export declare function invalidToken(kind: string, value: string): AuthoringError;
109
+ /**
110
+ * Refuse a number OOXML cannot spell. Every numeric attribute in the format is `xsd:double`,
111
+ * `xsd:unsignedInt` or a bounded flavour of one, and none of those lexical spaces has a form for a
112
+ * NaN or an infinity, so a value that reaches the file as `NaN` is a package Excel reports as
113
+ * damaged. Neither has the formula grammar, which is why the guard is here rather than in the XML
114
+ * serialiser: the two spellings of a number this library writes -- an attribute's and a formula
115
+ * literal's -- sit on opposite sides of the `core`/`xml` boundary and refuse the same values.
116
+ *
117
+ * Exported for the callers that must refuse before they write. A number can be unwritable and still
118
+ * be read on the way to the bytes: compared, summed, walked. A comparison against `NaN` or an
119
+ * infinity silently takes the wrong branch long before the value would have been serialised.
120
+ *
121
+ * @throws {AuthoringError} naming the value.
122
+ */
123
+ export declare function assertWritableNumber(value: number): void;
124
+ /** `U+0001`-style spelling of a code point, for an escape body or an error message. */
125
+ export declare function codePointHex(codePoint: number): string;
126
+ /**
127
+ * The error for a string carrying a character the target format cannot encode, or `undefined` when
128
+ * it carries none. Naming the code point and its offset is the whole value of the message: these
129
+ * strings arrive from a database column or a CSV field, not from a literal the author can see.
130
+ *
131
+ * Two formats refuse characters for unrelated reasons -- XML 1.0's `Char` production, and UTF-8's
132
+ * inability to encode a lone surrogate -- and each had grown its own copy of the rendering, which
133
+ * had already drifted: the CSV one omitted the zero padding, invisibly, because a surrogate is
134
+ * always four digits and nothing narrower had been added yet. The *patterns* stay beside their
135
+ * consumers, which is where they are understood; only the spelling and the message skeleton are
136
+ * shared, which is where they were wrong.
137
+ *
138
+ * `pattern` must not carry the `g` flag: a stateful `lastIndex` would make the same string answer
139
+ * differently on a second call.
140
+ *
141
+ * @param why the clause explaining the refusal, appended after the offset.
142
+ */
143
+ export declare function unrepresentable(text: string, pattern: RegExp, why: string): AuthoringError | undefined;
package/dist/errors.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { hex } from './hex.js';
1
2
  export class XlsxError extends Error {
2
3
  }
3
4
  export class AuthoringError extends XlsxError {
@@ -13,3 +14,24 @@ export class InternalError extends XlsxError {
13
14
  super(message === undefined ? REPORT_NOTICE : `${message}\n\n${REPORT_NOTICE}`, options);
14
15
  }
15
16
  }
17
+ export function quoted(name) {
18
+ return JSON.stringify(name);
19
+ }
20
+ export function invalidToken(kind, value) {
21
+ return new AuthoringError(`Invalid ${kind} ${quoted(value)}: not a value the OOXML enumeration allows`);
22
+ }
23
+ export function assertWritableNumber(value) {
24
+ if (Number.isFinite(value))
25
+ return;
26
+ throw new AuthoringError(`cannot write a non-finite number (${value}): it has no OOXML representation`);
27
+ }
28
+ export function codePointHex(codePoint) {
29
+ return hex(codePoint, 4);
30
+ }
31
+ export function unrepresentable(text, pattern, why) {
32
+ const found = pattern.exec(text);
33
+ if (found === null)
34
+ return undefined;
35
+ const codePoint = text.codePointAt(found.index);
36
+ return new AuthoringError(`cannot write U+${codePointHex(codePoint)} at offset ${found.index}: ${why}`);
37
+ }
package/dist/hex.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `value` in uppercase hexadecimal, zero-padded to at least `digits`.
3
+ *
4
+ * Uppercase because five of the six sites this replaced were, because OOXML writes `ARGB` values that
5
+ * way, and because a hex digit read by a human beside a decimal number is easier to tell apart when
6
+ * it is not lowercase. A value wider than `digits` is not truncated: padding is a minimum.
7
+ */
8
+ export declare function hex(value: number, digits: number): string;
package/dist/hex.js ADDED
@@ -0,0 +1,3 @@
1
+ export function hex(value, digits) {
2
+ return value.toString(16).toUpperCase().padStart(digits, '0');
3
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Refuse a delimiter the codec cannot honour.
3
+ *
4
+ * A `RangeError` rather than an `AuthoringError`: this is a single scalar out of range, which
5
+ * `src/errors.ts` reserves for the native types on the grounds that they exist for exactly that.
6
+ *
7
+ * @throws {RangeError} if the delimiter is not exactly one character.
8
+ */
9
+ export declare function assertDelimiter(delimiter: string): void;
@@ -0,0 +1,6 @@
1
+ import { quoted } from '../../errors.js';
2
+ export function assertDelimiter(delimiter) {
3
+ if (delimiter.length !== 1) {
4
+ throw new RangeError(`CSV delimiter must be a single character, got ${quoted(delimiter)}`);
5
+ }
6
+ }
@@ -1,10 +1,9 @@
1
1
  import { Workbook } from '../../core/workbook.js';
2
+ import { assertDelimiter } from './delimiter.js';
2
3
  export function readCsv(input, options = {}) {
3
4
  const text = stripBom(typeof input === 'string' ? input : new TextDecoder('utf-8', { ignoreBOM: true }).decode(input));
4
5
  const delimiter = options.delimiter ?? ',';
5
- if (delimiter.length !== 1) {
6
- throw new RangeError(`CSV delimiter must be a single character, got ${JSON.stringify(delimiter)}`);
7
- }
6
+ assertDelimiter(delimiter);
8
7
  let rows = parseCsvRows(text, delimiter);
9
8
  if (options.headers)
10
9
  rows = rows.slice(1);
@@ -52,7 +51,15 @@ function parseCsvRows(text, delimiter) {
52
51
  row = [];
53
52
  field = '';
54
53
  }
55
- else if (ch !== '\r') {
54
+ else if (ch === '\r') {
55
+ if (text[i + 1] !== '\n') {
56
+ row.push(field);
57
+ rows.push(row);
58
+ row = [];
59
+ field = '';
60
+ }
61
+ }
62
+ else {
56
63
  field += ch;
57
64
  }
58
65
  }
@@ -18,8 +18,15 @@ export interface CsvWriteOptions {
18
18
  readonly delimiter?: string;
19
19
  /** Line separator between rows; defaults to `"\n"`. */
20
20
  readonly rowDelimiter?: string;
21
- /** A token format (e.g. `"MM/DD/YYYY"`) for Date cells; without it a Date renders as a full
22
- * ISO-8601 timestamp. */
21
+ /**
22
+ * An Excel number-format code (e.g. `"yyyy-mm-dd"`, `"d mmm yy hh:mm"`) for Date cells; without it
23
+ * a Date renders as a full ISO-8601 timestamp.
24
+ *
25
+ * The same vocabulary as {@link Cell.numFmt}, so `writeCsv(wb, {dateFormat: cell.numFmt})` renders
26
+ * what the cell would show. It used to be a moment.js-style token set, which is case-sensitive and
27
+ * spells the month `MM` and the minute `mm`: passing this library's own format codes to it produced
28
+ * `2024-45-dd` with no throw and no warning. See ADR 0041.
29
+ */
23
30
  readonly dateFormat?: string;
24
31
  /** Render Date cells in UTC rather than the runner's local time. */
25
32
  readonly dateUTC?: boolean;