@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,41 +1,85 @@
1
1
  import { DEFAULT_THEME_COLOR_SCHEME, isThemeColorSlot, normalizeThemeColor, } from '../../core/theme.js';
2
- import { decodeEntities } from '../../xml/xml-scan.js';
2
+ import { elementRange, parseXml } from '../../xml/xml-read.js';
3
+ import { localName } from '../../xml/xml-scan.js';
3
4
  import { escapeAttr } from '../../xml/xml.js';
4
- const SCHEME_SLOT = new RegExp('<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)\\b[^>]*>' + '\\s*<a:(srgbClr|sysClr)\\b([^>]*)>', 'g');
5
- export function parseThemeColorScheme(themeXml) {
6
- const block = /<a:clrScheme\b[^>]*>([\s\S]*?)<\/a:clrScheme>/.exec(themeXml);
7
- if (block === null)
8
- return {};
5
+ const COLOR_ELEMENTS = new Set(['srgbClr', 'sysClr']);
6
+ function readThemeScheme(themeXml) {
7
+ const colors = {};
8
+ const elements = {};
9
+ const fonts = {};
10
+ let prefix = '';
11
+ let container;
12
+ let slot;
13
+ let fontSlot;
14
+ parseXml(themeXml, {
15
+ onOpen(name, attrs, _selfClosing, scope) {
16
+ const local = localName(name);
17
+ if (local === 'theme') {
18
+ const colon = name.indexOf(':');
19
+ prefix = colon === -1 ? '' : name.slice(0, colon + 1);
20
+ return;
21
+ }
22
+ if (local === 'clrScheme' || local === 'fontScheme') {
23
+ container = local;
24
+ return;
25
+ }
26
+ if (container === 'clrScheme') {
27
+ if (isThemeColorSlot(local)) {
28
+ slot = local;
29
+ }
30
+ else if (slot !== undefined && COLOR_ELEMENTS.has(local)) {
31
+ const value = local === 'sysClr' ? attrs.lastClr : attrs.val;
32
+ if (value !== undefined && /^[0-9a-fA-F]{6}$/.test(value))
33
+ colors[slot] = value;
34
+ elements[slot] = emptyElement(name, attrs);
35
+ slot = undefined;
36
+ }
37
+ return;
38
+ }
39
+ if (container === 'fontScheme') {
40
+ if (local === 'majorFont')
41
+ fontSlot = 'major';
42
+ else if (local === 'minorFont')
43
+ fontSlot = 'minor';
44
+ else if (local === 'latin' && fontSlot !== undefined && attrs.typeface !== undefined) {
45
+ fonts[fontSlot] ??= attrs.typeface;
46
+ }
47
+ }
48
+ void scope;
49
+ },
50
+ onClose(name) {
51
+ const local = localName(name);
52
+ if (local === 'clrScheme' || local === 'fontScheme') {
53
+ container = undefined;
54
+ slot = undefined;
55
+ fontSlot = undefined;
56
+ }
57
+ else if (local === 'majorFont' || local === 'minorFont') {
58
+ fontSlot = undefined;
59
+ }
60
+ else if (slot !== undefined && local === slot) {
61
+ slot = undefined;
62
+ }
63
+ },
64
+ });
9
65
  const scheme = {};
10
- for (const match of (block[1] ?? '').matchAll(SCHEME_SLOT)) {
11
- const slot = match[1];
12
- if (slot === undefined || !isThemeColorSlot(slot))
13
- continue;
14
- const attrs = match[3] ?? '';
15
- const source = match[2] === 'sysClr' ? /\blastClr="([^"]*)"/ : /\bval="([^"]*)"/;
16
- const value = source.exec(attrs)?.[1];
17
- if (value !== undefined && /^[0-9a-fA-F]{6}$/.test(value))
18
- scheme[slot] = value;
19
- }
20
- return scheme;
66
+ if (fonts.major !== undefined)
67
+ scheme.major = fonts.major;
68
+ if (fonts.minor !== undefined)
69
+ scheme.minor = fonts.minor;
70
+ return { colors, elements, fonts: scheme, prefix };
71
+ }
72
+ function emptyElement(name, attrs) {
73
+ let out = `<${name}`;
74
+ for (const key in attrs)
75
+ out += ` ${key}="${escapeAttr(attrs[key] ?? '')}"`;
76
+ return `${out}/>`;
77
+ }
78
+ export function parseThemeColorScheme(themeXml) {
79
+ return readThemeScheme(themeXml).colors;
21
80
  }
22
81
  export function parseThemeFontScheme(themeXml) {
23
- const block = /<a:fontScheme\b[^>]*>([\s\S]*?)<\/a:fontScheme>/.exec(themeXml);
24
- if (block === null)
25
- return {};
26
- const face = (which) => {
27
- const font = new RegExp(`<a:${which}\\b[^>]*>([\\s\\S]*?)</a:${which}>`).exec(block[1] ?? '');
28
- const typeface = /<a:latin\b[^>]*\btypeface="([^"]*)"/.exec(font?.[1] ?? '')?.[1];
29
- return typeface === undefined ? undefined : decodeEntities(typeface);
30
- };
31
- const scheme = {};
32
- const major = face('majorFont');
33
- const minor = face('minorFont');
34
- if (major !== undefined)
35
- scheme.major = major;
36
- if (minor !== undefined)
37
- scheme.minor = minor;
38
- return scheme;
82
+ return readThemeScheme(themeXml).fonts;
39
83
  }
