@shbernal/ts-xlsx 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +230 -0
  3. package/dist/core/address.d.ts +49 -0
  4. package/dist/core/address.js +125 -0
  5. package/dist/core/autofilter.d.ts +55 -0
  6. package/dist/core/autofilter.js +48 -0
  7. package/dist/core/cell.d.ts +132 -0
  8. package/dist/core/cell.js +249 -0
  9. package/dist/core/color-resolution.d.ts +58 -0
  10. package/dist/core/color-resolution.js +170 -0
  11. package/dist/core/column.d.ts +87 -0
  12. package/dist/core/column.js +167 -0
  13. package/dist/core/comment-thread.d.ts +116 -0
  14. package/dist/core/comment-thread.js +62 -0
  15. package/dist/core/conditional-formatting-overlay.d.ts +15 -0
  16. package/dist/core/conditional-formatting-overlay.js +24 -0
  17. package/dist/core/conditional-formatting.d.ts +70 -0
  18. package/dist/core/conditional-formatting.js +35 -0
  19. package/dist/core/containers.d.ts +2 -0
  20. package/dist/core/containers.js +25 -0
  21. package/dist/core/data-validation-overlay.d.ts +28 -0
  22. package/dist/core/data-validation-overlay.js +56 -0
  23. package/dist/core/data-validation.d.ts +38 -0
  24. package/dist/core/data-validation.js +13 -0
  25. package/dist/core/date.d.ts +25 -0
  26. package/dist/core/date.js +58 -0
  27. package/dist/core/formula.d.ts +49 -0
  28. package/dist/core/formula.js +319 -0
  29. package/dist/core/grid-edits.d.ts +22 -0
  30. package/dist/core/grid-edits.js +206 -0
  31. package/dist/core/image.d.ts +76 -0
  32. package/dist/core/image.js +65 -0
  33. package/dist/core/internal.d.ts +11 -0
  34. package/dist/core/internal.js +29 -0
  35. package/dist/core/merge.d.ts +13 -0
  36. package/dist/core/merge.js +26 -0
  37. package/dist/core/modern-functions.d.ts +1 -0
  38. package/dist/core/modern-functions.js +165 -0
  39. package/dist/core/page-setup.d.ts +94 -0
  40. package/dist/core/page-setup.js +6 -0
  41. package/dist/core/pivot-table.d.ts +113 -0
  42. package/dist/core/pivot-table.js +249 -0
  43. package/dist/core/preserved.d.ts +58 -0
  44. package/dist/core/preserved.js +5 -0
  45. package/dist/core/protection.d.ts +67 -0
  46. package/dist/core/protection.js +68 -0
  47. package/dist/core/range.d.ts +112 -0
  48. package/dist/core/range.js +283 -0
  49. package/dist/core/row.d.ts +73 -0
  50. package/dist/core/row.js +135 -0
  51. package/dist/core/style.d.ts +246 -0
  52. package/dist/core/style.js +125 -0
  53. package/dist/core/table-style.d.ts +67 -0
  54. package/dist/core/table-style.js +90 -0
  55. package/dist/core/table.d.ts +194 -0
  56. package/dist/core/table.js +291 -0
  57. package/dist/core/theme.d.ts +90 -0
  58. package/dist/core/theme.js +272 -0
  59. package/dist/core/value.d.ts +115 -0
  60. package/dist/core/value.js +144 -0
  61. package/dist/core/workbook-protection.d.ts +27 -0
  62. package/dist/core/workbook-protection.js +25 -0
  63. package/dist/core/workbook.d.ts +528 -0
  64. package/dist/core/workbook.js +732 -0
  65. package/dist/core/worksheet-model.d.ts +34 -0
  66. package/dist/core/worksheet-model.js +100 -0
  67. package/dist/core/worksheet.d.ts +647 -0
  68. package/dist/core/worksheet.js +1025 -0
  69. package/dist/customui/errors.d.ts +15 -0
  70. package/dist/customui/errors.js +15 -0
  71. package/dist/customui/index.d.ts +2 -0
  72. package/dist/customui/index.js +7 -0
  73. package/dist/customui/ribbon.d.ts +86 -0
  74. package/dist/customui/ribbon.js +180 -0
  75. package/dist/entries/core.d.ts +23 -0
  76. package/dist/entries/core.js +20 -0
  77. package/dist/entries/csv.d.ts +2 -0
  78. package/dist/entries/csv.js +6 -0
  79. package/dist/entries/customui.d.ts +1 -0
  80. package/dist/entries/customui.js +3 -0
  81. package/dist/entries/errors.d.ts +7 -0
  82. package/dist/entries/errors.js +20 -0
  83. package/dist/entries/vba.d.ts +2 -0
  84. package/dist/entries/vba.js +7 -0
  85. package/dist/entries/xlsb.d.ts +1 -0
  86. package/dist/entries/xlsb.js +7 -0
  87. package/dist/entries/xlsx.d.ts +5 -0
  88. package/dist/entries/xlsx.js +18 -0
  89. package/dist/errors.d.ts +65 -0
  90. package/dist/errors.js +66 -0
  91. package/dist/index.d.ts +7 -0
  92. package/dist/index.js +19 -0
  93. package/dist/io/csv/read.d.ts +15 -0
  94. package/dist/io/csv/read.js +113 -0
  95. package/dist/io/csv/write.d.ts +28 -0
  96. package/dist/io/csv/write.js +124 -0
  97. package/dist/io/opc/errors.d.ts +46 -0
  98. package/dist/io/opc/errors.js +49 -0
  99. package/dist/io/opc/inflate.d.ts +15 -0
  100. package/dist/io/opc/inflate.js +91 -0
  101. package/dist/io/opc/namespaces.d.ts +18 -0
  102. package/dist/io/opc/namespaces.js +18 -0
  103. package/dist/io/opc/part-paths.d.ts +4 -0
  104. package/dist/io/opc/part-paths.js +35 -0
  105. package/dist/io/opc/read-opc.d.ts +24 -0
  106. package/dist/io/opc/read-opc.js +153 -0
  107. package/dist/io/opc/read-options.d.ts +10 -0
  108. package/dist/io/opc/read-options.js +7 -0
  109. package/dist/io/opc/rels.d.ts +15 -0
  110. package/dist/io/opc/rels.js +28 -0
  111. package/dist/io/opc/sniff-format.d.ts +27 -0
  112. package/dist/io/opc/sniff-format.js +92 -0
  113. package/dist/io/style/xf-style.d.ts +47 -0
  114. package/dist/io/style/xf-style.js +95 -0
  115. package/dist/io/xlsb/errors.d.ts +15 -0
  116. package/dist/io/xlsb/errors.js +15 -0
  117. package/dist/io/xlsb/formula.d.ts +48 -0
  118. package/dist/io/xlsb/formula.js +398 -0
  119. package/dist/io/xlsb/primitives.d.ts +86 -0
  120. package/dist/io/xlsb/primitives.js +230 -0
  121. package/dist/io/xlsb/ptg-functions.d.ts +16 -0
  122. package/dist/io/xlsb/ptg-functions.js +659 -0
  123. package/dist/io/xlsb/read-shared-strings.d.ts +2 -0
  124. package/dist/io/xlsb/read-shared-strings.js +22 -0
  125. package/dist/io/xlsb/read-styles.d.ts +4 -0
  126. package/dist/io/xlsb/read-styles.js +386 -0
  127. package/dist/io/xlsb/read-worksheet.d.ts +9 -0
  128. package/dist/io/xlsb/read-worksheet.js +301 -0
  129. package/dist/io/xlsb/read.d.ts +20 -0
  130. package/dist/io/xlsb/read.js +199 -0
  131. package/dist/io/xlsb/record-stream.d.ts +14 -0
  132. package/dist/io/xlsb/record-stream.js +56 -0
  133. package/dist/io/xlsb/record-types.d.ts +55 -0
  134. package/dist/io/xlsb/record-types.js +73 -0
  135. package/dist/io/xlsx/cell-accumulator.d.ts +25 -0
  136. package/dist/io/xlsx/cell-accumulator.js +172 -0
  137. package/dist/io/xlsx/cell-value.d.ts +32 -0
  138. package/dist/io/xlsx/cell-value.js +85 -0
  139. package/dist/io/xlsx/comments.d.ts +74 -0
  140. package/dist/io/xlsx/comments.js +243 -0
  141. package/dist/io/xlsx/conditional-formatting.d.ts +32 -0
  142. package/dist/io/xlsx/conditional-formatting.js +464 -0
  143. package/dist/io/xlsx/data-validation.d.ts +20 -0
  144. package/dist/io/xlsx/data-validation.js +258 -0
  145. package/dist/io/xlsx/edit-vba.d.ts +23 -0
  146. package/dist/io/xlsx/edit-vba.js +139 -0
  147. package/dist/io/xlsx/errors.d.ts +17 -0
  148. package/dist/io/xlsx/errors.js +17 -0
  149. package/dist/io/xlsx/hyperlinks.d.ts +42 -0
  150. package/dist/io/xlsx/hyperlinks.js +139 -0
  151. package/dist/io/xlsx/images.d.ts +34 -0
  152. package/dist/io/xlsx/images.js +218 -0
  153. package/dist/io/xlsx/namespaces.d.ts +55 -0
  154. package/dist/io/xlsx/namespaces.js +55 -0
  155. package/dist/io/xlsx/package-plan.d.ts +103 -0
  156. package/dist/io/xlsx/package-plan.js +168 -0
  157. package/dist/io/xlsx/pivot-read.d.ts +5 -0
  158. package/dist/io/xlsx/pivot-read.js +134 -0
  159. package/dist/io/xlsx/pivot.d.ts +12 -0
  160. package/dist/io/xlsx/pivot.js +141 -0
  161. package/dist/io/xlsx/read-rows.d.ts +85 -0
  162. package/dist/io/xlsx/read-rows.js +286 -0
  163. package/dist/io/xlsx/read-styles.d.ts +8 -0
  164. package/dist/io/xlsx/read-styles.js +551 -0
  165. package/dist/io/xlsx/read-worksheet.d.ts +4 -0
  166. package/dist/io/xlsx/read-worksheet.js +528 -0
  167. package/dist/io/xlsx/read.d.ts +32 -0
  168. package/dist/io/xlsx/read.js +680 -0
  169. package/dist/io/xlsx/relationships.d.ts +33 -0
  170. package/dist/io/xlsx/relationships.js +47 -0
  171. package/dist/io/xlsx/rich-runs.d.ts +13 -0
  172. package/dist/io/xlsx/rich-runs.js +59 -0
  173. package/dist/io/xlsx/rich-text.d.ts +7 -0
  174. package/dist/io/xlsx/rich-text.js +23 -0
  175. package/dist/io/xlsx/shared-formulas.d.ts +6 -0
  176. package/dist/io/xlsx/shared-formulas.js +52 -0
  177. package/dist/io/xlsx/shared-strings-read.d.ts +2 -0
  178. package/dist/io/xlsx/shared-strings-read.js +70 -0
  179. package/dist/io/xlsx/shared-strings.d.ts +17 -0
  180. package/dist/io/xlsx/shared-strings.js +50 -0
  181. package/dist/io/xlsx/sheet-properties.d.ts +13 -0
  182. package/dist/io/xlsx/sheet-properties.js +211 -0
  183. package/dist/io/xlsx/styles.d.ts +138 -0
  184. package/dist/io/xlsx/styles.js +821 -0
  185. package/dist/io/xlsx/tables.d.ts +9 -0
  186. package/dist/io/xlsx/tables.js +208 -0
  187. package/dist/io/xlsx/threaded-comments.d.ts +102 -0
  188. package/dist/io/xlsx/threaded-comments.js +308 -0
  189. package/dist/io/xlsx/workbook-xml.d.ts +12 -0
  190. package/dist/io/xlsx/workbook-xml.js +353 -0
  191. package/dist/io/xlsx/worksheet-xml.d.ts +86 -0
  192. package/dist/io/xlsx/worksheet-xml.js +643 -0
  193. package/dist/io/xlsx/write-stream.d.ts +171 -0
  194. package/dist/io/xlsx/write-stream.js +399 -0
  195. package/dist/io/xlsx/write.d.ts +81 -0
  196. package/dist/io/xlsx/write.js +396 -0
  197. package/dist/io/xlsx/x14-ext.d.ts +9 -0
  198. package/dist/io/xlsx/x14-ext.js +12 -0
  199. package/dist/vba/cfb-writer.d.ts +19 -0
  200. package/dist/vba/cfb-writer.js +277 -0
  201. package/dist/vba/cfb.d.ts +16 -0
  202. package/dist/vba/cfb.js +324 -0
  203. package/dist/vba/codepage.d.ts +18 -0
  204. package/dist/vba/codepage.js +87 -0
  205. package/dist/vba/errors.d.ts +24 -0
  206. package/dist/vba/errors.js +24 -0
  207. package/dist/vba/index.d.ts +5 -0
  208. package/dist/vba/index.js +15 -0
  209. package/dist/vba/ms-ovba.d.ts +12 -0
  210. package/dist/vba/ms-ovba.js +176 -0
  211. package/dist/vba/project-editor.d.ts +62 -0
  212. package/dist/vba/project-editor.js +377 -0
  213. package/dist/vba/project.d.ts +45 -0
  214. package/dist/vba/project.js +160 -0
  215. package/dist/vba/vba-encoding.d.ts +14 -0
  216. package/dist/vba/vba-encoding.js +39 -0
  217. package/dist/xml/errors.d.ts +15 -0
  218. package/dist/xml/errors.js +15 -0
  219. package/dist/xml/xml-read.d.ts +107 -0
  220. package/dist/xml/xml-read.js +283 -0
  221. package/dist/xml/xml.d.ts +41 -0
  222. package/dist/xml/xml.js +86 -0
  223. package/package.json +117 -0
