@shbernal/ts-xlsx 3.0.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 (213) 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 +4 -3
  7. package/dist/core/autofilter.js +31 -30
  8. package/dist/core/axis-handle.d.ts +2 -0
  9. package/dist/core/axis-handle.js +5 -2
  10. package/dist/core/cell.d.ts +4 -3
  11. package/dist/core/cell.js +4 -6
  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 +1 -0
  16. package/dist/core/column.js +3 -0
  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 +9 -1
  21. package/dist/core/conditional-formatting.js +34 -18
  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 +6 -0
  27. package/dist/core/data-validation.js +15 -4
  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 +13 -9
  39. package/dist/core/image.js +7 -5
  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.d.ts +5 -4
  44. package/dist/core/merge.js +18 -19
  45. package/dist/core/pivot-table.js +63 -45
  46. package/dist/core/protection.js +2 -11
  47. package/dist/core/range.js +34 -30
  48. package/dist/core/row-input.js +2 -2
  49. package/dist/core/row.d.ts +1 -0
  50. package/dist/core/row.js +3 -0
  51. package/dist/core/style.d.ts +85 -70
  52. package/dist/core/style.js +92 -28
  53. package/dist/core/table-style.d.ts +1 -1
  54. package/dist/core/table-style.js +4 -4
  55. package/dist/core/table.d.ts +27 -9
  56. package/dist/core/table.js +51 -34
  57. package/dist/core/theme.js +6 -5
  58. package/dist/core/value.d.ts +9 -0
  59. package/dist/core/value.js +36 -34
  60. package/dist/core/workbook-media.d.ts +34 -0
  61. package/dist/core/workbook-media.js +44 -0
  62. package/dist/core/workbook-styles.d.ts +67 -1
  63. package/dist/core/workbook-vba.js +3 -2
  64. package/dist/core/workbook.d.ts +23 -2
  65. package/dist/core/workbook.js +24 -40
  66. package/dist/core/worksheet-comments.d.ts +2 -1
  67. package/dist/core/worksheet-comments.js +20 -13
  68. package/dist/core/worksheet-merges.d.ts +44 -0
  69. package/dist/core/worksheet-merges.js +59 -0
  70. package/dist/core/worksheet-model.d.ts +9 -1
  71. package/dist/core/worksheet-model.js +24 -9
  72. package/dist/core/worksheet.d.ts +55 -6
  73. package/dist/core/worksheet.js +36 -44
  74. package/dist/customui/ribbon.js +2 -1
  75. package/dist/entries/core.d.ts +18 -13
  76. package/dist/entries/core.js +12 -6
  77. package/dist/entries/xlsx.d.ts +1 -1
  78. package/dist/errors.d.ts +64 -0
  79. package/dist/errors.js +22 -0
  80. package/dist/hex.d.ts +8 -0
  81. package/dist/hex.js +3 -0
  82. package/dist/io/csv/delimiter.d.ts +9 -0
  83. package/dist/io/csv/delimiter.js +6 -0
  84. package/dist/io/csv/read.js +11 -4
  85. package/dist/io/csv/write.d.ts +9 -2
  86. package/dist/io/csv/write.js +9 -38
  87. package/dist/io/opc/inflate.js +14 -0
  88. package/dist/io/opc/namespaces.d.ts +16 -0
  89. package/dist/io/opc/namespaces.js +4 -0
  90. package/dist/io/opc/part-paths.d.ts +9 -0
  91. package/dist/io/opc/part-paths.js +1 -1
  92. package/dist/io/opc/read-opc.d.ts +54 -14
  93. package/dist/io/opc/read-opc.js +42 -20
  94. package/dist/io/opc/read-options.d.ts +5 -1
  95. package/dist/io/opc/sniff-format.d.ts +3 -3
  96. package/dist/io/opc/sniff-format.js +2 -2
  97. package/dist/io/style/xf-style.d.ts +23 -1
  98. package/dist/io/style/xf-style.js +8 -0
  99. package/dist/io/xlsb/formula.d.ts +2 -2
  100. package/dist/io/xlsb/formula.js +49 -34
  101. package/dist/io/xlsb/primitives.js +8 -14
  102. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  103. package/dist/io/xlsb/ptg-functions.js +385 -596
  104. package/dist/io/xlsb/read-styles.js +45 -81
  105. package/dist/io/xlsb/read-worksheet.d.ts +20 -1
  106. package/dist/io/xlsb/read-worksheet.js +73 -89
  107. package/dist/io/xlsb/read.d.ts +6 -3
  108. package/dist/io/xlsb/read.js +44 -23
  109. package/dist/io/xlsb/record-stream.d.ts +30 -0
  110. package/dist/io/xlsb/record-stream.js +20 -0
  111. package/dist/io/xlsb/record-types.d.ts +1 -5
  112. package/dist/io/xlsb/record-types.js +1 -5
  113. package/dist/io/xlsx/cell-accumulator.d.ts +8 -1
  114. package/dist/io/xlsx/cell-accumulator.js +32 -9
  115. package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
  116. package/dist/io/xlsx/cell-style-resolution.js +29 -0
  117. package/dist/io/xlsx/cell-value.d.ts +5 -3
  118. package/dist/io/xlsx/cell-value.js +13 -15
  119. package/dist/io/xlsx/color-xml.js +3 -2
  120. package/dist/io/xlsx/column-budget.d.ts +26 -0
  121. package/dist/io/xlsx/column-budget.js +22 -0
  122. package/dist/io/xlsx/comments.d.ts +6 -2
  123. package/dist/io/xlsx/comments.js +36 -32
  124. package/dist/io/xlsx/conditional-formatting.d.ts +6 -3
  125. package/dist/io/xlsx/conditional-formatting.js +30 -26
  126. package/dist/io/xlsx/data-validation.js +29 -27
  127. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  128. package/dist/io/xlsx/edit-vba.js +14 -21
  129. package/dist/io/xlsx/font-xml.d.ts +7 -0
  130. package/dist/io/xlsx/font-xml.js +104 -0
  131. package/dist/io/xlsx/hyperlinks.d.ts +37 -5
  132. package/dist/io/xlsx/hyperlinks.js +22 -13
  133. package/dist/io/xlsx/images.js +11 -13
  134. package/dist/io/xlsx/namespaces.d.ts +16 -0
  135. package/dist/io/xlsx/namespaces.js +7 -0
  136. package/dist/io/xlsx/package-plan.d.ts +14 -2
  137. package/dist/io/xlsx/package-plan.js +39 -11
  138. package/dist/io/xlsx/pivot.js +8 -7
  139. package/dist/io/xlsx/read-parts.d.ts +59 -0
  140. package/dist/io/xlsx/read-parts.js +252 -0
  141. package/dist/io/xlsx/read-pivot.js +2 -1
  142. package/dist/io/xlsx/read-repair.d.ts +26 -0
  143. package/dist/io/xlsx/read-repair.js +39 -0
  144. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +6 -3
  145. package/dist/io/xlsx/read-rows.d.ts +18 -5
  146. package/dist/io/xlsx/read-rows.js +45 -31
  147. package/dist/io/xlsx/read-shared-strings.js +3 -2
  148. package/dist/io/xlsx/read-styles.d.ts +1 -6
  149. package/dist/io/xlsx/read-styles.js +6 -71
  150. package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
  151. package/dist/io/xlsx/read-workbook-xml.js +170 -0
  152. package/dist/io/xlsx/read-worksheet.d.ts +2 -1
  153. package/dist/io/xlsx/read-worksheet.js +35 -164
  154. package/dist/io/xlsx/read.d.ts +5 -12
  155. package/dist/io/xlsx/read.js +73 -406
  156. package/dist/io/xlsx/rich-text.js +1 -1
  157. package/dist/io/xlsx/row-position.d.ts +18 -0
  158. package/dist/io/xlsx/row-position.js +11 -0
  159. package/dist/io/xlsx/row-xml.d.ts +128 -0
  160. package/dist/io/xlsx/row-xml.js +203 -0
  161. package/dist/io/xlsx/shared-formulas.js +2 -2
  162. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  163. package/dist/io/xlsx/sheet-properties.js +145 -9
  164. package/dist/io/xlsx/style-elements.d.ts +43 -0
  165. package/dist/io/xlsx/style-elements.js +156 -0
  166. package/dist/io/xlsx/styles.d.ts +12 -3
  167. package/dist/io/xlsx/styles.js +5 -207
  168. package/dist/io/xlsx/tables.js +10 -14
  169. package/dist/io/xlsx/theme-xml.d.ts +26 -4
  170. package/dist/io/xlsx/theme-xml.js +107 -69
  171. package/dist/io/xlsx/threaded-comments.d.ts +8 -10
  172. package/dist/io/xlsx/threaded-comments.js +2 -1
  173. package/dist/io/xlsx/workbook-xml.d.ts +54 -4
  174. package/dist/io/xlsx/workbook-xml.js +60 -34
  175. package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
  176. package/dist/io/xlsx/worksheet-xml.js +48 -229
  177. package/dist/io/xlsx/write-stream.d.ts +65 -16
  178. package/dist/io/xlsx/write-stream.js +50 -20
  179. package/dist/io/xlsx/write.d.ts +1 -2
  180. package/dist/io/xlsx/write.js +114 -74
  181. package/dist/rel-type.d.ts +16 -0
  182. package/dist/rel-type.js +6 -0
  183. package/dist/token-set.d.ts +13 -0
  184. package/dist/token-set.js +4 -1
  185. package/dist/vba/bytes.d.ts +14 -5
  186. package/dist/vba/bytes.js +16 -6
  187. package/dist/vba/cfb-format.d.ts +8 -0
  188. package/dist/vba/cfb-format.js +1 -0
  189. package/dist/vba/cfb-writer.d.ts +9 -0
  190. package/dist/vba/cfb-writer.js +12 -9
  191. package/dist/vba/cfb.d.ts +13 -1
  192. package/dist/vba/cfb.js +73 -51
  193. package/dist/vba/codepage.js +2 -1
  194. package/dist/vba/errors.d.ts +7 -0
  195. package/dist/vba/ms-ovba.js +51 -16
  196. package/dist/vba/project-editor.js +35 -40
  197. package/dist/vba/project.d.ts +11 -1
  198. package/dist/vba/project.js +27 -8
  199. package/dist/vba/vba-encoding.js +4 -5
  200. package/dist/xml/xml-attrs.d.ts +37 -0
  201. package/dist/xml/xml-attrs.js +30 -0
  202. package/dist/xml/xml-chars.d.ts +40 -0
  203. package/dist/xml/xml-chars.js +12 -0
  204. package/dist/xml/xml-namespaces.d.ts +31 -0
  205. package/dist/xml/xml-namespaces.js +50 -0
  206. package/dist/xml/xml-read.d.ts +72 -10
  207. package/dist/xml/xml-read.js +94 -18
  208. package/dist/xml/xml-scan.d.ts +15 -37
  209. package/dist/xml/xml-scan.js +12 -40
  210. package/dist/xml/xml.d.ts +28 -27
  211. package/dist/xml/xml.js +24 -24
  212. package/package.json +12 -7
  213. /package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +0 -0