40
84
  const SCHEME_ELEMENT_ORDER = [
41
85
  'dk1',
@@ -52,49 +96,43 @@ const SCHEME_ELEMENT_ORDER = [
52
96
  'folHlink',
53
97
  ];
54
98
  export function applyThemeOverrides(baseXml, overrides) {
55
- let xml = baseXml;
99
+ const { elements: sourceElements, prefix } = readThemeScheme(baseXml);
100
+ const edits = [];
56
101
  const colors = overrides.colors ?? {};
57
102
  if (Object.keys(colors).length > 0) {
58
- const sourceElements = parseThemeColorElements(baseXml);
59
- const body = SCHEME_ELEMENT_ORDER.map((slot) => {
60
- const authored = colors[slot];
61
- const inner = authored !== undefined
62
- ? `<a:srgbClr val="${normalizeThemeColor(authored)}"/>`
63
- : (sourceElements[slot] ?? `<a:srgbClr val="${DEFAULT_THEME_COLOR_SCHEME[slot]}"/>`);
64
- return `<a:${slot}>${inner}</a:${slot}>`;
65
- }).join('');
66
- xml = replaceBlockBody(xml, 'clrScheme', body);
103
+ const scheme = elementRange(baseXml, ['clrScheme']);
104
+ if (scheme !== undefined) {
105
+ const body = SCHEME_ELEMENT_ORDER.map((slot) => {
106
+ const authored = colors[slot];
107
+ const inner = authored !== undefined
108
+ ? `<${prefix}srgbClr val="${normalizeThemeColor(authored)}"/>`
109
+ : (sourceElements[slot] ??
110
+ `<${prefix}srgbClr val="${DEFAULT_THEME_COLOR_SCHEME[slot]}"/>`);
111
+ return `<${prefix}${slot}>${inner}</${prefix}${slot}>`;
112
+ }).join('');
113
+ edits.push({ start: scheme.contentStart, end: scheme.contentEnd, text: body });
114
+ }
67
115
  }
68
116
  const { major, minor } = overrides.fonts ?? {};
117
+ const latin = (which, typeface) => {
118
+ const found = elementRange(baseXml, [which, 'latin']);
119
+ if (found === undefined)
120
+ return;
121
+ const attrs = { ...found.attrs, typeface };
122
+ let rendered = `<${found.name}`;
123
+ for (const key in attrs)
124
+ rendered += ` ${key}="${escapeAttr(attrs[key] ?? '')}"`;
125
+ edits.push({ start: found.start, end: found.end, text: `${rendered}/>` });
126
+ };
69
127
  if (major !== undefined)
70
- xml = replaceLatinTypeface(xml, 'majorFont', major);
128
+ latin('majorFont', major);
71
129
  if (minor !== undefined)
72
- xml = replaceLatinTypeface(xml, 'minorFont', minor);
73
- return xml;
74
- }
75
- function parseThemeColorElements(themeXml) {
76
- const block = /<a:clrScheme\b[^>]*>([\s\S]*?)<\/a:clrScheme>/.exec(themeXml);
77
- if (block === null)
78
- return {};
79
- const elements = {};
80
- const pattern = /<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)>([\s\S]*?)<\/a:\1>|<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)\/>/g;
81
- for (const match of (block[1] ?? '').matchAll(pattern)) {
82
- const slot = match[1] ?? match[3];
83
- if (slot === undefined || !isThemeColorSlot(slot))
84
- continue;
85
- const inner = match[2];
86
- if (inner !== undefined && inner !== '')
87
- elements[slot] = inner;
130
+ latin('minorFont', minor);
131
+ let xml = baseXml;
132
+ for (const edit of edits.sort((a, b) => b.start - a.start)) {
133
+ xml = xml.slice(0, edit.start) + edit.text + xml.slice(edit.end);
88
134
  }
89
- return elements;
90
- }
91
- function replaceBlockBody(xml, name, body) {
92
- const pattern = new RegExp(`(<a:${name}\\b[^>]*>)[\\s\\S]*?(</a:${name}>)`);
93
- return xml.replace(pattern, (_all, open, close) => `${open}${body}${close}`);
94
- }
95
- function replaceLatinTypeface(xml, which, typeface) {
96
- const pattern = new RegExp(`(<a:${which}\\b[^>]*>[\\s\\S]*?<a:latin\\b)[^>]*(/>)`);
97
- return xml.replace(pattern, (_all, open, close) => `${open} typeface="${escapeAttr(typeface)}"${close}`);
135
+ return xml;
98
136
  }
99
137
  export const DEFAULT_THEME_XML = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
100
138
  '<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme">' +