@@ -0,0 +1,353 @@
1
+ // Workbook-level serialisation: the package's `[Content_Types].xml`, its root and workbook `.rels`
2
+ // parts, `xl/workbook.xml` (sheets, defined names, calc/protection settings, pivot-cache and slicer
3
+ // registrations), and the `docProps` core/app property parts.
4
+ import { mangleFormula, quoteSheetName } from "../../core/formula.js";
5
+ import { WORKBOOK_PROTECTION_CREDENTIAL_ATTRS } from "../../core/workbook-protection.js";
6
+ import { escapeAttr, escapeText, XML_DECLARATION } from "../../xml/xml.js";
7
+ import { extensionOf, relativePartPath, THEME_PART_PATH } from "../opc/part-paths.js";
8
+ import { relationship, relationshipsPart } from "../opc/rels.js";
9
+ import { imageContentType } from "./images.js";
10
+ import { SLICER_CACHES_EXT_URI } from "./namespaces.js";
11
+ import { NS, REL } from "./relationships.js";
12
+ import { x14Ext } from "./x14-ext.js";
13
+ const CT = {
14
+ workbook: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml',
15
+ macroEnabledWorkbook: 'application/vnd.ms-excel.sheet.macroEnabled.main+xml',
16
+ worksheet: 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml',
17
+ styles: 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml',
18
+ theme: 'application/vnd.openxmlformats-officedocument.theme+xml',
19
+ core: 'application/vnd.openxmlformats-package.core-properties+xml',
20
+ app: 'application/vnd.openxmlformats-officedocument.extended-properties+xml',
21
+ table: 'application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml',
22
+ comments: 'application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml',
23
+ vml: 'application/vnd.openxmlformats-officedocument.vmlDrawing',
24
+ drawing: 'application/vnd.openxmlformats-officedocument.drawing+xml',
25
+ printerSettings: 'application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings',
26
+ sharedStrings: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml',
27
+ pivotTable: 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml',
28
+ pivotCacheDefinition: 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml',
29
+ pivotCacheRecords: 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml',
30
+ // Both threaded-comment parts are Microsoft extensions, hence the `vnd.ms-excel` vendor prefix rather
31
+ // than the `openxmlformats-officedocument` one every standard part carries.
32
+ threadedComments: 'application/vnd.ms-excel.threadedcomments+xml',
33
+ person: 'application/vnd.ms-excel.person+xml',
34
+ };
35
+ export function contentTypesXml(sheetCount, tables, commentNumbers, drawingNumbers, printerSettingsNumbers, mediaExtensions, hasSharedStrings, preservedParts, pivots, preservedWorkbookRefs, threadedCommentNumbers, hasPersons) {
36
+ // One extension→default-content-type map both halves read: the defaults render it, the overrides
37
+ // correct any preserved part whose own type differs from its extension's default. Sharing it is what
38
+ // keeps a `<Default>` and its `<Override>`s from ever disagreeing.
39
+ const extensionDefaults = buildExtensionDefaults(commentNumbers, printerSettingsNumbers, mediaExtensions, preservedParts);
40
+ return (XML_DECLARATION +
41
+ `<Types xmlns="${NS.contentTypes}">` +
42
+ contentTypeDefaults(extensionDefaults) +
43
+ contentTypeOverrides(sheetCount, tables, drawingNumbers, commentNumbers, hasSharedStrings, preservedParts, pivots, preservedWorkbookRefs, extensionDefaults, threadedCommentNumbers, hasPersons) +
44
+ '</Types>');
45
+ }
46
+ // The content type declared at the `<Default>` level for each file extension the package carries, in
47
+ // emission order: the built-in rels/xml pair, the vml/bin/image kinds the writer itself generates,
48
+ // then one default per *new* extension a preserved binary part introduces (first part of an extension
49
+ // wins). Keys are lower-cased for case-insensitive extension matching; each value keeps the extension
50
+ // token as it will be emitted so rendering stays byte-stable. A preserved `.xml` part is deliberately
51
+ // left out — it always carries its own type as a per-part override, never a generic `xml` default.
52
+ function buildExtensionDefaults(commentNumbers, printerSettingsNumbers, mediaExtensions, preservedParts) {
53
+ const defaults = new Map();
54
+ const add = (extension, contentType) => {
55
+ const key = extension.toLowerCase();
56
+ if (!defaults.has(key))
57
+ defaults.set(key, { extension, contentType });
58
+ };
59
+ add('rels', 'application/vnd.openxmlformats-package.relationships+xml');
60
+ add('xml', 'application/xml');
61
+ if (commentNumbers.length > 0)
62
+ add('vml', CT.vml);
63
+ if (printerSettingsNumbers.length > 0)
64
+ add('bin', CT.printerSettings);
65
+ for (const ext of mediaExtensions)
66
+ add(ext, imageContentType(ext));
67
+ for (const part of preservedParts) {
68
+ const ext = extensionOf(part.path);
69
+ if (ext.toLowerCase() === 'xml')
70
+ continue;
71
+ add(ext, part.contentType);
72
+ }
73
+ return defaults;
74
+ }
75
+ // A macro-enabled workbook's own VBA project is round-tripped as a preserved workbook reference
76
+ // (see `isPreservedWorkbookRelType` in read.ts), so its presence is what distinguishes a re-emitted
77
+ // .xlsm from a plain .xlsx: `xl/workbook.xml` must declare the macro-enabled content type or Excel
78
+ // flags the package as needing repair on open.
79
+ function isMacroEnabled(preservedWorkbookRefs) {
80
+ return preservedWorkbookRefs.some((ref) => ref.relType.endsWith('/vbaProject'));
81
+ }
82
+ // The extension-level `<Default>` declarations, rendered from the shared extension-default map (see
83
+ // {@link buildExtensionDefaults}) in insertion order. The raw bytes of a VML, a printer-settings blob,
84
+ // or an image carry no XML content type of their own, so each is declared once per extension rather
85
+ // than per part; a preserved binary part whose type differs from its extension's default is corrected
86
+ // by a per-part `<Override>` in {@link contentTypeOverrides}.
87
+ function contentTypeDefaults(extensionDefaults) {
88
+ return [...extensionDefaults.values()]
89
+ .map(({ extension, contentType }) => defaultType(extension, contentType))
90
+ .join('');
91
+ }
92
+ // The per-part `<Override>` declarations, in canonical package order: workbook, worksheets, tables,
93
+ // drawings, comments, the threaded-comment parts, each pivot's three parts, theme, styles, the optional
94
+ // shared strings and threaded-comment person registry, the doc-props pair, then any preserved part whose
95
+ // content type its extension's `<Default>` does not already carry — every `.xml` part (the generic `xml`
96
+ // default never matches a real part type) and any binary part sharing an extension with a differently-typed
97
+ // sibling (a `vbaProjectSignature.bin` next to a `vbaProject.bin`), which a lone extension default would
98
+ // otherwise mis-type.
99
+ function contentTypeOverrides(sheetCount, tables, drawingNumbers, commentNumbers, hasSharedStrings, preservedParts, pivots, preservedWorkbookRefs, extensionDefaults, threadedCommentNumbers, hasPersons) {
100
+ const preservedOverrides = preservedParts
101
+ // A preserved theme lands at the fixed theme path, whose override is already in the list below —
102
+ // and OPC forbids declaring the same PartName twice. The fixed declaration is the right one to
103
+ // keep: the part reached the model through a `.../theme` relationship, so its type is the theme
104
+ // type whatever the source package happened to declare.
105
+ .filter((part) => part.path !== THEME_PART_PATH)
106
+ .filter((part) => extensionDefaults.get(extensionOf(part.path).toLowerCase())?.contentType !==
107
+ part.contentType)
108
+ .map((part) => override(`/${part.path}`, part.contentType));
109
+ return [
110
+ override('/xl/workbook.xml', isMacroEnabled(preservedWorkbookRefs) ? CT.macroEnabledWorkbook : CT.workbook),
111
+ ...Array.from({ length: sheetCount }, (_, i) => override(`/xl/worksheets/sheet${i + 1}.xml`, CT.worksheet)),
112
+ ...tables.map(({ number }) => override(`/xl/tables/table${number}.xml`, CT.table)),
113
+ ...drawingNumbers.map((number) => override(`/xl/drawings/drawing${number}.xml`, CT.drawing)),
114
+ ...commentNumbers.map((number) => override(`/xl/comments${number}.xml`, CT.comments)),
115
+ ...threadedCommentNumbers.map((number) => override(`/xl/threadedComments/threadedComment${number}.xml`, CT.threadedComments)),
116
+ ...pivots.map(({ number }) => override(`/xl/pivotTables/pivotTable${number}.xml`, CT.pivotTable)),
117
+ ...pivots.map(({ number }) => override(`/xl/pivotCache/pivotCacheDefinition${number}.xml`, CT.pivotCacheDefinition)),
118
+ ...pivots.map(({ number }) => override(`/xl/pivotCache/pivotCacheRecords${number}.xml`, CT.pivotCacheRecords)),
119
+ override(`/${THEME_PART_PATH}`, CT.theme),
120
+ override('/xl/styles.xml', CT.styles),
121
+ ...(hasSharedStrings ? [override('/xl/sharedStrings.xml', CT.sharedStrings)] : []),
122
+ ...(hasPersons ? [override('/xl/persons/person.xml', CT.person)] : []),
123
+ override('/docProps/core.xml', CT.core),
124
+ override('/docProps/app.xml', CT.app),
125
+ ...preservedOverrides,
126
+ ].join('');
127
+ }
128
+ function override(partName, contentType) {
129
+ return `<Override PartName="${partName}" ContentType="${contentType}"/>`;
130
+ }
131
+ // A `<Default>` content-type declaration binding a file extension to the type every part with that
132
+ // extension carries — the extension-level counterpart to {@link override}'s per-part declaration.
133
+ function defaultType(extension, contentType) {
134
+ return `<Default Extension="${extension}" ContentType="${contentType}"/>`;
135
+ }
136
+ // The package root relationships: the three the writer regenerates from the model (the office
137
+ // document and the core/app properties), followed by any preserved root references — customUI ribbon
138
+ // parts, custom properties, a thumbnail — re-declared with fresh ids past the fixed three so a
139
+ // round-trip keeps content wired from `_rels/.rels` that the model does not otherwise emit.
140
+ export function rootRelsXml(rootRefs) {
141
+ return relationshipsPart([
142
+ relationship('rId1', REL.officeDocument, 'xl/workbook.xml'),
143
+ relationship('rId2', REL.coreProps, 'docProps/core.xml'),
144
+ relationship('rId3', REL.extProps, 'docProps/app.xml'),
145
+ ...rootRefs.map((ref, i) => relationship(`rId${4 + i}`, ref.relType, escapeAttr(ref.entryPath))),
146
+ ]);
147
+ }
148
+ export function workbookXml(workbook, preservedRels, pivots) {
149
+ const sheets = workbook.worksheets;
150
+ const entries = sheets
151
+ .map((sheet, i) => {
152
+ const state = sheet.state === 'visible' ? '' : ` state="${sheet.state}"`;
153
+ return `<sheet name="${escapeAttr(sheet.name)}" sheetId="${sheet.id}"${state} r:id="rId${i + 1}"/>`;
154
+ })
155
+ .join('');
156
+ return (XML_DECLARATION +
157
+ `<workbook xmlns="${NS.main}" xmlns:r="${NS.docRels}">` +
158
+ workbookProtectionXml(workbook) +
159
+ bookViewsXml(workbook) +
160
+ `<sheets>${entries}</sheets>` +
161
+ externalReferencesXml(preservedRels) +
162
+ definedNamesXml(workbook) +
163
+ calcPrXml(workbook) +
164
+ pivotCachesXml(preservedRels, pivots) +
165
+ workbookExtLstXml(preservedRels) +
166
+ '</workbook>');
167
+ }
168
+ // `<bookViews>` follows `<workbookProtection>` and precedes `<sheets>` in CT_Workbook order. Unlike
169
+ // every other optional block here it is emitted unconditionally: a consumer restores the document
170
+ // window from this rect and lays the sheet's panes out inside it, so a package without one leaves a
171
+ // frozen split measured against an uninitialised window (see `DEFAULT_WORKBOOK_VIEW`). The
172
+ // visibility and minimised flags are written only when they differ from the schema default, and
173
+ // `activeTab` only for a non-first sheet — exactly the shape Excel writes for an ordinary window.
174
+ function bookViewsXml(workbook) {
175
+ const view = workbook.view;
176
+ const activeTab = workbook.activeTabIndex;
177
+ const attrs = (view.visibility !== undefined && view.visibility !== 'visible'
178
+ ? ` visibility="${view.visibility}"`
179
+ : '') +
180
+ (view.minimized ? ' minimized="1"' : '') +
181
+ ` xWindow="${view.x}" yWindow="${view.y}" windowWidth="${view.width}" windowHeight="${view.height}"` +
182
+ (activeTab === 0 ? '' : ` activeTab="${activeTab}"`);
183
+ return `<bookViews><workbookView${attrs}/></bookViews>`;
184
+ }
185
+ // The `<externalReferences>` element registers each preserved external link (a link to a source
186
+ // workbook), wired to the relationship reaching its `externalLink` part. It follows `<sheets>` in
187
+ // CT_Workbook order and precedes `<definedNames>`; its child order is the `[n]` index a formula or
188
+ // defined name resolves an external cell through, so entries are emitted by their captured
189
+ // `externalReferenceIndex` — keeping every `[1]`, `[2]`, … pointing at the same linked workbook it did
190
+ // before the round-trip. '' when no external link was preserved.
191
+ function externalReferencesXml(preservedRels) {
192
+ const links = preservedRels
193
+ .filter((ref) => ref.externalReferenceIndex !== undefined)
194
+ .sort((a, b) => a.externalReferenceIndex - b.externalReferenceIndex);
195
+ if (links.length === 0)
196
+ return '';
197
+ const entries = links.map((ref) => `<externalReference r:id="${ref.relId}"/>`).join('');
198
+ return `<externalReferences>${entries}</externalReferences>`;
199
+ }
200
+ // The workbook-body `<x14:slicerCaches>` extension that registers each preserved slicer cache, wired
201
+ // to the relationship reaching its cache part. Slicer caches (unlike pivot caches, which register in
202
+ // `<pivotCaches>`) live only in this extension block, so re-emitting it is what lets Excel rediscover
203
+ // the slicers. `<extLst>` is the final child of CT_Workbook. '' when no slicer cache was preserved.
204
+ function workbookExtLstXml(preservedRels) {
205
+ const caches = preservedRels.filter((ref) => ref.relType.endsWith('/slicerCache'));
206
+ if (caches.length === 0)
207
+ return '';
208
+ const entries = caches.map((ref) => `<x14:slicerCache r:id="${ref.relId}"/>`).join('');
209
+ return `<extLst>${x14Ext(SLICER_CACHES_EXT_URI, `<x14:slicerCaches>${entries}</x14:slicerCaches>`)}</extLst>`;
210
+ }
211
+ // The `<pivotCaches>` element registers each pivot cache under the `cacheId` a pivot table resolves
212
+ // its cache through, wired to the relationship that reaches the cache definition. It follows
213
+ // `<calcPr>` in CT_Workbook order and carries both preserved caches (passed through from a read file)
214
+ // and caches the writer generated for modeled pivot tables. A slicer cache (no `cacheId`) is
215
+ // registered in a workbook extension block, not here, so it is skipped.
216
+ function pivotCachesXml(preservedRels, pivots) {
217
+ const preserved = preservedRels
218
+ .filter((ref) => ref.pivotCacheId !== undefined)
219
+ .map((ref) => `<pivotCache cacheId="${escapeAttr(ref.pivotCacheId)}" r:id="${ref.relId}"/>`);
220
+ const generated = pivots.map((pivot) => `<pivotCache cacheId="${escapeAttr(pivot.cacheId)}" r:id="${pivot.workbookRelId}"/>`);
221
+ const entries = [...preserved, ...generated];
222
+ if (entries.length === 0)
223
+ return '';
224
+ return `<pivotCaches>${entries.join('')}</pivotCaches>`;
225
+ }
226
+ // `<workbookProtection>` precedes `<sheets>` in CT_Workbook order. It re-emits the workbook's
227
+ // structure/window lock flags (each written only when true, so an unlocked aspect stays absent) and
228
+ // the preserved password/agile-hash credential attributes verbatim. Emitted only when the workbook
229
+ // actually declares protection — the flags or a credential — so an unprotected workbook stays clean.
230
+ function workbookProtectionXml(workbook) {
231
+ const p = workbook.protection;
232
+ if (p === undefined)
233
+ return '';
234
+ const attrs = [];
235
+ if (p.lockStructure)
236
+ attrs.push('lockStructure="1"');
237
+ if (p.lockWindows)
238
+ attrs.push('lockWindows="1"');
239
+ if (p.lockRevision)
240
+ attrs.push('lockRevision="1"');
241
+ for (const key of WORKBOOK_PROTECTION_CREDENTIAL_ATTRS) {
242
+ const value = p.credentials?.[key];
243
+ if (value !== undefined)
244
+ attrs.push(`${key}="${escapeAttr(value)}"`);
245
+ }
246
+ if (attrs.length === 0)
247
+ return '';
248
+ return `<workbookProtection ${attrs.join(' ')}/>`;
249
+ }
250
+ // The `calcId` desktop Excel stamps into `<calcPr>` — the build number of the calc engine that last
251
+ // evaluated the workbook. Consumers only compare it to decide whether cached results are stale; we
252
+ // emit a fixed recent value alongside `fullCalcOnLoad`, which already forces a full recalculation.
253
+ const EXCEL_CALC_ID = '171027';
254
+ // `<calcPr>` follows `<definedNames>` in CT_Workbook order and carries the calculation settings.
255
+ // Today the model exposes a single one: `fullCalcOnLoad`, which tells the consumer to recalculate
256
+ // every formula on open instead of trusting the cached results. Emitted only when set, so an
257
+ // unmarked workbook keeps the element (and its `calcId`) out of the file entirely.
258
+ function calcPrXml(workbook) {
259
+ return workbook.fullCalcOnLoad ? `<calcPr calcId="${EXCEL_CALC_ID}" fullCalcOnLoad="1"/>` : '';
260
+ }
261
+ // The `<definedNames>` block follows `<sheets>` in the schema. A sheet-scoped name carries a
262
+ // `localSheetId` — the 0-based position of its sheet among the `<sheet>` entries, NOT the sheet's
263
+ // own id — so the index is resolved against the worksheet order here. The refersTo formula is the
264
+ // element's text content, run through the same `_xlfn.` function mangling the writer applies to a
265
+ // cell formula so a name defined as a modern function (a LAMBDA, an XLOOKUP-based name) is stored
266
+ // under the prefix Excel requires; a plain reference has no function call and passes through
267
+ // untouched. Only names that are actually set emit anything.
268
+ function definedNamesXml(workbook) {
269
+ const sheets = workbook.worksheets;
270
+ const userEntries = workbook.definedNames.map((name) => {
271
+ const scopeAttr = name.scope === undefined
272
+ ? ''
273
+ : ` localSheetId="${sheets.findIndex((sheet) => sheet.name === name.scope)}"`;
274
+ const commentAttr = name.comment === undefined ? '' : ` comment="${escapeAttr(name.comment)}"`;
275
+ const hiddenAttr = name.hidden ? ' hidden="1"' : '';
276
+ return (`<definedName name="${escapeAttr(name.name)}"${scopeAttr}${commentAttr}${hiddenAttr}>` +
277
+ `${escapeText(mangleFormula(name.refersTo))}</definedName>`);
278
+ });
279
+ // Every sheet-level autofilter contributes the hidden, sheet-scoped `_FilterDatabase` built-in that
280
+ // Excel derives from its range. The reader drops these on load and rebuilds them from the sheet's
281
+ // `<autoFilter>`, so `Worksheet.autoFilter` stays the single source of truth and a round-trip never
282
+ // duplicates them.
283
+ const filterEntries = sheets.flatMap((sheet, index) => sheet.autoFilter === undefined
284
+ ? []
285
+ : [
286
+ `<definedName name="_xlnm._FilterDatabase" localSheetId="${index}" hidden="1">` +
287
+ `${escapeText(filterDatabaseRefersTo(sheet.name, sheet.autoFilter.ref))}</definedName>`,
288
+ ]);
289
+ const entries = [...userEntries, ...filterEntries];
290
+ if (entries.length === 0)
291
+ return '';
292
+ return `<definedNames>${entries.join('')}</definedNames>`;
293
+ }
294
+ // Build the sheet-qualified, fully-absolute reference a `_FilterDatabase` name carries
295
+ // (`'Sheet 1'!$A$1:$C$10`) from a sheet name and its already-canonical `A1:C10` autofilter range.
296
+ function filterDatabaseRefersTo(sheetName, range) {
297
+ const absolute = range.replace(/([A-Z]+)(\d+)/g, '$$$1$$$2');
298
+ return `${quoteSheetName(sheetName)}!${absolute}`;
299
+ }
300
+ // The relationships the workbook part always carries after its per-sheet rels: `styles.xml` and
301
+ // `theme/theme1.xml`. Their count anchors every downstream rel id — `sharedStrings.xml` and the
302
+ // threaded-comment person registry (when present), then the preserved/pivot caches — so `write.ts`
303
+ // derives its `workbookRelBase` from this same constant rather than repeating the literal and risking
304
+ // drift.
305
+ export const FIXED_WORKBOOK_REL_COUNT = 2;
306
+ export function workbookRelsXml(sheetCount, hasSharedStrings, personsRelId, preservedRels, pivots) {
307
+ return relationshipsPart([
308
+ ...Array.from({ length: sheetCount }, (_, i) => relationship(`rId${i + 1}`, REL.worksheet, `worksheets/sheet${i + 1}.xml`)),
309
+ relationship(`rId${sheetCount + 1}`, REL.styles, 'styles.xml'),
310
+ relationship(`rId${sheetCount + FIXED_WORKBOOK_REL_COUNT}`, REL.theme, relativePartPath('xl/workbook.xml', THEME_PART_PATH)),
311
+ ...(hasSharedStrings
312
+ ? [
313
+ relationship(`rId${sheetCount + FIXED_WORKBOOK_REL_COUNT + 1}`, REL.sharedStrings, 'sharedStrings.xml'),
314
+ ]
315
+ : []),
316
+ // The threaded-comment identity registry every conversation on every sheet resolves its authors and
317
+ // @mentions through. Workbook-level and singular, so this one relationship serves all the sheets.
318
+ ...(personsRelId === null
319
+ ? []
320
+ : [relationship(personsRelId, REL.person, 'persons/person.xml')]),
321
+ // A preserved cache's target is package-absolute; express it relative to the workbook part.
322
+ ...preservedRels.map((ref) => relationship(ref.relId, ref.relType, escapeAttr(relativePartPath('xl/workbook.xml', ref.entryPath)))),
323
+ // A generated pivot cache's workbook relationship reaches its cache definition part.
324
+ ...pivots.map((pivot) => relationship(pivot.workbookRelId, REL.pivotCacheDefinition, `pivotCache/pivotCacheDefinition${pivot.number}.xml`)),
325
+ ]);
326
+ }
327
+ export function corePropsXml(properties) {
328
+ const parts = [];
329
+ if (properties.creator !== undefined) {
330
+ parts.push(`<dc:creator>${escapeText(properties.creator)}</dc:creator>`);
331
+ }
332
+ if (properties.lastModifiedBy !== undefined) {
333
+ parts.push(`<cp:lastModifiedBy>${escapeText(properties.lastModifiedBy)}</cp:lastModifiedBy>`);
334
+ }
335
+ if (properties.created) {
336
+ parts.push(w3cdtf('created', properties.created));
337
+ }
338
+ if (properties.modified) {
339
+ parts.push(w3cdtf('modified', properties.modified));
340
+ }
341
+ return (XML_DECLARATION +
342
+ `<cp:coreProperties xmlns:cp="${NS.coreProps}" xmlns:dc="${NS.dc}" xmlns:dcterms="${NS.dcterms}" ` +
343
+ `xmlns:dcmitype="${NS.dcmitype}" xmlns:xsi="${NS.xsi}">` +
344
+ parts.join('') +
345
+ '</cp:coreProperties>');
346
+ }
347
+ function w3cdtf(element, date) {
348
+ return `<dcterms:${element} xsi:type="dcterms:W3CDTF">${date.toISOString()}</dcterms:${element}>`;
349
+ }
350
+ export function appPropsXml() {
351
+ return (XML_DECLARATION +
352
+ `<Properties xmlns="${NS.extProps}"><Application>ts-xlsx</Application></Properties>`);
353
+ }
@@ -0,0 +1,86 @@
1
+ import type { Cell } from '../../core/cell.ts';
2
+ import type { ColumnProperties, RowProperties, Worksheet } from '../../core/worksheet.ts';
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';
6
+ import type { SharedStringTable } from './shared-strings.ts';
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 — the sheet's dimension is then 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
+ export interface SheetReferences {
45
+ readonly drawingRelId: string | null;
46
+ readonly legacyDrawingRelId: string | null;
47
+ readonly printerSettingsRelId: string | null;
48
+ readonly backgroundRelId: string | null;
49
+ readonly legacyDrawingHFRelId: string | null;
50
+ readonly slicerRelIds: readonly string[];
51
+ }
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
+ /**
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.
58
+ */
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>;
66
+ readonly styles: StyleRegistry;
67
+ readonly sharedStrings: SharedStringTable | null;
68
+ readonly sharedRoles: ReadonlyMap<string, SharedFormulaRole>;
69
+ readonly collapsedSummaries: ReadonlySet<number>;
70
+ }
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;