@@ -1,57 +1,25 @@
1
- import { decodeRange, encodeAddress } from '../../core/address.js';
2
- import { cellHasOwnStyle } from '../../core/cell.js';
3
- import { DEFAULT_DATE_NUMFMT, dateToSerial } from '../../core/date.js';
4
- import { mangleFormula } from '../../core/formula.js';
5
- import { NAMED_STYLE_ID } from '../../core/internal.js';
6
- import { CELL_STYLE_FACETS, pickStyleFacets } from '../../core/style.js';
7
- import { detectValueType, isDataTableFormulaValue, isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, } from '../../core/value.js';
8
- import { AuthoringError, InternalError } from '../../errors.js';
9
- import { assertWritableNumber, escapeAttr, escapeSpreadsheetText, escapeText, numberText, textAttr, textElement, XML_DECLARATION, } from '../../xml/xml.js';
1
+ import { decodeRange, encodeRect } from '../../core/address.js';
2
+ import { pickStyleFacets } from '../../core/style.js';
3
+ import { AuthoringError, InternalError, quoted } from '../../errors.js';
4
+ import { escapeAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
10
5
  import { relationship, relationshipsPart } from '../opc/rels.js';
11
- import { conditionalFormattingsExtXml, conditionalFormattingsXml } from './conditional-formatting.js';
6
+ import { conditionalFormattingsExtXml, conditionalFormattingsXml, dataBarExtLinks, } from './conditional-formatting.js';
12
7
  import { dataValidationsExtXml, dataValidationsXml } from './data-validation.js';
13
- import { hyperlinksXml } from './hyperlinks.js';
8
+ import { hyperlinksXml, isExternalHyperlink } from './hyperlinks.js';
14
9
  import { SLICER_LIST_EXT_URI } from './namespaces.js';
15
10
  import { commentsPart, drawingPart, mediaPart, pivotTablePart, printerSettingsPart, tablePart, targetFromWorksheet, threadedCommentsPart, vmlDrawingPart, } from './part-names.js';
16
11
  import { NS, REL } from './relationships.js';
17
- import { richTextRunsXml } from './rich-text.js';
12
+ import { assertWritableLevel, buildColumnDefaults, Extent, outlineAttr, renderRow, rowOpenTag, } from './row-xml.js';
18
13
  import { planSharedFormulas } from './shared-formulas.js';
19
14
  import { autoFilterXml, headerFooterXml, pageBreaksXml, pageMarginsXml, pageSetupXml, printOptionsXml, sheetProtectionXml, sheetPrXml, sheetViewsXml, } from './sheet-properties.js';
20
15
  import { x14Ext } from './x14-ext.js';
21
- export class Extent {
22
- top = Infinity;
23
- left = Infinity;
24
- bottom = -Infinity;
25
- right = -Infinity;
26
- constructor(seed) {
27
- if (seed) {
28
- this.top = seed.top;
29
- this.left = seed.left;
30
- this.bottom = seed.bottom;
31
- this.right = seed.right;
32
- }
33
- }
34
- get isEmpty() {
35
- return this.bottom === -Infinity;
36
- }
37
- add(row, minCol, maxCol) {
38
- if (minCol === Infinity)
39
- return;
40
- if (row < this.top)
41
- this.top = row;
42
- if (row > this.bottom)
43
- this.bottom = row;
44
- if (minCol < this.left)
45
- this.left = minCol;
46
- if (maxCol > this.right)
47
- this.right = maxCol;
48
- }
49
- }
50
- export function worksheetXml(sheet, tables, styles, references, hyperlinks, sharedStrings, active, flushed) {
16
+ export function worksheetXml(inputs) {
17
+ const { sheet, tables, styles, references, hyperlinks, sharedStrings, dateEpoch, active, flushed } = inputs;
51
18
  validateMerges(sheet);
52
19
  const columnDefaults = buildColumnDefaults(sheet);
53
20
  const sharedRoles = planSharedFormulas(sheet);
54
- const rowOutline = scanRowOutline(sheet);
21
+ const extLinks = dataBarExtLinks(sheet.conditionalFormattings);
22
+ const rowOutline = scanRowOutline(sheet, flushed);
55
23
  const collapsedSummaries = rowOutline.collapsedSummaries;
56
24
  const context = {
57
25
  columnDefaults,
@@ -59,6 +27,7 @@ export function worksheetXml(sheet, tables, styles, references, hyperlinks, shar
59
27
  sharedStrings,
60
28
  sharedRoles,
61
29
  collapsedSummaries,
30
+ dateEpoch,
62
31
  };
63
32
  const liveRows = [];
64
33
  const extent = new Extent(flushed?.extent);
@@ -69,11 +38,9 @@ export function worksheetXml(sheet, tables, styles, references, hyperlinks, shar
69
38
  liveRows.push({ number: entry.number, xml });
70
39
  extent.add(entry.number, minCol, maxCol);
71
40
  }
72
- const dimensionRef = extent.isEmpty
73
- ? 'A1'
74
- : `${encodeAddress(extent.left, extent.top)}:${encodeAddress(extent.right, extent.bottom)}`;
41
+ const dimensionRef = extent.isEmpty ? 'A1' : encodeRect(extent);
75
42
  const orderedRows = flushed
76
- ? [...flushed.rows, ...liveRows].sort((a, b) => a.number - b.number)
43
+ ? [...flushed.rows.map((row) => completeCollapsed(row, collapsedSummaries)), ...liveRows].sort((a, b) => a.number - b.number)
77
44
  : liveRows;
78
45
  const bodyXml = orderedRows.map((row) => row.xml).join('');
79
46
  const sheetData = bodyXml === '' ? '<sheetData/>' : `<sheetData>${bodyXml}</sheetData>`;
@@ -84,14 +51,14 @@ export function worksheetXml(sheet, tables, styles, references, hyperlinks, shar
84
51
  sheetViewsXml(sheet.view, active) +
85
52
  sheetFormatPr(sheet.properties, {
86
53
  col: maxColumnOutlineLevel(sheet),
87
- row: Math.max(rowOutline.maxLevel, flushed?.maxRowOutlineLevel ?? 0),
54
+ row: rowOutline.maxLevel,
88
55
  }) +
89
56
  colsXml(sheet, styles) +
90
57
  sheetData +
91
58
  sheetProtectionXml(sheet.protection) +
92
59
  autoFilterXml(sheet.autoFilter) +
93
60
  mergeCellsXml(sheet.merges) +
94
- conditionalFormattingsXml(sheet.conditionalFormattings, styles) +
61
+ conditionalFormattingsXml(sheet.conditionalFormattings, styles, extLinks) +
95
62
  dataValidationsXml(sheet.dataValidations) +
96
63
  hyperlinksXml(hyperlinks) +
97
64
  printOptionsXml(sheet.printOptions) +
@@ -105,57 +72,12 @@ export function worksheetXml(sheet, tables, styles, references, hyperlinks, shar
105
72
  refElement('legacyDrawingHF', references.legacyDrawingHFRelId) +
106
73
  refElement('picture', references.backgroundRelId) +
107
74
  tablePartsXml(tables) +
108
- worksheetExtLstXml(sheet, references.slicerRelIds) +
75
+ worksheetExtLstXml(sheet, references.slicerRelIds, extLinks) +
109
76
  '</worksheet>');
110
77
  }
111
- export function buildColumnDefaults(sheet) {
112
- const columnDefaults = new Map();
113
- for (const { index, properties } of sheet.columns())
114
- columnDefaults.set(index, properties ?? {});
115
- return columnDefaults;
116
- }
117
- export function renderRow(entry, ctx) {
118
- const { number, cells, properties } = entry;
119
- const rendered = cells.filter((cell) => cell.value !== null || cellHasOwnStyle(cell));
120
- const attrs = rowAttrs(properties, ctx.styles, ctx.collapsedSummaries.has(number));
121
- if (rendered.length === 0 && attrs === '')
122
- return { xml: '', minCol: Infinity, maxCol: -Infinity };
123
- const rowFill = properties?.fill;
124
- const cellsXml = rendered
125
- .map((cell) => {
126
- const style = ctx.styles.styleId(composeCellStyle(cell, rowFill, ctx.columnDefaults.get(cell.col)));
127
- return cellXml(cell, style, ctx.sharedRoles.get(cell.address), ctx.sharedStrings);
128
- })
129
- .join('');
130
- let minCol = Infinity;
131
- let maxCol = -Infinity;
132
- for (const cell of rendered) {
133
- if (cell.col < minCol)
134
- minCol = cell.col;
135
- if (cell.col > maxCol)
136
- maxCol = cell.col;
137
- }
138
- return { xml: `<row r="${number}"${attrs}>${cellsXml}</row>`, minCol, maxCol };
139
- }
140
- function composeCellStyle(cell, rowFill, colDef) {
141
- const style = {};
142
- for (const facet of CELL_STYLE_FACETS)
143
- inheritFacet(style, facet, cell, colDef);
144
- style.fill = cell.fill ?? rowFill ?? colDef?.fill;
145
- style.numFmt = cell.numFmt ?? colDef?.numFmt ?? dateDefaultNumFmt(cell.value);
146
- if (cell.quotePrefix !== undefined)
147
- style.quotePrefix = cell.quotePrefix;
148
- const xfId = cell[NAMED_STYLE_ID];
149
- if (xfId !== undefined)
150
- style.xfId = xfId;
151
- return style;
152
- }
153
- function inheritFacet(target, key, cell, colDef) {
154
- target[key] = cell[key] ?? colDef?.[key];
155
- }
156
- function worksheetExtLstXml(sheet, slicerRelIds) {
78
+ function worksheetExtLstXml(sheet, slicerRelIds, extLinks) {
157
79
  const exts = [
158
- conditionalFormattingsExtXml(sheet.conditionalFormattings),
80
+ conditionalFormattingsExtXml(sheet.conditionalFormattings, extLinks),
159
81
  dataValidationsExtXml(sheet.dataValidations),
160
82
  slicerListExtXml(slicerRelIds),
161
83
  ].filter((ext) => ext !== '');
@@ -181,7 +103,7 @@ function validateMerges(sheet) {
181
103
  top <= region.bottom &&
182
104
  bottom >= region.top;
183
105
  if (overlaps) {
184
- throw new AuthoringError(`merged range ${merge} overlaps table "${table.name}" (${table.range}): Excel forbids a merge inside a table`);
106
+ throw new AuthoringError(`merged range ${merge} overlaps table ${quoted(table.name)} (${table.range}): Excel forbids a merge inside a table`);
185
107
  }
186
108
  }
187
109
  }
@@ -203,7 +125,8 @@ function tablePartsXml(tables) {
203
125
  const parts = tables.map(({ relId }) => `<tablePart r:id="${relId}"/>`).join('');
204
126
  return `<tableParts count="${tables.length}">${parts}</tableParts>`;
205
127
  }
206
- export function worksheetRelsXml(tables, drawing, comments, threadedComments, printerSettings, background, hyperlinks, preservedReferences, pivots) {
128
+ export function worksheetRelsXml(plan) {
129
+ const { tables, drawing, comments, threadedComments, printerSettings, background, hyperlinks, preservedRefs: preservedReferences, pivots, } = plan;
207
130
  const rels = [
208
131
  ...tables.map(({ relId, number }) => relationship(relId, REL.table, targetFromWorksheet(tablePart(number)))),
209
132
  ...pivots.map((pivot) => relationship(pivot.sheetRelId, REL.pivotTable, targetFromWorksheet(pivotTablePart(pivot.number)))),
@@ -233,12 +156,10 @@ export function worksheetRelsXml(tables, drawing, comments, threadedComments, pr
233
156
  : [
234
157
  relationship(background.relId, REL.image, targetFromWorksheet(mediaPart(background.mediaNumber, background.extension))),
235
158
  ]),
236
- ...preservedReferences.map((reference) => relationship(reference.relId, reference.relType, escapeAttr(targetFromWorksheet(reference.entryPath)))),
159
+ ...preservedReferences.map((reference) => relationship(reference.relId, reference.relType, targetFromWorksheet(reference.entryPath))),
237
160
  ...hyperlinks
238
- .filter((link) => link.relId !== undefined && link.target !== undefined)
239
- .map((link) => relationship(link.relId, REL.hyperlink, link.target, {
240
- external: true,
241
- })),
161
+ .filter(isExternalHyperlink)
162
+ .map((link) => relationship(link.relId, REL.hyperlink, link.target, { external: true })),
242
163
  ];
243
164
  return relationshipsPart(rels);
244
165
  }
@@ -255,12 +176,6 @@ function sheetFormatPr(properties, outlineLevel) {
255
176
  attrs += outlineAttr('outlineLevelRow', outlineLevel.row);
256
177
  return `<sheetFormatPr${attrs}/>`;
257
178
  }
258
- function outlineAttr(name, level) {
259
- if (level === undefined)
260
- return '';
261
- const text = numberText(level);
262
- return level > 0 ? ` ${name}="${text}"` : '';
263
- }
264
179
  function maxColumnOutlineLevel(sheet) {
265
180
  let max = 0;
266
181
  for (const column of sheet.columns())
@@ -313,33 +228,35 @@ function colBody(properties, styles) {
313
228
  }
314
229
  return meaningful ? attrs : null;
315
230
  }
316
- function rowAttrs(properties, styles, collapsedSummary) {
317
- if (properties === undefined)
318
- return collapsedSummary ? ' collapsed="1"' : '';
319
- let attrs = '';
320
- if (properties.height !== undefined)
321
- attrs += ` ht="${numberText(properties.height)}" customHeight="1"`;
322
- if (properties.hidden)
323
- attrs += ' hidden="1"';
324
- attrs += outlineAttr('outlineLevel', properties.outlineLevel);
325
- if (properties.collapsed || collapsedSummary)
326
- attrs += ' collapsed="1"';
327
- const style = styles.styleId({ fill: properties.fill });
328
- if (style !== 0)
329
- attrs += ` s="${style}" customFormat="1"`;
330
- return attrs;
231
+ function completeCollapsed(row, collapsedSummaries) {
232
+ if (!collapsedSummaries.has(row.number) || row.attrs.includes(' collapsed="1"')) {
233
+ return { number: row.number, xml: row.xml };
234
+ }
235
+ const open = rowOpenTag(row.number);
236
+ if (!row.xml.startsWith(open)) {
237
+ throw new InternalError(`flushed row ${row.number} does not open with ${quoted(open)}`);
238
+ }
239
+ return {
240
+ number: row.number,
241
+ xml: `${open} collapsed="1"${row.xml.slice(open.length)}`,
242
+ };
331
243
  }
332
- function scanRowOutline(sheet) {
244
+ function scanRowOutline(sheet, flushed) {
333
245
  const level = new Map();
334
246
  const hidden = new Map();
335
247
  let maxLevel = 0;
336
- for (const { number, properties } of sheet.rows()) {
337
- const rowLevel = properties?.outlineLevel ?? 0;
338
- assertWritableNumber(rowLevel);
248
+ const note = (number, rowLevel, rowHidden) => {
249
+ assertWritableLevel(`row ${number} outlineLevel`, rowLevel);
339
250
  level.set(number, rowLevel);
340
- hidden.set(number, properties?.hidden ?? false);
251
+ hidden.set(number, rowHidden);
341
252
  if (rowLevel > maxLevel)
342
253
  maxLevel = rowLevel;
254
+ };
255
+ for (const [number, entry] of flushed?.rowOutline ?? []) {
256
+ note(number, entry.outlineLevel, entry.hidden);
257
+ }
258
+ for (const { number, properties } of sheet.rows()) {
259
+ note(number, properties?.outlineLevel ?? 0, properties?.hidden ?? false);
343
260
  }
344
261
  const levelOf = (row) => level.get(row) ?? 0;
345
262
  const step = sheet.outline.summaryBelow === false ? 1 : -1;
@@ -348,7 +265,7 @@ function scanRowOutline(sheet) {
348
265
  let detail = summary + step;
349
266
  let sawDetail = false;
350
267
  let allHidden = true;
351
- while (levelOf(detail) > summaryLevel) {
268
+ while (level.get(detail) !== undefined && levelOf(detail) > summaryLevel) {
352
269
  sawDetail = true;
353
270
  if (!hidden.get(detail))
354
271
  allHidden = false;
@@ -359,101 +276,3 @@ function scanRowOutline(sheet) {
359
276
  }
360
277
  return { collapsedSummaries, maxLevel };
361
278
  }
362
- function dateDefaultNumFmt(value) {
363
- const date = value instanceof Date
364
- ? value
365
- : (isFormulaValue(value) || isSharedFormulaValue(value)) && value.result instanceof Date
366
- ? value.result
367
- : undefined;
368
- return date !== undefined && !Number.isNaN(date.getTime()) ? DEFAULT_DATE_NUMFMT : undefined;
369
- }
370
- function cellXml(cell, style, shared, sharedStrings) {
371
- const ref = cell.address;
372
- const value = cell.value;
373
- const s = style !== 0 ? ` s="${style}"` : '';
374
- const formula = cellFormulaXml(ref, s, value, shared);
375
- if (formula !== undefined)
376
- return formula;
377
- if (value instanceof Date) {
378
- if (Number.isNaN(value.getTime()))
379
- return `<c r="${ref}"${s}/>`;
380
- return `<c r="${ref}"${s}><v>${numberText(dateToSerial(value))}</v></c>`;
381
- }
382
- if (typeof value === 'number') {
383
- if (!Number.isFinite(value))
384
- return `<c r="${ref}"${s}/>`;
385
- return `<c r="${ref}"${s}><v>${numberText(value)}</v></c>`;
386
- }
387
- if (typeof value === 'boolean') {
388
- return `<c r="${ref}"${s} t="b"><v>${value ? 1 : 0}</v></c>`;
389
- }
390
- if (typeof value === 'string') {
391
- if (sharedStrings !== null) {
392
- return `<c r="${ref}"${s} t="s"><v>${sharedStrings.intern(value)}</v></c>`;
393
- }
394
- return `<c r="${ref}"${s} t="inlineStr"><is>${textElement(value)}</is></c>`;
395
- }
396
- if (isRichTextValue(value)) {
397
- if (sharedStrings !== null) {
398
- return `<c r="${ref}"${s} t="s"><v>${sharedStrings.intern(value)}</v></c>`;
399
- }
400
- return `<c r="${ref}"${s} t="inlineStr"><is>${richTextRunsXml(value.richText)}</is></c>`;
401
- }
402
- if (isHyperlinkValue(value)) {
403
- const label = typeof value.text === 'string'
404
- ? textElement(value.text)
405
- : richTextRunsXml(value.text.richText);
406
- return `<c r="${ref}"${s} t="inlineStr"><is>${label}</is></c>`;
407
- }
408
- if (isErrorValue(value)) {
409
- return `<c r="${ref}"${s} t="e"><v>${value.error}</v></c>`;
410
- }
411
- if (value === null)
412
- return `<c r="${ref}"${s}/>`;
413
- throw new InternalError(`writing a ${detectValueType(value)} cell value has no arm: every CellValue kind is handled above`);
414
- }
415
- function cellFormulaXml(ref, s, value, shared) {
416
- if (shared !== undefined) {
417
- if (shared.ref !== undefined && isFormulaValue(value)) {
418
- const f = `<f t="shared" ref="${shared.ref}" si="${shared.si}">${escapeText(mangleFormula(value.formula))}</f>`;
419
- return formulaBodyXml(ref, s, f, value.result);
420
- }
421
- const result = isSharedFormulaValue(value) ? value.result : undefined;
422
- return formulaBodyXml(ref, s, `<f t="shared" si="${shared.si}"/>`, result);
423
- }
424
- if (isDataTableFormulaValue(value)) {
425
- const attrs = `ref="${escapeAttr(value.ref)}"` +
426
- ` dt2D="${value.dataTable2D ? 1 : 0}"` +
427
- ` dtr="${value.dataTableRow ? 1 : 0}"` +
428
- textAttr('r1', value.r1) +
429
- textAttr('r2', value.r2);
430
- return formulaBodyXml(ref, s, `<f t="dataTable" ${attrs}/>`, value.result);
431
- }
432
- if (isFormulaValue(value)) {
433
- return formulaBodyXml(ref, s, `<f>${escapeText(mangleFormula(value.formula))}</f>`, value.result);
434
- }
435
- return undefined;
436
- }
437
- function formulaBodyXml(ref, s, f, result) {
438
- if (result === undefined || (typeof result === 'number' && !Number.isFinite(result))) {
439
- return `<c r="${ref}"${s}>${f}</c>`;
440
- }
441
- if (typeof result === 'number') {
442
- return `<c r="${ref}"${s}>${f}<v>${numberText(result)}</v></c>`;
443
- }
444
- if (typeof result === 'boolean') {
445
- return `<c r="${ref}"${s} t="b">${f}<v>${result ? 1 : 0}</v></c>`;
446
- }
447
- if (typeof result === 'string') {
448
- return `<c r="${ref}"${s} t="str">${f}<v>${escapeSpreadsheetText(result)}</v></c>`;
449
- }
450
- if (isErrorValue(result)) {
451
- return `<c r="${ref}"${s} t="e">${f}<v>${result.error}</v></c>`;
452
- }
453
- if (result instanceof Date) {
454
- if (Number.isNaN(result.getTime()))
455
- return `<c r="${ref}"${s}>${f}</c>`;
456
- return `<c r="${ref}"${s}>${f}<v>${numberText(dateToSerial(result))}</v></c>`;
457
- }
458
- throw new InternalError('writing a non-primitive formula result has no arm: every FormulaResult kind is handled above');
459
- }
@@ -3,13 +3,15 @@ import type { AutoFilter } from '../../core/autofilter.ts';
3
3
  import type { Cell } from '../../core/cell.ts';
4
4
  import type { ConditionalFormatting } from '../../core/conditional-formatting.ts';
5
5
  import type { DataValidation } from '../../core/data-validation.ts';
6
+ import type { DateEpoch } from '../../core/date.ts';
6
7
  import type { AnchorPoint } from '../../core/image.ts';
8
+ import { INTERNAL } from '../../core/internal.ts';
7
9
  import type { SheetProtectionOptions } from '../../core/protection.ts';
8
10
  import { type CellValue } from '../../core/value.ts';
9
11
  import { type AddImageOptions, type AddWorksheetOptions, Workbook } from '../../core/workbook.ts';
10
12
  import type { Worksheet } from '../../core/worksheet.ts';
13
+ import { type FlushedSheet } from './row-xml.ts';
11
14
  import type { StyleRegistry } from './styles.ts';
12
- import { type FlushedSheet } from './write.ts';
13
15
  /** Calculation settings applied to the streamed workbook. Mirrors the {@link Workbook} flags. */
14
16
  export interface CalcProperties {
15
17
  /** Ask the consumer to recalculate every formula on open: the OOXML `fullCalcOnLoad` flag. */
@@ -45,6 +47,13 @@ export type WorkbookStreamWriterOptions = SinkOptions & {
45
47
  * same {@link WriteOptions.useSharedStrings} the buffered writer exposes. Off by default.
46
48
  */
47
49
  readonly useSharedStrings?: boolean;
50
+ /**
51
+ * Which date system this workbook's serials count in ({@link Workbook.dateEpoch}). A construction
52
+ * option rather than a settable property, unlike on the buffered writer: an eager writer serialises
53
+ * each row as it is committed, so a system changed part-way through would leave the rows before the
54
+ * change counting from a different day than the rows after it.
55
+ */
56
+ readonly dateEpoch?: DateEpoch;
48
57
  };
49
58
  /**
50
59
  * A row appended to a {@link WorksheetStreamWriter}. Style its cells through {@link cells}, then call
@@ -68,7 +77,13 @@ export declare class StreamedRow {
68
77
  */
69
78
  export declare class WorksheetStreamWriter {
70
79
  #private;
71
- constructor(sheet: Worksheet, eager: boolean, styles: StyleRegistry);
80
+ private constructor();
81
+ /**
82
+ * The workbook writer's construction channel. A static member keyed by the same symbol as the
83
+ * instance one below, so both halves of this class's internal wiring are reached the one way; see
84
+ * `core/internal.ts`.
85
+ */
86
+ static readonly [INTERNAL]: WorksheetStreamWriterFactory;
72
87
  /** The sheet's name. */
73
88
  get name(): string;
74
89
  /** The number of rows written so far. Spans gaps and formatted-only rows, like the model, and
@@ -78,16 +93,6 @@ export declare class WorksheetStreamWriter {
78
93
  addRow(values: CellValue[]): StreamedRow;
79
94
  /** Append a batch of rows in one call, each landing directly below the previous. */
80
95
  addRows(rows: CellValue[][]): StreamedRow[];
81
- /**
82
- * Serialise an eagerly-committed row and release its cells from the model. Called by
83
- * {@link StreamedRow.commit}; the row's `<row>` XML is retained (interned into the workbook's live
84
- * style registry so its ids stay valid) and the cell graph is dropped, bounding peak memory.
85
- *
86
- * @throws {AuthoringError} if the row carries a shared-formula cell: a finished row cannot join the
87
- * whole-sheet formula planning, so shared formulas must be authored through {@link getCell}.
88
- */
89
- flushRow(number: number, cells: readonly Cell[]): void;
90
- flushedSheet(): FlushedSheet | undefined;
91
96
  /**
92
97
  * Address a cell by its A1 reference to read or style it before the sheet is committed. The row it
93
98
  * names must still be live: a row whose {@link StreamedRow.commit} has run is finished.
@@ -139,6 +144,41 @@ export declare class WorksheetStreamWriter {
139
144
  /** Whether the sheet has been committed. */
140
145
  get committed(): boolean;
141
146
  get model(): Worksheet;
147
+ readonly [INTERNAL]: WorksheetStreamWriterInternals;
148
+ }
149
+ /**
150
+ * How a {@link WorksheetStreamWriter} comes into being. Its parameters are the writer's own plumbing
151
+ * -- the interning table its rows' style ids are assigned from, the epoch its dates are serialised
152
+ * against -- and a consumer supplies neither, because a consumer receives the sheet writer from
153
+ * {@link WorkbookStreamWriter.addWorksheet}. Reached as `WorksheetStreamWriter[INTERNAL]`.
154
+ */
155
+ export interface WorksheetStreamWriterFactory {
156
+ create(sheet: Worksheet, eager: boolean, styles: StyleRegistry, dateEpoch: DateEpoch): WorksheetStreamWriter;
157
+ }
158
+ /**
159
+ * What the streaming writer's own machinery may do to a sheet writer that a caller may not, the same
160
+ * boundary `sheet[INTERNAL]` draws around the model. Reached as `writer[INTERNAL]`; see
161
+ * `core/internal.ts`.
162
+ *
163
+ * Both members were public until they were not. Neither is callable by anyone but this module, and
164
+ * between them they named seven writer-internal types -- `StyleRegistry`, `FlushedSheet` and the
165
+ * shapes those reach -- on a published class's signature, so a consumer could hold values whose types
166
+ * the package would not export. They carried `@unpublished` to record that, which is a note rather
167
+ * than a boundary; this is the boundary.
168
+ */
169
+ export interface WorksheetStreamWriterInternals {
170
+ /**
171
+ * Serialise an eagerly-committed row and release its cells from the model. Called by
172
+ * {@link StreamedRow.commit}; the row's `<row>` XML is retained (interned into the workbook's live
173
+ * style registry so its ids stay valid) and the cell graph is dropped, bounding peak memory.
174
+ *
175
+ * @throws {AuthoringError} if the row carries a shared-formula cell: a finished row cannot join the
176
+ * whole-sheet formula planning, so shared formulas must be authored through
177
+ * {@link WorksheetStreamWriter.getCell}.
178
+ */
179
+ flushRow(number: number, cells: readonly Cell[]): void;
180
+ /** The rows this writer flushed, or undefined if none. Handed to `buildPackageParts` at commit. */
181
+ flushedSheet(): FlushedSheet | undefined;
142
182
  }
143
183
  /**
144
184
  * A workbook written incrementally to a Node stream. Add worksheets, append their rows, commit each
@@ -169,9 +209,18 @@ export declare class WorkbookStreamWriter {
169
209
  /** Create a worksheet and append it to the workbook. */
170
210
  addWorksheet(name: string, options?: AddWorksheetOptions): WorksheetStreamWriter;
171
211
  /**
172
- * Assemble the workbook into its package, stream the bytes through {@link stream}, and resolve with
173
- * the same bytes. Every sheet is frozen first, so a row added after this rejects legibly. Idempotent
174
- * only in that a second call throws rather than re-emitting.
212
+ * Assemble the workbook into its package and stream the bytes out. Every sheet is frozen first, so
213
+ * a row added after this rejects legibly. Idempotent only in that a second call throws rather than
214
+ * re-emitting.
215
+ *
216
+ * **What it resolves with, and why it can be `undefined`.** The archive is handed back only when
217
+ * somebody asked for it: when no sink was supplied, or when {@link stream} was touched, since a
218
+ * `PassThrough` nobody drained would otherwise be the only copy. A caller who passed `{filename}`
219
+ * or their own `stream` and never reached for {@link stream} gets `undefined`, and the package is
220
+ * never materialised as a whole: retaining every chunk to concatenate them at the end costs a
221
+ * second full-size buffer on top of the part map and the archive itself, which is exactly the
222
+ * memory the caller passed a sink to avoid. The type says so rather than the prose alone, because
223
+ * a promise that resolves with a value the caller was told to ignore is a promise they will use.
175
224
  *
176
225
  * If assembling or zipping the package fails, the returned promise rejects *and* every stream this
177
226
  * writer was given or handed out is destroyed with that error. A caller piping {@link stream}, or
@@ -179,6 +228,6 @@ export declare class WorkbookStreamWriter {
179
228
  * end; and it is an `error` rather than a clean `end` because the bytes written so far are a
180
229
  * truncated package that must not be read as a whole one.
181
230
  */
182
- commit(): Promise<Uint8Array>;
231
+ commit(): Promise<Uint8Array | undefined>;
183
232
  }
184
233
  export {};