@@ -1,14 +1,12 @@
1
1
  import { type CommentThread, type MentionRef, type Person } from '../../core/comment-thread.ts';
2
- /** A registered author of threaded comments: one `<person>` of `xl/persons/person.xml`. */
3
- export interface ParsedPerson {
4
- /** Brace-wrapped GUID a message's `personId` points at. */
5
- readonly id: string;
6
- readonly displayName: string;
7
- /** Identity-provider handle, `S::<email>::<tenant-guid>` for an AzureAD account. */
8
- readonly userId?: string;
9
- /** Identity provider, e.g. `AD`. */
10
- readonly providerId?: string;
11
- }
2
+ /**
3
+ * A registered author of threaded comments: one `<person>` of `xl/persons/person.xml`, which is the
4
+ * model's own {@link Person} with nothing added. Declared as an alias for the same reason
5
+ * {@link ParsedMention} is: this file's write half already emits `Person` directly, so re-declaring
6
+ * the four fields here made one feature module name one shape two ways, with nothing keeping the two
7
+ * in step.
8
+ */
9
+ export type ParsedPerson = Person;
12
10
  /**
13
11
  * One `<mention>` of a message's `<mentions>` block: the model's own {@link MentionRef}, which is
14
12
  * exactly the four wire attributes with nothing resolved yet. Declaring it a second time here would
@@ -1,7 +1,8 @@
1
1
  import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
2
2
  import { MENTION_OFFSET_MAX, } from '../../core/comment-thread.js';
3
+ import { decodeSpreadsheetText, numInteger } from '../../xml/xml-attrs.js';
3
4
  import { parseXml, TextCapture } from '../../xml/xml-read.js';
4
- import { boolStrict, decodeSpreadsheetText, localName, numInteger, } from '../../xml/xml-scan.js';
5
+ import { boolStrict, localName } from '../../xml/xml-scan.js';
5
6
  import { escapeAttr, escapeSpreadsheetText, textAttr, XML_DECLARATION } from '../../xml/xml.js';
6
7
  import { THREADED_COMMENTS_NS } from './namespaces.js';
7
8
  export function parsePersons(xml) {
@@ -3,10 +3,60 @@ import type { PivotPlan, PreservedPartPlan, PreservedRootReferencePlan, Preserve
3
3
  export type PreservedWorkbookRel = PreservedWorkbookReferencePlan & {
4
4
  readonly relId: string;
5
5
  };
6
- export declare function contentTypesXml(sheetCount: number, tables: readonly TablePlan[], commentNumbers: readonly number[], drawingNumbers: readonly number[], printerSettingsNumbers: readonly number[], mediaExtensions: readonly string[], hasSharedStrings: boolean, preservedParts: readonly PreservedPartPlan[], pivots: readonly PivotPlan[], preservedWorkbookRefs: readonly PreservedWorkbookReferencePlan[], threadedCommentNumbers: readonly number[], hasPersons: boolean): string;
6
+ /**
7
+ * The workbook part's relationship ids, drawn once by the writer's `assignWorkbookRelIds` and handed
8
+ * to every consumer rather than re-derived by any of them.
9
+ *
10
+ * Declared here because both consumers are here: the rels part wires the ids, and the workbook body
11
+ * *references* them from `<sheet r:id>`. The body used to spell that reference `rId${i + 1}`, which
12
+ * agreed with the allocator only because sheets happen to be the first ids it hands out. Anything
13
+ * laid ahead of them would have re-pointed every sheet at the wrong part, and the package would have
14
+ * stayed schema-valid while doing it -- a class of corruption no validator catches and no round-trip
15
+ * through this library notices, because the reader resolves the same wrong ids consistently.
16
+ */
17
+ export interface WorkbookRelPlan {
18
+ readonly sheetRelIds: readonly string[];
19
+ readonly stylesRelId: string;
20
+ readonly themeRelId: string;
21
+ readonly sharedStringsRelId: string | null;
22
+ readonly personsRelId: string | null;
23
+ readonly preservedWorkbookRels: readonly PreservedWorkbookRel[];
24
+ }
25
+ /**
26
+ * What `[Content_Types].xml` needs to know about a package: every part family it declares.
27
+ *
28
+ * One object rather than twelve positional parameters, eight of them arrays and four of those bare
29
+ * `readonly number[]`, which two adjacent ones could be silently transposed between. That is the same
30
+ * hazard `SheetReferences`, `SheetPlan` and `WorksheetXmlInputs` remove elsewhere in this writer, and
31
+ * it travels the whole way down: the two functions that render from this take it as it stands rather
32
+ * than re-scattering its fields across their own parameter lists.
33
+ */
34
+ export interface ContentTypeInputs {
35
+ readonly sheetCount: number;
36
+ readonly tables: readonly TablePlan[];
37
+ readonly commentNumbers: readonly number[];
38
+ readonly drawingNumbers: readonly number[];
39
+ readonly printerSettingsNumbers: readonly number[];
40
+ readonly mediaExtensions: readonly string[];
41
+ readonly hasSharedStrings: boolean;
42
+ readonly preservedParts: readonly PreservedPartPlan[];
43
+ readonly pivots: readonly PivotPlan[];
44
+ readonly preservedWorkbookRefs: readonly PreservedWorkbookReferencePlan[];
45
+ readonly threadedCommentNumbers: readonly number[];
46
+ readonly hasPersons: boolean;
47
+ }
48
+ export declare function contentTypesXml(inputs: ContentTypeInputs): string;
7
49
  export declare function rootRelsXml(rootRefs: readonly PreservedRootReferencePlan[]): string;
