xlsx-template-browser 0.2.1 → 0.3.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.
package/dist/index.js ADDED
@@ -0,0 +1,2249 @@
1
+ // src/functions/file-helpers.ts
2
+ var fetchFile = async (url) => {
3
+ const response = await fetch(url);
4
+ if (!response.ok) {
5
+ throw new Error(
6
+ `Failed to download file: ${response.status} ${response.statusText}`
7
+ );
8
+ }
9
+ return await response.arrayBuffer();
10
+ };
11
+ var downloadURL = (data, fileName) => {
12
+ const a = document.createElement("a");
13
+ a.href = data;
14
+ a.download = fileName;
15
+ a.style = "display: none";
16
+ document.body.append(a);
17
+ a.click();
18
+ a.remove();
19
+ };
20
+ var downloadFile = (data, fileName, mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") => {
21
+ const blob = new Blob([data], { type: mimeType });
22
+ const url = window.URL.createObjectURL(blob);
23
+ downloadURL(url, fileName);
24
+ setTimeout(() => {
25
+ return window.URL.revokeObjectURL(url);
26
+ }, 100);
27
+ };
28
+
29
+ // src/functions/data-helpers.ts
30
+ var isNumeric = (v) => {
31
+ return /^\d+$/.test(v);
32
+ };
33
+ var parseNotation = (input) => {
34
+ const result = [];
35
+ let i = 0;
36
+ while (i < input.length) {
37
+ const char = input[i];
38
+ if (char === ".") {
39
+ i++;
40
+ continue;
41
+ }
42
+ if (char === "[") {
43
+ i++;
44
+ const quote = input[i] === '"' || input[i] === "'" ? input[i++] : null;
45
+ let value2 = "";
46
+ while (i < input.length) {
47
+ if (quote !== null) {
48
+ if (input[i] === quote && input[i + 1] === "]") break;
49
+ } else {
50
+ if (input[i] === "]") break;
51
+ }
52
+ value2 += input[i++];
53
+ }
54
+ i += quote !== null ? 2 : 1;
55
+ result.push(isNumeric(value2) ? Number(value2) : value2);
56
+ continue;
57
+ }
58
+ let value = "";
59
+ while (i < input.length) {
60
+ const c = input[i];
61
+ if (c === "." || c === "[") break;
62
+ value += c;
63
+ i++;
64
+ }
65
+ result.push(value);
66
+ }
67
+ return result;
68
+ };
69
+ var getDeep = (obj, accessors) => {
70
+ for (let i = 0; i < accessors.length; i++) {
71
+ if (obj === void 0) {
72
+ return void 0;
73
+ }
74
+ if (obj === null) {
75
+ return obj;
76
+ }
77
+ if (Array.isArray(obj) && typeof accessors[i] === "string") {
78
+ return obj.map((el) => getDeep(el, accessors.slice(i)));
79
+ }
80
+ const accessor = accessors[i];
81
+ if (accessor === void 0) {
82
+ continue;
83
+ }
84
+ obj = obj[accessor];
85
+ }
86
+ return obj;
87
+ };
88
+ var getByNotation = (obj, props) => {
89
+ const parsed = parseNotation(props);
90
+ const value = getDeep(obj, parsed);
91
+ return value ?? "";
92
+ };
93
+ var createDataStore = (data) => {
94
+ const accessors = /* @__PURE__ */ new Map();
95
+ const get = (accessor) => {
96
+ accessor = accessor.trim();
97
+ if (accessors.has(accessor)) {
98
+ return accessors.get(accessor);
99
+ }
100
+ const value = getByNotation(data, accessor);
101
+ accessors.set(accessor, value);
102
+ return value;
103
+ };
104
+ return { get };
105
+ };
106
+ var isElement = (value) => {
107
+ return value instanceof Element || value instanceof Node && value.nodeType === Node.ELEMENT_NODE;
108
+ };
109
+
110
+ // src/functions/constants.ts
111
+ var NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
112
+ var NS_PREFIX = "http://schemas.openxmlformats.org/officeDocument/2006";
113
+ var NS_TC = "http://schemas.microsoft.com/office/spreadsheetml/2018/threadedcomments";
114
+ var GLOBAL_RELS = "_rels/.rels";
115
+ var OFFICE_DOCUMENT_TYPE = `${NS_PREFIX}/relationships`;
116
+ var NS_REL = "http://schemas.openxmlformats.org/package/2006/relationships";
117
+ var TAGS_ORDER = [
118
+ "sheetData",
119
+ "mergeCells",
120
+ "conditionalFormatting",
121
+ "dataValidations",
122
+ "pageMargins",
123
+ "tableParts",
124
+ "extLst",
125
+ "legacyDrawing"
126
+ ];
127
+
128
+ // src/functions/excel-helpers.ts
129
+ var toCellRef = (ref) => {
130
+ let col = ref.column;
131
+ if (col <= 0) throw new Error(`Invalid column: ${col}`);
132
+ let column = "";
133
+ while (col > 0) {
134
+ const rem = (col - 1) % 26;
135
+ column = String.fromCharCode(65 + rem) + column;
136
+ col = Math.floor((col - 1) / 26);
137
+ }
138
+ const colPart = `${ref.columnAbsolute === true ? "$" : ""}${column}`;
139
+ const rowPart = `${ref.rowAbsolute === true ? "$" : ""}${ref.row}`;
140
+ if (ref.row <= 0) throw new Error(`Invalid row: ${ref.row}`);
141
+ return colPart + rowPart;
142
+ };
143
+ var parseCellRef = (ref) => {
144
+ const normalized = ref.trim().toUpperCase();
145
+ let column = 0;
146
+ let i = 0;
147
+ let columnAbsolute = false;
148
+ let rowAbsolute = false;
149
+ if (normalized[i] === "$") {
150
+ columnAbsolute = true;
151
+ i++;
152
+ }
153
+ for (; i < normalized.length; i++) {
154
+ const code = normalized.charCodeAt(i);
155
+ if (code < 65 || code > 90) break;
156
+ column = column * 26 + (code - 64);
157
+ }
158
+ if (normalized[i] === "$") {
159
+ rowAbsolute = true;
160
+ i++;
161
+ }
162
+ const rowStr = normalized.slice(i);
163
+ const row = Number(rowStr);
164
+ if (column === 0 || rowStr === "" || Number.isNaN(row)) {
165
+ throw new Error(`Invalid cell reference: ${ref}`);
166
+ }
167
+ return { row, column, rowAbsolute, columnAbsolute };
168
+ };
169
+ var parseCellRange = (ref) => {
170
+ const parts = ref.split(":");
171
+ if (parts.length === 0 || parts.length > 2) {
172
+ throw new Error(`Invalid cell range: ${ref}`);
173
+ }
174
+ if (parts.length === 1) parts.push(parts[0]);
175
+ const [start, end] = parts;
176
+ if (start === void 0 || end === void 0) throw new Error(`Cell ${ref} is broken`);
177
+ const startRef = parseCellRef(start);
178
+ const endRef = parseCellRef(end);
179
+ return {
180
+ rowStart: startRef.row,
181
+ columnStart: startRef.column,
182
+ rowAbsoluteStart: startRef.rowAbsolute,
183
+ columnAbsoluteStart: startRef.columnAbsolute,
184
+ rowEnd: endRef.row,
185
+ columnEnd: endRef.column,
186
+ rowAbsoluteEnd: endRef.rowAbsolute,
187
+ columnAbsoluteEnd: endRef.columnAbsolute
188
+ };
189
+ };
190
+ var toCellRange = (range) => {
191
+ if (range.rowStart === void 0 && range.rowEnd === void 0) {
192
+ if (range.columnStart === void 0 || range.columnEnd === void 0) {
193
+ throw new Error("Range is not defined");
194
+ }
195
+ return [
196
+ range.columnAbsoluteStart === true ? "$" : "",
197
+ String(range.columnStart),
198
+ ":",
199
+ range.columnAbsoluteEnd === true ? "$" : "",
200
+ String(range.columnEnd)
201
+ ].join("");
202
+ }
203
+ if (range.columnStart === void 0 && range.columnEnd === void 0) {
204
+ if (range.rowStart === void 0 || range.rowEnd === void 0) {
205
+ throw new Error("Range is not defined");
206
+ }
207
+ return [
208
+ range.rowAbsoluteStart === true ? "$" : "",
209
+ getExcelColumnName(range.rowStart),
210
+ ":",
211
+ range.rowAbsoluteEnd === true ? "$" : "",
212
+ getExcelColumnName(range.rowEnd)
213
+ ].join("");
214
+ }
215
+ if (range.rowStart === void 0 || range.columnStart === void 0 || range.rowEnd === void 0 || range.columnEnd === void 0) {
216
+ throw new Error("Range is not defined");
217
+ }
218
+ const start = toCellRef({
219
+ row: range.rowStart,
220
+ column: range.columnStart,
221
+ rowAbsolute: range.rowAbsoluteStart,
222
+ columnAbsolute: range.columnAbsoluteStart
223
+ });
224
+ if (range.rowEnd === 0 && range.columnEnd === 0) return start;
225
+ const end = toCellRef({
226
+ row: range.rowEnd,
227
+ column: range.columnEnd,
228
+ rowAbsolute: range.rowAbsoluteEnd,
229
+ columnAbsolute: range.columnAbsoluteEnd
230
+ });
231
+ return `${start}:${end}`;
232
+ };
233
+ var getExcelColumnName = (index) => {
234
+ let result = "";
235
+ while (index > 0) {
236
+ index--;
237
+ result = String.fromCharCode(65 + index % 26) + result;
238
+ index = Math.floor(index / 26);
239
+ }
240
+ return result;
241
+ };
242
+ var dateToExcel = (value) => {
243
+ const date = new Date(value);
244
+ const dateValue = 25569 + (date.getTime() - date.getTimezoneOffset() * 60 * 1e3) / (1e3 * 60 * 60 * 24);
245
+ return String(dateValue);
246
+ };
247
+ var convertValueToExcel = ({ value, cellType }) => {
248
+ if (cellType === null) {
249
+ return null;
250
+ }
251
+ switch (cellType) {
252
+ case "formula": {
253
+ if (typeof value === "object") {
254
+ value = String(value);
255
+ }
256
+ if (typeof value === "string") {
257
+ return value.startsWith("=") ? value.slice(1) : value;
258
+ }
259
+ break;
260
+ }
261
+ case "date": {
262
+ if (value instanceof Date || typeof value === "string") {
263
+ return dateToExcel(value);
264
+ }
265
+ if (typeof value === "object") {
266
+ return dateToExcel(String(value));
267
+ }
268
+ break;
269
+ }
270
+ case "boolean": {
271
+ if (typeof value === "boolean") {
272
+ return String(Number(value));
273
+ }
274
+ if (typeof value === "object") {
275
+ return String(Number(/true/i.test(String(value))));
276
+ }
277
+ break;
278
+ }
279
+ case "number": {
280
+ if (typeof value === "object") {
281
+ return String(value);
282
+ }
283
+ if (typeof value === "number") {
284
+ return value.toString();
285
+ }
286
+ break;
287
+ }
288
+ default: {
289
+ if (typeof value === "object") {
290
+ return JSON.stringify(value);
291
+ }
292
+ }
293
+ }
294
+ return String(value);
295
+ };
296
+ var urlPatten = String.raw`^(?!mailto:)(?:(?:http|https|ftp)://)(?:\S+(?::\S*)?@)?(?:(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))|localhost)(?::\d{2,5})?(?:(/|\?|#)[^\s]*)?$`;
297
+ var urlRegex = new RegExp(urlPatten, "i");
298
+ var guessDataType = (value) => {
299
+ if (value === void 0 || value === null || value === "") {
300
+ return null;
301
+ }
302
+ if (typeof value === "number") {
303
+ return "number";
304
+ }
305
+ if (typeof value === "boolean") {
306
+ return "boolean";
307
+ }
308
+ if (value instanceof Date) {
309
+ return isNaN(value.getTime()) ? null : "date";
310
+ }
311
+ if (typeof value === "string") {
312
+ if (value.startsWith("=")) return "formula";
313
+ if (urlRegex.test(value)) return "url";
314
+ return "string";
315
+ }
316
+ if (typeof value === "object") {
317
+ if ("$cellType" in value) {
318
+ return value.$cellType;
319
+ }
320
+ return "string";
321
+ }
322
+ return null;
323
+ };
324
+ var getRelationsPath = (partPath) => {
325
+ const slash = partPath.lastIndexOf("/");
326
+ const file = partPath.slice(slash + 1);
327
+ return `_rels/${file}.rels`;
328
+ };
329
+ var isInRange = (c, r) => c.column >= r.columnStart && c.column <= r.columnEnd && c.row >= r.rowStart && c.row <= r.rowEnd;
330
+
331
+ // src/functions/get-offsets.ts
332
+ function getOffsets({
333
+ cells,
334
+ mergedCells,
335
+ tables
336
+ }) {
337
+ let rowOffset = 0;
338
+ let initialRowOffset = 0;
339
+ let columnOffset = 0;
340
+ let currentRow = -1;
341
+ const offsets = {
342
+ row: {},
343
+ col: {}
344
+ };
345
+ for (const cell of cells) {
346
+ if (cell.inTable !== void 0) {
347
+ const table = tables.get(cell.inTable);
348
+ if (table === void 0) continue;
349
+ if (cell.column === table.range.columnEnd && cell.row === table.range.rowStart) {
350
+ const tableHeight = table.range.rowEnd - table.range.rowStart + 1;
351
+ for (let i = 0; i < tableHeight; i++) {
352
+ const row = cell.row + i;
353
+ offsets.col[row] ??= {};
354
+ offsets.col[row][table.range.columnEnd] = table.extension.cols;
355
+ }
356
+ table.finalize();
357
+ continue;
358
+ }
359
+ }
360
+ if (!Array.isArray(cell.newValue)) continue;
361
+ if (currentRow !== cell.row) {
362
+ initialRowOffset = rowOffset;
363
+ currentRow = cell.row;
364
+ columnOffset = 0;
365
+ }
366
+ if (offsets.col[currentRow] !== void 0) {
367
+ for (const column of Object.keys(offsets.col[currentRow])) {
368
+ const index = parseInt(column);
369
+ if (index < cell.column) columnOffset = columnOffset = Math.max(columnOffset, offsets.col[currentRow][index]);
370
+ }
371
+ }
372
+ let unit = 1;
373
+ let rows = 1;
374
+ if (cell.inMergedCell !== void 0) {
375
+ const merge = mergedCells.get(cell.inMergedCell);
376
+ if (merge !== void 0) {
377
+ unit = "$isTable" in cell.newValue ? merge.height : merge.width;
378
+ rows = merge.height;
379
+ }
380
+ }
381
+ if ("$isTable" in cell.newValue) {
382
+ const existingOffset = offsets.row[cell.row] ?? 0;
383
+ const newOffset = initialRowOffset + cell.newValue.length * unit - unit;
384
+ rowOffset = Math.max(newOffset, existingOffset ?? 0);
385
+ offsets.row[cell.row] = rowOffset;
386
+ continue;
387
+ }
388
+ const delta = cell.newValue.length * unit - unit;
389
+ columnOffset = columnOffset + delta;
390
+ for (let i = 0; i < rows; i++) {
391
+ const row = cell.row + i;
392
+ offsets.col[row] ??= {};
393
+ offsets.col[row][cell.column] = columnOffset;
394
+ }
395
+ }
396
+ return offsets;
397
+ }
398
+ var getRowShift = (cell, offsets) => {
399
+ let shift = 0;
400
+ for (const r in offsets.row) {
401
+ const ri = Number(r);
402
+ if (ri < cell.row) shift = offsets.row[ri];
403
+ }
404
+ return shift;
405
+ };
406
+ var getColumnShift = (cell, offsets) => {
407
+ const rowOffsets = offsets.col;
408
+ const colOffsets = rowOffsets[cell.row];
409
+ if (colOffsets === void 0) return 0;
410
+ let shift = 0;
411
+ for (const c in colOffsets) {
412
+ const ci = Number(c);
413
+ if (ci < cell.column) shift = colOffsets[ci];
414
+ }
415
+ return shift;
416
+ };
417
+
418
+ // src/functions/xml-helpers.ts
419
+ import JSZip from "jszip";
420
+ var readXlsx = async (buffer) => {
421
+ const zip = new JSZip();
422
+ const content = await zip.loadAsync(buffer);
423
+ return content;
424
+ };
425
+ var createXml = ({
426
+ coreSchema,
427
+ specificSchema,
428
+ tagName
429
+ }) => {
430
+ const doc = document.implementation.createDocument(specificSchema, tagName, null);
431
+ const root = doc.documentElement;
432
+ root.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", specificSchema);
433
+ if (coreSchema !== void 0) {
434
+ root.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:x", coreSchema);
435
+ }
436
+ return doc;
437
+ };
438
+ var serializer = new XMLSerializer();
439
+ var serializeXml = (doc) => {
440
+ let serialized = serializer.serializeToString(doc);
441
+ if (!serialized.startsWith("<?xml")) {
442
+ serialized = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + serialized;
443
+ }
444
+ return serialized;
445
+ };
446
+ var valueToString = (value) => {
447
+ if (value === void 0 || value === null) {
448
+ return "";
449
+ }
450
+ if (Array.isArray(value)) {
451
+ return value.map(valueToString).toString();
452
+ }
453
+ if (typeof value === "object") {
454
+ if ("$cellType" in value) {
455
+ return String(value);
456
+ }
457
+ return JSON.stringify(value);
458
+ }
459
+ if (typeof value === "number") {
460
+ return value.toString();
461
+ }
462
+ return String(value);
463
+ };
464
+ var createSharedString = (text, doc = document) => {
465
+ const si = doc.createElementNS(NS, "si");
466
+ const t = doc.createElementNS(NS, "t");
467
+ if (/^\s|\s$/.test(text)) {
468
+ t.setAttributeNS(
469
+ "http://www.w3.org/XML/1998/namespace",
470
+ "xml:space",
471
+ "preserve"
472
+ );
473
+ }
474
+ t.textContent = text;
475
+ si.append(t);
476
+ return si;
477
+ };
478
+ var toType = {
479
+ null: "s",
480
+ error: "e",
481
+ number: null,
482
+ boolean: "b",
483
+ string: "s",
484
+ date: null,
485
+ formula: null,
486
+ url: "s"
487
+ };
488
+ var createCell = ({
489
+ value,
490
+ type,
491
+ style,
492
+ row,
493
+ column,
494
+ formula,
495
+ xml
496
+ }) => {
497
+ const c = xml.createElementNS(NS, "c");
498
+ if (style !== null) c.setAttribute("s", style);
499
+ const ref = toCellRef({ row, column });
500
+ c.setAttribute("r", ref);
501
+ if (type !== null) {
502
+ const excelType = toType[type];
503
+ if (excelType !== void 0 && excelType !== null) c.setAttribute("t", excelType);
504
+ }
505
+ if (formula !== null && formula !== void 0) {
506
+ const f = xml.createElementNS(NS, "f");
507
+ f.textContent = formula;
508
+ c.append(f);
509
+ }
510
+ if (value !== null && value !== void 0) {
511
+ const v = xml.createElementNS(NS, "v");
512
+ v.textContent = String(value);
513
+ c.append(v);
514
+ }
515
+ return c;
516
+ };
517
+
518
+ // src/functions/get-replacements.ts
519
+ var accessorRegex = /(?<=^\$\{)(?<table>\s*table:\s*)?(?<accessor>[^}]+?)(?=\s*}$)/;
520
+ var globalAccessorRegex = /\$\{(?<accessor>[^}]+)}/g;
521
+ var replaceAccessors = (text, dataStore) => {
522
+ return text.replace(globalAccessorRegex, (_match, accessor) => {
523
+ return valueToString(dataStore.get(accessor));
524
+ });
525
+ };
526
+ var getReplacements = (oldValues, dataStore) => {
527
+ const replacements = /* @__PURE__ */ new Map();
528
+ let index = -1;
529
+ for (const si of oldValues) {
530
+ index++;
531
+ if (si.children.length === 1 && si.children[0].tagName === "t") {
532
+ const element = si.children[0];
533
+ const text = element.textContent;
534
+ const match = text.match(accessorRegex);
535
+ if (match === null) {
536
+ const value2 = replaceAccessors(text, dataStore);
537
+ replacements.set(String(index), value2);
538
+ continue;
539
+ }
540
+ if (match.groups === void 0) {
541
+ replacements.set(String(index), text);
542
+ continue;
543
+ }
544
+ const isTable = typeof match.groups.table === "string";
545
+ const value = dataStore.get(match.groups.accessor);
546
+ const cloned = structuredClone(value);
547
+ if (isTable) Object.defineProperty(cloned, "$isTable", { value: true, enumerable: false });
548
+ replacements.set(String(index), cloned);
549
+ continue;
550
+ }
551
+ for (const t of Array.from(si.querySelectorAll("t"))) {
552
+ const text = t.textContent ?? "";
553
+ t.textContent = replaceAccessors(text, dataStore);
554
+ replacements.set(String(index), si);
555
+ }
556
+ }
557
+ return replacements;
558
+ };
559
+
560
+ // src/functions/process-worksheets.ts
561
+ var processWorksheets = async ({
562
+ workbook,
563
+ dataStore
564
+ }) => {
565
+ const {
566
+ styles,
567
+ sharedStrings,
568
+ dataValidations,
569
+ pivotTables,
570
+ extensionLists
571
+ } = workbook;
572
+ const oldValues = sharedStrings.oldValues;
573
+ const replacements = getReplacements(oldValues, dataStore);
574
+ for (const worksheet of workbook.sheets) {
575
+ const {
576
+ name,
577
+ cells,
578
+ mergedCells,
579
+ hyperlinks,
580
+ comments,
581
+ conditionalFormattings,
582
+ tables,
583
+ xml,
584
+ replaceCells
585
+ } = worksheet;
586
+ comments.changeComments(dataStore);
587
+ const processedCells = cells.filter((cell) => {
588
+ if (cell.inMergedCell !== void 0) {
589
+ if ((cell.value === void 0 || cell.value === null) && (cell.formula === void 0 || cell.formula === null)) return false;
590
+ }
591
+ return true;
592
+ }).map((cell) => {
593
+ if (cell.type !== "string") return cell;
594
+ if (cell.value === void 0) return cell;
595
+ if (cell.value === null) return cell;
596
+ cell.newValue = replacements.get(String(cell.value));
597
+ if (cell.inTable !== void 0) {
598
+ const table = tables.get(cell.inTable);
599
+ table?.extend(cell);
600
+ }
601
+ if (cell.inConditionalFormatting !== void 0) {
602
+ const formatting = conditionalFormattings.get(cell.inConditionalFormatting);
603
+ formatting?.extend(cell);
604
+ }
605
+ if (cell.inDataValidation !== void 0) {
606
+ const dataValidation = dataValidations.get(cell.inDataValidation);
607
+ dataValidation?.extend(cell);
608
+ }
609
+ if (cell.inDataValidationList !== void 0) {
610
+ const dataValidation = dataValidations.get(cell.inDataValidationList);
611
+ dataValidation?.extendFormula(cell);
612
+ }
613
+ if (cell.inExtensionList !== void 0) {
614
+ const extension = extensionLists.get(cell.inExtensionList);
615
+ extension?.extend(cell);
616
+ }
617
+ if (cell.inExtensionListSource !== void 0) {
618
+ const extension = extensionLists.get(cell.inExtensionListSource);
619
+ extension?.extendFormula(cell);
620
+ }
621
+ if (cell.inPivotTableSource !== void 0) {
622
+ const pivotTableSource = pivotTables.get(cell.inPivotTableSource);
623
+ pivotTableSource?.extend(cell);
624
+ }
625
+ return cell;
626
+ });
627
+ processedCells.sort((cell1, cell2) => {
628
+ if (cell1.row !== cell2.row) return cell1.row - cell2.row;
629
+ return cell1.column - cell2.column;
630
+ });
631
+ const offsets = getOffsets({
632
+ cells: processedCells,
633
+ mergedCells,
634
+ tables
635
+ });
636
+ const movedCells = processedCells.map((cell) => {
637
+ const colOffset = getColumnShift(cell, offsets);
638
+ const rowOffset = getRowShift(cell, offsets);
639
+ cell.column = cell.column + colOffset;
640
+ cell.row = cell.row + rowOffset;
641
+ return cell;
642
+ });
643
+ const extendedCells = [];
644
+ for (const cell of movedCells) {
645
+ if (cell.type !== "string" || !Array.isArray(cell.newValue)) {
646
+ extendedCells.push(cell);
647
+ continue;
648
+ }
649
+ if (Array.isArray(cell.newValue)) {
650
+ const mergedCell = mergedCells.get(cell.inMergedCell ?? "");
651
+ let width = 1;
652
+ let height = 1;
653
+ if (mergedCell !== void 0) {
654
+ width = mergedCell.width;
655
+ height = mergedCell.height;
656
+ }
657
+ for (let i = 0; i < cell.newValue.length; i++) {
658
+ const newCell = { ...cell };
659
+ newCell.newValue = cell.newValue[i];
660
+ newCell.column = newCell.column + ("$isTable" in cell.newValue ? 0 : width) * i;
661
+ newCell.row = newCell.row + ("$isTable" in cell.newValue ? height : 0) * i;
662
+ if (mergedCell !== void 0) {
663
+ mergedCells.add({
664
+ columnStart: newCell.column,
665
+ rowStart: newCell.row,
666
+ columnEnd: newCell.column + width - 1,
667
+ rowEnd: newCell.row + height - 1
668
+ });
669
+ }
670
+ extendedCells.push(newCell);
671
+ }
672
+ }
673
+ }
674
+ const newCells = extendedCells.map((cell) => {
675
+ if (isElement(cell.newValue)) {
676
+ const sharedValue = sharedStrings.get(cell.newValue);
677
+ cell.value = sharedValue;
678
+ return cell;
679
+ }
680
+ if (cell.type !== "string") {
681
+ if (cell.type === "formula") cell.value = null;
682
+ return cell;
683
+ }
684
+ const newValue = cell.newValue === void 0 ? cell.value : cell.newValue;
685
+ const cellType = cell.type === "string" ? guessDataType(newValue) : cell.type;
686
+ const value = convertValueToExcel({ value: newValue, cellType });
687
+ cell.type = cellType;
688
+ if ((cellType === "formula" || cellType === "error") && typeof value === "string") {
689
+ if (cell.formula === void 0) cell.formula = value;
690
+ cell.type = "formula";
691
+ cell.value = null;
692
+ return cell;
693
+ }
694
+ if (cellType === "string" && typeof value === "string") {
695
+ const sharedValue = sharedStrings.get(value);
696
+ cell.value = sharedValue;
697
+ return cell;
698
+ }
699
+ if (cellType === "url" && typeof value === "string") {
700
+ cell.style = styles.getHyperlinkFormat();
701
+ const sharedValue = sharedStrings.get(value);
702
+ cell.value = sharedValue;
703
+ hyperlinks.add({ range: cell, url: value });
704
+ return cell;
705
+ }
706
+ if (cellType === "date") {
707
+ cell.style = styles.getDateFormat();
708
+ }
709
+ cell.value = value;
710
+ return cell;
711
+ });
712
+ const rowsMap = /* @__PURE__ */ new Map();
713
+ for (const cell of newCells) {
714
+ let row = rowsMap.get(cell.row);
715
+ if (row === void 0) {
716
+ row = xml.createElementNS(NS, "row");
717
+ row.setAttribute("r", String(cell.row));
718
+ rowsMap.set(cell.row, row);
719
+ row = rowsMap.get(cell.row);
720
+ }
721
+ row?.append(createCell({ ...cell, xml }));
722
+ }
723
+ const original = xml.querySelector("sheetData");
724
+ if (original === null) throw new Error("Excel workbook is malformed");
725
+ const newSheetData = original.cloneNode();
726
+ if (newSheetData === void 0) throw new Error("Excel workbook is mailformed");
727
+ for (const row of rowsMap.values()) {
728
+ newSheetData.append(row);
729
+ }
730
+ const validations = dataStore.get("$validations");
731
+ if (Array.isArray(validations)) {
732
+ for (const validation of validations) {
733
+ if (validation.sheet !== name) continue;
734
+ dataValidations.add(xml, validation);
735
+ }
736
+ }
737
+ replaceCells(newSheetData);
738
+ comments.save();
739
+ conditionalFormattings.save();
740
+ tables.save();
741
+ hyperlinks.save();
742
+ }
743
+ dataValidations.save();
744
+ extensionLists.save();
745
+ pivotTables.save();
746
+ for (const worksheet of workbook.sheets) worksheet.save();
747
+ };
748
+
749
+ // src/functions/global-helpers.ts
750
+ var parser = new DOMParser();
751
+ var generateUUID = () => {
752
+ return crypto.randomUUID();
753
+ };
754
+
755
+ // src/functions/get-styles.ts
756
+ var DATE_FORMAT = String.raw`yyyy\-mm\-dd;@`;
757
+ var serializer2 = new XMLSerializer();
758
+ var hasAtLeastTwoDateParts = (s) => {
759
+ let count = 0;
760
+ const lower = s.toLowerCase();
761
+ for (const p of ["y", "m", "d", "h"]) {
762
+ if (lower.includes(p) && ++count >= 2) return true;
763
+ }
764
+ return false;
765
+ };
766
+ var createStyles = async (zip, target) => {
767
+ let numFmts;
768
+ let numFmtId;
769
+ let cachedDateFormatId = null;
770
+ let cachedLinkFormatId = null;
771
+ let isDirty = false;
772
+ const xmlStyle = await zip.file(`xl/${target}`)?.async("string");
773
+ if (xmlStyle === void 0) throw new Error("This Excel file is corrupted: styles.xml missing");
774
+ const domStyle = parser.parseFromString(xmlStyle, "application/xml");
775
+ const styleSheet = domStyle.querySelector("styleSheet");
776
+ const cellXfs = domStyle.querySelector("cellXfs");
777
+ if (styleSheet === null || cellXfs === null) throw new Error("Invalid styles.xml structure");
778
+ numFmts = domStyle.querySelector("numFmts");
779
+ if (numFmts === null) {
780
+ numFmts = domStyle.createElementNS(NS, "numFmts");
781
+ styleSheet.prepend(numFmts);
782
+ }
783
+ const existingIds = Array.from(
784
+ domStyle.querySelectorAll("numFmt"),
785
+ (el) => Number(el.getAttribute("numFmtId"))
786
+ ).filter(Number.isFinite);
787
+ numFmtId = Math.max(163, ...existingIds);
788
+ const save = () => {
789
+ if (!isDirty) return;
790
+ const newXfData = serializer2.serializeToString(domStyle);
791
+ zip.file(`xl/${target}`, newXfData);
792
+ };
793
+ const getDateFormat = () => {
794
+ const existingDateFormat = Array.from(
795
+ domStyle.querySelectorAll("cellXfs xf")
796
+ ).findIndex((el) => {
797
+ return ["14", "15", "16", "17", "18", "22"].includes(el.getAttribute("numFmtId") ?? "") || hasAtLeastTwoDateParts(el.getAttribute("formatCode") ?? "");
798
+ });
799
+ if (existingDateFormat !== -1) {
800
+ cachedDateFormatId = String(existingDateFormat);
801
+ return cachedDateFormatId;
802
+ }
803
+ numFmtId++;
804
+ const numFmt = domStyle.createElementNS(NS, "numFmt");
805
+ numFmt.setAttribute("numFmtId", String(numFmtId));
806
+ numFmt.setAttribute("formatCode", DATE_FORMAT);
807
+ if (numFmts === null || cellXfs === null) throw new Error("Number formats are mailformed");
808
+ numFmts.append(numFmt);
809
+ numFmts.setAttribute("count", String(numFmts.children.length));
810
+ const xf = domStyle.createElementNS(NS, "xf");
811
+ xf.setAttribute("numFmtId", String(numFmtId));
812
+ xf.setAttribute("fontId", "0");
813
+ xf.setAttribute("fillId", "0");
814
+ xf.setAttribute("borderId", "0");
815
+ xf.setAttribute("xfId", "0");
816
+ xf.setAttribute("applyNumberFormat", "1");
817
+ cellXfs.append(xf);
818
+ cellXfs.setAttribute("count", String(cellXfs.children.length));
819
+ isDirty = true;
820
+ cachedDateFormatId = String(cellXfs.children.length - 1);
821
+ return cachedDateFormatId;
822
+ };
823
+ const getHyperlinkFormat = () => {
824
+ const fonts = domStyle.querySelector("fonts");
825
+ if (fonts === null || cellXfs === null) {
826
+ throw new Error("styles.xml malformed");
827
+ }
828
+ let fontId = Array.from(fonts.children).findIndex((font) => {
829
+ return font.querySelector("u") !== void 0 && font.querySelector("color")?.getAttribute("theme") === "10";
830
+ });
831
+ if (fontId === -1) {
832
+ const font = domStyle.createElementNS(NS, "font");
833
+ const color = domStyle.createElementNS(NS, "color");
834
+ color.setAttribute("theme", "10");
835
+ const underline = domStyle.createElementNS(NS, "u");
836
+ font.append(color, underline);
837
+ fonts.append(font);
838
+ fonts.setAttribute("count", String(fonts.children.length));
839
+ fontId = fonts.children.length - 1;
840
+ }
841
+ const xfIndex = Array.from(cellXfs.children).findIndex(
842
+ (xf2) => xf2.getAttribute("fontId") === String(fontId)
843
+ );
844
+ if (xfIndex !== -1) return String(xfIndex);
845
+ const xf = domStyle.createElementNS(NS, "xf");
846
+ xf.setAttribute("fontId", String(fontId));
847
+ xf.setAttribute("fillId", "0");
848
+ xf.setAttribute("borderId", "0");
849
+ xf.setAttribute("xfId", "0");
850
+ xf.setAttribute("applyFont", "1");
851
+ cellXfs.append(xf);
852
+ cellXfs.setAttribute("count", String(cellXfs.children.length));
853
+ isDirty = true;
854
+ cachedLinkFormatId = String(cellXfs.children.length - 1);
855
+ return cachedLinkFormatId;
856
+ };
857
+ return { save, getDateFormat, getHyperlinkFormat };
858
+ };
859
+
860
+ // src/functions/get-shared-strings.ts
861
+ var getSharedStrings = async (xlsx, target) => {
862
+ let isDirty = false;
863
+ const filename = `xl/${target}`;
864
+ const xmlText = await xlsx.file(filename)?.async("string");
865
+ if (xmlText === void 0) {
866
+ throw new Error("This Excel template has no strings");
867
+ }
868
+ const xml = parser.parseFromString(xmlText, "application/xml");
869
+ const sst = xml.querySelector("sst");
870
+ if (sst === null) throw new Error("This Excel template has no strings");
871
+ const oldValues = Array.from(sst.children);
872
+ const strings = /* @__PURE__ */ new Map();
873
+ const elements = /* @__PURE__ */ new WeakMap();
874
+ const list = [];
875
+ const get = (el) => {
876
+ if (typeof el === "string") {
877
+ const value2 = strings.get(el);
878
+ if (value2 !== void 0) {
879
+ return value2;
880
+ }
881
+ const newString = createSharedString(el, xml);
882
+ const index2 = String(list.length);
883
+ strings.set(el, index2);
884
+ list.push(newString);
885
+ isDirty = true;
886
+ return index2;
887
+ }
888
+ const value = elements.get(el);
889
+ if (value !== void 0) {
890
+ return value;
891
+ }
892
+ const index = String(list.length);
893
+ elements.set(el, index);
894
+ list.push(el);
895
+ isDirty = true;
896
+ return index;
897
+ };
898
+ const save = () => {
899
+ if (!isDirty) return;
900
+ sst?.replaceChildren(...list);
901
+ const text = serializeXml(xml);
902
+ xlsx.file(filename, text);
903
+ };
904
+ return {
905
+ oldValues,
906
+ get,
907
+ save
908
+ };
909
+ };
910
+
911
+ // src/functions/cells-helpers.ts
912
+ var typesDict = {
913
+ f: "formula",
914
+ e: "error",
915
+ b: "boolean",
916
+ s: "string",
917
+ n: "number"
918
+ };
919
+ var cellToModel = (cell) => {
920
+ const currentType = cell.getAttribute("t");
921
+ const cellType = typesDict[currentType ?? "n"];
922
+ const cellRef = cell.getAttribute("r");
923
+ const cellStyle = cell.getAttribute("s");
924
+ let value = cell.querySelector("v")?.textContent;
925
+ if (cellType === "number" && typeof value === "string") value = parseFloat(value);
926
+ const formula = cell.querySelector("f")?.textContent;
927
+ if (cellRef === null) {
928
+ throw new Error("Excel template is mailformed");
929
+ }
930
+ const { row, column } = parseCellRef(cellRef);
931
+ return {
932
+ ref: cellRef,
933
+ row,
934
+ column,
935
+ style: cellStyle,
936
+ type: cellType,
937
+ ...formula !== void 0 ? { formula } : {},
938
+ ...value !== void 0 ? { value } : {}
939
+ };
940
+ };
941
+
942
+ // src/functions/get-cells.ts
943
+ var getCells = (xml) => {
944
+ const sheetData = xml.querySelector("sheetData") ?? xml.createElementNS(NS, "sheetData");
945
+ const cellsXml = sheetData.querySelectorAll("c");
946
+ const cells = cellsXml === void 0 ? [] : Array.from(cellsXml).map((c) => cellToModel(c));
947
+ return cells;
948
+ };
949
+
950
+ // src/functions/get-merged-cells.ts
951
+ var getMergedCells = (xml) => {
952
+ const mergedCells = /* @__PURE__ */ new Map();
953
+ const mergeCellsParent = xml.querySelector("mergeCells") ?? xml.createElementNS(NS, "mergeCells");
954
+ const mergeCellsXml = mergeCellsParent.querySelectorAll("mergeCell");
955
+ for (const mergedCell of Array.from(mergeCellsXml)) {
956
+ const ref = mergedCell.getAttribute("ref");
957
+ if (ref === null) continue;
958
+ const range = parseCellRange(ref);
959
+ mergedCells.set(ref, {
960
+ range,
961
+ width: range.columnEnd - range.columnStart + 1,
962
+ height: range.rowEnd - range.rowStart + 1,
963
+ ref,
964
+ xml: mergedCell
965
+ });
966
+ }
967
+ mergeCellsParent.replaceChildren();
968
+ const add = (ref) => {
969
+ const mergedCell = xml.createElementNS(NS, "mergeCell");
970
+ mergedCell.setAttribute("ref", toCellRange(ref));
971
+ mergeCellsParent.appendChild(mergedCell);
972
+ };
973
+ const get = (ref) => {
974
+ return mergedCells.get(ref);
975
+ };
976
+ const findByCell = (cell) => {
977
+ for (const [name, value] of mergedCells) {
978
+ if (isInRange(cell, value.range)) return name;
979
+ }
980
+ return null;
981
+ };
982
+ return {
983
+ add,
984
+ get,
985
+ findByCell
986
+ };
987
+ };
988
+
989
+ // src/functions/get-hyperlinks.ts
990
+ var addHyperlink = (doc, hyperlinks, ref, rel) => {
991
+ const id = generateUUID();
992
+ const el = doc.createElementNS(NS, "hyperlink");
993
+ el.setAttribute("ref", ref);
994
+ el.setAttribute("r:id", rel);
995
+ el.setAttribute("xr:uid", `{${id.toUpperCase()}}`);
996
+ hyperlinks.appendChild(el);
997
+ };
998
+ var getHyperlinks = ({
999
+ xml,
1000
+ relations
1001
+ }) => {
1002
+ let exists = true;
1003
+ let hyperLinksParent = xml.querySelector("hyperlinks");
1004
+ if (hyperLinksParent === null) {
1005
+ hyperLinksParent = xml.createElementNS(NS, "hyperlinks");
1006
+ exists = false;
1007
+ }
1008
+ const hyperLinksXml = hyperLinksParent.querySelectorAll("hyperlink");
1009
+ const existingLinks = /* @__PURE__ */ new Map();
1010
+ for (const hyperlink of hyperLinksXml) {
1011
+ const ref = hyperlink.getAttribute("ref");
1012
+ const rId = hyperlink.getAttribute("r:id");
1013
+ const xrUid = hyperlink.getAttribute("xr:uid");
1014
+ if (ref === null || rId === null || xrUid === null) continue;
1015
+ existingLinks.set(ref, {
1016
+ ref,
1017
+ "r:id": rId,
1018
+ "xr:uid": xrUid,
1019
+ xml: hyperlink
1020
+ });
1021
+ }
1022
+ const save = () => {
1023
+ if (exists) return;
1024
+ const pageMargins = xml.querySelector("pageMargins");
1025
+ if (pageMargins !== null) pageMargins.parentNode?.insertBefore(hyperLinksParent, pageMargins);
1026
+ };
1027
+ const add = ({ range, url }) => {
1028
+ let id;
1029
+ const ref = toCellRef(range);
1030
+ const oldUrlElemnt = xml.querySelector(`Relationship[Target="${url}"]`);
1031
+ if (oldUrlElemnt !== null) {
1032
+ const oldUrl = oldUrlElemnt.getAttribute("Id");
1033
+ if (oldUrl !== null) id = oldUrl;
1034
+ } else {
1035
+ id = relations.add({
1036
+ type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
1037
+ target: url,
1038
+ targetMode: "External"
1039
+ });
1040
+ }
1041
+ if (id === void 0) throw new Error("The URL is mailformed");
1042
+ addHyperlink(xml, hyperLinksParent, ref, id);
1043
+ };
1044
+ return {
1045
+ add,
1046
+ save
1047
+ };
1048
+ };
1049
+
1050
+ // src/functions/get-comments.ts
1051
+ var addThreadedComment = (doc, c) => {
1052
+ const id = crypto.randomUUID();
1053
+ const el = doc.createElementNS(NS_TC, "threadedComment");
1054
+ el.setAttribute("ref", c.ref);
1055
+ el.setAttribute("dT", (/* @__PURE__ */ new Date()).toISOString());
1056
+ el.setAttribute("personId", c.personId ?? "");
1057
+ el.setAttribute("id", "{" + id.toUpperCase() + "}");
1058
+ const text = doc.createElementNS(NS_TC, "text");
1059
+ text.textContent = c.text;
1060
+ el.appendChild(text);
1061
+ doc.documentElement.appendChild(el);
1062
+ return id;
1063
+ };
1064
+ var getComments = async ({
1065
+ xlsx,
1066
+ relations,
1067
+ workbook,
1068
+ id
1069
+ }) => {
1070
+ let isDirty = false;
1071
+ const guessedFilename = `../threadedComments/threadedComment${id.replace("rId", "")}.xml`;
1072
+ const commentsRelations = relations.getElements("threadedComment");
1073
+ if (commentsRelations.length > 1) throw new Error("Comments are mailformed");
1074
+ let relationExists = commentsRelations.length > 0;
1075
+ const commentsFileName = (commentsRelations[0]?.target ?? guessedFilename).replace("..", "xl");
1076
+ const xmlText = await xlsx.file(commentsFileName)?.async("string");
1077
+ const xml = xmlText === void 0 ? createXml({ coreSchema: NS, specificSchema: NS_TC, tagName: "threadedComments" }) : parser.parseFromString(xmlText, "application/xml");
1078
+ if (xmlText === void 0) isDirty = true;
1079
+ if (xml.getElementsByTagName("parsererror").length > 0) {
1080
+ throw new Error(`Invalid relations file for ${commentsFileName}`);
1081
+ }
1082
+ const oldCommentsMap = /* @__PURE__ */ new Map();
1083
+ const commentsXml = xml.querySelectorAll("threadedComment");
1084
+ for (const comment of commentsXml) {
1085
+ const ref = comment.getAttribute("ref");
1086
+ const dT = comment.getAttribute("dT");
1087
+ const personId = comment.getAttribute("personId");
1088
+ const id2 = comment.getAttribute("id");
1089
+ const text = comment.querySelector("text")?.textContent;
1090
+ if (ref === null || dT === null || personId === null || id2 === null) continue;
1091
+ oldCommentsMap.set(ref, {
1092
+ ref,
1093
+ dT,
1094
+ personId,
1095
+ id: id2,
1096
+ text: text ?? "",
1097
+ xml: comment
1098
+ });
1099
+ }
1100
+ const changeComments = (datastore) => {
1101
+ for (const [, comment] of oldCommentsMap) {
1102
+ comment.text = replaceAccessors(comment.text, datastore);
1103
+ const text = comment.xml.querySelector("text");
1104
+ if (text === null) continue;
1105
+ text.textContent = comment.text;
1106
+ isDirty = true;
1107
+ }
1108
+ };
1109
+ let oldComments;
1110
+ let drawings;
1111
+ const setOldComments = (props) => {
1112
+ oldComments = props;
1113
+ };
1114
+ const setDrawings = (props) => {
1115
+ drawings = props;
1116
+ };
1117
+ const add = ({ ref, text, row, column }) => {
1118
+ addRelation();
1119
+ if (ref === void 0) {
1120
+ if (row === void 0 || column === void 0) {
1121
+ throw new Error("Comment position is undefined");
1122
+ }
1123
+ ref = toCellRef({ row, column });
1124
+ }
1125
+ if (row === void 0 && column === void 0) {
1126
+ if (ref === void 0) {
1127
+ throw new Error("Comment position is undefined");
1128
+ }
1129
+ const location = parseCellRef(ref);
1130
+ row = location.row;
1131
+ column = location.column;
1132
+ }
1133
+ const { persons } = workbook;
1134
+ const systemPersonId = persons.getSystemUserId();
1135
+ const id2 = addThreadedComment(xml, {
1136
+ personId: systemPersonId,
1137
+ ref,
1138
+ text
1139
+ });
1140
+ oldComments?.add({ id: `{${id2.toUpperCase()}}`, ref, text });
1141
+ drawings?.add({ row: String(row), column: String(column) });
1142
+ isDirty = true;
1143
+ };
1144
+ const getComment = (ref) => {
1145
+ return oldCommentsMap.get(ref)?.ref;
1146
+ };
1147
+ const addRelation = () => {
1148
+ if (relationExists) return;
1149
+ const commentsRelation = {
1150
+ type: "http://schemas.microsoft.com/office/2017/10/relationships/threadedComment",
1151
+ target: `../threadedComments/threadedComment${id.replace("rId", "")}.xml`
1152
+ };
1153
+ relations.add(commentsRelation);
1154
+ relationExists = true;
1155
+ };
1156
+ const save = () => {
1157
+ if (!isDirty) return;
1158
+ if (oldComments !== void 0) oldComments.save();
1159
+ if (drawings !== void 0) drawings.save();
1160
+ const text = serializeXml(xml);
1161
+ xlsx.file(commentsFileName, text);
1162
+ };
1163
+ return {
1164
+ add,
1165
+ changeComments,
1166
+ getComment,
1167
+ setDrawings,
1168
+ setOldComments,
1169
+ save
1170
+ };
1171
+ };
1172
+
1173
+ // src/functions/get-conditional-formatting.ts
1174
+ var getConditionalFormatting = (xml) => {
1175
+ const conditionalFormattingXml = xml.querySelectorAll("conditionalFormatting");
1176
+ const conditionalFormattings = /* @__PURE__ */ new Map();
1177
+ let index = -1;
1178
+ for (const formatting of conditionalFormattingXml) {
1179
+ const sqref = formatting.getAttribute("sqref");
1180
+ const formula = formatting.querySelector("formula")?.textContent;
1181
+ if (sqref === null) continue;
1182
+ index++;
1183
+ const range = parseCellRange(sqref);
1184
+ const extension = {
1185
+ rows: 0,
1186
+ cols: 0
1187
+ };
1188
+ const extend = (cell) => {
1189
+ if (Array.isArray(cell.newValue)) {
1190
+ const length = cell.newValue.length - 1;
1191
+ if ("$isTable" in cell.newValue) {
1192
+ extension.rows = Math.max(length, extension.rows);
1193
+ } else {
1194
+ extension.cols = Math.max(length, extension.cols);
1195
+ }
1196
+ }
1197
+ };
1198
+ const currentFormatting = {
1199
+ sqref,
1200
+ range,
1201
+ extension,
1202
+ extend,
1203
+ ...formula === void 0 ? {} : { formula },
1204
+ xml: formatting
1205
+ };
1206
+ conditionalFormattings.set(String(index), currentFormatting);
1207
+ }
1208
+ const save = () => {
1209
+ for (const formatting of conditionalFormattings.values()) {
1210
+ formatting.range.columnEnd = formatting.range.columnEnd + formatting.extension.cols;
1211
+ formatting.range.rowEnd = formatting.range.rowEnd + formatting.extension.rows;
1212
+ const ref = toCellRange(formatting.range);
1213
+ formatting.xml.setAttribute("sqref", ref);
1214
+ }
1215
+ };
1216
+ const get = (ref) => {
1217
+ return conditionalFormattings.get(ref);
1218
+ };
1219
+ const findByCell = (cell) => {
1220
+ for (const [name, value] of conditionalFormattings) {
1221
+ if (isInRange(cell, value.range)) return name;
1222
+ }
1223
+ return null;
1224
+ };
1225
+ return {
1226
+ get,
1227
+ save,
1228
+ findByCell
1229
+ };
1230
+ };
1231
+
1232
+ // src/functions/get-tables.ts
1233
+ var serializer3 = new XMLSerializer();
1234
+ var getTables = async ({
1235
+ xlsx,
1236
+ relations
1237
+ }) => {
1238
+ const tablesProps = relations.getElements("table");
1239
+ const tableFiles = tablesProps.map((el) => el.target);
1240
+ const tables = /* @__PURE__ */ new Map();
1241
+ for (let file of tableFiles) {
1242
+ file = file.replace(/^\.\./, "xl");
1243
+ const xmlText = await xlsx.file(file)?.async("string");
1244
+ if (xmlText === void 0) {
1245
+ continue;
1246
+ }
1247
+ const xml = parser.parseFromString(xmlText, "application/xml");
1248
+ const table = xml.querySelector("table");
1249
+ if (table === null) continue;
1250
+ const ref = table.getAttribute("ref");
1251
+ if (ref === null) continue;
1252
+ const range = parseCellRange(ref);
1253
+ const headers = Array.from(xml.querySelectorAll("tableColumn")).map((el) => el.getAttribute("name")).filter((el) => el !== null);
1254
+ const initialHeadersLength = headers.length;
1255
+ const extension = {
1256
+ rows: 0,
1257
+ cols: 0
1258
+ };
1259
+ const existingHeaders = new Set(headers);
1260
+ const addHeader = (name, index) => {
1261
+ let newName = name;
1262
+ let j = 0;
1263
+ while (existingHeaders.has(newName)) {
1264
+ j++;
1265
+ newName = name + String(j);
1266
+ }
1267
+ existingHeaders.add(newName);
1268
+ headers.splice(index, 0, newName);
1269
+ return newName;
1270
+ };
1271
+ const createHeaders = (names) => {
1272
+ return names.map((name, i) => {
1273
+ const h = xml.createElementNS(NS, "tableColumn");
1274
+ h.setAttribute("id", String(i + 1));
1275
+ h.setAttribute("name", name);
1276
+ h.setAttribute("xr3:uid", `{${generateUUID().toUpperCase()}}`);
1277
+ return h;
1278
+ });
1279
+ };
1280
+ const extend = (cell) => {
1281
+ const t = tables.get(file);
1282
+ if (t === void 0) return;
1283
+ if (Array.isArray(cell.newValue)) {
1284
+ const isHeader = cell.row === range.rowStart;
1285
+ if (isHeader) {
1286
+ const index = cell.column - range.columnStart;
1287
+ const removed = headers.splice(index, 1);
1288
+ existingHeaders.delete(removed[0]);
1289
+ cell.newValue = cell.newValue.map((header, i) => {
1290
+ return addHeader(header, i + index);
1291
+ });
1292
+ }
1293
+ }
1294
+ if (Array.isArray(cell.newValue)) {
1295
+ const length = cell.newValue.length - 1;
1296
+ if ("$isTable" in cell.newValue) {
1297
+ t.extension.rows = t.extension.rows + length;
1298
+ } else {
1299
+ t.extension.cols = t.extension.cols + length;
1300
+ }
1301
+ }
1302
+ t.isDirty = true;
1303
+ };
1304
+ const finalize = () => {
1305
+ const headersLength = range.columnEnd - range.columnStart + extension.cols + 1;
1306
+ const headersDifference = headersLength - initialHeadersLength;
1307
+ if (headersDifference !== 0) {
1308
+ for (let i = 0; i < headersDifference; i++) {
1309
+ addHeader("Column", range.columnEnd + i - 1);
1310
+ }
1311
+ if (tableInMap.lastHeaderCell !== void 0) {
1312
+ tableInMap.lastHeaderCell.newValue = headers.slice(initialHeadersLength - 1);
1313
+ }
1314
+ }
1315
+ const newHeaders = createHeaders(headers);
1316
+ xml.querySelector("tableColumns")?.replaceChildren(...newHeaders);
1317
+ range.columnEnd = range.columnEnd + extension.cols;
1318
+ range.rowEnd = range.rowEnd + extension.rows;
1319
+ const ref2 = toCellRange(range);
1320
+ table.setAttribute("ref", ref2);
1321
+ const autofilter = table.querySelector("autoFilter");
1322
+ if (autofilter !== null) autofilter.setAttribute("ref", ref2);
1323
+ };
1324
+ const save2 = () => {
1325
+ const newData = serializer3.serializeToString(xml);
1326
+ xlsx.file(file, newData);
1327
+ };
1328
+ const tableInMap = {
1329
+ xml,
1330
+ ref,
1331
+ range,
1332
+ extend,
1333
+ extension,
1334
+ finalize,
1335
+ save: save2,
1336
+ isDirty: false
1337
+ };
1338
+ tables.set(file, tableInMap);
1339
+ }
1340
+ const get = (tableName) => {
1341
+ return tables.get(tableName);
1342
+ };
1343
+ const save = () => {
1344
+ for (const value of tables.values()) {
1345
+ if (value.isDirty) value.save();
1346
+ }
1347
+ };
1348
+ const findByCell = (cell) => {
1349
+ for (const [name, table] of tables) {
1350
+ if (cell.column === table.range.columnEnd && cell.row === table.range.rowStart) {
1351
+ table.lastHeaderCell = cell;
1352
+ }
1353
+ if (isInRange(cell, table.range)) return name;
1354
+ }
1355
+ return null;
1356
+ };
1357
+ return {
1358
+ get,
1359
+ save,
1360
+ findByCell
1361
+ };
1362
+ };
1363
+
1364
+ // src/functions/get-drawings.ts
1365
+ var serializeXml2 = (content) => `
1366
+ <xml xmlns:v="urn:schemas-microsoft-com:vml"
1367
+ xmlns:o="urn:schemas-microsoft-com:office:office"
1368
+ xmlns:x="urn:schemas-microsoft-com:office:excel">
1369
+
1370
+ <o:shapelayout v:ext="edit">
1371
+ <o:idmap v:ext="edit" data="1"/>
1372
+ </o:shapelayout>
1373
+
1374
+ <v:shapetype id="_x0000_t202" coordsize="21600,21600" o:spt="202" path="m,l,21600r21600,l21600,xe">
1375
+ <v:stroke joinstyle="miter"/>
1376
+ <v:path gradientshapeok="t" o:connecttype="rect"/>
1377
+ </v:shapetype>
1378
+ ${content}
1379
+ </xml>
1380
+ `;
1381
+ var createShape = ({
1382
+ id,
1383
+ row,
1384
+ column
1385
+ }) => {
1386
+ return `<v:shape id="_x0000_s${String(1025 + id)}" type="#_x0000_t202"
1387
+ style="position:absolute;width:10pt;height:10pt;visibility:hidden"
1388
+ fillcolor="none" strokecolor="none">
1389
+ <v:fill color2="infoBackground [80]"/>
1390
+ <v:shadow color="none [81]" obscured="t"/>
1391
+ <v:path o:connecttype="none"/>
1392
+ <v:textbox style="mso-direction-alt:auto">
1393
+ <div style="text-align:left"/>
1394
+ </v:textbox>
1395
+ <x:ClientData ObjectType="Note">
1396
+ <x:MoveWithCells/>
1397
+ <x:SizeWithCells/>
1398
+ <x:Anchor>${column},0,${row},0,${column + 1},0,${row + 2},0</x:Anchor>
1399
+ <x:AutoFill>False</x:AutoFill>
1400
+ <x:Row>${row}</x:Row>
1401
+ <x:Column>${column}</x:Column>
1402
+ </x:ClientData>
1403
+ </v:shape>`;
1404
+ };
1405
+ var getDrawings = async ({
1406
+ id,
1407
+ relations,
1408
+ xlsx
1409
+ }) => {
1410
+ const drawingRelation = relations.getElements("vmlDrawing");
1411
+ const guessedFilename = `../drawings/vmlDrawing${id.replace("rId", "")}.vml`;
1412
+ const noRelation = drawingRelation.length === 0;
1413
+ const filename = (drawingRelation[0]?.target ?? guessedFilename).replace("..", "xl");
1414
+ const unique = /* @__PURE__ */ new Set();
1415
+ let isDirty = false;
1416
+ const drawings = [];
1417
+ const xmlText = await xlsx.file(filename)?.async("string");
1418
+ if (xmlText !== void 0) {
1419
+ const vmlDoc = parser.parseFromString(xmlText, "application/xml");
1420
+ const shapes = vmlDoc.getElementsByTagName("v:shape");
1421
+ for (const shape of shapes) {
1422
+ const row = shape.getElementsByTagName("x:Row")[0]?.textContent;
1423
+ const column = shape.getElementsByTagName("x:Column")[0]?.textContent;
1424
+ if (row === void 0 || column === void 0) continue;
1425
+ const rowValue = String(parseInt(row) + 1);
1426
+ const columnValue = String(parseInt(column) + 1);
1427
+ drawings.push({ row: rowValue, column: columnValue });
1428
+ }
1429
+ }
1430
+ const add = ({ row, column }) => {
1431
+ const ref = `${row}:${column}`;
1432
+ if (unique.has(ref)) return;
1433
+ drawings.push({ row, column });
1434
+ unique.add(ref);
1435
+ isDirty = true;
1436
+ };
1437
+ const save = () => {
1438
+ if (!isDirty) return;
1439
+ if (noRelation) {
1440
+ relations.add({
1441
+ target: filename.replace("xl", ".."),
1442
+ type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
1443
+ });
1444
+ }
1445
+ const content = drawings.map((drawing, i) => {
1446
+ return createShape({
1447
+ id: i,
1448
+ row: parseInt(drawing.row) - 1,
1449
+ column: parseInt(drawing.column) - 1
1450
+ });
1451
+ }).join("");
1452
+ const data = serializeXml2(content);
1453
+ xlsx.file(filename, data);
1454
+ };
1455
+ return {
1456
+ add,
1457
+ save
1458
+ };
1459
+ };
1460
+
1461
+ // src/functions/get-old-comments.ts
1462
+ var EXCEL_NOTIFICATION = `[Threaded comment]
1463
+
1464
+ Your version of Excel allows you to read this threaded comment; however, any edits to it will get removed if the file is opened in a newer version of Excel. Learn more: https://go.microsoft.com/fwlink/?linkid=870924
1465
+
1466
+ Comment:
1467
+ `;
1468
+ var NS_MC = "http://schemas.openxmlformats.org/markup-compatibility/2006";
1469
+ var NS_XR = "http://schemas.microsoft.com/office/spreadsheetml/2014/revision";
1470
+ var createComment = ({ xml, ref, id, text, author }) => {
1471
+ const comment = xml.createElementNS(NS, "comment");
1472
+ comment.setAttribute("ref", ref);
1473
+ comment.setAttribute("xr:uid", id);
1474
+ comment.setAttribute("authorId", author);
1475
+ comment.setAttribute("shapeId", "0");
1476
+ const textTag = xml.createElementNS(NS, "text");
1477
+ const tTag = xml.createElementNS(NS, "t");
1478
+ tTag.textContent = text;
1479
+ textTag.appendChild(tTag);
1480
+ comment.appendChild(textTag);
1481
+ return comment;
1482
+ };
1483
+ var createAuthor = ({ xml, id }) => {
1484
+ const author = xml.createElementNS(NS, "author");
1485
+ author.textContent = `tc=${id}`;
1486
+ return author;
1487
+ };
1488
+ var getOldComments = async ({
1489
+ id,
1490
+ relations,
1491
+ xlsx
1492
+ }) => {
1493
+ const oldCommentsRelation = relations.getElements("comments");
1494
+ const guessedFilename = `../comments${id.replace("rId", "")}.xml`;
1495
+ const noRelation = oldCommentsRelation.length === 0;
1496
+ const filename = (oldCommentsRelation[0]?.target ?? guessedFilename).replace("..", "xl");
1497
+ let isDirty = false;
1498
+ const comments = [];
1499
+ const xmlText = await xlsx.file(filename)?.async("string");
1500
+ const xml = xmlText === void 0 ? createXml({ specificSchema: NS, tagName: "comments" }) : parser.parseFromString(xmlText, "application/xml");
1501
+ if (xmlText === void 0) {
1502
+ xml.querySelector("comments")?.setAttribute("xmlns:mc", NS_MC);
1503
+ xml.querySelector("comments")?.setAttribute("xmlns:xr", NS_XR);
1504
+ xml.querySelector("comments")?.setAttribute("mc:Ignorable", "xr");
1505
+ xml.querySelector("comments")?.appendChild(xml.createElementNS(NS, "authors"));
1506
+ xml.querySelector("comments")?.appendChild(xml.createElementNS(NS, "commentList"));
1507
+ }
1508
+ const parent = xml.querySelector("comments");
1509
+ const authors = xml.querySelector("authors");
1510
+ const commentList = xml.querySelector("commentList");
1511
+ if (parent === null || authors === null || commentList === null) throw new Error("Comments are mailformed");
1512
+ const uniqueRefs = /* @__PURE__ */ new Set();
1513
+ if (xmlText !== void 0) {
1514
+ const currentComments = commentList.getElementsByTagName("comment");
1515
+ for (const comment of currentComments) {
1516
+ const ref = comment.getAttribute("ref");
1517
+ const id2 = comment.getAttribute("xr:uid");
1518
+ let text = comment.querySelector("t")?.textContent ?? "";
1519
+ text = text.replace(EXCEL_NOTIFICATION, "");
1520
+ if (ref === null || id2 === null) continue;
1521
+ comments.push({ ref, id: id2, text });
1522
+ }
1523
+ }
1524
+ const add = ({ ref, id: id2, text }) => {
1525
+ if (uniqueRefs.has(ref)) return;
1526
+ uniqueRefs.add(ref);
1527
+ comments.push({ ref, id: id2, text });
1528
+ isDirty = true;
1529
+ };
1530
+ const save = () => {
1531
+ if (!isDirty) return;
1532
+ if (noRelation) {
1533
+ relations.add({
1534
+ target: filename.replace("xl", ".."),
1535
+ type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
1536
+ });
1537
+ }
1538
+ const newComments = [];
1539
+ const newAuthors = [];
1540
+ let index = 0;
1541
+ for (const comment of comments) {
1542
+ const newComment = createComment({ xml, author: String(index), ...comment });
1543
+ const author = createAuthor({ xml, id: comment.id });
1544
+ newComments.push(newComment);
1545
+ newAuthors.push(author);
1546
+ index++;
1547
+ }
1548
+ authors.replaceChildren(...newAuthors);
1549
+ commentList?.replaceChildren(...newComments);
1550
+ const data = serializeXml(xml);
1551
+ xlsx.file(filename, data);
1552
+ };
1553
+ return {
1554
+ add,
1555
+ save
1556
+ };
1557
+ };
1558
+
1559
+ // src/functions/get-sheets.ts
1560
+ var getSheet = async ({
1561
+ xlsx,
1562
+ xml,
1563
+ relations,
1564
+ workbook,
1565
+ target,
1566
+ id,
1567
+ name
1568
+ }) => {
1569
+ const filename = `xl/${target}`;
1570
+ const {
1571
+ pivotTables,
1572
+ dataValidations,
1573
+ extensionLists
1574
+ } = workbook;
1575
+ const initialCells = getCells(xml);
1576
+ const mergedCells = getMergedCells(xml);
1577
+ const hyperlinks = getHyperlinks({ xml, relations });
1578
+ const comments = await getComments({ xlsx, workbook, relations, id });
1579
+ const conditionalFormattings = getConditionalFormatting(xml);
1580
+ const tables = await getTables({ xlsx, relations });
1581
+ const drawings = await getDrawings({ xlsx, id, relations });
1582
+ const oldComments = await getOldComments({ xlsx, id, relations });
1583
+ comments.setDrawings(drawings);
1584
+ comments.setOldComments(oldComments);
1585
+ const cells = initialCells.map((cell) => {
1586
+ const comment = comments.getComment(cell.ref);
1587
+ if (comment !== void 0) cell.hasComment = comment;
1588
+ const table = tables.findByCell(cell);
1589
+ if (table !== null) cell.inTable = table;
1590
+ const mergeCell = mergedCells.findByCell(cell);
1591
+ if (mergeCell !== null) cell.inMergedCell = mergeCell;
1592
+ const pivotTable = pivotTables.findByCell(name, cell);
1593
+ if (pivotTable !== null) cell.inPivotTableSource = pivotTable;
1594
+ const dataValidation = dataValidations.findByCell(name, cell);
1595
+ if (dataValidation !== null) cell.inDataValidation = dataValidation;
1596
+ const dataValidationList = dataValidations.findListByCell(name, cell);
1597
+ if (dataValidationList !== null) cell.inDataValidationList = dataValidationList;
1598
+ const conditionalFormatting = conditionalFormattings.findByCell(cell);
1599
+ if (conditionalFormatting !== null) cell.inConditionalFormatting = conditionalFormatting;
1600
+ const extension = conditionalFormattings.findByCell(cell);
1601
+ if (extension !== null) cell.inExtensionList = extension;
1602
+ const extensionListSource = extensionLists.findListByCell(name, cell);
1603
+ if (extensionListSource !== null) cell.inExtensionListSource = extensionListSource;
1604
+ return cell;
1605
+ });
1606
+ const replaceCells = (element) => {
1607
+ xml.querySelector("sheetData")?.replaceWith(element);
1608
+ };
1609
+ const cleanUp = () => {
1610
+ const hyperlinks2 = xml.querySelector("hyperlinks");
1611
+ if (hyperlinks2?.childNodes.length === 0) hyperlinks2.remove();
1612
+ const mergeCells = xml.querySelector("mergeCells");
1613
+ if (mergeCells?.childNodes.length === 0) mergeCells.remove();
1614
+ };
1615
+ const save = () => {
1616
+ cleanUp();
1617
+ const newData = serializeXml(xml);
1618
+ relations.save();
1619
+ xlsx.file(filename, newData);
1620
+ };
1621
+ return {
1622
+ xml,
1623
+ sheetId: id,
1624
+ relations,
1625
+ target,
1626
+ id,
1627
+ name,
1628
+ cells,
1629
+ mergedCells,
1630
+ hyperlinks,
1631
+ comments,
1632
+ dataValidations,
1633
+ extensionLists,
1634
+ conditionalFormattings,
1635
+ drawings,
1636
+ oldComments,
1637
+ tables,
1638
+ replaceCells,
1639
+ save
1640
+ };
1641
+ };
1642
+
1643
+ // src/functions/get-relations.ts
1644
+ var getRelations = async ({ xlsx, filename }) => {
1645
+ let isDirty = false;
1646
+ const relFilename = `xl/${filename}`;
1647
+ const xmlText = await xlsx.file(relFilename)?.async("string");
1648
+ const xml = xmlText === void 0 ? createXml({ specificSchema: NS_REL, tagName: "Relationships" }) : parser.parseFromString(xmlText, "application/xml");
1649
+ if (xmlText === void 0) isDirty = true;
1650
+ if (xml.getElementsByTagName("parsererror").length > 0) {
1651
+ throw new Error(`Invalid relations file for ${filename}`);
1652
+ }
1653
+ const realationsXml = Array.from(xml.querySelectorAll("Relationship"));
1654
+ let relations = [];
1655
+ for (const el of realationsXml) {
1656
+ const id = el.getAttribute("Id");
1657
+ const target = el.getAttribute("Target");
1658
+ const elementType = el.getAttribute("Type");
1659
+ const targetMode = el.getAttribute("TargetMode");
1660
+ if (id === null || target === null || elementType === null) continue;
1661
+ const realType = elementType.slice(elementType.lastIndexOf("/") + 1);
1662
+ relations.push({
1663
+ id,
1664
+ target,
1665
+ type: elementType,
1666
+ realType,
1667
+ ...targetMode === null ? {} : { targetMode }
1668
+ });
1669
+ }
1670
+ const getNextId = () => {
1671
+ let max = 0;
1672
+ xml.querySelectorAll("Relationship[Id^='rId']").forEach((el) => {
1673
+ const n = Number((el.getAttribute("Id") ?? "Id1").slice(3));
1674
+ if (!Number.isNaN(n)) max = Math.max(max, n);
1675
+ });
1676
+ return `rId${max + 1}`;
1677
+ };
1678
+ const add = (rel) => {
1679
+ const id = getNextId();
1680
+ const root = xml.querySelector("Relationships");
1681
+ if (root === null) return "NA";
1682
+ const el = xml.createElementNS(NS_REL, "Relationship");
1683
+ el.setAttribute("Id", id);
1684
+ el.setAttribute("Type", rel.type);
1685
+ el.setAttribute("Target", rel.target);
1686
+ if (rel.targetMode !== void 0) el.setAttribute("TargetMode", rel.targetMode);
1687
+ root.appendChild(el);
1688
+ relations.push({ id, type: rel.type, target: rel.target });
1689
+ isDirty = true;
1690
+ return id;
1691
+ };
1692
+ const get = ({ by, value }) => {
1693
+ return relations.filter((relation) => relation[by] === value);
1694
+ };
1695
+ const remove = ({ by, value }) => {
1696
+ const valuesToRemove = relations.filter((relation) => relation[by] === value);
1697
+ for (const value2 of valuesToRemove) {
1698
+ xml.querySelector(`Relationship[id="${String(value2)}"]`)?.remove();
1699
+ }
1700
+ relations = relations.filter((relation) => relation[by] !== value);
1701
+ };
1702
+ const save = () => {
1703
+ if (!isDirty) return;
1704
+ const newData = serializeXml(xml);
1705
+ xlsx.file(relFilename, newData);
1706
+ };
1707
+ const getElements = (element) => {
1708
+ return relations.filter((el) => el.realType === element);
1709
+ };
1710
+ return {
1711
+ add,
1712
+ remove,
1713
+ get,
1714
+ save,
1715
+ getElements
1716
+ };
1717
+ };
1718
+
1719
+ // src/functions/get-pivot-tables.ts
1720
+ var serializer4 = new XMLSerializer();
1721
+ var getPivotTables = async (xlsx) => {
1722
+ const pivotTables = /* @__PURE__ */ new Map();
1723
+ const pivotFiles = Object.keys(xlsx.files).filter((f) => f.startsWith("xl/pivotTables/") && f.endsWith(".xml")).map((f) => ({
1724
+ definitionFilename: f,
1725
+ relsFilename: f.replace("/pivotTables", "/pivotTables/_rels") + ".rels"
1726
+ }));
1727
+ for (const { definitionFilename, relsFilename } of pivotFiles) {
1728
+ const tableDefinitionText = await xlsx.file(definitionFilename)?.async("string");
1729
+ if (tableDefinitionText === void 0) continue;
1730
+ const tableDefinitionXml = parser.parseFromString(tableDefinitionText, "application/xml");
1731
+ const tableDefinition = tableDefinitionXml.querySelector("pivotTableDefinition");
1732
+ if (tableDefinition === null) continue;
1733
+ const xmlRelsText = await xlsx.file(relsFilename)?.async("string");
1734
+ if (xmlRelsText === void 0) continue;
1735
+ const rels = parser.parseFromString(xmlRelsText, "application/xml");
1736
+ const cacheTarget = rels.querySelector(`Relationship[Type="${NS_PREFIX}/relationships/pivotCacheDefinition"]`)?.getAttribute("Target");
1737
+ if (cacheTarget === void 0 || cacheTarget === null) continue;
1738
+ const cacheFile = cacheTarget.replace("../", "xl/");
1739
+ const cacheText = await xlsx.file(cacheFile)?.async("string");
1740
+ if (cacheText === void 0) continue;
1741
+ const cache = parser.parseFromString(cacheText, "application/xml");
1742
+ const workSheetSource = cache.querySelector("worksheetSource");
1743
+ if (workSheetSource === null) continue;
1744
+ const ref = workSheetSource.getAttribute("ref");
1745
+ const sheet = workSheetSource.getAttribute("sheet");
1746
+ if (ref === null || sheet === null) continue;
1747
+ const range = parseCellRange(ref);
1748
+ const extension = {
1749
+ rows: 0,
1750
+ cols: 0
1751
+ };
1752
+ const extend = (cell) => {
1753
+ if (Array.isArray(cell.newValue)) {
1754
+ const length = cell.newValue.length - 1;
1755
+ if ("$isTable" in cell.newValue) {
1756
+ extension.rows = Math.max(length, extension.rows);
1757
+ } else {
1758
+ extension.cols = Math.max(length, extension.cols);
1759
+ }
1760
+ }
1761
+ };
1762
+ const save2 = () => {
1763
+ tableDefinition.setAttribute("refreshOnLoad", "1");
1764
+ tableDefinition.setAttribute("saveData", "0");
1765
+ const cacheDefinition = cache.querySelector("pivotCacheDefinition");
1766
+ if (cacheDefinition !== null) {
1767
+ cacheDefinition.setAttribute("refreshOnLoad", "1");
1768
+ cacheDefinition.setAttribute("saveData", "0");
1769
+ cacheDefinition.setAttribute("invalid", "1");
1770
+ cacheDefinition.setAttribute("recordCount", "1");
1771
+ }
1772
+ const newDefinition = serializer4.serializeToString(tableDefinition);
1773
+ xlsx.file(definitionFilename, newDefinition);
1774
+ range.columnEnd = range.columnEnd + extension.cols;
1775
+ range.rowEnd = range.rowEnd + extension.rows;
1776
+ const ref2 = toCellRange(range);
1777
+ workSheetSource.setAttribute("ref", ref2);
1778
+ const newCache = serializer4.serializeToString(cache);
1779
+ xlsx.file(cacheFile, newCache);
1780
+ };
1781
+ const pivotTable = {
1782
+ ref,
1783
+ sheet,
1784
+ range,
1785
+ definitionXml: tableDefinitionXml,
1786
+ cacheXml: cache,
1787
+ extension,
1788
+ extend,
1789
+ save: save2
1790
+ };
1791
+ pivotTables.set(definitionFilename, pivotTable);
1792
+ }
1793
+ const get = (tableName) => {
1794
+ return pivotTables.get(tableName);
1795
+ };
1796
+ const save = () => {
1797
+ for (const table of pivotTables.values()) {
1798
+ table.save();
1799
+ }
1800
+ };
1801
+ const findByCell = (sheet, cell) => {
1802
+ for (const [name, value] of pivotTables) {
1803
+ if (value.sheet !== sheet) continue;
1804
+ if (isInRange(cell, value.range)) return name;
1805
+ }
1806
+ return null;
1807
+ };
1808
+ return {
1809
+ get,
1810
+ save,
1811
+ findByCell
1812
+ };
1813
+ };
1814
+
1815
+ // src/functions/get-formulas.ts
1816
+ var sheetRegex = "(?:(?<sheet>(?:'(?:[^'!]|'')+'|[^'!()+/*-]+))!)";
1817
+ var completeRefRegex = "\\$?[A-Z]+\\$?[0-9]+(?::\\$?[A-Z]+\\$?[0-9]+)?";
1818
+ var partialRefRegex = "\\$?[A-Z]+(?::\\$?[A-Z]+)|\\$?[0-9]+(?::\\$?[0-9]+)";
1819
+ var re = new RegExp(`(?<![A-Z0-9])(?:${sheetRegex})?(?<ref>${completeRefRegex}|${partialRefRegex})(?![A-Z0-9])`, "g");
1820
+ var getFormulasFromReferences = (s) => [...s.matchAll(re)].map((m) => ({
1821
+ sheet: m.groups?.sheet?.replace(/^'|'$/g, "").replace(/''/g, "'"),
1822
+ ref: m.groups?.ref
1823
+ }));
1824
+
1825
+ // src/functions/get-data-validations.ts
1826
+ var createDataValidation = (doc, dataValidation) => {
1827
+ if (dataValidation.sqref === void 0) throw new Error("The validation area is not defined");
1828
+ if (dataValidation.formula === void 0) throw new Error("The data validation formula is not defined");
1829
+ const dataValidationTag = doc.createElementNS(NS, "dataValidation");
1830
+ const formulaTag = doc.createElementNS(NS, "formula1");
1831
+ dataValidationTag.setAttribute("allowBlank", "1");
1832
+ dataValidationTag.setAttribute("showInputMessage", "1");
1833
+ dataValidationTag.setAttribute("showErrorMessage", "1");
1834
+ dataValidationTag.setAttribute("sqref", dataValidation.sqref);
1835
+ dataValidationTag.setAttribute("type", dataValidation.type);
1836
+ formulaTag.textContent = dataValidation.formula;
1837
+ dataValidationTag.appendChild(formulaTag);
1838
+ return dataValidationTag;
1839
+ };
1840
+ var getDataValidations = (sheets) => {
1841
+ const dataValidations = /* @__PURE__ */ new Map();
1842
+ let index = -1;
1843
+ for (const sheet of sheets) {
1844
+ const dataValidationsParent = sheet.xml.querySelector("dataValidations");
1845
+ if (dataValidationsParent === null) continue;
1846
+ const dataValidationsXml = dataValidationsParent.querySelectorAll("dataValidation");
1847
+ for (const dataValidation of dataValidationsXml) {
1848
+ const sheetName = sheet.name;
1849
+ const extension = { rows: 0, cols: 0 };
1850
+ const formulaExtension = { rows: 0, cols: 0 };
1851
+ const sqref = dataValidation.getAttribute("sqref");
1852
+ const formula = dataValidation.querySelector("formula1")?.textContent;
1853
+ if (sqref === null) continue;
1854
+ index++;
1855
+ const range = parseCellRange(sqref);
1856
+ const validation = {
1857
+ sheet: sheetName,
1858
+ sqref,
1859
+ range,
1860
+ xml: dataValidation
1861
+ };
1862
+ if (formula !== void 0) {
1863
+ validation.formula = formula;
1864
+ const references = getFormulasFromReferences(formula);
1865
+ if (references.length !== 1) continue;
1866
+ const reference = references[0];
1867
+ if (reference.ref === void 0) continue;
1868
+ validation.listRange = {
1869
+ ref: reference.ref,
1870
+ sheet: reference.sheet ?? sheetName
1871
+ };
1872
+ if (reference.ref === formula) {
1873
+ validation.listRange.range = parseCellRange(reference.ref);
1874
+ }
1875
+ }
1876
+ const extend = (cell) => {
1877
+ if (Array.isArray(cell.newValue)) {
1878
+ const length = cell.newValue.length - 1;
1879
+ if ("$isTable" in cell.newValue) {
1880
+ extension.rows = Math.max(length, extension.rows);
1881
+ } else {
1882
+ extension.cols = Math.max(length, extension.cols);
1883
+ }
1884
+ }
1885
+ };
1886
+ const extendFormula = (cell) => {
1887
+ if (Array.isArray(cell.newValue)) {
1888
+ const length = cell.newValue.length - 1;
1889
+ if ("$isTable" in cell.newValue) {
1890
+ formulaExtension.rows = Math.max(length, formulaExtension.rows);
1891
+ } else {
1892
+ formulaExtension.cols = Math.max(length, formulaExtension.cols);
1893
+ }
1894
+ }
1895
+ };
1896
+ validation.extension = extension;
1897
+ validation.formulaExtension = formulaExtension;
1898
+ validation.extend = extend;
1899
+ validation.extendFormula = extendFormula;
1900
+ dataValidations.set(String(index), validation);
1901
+ }
1902
+ }
1903
+ const save = () => {
1904
+ for (const dataValidation of dataValidations.values()) {
1905
+ dataValidation.range.columnEnd = dataValidation.range.columnEnd + dataValidation.extension.cols;
1906
+ dataValidation.range.rowEnd = dataValidation.range.rowEnd + dataValidation.extension.rows;
1907
+ const ref = toCellRange(dataValidation.range);
1908
+ dataValidation.xml.setAttribute("sqref", ref);
1909
+ if (dataValidation.listRange?.range !== void 0) {
1910
+ dataValidation.listRange.range.columnEnd = dataValidation.listRange.range.columnEnd + dataValidation.formulaExtension.cols;
1911
+ dataValidation.listRange.range.rowEnd = dataValidation.listRange.range.rowEnd + dataValidation.formulaExtension.rows;
1912
+ const listRef = toCellRange(dataValidation.listRange.range);
1913
+ const formula = dataValidation.xml.querySelector("formula1");
1914
+ if (formula !== null) {
1915
+ formula.textContent = listRef;
1916
+ }
1917
+ }
1918
+ }
1919
+ };
1920
+ const add = (xml, dataValidation) => {
1921
+ if (dataValidation.sqref === void 0 && dataValidation.range === void 0) {
1922
+ throw new Error("The validation area is not defined");
1923
+ }
1924
+ if (dataValidation.type === "formula" && dataValidation.formula === void 0) {
1925
+ throw new Error("Data validation formula is not defined");
1926
+ }
1927
+ if (dataValidation.type === "list" && dataValidation.formula === void 0 && dataValidation.range === void 0) {
1928
+ throw new Error("Data validation list is not defined");
1929
+ }
1930
+ if (dataValidation.range !== void 0) {
1931
+ dataValidation.sqref = toCellRange(dataValidation.range);
1932
+ }
1933
+ if (dataValidation.formula === void 0) {
1934
+ if (dataValidation.listRange?.range === void 0) {
1935
+ throw new Error("The data validation list is not defined");
1936
+ }
1937
+ dataValidation.formula = toCellRange(dataValidation.listRange.range);
1938
+ }
1939
+ const dataValidationXml = createDataValidation(sheets[0].xml, dataValidation);
1940
+ const dataValidationsTag = xml.querySelector("dataValidations");
1941
+ if (dataValidationsTag === null) {
1942
+ const elements = TAGS_ORDER.slice(TAGS_ORDER.indexOf("dataValidations") + 1);
1943
+ let element = null;
1944
+ for (const elName of elements) {
1945
+ const foundElement = xml.querySelector(elName);
1946
+ if (foundElement !== null) {
1947
+ element = foundElement;
1948
+ break;
1949
+ }
1950
+ }
1951
+ xml.querySelector("worksheet")?.insertBefore(xml.createElementNS(NS, "dataValidations"), element);
1952
+ }
1953
+ xml.querySelector("dataValidations")?.appendChild(dataValidationXml);
1954
+ };
1955
+ const get = (ref) => {
1956
+ return dataValidations.get(ref);
1957
+ };
1958
+ const findByCell = (sheetName, cell) => {
1959
+ for (const [name, value] of dataValidations) {
1960
+ if (sheetName !== value.sheet) continue;
1961
+ if (isInRange(cell, value.range)) return name;
1962
+ }
1963
+ return null;
1964
+ };
1965
+ const findListByCell = (sheetName, cell) => {
1966
+ for (const [name, value] of dataValidations) {
1967
+ if (value.listRange?.range === void 0) continue;
1968
+ if (value.listRange.sheet !== sheetName) continue;
1969
+ if (isInRange(cell, value.listRange.range)) return name;
1970
+ }
1971
+ return null;
1972
+ };
1973
+ return {
1974
+ add,
1975
+ save,
1976
+ get,
1977
+ findByCell,
1978
+ findListByCell
1979
+ };
1980
+ };
1981
+
1982
+ // src/functions/get-extension-lists.ts
1983
+ var getExtensionLists = (sheets) => {
1984
+ const extensions = /* @__PURE__ */ new Map();
1985
+ let index = -1;
1986
+ for (const sheet of sheets) {
1987
+ const extenstionListsParent = sheet.xml.querySelector("extLst dataValidations") ?? sheet.xml.createElementNS(NS, "dataValidations");
1988
+ const extenstionListsXml = extenstionListsParent.querySelectorAll("dataValidation");
1989
+ for (const extensionList of extenstionListsXml) {
1990
+ const sheetName = sheet.name;
1991
+ const extension = {
1992
+ rows: 0,
1993
+ cols: 0
1994
+ };
1995
+ const formulaExtension = {
1996
+ rows: 0,
1997
+ cols: 0
1998
+ };
1999
+ const sqref = extensionList.querySelector("sqref")?.textContent;
2000
+ const formula = extensionList.querySelector("formula1 f")?.textContent;
2001
+ if (sqref === void 0 || formula === void 0) continue;
2002
+ index++;
2003
+ const range = parseCellRange(sqref);
2004
+ const currentExtension = {
2005
+ xml: extensionList,
2006
+ sheet: sheetName,
2007
+ sqref,
2008
+ range
2009
+ };
2010
+ if (formula !== void 0) {
2011
+ currentExtension.formula = formula;
2012
+ const references = getFormulasFromReferences(formula);
2013
+ if (references.length !== 1) continue;
2014
+ const reference = references[0];
2015
+ if (reference.ref === void 0) continue;
2016
+ currentExtension.listRange = {
2017
+ ref: reference.ref,
2018
+ sheet: reference.sheet ?? sheetName
2019
+ };
2020
+ if (`${String(reference.sheet)}!${String(reference.ref)}` === formula) {
2021
+ currentExtension.listRange.range = parseCellRange(reference.ref);
2022
+ }
2023
+ }
2024
+ const extend = (cell) => {
2025
+ if (Array.isArray(cell.newValue)) {
2026
+ const length = cell.newValue.length - 1;
2027
+ if ("$isTable" in cell.newValue) {
2028
+ extension.rows = Math.max(length, extension.rows);
2029
+ } else {
2030
+ extension.cols = Math.max(length, extension.cols);
2031
+ }
2032
+ }
2033
+ };
2034
+ const extendFormula = (cell) => {
2035
+ if (Array.isArray(cell.newValue)) {
2036
+ const length = cell.newValue.length - 1;
2037
+ if ("$isTable" in cell.newValue) {
2038
+ formulaExtension.rows = Math.max(length, formulaExtension.rows);
2039
+ } else {
2040
+ formulaExtension.cols = Math.max(length, formulaExtension.cols);
2041
+ }
2042
+ }
2043
+ };
2044
+ currentExtension.extension = extension;
2045
+ currentExtension.formulaExtension = formulaExtension;
2046
+ currentExtension.extend = extend;
2047
+ currentExtension.extendFormula = extendFormula;
2048
+ extensions.set(String(index), currentExtension);
2049
+ }
2050
+ }
2051
+ const save = () => {
2052
+ for (const extension of extensions.values()) {
2053
+ extension.range.columnEnd = extension.range.columnEnd + extension.extension.cols;
2054
+ extension.range.rowEnd = extension.range.rowEnd + extension.extension.rows;
2055
+ const ref = toCellRange(extension.range);
2056
+ const sqref = extension.xml.querySelector("sqref");
2057
+ if (sqref !== null) sqref.textContent = ref;
2058
+ extension.xml.setAttribute("sqref", ref);
2059
+ if (extension.listRange?.range !== void 0) {
2060
+ extension.listRange.range.columnEnd = extension.listRange.range.columnEnd + extension.formulaExtension.cols;
2061
+ extension.listRange.range.rowEnd = extension.listRange.range.rowEnd + extension.formulaExtension.rows;
2062
+ const listRef = toCellRange(extension.listRange.range);
2063
+ const formula = extension.xml.querySelector("formula1 f");
2064
+ if (formula !== null) {
2065
+ formula.textContent = String(extension.listRange.sheet) + "!" + listRef;
2066
+ }
2067
+ }
2068
+ }
2069
+ };
2070
+ const get = (ref) => {
2071
+ return extensions.get(ref);
2072
+ };
2073
+ const findByCell = (cell) => {
2074
+ for (const [name, value] of extensions) {
2075
+ if (isInRange(cell, value.range)) return name;
2076
+ }
2077
+ return null;
2078
+ };
2079
+ const findListByCell = (sheetName, cell) => {
2080
+ for (const [name, value] of extensions) {
2081
+ if (value.listRange?.range === void 0) continue;
2082
+ if (value.listRange.sheet !== sheetName) continue;
2083
+ if (isInRange(cell, value.listRange.range)) return name;
2084
+ }
2085
+ return null;
2086
+ };
2087
+ return {
2088
+ save,
2089
+ get,
2090
+ findByCell,
2091
+ findListByCell
2092
+ };
2093
+ };
2094
+
2095
+ // src/functions/get-persons.ts
2096
+ var addPerson = (doc, person) => {
2097
+ const el = doc.createElementNS(NS_TC, "person");
2098
+ el.setAttribute("displayName", person.displayName);
2099
+ el.setAttribute("id", person.id);
2100
+ el.setAttribute("userId", person.userId);
2101
+ el.setAttribute("providerId", person.providerId);
2102
+ doc.documentElement.appendChild(el);
2103
+ };
2104
+ var getPersons = async (xlsx, target) => {
2105
+ let isDirty = false;
2106
+ const xmlText = await xlsx.file(`xl/${target}`)?.async("string");
2107
+ if (xmlText === void 0) isDirty = true;
2108
+ const xml = xmlText === void 0 ? createXml({ coreSchema: NS, specificSchema: NS_TC, tagName: "personList" }) : parser.parseFromString(xmlText, "application/xml");
2109
+ if (xml.getElementsByTagName("parsererror").length > 0) {
2110
+ throw new Error("Invalid person.xml");
2111
+ }
2112
+ const getSystemUserId = () => {
2113
+ const existing = Array.from(xml.getElementsByTagNameNS(NS_TC, "person")).find((p) => p.getAttribute("displayName") === "System")?.getAttribute("id");
2114
+ if (existing !== null && existing !== void 0) return existing;
2115
+ const systemPerson = {
2116
+ id: `{${generateUUID().toUpperCase()}}`,
2117
+ displayName: "System",
2118
+ userId: "S::system@local::00000000-0000-0000-0000-000000000000",
2119
+ providerId: "AD"
2120
+ };
2121
+ addPerson(xml, systemPerson);
2122
+ isDirty = true;
2123
+ return systemPerson.id;
2124
+ };
2125
+ const save = () => {
2126
+ if (!isDirty) return;
2127
+ const newData = serializeXml(xml);
2128
+ xlsx.file(`xl/${target}`, newData);
2129
+ };
2130
+ return {
2131
+ getSystemUserId,
2132
+ save
2133
+ };
2134
+ };
2135
+
2136
+ // src/functions/get-workbook.ts
2137
+ var getWorkbook = async (xlsx) => {
2138
+ const relsXmlText = await xlsx.file(GLOBAL_RELS)?.async("string");
2139
+ if (relsXmlText === void 0) throw new Error("This Excel file has no relations");
2140
+ const relsXml = parser.parseFromString(relsXmlText, "application/xml");
2141
+ const workBookElement = relsXml.querySelector(`Relationship[Type="${OFFICE_DOCUMENT_TYPE}/officeDocument"]`);
2142
+ if (workBookElement === null) throw new Error("This Excel file has no workbook");
2143
+ const workbookPath = workBookElement.getAttribute("Target");
2144
+ if (workbookPath === null) throw new Error("This Excel file has no workbook");
2145
+ const workbookXmlText = await xlsx.file(workbookPath)?.async("string");
2146
+ if (workbookXmlText === void 0) throw new Error("This Excel file has no workbook");
2147
+ const workbookXml = parser.parseFromString(workbookXmlText, "application/xml");
2148
+ const sheetsParent = workbookXml.querySelector("sheets");
2149
+ if (sheetsParent === null) throw new Error("This Excel file has no sheets");
2150
+ const sheetsXml = Array.from(sheetsParent.children);
2151
+ const sheets = sheetsXml.map((el) => ({
2152
+ id: el.getAttribute("r:id") ?? void 0,
2153
+ name: el.getAttribute("name") ?? void 0,
2154
+ sheetId: el.getAttribute("sheetId") ?? void 0
2155
+ }));
2156
+ const relationsPath = getRelationsPath(workbookPath);
2157
+ const relations = await getRelations({ xlsx, filename: relationsPath });
2158
+ const workbook = {};
2159
+ workbook.relations = relations;
2160
+ const styleProps = relations.getElements("styles")[0];
2161
+ if (styleProps === void 0) throw new Error("Styles are mailformed");
2162
+ const styles = await createStyles(xlsx, styleProps.target);
2163
+ workbook.styles = styles;
2164
+ const sharedStringsProps = relations.getElements("sharedStrings")[0];
2165
+ if (sharedStringsProps === void 0) throw new Error("This file does not contain strings");
2166
+ const sharedStrings = await getSharedStrings(xlsx, sharedStringsProps.target);
2167
+ workbook.sharedStrings = sharedStrings;
2168
+ const persons = await getPersons(xlsx, "persons/person.xml");
2169
+ workbook.persons = persons;
2170
+ const pivotTables = await getPivotTables(xlsx);
2171
+ workbook.pivotTables = pivotTables;
2172
+ const sheetsRels = relations.getElements("worksheet");
2173
+ const sheetsData = await Promise.all(sheetsRels.map(async (s) => {
2174
+ if (s?.target === void 0 || s?.id === void 0) throw new Error("Excel file is mailformed");
2175
+ const currentSheet = sheets.find((sheet2) => sheet2.id === s.id);
2176
+ if (currentSheet === void 0 || currentSheet.name === void 0) throw new Error("Excel file is mailformed");
2177
+ const relsTarget = s.target.replace(/worksheets\//, "worksheets/_rels/") + ".rels";
2178
+ const relations2 = await getRelations({ xlsx, filename: relsTarget });
2179
+ const sheetXmlText = await xlsx.file(`xl/${s.target}`)?.async("string");
2180
+ if (sheetXmlText === void 0) throw new Error(`${s.target} sheet is mailformed`);
2181
+ const xml = parser.parseFromString(sheetXmlText, "application/xml");
2182
+ const sheet = {
2183
+ xml,
2184
+ relations: relations2,
2185
+ target: s.target,
2186
+ id: s.id,
2187
+ name: currentSheet.name
2188
+ };
2189
+ Object.assign(currentSheet, sheet);
2190
+ return sheet;
2191
+ }));
2192
+ const dataValidations = getDataValidations(sheetsData);
2193
+ workbook.dataValidations = dataValidations;
2194
+ const extensionLists = getExtensionLists(sheetsData);
2195
+ workbook.extensionLists = extensionLists;
2196
+ const enrichedSheets = await Promise.all(sheetsData.map(async (sheet) => await getSheet({
2197
+ xlsx,
2198
+ workbook,
2199
+ ...sheet
2200
+ })));
2201
+ workbook.sheets = enrichedSheets;
2202
+ workbook.save = () => {
2203
+ const calcChain = relations.get({ by: "realType", value: "calcChain" });
2204
+ for (const { target } of calcChain) {
2205
+ xlsx.remove(`xl/${target}`);
2206
+ }
2207
+ relations.remove({ by: "realType", value: "calcChain" });
2208
+ sharedStrings.save();
2209
+ styles.save();
2210
+ };
2211
+ return workbook;
2212
+ };
2213
+
2214
+ // src/functions/generate-file.ts
2215
+ var generateTemplate = async (buffer, data) => {
2216
+ const xlsx = await readXlsx(buffer);
2217
+ const workbook = await getWorkbook(xlsx);
2218
+ const dataStore = createDataStore(data);
2219
+ await processWorksheets({
2220
+ workbook,
2221
+ dataStore
2222
+ });
2223
+ workbook.save();
2224
+ workbook.persons.save();
2225
+ const file = await xlsx.generateAsync({ type: "blob" });
2226
+ return file;
2227
+ };
2228
+
2229
+ // src/index.ts
2230
+ var generateXlsx = async (template, data) => {
2231
+ if (template === void 0 || data === void 0) throw Error("No template or data provided");
2232
+ if (typeof template === "string") {
2233
+ if (!/^https|ftp?:/.test(template)) throw new Error("Template URL is invalid");
2234
+ const response = await fetchFile(template);
2235
+ const fileContent = await generateTemplate(response, data);
2236
+ return fileContent;
2237
+ }
2238
+ return await generateTemplate(template, data);
2239
+ };
2240
+ var downloadXlsx = async (template, data, fileName, mimeType) => {
2241
+ if (fileName === void 0) fileName = (/* @__PURE__ */ new Date()).toLocaleDateString("sv") + " - Report.xlsx";
2242
+ const fileContent = await generateXlsx(template, data);
2243
+ downloadFile(fileContent, fileName, mimeType);
2244
+ };
2245
+ export {
2246
+ downloadXlsx,
2247
+ generateXlsx
2248
+ };
2249
+ //# sourceMappingURL=index.js.map