8
- export declare function workbookXml(workbook: Workbook, preservedRels: readonly PreservedWorkbookRel[], pivots: readonly PivotPlan[]): string;
9
- export declare const FIXED_WORKBOOK_REL_COUNT = 2;
10
- export declare function workbookRelsXml(sheetCount: number, hasSharedStrings: boolean, personsRelId: string | null, preservedRels: readonly PreservedWorkbookRel[], pivots: readonly PivotPlan[]): string;
50
+ export declare function workbookXml(workbook: Workbook, plan: WorkbookRelPlan, pivots: readonly PivotPlan[]): string;
51
+ /**
52
+ * The workbook part's `.rels`, rendered from the ids the planner drew.
53
+ *
54
+ * Every id arrives in `plan`; none is computed here. It used to re-derive the styles, theme and
55
+ * shared-strings ids from the sheet count and a shared constant while `write.ts` independently summed
56
+ * the same inputs to place everything after them, so one sequence was spelled in two files and only
57
+ * its fixed part was shared. The relationship *order* is still stated here, because that is what this
58
+ * function is; the numbering is not, because two numberings of one sequence is how they drift apart.
59
+ */
60
+ export declare function workbookRelsXml(plan: WorkbookRelPlan, pivots: readonly PivotPlan[]): string;
11
61
  export declare function corePropsXml(properties: WorkbookProperties): string;
12
62
  export declare function appPropsXml(properties: WorkbookProperties): string;
@@ -1,7 +1,9 @@
1
1
  import { mangleFormula, quoteSheetName } from '../../core/formula.js';
2
2
  import { WORKBOOK_PROTECTION_CREDENTIAL_ATTRS } from '../../core/workbook-protection.js';
3
3
  import { isVisibility } from '../../core/worksheet.js';
4
- import { checkedToken, escapeAttr, escapeText, numAttr, textAttr, XML_DECLARATION, } from '../../xml/xml.js';
4
+ import { AuthoringError, InternalError, quoted } from '../../errors.js';
5
+ import { isRelType } from '../../rel-type.js';
6
+ import { assertWritableDate, checkedToken, escapeAttr, escapeText, numAttr, textAttr, XML_DECLARATION, } from '../../xml/xml.js';
5
7
  import { extensionOf, THEME_PART_PATH } from '../opc/part-paths.js';
6
8
  import { relationship, relationshipsPart } from '../opc/rels.js';
7
9
  import { imageContentType } from './images.js';
@@ -29,15 +31,16 @@ const CT = {
29
31
  threadedComments: 'application/vnd.ms-excel.threadedcomments+xml',
30
32
  person: 'application/vnd.ms-excel.person+xml',
31
33
  };
32
- export function contentTypesXml(sheetCount, tables, commentNumbers, drawingNumbers, printerSettingsNumbers, mediaExtensions, hasSharedStrings, preservedParts, pivots, preservedWorkbookRefs, threadedCommentNumbers, hasPersons) {
33
- const extensionDefaults = buildExtensionDefaults(commentNumbers, printerSettingsNumbers, mediaExtensions, preservedParts);
34
+ export function contentTypesXml(inputs) {
35
+ const extensionDefaults = buildExtensionDefaults(inputs);
34
36
  return (XML_DECLARATION +
35
37
  `<Types xmlns="${NS.contentTypes}">` +
36
38
  contentTypeDefaults(extensionDefaults) +
37
- contentTypeOverrides(sheetCount, tables, drawingNumbers, commentNumbers, hasSharedStrings, preservedParts, pivots, preservedWorkbookRefs, extensionDefaults, threadedCommentNumbers, hasPersons) +
39
+ contentTypeOverrides(inputs, extensionDefaults) +
38
40
  '</Types>');
39
41
  }
40
- function buildExtensionDefaults(commentNumbers, printerSettingsNumbers, mediaExtensions, preservedParts) {
42
+ function buildExtensionDefaults(inputs) {
43
+ const { commentNumbers, printerSettingsNumbers, mediaExtensions, preservedParts } = inputs;
41
44
  const defaults = new Map();
42
45
  const add = (extension, contentType) => {
43
46
  const key = extension.toLowerCase();
@@ -54,25 +57,25 @@ function buildExtensionDefaults(commentNumbers, printerSettingsNumbers, mediaExt
54
57
  add(ext, imageContentType(ext));
55
58
  for (const part of preservedParts) {
56
59
  const ext = extensionOf(part.path);
57
- if (ext.toLowerCase() === 'xml')
60
+ if (ext === 'xml')
58
61
  continue;
59
62
  add(ext, part.contentType);
60
63
  }
61
64
  return defaults;
62
65
  }
63
66
  function isMacroEnabled(preservedWorkbookRefs) {
64
- return preservedWorkbookRefs.some((ref) => ref.relType.endsWith('/vbaProject'));
67
+ return preservedWorkbookRefs.some((ref) => isRelType(ref.relType, 'vbaProject'));
65
68
  }
66
69
  function contentTypeDefaults(extensionDefaults) {
67
70
  return [...extensionDefaults.values()]
68
71
  .map(({ extension, contentType }) => defaultType(extension, contentType))
69
72
  .join('');
70
73
  }
71
- function contentTypeOverrides(sheetCount, tables, drawingNumbers, commentNumbers, hasSharedStrings, preservedParts, pivots, preservedWorkbookRefs, extensionDefaults, threadedCommentNumbers, hasPersons) {
74
+ function contentTypeOverrides(inputs, extensionDefaults) {
75
+ const { sheetCount, tables, drawingNumbers, commentNumbers, threadedCommentNumbers, pivots, hasSharedStrings, hasPersons, preservedParts, preservedWorkbookRefs, } = inputs;
72
76
  const preservedOverrides = preservedParts
73
77
  .filter((part) => part.path !== THEME_PART_PATH)
74
- .filter((part) => extensionDefaults.get(extensionOf(part.path).toLowerCase())?.contentType !==
75
- part.contentType)
78
+ .filter((part) => extensionDefaults.get(extensionOf(part.path))?.contentType !== part.contentType)
76
79
  .map((part) => override(part.path, part.contentType));
77
80
  return [
78
81
  override(WORKBOOK_PART, isMacroEnabled(preservedWorkbookRefs) ? CT.macroEnabledWorkbook : CT.workbook),
@@ -94,10 +97,10 @@ function contentTypeOverrides(sheetCount, tables, drawingNumbers, commentNumbers
94
97
  ].join('');
95
98
  }
96
99
  function override(partPath, contentType) {
97
- return `<Override PartName="/${partPath}" ContentType="${contentType}"/>`;
100
+ return `<Override PartName="/${escapeAttr(partPath)}" ContentType="${escapeAttr(contentType)}"/>`;
98
101
  }
99
102
  function defaultType(extension, contentType) {
100
- return `<Default Extension="${extension}" ContentType="${contentType}"/>`;
103
+ return `<Default Extension="${escapeAttr(extension)}" ContentType="${escapeAttr(contentType)}"/>`;
101
104
  }
102
105
  export function rootRelsXml(rootRefs) {
103
106
  return relationshipsPart([
@@ -107,18 +110,25 @@ export function rootRelsXml(rootRefs) {
107
110
  ...rootRefs.map((ref, i) => relationship(`rId${4 + i}`, ref.relType, ref.entryPath)),
108
111
  ]);
109
112
  }
110
- export function workbookXml(workbook, preservedRels, pivots) {
113
+ export function workbookXml(workbook, plan, pivots) {
114
+ const preservedRels = plan.preservedWorkbookRels;
111
115
  const sheets = workbook.worksheets;
112
116
  const entries = sheets
113
117
  .map((sheet, i) => {
118
+ const relId = plan.sheetRelIds[i];
119
+ if (relId === undefined) {
120
+ throw new InternalError(`worksheet ${i + 1} of ${sheets.length} has no relationship id: the plan was drawn for ` +
121
+ `${plan.sheetRelIds.length} sheet(s)`);
122
+ }
114
123
  const state = sheet.state === 'visible'
115
124
  ? ''
116
125
  : ` state="${checkedToken(sheet.state, isVisibility, 'sheet visibility')}"`;
117
- return `<sheet name="${escapeAttr(sheet.name)}" sheetId="${sheet.id}"${state} r:id="rId${i + 1}"/>`;
126
+ return `<sheet name="${escapeAttr(sheet.name)}" sheetId="${sheet.id}"${state} r:id="${relId}"/>`;
118
127
  })
119
128
  .join('');
120
129
  return (XML_DECLARATION +
121
130
  `<workbook xmlns="${NS.main}" xmlns:r="${NS.docRels}">` +
131
+ workbookPrXml(workbook) +
122
132
  workbookProtectionXml(workbook) +
123
133
  bookViewsXml(workbook) +
124
134
  `<sheets>${entries}</sheets>` +
@@ -129,6 +139,11 @@ export function workbookXml(workbook, preservedRels, pivots) {
129
139
  workbookExtLstXml(preservedRels) +
130
140
  '</workbook>');
131
141
  }
142
+ function workbookPrXml(workbook) {
143
+ const attrs = (workbook.dateEpoch === 1904 ? ' date1904="1"' : '') +
144
+ (workbook.codeName === undefined ? '' : ` codeName="${escapeAttr(workbook.codeName)}"`);
145
+ return attrs === '' ? '' : `<workbookPr${attrs}/>`;
146
+ }
132
147
  function bookViewsXml(workbook) {
133
148
  const view = workbook.view;
134
149
  const activeTab = workbook.activeTabIndex;
@@ -146,23 +161,23 @@ function bookViewsXml(workbook) {
146
161
  function externalReferencesXml(preservedRels) {
147
162
  const links = preservedRels
148
163
  .filter((ref) => ref.externalReferenceIndex !== undefined)
149
- .sort((a, b) => a.externalReferenceIndex - b.externalReferenceIndex);
164
+ .sort((a, b) => (a.externalReferenceIndex ?? 0) - (b.externalReferenceIndex ?? 0));
150
165
  if (links.length === 0)
151
166
  return '';
152
167
  const entries = links.map((ref) => `<externalReference r:id="${ref.relId}"/>`).join('');
153
168
  return `<externalReferences>${entries}</externalReferences>`;
154
169
  }
155
170
  function workbookExtLstXml(preservedRels) {
156
- const caches = preservedRels.filter((ref) => ref.relType.endsWith('/slicerCache'));
171
+ const caches = preservedRels.filter((ref) => isRelType(ref.relType, 'slicerCache'));
157
172
  if (caches.length === 0)
158
173
  return '';
159
174
  const entries = caches.map((ref) => `<x14:slicerCache r:id="${ref.relId}"/>`).join('');
160
175
  return `<extLst>${x14Ext(SLICER_CACHES_EXT_URI, `<x14:slicerCaches>${entries}</x14:slicerCaches>`)}</extLst>`;
161
176
  }
162
177
  function pivotCachesXml(preservedRels, pivots) {
163
- const preserved = preservedRels
164
- .filter((ref) => ref.pivotCacheId !== undefined)
165
- .map((ref) => `<pivotCache cacheId="${escapeAttr(ref.pivotCacheId)}" r:id="${ref.relId}"/>`);
178
+ const preserved = preservedRels.flatMap((ref) => ref.pivotCacheId === undefined
179
+ ? []
180
+ : [`<pivotCache cacheId="${escapeAttr(ref.pivotCacheId)}" r:id="${ref.relId}"/>`]);
166
181
  const generated = pivots.map((pivot) => `<pivotCache cacheId="${escapeAttr(pivot.cacheId)}" r:id="${pivot.workbookRelId}"/>`);
167
182
  const entries = [...preserved, ...generated];
168
183
  if (entries.length === 0)
@@ -193,12 +208,20 @@ const EXCEL_CALC_ID = '171027';
193
208
  function calcPrXml(workbook) {
194
209
  return workbook.fullCalcOnLoad ? `<calcPr calcId="${EXCEL_CALC_ID}" fullCalcOnLoad="1"/>` : '';
195
210
  }
211
+ function localSheetId(sheets, name, scope) {
212
+ const wanted = scope.toLowerCase();
213
+ const index = sheets.findIndex((sheet) => sheet.name.toLowerCase() === wanted);
214
+ if (index === -1) {
215
+ throw new AuthoringError(`defined name ${quoted(name)} is scoped to worksheet ${quoted(scope)}, which this workbook does not contain`);
216
+ }
217
+ return index;
218
+ }
196
219
  function definedNamesXml(workbook) {
197
220
  const sheets = workbook.worksheets;
198
221
  const userEntries = workbook.definedNames.map((name) => {
199
222
  const scopeAttr = name.scope === undefined
200
223
  ? ''
201
- : ` localSheetId="${sheets.findIndex((sheet) => sheet.name === name.scope)}"`;
224
+ : ` localSheetId="${localSheetId(sheets, name.name, name.scope)}"`;
202
225
  const commentAttr = textAttr('comment', name.comment);
203
226
  const hiddenAttr = name.hidden ? ' hidden="1"' : '';
204
227
  return (`<definedName name="${escapeAttr(name.name)}"${scopeAttr}${commentAttr}${hiddenAttr}>` +
@@ -219,21 +242,22 @@ function filterDatabaseRefersTo(sheetName, range) {
219
242
  const absolute = range.replace(/([A-Z]+)(\d+)/g, '$$$1$$$2');
220
243
  return `${quoteSheetName(sheetName)}!${absolute}`;
221
244
  }
222
- export const FIXED_WORKBOOK_REL_COUNT = 2;
223
- export function workbookRelsXml(sheetCount, hasSharedStrings, personsRelId, preservedRels, pivots) {
245
+ export function workbookRelsXml(plan, pivots) {
246
+ const { personsRelId } = plan;
247
+ const preservedRels = plan.preservedWorkbookRels;
224
248
  return relationshipsPart([
225
- ...Array.from({ length: sheetCount }, (_, i) => relationship(`rId${i + 1}`, REL.worksheet, targetFromWorkbook(worksheetPart(i + 1)))),
226
- relationship(`rId${sheetCount + 1}`, REL.styles, 'styles.xml'),
227
- relationship(`rId${sheetCount + FIXED_WORKBOOK_REL_COUNT}`, REL.theme, targetFromWorkbook(THEME_PART_PATH)),
228
- ...(hasSharedStrings
229
- ? [
230
- relationship(`rId${sheetCount + FIXED_WORKBOOK_REL_COUNT + 1}`, REL.sharedStrings, targetFromWorkbook(SHARED_STRINGS_PART)),
231
- ]
232
- : []),
249
+ ...plan.sheetRelIds.map((relId, i) => relationship(relId, REL.worksheet, targetFromWorkbook(worksheetPart(i + 1)))),
250
+ relationship(plan.stylesRelId, REL.styles, targetFromWorkbook(STYLES_PART)),
251
+ relationship(plan.themeRelId, REL.theme, targetFromWorkbook(THEME_PART_PATH)),
252
+ ...(plan.sharedStringsRelId === null
253
+ ? []
254
+ : [
255
+ relationship(plan.sharedStringsRelId, REL.sharedStrings, targetFromWorkbook(SHARED_STRINGS_PART)),
256
+ ]),
233
257
  ...(personsRelId === null
234
258
  ? []
235
259
  : [relationship(personsRelId, REL.person, targetFromWorkbook(PERSONS_PART))]),
236
- ...preservedRels.map((ref) => relationship(ref.relId, ref.relType, escapeAttr(targetFromWorkbook(ref.entryPath)))),
260
+ ...preservedRels.map((ref) => relationship(ref.relId, ref.relType, targetFromWorkbook(ref.entryPath))),
237
261
  ...pivots.map((pivot) => relationship(pivot.workbookRelId, REL.pivotCacheDefinition, targetFromWorkbook(pivotCacheDefinitionPart(pivot.number)))),
238
262
  ]);
239
263
  }
@@ -248,10 +272,10 @@ export function corePropsXml(properties) {
248
272
  if (properties.lastModifiedBy !== undefined) {
249
273
  parts.push(`<cp:lastModifiedBy>${escapeText(properties.lastModifiedBy)}</cp:lastModifiedBy>`);
250
274
  }
251
- if (properties.created) {
275
+ if (properties.created !== undefined) {
252
276
  parts.push(w3cdtf('created', properties.created));
253
277
  }
254
- if (properties.modified) {
278
+ if (properties.modified !== undefined) {
255
279
  parts.push(w3cdtf('modified', properties.modified));
256
280
  }
257
281
  return (XML_DECLARATION +
@@ -261,7 +285,9 @@ export function corePropsXml(properties) {
261
285
  '</cp:coreProperties>');
262
286
  }
263
287
  function w3cdtf(element, date) {
264
- return `<dcterms:${element} xsi:type="dcterms:W3CDTF">${date.toISOString()}</dcterms:${element}>`;
288
+ assertWritableDate(date, `the document property ${quoted(element)}`);
289
+ const stamp = date.toISOString().replace(/\.\d{3}Z$/, 'Z');
290
+ return `<dcterms:${element} xsi:type="dcterms:W3CDTF">${stamp}</dcterms:${element}>`;
265
291
  }
266
292
  export function appPropsXml(properties) {
267
293
  const company = properties.company === undefined ? '' : `<Company>${escapeText(properties.company)}</Company>`;
@@ -1,46 +1,10 @@
1
- import { type Cell } from '../../core/cell.ts';
2
- import type { ColumnProperties, RowProperties, Worksheet } from '../../core/worksheet.ts';
1
+ import type { DateEpoch } from '../../core/date.ts';
2
+ import type { Worksheet } from '../../core/worksheet.ts';
3
3
  import { type HyperlinkPlan } from './hyperlinks.ts';
4
- import type { BackgroundPlan, CommentPlan, DrawingPlan, PivotPlan, PreservedReferencePlan, PrinterSettingsPlan, TablePlan, ThreadedCommentPlan } from './package-plan.ts';
5
- import { type SharedFormulaRole } from './shared-formulas.ts';
4
+ import type { SheetPlan, TablePlan } from './package-plan.ts';
5
+ import { type FlushedSheet } from './row-xml.ts';
6
6
  import type { SharedStringTable } from './shared-strings.ts';
7
7
  import type { StyleRegistry } from './styles.ts';
8
- /**
9
- * The used-cell extent of a sheet: the top-left/bottom-right grid bounds that fold into the
10
- * `<dimension>`. Rows carrying only formatting (a row height, an outline level) do not extend the
11
- * used range, matching how Excel records `<dimension>`, so {@link add} ignores them. A fresh extent
12
- * holds the `Infinity`/`-Infinity` sentinels; {@link isEmpty} reports that no used cell has been seen.
13
- */
14
- export declare class Extent {
15
- top: number;
16
- left: number;
17
- bottom: number;
18
- right: number;
19
- constructor(seed?: Extent);
20
- /** Whether no used cell has been folded in yet, in which case the dimension is the lone cell `A1`. */
21
- get isEmpty(): boolean;
22
- /** Fold a rendered row's used-column span into the extent. `minCol` is `Infinity` when the row
23
- * carried no cells (only formatting), which extends nothing. */
24
- add(row: number, minCol: number, maxCol: number): void;
25
- }
26
- /**
27
- * A worksheet's eagerly-serialised rows: each row's `<row>` XML tagged with its number (so it merges
28
- * into ascending order with the sheet's remaining live rows, whatever order it was committed in), plus
29
- * the used-cell {@link Extent} they span. The buffered pass folds that extent into the sheet's dimension.
30
- */
31
- export interface FlushedSheet {
32
- readonly rows: ReadonlyArray<{
33
- readonly number: number;
34
- readonly xml: string;
35
- }>;
36
- readonly extent: Extent;
37
- /**
38
- * The deepest row outline level among the flushed rows. Carried across the eviction because
39
- * `<sheetFormatPr outlineLevelRow>` is derived from every row on the sheet, and a flushed row's
40
- * properties are gone from the model by the time the header is serialised.
41
- */
42
- readonly maxRowOutlineLevel: number;
43
- }
44
8
  export interface SheetReferences {
45
9
  readonly drawingRelId: string | null;
46
10
  readonly legacyDrawingRelId: string | null;
@@ -49,38 +13,27 @@ export interface SheetReferences {
49
13
  readonly legacyDrawingHFRelId: string | null;
50
14
  readonly slicerRelIds: readonly string[];
51
15
  }
52
- export declare function worksheetXml(sheet: Worksheet, tables: readonly TablePlan[], styles: StyleRegistry, references: SheetReferences, hyperlinks: readonly HyperlinkPlan[], sharedStrings: SharedStringTable | null, active: boolean, flushed?: FlushedSheet): string;
53
16
  /**
54
- * A column's style facets are defaults its cells inherit unless they override them; the writer
55
- * composes each cell's full style up front (cell over row over column, per facet) so a cell that
56
- * overrides one facet still carries the column's others, rather than silently dropping them. Frozen
57
- * once by the streaming writer at its first flush so every eagerly-rendered row sees the same defaults.
17
+ * Everything one worksheet part is rendered from: the sheet itself, the parts of the package plan
18
+ * that belong to it, and the two registries a sheet pass interns into.
19
+ *
20
+ * One object for the reason {@link SheetReferences} is one object, applied to the layer above it: a
21
+ * run of positional arguments carrying two `readonly …[]`s, two nullable references and a bare
22
+ * boolean is a run whose order the call site cannot be read against. `active:` is the clearest of
23
+ * them -- as a positional it needed a comment at the call site to say which boolean it was.
58
24
  */
59
- export declare function buildColumnDefaults(sheet: Worksheet): Map<number, ColumnProperties>;
60
- /** The whole-sheet context a single row needs to serialise: the column defaults it inherits, the
61
- * style/string tables it interns into, the shared-formula roles its cells play, and the collapsed
62
- * outline summaries whose toggle it must stamp. The streaming writer supplies empty shared-formula
63
- * and collapsed-summary sets, since those are whole-sheet derivations a flushed row cannot join. */
64
- export interface RowRenderContext {
65
- readonly columnDefaults: ReadonlyMap<number, ColumnProperties>;
25
+ export interface WorksheetXmlInputs {
26
+ readonly sheet: Worksheet;
27
+ readonly tables: readonly TablePlan[];
66
28
  readonly styles: StyleRegistry;
29
+ readonly references: SheetReferences;
30
+ readonly hyperlinks: readonly HyperlinkPlan[];
67
31
  readonly sharedStrings: SharedStringTable | null;
68
- readonly sharedRoles: ReadonlyMap<string, SharedFormulaRole>;
69
- readonly collapsedSummaries: ReadonlySet<number>;
32
+ readonly dateEpoch: DateEpoch;
33
+ /** Whether this is the workbook's one selected sheet. */
34
+ readonly active: boolean;
35
+ /** The rows the streaming writer already serialised and evicted, absent on the buffered path. */
36
+ readonly flushed?: FlushedSheet | undefined;
70
37
  }
71
- /**
72
- * Serialise one row to its `<row>` element, or '' when the row has neither data nor its own
73
- * formatting. Returns the used-column bounds (`Infinity`/`-Infinity` when nothing was rendered) so a
74
- * caller can fold them into the sheet dimension. Shared by the buffered sheet pass and the streaming
75
- * writer's eager flush, so both emit byte-identical rows.
76
- */
77
- export declare function renderRow(entry: {
78
- readonly number: number;
79
- readonly cells: readonly Cell[];
80
- readonly properties: RowProperties | undefined;
81
- }, ctx: RowRenderContext): {
82
- xml: string;
83
- minCol: number;
84
- maxCol: number;
85
- };
86
- export declare function worksheetRelsXml(tables: readonly TablePlan[], drawing: DrawingPlan | null, comments: CommentPlan | null, threadedComments: ThreadedCommentPlan | null, printerSettings: PrinterSettingsPlan | null, background: BackgroundPlan | null, hyperlinks: readonly HyperlinkPlan[], preservedReferences: readonly PreservedReferencePlan[], pivots: readonly PivotPlan[]): string;
38
+ export declare function worksheetXml(inputs: WorksheetXmlInputs): string;
39
+ export declare function worksheetRelsXml(plan: SheetPlan): string;