@justybase/spreadsheet-tasks 2.2.0 → 3.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.
- package/CHANGELOG.md +64 -0
- package/browser/browser-spreadsheet.js +131 -43
- package/browser/justybase-spreadsheet.min.js +13 -13
- package/dist/cjs/index.d.ts +107 -5
- package/dist/cjs/index.js +1299 -431
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +1284 -431
- package/dist/esm/index.js.map +1 -1
- package/docs/API.md +66 -1
- package/package.json +4 -3
package/dist/cjs/index.js
CHANGED
|
@@ -35,6 +35,9 @@ __export(index_exports, {
|
|
|
35
35
|
ExcelReaderAbstract: () => ExcelReaderAbstract,
|
|
36
36
|
F: () => F,
|
|
37
37
|
INVALID_SHEET_NAME_CHARS: () => INVALID_SHEET_NAME_CHARS,
|
|
38
|
+
MAX_CELL_CHARS: () => MAX_CELL_CHARS,
|
|
39
|
+
MAX_COLS: () => MAX_COLS,
|
|
40
|
+
MAX_ROWS: () => MAX_ROWS,
|
|
38
41
|
ReaderFactory: () => ReaderFactory,
|
|
39
42
|
StreamingSheetState: () => StreamingSheetState,
|
|
40
43
|
XlsbReader: () => XlsbReader,
|
|
@@ -45,13 +48,25 @@ __export(index_exports, {
|
|
|
45
48
|
XlsxUpdater: () => XlsxUpdater,
|
|
46
49
|
XlsxWriter: () => XlsxWriter,
|
|
47
50
|
applyHeaderWidths: () => applyHeaderWidths,
|
|
51
|
+
assertCellTextLength: () => assertCellTextLength,
|
|
52
|
+
assertColumnCount: () => assertColumnCount,
|
|
53
|
+
assertRowCount: () => assertRowCount,
|
|
54
|
+
assertRowFitsColumns: () => assertRowFitsColumns,
|
|
55
|
+
columnIndexToLetter: () => columnIndexToLetter,
|
|
56
|
+
columnLetterToIndex: () => columnLetterToIndex,
|
|
48
57
|
convertXlsbToXlsx: () => convertXlsbToXlsx,
|
|
49
58
|
convertXlsxToXlsb: () => convertXlsxToXlsb,
|
|
50
59
|
defaultColWidth: () => defaultColWidth,
|
|
60
|
+
escapeXmlText: () => escapeXmlText,
|
|
51
61
|
getFormat: () => getFormat,
|
|
52
62
|
initColWidths: () => initColWidths,
|
|
53
63
|
isFormattedCell: () => isFormattedCell,
|
|
64
|
+
parseSharedStringsXml: () => parseSharedStringsXml,
|
|
65
|
+
parseTagAttributes: () => parseTagAttributes,
|
|
66
|
+
resolveRelTarget: () => resolveRelTarget,
|
|
54
67
|
sanitizeSheetName: () => sanitizeSheetName,
|
|
68
|
+
trimTrailingEmptyRows: () => trimTrailingEmptyRows,
|
|
69
|
+
unescapeXml: () => unescapeXml,
|
|
55
70
|
unwrapCell: () => unwrapCell,
|
|
56
71
|
updateColWidthsFromRows: () => updateColWidthsFromRows
|
|
57
72
|
});
|
|
@@ -171,6 +186,143 @@ var BigBuffer = class {
|
|
|
171
186
|
}
|
|
172
187
|
};
|
|
173
188
|
|
|
189
|
+
// src/Formats.ts
|
|
190
|
+
var F = {
|
|
191
|
+
THOUSANDS_SEP: "#,##0",
|
|
192
|
+
CURRENCY_PLN: '#,##0.00 "z\u0142"',
|
|
193
|
+
CURRENCY_EUR: "#,##0.00 \u20AC",
|
|
194
|
+
PERCENTAGE: "0%",
|
|
195
|
+
SCIENTIFIC: "0.00E+00",
|
|
196
|
+
TWO_DECIMALS: "#,##0.00",
|
|
197
|
+
TEXT: "@",
|
|
198
|
+
LEADING_ZEROS: "000000000",
|
|
199
|
+
DATE_SHORT: "dd.mm.yyyy",
|
|
200
|
+
DATE_LONG: "d mmmm yyyy",
|
|
201
|
+
DATE_DAY_MONTH_YEAR: "dd-mm-yyyy",
|
|
202
|
+
DATE_ISO: "yyyy-mm-dd",
|
|
203
|
+
DATE_MONTH_YEAR: "mmmm yyyy",
|
|
204
|
+
DATE_WEEKDAY: "dddd, d mmmm yyyy",
|
|
205
|
+
DATE_DAY_MONTH: "d mmmm",
|
|
206
|
+
DATE_YEAR_ONLY: "yyyy",
|
|
207
|
+
DATETIME_SHORT: "dd.mm.yyyy hh:mm",
|
|
208
|
+
DATETIME_LONG: "d mmmm yyyy hh:mm:ss",
|
|
209
|
+
TIME_HH_MM: "hh:mm",
|
|
210
|
+
TIME_HH_MM_SS: "hh:mm:ss",
|
|
211
|
+
TIME_12H: "h:mm AM/PM",
|
|
212
|
+
DATETIME_24H: "dd.mm.yyyy hh:mm:ss",
|
|
213
|
+
DATETIME_ISO: 'yyyy-mm-dd"T"hh:mm:ss',
|
|
214
|
+
TIME_MS: "hh:mm:ss.000"
|
|
215
|
+
};
|
|
216
|
+
function isFormattedCell(val) {
|
|
217
|
+
return val !== null && val !== void 0 && typeof val === "object" && "format" in val && typeof val.format === "string";
|
|
218
|
+
}
|
|
219
|
+
function unwrapCell(val) {
|
|
220
|
+
if (isFormattedCell(val)) {
|
|
221
|
+
return val.value;
|
|
222
|
+
}
|
|
223
|
+
return val;
|
|
224
|
+
}
|
|
225
|
+
function getFormat(val) {
|
|
226
|
+
if (isFormattedCell(val)) {
|
|
227
|
+
return val.format;
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// src/writerHelpers.ts
|
|
233
|
+
var OA_EPOCH_1900 = Date.UTC(1899, 11, 30);
|
|
234
|
+
var MARCH_1_1900 = Date.UTC(1900, 2, 1);
|
|
235
|
+
function oaDateToTime(oaDate, epochMs) {
|
|
236
|
+
let serial = oaDate;
|
|
237
|
+
if (epochMs === OA_EPOCH_1900 && serial >= 1 && serial < 60) {
|
|
238
|
+
serial += 1;
|
|
239
|
+
}
|
|
240
|
+
return Math.round(serial * 864e5) + epochMs;
|
|
241
|
+
}
|
|
242
|
+
function timeToOaDate(timeMs, epochMs) {
|
|
243
|
+
let serial = (timeMs - epochMs) / 864e5;
|
|
244
|
+
if (epochMs === OA_EPOCH_1900 && timeMs < MARCH_1_1900) {
|
|
245
|
+
serial -= 1;
|
|
246
|
+
}
|
|
247
|
+
return serial;
|
|
248
|
+
}
|
|
249
|
+
var INVALID_SHEET_NAME_CHARS = /[\\/*?[\]:]/g;
|
|
250
|
+
function sanitizeSheetName(name, sheetCount) {
|
|
251
|
+
if (!name || typeof name !== "string") {
|
|
252
|
+
return `Sheet${sheetCount + 1}`;
|
|
253
|
+
}
|
|
254
|
+
let sanitized = name.replace(INVALID_SHEET_NAME_CHARS, "_");
|
|
255
|
+
if (sanitized.length > 31) {
|
|
256
|
+
sanitized = sanitized.substring(0, 31);
|
|
257
|
+
}
|
|
258
|
+
if (sanitized.trim().length === 0) {
|
|
259
|
+
sanitized = `Sheet${sheetCount + 1}`;
|
|
260
|
+
}
|
|
261
|
+
return sanitized;
|
|
262
|
+
}
|
|
263
|
+
var MAX_ROWS = 1048576;
|
|
264
|
+
var MAX_COLS = 16384;
|
|
265
|
+
var MAX_CELL_CHARS = 32767;
|
|
266
|
+
function assertCellTextLength(text) {
|
|
267
|
+
if (text.length > MAX_CELL_CHARS) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
`Excel cell text exceeds ${MAX_CELL_CHARS} UTF-16 code units (got ${text.length}).`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function assertColumnCount(columnCount) {
|
|
274
|
+
if (!Number.isInteger(columnCount) || columnCount < 1 || columnCount > MAX_COLS) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
`Excel column count must be 1..${MAX_COLS} (got ${columnCount}).`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function assertRowFitsColumns(kind, length, columnCount) {
|
|
281
|
+
if (length > columnCount) {
|
|
282
|
+
throw new Error(
|
|
283
|
+
`Excel ${kind} length ${length} exceeds columnCount ${columnCount}.`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function assertRowCount(totalRows) {
|
|
288
|
+
if (totalRows > MAX_ROWS) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
`Excel row count exceeds ${MAX_ROWS} (got ${totalRows}).`
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function initColWidths(columnCount) {
|
|
295
|
+
return new Array(columnCount).fill(-1);
|
|
296
|
+
}
|
|
297
|
+
function applyHeaderWidths(colWidths, headers, columnCount) {
|
|
298
|
+
for (let i = 0; i < columnCount; i++) {
|
|
299
|
+
const len = headers[i] ? headers[i].length + 1 : 0;
|
|
300
|
+
let width = 1.25 * len + 2;
|
|
301
|
+
if (width > 80) width = 80;
|
|
302
|
+
if (colWidths[i] < width) colWidths[i] = width;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function updateColWidthsFromRows(colWidths, rows, sampleLimit = 100) {
|
|
306
|
+
for (let r = 0; r < Math.min(rows.length, sampleLimit); r++) {
|
|
307
|
+
const row = rows[r];
|
|
308
|
+
for (let c = 0; c < row.length; c++) {
|
|
309
|
+
const raw = row[c];
|
|
310
|
+
if (raw === null || raw === void 0) continue;
|
|
311
|
+
const val = isFormattedCell(raw) ? raw.value : raw;
|
|
312
|
+
const len = val instanceof Date ? 20 : String(val).length + 1;
|
|
313
|
+
let width = 1.25 * len + 2;
|
|
314
|
+
if (width > 80) width = 80;
|
|
315
|
+
if (colWidths[c] < width) colWidths[c] = width;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function defaultColWidth(width, floor = false) {
|
|
320
|
+
if (width > 0) {
|
|
321
|
+
return floor ? Math.floor(width) : width;
|
|
322
|
+
}
|
|
323
|
+
return 10;
|
|
324
|
+
}
|
|
325
|
+
|
|
174
326
|
// src/ExcelReaderAbstract.ts
|
|
175
327
|
var ExcelReaderAbstract = class {
|
|
176
328
|
constructor() {
|
|
@@ -185,8 +337,7 @@ var ExcelReaderAbstract = class {
|
|
|
185
337
|
dispose() {
|
|
186
338
|
}
|
|
187
339
|
getDateTimeFromOaDate(oaDate) {
|
|
188
|
-
|
|
189
|
-
return new Date(ms);
|
|
340
|
+
return new Date(oaDateToTime(oaDate, this._oaEpoch));
|
|
190
341
|
}
|
|
191
342
|
};
|
|
192
343
|
|
|
@@ -446,6 +597,208 @@ var BiffReaderWriter = class {
|
|
|
446
597
|
|
|
447
598
|
// src/XlsbReader.ts
|
|
448
599
|
var import_adm_zip = __toESM(require("adm-zip"));
|
|
600
|
+
|
|
601
|
+
// src/xmlUtils.ts
|
|
602
|
+
function hasInvalidControlChars(str) {
|
|
603
|
+
for (let i = 0; i < str.length; i++) {
|
|
604
|
+
const c = str.charCodeAt(i);
|
|
605
|
+
if (c < 32 && c !== 9 && c !== 10 && c !== 13) {
|
|
606
|
+
return true;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
function stripInvalidControlChars(str) {
|
|
612
|
+
let out = "";
|
|
613
|
+
for (let i = 0; i < str.length; i++) {
|
|
614
|
+
const c = str.charCodeAt(i);
|
|
615
|
+
if (c === 9 || c === 10 || c === 13 || c >= 32 && c <= 55295 || c >= 57344 && c <= 65533) {
|
|
616
|
+
out += str[i];
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return out;
|
|
620
|
+
}
|
|
621
|
+
function escapeXmlText(str) {
|
|
622
|
+
if (!/&|<|>|"|'/.test(str) && !hasInvalidControlChars(str)) {
|
|
623
|
+
return str;
|
|
624
|
+
}
|
|
625
|
+
let out = str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
626
|
+
if (hasInvalidControlChars(out)) {
|
|
627
|
+
out = stripInvalidControlChars(out);
|
|
628
|
+
}
|
|
629
|
+
return out;
|
|
630
|
+
}
|
|
631
|
+
function unescapeXml(str) {
|
|
632
|
+
if (!str || str.indexOf("&") === -1) return str;
|
|
633
|
+
return str.replace(/&(?:amp|lt|gt|quot|apos|#\d+|#x[0-9a-fA-F]+);/g, (entity) => {
|
|
634
|
+
switch (entity) {
|
|
635
|
+
case "&":
|
|
636
|
+
return "&";
|
|
637
|
+
case "<":
|
|
638
|
+
return "<";
|
|
639
|
+
case ">":
|
|
640
|
+
return ">";
|
|
641
|
+
case """:
|
|
642
|
+
return '"';
|
|
643
|
+
case "'":
|
|
644
|
+
return "'";
|
|
645
|
+
default: {
|
|
646
|
+
if (entity[1] === "#") {
|
|
647
|
+
const body = entity.slice(2, -1);
|
|
648
|
+
const code = body[0] === "x" || body[0] === "X" ? parseInt(body.slice(1), 16) : parseInt(body, 10);
|
|
649
|
+
if (Number.isFinite(code)) {
|
|
650
|
+
try {
|
|
651
|
+
return String.fromCodePoint(code);
|
|
652
|
+
} catch {
|
|
653
|
+
return entity;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return entity;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
function parseTagAttributes(tag) {
|
|
663
|
+
const attrs = {};
|
|
664
|
+
const re = /([A-Za-z_][\w.:-]*)\s*=\s*("([^"]*)"|'([^']*)')/g;
|
|
665
|
+
let m;
|
|
666
|
+
while ((m = re.exec(tag)) !== null) {
|
|
667
|
+
const name = m[1];
|
|
668
|
+
const value = m[3] !== void 0 ? m[3] : m[4];
|
|
669
|
+
attrs[name] = value;
|
|
670
|
+
const colon = name.indexOf(":");
|
|
671
|
+
if (colon !== -1) {
|
|
672
|
+
const local = name.slice(colon + 1);
|
|
673
|
+
if (!(local in attrs)) attrs[local] = value;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return attrs;
|
|
677
|
+
}
|
|
678
|
+
function resolveRelTarget(target) {
|
|
679
|
+
if (!target) return target;
|
|
680
|
+
if (target.startsWith("/")) return target.slice(1);
|
|
681
|
+
if (target.startsWith("xl/")) return target;
|
|
682
|
+
return "xl/" + target;
|
|
683
|
+
}
|
|
684
|
+
function columnIndexToLetter(colIndex) {
|
|
685
|
+
let n = colIndex + 1;
|
|
686
|
+
let result = "";
|
|
687
|
+
while (n > 0) {
|
|
688
|
+
const rem = (n - 1) % 26;
|
|
689
|
+
result = String.fromCharCode(65 + rem) + result;
|
|
690
|
+
n = Math.floor((n - 1) / 26);
|
|
691
|
+
}
|
|
692
|
+
return result;
|
|
693
|
+
}
|
|
694
|
+
function columnLetterToIndex(letter) {
|
|
695
|
+
let column = 0;
|
|
696
|
+
const length = letter.length;
|
|
697
|
+
for (let i = 0; i < length; i++) {
|
|
698
|
+
column += (letter.charCodeAt(i) - 64) * Math.pow(26, length - i - 1);
|
|
699
|
+
}
|
|
700
|
+
return column - 1;
|
|
701
|
+
}
|
|
702
|
+
function trimTrailingEmptyRows(rows) {
|
|
703
|
+
let end = rows.length;
|
|
704
|
+
while (end > 0 && rows[end - 1].every((v) => v === null || v === void 0)) {
|
|
705
|
+
end--;
|
|
706
|
+
}
|
|
707
|
+
return end === rows.length ? rows : rows.slice(0, end);
|
|
708
|
+
}
|
|
709
|
+
function parseSharedStringsXml(xml) {
|
|
710
|
+
const result = [];
|
|
711
|
+
const itemRegex = /<(?:(?:[A-Za-z_][\w.-]*):)?si\b[^>]*>([\s\S]*?)<\/(?:(?:[A-Za-z_][\w.-]*):)?si\s*>|<(?:(?:[A-Za-z_][\w.-]*):)?si\b[^>]*\/>/gi;
|
|
712
|
+
let itemMatch;
|
|
713
|
+
while ((itemMatch = itemRegex.exec(xml)) !== null) {
|
|
714
|
+
const full = itemMatch[0];
|
|
715
|
+
if (full.endsWith("/>") && itemMatch[1] === void 0) {
|
|
716
|
+
result.push("");
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
let content = itemMatch[1] ?? "";
|
|
720
|
+
content = content.replace(/<(?:(?:[A-Za-z_][\w.-]*):)?rPh\b[^>]*>[\s\S]*?<\/(?:(?:[A-Za-z_][\w.-]*):)?rPh\s*>/gi, "");
|
|
721
|
+
content = content.replace(/<(?:(?:[A-Za-z_][\w.-]*):)?rPh\b[^>]*\/>/gi, "");
|
|
722
|
+
const textRegex = /<(?:(?:[A-Za-z_][\w.-]*):)?t\b[^>]*>([\s\S]*?)<\/(?:(?:[A-Za-z_][\w.-]*):)?t\s*>/gi;
|
|
723
|
+
let value = "";
|
|
724
|
+
let textMatch;
|
|
725
|
+
while ((textMatch = textRegex.exec(content)) !== null) {
|
|
726
|
+
value += textMatch[1];
|
|
727
|
+
}
|
|
728
|
+
result.push(unescapeXml(value));
|
|
729
|
+
}
|
|
730
|
+
return result;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// src/biff12Utils.ts
|
|
734
|
+
function readVlq(buf, pos) {
|
|
735
|
+
let value = 0;
|
|
736
|
+
let shift = 0;
|
|
737
|
+
let byte;
|
|
738
|
+
do {
|
|
739
|
+
byte = buf[pos++];
|
|
740
|
+
value |= (byte & 127) << shift;
|
|
741
|
+
shift += 7;
|
|
742
|
+
} while ((byte & 128) !== 0);
|
|
743
|
+
return { value, nextPos: pos };
|
|
744
|
+
}
|
|
745
|
+
function vlqBytes(value) {
|
|
746
|
+
const out = [];
|
|
747
|
+
while (value >= 128) {
|
|
748
|
+
out.push(value & 127 | 128);
|
|
749
|
+
value >>>= 7;
|
|
750
|
+
}
|
|
751
|
+
out.push(value & 127);
|
|
752
|
+
return out;
|
|
753
|
+
}
|
|
754
|
+
function readRecord(buf, pos, rec) {
|
|
755
|
+
const idRes = readVlq(buf, pos);
|
|
756
|
+
if (idRes.nextPos > buf.length) return false;
|
|
757
|
+
const lenRes = readVlq(buf, idRes.nextPos);
|
|
758
|
+
if (lenRes.nextPos + lenRes.value > buf.length) return false;
|
|
759
|
+
rec.headerStart = pos;
|
|
760
|
+
rec.dataStart = lenRes.nextPos;
|
|
761
|
+
rec.dataEnd = lenRes.nextPos + lenRes.value;
|
|
762
|
+
rec.id = idRes.value;
|
|
763
|
+
rec.len = lenRes.value;
|
|
764
|
+
return true;
|
|
765
|
+
}
|
|
766
|
+
function buildRecord(id, payload) {
|
|
767
|
+
const idBytes = vlqBytes(id);
|
|
768
|
+
const lenBytes = vlqBytes(payload.length);
|
|
769
|
+
const header = Buffer.alloc(idBytes.length + lenBytes.length);
|
|
770
|
+
idBytes.forEach((b, i) => header[i] = b);
|
|
771
|
+
lenBytes.forEach((b, i) => header[idBytes.length + i] = b);
|
|
772
|
+
return Buffer.concat([header, payload]);
|
|
773
|
+
}
|
|
774
|
+
function readUtf16(buf, start, charCount) {
|
|
775
|
+
const end = start + charCount * 2;
|
|
776
|
+
if (end > buf.length) return "";
|
|
777
|
+
return buf.toString("utf16le", start, end);
|
|
778
|
+
}
|
|
779
|
+
function parseSharedStringsBin(buf) {
|
|
780
|
+
const values = [];
|
|
781
|
+
let total = 0;
|
|
782
|
+
let unique = 0;
|
|
783
|
+
let endSstOffset = buf.length;
|
|
784
|
+
const rec = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
785
|
+
let pos = 0;
|
|
786
|
+
while (readRecord(buf, pos, rec)) {
|
|
787
|
+
pos = rec.dataEnd;
|
|
788
|
+
if (rec.id === 159) {
|
|
789
|
+
total = buf.readUInt32LE(rec.dataStart);
|
|
790
|
+
unique = buf.readUInt32LE(rec.dataStart + 4);
|
|
791
|
+
} else if (rec.id === 19) {
|
|
792
|
+
const cch = buf.readUInt32LE(rec.dataStart + 1);
|
|
793
|
+
values.push(readUtf16(buf, rec.dataStart + 5, cch));
|
|
794
|
+
} else if (rec.id === 160) {
|
|
795
|
+
endSstOffset = rec.headerStart;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
return { values, total, unique, endSstOffset };
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// src/XlsbReader.ts
|
|
449
802
|
var XlsbReader = class extends ExcelReaderAbstract {
|
|
450
803
|
constructor() {
|
|
451
804
|
super();
|
|
@@ -457,12 +810,29 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
457
810
|
this._reader = null;
|
|
458
811
|
this._currentRow = [];
|
|
459
812
|
this._pendingRowIndex = -1;
|
|
813
|
+
this._nextExpectedRow = 0;
|
|
460
814
|
this._eof = false;
|
|
461
815
|
this.xfIdToNumFmtId = [];
|
|
462
816
|
this.customDateFormats = null;
|
|
463
817
|
}
|
|
464
|
-
async open(
|
|
465
|
-
this.
|
|
818
|
+
async open(path9, readSharedStrings = true) {
|
|
819
|
+
await this.close();
|
|
820
|
+
this.sheetNames = [];
|
|
821
|
+
this.sheets = [];
|
|
822
|
+
this.sharedStrings = [];
|
|
823
|
+
this.xfIdToNumFmtId = [];
|
|
824
|
+
this.customDateFormats = /* @__PURE__ */ new Set();
|
|
825
|
+
this.fieldCount = 0;
|
|
826
|
+
this.resultsCount = 0;
|
|
827
|
+
this.actualSheetName = "";
|
|
828
|
+
this._currentSheetIndex = -1;
|
|
829
|
+
this._currentRow = [];
|
|
830
|
+
this._pendingRowIndex = -1;
|
|
831
|
+
this._nextExpectedRow = 0;
|
|
832
|
+
this._eof = false;
|
|
833
|
+
this._reader = null;
|
|
834
|
+
this._oaEpoch = Date.UTC(1899, 11, 30);
|
|
835
|
+
this.zip = new import_adm_zip.default(path9);
|
|
466
836
|
const wbEntry = this.zip.getEntry("xl/workbook.bin");
|
|
467
837
|
if (wbEntry) {
|
|
468
838
|
const buf = wbEntry.getData();
|
|
@@ -479,20 +849,39 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
479
849
|
const relsEntry = this.zip.getEntry("xl/_rels/workbook.bin.rels");
|
|
480
850
|
if (relsEntry) {
|
|
481
851
|
const xml = relsEntry.getData().toString("utf8");
|
|
482
|
-
const
|
|
483
|
-
let
|
|
852
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
853
|
+
let tagMatch;
|
|
484
854
|
const rIdToTarget = {};
|
|
485
|
-
while ((
|
|
486
|
-
|
|
855
|
+
while ((tagMatch = relTagRe.exec(xml)) !== null) {
|
|
856
|
+
const attrs = parseTagAttributes(tagMatch[0]);
|
|
857
|
+
const id = attrs["Id"] ?? attrs["id"];
|
|
858
|
+
const target = attrs["Target"] ?? attrs["target"];
|
|
859
|
+
if (id !== void 0 && target !== void 0) {
|
|
860
|
+
rIdToTarget[id] = target;
|
|
861
|
+
}
|
|
487
862
|
}
|
|
488
863
|
for (const sheet of this.sheets) {
|
|
489
|
-
|
|
864
|
+
const target = rIdToTarget[sheet.rId];
|
|
490
865
|
if (target) {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
866
|
+
sheet.path = resolveRelTarget(target);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
try {
|
|
871
|
+
const raw = wbEntry ? wbEntry.getData() : null;
|
|
872
|
+
if (raw) {
|
|
873
|
+
const rec = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
874
|
+
let pos = 0;
|
|
875
|
+
while (readRecord(raw, pos, rec)) {
|
|
876
|
+
if (rec.id === 153 && rec.len >= 4) {
|
|
877
|
+
const is1904 = (raw[rec.dataStart] & 1) !== 0;
|
|
878
|
+
this._oaEpoch = is1904 ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30);
|
|
879
|
+
break;
|
|
880
|
+
}
|
|
881
|
+
pos = rec.dataEnd;
|
|
494
882
|
}
|
|
495
883
|
}
|
|
884
|
+
} catch {
|
|
496
885
|
}
|
|
497
886
|
if (readSharedStrings) {
|
|
498
887
|
const ssPath = "xl/sharedStrings.bin";
|
|
@@ -507,8 +896,8 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
507
896
|
this.resultsCount = this.sheets.length;
|
|
508
897
|
this._currentSheetIndex = -1;
|
|
509
898
|
}
|
|
510
|
-
_readSharedStrings(
|
|
511
|
-
const entry = this.zip.getEntry(
|
|
899
|
+
_readSharedStrings(path9) {
|
|
900
|
+
const entry = this.zip.getEntry(path9);
|
|
512
901
|
if (!entry) return;
|
|
513
902
|
const reader = new BiffReaderWriter(entry.getData());
|
|
514
903
|
while (reader.readSharedStrings()) {
|
|
@@ -517,8 +906,8 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
517
906
|
}
|
|
518
907
|
}
|
|
519
908
|
}
|
|
520
|
-
_readStyles(
|
|
521
|
-
const entry = this.zip.getEntry(
|
|
909
|
+
_readStyles(path9) {
|
|
910
|
+
const entry = this.zip.getEntry(path9);
|
|
522
911
|
if (!entry) return;
|
|
523
912
|
const reader = new BiffReaderWriter(entry.getData());
|
|
524
913
|
while (reader.readStyles()) {
|
|
@@ -529,21 +918,54 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
529
918
|
getSheetNames() {
|
|
530
919
|
return this.sheetNames;
|
|
531
920
|
}
|
|
921
|
+
async selectSheet(nameOrIndex) {
|
|
922
|
+
let index = -1;
|
|
923
|
+
if (typeof nameOrIndex === "number") {
|
|
924
|
+
index = nameOrIndex;
|
|
925
|
+
} else {
|
|
926
|
+
index = this.sheetNames.indexOf(nameOrIndex);
|
|
927
|
+
if (index === -1) throw new Error(`Sheet '${nameOrIndex}' not found`);
|
|
928
|
+
}
|
|
929
|
+
if (index < 0 || index >= this.sheets.length) {
|
|
930
|
+
throw new Error(`Sheet index ${String(nameOrIndex)} out of range`);
|
|
931
|
+
}
|
|
932
|
+
this._currentSheetIndex = index;
|
|
933
|
+
if (!this._initSheet(index)) {
|
|
934
|
+
throw new Error(`XlsbReader: worksheet part missing for sheet '${this.sheetNames[index]}'.`);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
async nextResult() {
|
|
938
|
+
const next = this._currentSheetIndex + 1;
|
|
939
|
+
if (next >= this.sheets.length) return false;
|
|
940
|
+
this._currentSheetIndex = next;
|
|
941
|
+
if (!this._initSheet(next)) {
|
|
942
|
+
throw new Error(`XlsbReader: worksheet part missing for sheet '${this.sheetNames[next]}'.`);
|
|
943
|
+
}
|
|
944
|
+
return true;
|
|
945
|
+
}
|
|
532
946
|
async read() {
|
|
533
947
|
if (this._currentSheetIndex === -1) {
|
|
534
948
|
this._currentSheetIndex = 0;
|
|
535
949
|
if (!this._initSheet(0)) return false;
|
|
536
950
|
}
|
|
537
951
|
if (this._eof) return false;
|
|
952
|
+
if (this._pendingRowIndex > this._nextExpectedRow && this._pendingRowIndex < MAX_ROWS) {
|
|
953
|
+
this._currentRow = [];
|
|
954
|
+
this._nextExpectedRow++;
|
|
955
|
+
return true;
|
|
956
|
+
}
|
|
538
957
|
this._currentRow = [];
|
|
539
958
|
while (true) {
|
|
540
959
|
const hasRecord = this._reader.readWorksheet();
|
|
541
960
|
if (!hasRecord) {
|
|
542
961
|
this._eof = true;
|
|
962
|
+
this._nextExpectedRow = this._pendingRowIndex + 1;
|
|
543
963
|
return true;
|
|
544
964
|
}
|
|
545
965
|
if (this._reader._rowIndex !== -1 && this._reader._rowIndex !== this._pendingRowIndex) {
|
|
966
|
+
const completed = this._pendingRowIndex;
|
|
546
967
|
this._pendingRowIndex = this._reader._rowIndex;
|
|
968
|
+
this._nextExpectedRow = completed + 1;
|
|
547
969
|
return true;
|
|
548
970
|
}
|
|
549
971
|
if (this._reader._readCell) {
|
|
@@ -584,13 +1006,17 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
584
1006
|
}
|
|
585
1007
|
}
|
|
586
1008
|
_initSheet(index) {
|
|
587
|
-
if (index >= this.sheets.length) return false;
|
|
1009
|
+
if (index < 0 || index >= this.sheets.length) return false;
|
|
588
1010
|
const sheet = this.sheets[index];
|
|
589
|
-
const entry = this.zip.getEntry(sheet.path);
|
|
1011
|
+
const entry = sheet.path ? this.zip.getEntry(sheet.path) : void 0;
|
|
590
1012
|
if (!entry) return false;
|
|
591
1013
|
this._reader = new BiffReaderWriter(entry.getData());
|
|
592
1014
|
this._eof = false;
|
|
593
1015
|
this._pendingRowIndex = -1;
|
|
1016
|
+
this._nextExpectedRow = 0;
|
|
1017
|
+
this._currentRow = [];
|
|
1018
|
+
this.fieldCount = 0;
|
|
1019
|
+
this.actualSheetName = sheet.name;
|
|
594
1020
|
while (this._reader.readWorksheet()) {
|
|
595
1021
|
if (this._reader._rowIndex !== -1) {
|
|
596
1022
|
this._pendingRowIndex = this._reader._rowIndex;
|
|
@@ -598,7 +1024,7 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
598
1024
|
}
|
|
599
1025
|
}
|
|
600
1026
|
this._eof = true;
|
|
601
|
-
return
|
|
1027
|
+
return true;
|
|
602
1028
|
}
|
|
603
1029
|
getValue(i) {
|
|
604
1030
|
if (i < 0 || i >= this._currentRow.length) return null;
|
|
@@ -608,84 +1034,15 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
608
1034
|
|
|
609
1035
|
// src/XlsxReader.ts
|
|
610
1036
|
var import_yauzl = __toESM(require("yauzl"));
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
}
|
|
620
|
-
return false;
|
|
621
|
-
}
|
|
622
|
-
function stripInvalidControlChars(str) {
|
|
623
|
-
let out = "";
|
|
624
|
-
for (let i = 0; i < str.length; i++) {
|
|
625
|
-
const c = str.charCodeAt(i);
|
|
626
|
-
if (c === 9 || c === 10 || c === 13 || c >= 32 && c <= 55295 || c >= 57344 && c <= 65533) {
|
|
627
|
-
out += str[i];
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
return out;
|
|
631
|
-
}
|
|
632
|
-
function escapeXmlText(str) {
|
|
633
|
-
if (!/&|<|>|"|'/.test(str) && !hasInvalidControlChars(str)) {
|
|
634
|
-
return str;
|
|
635
|
-
}
|
|
636
|
-
let out = str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
637
|
-
if (hasInvalidControlChars(out)) {
|
|
638
|
-
out = stripInvalidControlChars(out);
|
|
639
|
-
}
|
|
640
|
-
return out;
|
|
641
|
-
}
|
|
642
|
-
function unescapeXml(str) {
|
|
643
|
-
if (!str || str.indexOf("&") === -1) return str;
|
|
644
|
-
return str.replace(/&#x([0-9a-f]+);/gi, (_match, hex) => String.fromCodePoint(parseInt(hex, 16))).replace(/&#(\d+);/g, (_match, decimal) => String.fromCodePoint(parseInt(decimal, 10))).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
645
|
-
}
|
|
646
|
-
function columnIndexToLetter(colIndex) {
|
|
647
|
-
let n = colIndex + 1;
|
|
648
|
-
let result = "";
|
|
649
|
-
while (n > 0) {
|
|
650
|
-
const rem = (n - 1) % 26;
|
|
651
|
-
result = String.fromCharCode(65 + rem) + result;
|
|
652
|
-
n = Math.floor((n - 1) / 26);
|
|
653
|
-
}
|
|
654
|
-
return result;
|
|
655
|
-
}
|
|
656
|
-
function columnLetterToIndex(letter) {
|
|
657
|
-
let column = 0;
|
|
658
|
-
const length = letter.length;
|
|
659
|
-
for (let i = 0; i < length; i++) {
|
|
660
|
-
column += (letter.charCodeAt(i) - 64) * Math.pow(26, length - i - 1);
|
|
661
|
-
}
|
|
662
|
-
return column - 1;
|
|
663
|
-
}
|
|
664
|
-
function trimTrailingEmptyRows(rows) {
|
|
665
|
-
let end = rows.length;
|
|
666
|
-
while (end > 0 && rows[end - 1].every((v) => v === null || v === void 0)) {
|
|
667
|
-
end--;
|
|
668
|
-
}
|
|
669
|
-
return end === rows.length ? rows : rows.slice(0, end);
|
|
670
|
-
}
|
|
671
|
-
function parseSharedStringsXml(xml) {
|
|
672
|
-
const result = [];
|
|
673
|
-
const itemRegex = /<(?:(?:[A-Za-z_][\w.-]*):)?si\b[^>]*>([\s\S]*?)<\/(?:(?:[A-Za-z_][\w.-]*):)?si\s*>/gi;
|
|
674
|
-
let itemMatch;
|
|
675
|
-
while ((itemMatch = itemRegex.exec(xml)) !== null) {
|
|
676
|
-
const content = itemMatch[1];
|
|
677
|
-
const textRegex = /<(?:(?:[A-Za-z_][\w.-]*):)?t\b[^>]*>([\s\S]*?)<\/(?:(?:[A-Za-z_][\w.-]*):)?t\s*>/gi;
|
|
678
|
-
let value = "";
|
|
679
|
-
let textMatch;
|
|
680
|
-
while ((textMatch = textRegex.exec(content)) !== null) {
|
|
681
|
-
value += textMatch[1];
|
|
682
|
-
}
|
|
683
|
-
result.push(unescapeXml(value));
|
|
684
|
-
}
|
|
685
|
-
return result;
|
|
1037
|
+
function parseIsoDateUtc(value) {
|
|
1038
|
+
let text = value.trim().replace(" ", "T");
|
|
1039
|
+
if (!text) return null;
|
|
1040
|
+
if (!/z$|[+-]\d{2}:?\d{2}$/i.test(text) && text.includes("T")) {
|
|
1041
|
+
text += "Z";
|
|
1042
|
+
}
|
|
1043
|
+
const date = new Date(text);
|
|
1044
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
686
1045
|
}
|
|
687
|
-
|
|
688
|
-
// src/XlsxReader.ts
|
|
689
1046
|
var XlsxReader = class extends ExcelReaderAbstract {
|
|
690
1047
|
constructor() {
|
|
691
1048
|
super();
|
|
@@ -697,13 +1054,34 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
697
1054
|
this._currentSheetIndex = -1;
|
|
698
1055
|
this._sheetXml = null;
|
|
699
1056
|
this._xmlPos = 0;
|
|
1057
|
+
/** End of the sheetData element; row tags must not be searched past it. */
|
|
1058
|
+
this._xmlEnd = 0;
|
|
700
1059
|
this._currentRow = [];
|
|
1060
|
+
this._expectedRowNum = 1;
|
|
1061
|
+
this._pendingEmptyCount = 0;
|
|
701
1062
|
this.xfIdToNumFmtId = [];
|
|
702
1063
|
this.customDateFormats = /* @__PURE__ */ new Set();
|
|
703
1064
|
}
|
|
704
|
-
async open(
|
|
705
|
-
|
|
706
|
-
|
|
1065
|
+
async open(path9, readSharedStrings = true) {
|
|
1066
|
+
await this.close();
|
|
1067
|
+
this.sheetNames = [];
|
|
1068
|
+
this.sheets = [];
|
|
1069
|
+
this.sharedStrings = [];
|
|
1070
|
+
this.xfIdToNumFmtId = [];
|
|
1071
|
+
this.customDateFormats = /* @__PURE__ */ new Set();
|
|
1072
|
+
this.fieldCount = 0;
|
|
1073
|
+
this.resultsCount = 0;
|
|
1074
|
+
this.actualSheetName = "";
|
|
1075
|
+
this._currentSheetIndex = -1;
|
|
1076
|
+
this._sheetXml = null;
|
|
1077
|
+
this._xmlPos = 0;
|
|
1078
|
+
this._xmlEnd = 0;
|
|
1079
|
+
this._currentRow = [];
|
|
1080
|
+
this._expectedRowNum = 1;
|
|
1081
|
+
this._pendingEmptyCount = 0;
|
|
1082
|
+
this._oaEpoch = Date.UTC(1899, 11, 30);
|
|
1083
|
+
return new Promise((resolve6, reject) => {
|
|
1084
|
+
import_yauzl.default.open(path9, { lazyEntries: true, autoClose: false }, async (err, zipfile) => {
|
|
707
1085
|
if (err) return reject(err);
|
|
708
1086
|
this.zipfile = zipfile;
|
|
709
1087
|
this.entries = /* @__PURE__ */ new Map();
|
|
@@ -716,28 +1094,40 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
716
1094
|
const wbRelsContent = await this._readZipEntryContent("xl/_rels/workbook.xml.rels");
|
|
717
1095
|
const rIdToTarget = {};
|
|
718
1096
|
if (wbRelsContent) {
|
|
719
|
-
const
|
|
720
|
-
let
|
|
721
|
-
while ((
|
|
722
|
-
|
|
1097
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
1098
|
+
let tagMatch;
|
|
1099
|
+
while ((tagMatch = relTagRe.exec(wbRelsContent)) !== null) {
|
|
1100
|
+
const attrs = parseTagAttributes(tagMatch[0]);
|
|
1101
|
+
const id = attrs["Id"] ?? attrs["id"];
|
|
1102
|
+
const target = attrs["Target"] ?? attrs["target"];
|
|
1103
|
+
if (id !== void 0 && target !== void 0) {
|
|
1104
|
+
rIdToTarget[id] = target;
|
|
1105
|
+
}
|
|
723
1106
|
}
|
|
724
1107
|
}
|
|
725
1108
|
const wbContent = await this._readZipEntryContent("xl/workbook.xml");
|
|
726
1109
|
if (wbContent) {
|
|
727
|
-
const
|
|
728
|
-
let
|
|
729
|
-
while ((
|
|
730
|
-
const
|
|
731
|
-
const
|
|
732
|
-
const
|
|
1110
|
+
const sheetTagRe = /<sheet\b[^>]*>/g;
|
|
1111
|
+
let tagMatch;
|
|
1112
|
+
while ((tagMatch = sheetTagRe.exec(wbContent)) !== null) {
|
|
1113
|
+
const attrs = parseTagAttributes(tagMatch[0]);
|
|
1114
|
+
const rawName = attrs["name"];
|
|
1115
|
+
const sheetId = attrs["sheetId"] ?? "";
|
|
1116
|
+
const rId = attrs["r:id"] ?? attrs["id"] ?? attrs["rId"];
|
|
1117
|
+
if (rawName === void 0 || rId === void 0) continue;
|
|
1118
|
+
const name = unescapeXml(rawName);
|
|
733
1119
|
const target = rIdToTarget[rId];
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
fullPath = "xl/" + fullPath;
|
|
737
|
-
}
|
|
1120
|
+
if (!target) continue;
|
|
1121
|
+
const fullPath = resolveRelTarget(target);
|
|
738
1122
|
this.sheetNames.push(name);
|
|
739
1123
|
this.sheets.push({ name, sheetId, rId, path: fullPath });
|
|
740
1124
|
}
|
|
1125
|
+
const wbPrMatch = /<workbookPr\b[^>]*>/i.exec(wbContent);
|
|
1126
|
+
if (wbPrMatch) {
|
|
1127
|
+
const wbPrAttrs = parseTagAttributes(wbPrMatch[0]);
|
|
1128
|
+
const v = (wbPrAttrs["date1904"] ?? "").trim().toLowerCase();
|
|
1129
|
+
this._oaEpoch = v === "1" || v === "true" ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30);
|
|
1130
|
+
}
|
|
741
1131
|
}
|
|
742
1132
|
if (readSharedStrings) {
|
|
743
1133
|
const ssContent = await this._readZipEntryContent("xl/sharedStrings.xml");
|
|
@@ -751,7 +1141,7 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
751
1141
|
}
|
|
752
1142
|
this.resultsCount = this.sheets.length;
|
|
753
1143
|
this._currentSheetIndex = -1;
|
|
754
|
-
|
|
1144
|
+
resolve6();
|
|
755
1145
|
} catch (e) {
|
|
756
1146
|
reject(e);
|
|
757
1147
|
}
|
|
@@ -765,16 +1155,16 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
765
1155
|
this.zipfile.close();
|
|
766
1156
|
}
|
|
767
1157
|
}
|
|
768
|
-
async _readZipEntryContent(
|
|
769
|
-
const entry = this.entries.get(
|
|
1158
|
+
async _readZipEntryContent(path9) {
|
|
1159
|
+
const entry = this.entries.get(path9);
|
|
770
1160
|
if (!entry) return null;
|
|
771
|
-
return new Promise((
|
|
1161
|
+
return new Promise((resolve6, reject) => {
|
|
772
1162
|
this.zipfile.openReadStream(entry, (err, readStream) => {
|
|
773
1163
|
if (err) return reject(err);
|
|
774
1164
|
const chunks = [];
|
|
775
1165
|
readStream.on("data", (chunk) => chunks.push(chunk));
|
|
776
1166
|
readStream.on("end", () => {
|
|
777
|
-
|
|
1167
|
+
resolve6(Buffer.concat(chunks).toString("utf8"));
|
|
778
1168
|
});
|
|
779
1169
|
readStream.on("error", reject);
|
|
780
1170
|
});
|
|
@@ -822,6 +1212,27 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
822
1212
|
return false;
|
|
823
1213
|
}
|
|
824
1214
|
}
|
|
1215
|
+
async selectSheet(nameOrIndex) {
|
|
1216
|
+
let index = -1;
|
|
1217
|
+
if (typeof nameOrIndex === "number") {
|
|
1218
|
+
index = nameOrIndex;
|
|
1219
|
+
} else {
|
|
1220
|
+
index = this.sheetNames.indexOf(nameOrIndex);
|
|
1221
|
+
if (index === -1) throw new Error(`Sheet '${nameOrIndex}' not found`);
|
|
1222
|
+
}
|
|
1223
|
+
if (index < 0 || index >= this.sheets.length) {
|
|
1224
|
+
throw new Error(`Sheet index ${String(nameOrIndex)} out of range`);
|
|
1225
|
+
}
|
|
1226
|
+
this._currentSheetIndex = index;
|
|
1227
|
+
await this._initSheet(index);
|
|
1228
|
+
}
|
|
1229
|
+
async nextResult() {
|
|
1230
|
+
const next = this._currentSheetIndex + 1;
|
|
1231
|
+
if (next >= this.sheets.length) return false;
|
|
1232
|
+
this._currentSheetIndex = next;
|
|
1233
|
+
await this._initSheet(next);
|
|
1234
|
+
return true;
|
|
1235
|
+
}
|
|
825
1236
|
async _initSheet(index) {
|
|
826
1237
|
if (index >= this.sheets.length) return;
|
|
827
1238
|
const sheet = this.sheets[index];
|
|
@@ -831,23 +1242,106 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
831
1242
|
this._sheetXml = "";
|
|
832
1243
|
}
|
|
833
1244
|
this._xmlPos = 0;
|
|
1245
|
+
this._xmlEnd = this._sheetXml.length;
|
|
1246
|
+
this._expectedRowNum = 1;
|
|
1247
|
+
this._pendingEmptyCount = 0;
|
|
1248
|
+
this._currentRow = [];
|
|
1249
|
+
this.fieldCount = 0;
|
|
834
1250
|
const sd = this._sheetXml.indexOf("<sheetData>");
|
|
835
|
-
if (sd !== -1)
|
|
1251
|
+
if (sd !== -1) {
|
|
1252
|
+
this._xmlPos = sd + 11;
|
|
1253
|
+
const se = this._sheetXml.indexOf("</sheetData>", this._xmlPos);
|
|
1254
|
+
if (se !== -1) this._xmlEnd = se;
|
|
1255
|
+
} else {
|
|
1256
|
+
const m = /<(?:[A-Za-z_][\w.-]*:)?sheetData\b[^>]*>/i.exec(this._sheetXml);
|
|
1257
|
+
if (m && m.index !== void 0) {
|
|
1258
|
+
this._xmlPos = m.index + m[0].length;
|
|
1259
|
+
if (/\/>\s*$/.test(m[0])) {
|
|
1260
|
+
this._xmlEnd = this._xmlPos;
|
|
1261
|
+
} else {
|
|
1262
|
+
const se = this._sheetXml.indexOf("</sheetData>", this._xmlPos);
|
|
1263
|
+
if (se !== -1) this._xmlEnd = se;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
836
1267
|
}
|
|
837
1268
|
_readNextRow() {
|
|
838
1269
|
if (!this._sheetXml) return false;
|
|
839
|
-
|
|
1270
|
+
if (this._pendingEmptyCount > 0) {
|
|
1271
|
+
this._pendingEmptyCount--;
|
|
1272
|
+
this._currentRow = [];
|
|
1273
|
+
this._expectedRowNum++;
|
|
1274
|
+
return true;
|
|
1275
|
+
}
|
|
1276
|
+
const rowStart = this._findRowTagStart(this._xmlPos);
|
|
840
1277
|
if (rowStart === -1) return false;
|
|
1278
|
+
const rowTagEnd = this._sheetXml.indexOf(">", rowStart);
|
|
1279
|
+
if (rowTagEnd === -1) return false;
|
|
1280
|
+
const rowTag = this._sheetXml.substring(rowStart, rowTagEnd + 1);
|
|
1281
|
+
const isSelfClosing = this._sheetXml.charCodeAt(rowTagEnd - 1) === 47;
|
|
1282
|
+
const rowAttrs = parseTagAttributes(rowTag);
|
|
1283
|
+
const rRaw = rowAttrs["r"];
|
|
1284
|
+
let rowNum = this._expectedRowNum;
|
|
1285
|
+
if (rRaw !== void 0) {
|
|
1286
|
+
const parsed = parseInt(rRaw, 10);
|
|
1287
|
+
if (Number.isFinite(parsed) && parsed >= 1 && parsed <= MAX_ROWS) rowNum = parsed;
|
|
1288
|
+
}
|
|
1289
|
+
if (rowNum > this._expectedRowNum) {
|
|
1290
|
+
const gap = rowNum - this._expectedRowNum;
|
|
1291
|
+
this._pendingEmptyCount = gap - 1;
|
|
1292
|
+
this._currentRow = [];
|
|
1293
|
+
this._expectedRowNum++;
|
|
1294
|
+
return true;
|
|
1295
|
+
}
|
|
1296
|
+
if (rowNum < this._expectedRowNum) {
|
|
1297
|
+
if (isSelfClosing) {
|
|
1298
|
+
this._xmlPos = rowTagEnd + 1;
|
|
1299
|
+
this._currentRow = [];
|
|
1300
|
+
return true;
|
|
1301
|
+
}
|
|
1302
|
+
const rowEnd2 = this._sheetXml.indexOf("</row>", rowStart);
|
|
1303
|
+
if (rowEnd2 === -1) return false;
|
|
1304
|
+
this._xmlPos = rowEnd2 + 6;
|
|
1305
|
+
this._parseRowByIndex(rowStart, rowEnd2);
|
|
1306
|
+
this._expectedRowNum = Math.max(this._expectedRowNum + 1, rowNum + 1);
|
|
1307
|
+
return true;
|
|
1308
|
+
}
|
|
1309
|
+
if (isSelfClosing) {
|
|
1310
|
+
this._xmlPos = rowTagEnd + 1;
|
|
1311
|
+
this._currentRow = [];
|
|
1312
|
+
this._expectedRowNum = rowNum + 1;
|
|
1313
|
+
return true;
|
|
1314
|
+
}
|
|
841
1315
|
const rowEnd = this._sheetXml.indexOf("</row>", rowStart);
|
|
842
1316
|
if (rowEnd === -1) return false;
|
|
843
1317
|
this._xmlPos = rowEnd + 6;
|
|
844
1318
|
this._parseRowByIndex(rowStart, rowEnd);
|
|
1319
|
+
this._expectedRowNum = rowNum + 1;
|
|
845
1320
|
return true;
|
|
846
1321
|
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Find the next `<row …>` start, ignoring similarly named elements such as
|
|
1324
|
+
* `<rowBreaks>` that follow `</sheetData>`.
|
|
1325
|
+
*/
|
|
1326
|
+
_findRowTagStart(from) {
|
|
1327
|
+
const xml = this._sheetXml;
|
|
1328
|
+
let pos = from;
|
|
1329
|
+
while (pos < this._xmlEnd) {
|
|
1330
|
+
const idx = xml.indexOf("<row", pos);
|
|
1331
|
+
if (idx === -1 || idx >= this._xmlEnd) return -1;
|
|
1332
|
+
const next = xml.charCodeAt(idx + 4);
|
|
1333
|
+
if (next === 62 || next === 47 || next === 32 || next === 9 || next === 10 || next === 13) {
|
|
1334
|
+
return idx;
|
|
1335
|
+
}
|
|
1336
|
+
pos = idx + 4;
|
|
1337
|
+
}
|
|
1338
|
+
return -1;
|
|
1339
|
+
}
|
|
847
1340
|
_parseRowByIndex(rowStart, rowEnd) {
|
|
848
1341
|
this._currentRow = [];
|
|
849
1342
|
const xml = this._sheetXml;
|
|
850
1343
|
let pos = xml.indexOf(">", rowStart) + 1;
|
|
1344
|
+
let nextCol = 0;
|
|
851
1345
|
while (pos < rowEnd) {
|
|
852
1346
|
const cStart = xml.indexOf("<c", pos);
|
|
853
1347
|
if (cStart === -1 || cStart >= rowEnd) break;
|
|
@@ -881,6 +1375,7 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
881
1375
|
}
|
|
882
1376
|
}
|
|
883
1377
|
let val = null;
|
|
1378
|
+
let inlineVals = null;
|
|
884
1379
|
if (xml.charCodeAt(nextTagClose - 1) === 47) {
|
|
885
1380
|
pos = nextTagClose + 1;
|
|
886
1381
|
} else {
|
|
@@ -895,20 +1390,28 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
895
1390
|
}
|
|
896
1391
|
} else {
|
|
897
1392
|
if (type === "inlineStr") {
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1393
|
+
inlineVals = [];
|
|
1394
|
+
let tSearch = nextTagClose;
|
|
1395
|
+
while (true) {
|
|
1396
|
+
const tStart = xml.indexOf("<t", tSearch);
|
|
1397
|
+
if (tStart === -1 || tStart >= cellContentEnd) break;
|
|
1398
|
+
const tClose = xml.indexOf(">", tStart);
|
|
1399
|
+
if (tClose === -1 || tClose >= cellContentEnd) break;
|
|
1400
|
+
const tContentStart = tClose + 1;
|
|
901
1401
|
const tEnd = xml.indexOf("</t>", tContentStart);
|
|
902
|
-
if (tEnd
|
|
903
|
-
|
|
904
|
-
|
|
1402
|
+
if (tEnd === -1 || tEnd > cellContentEnd) break;
|
|
1403
|
+
inlineVals.push(xml.substring(tContentStart, tEnd));
|
|
1404
|
+
tSearch = tEnd + 4;
|
|
905
1405
|
}
|
|
1406
|
+
if (inlineVals.length === 0) inlineVals = null;
|
|
906
1407
|
}
|
|
907
1408
|
}
|
|
908
1409
|
pos = cellContentEnd + 4;
|
|
909
1410
|
}
|
|
910
1411
|
let finalVal = null;
|
|
911
|
-
if (
|
|
1412
|
+
if (inlineVals !== null) {
|
|
1413
|
+
finalVal = unescapeXml(inlineVals.join(""));
|
|
1414
|
+
} else if (val !== null) {
|
|
912
1415
|
if (type === "s") {
|
|
913
1416
|
const idx = parseInt(val, 10);
|
|
914
1417
|
finalVal = this.sharedStrings[idx];
|
|
@@ -916,6 +1419,12 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
916
1419
|
finalVal = val === "1" || val === "true";
|
|
917
1420
|
} else if (type === "inlineStr") {
|
|
918
1421
|
finalVal = unescapeXml(val);
|
|
1422
|
+
} else if (type === "str") {
|
|
1423
|
+
finalVal = unescapeXml(val);
|
|
1424
|
+
} else if (type === "e") {
|
|
1425
|
+
finalVal = unescapeXml(val);
|
|
1426
|
+
} else if (type === "d") {
|
|
1427
|
+
finalVal = parseIsoDateUtc(val) ?? unescapeXml(val);
|
|
919
1428
|
} else if (type === "n" || type === "") {
|
|
920
1429
|
finalVal = parseFloat(val);
|
|
921
1430
|
if (styleIndex > 0 && this.xfIdToNumFmtId) {
|
|
@@ -933,10 +1442,10 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
933
1442
|
}
|
|
934
1443
|
}
|
|
935
1444
|
}
|
|
936
|
-
if (colIndex
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1445
|
+
if (colIndex === -1) colIndex = nextCol;
|
|
1446
|
+
nextCol = colIndex + 1;
|
|
1447
|
+
this._currentRow[colIndex] = finalVal;
|
|
1448
|
+
if (colIndex >= this.fieldCount) this.fieldCount = colIndex + 1;
|
|
940
1449
|
}
|
|
941
1450
|
}
|
|
942
1451
|
getValue(i) {
|
|
@@ -966,99 +1475,11 @@ var ReaderFactory = class {
|
|
|
966
1475
|
|
|
967
1476
|
// src/XlsbWriter.ts
|
|
968
1477
|
var fs = __toESM(require("fs"));
|
|
1478
|
+
var path2 = __toESM(require("path"));
|
|
1479
|
+
var import_crypto = require("crypto");
|
|
969
1480
|
var import_archiver = __toESM(require("archiver"));
|
|
970
1481
|
var import_stream = require("stream");
|
|
971
1482
|
|
|
972
|
-
// src/Formats.ts
|
|
973
|
-
var F = {
|
|
974
|
-
THOUSANDS_SEP: "#,##0",
|
|
975
|
-
CURRENCY_PLN: '#,##0.00 "z\u0142"',
|
|
976
|
-
CURRENCY_EUR: "#,##0.00 \u20AC",
|
|
977
|
-
PERCENTAGE: "0%",
|
|
978
|
-
SCIENTIFIC: "0.00E+00",
|
|
979
|
-
TWO_DECIMALS: "#,##0.00",
|
|
980
|
-
TEXT: "@",
|
|
981
|
-
LEADING_ZEROS: "000000000",
|
|
982
|
-
DATE_SHORT: "dd.mm.yyyy",
|
|
983
|
-
DATE_LONG: "d mmmm yyyy",
|
|
984
|
-
DATE_DAY_MONTH_YEAR: "dd-mm-yyyy",
|
|
985
|
-
DATE_ISO: "yyyy-mm-dd",
|
|
986
|
-
DATE_MONTH_YEAR: "mmmm yyyy",
|
|
987
|
-
DATE_WEEKDAY: "dddd, d mmmm yyyy",
|
|
988
|
-
DATE_DAY_MONTH: "d mmmm",
|
|
989
|
-
DATE_YEAR_ONLY: "yyyy",
|
|
990
|
-
DATETIME_SHORT: "dd.mm.yyyy hh:mm",
|
|
991
|
-
DATETIME_LONG: "d mmmm yyyy hh:mm:ss",
|
|
992
|
-
TIME_HH_MM: "hh:mm",
|
|
993
|
-
TIME_HH_MM_SS: "hh:mm:ss",
|
|
994
|
-
TIME_12H: "h:mm AM/PM",
|
|
995
|
-
DATETIME_24H: "dd.mm.yyyy hh:mm:ss",
|
|
996
|
-
DATETIME_ISO: 'yyyy-mm-dd"T"hh:mm:ss',
|
|
997
|
-
TIME_MS: "hh:mm:ss.000"
|
|
998
|
-
};
|
|
999
|
-
function isFormattedCell(val) {
|
|
1000
|
-
return val !== null && val !== void 0 && typeof val === "object" && "format" in val && typeof val.format === "string";
|
|
1001
|
-
}
|
|
1002
|
-
function unwrapCell(val) {
|
|
1003
|
-
if (isFormattedCell(val)) {
|
|
1004
|
-
return val.value;
|
|
1005
|
-
}
|
|
1006
|
-
return val;
|
|
1007
|
-
}
|
|
1008
|
-
function getFormat(val) {
|
|
1009
|
-
if (isFormattedCell(val)) {
|
|
1010
|
-
return val.format;
|
|
1011
|
-
}
|
|
1012
|
-
return null;
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
// src/writerHelpers.ts
|
|
1016
|
-
var INVALID_SHEET_NAME_CHARS = /[\\/*?[\]:]/g;
|
|
1017
|
-
function sanitizeSheetName(name, sheetCount) {
|
|
1018
|
-
if (!name || typeof name !== "string") {
|
|
1019
|
-
return `Sheet${sheetCount + 1}`;
|
|
1020
|
-
}
|
|
1021
|
-
let sanitized = name.replace(INVALID_SHEET_NAME_CHARS, "_");
|
|
1022
|
-
if (sanitized.length > 31) {
|
|
1023
|
-
sanitized = sanitized.substring(0, 31);
|
|
1024
|
-
}
|
|
1025
|
-
if (sanitized.trim().length === 0) {
|
|
1026
|
-
sanitized = `Sheet${sheetCount + 1}`;
|
|
1027
|
-
}
|
|
1028
|
-
return sanitized;
|
|
1029
|
-
}
|
|
1030
|
-
function initColWidths(columnCount) {
|
|
1031
|
-
return new Array(columnCount).fill(-1);
|
|
1032
|
-
}
|
|
1033
|
-
function applyHeaderWidths(colWidths, headers, columnCount) {
|
|
1034
|
-
for (let i = 0; i < columnCount; i++) {
|
|
1035
|
-
const len = headers[i] ? headers[i].length + 1 : 0;
|
|
1036
|
-
let width = 1.25 * len + 2;
|
|
1037
|
-
if (width > 80) width = 80;
|
|
1038
|
-
if (colWidths[i] < width) colWidths[i] = width;
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
function updateColWidthsFromRows(colWidths, rows, sampleLimit = 100) {
|
|
1042
|
-
for (let r = 0; r < Math.min(rows.length, sampleLimit); r++) {
|
|
1043
|
-
const row = rows[r];
|
|
1044
|
-
for (let c = 0; c < row.length; c++) {
|
|
1045
|
-
const raw = row[c];
|
|
1046
|
-
if (raw === null || raw === void 0) continue;
|
|
1047
|
-
const val = isFormattedCell(raw) ? raw.value : raw;
|
|
1048
|
-
const len = val instanceof Date ? 20 : String(val).length + 1;
|
|
1049
|
-
let width = 1.25 * len + 2;
|
|
1050
|
-
if (width > 80) width = 80;
|
|
1051
|
-
if (colWidths[c] < width) colWidths[c] = width;
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
function defaultColWidth(width, floor = false) {
|
|
1056
|
-
if (width > 0) {
|
|
1057
|
-
return floor ? Math.floor(width) : width;
|
|
1058
|
-
}
|
|
1059
|
-
return 10;
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
1483
|
// src/StreamingSheetState.ts
|
|
1063
1484
|
var StreamingSheetState = class {
|
|
1064
1485
|
constructor() {
|
|
@@ -1111,6 +1532,8 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
1111
1532
|
* @param filePath Destination `.xlsb` path on disk.
|
|
1112
1533
|
*/
|
|
1113
1534
|
constructor(filePath) {
|
|
1535
|
+
this._outputError = null;
|
|
1536
|
+
this._finalized = false;
|
|
1114
1537
|
this.sheetCount = 0;
|
|
1115
1538
|
this.sheetList = [];
|
|
1116
1539
|
this.sstDic = /* @__PURE__ */ new Map();
|
|
@@ -1121,8 +1544,20 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
1121
1544
|
this._nextXfIdx = 4;
|
|
1122
1545
|
this._stFmtRecords = /* @__PURE__ */ new Map();
|
|
1123
1546
|
this.stream = new StreamingSheetState();
|
|
1124
|
-
this.
|
|
1547
|
+
this._targetPath = path2.resolve(filePath);
|
|
1548
|
+
fs.mkdirSync(path2.dirname(this._targetPath), { recursive: true });
|
|
1549
|
+
this._tempPath = path2.join(
|
|
1550
|
+
path2.dirname(this._targetPath),
|
|
1551
|
+
`.${path2.basename(this._targetPath)}.${(0, import_crypto.randomUUID)()}.tmp`
|
|
1552
|
+
);
|
|
1553
|
+
this.output = fs.createWriteStream(this._tempPath);
|
|
1554
|
+
this.output.on("error", (err) => {
|
|
1555
|
+
this._outputError = err;
|
|
1556
|
+
});
|
|
1125
1557
|
this.archive = (0, import_archiver.default)("zip");
|
|
1558
|
+
this.archive.on("error", (err) => {
|
|
1559
|
+
this._outputError = err;
|
|
1560
|
+
});
|
|
1126
1561
|
this.archive.pipe(this.output);
|
|
1127
1562
|
this._oaEpoch = Date.UTC(1899, 11, 30);
|
|
1128
1563
|
this._sheet1Bytes = Buffer.from([
|
|
@@ -2992,6 +3427,11 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
2992
3427
|
* @param options Optional settings (hidden, doAutofilter)
|
|
2993
3428
|
*/
|
|
2994
3429
|
startSheet(sheetName, columnCount, headers, options = {}) {
|
|
3430
|
+
assertColumnCount(columnCount);
|
|
3431
|
+
if (headers !== void 0) {
|
|
3432
|
+
assertRowFitsColumns("header", headers.length, columnCount);
|
|
3433
|
+
for (const h of headers) assertCellTextLength(h);
|
|
3434
|
+
}
|
|
2995
3435
|
const { hidden = false, doAutofilter = true, sampleRows } = options;
|
|
2996
3436
|
this.addSheet(sheetName, hidden);
|
|
2997
3437
|
this.stream.begin(columnCount, doAutofilter && headers !== void 0, new BigBuffer());
|
|
@@ -3057,6 +3497,7 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3057
3497
|
`Row length mismatch. Expected ${this.stream.endCol - this.stream.startCol} columns, got ${row.length}`
|
|
3058
3498
|
);
|
|
3059
3499
|
}
|
|
3500
|
+
assertRowCount(this.stream.rowNum + 1);
|
|
3060
3501
|
this.createRowHeader(bigBuf, this.stream.rowNum, this.stream.startCol, this.stream.endCol);
|
|
3061
3502
|
for (let c = 0; c < row.length; c++) {
|
|
3062
3503
|
const raw = row[c];
|
|
@@ -3079,7 +3520,7 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3079
3520
|
} else if (typeof val === "boolean") {
|
|
3080
3521
|
this.writeBool(bigBuf, val, c);
|
|
3081
3522
|
} else if (val instanceof Date) {
|
|
3082
|
-
const oaDate = (val.getTime()
|
|
3523
|
+
const oaDate = timeToOaDate(val.getTime(), this._oaEpoch);
|
|
3083
3524
|
this.writeDouble(bigBuf, oaDate, c, fmtString !== null ? styleNum : 1);
|
|
3084
3525
|
} else if (val !== null && val !== void 0) {
|
|
3085
3526
|
this.writeString(bigBuf, val.toString(), c, false, fmtString !== null ? styleNum : void 0);
|
|
@@ -3136,6 +3577,16 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3136
3577
|
} else if (headers) {
|
|
3137
3578
|
columnCount = headers.length;
|
|
3138
3579
|
}
|
|
3580
|
+
if (columnCount > 0) assertColumnCount(columnCount);
|
|
3581
|
+
const totalRowsCheck = rows.length + (headers ? 1 : 0);
|
|
3582
|
+
assertRowCount(totalRowsCheck);
|
|
3583
|
+
if (headers) {
|
|
3584
|
+
assertRowFitsColumns("header", headers.length, columnCount);
|
|
3585
|
+
for (const h of headers) assertCellTextLength(h);
|
|
3586
|
+
}
|
|
3587
|
+
for (let r = 0; r < rows.length; r++) {
|
|
3588
|
+
assertRowFitsColumns("row", rows[r].length, columnCount);
|
|
3589
|
+
}
|
|
3139
3590
|
const colWidths = initColWidths(columnCount);
|
|
3140
3591
|
if (headers) {
|
|
3141
3592
|
applyHeaderWidths(colWidths, headers, columnCount);
|
|
@@ -3211,7 +3662,7 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3211
3662
|
} else if (typeof val === "boolean") {
|
|
3212
3663
|
this.writeBool(bigBuf, val, c);
|
|
3213
3664
|
} else if (val instanceof Date) {
|
|
3214
|
-
const oaDate = (val.getTime()
|
|
3665
|
+
const oaDate = timeToOaDate(val.getTime(), this._oaEpoch);
|
|
3215
3666
|
this.writeDouble(bigBuf, oaDate, c, fmtString !== null ? styleNum : 1);
|
|
3216
3667
|
} else if (val !== null && val !== void 0) {
|
|
3217
3668
|
this.writeString(bigBuf, val.toString(), c, false, fmtString !== null ? styleNum : void 0);
|
|
@@ -3294,13 +3745,14 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3294
3745
|
bigBuf.writeUnsafeByte(val ? 1 : 0);
|
|
3295
3746
|
}
|
|
3296
3747
|
writeDateTime(bigBuf, date, colNum) {
|
|
3297
|
-
const oaDate = (date.getTime()
|
|
3748
|
+
const oaDate = timeToOaDate(date.getTime(), this._oaEpoch);
|
|
3298
3749
|
this.writeDouble(bigBuf, oaDate, colNum, 1);
|
|
3299
3750
|
}
|
|
3300
3751
|
_getDateWidth(_date) {
|
|
3301
3752
|
return 19;
|
|
3302
3753
|
}
|
|
3303
3754
|
writeString(bigBuf, val, colNum, bolded = false, styleOverride) {
|
|
3755
|
+
assertCellTextLength(val);
|
|
3304
3756
|
let index;
|
|
3305
3757
|
if (this.sstDic.has(val)) {
|
|
3306
3758
|
index = this.sstDic.get(val);
|
|
@@ -3326,8 +3778,8 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3326
3778
|
bigBuf.writeByte(159);
|
|
3327
3779
|
bigBuf.writeByte(1);
|
|
3328
3780
|
bigBuf.writeByte(8);
|
|
3329
|
-
bigBuf.writeInt32LE(this.sstCntUnique);
|
|
3330
3781
|
bigBuf.writeInt32LE(this.sstCntAll);
|
|
3782
|
+
bigBuf.writeInt32LE(this.sstCntUnique);
|
|
3331
3783
|
for (const [txt] of this.sstDic) {
|
|
3332
3784
|
const txtLen = txt.length;
|
|
3333
3785
|
bigBuf.writeByte(19);
|
|
@@ -3380,8 +3832,61 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3380
3832
|
* Must be awaited before the file is fully written.
|
|
3381
3833
|
*/
|
|
3382
3834
|
finalize() {
|
|
3383
|
-
return new Promise((
|
|
3835
|
+
return new Promise((resolve6, reject) => {
|
|
3836
|
+
if (this._finalized) {
|
|
3837
|
+
reject(new Error("XlsbWriter: already finalized"));
|
|
3838
|
+
return;
|
|
3839
|
+
}
|
|
3840
|
+
this._finalized = true;
|
|
3841
|
+
let settled = false;
|
|
3842
|
+
const cleanupTemp = () => {
|
|
3843
|
+
try {
|
|
3844
|
+
if (fs.existsSync(this._tempPath)) fs.rmSync(this._tempPath);
|
|
3845
|
+
} catch {
|
|
3846
|
+
}
|
|
3847
|
+
};
|
|
3848
|
+
const fail = (err) => {
|
|
3849
|
+
if (settled) return;
|
|
3850
|
+
settled = true;
|
|
3851
|
+
try {
|
|
3852
|
+
this.archive.abort();
|
|
3853
|
+
} catch {
|
|
3854
|
+
}
|
|
3855
|
+
try {
|
|
3856
|
+
this.output.destroy();
|
|
3857
|
+
} catch {
|
|
3858
|
+
}
|
|
3859
|
+
cleanupTemp();
|
|
3860
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
3861
|
+
};
|
|
3862
|
+
const succeed = () => {
|
|
3863
|
+
if (settled) return;
|
|
3864
|
+
if (this._outputError) {
|
|
3865
|
+
fail(this._outputError);
|
|
3866
|
+
return;
|
|
3867
|
+
}
|
|
3868
|
+
settled = true;
|
|
3869
|
+
try {
|
|
3870
|
+
const fd = fs.openSync(this._tempPath, "r+");
|
|
3871
|
+
try {
|
|
3872
|
+
fs.fsyncSync(fd);
|
|
3873
|
+
} finally {
|
|
3874
|
+
fs.closeSync(fd);
|
|
3875
|
+
}
|
|
3876
|
+
try {
|
|
3877
|
+
const existing = fs.statSync(this._targetPath);
|
|
3878
|
+
fs.chmodSync(this._tempPath, existing.mode & 511);
|
|
3879
|
+
} catch {
|
|
3880
|
+
}
|
|
3881
|
+
fs.renameSync(this._tempPath, this._targetPath);
|
|
3882
|
+
resolve6();
|
|
3883
|
+
} catch (err) {
|
|
3884
|
+
cleanupTemp();
|
|
3885
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
3886
|
+
}
|
|
3887
|
+
};
|
|
3384
3888
|
try {
|
|
3889
|
+
if (this._outputError) throw this._outputError;
|
|
3385
3890
|
this.saveSst();
|
|
3386
3891
|
this.archive.append(this._buildStylesBin(), { name: "xl/styles.bin" });
|
|
3387
3892
|
const wbBuffers = [];
|
|
@@ -3478,11 +3983,12 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3478
3983
|
</Relationships>`;
|
|
3479
3984
|
this.archive.append(wsRels, { name: `xl/worksheets/_rels/${sheet.nameInArchive}.rels` });
|
|
3480
3985
|
}
|
|
3481
|
-
this.output.
|
|
3482
|
-
this.
|
|
3986
|
+
this.output.once("close", succeed);
|
|
3987
|
+
this.output.once("error", fail);
|
|
3988
|
+
this.archive.once("error", fail);
|
|
3483
3989
|
this.archive.finalize();
|
|
3484
3990
|
} catch (err) {
|
|
3485
|
-
|
|
3991
|
+
fail(err);
|
|
3486
3992
|
}
|
|
3487
3993
|
});
|
|
3488
3994
|
}
|
|
@@ -3490,27 +3996,17 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3490
3996
|
|
|
3491
3997
|
// src/XlsxWriter.ts
|
|
3492
3998
|
var fs2 = __toESM(require("fs"));
|
|
3999
|
+
var path3 = __toESM(require("path"));
|
|
4000
|
+
var import_crypto2 = require("crypto");
|
|
3493
4001
|
var import_archiver2 = __toESM(require("archiver"));
|
|
3494
4002
|
var import_stream2 = require("stream");
|
|
3495
|
-
var COLUMN_LETTERS = (() => {
|
|
3496
|
-
const letters = [];
|
|
3497
|
-
for (let i = 65; i < 91; i++) {
|
|
3498
|
-
letters.push(String.fromCharCode(i));
|
|
3499
|
-
}
|
|
3500
|
-
const temp = [];
|
|
3501
|
-
for (const p of letters) {
|
|
3502
|
-
for (let i = 65; i < 91; i++) {
|
|
3503
|
-
temp.push(p + String.fromCharCode(i));
|
|
3504
|
-
}
|
|
3505
|
-
}
|
|
3506
|
-
letters.push(...temp);
|
|
3507
|
-
return letters;
|
|
3508
|
-
})();
|
|
3509
4003
|
var XlsxWriter = class {
|
|
3510
4004
|
/**
|
|
3511
4005
|
* @param filePath Destination `.xlsx` path on disk.
|
|
3512
4006
|
*/
|
|
3513
4007
|
constructor(filePath) {
|
|
4008
|
+
this._outputError = null;
|
|
4009
|
+
this._finalized = false;
|
|
3514
4010
|
this.sheetCount = 0;
|
|
3515
4011
|
this.sheetList = [];
|
|
3516
4012
|
this.sstArray = [];
|
|
@@ -3524,13 +4020,25 @@ var XlsxWriter = class {
|
|
|
3524
4020
|
this._nextXfIndex = 4;
|
|
3525
4021
|
this.stream = new StreamingSheetState();
|
|
3526
4022
|
this.currentColLetters = [];
|
|
3527
|
-
this.
|
|
4023
|
+
this._targetPath = path3.resolve(filePath);
|
|
4024
|
+
fs2.mkdirSync(path3.dirname(this._targetPath), { recursive: true });
|
|
4025
|
+
this._tempPath = path3.join(
|
|
4026
|
+
path3.dirname(this._targetPath),
|
|
4027
|
+
`.${path3.basename(this._targetPath)}.${(0, import_crypto2.randomUUID)()}.tmp`
|
|
4028
|
+
);
|
|
4029
|
+
this.output = fs2.createWriteStream(this._tempPath);
|
|
4030
|
+
this.output.on("error", (err) => {
|
|
4031
|
+
this._outputError = err;
|
|
4032
|
+
});
|
|
3528
4033
|
this.archive = (0, import_archiver2.default)("zip");
|
|
4034
|
+
this.archive.on("error", (err) => {
|
|
4035
|
+
this._outputError = err;
|
|
4036
|
+
});
|
|
3529
4037
|
this.archive.pipe(this.output);
|
|
3530
4038
|
this._oaEpoch = Date.UTC(1899, 11, 30);
|
|
3531
4039
|
}
|
|
3532
4040
|
_getColumnLetter(colIndex) {
|
|
3533
|
-
return colIndex
|
|
4041
|
+
return columnIndexToLetter(colIndex);
|
|
3534
4042
|
}
|
|
3535
4043
|
/** @deprecated Internal; kept for unit-test access. Prefer sanitizeSheetName(). */
|
|
3536
4044
|
_sanitizeSheetName(name) {
|
|
@@ -3582,6 +4090,11 @@ var XlsxWriter = class {
|
|
|
3582
4090
|
* Start streaming a sheet. Call `writeRow` for each row, then `endSheet`.
|
|
3583
4091
|
*/
|
|
3584
4092
|
startSheet(sheetName, columnCount, headers, options = {}) {
|
|
4093
|
+
assertColumnCount(columnCount);
|
|
4094
|
+
if (headers !== void 0) {
|
|
4095
|
+
assertRowFitsColumns("header", headers.length, columnCount);
|
|
4096
|
+
for (const h of headers) assertCellTextLength(h);
|
|
4097
|
+
}
|
|
3585
4098
|
const { hidden = false, doAutofilter = true, sampleRows } = options;
|
|
3586
4099
|
this.addSheet(sheetName, hidden);
|
|
3587
4100
|
this.stream.begin(columnCount, doAutofilter && headers !== void 0, new BigBuffer());
|
|
@@ -3638,6 +4151,7 @@ var XlsxWriter = class {
|
|
|
3638
4151
|
);
|
|
3639
4152
|
}
|
|
3640
4153
|
this.stream.rowNum++;
|
|
4154
|
+
assertRowCount(this.stream.rowNum);
|
|
3641
4155
|
bigBuf.writeString(`<row r="${this.stream.rowNum}">`);
|
|
3642
4156
|
for (let c = 0; c < row.length; c++) {
|
|
3643
4157
|
const raw = row[c];
|
|
@@ -3701,6 +4215,16 @@ var XlsxWriter = class {
|
|
|
3701
4215
|
} else if (headers) {
|
|
3702
4216
|
columnCount = headers.length;
|
|
3703
4217
|
}
|
|
4218
|
+
if (columnCount > 0) assertColumnCount(columnCount);
|
|
4219
|
+
const totalRows = rows.length + (headers ? 1 : 0);
|
|
4220
|
+
assertRowCount(totalRows);
|
|
4221
|
+
if (headers) {
|
|
4222
|
+
assertRowFitsColumns("header", headers.length, columnCount);
|
|
4223
|
+
for (const h of headers) assertCellTextLength(h);
|
|
4224
|
+
}
|
|
4225
|
+
for (let r = 0; r < rows.length; r++) {
|
|
4226
|
+
assertRowFitsColumns("row", rows[r].length, columnCount);
|
|
4227
|
+
}
|
|
3704
4228
|
const colWidths = initColWidths(columnCount);
|
|
3705
4229
|
const colLetters = new Array(columnCount);
|
|
3706
4230
|
for (let i = 0; i < columnCount; i++) {
|
|
@@ -3712,7 +4236,6 @@ var XlsxWriter = class {
|
|
|
3712
4236
|
updateColWidthsFromRows(colWidths, rows);
|
|
3713
4237
|
bigBuf.writeString('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>');
|
|
3714
4238
|
bigBuf.writeString('<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">');
|
|
3715
|
-
const totalRows = rows.length + (headers ? 1 : 0);
|
|
3716
4239
|
if (totalRows > 0 && columnCount > 0) {
|
|
3717
4240
|
bigBuf.writeString(`<dimension ref="A1:${colLetters[columnCount - 1]}${totalRows}"/>`);
|
|
3718
4241
|
} else {
|
|
@@ -3798,6 +4321,7 @@ var XlsxWriter = class {
|
|
|
3798
4321
|
});
|
|
3799
4322
|
}
|
|
3800
4323
|
_writeStringCell(bigBuf, val, colRef, rowNum, styleOverride) {
|
|
4324
|
+
assertCellTextLength(val);
|
|
3801
4325
|
let index = this.sstMap.get(val);
|
|
3802
4326
|
if (index === void 0) {
|
|
3803
4327
|
index = this.sstArray.length;
|
|
@@ -3819,29 +4343,79 @@ var XlsxWriter = class {
|
|
|
3819
4343
|
return xfIndex;
|
|
3820
4344
|
}
|
|
3821
4345
|
_toOADate(date) {
|
|
3822
|
-
return (date.getTime()
|
|
4346
|
+
return timeToOaDate(date.getTime(), this._oaEpoch);
|
|
3823
4347
|
}
|
|
3824
4348
|
_getDateWidth(_date) {
|
|
3825
4349
|
return 19;
|
|
3826
4350
|
}
|
|
3827
4351
|
/** Finalize the ZIP package and close the output stream. */
|
|
3828
4352
|
finalize() {
|
|
3829
|
-
return new Promise((
|
|
4353
|
+
return new Promise((resolve6, reject) => {
|
|
4354
|
+
if (this._finalized) {
|
|
4355
|
+
reject(new Error("XlsxWriter: already finalized"));
|
|
4356
|
+
return;
|
|
4357
|
+
}
|
|
4358
|
+
this._finalized = true;
|
|
4359
|
+
let settled = false;
|
|
4360
|
+
const cleanupTemp = () => {
|
|
4361
|
+
try {
|
|
4362
|
+
if (fs2.existsSync(this._tempPath)) fs2.rmSync(this._tempPath);
|
|
4363
|
+
} catch {
|
|
4364
|
+
}
|
|
4365
|
+
};
|
|
4366
|
+
const fail = (err) => {
|
|
4367
|
+
if (settled) return;
|
|
4368
|
+
settled = true;
|
|
4369
|
+
try {
|
|
4370
|
+
this.archive.abort();
|
|
4371
|
+
} catch {
|
|
4372
|
+
}
|
|
4373
|
+
try {
|
|
4374
|
+
this.output.destroy();
|
|
4375
|
+
} catch {
|
|
4376
|
+
}
|
|
4377
|
+
cleanupTemp();
|
|
4378
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
4379
|
+
};
|
|
4380
|
+
const succeed = () => {
|
|
4381
|
+
if (settled) return;
|
|
4382
|
+
if (this._outputError) {
|
|
4383
|
+
fail(this._outputError);
|
|
4384
|
+
return;
|
|
4385
|
+
}
|
|
4386
|
+
settled = true;
|
|
4387
|
+
try {
|
|
4388
|
+
const fd = fs2.openSync(this._tempPath, "r+");
|
|
4389
|
+
try {
|
|
4390
|
+
fs2.fsyncSync(fd);
|
|
4391
|
+
} finally {
|
|
4392
|
+
fs2.closeSync(fd);
|
|
4393
|
+
}
|
|
4394
|
+
try {
|
|
4395
|
+
const existing = fs2.statSync(this._targetPath);
|
|
4396
|
+
fs2.chmodSync(this._tempPath, existing.mode & 511);
|
|
4397
|
+
} catch {
|
|
4398
|
+
}
|
|
4399
|
+
fs2.renameSync(this._tempPath, this._targetPath);
|
|
4400
|
+
resolve6();
|
|
4401
|
+
} catch (err) {
|
|
4402
|
+
cleanupTemp();
|
|
4403
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
4404
|
+
}
|
|
4405
|
+
};
|
|
3830
4406
|
try {
|
|
4407
|
+
if (this._outputError) throw this._outputError;
|
|
3831
4408
|
this._writeSharedStrings();
|
|
3832
4409
|
this._writeStyles();
|
|
3833
4410
|
this._writeWorkbook();
|
|
3834
4411
|
this._writeContentTypes();
|
|
3835
4412
|
this._writeRels();
|
|
3836
|
-
this.output.
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
this.archive.on("error", (err) => {
|
|
3840
|
-
reject(err);
|
|
3841
|
-
});
|
|
4413
|
+
this.output.once("close", succeed);
|
|
4414
|
+
this.output.once("error", fail);
|
|
4415
|
+
this.archive.once("error", fail);
|
|
3842
4416
|
this.archive.finalize();
|
|
3843
4417
|
} catch (err) {
|
|
3844
|
-
|
|
4418
|
+
fail(err);
|
|
3845
4419
|
}
|
|
3846
4420
|
});
|
|
3847
4421
|
}
|
|
@@ -3973,13 +4547,13 @@ ${xfEntries.join("\n")}
|
|
|
3973
4547
|
// src/XlsxUpdater.ts
|
|
3974
4548
|
var fs5 = __toESM(require("fs"));
|
|
3975
4549
|
var os = __toESM(require("os"));
|
|
3976
|
-
var
|
|
4550
|
+
var path6 = __toESM(require("path"));
|
|
3977
4551
|
var import_adm_zip2 = __toESM(require("adm-zip"));
|
|
3978
4552
|
|
|
3979
4553
|
// src/atomicFile.ts
|
|
3980
4554
|
var fs3 = __toESM(require("fs"));
|
|
3981
|
-
var
|
|
3982
|
-
var
|
|
4555
|
+
var path4 = __toESM(require("path"));
|
|
4556
|
+
var import_crypto3 = require("crypto");
|
|
3983
4557
|
var realFileOperations = {
|
|
3984
4558
|
exists: (filePath) => fs3.existsSync(filePath),
|
|
3985
4559
|
rename: (sourcePath, destinationPath) => fs3.renameSync(sourcePath, destinationPath),
|
|
@@ -4002,7 +4576,7 @@ function readDestinationMode(target) {
|
|
|
4002
4576
|
}
|
|
4003
4577
|
}
|
|
4004
4578
|
function installTemporaryOutput(temporaryPath, destinationPath, overwrite, operations = realFileOperations) {
|
|
4005
|
-
const backupPath = `${destinationPath}.${(0,
|
|
4579
|
+
const backupPath = `${destinationPath}.${(0, import_crypto3.randomUUID)()}.replacement-backup`;
|
|
4006
4580
|
let backupCreated = false;
|
|
4007
4581
|
let installed = false;
|
|
4008
4582
|
if (operations.exists(destinationPath)) {
|
|
@@ -4050,11 +4624,11 @@ function installTemporaryOutput(temporaryPath, destinationPath, overwrite, opera
|
|
|
4050
4624
|
}
|
|
4051
4625
|
}
|
|
4052
4626
|
function writeBufferAtomically(buffer, destinationPath) {
|
|
4053
|
-
const target =
|
|
4054
|
-
fs3.mkdirSync(
|
|
4055
|
-
const temporaryPath =
|
|
4056
|
-
|
|
4057
|
-
`.${
|
|
4627
|
+
const target = path4.resolve(destinationPath);
|
|
4628
|
+
fs3.mkdirSync(path4.dirname(target), { recursive: true });
|
|
4629
|
+
const temporaryPath = path4.join(
|
|
4630
|
+
path4.dirname(target),
|
|
4631
|
+
`.${path4.basename(target)}.${(0, import_crypto3.randomUUID)()}.tmp`
|
|
4058
4632
|
);
|
|
4059
4633
|
const destinationMode = readDestinationMode(target);
|
|
4060
4634
|
try {
|
|
@@ -4090,8 +4664,8 @@ function writeBufferAtomically(buffer, destinationPath) {
|
|
|
4090
4664
|
|
|
4091
4665
|
// src/streamingZip.ts
|
|
4092
4666
|
var fs4 = __toESM(require("fs"));
|
|
4093
|
-
var
|
|
4094
|
-
var
|
|
4667
|
+
var path5 = __toESM(require("path"));
|
|
4668
|
+
var import_crypto4 = require("crypto");
|
|
4095
4669
|
var import_promises = require("stream/promises");
|
|
4096
4670
|
var import_stream3 = require("stream");
|
|
4097
4671
|
var import_archiver3 = __toESM(require("archiver"));
|
|
@@ -4103,8 +4677,8 @@ function readDestinationMode2(target) {
|
|
|
4103
4677
|
return void 0;
|
|
4104
4678
|
}
|
|
4105
4679
|
}
|
|
4106
|
-
function appendAndWait(archive, source, name) {
|
|
4107
|
-
return new Promise((
|
|
4680
|
+
function appendAndWait(archive, source, name, output) {
|
|
4681
|
+
return new Promise((resolve6, reject) => {
|
|
4108
4682
|
const cleanup = () => {
|
|
4109
4683
|
archive.off("entry", onEntry);
|
|
4110
4684
|
archive.off("error", onError);
|
|
@@ -4112,10 +4686,13 @@ function appendAndWait(archive, source, name) {
|
|
|
4112
4686
|
if (typeof maybeEmitter2.off === "function") {
|
|
4113
4687
|
maybeEmitter2.off("error", onError);
|
|
4114
4688
|
}
|
|
4689
|
+
if (output) {
|
|
4690
|
+
output.off("error", onError);
|
|
4691
|
+
}
|
|
4115
4692
|
};
|
|
4116
4693
|
const onEntry = () => {
|
|
4117
4694
|
cleanup();
|
|
4118
|
-
|
|
4695
|
+
resolve6();
|
|
4119
4696
|
};
|
|
4120
4697
|
const onError = (error) => {
|
|
4121
4698
|
cleanup();
|
|
@@ -4123,6 +4700,9 @@ function appendAndWait(archive, source, name) {
|
|
|
4123
4700
|
};
|
|
4124
4701
|
archive.once("entry", onEntry);
|
|
4125
4702
|
archive.once("error", onError);
|
|
4703
|
+
if (output) {
|
|
4704
|
+
output.once("error", onError);
|
|
4705
|
+
}
|
|
4126
4706
|
const maybeEmitter = source;
|
|
4127
4707
|
if (typeof maybeEmitter.once === "function") {
|
|
4128
4708
|
maybeEmitter.once("error", onError);
|
|
@@ -4136,11 +4716,11 @@ function appendAndWait(archive, source, name) {
|
|
|
4136
4716
|
});
|
|
4137
4717
|
}
|
|
4138
4718
|
async function writeAdmZipStreaming(zip, stagedParts, destinationPath) {
|
|
4139
|
-
const target =
|
|
4140
|
-
fs4.mkdirSync(
|
|
4141
|
-
const temporaryPath =
|
|
4142
|
-
|
|
4143
|
-
`.${
|
|
4719
|
+
const target = path5.resolve(destinationPath);
|
|
4720
|
+
fs4.mkdirSync(path5.dirname(target), { recursive: true });
|
|
4721
|
+
const temporaryPath = path5.join(
|
|
4722
|
+
path5.dirname(target),
|
|
4723
|
+
`.${path5.basename(target)}.${(0, import_crypto4.randomUUID)()}.tmp`
|
|
4144
4724
|
);
|
|
4145
4725
|
const destinationMode = readDestinationMode2(target);
|
|
4146
4726
|
let output = null;
|
|
@@ -4150,18 +4730,32 @@ async function writeAdmZipStreaming(zip, stagedParts, destinationPath) {
|
|
|
4150
4730
|
temporaryPath,
|
|
4151
4731
|
destinationMode !== void 0 ? { mode: destinationMode } : {}
|
|
4152
4732
|
);
|
|
4733
|
+
let outputError = null;
|
|
4734
|
+
output.on("error", (err) => {
|
|
4735
|
+
outputError = err;
|
|
4736
|
+
try {
|
|
4737
|
+
archive == null ? void 0 : archive.abort();
|
|
4738
|
+
} catch {
|
|
4739
|
+
}
|
|
4740
|
+
});
|
|
4153
4741
|
archive = (0, import_archiver3.default)("zip");
|
|
4742
|
+
archive.on("error", (err) => {
|
|
4743
|
+
outputError = err;
|
|
4744
|
+
});
|
|
4154
4745
|
archive.pipe(output);
|
|
4155
4746
|
for (const entry of zip.getEntries()) {
|
|
4747
|
+
if (outputError) throw outputError;
|
|
4156
4748
|
const stagedPath = stagedParts.get(entry.entryName);
|
|
4157
4749
|
if (stagedPath) {
|
|
4158
|
-
await appendAndWait(archive, fs4.createReadStream(stagedPath), entry.entryName);
|
|
4750
|
+
await appendAndWait(archive, fs4.createReadStream(stagedPath), entry.entryName, output);
|
|
4159
4751
|
} else {
|
|
4160
|
-
await appendAndWait(archive, import_stream3.Readable.from([entry.getData()]), entry.entryName);
|
|
4752
|
+
await appendAndWait(archive, import_stream3.Readable.from([entry.getData()]), entry.entryName, output);
|
|
4161
4753
|
}
|
|
4162
4754
|
}
|
|
4755
|
+
if (outputError) throw outputError;
|
|
4163
4756
|
await archive.finalize();
|
|
4164
4757
|
await (0, import_promises.finished)(output);
|
|
4758
|
+
if (outputError) throw outputError;
|
|
4165
4759
|
if (destinationMode !== void 0) {
|
|
4166
4760
|
try {
|
|
4167
4761
|
fs4.chmodSync(temporaryPath, destinationMode);
|
|
@@ -4195,6 +4789,23 @@ async function writeAdmZipStreaming(zip, stagedParts, destinationPath) {
|
|
|
4195
4789
|
}
|
|
4196
4790
|
|
|
4197
4791
|
// src/XlsxUpdater.ts
|
|
4792
|
+
function parseCellRange(ref) {
|
|
4793
|
+
const m = /^\$?([A-Za-z]+)\$?(\d+)(?::\$?([A-Za-z]+)\$?(\d+))?$/.exec(ref.trim());
|
|
4794
|
+
if (!m) return null;
|
|
4795
|
+
const c1 = columnLetterToIndex(m[1].toUpperCase());
|
|
4796
|
+
const r1 = parseInt(m[2], 10);
|
|
4797
|
+
const c2 = m[3] ? columnLetterToIndex(m[3].toUpperCase()) : c1;
|
|
4798
|
+
const r2 = m[4] ? parseInt(m[4], 10) : r1;
|
|
4799
|
+
if (!Number.isFinite(c1) || !Number.isFinite(r1) || !Number.isFinite(c2) || !Number.isFinite(r2)) {
|
|
4800
|
+
return null;
|
|
4801
|
+
}
|
|
4802
|
+
return {
|
|
4803
|
+
c1: Math.min(c1, c2),
|
|
4804
|
+
r1: Math.min(r1, r2),
|
|
4805
|
+
c2: Math.max(c1, c2),
|
|
4806
|
+
r2: Math.max(r1, r2)
|
|
4807
|
+
};
|
|
4808
|
+
}
|
|
4198
4809
|
var XlsxUpdater = class _XlsxUpdater {
|
|
4199
4810
|
/**
|
|
4200
4811
|
* @param path Path to an existing `.xlsx` file.
|
|
@@ -4281,6 +4892,8 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4281
4892
|
const dimRef = totalRows > 0 && lastCol >= 0 ? `A1:${columnIndexToLetter(lastCol)}${totalRows}` : "A1";
|
|
4282
4893
|
this._updatePivotCaches(sheetName, dimRef, rows.length);
|
|
4283
4894
|
this._addRefreshOnLoadToPivotTables();
|
|
4895
|
+
this._removeCalcChain();
|
|
4896
|
+
this._updateTableRefs(sheetName, dimRef);
|
|
4284
4897
|
}
|
|
4285
4898
|
/**
|
|
4286
4899
|
* Replace worksheet rows from a one-pass source without materialising the
|
|
@@ -4353,10 +4966,10 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4353
4966
|
if (outputError) throw outputError;
|
|
4354
4967
|
writtenBytes += data.length;
|
|
4355
4968
|
if (rowOutput.write(data)) return;
|
|
4356
|
-
await new Promise((
|
|
4969
|
+
await new Promise((resolve6, reject) => {
|
|
4357
4970
|
const onDrain = () => {
|
|
4358
4971
|
cleanup();
|
|
4359
|
-
|
|
4972
|
+
resolve6();
|
|
4360
4973
|
};
|
|
4361
4974
|
const onError = (error) => {
|
|
4362
4975
|
cleanup();
|
|
@@ -4396,8 +5009,8 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4396
5009
|
lastKeptEnd = writtenBytes;
|
|
4397
5010
|
}
|
|
4398
5011
|
}
|
|
4399
|
-
await new Promise((
|
|
4400
|
-
rowOutput.once("close",
|
|
5012
|
+
await new Promise((resolve6, reject) => {
|
|
5013
|
+
rowOutput.once("close", resolve6);
|
|
4401
5014
|
rowOutput.once("error", reject);
|
|
4402
5015
|
rowOutput.end();
|
|
4403
5016
|
});
|
|
@@ -4416,6 +5029,8 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4416
5029
|
this._commitSharedStrings();
|
|
4417
5030
|
this._updatePivotCaches(sheetName, dimRef, keptDataRows);
|
|
4418
5031
|
this._addRefreshOnLoadToPivotTables();
|
|
5032
|
+
this._removeCalcChain();
|
|
5033
|
+
this._updateTableRefs(sheetName, dimRef);
|
|
4419
5034
|
if (oldStage && oldStage !== outputPath) {
|
|
4420
5035
|
this._removeTemporaryFile(oldStage);
|
|
4421
5036
|
}
|
|
@@ -4489,7 +5104,7 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4489
5104
|
}
|
|
4490
5105
|
_ensureTemporaryDirectory() {
|
|
4491
5106
|
if (this.temporaryDirectory === null) {
|
|
4492
|
-
this.temporaryDirectory = fs5.mkdtempSync(
|
|
5107
|
+
this.temporaryDirectory = fs5.mkdtempSync(path6.join(os.tmpdir(), "spreadsheet-updater-"));
|
|
4493
5108
|
} else {
|
|
4494
5109
|
try {
|
|
4495
5110
|
fs5.mkdirSync(this.temporaryDirectory, { recursive: true });
|
|
@@ -4499,7 +5114,7 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4499
5114
|
return this.temporaryDirectory;
|
|
4500
5115
|
}
|
|
4501
5116
|
_temporaryPath(suffix) {
|
|
4502
|
-
return
|
|
5117
|
+
return path6.join(this._ensureTemporaryDirectory(), `part-${Date.now()}-${Math.random().toString(16).slice(2)}${suffix}`);
|
|
4503
5118
|
}
|
|
4504
5119
|
_removeTemporaryFile(filePath) {
|
|
4505
5120
|
try {
|
|
@@ -4539,10 +5154,15 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4539
5154
|
const rIdToTarget = {};
|
|
4540
5155
|
if (relsEntry) {
|
|
4541
5156
|
const rels = relsEntry.getData().toString("utf8");
|
|
4542
|
-
const
|
|
4543
|
-
let
|
|
4544
|
-
while ((
|
|
4545
|
-
|
|
5157
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
5158
|
+
let tagMatch;
|
|
5159
|
+
while ((tagMatch = relTagRe.exec(rels)) !== null) {
|
|
5160
|
+
const attrs = parseTagAttributes(tagMatch[0]);
|
|
5161
|
+
const id = attrs["Id"] ?? attrs["id"];
|
|
5162
|
+
const target = attrs["Target"] ?? attrs["target"];
|
|
5163
|
+
if (id !== void 0 && target !== void 0) {
|
|
5164
|
+
rIdToTarget[id] = target;
|
|
5165
|
+
}
|
|
4546
5166
|
}
|
|
4547
5167
|
}
|
|
4548
5168
|
const wbEntry = this.zip.getEntry("xl/workbook.xml");
|
|
@@ -4551,21 +5171,23 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4551
5171
|
let match;
|
|
4552
5172
|
while ((match = sheetRe.exec(wbXml)) !== null) {
|
|
4553
5173
|
const tag = match[0];
|
|
4554
|
-
const
|
|
4555
|
-
const
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
const
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
fullPath = fullPath.substring(1);
|
|
4562
|
-
} else if (!fullPath.startsWith("xl/")) {
|
|
4563
|
-
fullPath = "xl/" + fullPath;
|
|
4564
|
-
}
|
|
5174
|
+
const attrs = parseTagAttributes(tag);
|
|
5175
|
+
const rawName = attrs["name"];
|
|
5176
|
+
const rId = attrs["r:id"] ?? attrs["id"] ?? attrs["rId"];
|
|
5177
|
+
if (rawName === void 0 || rId === void 0) continue;
|
|
5178
|
+
const name = unescapeXml(rawName);
|
|
5179
|
+
const target = rIdToTarget[rId] ?? "";
|
|
5180
|
+
const fullPath = resolveRelTarget(target);
|
|
4565
5181
|
if (fullPath) {
|
|
4566
5182
|
this.sheetNameToPath.set(name, fullPath);
|
|
4567
5183
|
}
|
|
4568
5184
|
}
|
|
5185
|
+
const wbPrMatch = /<workbookPr\b[^>]*>/i.exec(wbXml);
|
|
5186
|
+
if (wbPrMatch) {
|
|
5187
|
+
const wbPrAttrs = parseTagAttributes(wbPrMatch[0]);
|
|
5188
|
+
const v = (wbPrAttrs["date1904"] ?? "").trim().toLowerCase();
|
|
5189
|
+
this._oaEpoch = v === "1" || v === "true" ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30);
|
|
5190
|
+
}
|
|
4569
5191
|
for (const entry of this.zip.getEntries()) {
|
|
4570
5192
|
const name = entry.entryName;
|
|
4571
5193
|
if (/^xl\/pivotCache\/pivotCacheDefinition\d*\.xml$/.test(name)) {
|
|
@@ -4760,7 +5382,7 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4760
5382
|
return `<c r="${colRef}${rowNum}" t="b"${colStyle > 0 ? ` s="${colStyle}"` : ""}><v>${val ? 1 : 0}</v></c>`;
|
|
4761
5383
|
}
|
|
4762
5384
|
if (val instanceof Date) {
|
|
4763
|
-
const oaDate = (val.getTime()
|
|
5385
|
+
const oaDate = timeToOaDate(val.getTime(), this._oaEpoch);
|
|
4764
5386
|
if (Number.isFinite(oaDate)) {
|
|
4765
5387
|
const style = colStyle > 0 ? colStyle : dateStyle !== null ? dateStyle : 0;
|
|
4766
5388
|
return `<c r="${colRef}${rowNum}"${style > 0 ? ` s="${style}"` : ""}><v>${oaDate}</v></c>`;
|
|
@@ -4977,8 +5599,8 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4977
5599
|
}
|
|
4978
5600
|
}
|
|
4979
5601
|
_addRefreshOnLoadToPivotTables() {
|
|
4980
|
-
for (const
|
|
4981
|
-
const entry = this.zip.getEntry(
|
|
5602
|
+
for (const path9 of this.pivotTableDefPaths) {
|
|
5603
|
+
const entry = this.zip.getEntry(path9);
|
|
4982
5604
|
if (!entry) continue;
|
|
4983
5605
|
let xml = entry.getData().toString("utf8");
|
|
4984
5606
|
const rootRe = /<pivotTableDefinition\b[^>]*>/;
|
|
@@ -4986,7 +5608,233 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4986
5608
|
if (!rootMatch || rootMatch[0].includes("refreshOnLoad")) continue;
|
|
4987
5609
|
const newTag = rootMatch[0].replace(/(\/?>)$/, ' refreshOnLoad="1"$1');
|
|
4988
5610
|
xml = xml.substring(0, rootMatch.index) + newTag + xml.substring(rootMatch.index + rootMatch[0].length);
|
|
4989
|
-
this.zip.updateFile(
|
|
5611
|
+
this.zip.updateFile(path9, Buffer.from(xml, "utf8"));
|
|
5612
|
+
}
|
|
5613
|
+
}
|
|
5614
|
+
/**
|
|
5615
|
+
* Remove the stale calculation chain (xl/calcChain.xml). Excel rebuilds it
|
|
5616
|
+
* on open; keeping stale <c> entries for the replaced sheet would force a
|
|
5617
|
+
* repair prompt. Removes the part, its Content_Types override and its
|
|
5618
|
+
* workbook rels entry (xlspy parity).
|
|
5619
|
+
*/
|
|
5620
|
+
_removeCalcChain() {
|
|
5621
|
+
const calcPath = "xl/calcChain.xml";
|
|
5622
|
+
if (!this.zip.getEntry(calcPath) && !this.stagedParts.has(calcPath)) {
|
|
5623
|
+
this._ensureCalcPrFullCalc();
|
|
5624
|
+
return;
|
|
5625
|
+
}
|
|
5626
|
+
try {
|
|
5627
|
+
this.zip.deleteFile(calcPath);
|
|
5628
|
+
} catch {
|
|
5629
|
+
}
|
|
5630
|
+
const stagedCalc = this.stagedParts.get(calcPath);
|
|
5631
|
+
if (stagedCalc) {
|
|
5632
|
+
this.stagedParts.delete(calcPath);
|
|
5633
|
+
try {
|
|
5634
|
+
fs5.rmSync(stagedCalc, { force: true });
|
|
5635
|
+
} catch {
|
|
5636
|
+
}
|
|
5637
|
+
}
|
|
5638
|
+
const ctEntry = this.zip.getEntry("[Content_Types].xml");
|
|
5639
|
+
if (ctEntry || this.stagedParts.has("[Content_Types].xml")) {
|
|
5640
|
+
try {
|
|
5641
|
+
let ct = this._entryData("[Content_Types].xml").toString("utf8");
|
|
5642
|
+
const before = ct;
|
|
5643
|
+
ct = ct.replace(/<Override\b[^>]*PartName="\/xl\/calcChain\.xml"[^>]*\/?>/gi, "");
|
|
5644
|
+
if (ct !== before) {
|
|
5645
|
+
this._writePartText("[Content_Types].xml", ct);
|
|
5646
|
+
}
|
|
5647
|
+
} catch {
|
|
5648
|
+
}
|
|
5649
|
+
}
|
|
5650
|
+
const relsPath = "xl/_rels/workbook.xml.rels";
|
|
5651
|
+
if (this.zip.getEntry(relsPath) || this.stagedParts.has(relsPath)) {
|
|
5652
|
+
try {
|
|
5653
|
+
const rels = this._entryData(relsPath).toString("utf8");
|
|
5654
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
5655
|
+
let m;
|
|
5656
|
+
let out = "";
|
|
5657
|
+
let last = 0;
|
|
5658
|
+
let changed = false;
|
|
5659
|
+
while ((m = relTagRe.exec(rels)) !== null) {
|
|
5660
|
+
const attrs = parseTagAttributes(m[0]);
|
|
5661
|
+
const type = attrs["Type"] ?? attrs["type"] ?? "";
|
|
5662
|
+
const target = attrs["Target"] ?? attrs["target"] ?? "";
|
|
5663
|
+
const isCalcChain = type.endsWith("/calcChain") && resolveRelTarget(target) === "xl/calcChain.xml";
|
|
5664
|
+
if (isCalcChain) {
|
|
5665
|
+
out += rels.substring(last, m.index);
|
|
5666
|
+
last = m.index + m[0].length;
|
|
5667
|
+
changed = true;
|
|
5668
|
+
}
|
|
5669
|
+
}
|
|
5670
|
+
if (changed) {
|
|
5671
|
+
out += rels.substring(last);
|
|
5672
|
+
this._writePartText(relsPath, out);
|
|
5673
|
+
}
|
|
5674
|
+
} catch {
|
|
5675
|
+
}
|
|
5676
|
+
}
|
|
5677
|
+
this._ensureCalcPrFullCalc();
|
|
5678
|
+
}
|
|
5679
|
+
_writePartText(name, text) {
|
|
5680
|
+
if (this.stagedParts.has(name)) {
|
|
5681
|
+
fs5.writeFileSync(this.stagedParts.get(name), text, "utf8");
|
|
5682
|
+
} else {
|
|
5683
|
+
this.zip.updateFile(name, Buffer.from(text, "utf8"));
|
|
5684
|
+
}
|
|
5685
|
+
}
|
|
5686
|
+
_ensureCalcPrFullCalc() {
|
|
5687
|
+
try {
|
|
5688
|
+
const wbPath = "xl/workbook.xml";
|
|
5689
|
+
if (!this.zip.getEntry(wbPath) && !this.stagedParts.has(wbPath)) return;
|
|
5690
|
+
let xml = this._entryData(wbPath).toString("utf8");
|
|
5691
|
+
const calcPrRe = /<(?:[A-Za-z_][\w.-]*:)?calcPr\b[^>]*>/i;
|
|
5692
|
+
const match = calcPrRe.exec(xml);
|
|
5693
|
+
if (match && match.index !== void 0) {
|
|
5694
|
+
const newTag = _XlsxUpdater._replaceOrAddXmlAttribute(match[0], "fullCalcOnLoad", "1");
|
|
5695
|
+
xml = xml.substring(0, match.index) + newTag + xml.substring(match.index + match[0].length);
|
|
5696
|
+
} else {
|
|
5697
|
+
const insertAt = _XlsxUpdater._calcPrInsertPosition(xml);
|
|
5698
|
+
if (insertAt < 0) return;
|
|
5699
|
+
xml = xml.substring(0, insertAt) + '<calcPr fullCalcOnLoad="1"/>' + xml.substring(insertAt);
|
|
5700
|
+
}
|
|
5701
|
+
this._writePartText(wbPath, xml);
|
|
5702
|
+
} catch {
|
|
5703
|
+
}
|
|
5704
|
+
}
|
|
5705
|
+
/**
|
|
5706
|
+
* Insertion offset for a missing `<calcPr>`. The element follows
|
|
5707
|
+
* sheets/externalReferences/definedNames in the workbook schema, so insert
|
|
5708
|
+
* after the last of those that exists instead of right after `<workbook>`.
|
|
5709
|
+
*/
|
|
5710
|
+
static _calcPrInsertPosition(xml) {
|
|
5711
|
+
const prefixed = "(?:[A-Za-z_][\\w.-]*:)?";
|
|
5712
|
+
const candidates = [
|
|
5713
|
+
new RegExp(`</${prefixed}sheets\\s*>`, "i"),
|
|
5714
|
+
new RegExp(`<${prefixed}externalReferences\\b[^>]*/>`, "i"),
|
|
5715
|
+
new RegExp(`<${prefixed}externalReferences\\b[^>]*>[\\s\\S]*?</${prefixed}externalReferences\\s*>`, "i"),
|
|
5716
|
+
new RegExp(`<${prefixed}definedNames\\b[^>]*/>`, "i"),
|
|
5717
|
+
new RegExp(`<${prefixed}definedNames\\b[^>]*>[\\s\\S]*?</${prefixed}definedNames\\s*>`, "i")
|
|
5718
|
+
];
|
|
5719
|
+
let insertAt = -1;
|
|
5720
|
+
for (const re of candidates) {
|
|
5721
|
+
const m = re.exec(xml);
|
|
5722
|
+
if (m && m.index !== void 0) {
|
|
5723
|
+
insertAt = Math.max(insertAt, m.index + m[0].length);
|
|
5724
|
+
}
|
|
5725
|
+
}
|
|
5726
|
+
if (insertAt >= 0) return insertAt;
|
|
5727
|
+
const close = new RegExp(`</${prefixed}workbook\\s*>`, "i").exec(xml);
|
|
5728
|
+
if (close && close.index !== void 0) return close.index;
|
|
5729
|
+
const root = new RegExp(`<${prefixed}workbook\\b[^>]*>`, "i").exec(xml);
|
|
5730
|
+
if (!root || root.index === void 0) return -1;
|
|
5731
|
+
return root.index + root[0].length;
|
|
5732
|
+
}
|
|
5733
|
+
/**
|
|
5734
|
+
* Update ListObject table ranges (xl/tables/*.xml) for the replaced sheet.
|
|
5735
|
+
* Only tables anchored at A1 are updated to the new dimension; tables
|
|
5736
|
+
* elsewhere are left untouched (moving them would corrupt the sheet).
|
|
5737
|
+
* Empty replacements (dimRef "A1") do not shrink multi-column tables.
|
|
5738
|
+
*/
|
|
5739
|
+
_updateTableRefs(sheetName, dimRef) {
|
|
5740
|
+
const sheetPath = this.sheetNameToPath.get(sheetName);
|
|
5741
|
+
if (!sheetPath) return;
|
|
5742
|
+
const slash = sheetPath.lastIndexOf("/");
|
|
5743
|
+
const dir = slash >= 0 ? sheetPath.substring(0, slash + 1) : "";
|
|
5744
|
+
const base = slash >= 0 ? sheetPath.substring(slash + 1) : sheetPath;
|
|
5745
|
+
const sheetRelsPath = `${dir}_rels/${base}.rels`;
|
|
5746
|
+
const sheetRelsEntry = this.zip.getEntry(sheetRelsPath);
|
|
5747
|
+
const hasStagedRels = this.stagedParts.has(sheetRelsPath);
|
|
5748
|
+
if (!sheetRelsEntry && !hasStagedRels) return;
|
|
5749
|
+
const tableTargets = [];
|
|
5750
|
+
try {
|
|
5751
|
+
const relsXml = this._entryData(sheetRelsPath).toString("utf8");
|
|
5752
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
5753
|
+
let m;
|
|
5754
|
+
while ((m = relTagRe.exec(relsXml)) !== null) {
|
|
5755
|
+
const attrs = parseTagAttributes(m[0]);
|
|
5756
|
+
const type = attrs["Type"] ?? attrs["type"] ?? "";
|
|
5757
|
+
const target = attrs["Target"] ?? attrs["target"] ?? "";
|
|
5758
|
+
if (!type.includes("/table") || !target) continue;
|
|
5759
|
+
let tablePath;
|
|
5760
|
+
if (target.startsWith("/")) {
|
|
5761
|
+
tablePath = resolveRelTarget(target);
|
|
5762
|
+
} else {
|
|
5763
|
+
const combined = dir + target;
|
|
5764
|
+
const parts = [];
|
|
5765
|
+
for (const seg of combined.split("/")) {
|
|
5766
|
+
if (seg === "..") parts.pop();
|
|
5767
|
+
else if (seg !== "." && seg !== "") parts.push(seg);
|
|
5768
|
+
}
|
|
5769
|
+
tablePath = parts.join("/");
|
|
5770
|
+
}
|
|
5771
|
+
tableTargets.push(tablePath);
|
|
5772
|
+
}
|
|
5773
|
+
} catch {
|
|
5774
|
+
return;
|
|
5775
|
+
}
|
|
5776
|
+
if (tableTargets.length === 0) return;
|
|
5777
|
+
const tables = [];
|
|
5778
|
+
for (const tablePath of tableTargets) {
|
|
5779
|
+
try {
|
|
5780
|
+
const hasEntry = !!this.zip.getEntry(tablePath);
|
|
5781
|
+
const hasStaged = this.stagedParts.has(tablePath);
|
|
5782
|
+
if (!hasEntry && !hasStaged) continue;
|
|
5783
|
+
const xml = this._entryData(tablePath).toString("utf8");
|
|
5784
|
+
const tableTagRe = /<(?:[A-Za-z_][\w.-]*:)?table\b[^>]*>/i;
|
|
5785
|
+
const tableMatch = tableTagRe.exec(xml);
|
|
5786
|
+
if (!tableMatch || tableMatch.index === void 0) continue;
|
|
5787
|
+
const refMatch = /\bref\s*=\s*("([^"]*)"|'([^']*)')/i.exec(tableMatch[0]);
|
|
5788
|
+
const ref = refMatch ? refMatch[2] ?? refMatch[3] ?? "" : "";
|
|
5789
|
+
tables.push({
|
|
5790
|
+
path: tablePath,
|
|
5791
|
+
xml,
|
|
5792
|
+
tag: tableMatch[0],
|
|
5793
|
+
tagIndex: tableMatch.index,
|
|
5794
|
+
ref,
|
|
5795
|
+
range: parseCellRange(ref),
|
|
5796
|
+
staged: hasStaged
|
|
5797
|
+
});
|
|
5798
|
+
} catch {
|
|
5799
|
+
}
|
|
5800
|
+
}
|
|
5801
|
+
if (tables.length === 0) return;
|
|
5802
|
+
const dimRange = parseCellRange(dimRef);
|
|
5803
|
+
if (!dimRange) return;
|
|
5804
|
+
for (const table of tables) {
|
|
5805
|
+
try {
|
|
5806
|
+
const origRange = table.range;
|
|
5807
|
+
if (!origRange) continue;
|
|
5808
|
+
if (origRange.r1 !== 1 || origRange.c1 !== 0) continue;
|
|
5809
|
+
if (dimRef === "A1" && !/^A1$/i.test(table.ref)) continue;
|
|
5810
|
+
const origCols = origRange.c2 - origRange.c1 + 1;
|
|
5811
|
+
const newCols = dimRange.c2 - dimRange.c1 + 1;
|
|
5812
|
+
if (newCols !== origCols) continue;
|
|
5813
|
+
let lastRow = dimRange.r2;
|
|
5814
|
+
for (const other of tables) {
|
|
5815
|
+
if (other === table || !other.range) continue;
|
|
5816
|
+
if (other.range.r1 <= 1) continue;
|
|
5817
|
+
if (other.range.c1 > origRange.c2 || other.range.c2 < origRange.c1) continue;
|
|
5818
|
+
lastRow = Math.min(lastRow, other.range.r1 - 1);
|
|
5819
|
+
}
|
|
5820
|
+
if (lastRow < 1) continue;
|
|
5821
|
+
const newRef = origRange.c2 === 0 && lastRow === 1 ? "A1" : `A1:${columnIndexToLetter(origRange.c2)}${lastRow}`;
|
|
5822
|
+
let xml = table.xml;
|
|
5823
|
+
const newTag = _XlsxUpdater._replaceOrAddXmlAttribute(table.tag, "ref", newRef);
|
|
5824
|
+
xml = xml.substring(0, table.tagIndex) + newTag + xml.substring(table.tagIndex + table.tag.length);
|
|
5825
|
+
const afRe = /<(?:[A-Za-z_][\w.-]*:)?autoFilter\b[^>]*>/i;
|
|
5826
|
+
const afMatch = afRe.exec(xml);
|
|
5827
|
+
if (afMatch && afMatch.index !== void 0) {
|
|
5828
|
+
const newAf = _XlsxUpdater._replaceOrAddXmlAttribute(afMatch[0], "ref", newRef);
|
|
5829
|
+
xml = xml.substring(0, afMatch.index) + newAf + xml.substring(afMatch.index + afMatch[0].length);
|
|
5830
|
+
}
|
|
5831
|
+
if (table.staged) {
|
|
5832
|
+
fs5.writeFileSync(this.stagedParts.get(table.path), xml, "utf8");
|
|
5833
|
+
} else {
|
|
5834
|
+
this.zip.updateFile(table.path, Buffer.from(xml, "utf8"));
|
|
5835
|
+
}
|
|
5836
|
+
} catch {
|
|
5837
|
+
}
|
|
4990
5838
|
}
|
|
4991
5839
|
}
|
|
4992
5840
|
};
|
|
@@ -4994,78 +5842,8 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4994
5842
|
// src/XlsbUpdater.ts
|
|
4995
5843
|
var fs6 = __toESM(require("fs"));
|
|
4996
5844
|
var os2 = __toESM(require("os"));
|
|
4997
|
-
var
|
|
5845
|
+
var path7 = __toESM(require("path"));
|
|
4998
5846
|
var import_adm_zip3 = __toESM(require("adm-zip"));
|
|
4999
|
-
|
|
5000
|
-
// src/biff12Utils.ts
|
|
5001
|
-
function readVlq(buf, pos) {
|
|
5002
|
-
let value = 0;
|
|
5003
|
-
let shift = 0;
|
|
5004
|
-
let byte;
|
|
5005
|
-
do {
|
|
5006
|
-
byte = buf[pos++];
|
|
5007
|
-
value |= (byte & 127) << shift;
|
|
5008
|
-
shift += 7;
|
|
5009
|
-
} while ((byte & 128) !== 0);
|
|
5010
|
-
return { value, nextPos: pos };
|
|
5011
|
-
}
|
|
5012
|
-
function vlqBytes(value) {
|
|
5013
|
-
const out = [];
|
|
5014
|
-
while (value >= 128) {
|
|
5015
|
-
out.push(value & 127 | 128);
|
|
5016
|
-
value >>>= 7;
|
|
5017
|
-
}
|
|
5018
|
-
out.push(value & 127);
|
|
5019
|
-
return out;
|
|
5020
|
-
}
|
|
5021
|
-
function readRecord(buf, pos, rec) {
|
|
5022
|
-
const idRes = readVlq(buf, pos);
|
|
5023
|
-
if (idRes.nextPos > buf.length) return false;
|
|
5024
|
-
const lenRes = readVlq(buf, idRes.nextPos);
|
|
5025
|
-
if (lenRes.nextPos + lenRes.value > buf.length) return false;
|
|
5026
|
-
rec.headerStart = pos;
|
|
5027
|
-
rec.dataStart = lenRes.nextPos;
|
|
5028
|
-
rec.dataEnd = lenRes.nextPos + lenRes.value;
|
|
5029
|
-
rec.id = idRes.value;
|
|
5030
|
-
rec.len = lenRes.value;
|
|
5031
|
-
return true;
|
|
5032
|
-
}
|
|
5033
|
-
function buildRecord(id, payload) {
|
|
5034
|
-
const idBytes = vlqBytes(id);
|
|
5035
|
-
const lenBytes = vlqBytes(payload.length);
|
|
5036
|
-
const header = Buffer.alloc(idBytes.length + lenBytes.length);
|
|
5037
|
-
idBytes.forEach((b, i) => header[i] = b);
|
|
5038
|
-
lenBytes.forEach((b, i) => header[idBytes.length + i] = b);
|
|
5039
|
-
return Buffer.concat([header, payload]);
|
|
5040
|
-
}
|
|
5041
|
-
function readUtf16(buf, start, charCount) {
|
|
5042
|
-
const end = start + charCount * 2;
|
|
5043
|
-
if (end > buf.length) return "";
|
|
5044
|
-
return buf.toString("utf16le", start, end);
|
|
5045
|
-
}
|
|
5046
|
-
function parseSharedStringsBin(buf) {
|
|
5047
|
-
const values = [];
|
|
5048
|
-
let total = 0;
|
|
5049
|
-
let unique = 0;
|
|
5050
|
-
let endSstOffset = buf.length;
|
|
5051
|
-
const rec = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
5052
|
-
let pos = 0;
|
|
5053
|
-
while (readRecord(buf, pos, rec)) {
|
|
5054
|
-
pos = rec.dataEnd;
|
|
5055
|
-
if (rec.id === 159) {
|
|
5056
|
-
total = buf.readUInt32LE(rec.dataStart);
|
|
5057
|
-
unique = buf.readUInt32LE(rec.dataStart + 4);
|
|
5058
|
-
} else if (rec.id === 19) {
|
|
5059
|
-
const cch = buf.readUInt32LE(rec.dataStart + 1);
|
|
5060
|
-
values.push(readUtf16(buf, rec.dataStart + 5, cch));
|
|
5061
|
-
} else if (rec.id === 160) {
|
|
5062
|
-
endSstOffset = rec.headerStart;
|
|
5063
|
-
}
|
|
5064
|
-
}
|
|
5065
|
-
return { values, total, unique, endSstOffset };
|
|
5066
|
-
}
|
|
5067
|
-
|
|
5068
|
-
// src/XlsbUpdater.ts
|
|
5069
5847
|
var CELL_RECORDS = /* @__PURE__ */ new Set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
|
5070
5848
|
var RK_INT_LOWER = -1 << 29;
|
|
5071
5849
|
var RK_INT_UPPER = (1 << 29) - 1;
|
|
@@ -5169,6 +5947,7 @@ var XlsbUpdater = class {
|
|
|
5169
5947
|
const rwLast = Math.max(0, rows.length + (headers ? 1 : 0) - 1);
|
|
5170
5948
|
const colLast = lastCol;
|
|
5171
5949
|
this._patchPivotCaches(sheetName, rwLast, colLast);
|
|
5950
|
+
this._removeCalcChain();
|
|
5172
5951
|
}
|
|
5173
5952
|
/** Replace worksheet rows from a one-pass source. */
|
|
5174
5953
|
async replaceSheetDataStream(sheetName, rows, options = {}) {
|
|
@@ -5236,10 +6015,10 @@ var XlsbUpdater = class {
|
|
|
5236
6015
|
if (outputError) throw outputError;
|
|
5237
6016
|
writtenBytes += data.length;
|
|
5238
6017
|
if (rowOutput.write(data)) return;
|
|
5239
|
-
await new Promise((
|
|
6018
|
+
await new Promise((resolve6, reject) => {
|
|
5240
6019
|
const onDrain = () => {
|
|
5241
6020
|
cleanup();
|
|
5242
|
-
|
|
6021
|
+
resolve6();
|
|
5243
6022
|
};
|
|
5244
6023
|
const onError = (error) => {
|
|
5245
6024
|
cleanup();
|
|
@@ -5290,8 +6069,8 @@ var XlsbUpdater = class {
|
|
|
5290
6069
|
lastKeptEnd = writtenBytes;
|
|
5291
6070
|
}
|
|
5292
6071
|
}
|
|
5293
|
-
await new Promise((
|
|
5294
|
-
rowOutput.once("close",
|
|
6072
|
+
await new Promise((resolve6, reject) => {
|
|
6073
|
+
rowOutput.once("close", resolve6);
|
|
5295
6074
|
rowOutput.once("error", reject);
|
|
5296
6075
|
rowOutput.end();
|
|
5297
6076
|
});
|
|
@@ -5354,6 +6133,7 @@ var XlsbUpdater = class {
|
|
|
5354
6133
|
Math.max(0, keptDataRows + (headers !== void 0 ? 1 : 0) - 1),
|
|
5355
6134
|
lastCol
|
|
5356
6135
|
);
|
|
6136
|
+
this._removeCalcChain();
|
|
5357
6137
|
if (oldStage && oldStage !== outputPath) this._removeTemporaryFile(oldStage);
|
|
5358
6138
|
createdParts.splice(createdParts.indexOf(outputPath), 1);
|
|
5359
6139
|
} catch (error) {
|
|
@@ -5424,7 +6204,7 @@ var XlsbUpdater = class {
|
|
|
5424
6204
|
}
|
|
5425
6205
|
_ensureTemporaryDirectory() {
|
|
5426
6206
|
if (this.temporaryDirectory === null) {
|
|
5427
|
-
this.temporaryDirectory = fs6.mkdtempSync(
|
|
6207
|
+
this.temporaryDirectory = fs6.mkdtempSync(path7.join(os2.tmpdir(), "spreadsheet-updater-"));
|
|
5428
6208
|
} else {
|
|
5429
6209
|
try {
|
|
5430
6210
|
fs6.mkdirSync(this.temporaryDirectory, { recursive: true });
|
|
@@ -5434,7 +6214,7 @@ var XlsbUpdater = class {
|
|
|
5434
6214
|
return this.temporaryDirectory;
|
|
5435
6215
|
}
|
|
5436
6216
|
_temporaryPath(suffix) {
|
|
5437
|
-
return
|
|
6217
|
+
return path7.join(this._ensureTemporaryDirectory(), `part-${Date.now()}-${Math.random().toString(16).slice(2)}${suffix}`);
|
|
5438
6218
|
}
|
|
5439
6219
|
_removeTemporaryFile(filePath) {
|
|
5440
6220
|
try {
|
|
@@ -5474,10 +6254,15 @@ var XlsbUpdater = class {
|
|
|
5474
6254
|
const rIdToTarget = {};
|
|
5475
6255
|
if (relsEntry) {
|
|
5476
6256
|
const rels = relsEntry.getData().toString("utf8");
|
|
5477
|
-
const
|
|
5478
|
-
let
|
|
5479
|
-
while ((
|
|
5480
|
-
|
|
6257
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
6258
|
+
let tagMatch;
|
|
6259
|
+
while ((tagMatch = relTagRe.exec(rels)) !== null) {
|
|
6260
|
+
const attrs = parseTagAttributes(tagMatch[0]);
|
|
6261
|
+
const id = attrs["Id"] ?? attrs["id"];
|
|
6262
|
+
const target = attrs["Target"] ?? attrs["target"];
|
|
6263
|
+
if (id !== void 0 && target !== void 0) {
|
|
6264
|
+
rIdToTarget[id] = target;
|
|
6265
|
+
}
|
|
5481
6266
|
}
|
|
5482
6267
|
}
|
|
5483
6268
|
const wbEntry = this.zip.getEntry("xl/workbook.bin");
|
|
@@ -5497,12 +6282,24 @@ var XlsbUpdater = class {
|
|
|
5497
6282
|
o += 4;
|
|
5498
6283
|
const name = readUtf16(wbBuf, o, nameLen);
|
|
5499
6284
|
let target = rIdToTarget[rId] ?? "";
|
|
5500
|
-
|
|
5501
|
-
else if (!target.startsWith("xl/")) target = "xl/" + target;
|
|
6285
|
+
target = resolveRelTarget(target);
|
|
5502
6286
|
if (target) {
|
|
5503
6287
|
this.sheetNameToPath.set(name, target);
|
|
5504
6288
|
}
|
|
5505
6289
|
}
|
|
6290
|
+
try {
|
|
6291
|
+
const rec1904 = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
6292
|
+
let p1904 = 0;
|
|
6293
|
+
while (readRecord(wbBuf, p1904, rec1904)) {
|
|
6294
|
+
if (rec1904.id === 153 && rec1904.len >= 4) {
|
|
6295
|
+
const is1904 = (wbBuf[rec1904.dataStart] & 1) !== 0;
|
|
6296
|
+
this._oaEpoch = is1904 ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30);
|
|
6297
|
+
break;
|
|
6298
|
+
}
|
|
6299
|
+
p1904 = rec1904.dataEnd;
|
|
6300
|
+
}
|
|
6301
|
+
} catch {
|
|
6302
|
+
}
|
|
5506
6303
|
}
|
|
5507
6304
|
for (const e of this.zip.getEntries()) {
|
|
5508
6305
|
if (/^xl\/pivotCache\/pivotCacheDefinition\d*\.bin$/.test(e.entryName)) {
|
|
@@ -5611,27 +6408,15 @@ var XlsbUpdater = class {
|
|
|
5611
6408
|
_findDateXf() {
|
|
5612
6409
|
const entry = this.zip.getEntry("xl/styles.bin");
|
|
5613
6410
|
if (!entry) return null;
|
|
5614
|
-
const
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
if (
|
|
5622
|
-
|
|
5623
|
-
xfIndex = 0;
|
|
5624
|
-
continue;
|
|
5625
|
-
}
|
|
5626
|
-
if (rec.id === 618) {
|
|
5627
|
-
inCellXfs = false;
|
|
5628
|
-
continue;
|
|
5629
|
-
}
|
|
5630
|
-
if (inCellXfs && rec.id === 47 && rec.len >= 4) {
|
|
5631
|
-
const numFmtId = stylesBuf.readUInt16LE(rec.dataStart + 2);
|
|
5632
|
-
const isDate = numFmtId >= 14 && numFmtId <= 22 || numFmtId >= 45 && numFmtId <= 47;
|
|
5633
|
-
if (isDate) return xfIndex;
|
|
5634
|
-
xfIndex++;
|
|
6411
|
+
const reader = new BiffReaderWriter(entry.getData());
|
|
6412
|
+
while (reader.readStyles()) {
|
|
6413
|
+
}
|
|
6414
|
+
const numFmtIds = reader._xfIndexToNumFmtId;
|
|
6415
|
+
const customDates = reader._customNumFmts;
|
|
6416
|
+
for (let i = 0; i < numFmtIds.length; i++) {
|
|
6417
|
+
const numFmtId = numFmtIds[i];
|
|
6418
|
+
if (numFmtId >= 14 && numFmtId <= 22 || numFmtId >= 45 && numFmtId <= 47 || customDates.has(numFmtId)) {
|
|
6419
|
+
return i;
|
|
5635
6420
|
}
|
|
5636
6421
|
}
|
|
5637
6422
|
return null;
|
|
@@ -5711,7 +6496,7 @@ var XlsbUpdater = class {
|
|
|
5711
6496
|
return buildRecord(4, payload);
|
|
5712
6497
|
}
|
|
5713
6498
|
if (val instanceof Date) {
|
|
5714
|
-
const oaDate = (val.getTime()
|
|
6499
|
+
const oaDate = timeToOaDate(val.getTime(), this._oaEpoch);
|
|
5715
6500
|
if (Number.isFinite(oaDate)) {
|
|
5716
6501
|
const style = colStyle > 0 ? colStyle : dateXf !== null ? dateXf : 0;
|
|
5717
6502
|
const payload = Buffer.alloc(16);
|
|
@@ -5778,10 +6563,10 @@ var XlsbUpdater = class {
|
|
|
5778
6563
|
const rec = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
5779
6564
|
let pos = 0;
|
|
5780
6565
|
while (readRecord(originalSheet, pos, rec)) {
|
|
5781
|
-
if (rec.id ===
|
|
6566
|
+
if (rec.id === 148 && rec.len >= 16) {
|
|
5782
6567
|
const shift = rec.dataStart >= rowsEnd ? delta : 0;
|
|
5783
|
-
patchInt32(rec.dataStart + shift +
|
|
5784
|
-
patchInt32(rec.dataStart + shift +
|
|
6568
|
+
patchInt32(rec.dataStart + shift + 4, lastRowIdx);
|
|
6569
|
+
patchInt32(rec.dataStart + shift + 12, lastCol);
|
|
5785
6570
|
break;
|
|
5786
6571
|
}
|
|
5787
6572
|
pos = rec.dataEnd;
|
|
@@ -5820,8 +6605,8 @@ var XlsbUpdater = class {
|
|
|
5820
6605
|
return { rowsStart, rowsEnd, autoFilterRanges };
|
|
5821
6606
|
}
|
|
5822
6607
|
/**
|
|
5823
|
-
* Keep the sheet dimension record (
|
|
5824
|
-
* Excel uses its rwLast (payload offset
|
|
6608
|
+
* Keep the sheet dimension record (0x94) in sync with the new data.
|
|
6609
|
+
* Excel uses its rwLast (payload offset 4) and colLast (payload offset 12)
|
|
5825
6610
|
* fields to read the full row range; stale values make the last written row
|
|
5826
6611
|
* invisible to Excel (and drop it from pivot refreshes).
|
|
5827
6612
|
*/
|
|
@@ -5829,9 +6614,9 @@ var XlsbUpdater = class {
|
|
|
5829
6614
|
const rec = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
5830
6615
|
let pos = 0;
|
|
5831
6616
|
while (readRecord(sheetBuf, pos, rec)) {
|
|
5832
|
-
if (rec.id ===
|
|
5833
|
-
sheetBuf.writeInt32LE(lastRowIdx, rec.dataStart +
|
|
5834
|
-
sheetBuf.writeInt32LE(lastCol, rec.dataStart +
|
|
6617
|
+
if (rec.id === 148 && rec.len >= 16) {
|
|
6618
|
+
sheetBuf.writeInt32LE(lastRowIdx, rec.dataStart + 4);
|
|
6619
|
+
sheetBuf.writeInt32LE(lastCol, rec.dataStart + 12);
|
|
5835
6620
|
return;
|
|
5836
6621
|
}
|
|
5837
6622
|
pos = rec.dataEnd;
|
|
@@ -5841,8 +6626,8 @@ var XlsbUpdater = class {
|
|
|
5841
6626
|
// Pivot cache integration
|
|
5842
6627
|
// ------------------------------------------------------------------
|
|
5843
6628
|
_patchPivotCaches(sheetName, rwLast, colLast) {
|
|
5844
|
-
for (const
|
|
5845
|
-
const entry = this.zip.getEntry(
|
|
6629
|
+
for (const path9 of this.pivotCacheDefPaths) {
|
|
6630
|
+
const entry = this.zip.getEntry(path9);
|
|
5846
6631
|
if (!entry) continue;
|
|
5847
6632
|
const buf = Buffer.from(entry.getData());
|
|
5848
6633
|
const rec = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
@@ -5874,16 +6659,84 @@ var XlsbUpdater = class {
|
|
|
5874
6659
|
}
|
|
5875
6660
|
}
|
|
5876
6661
|
if (patched) {
|
|
5877
|
-
this.zip.updateFile(
|
|
6662
|
+
this.zip.updateFile(path9, buf);
|
|
6663
|
+
}
|
|
6664
|
+
}
|
|
6665
|
+
}
|
|
6666
|
+
/**
|
|
6667
|
+
* Remove the stale calculation chain (xl/calcChain.bin). Excel rebuilds it
|
|
6668
|
+
* on open. Removes the part, its Content_Types override and its workbook
|
|
6669
|
+
* rels entry. XLSB tables are not patched (documented limitation).
|
|
6670
|
+
*/
|
|
6671
|
+
_removeCalcChain() {
|
|
6672
|
+
const calcPath = "xl/calcChain.bin";
|
|
6673
|
+
if (!this.zip.getEntry(calcPath) && !this.stagedParts.has(calcPath)) return;
|
|
6674
|
+
try {
|
|
6675
|
+
this.zip.deleteFile(calcPath);
|
|
6676
|
+
} catch {
|
|
6677
|
+
}
|
|
6678
|
+
const stagedCalc = this.stagedParts.get(calcPath);
|
|
6679
|
+
if (stagedCalc) {
|
|
6680
|
+
this.stagedParts.delete(calcPath);
|
|
6681
|
+
try {
|
|
6682
|
+
fs6.rmSync(stagedCalc, { force: true });
|
|
6683
|
+
} catch {
|
|
6684
|
+
}
|
|
6685
|
+
}
|
|
6686
|
+
const ctPath = "[Content_Types].xml";
|
|
6687
|
+
if (this.zip.getEntry(ctPath) || this.stagedParts.has(ctPath)) {
|
|
6688
|
+
try {
|
|
6689
|
+
const ct = this._entryData(ctPath).toString("utf8");
|
|
6690
|
+
const updated = ct.replace(
|
|
6691
|
+
/<Override\b[^>]*PartName="\/xl\/calcChain\.bin"[^>]*\/?>/gi,
|
|
6692
|
+
""
|
|
6693
|
+
);
|
|
6694
|
+
if (updated !== ct) this._writePartText(ctPath, updated);
|
|
6695
|
+
} catch {
|
|
6696
|
+
}
|
|
6697
|
+
}
|
|
6698
|
+
const relsPath = "xl/_rels/workbook.bin.rels";
|
|
6699
|
+
if (this.zip.getEntry(relsPath) || this.stagedParts.has(relsPath)) {
|
|
6700
|
+
try {
|
|
6701
|
+
const rels = this._entryData(relsPath).toString("utf8");
|
|
6702
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
6703
|
+
let m;
|
|
6704
|
+
let out = "";
|
|
6705
|
+
let last = 0;
|
|
6706
|
+
let changed = false;
|
|
6707
|
+
while ((m = relTagRe.exec(rels)) !== null) {
|
|
6708
|
+
const attrs = parseTagAttributes(m[0]);
|
|
6709
|
+
const type = attrs["Type"] ?? attrs["type"] ?? "";
|
|
6710
|
+
const target = attrs["Target"] ?? attrs["target"] ?? "";
|
|
6711
|
+
if (type.endsWith("/calcChain") && resolveRelTarget(target) === "xl/calcChain.bin") {
|
|
6712
|
+
out += rels.substring(last, m.index);
|
|
6713
|
+
last = m.index + m[0].length;
|
|
6714
|
+
changed = true;
|
|
6715
|
+
}
|
|
6716
|
+
}
|
|
6717
|
+
if (changed) {
|
|
6718
|
+
out += rels.substring(last);
|
|
6719
|
+
this._writePartText(relsPath, out);
|
|
6720
|
+
}
|
|
6721
|
+
} catch {
|
|
5878
6722
|
}
|
|
5879
6723
|
}
|
|
5880
6724
|
}
|
|
6725
|
+
/** Write an XML/text part to its staged file when present, else into the ZIP. */
|
|
6726
|
+
_writePartText(name, text) {
|
|
6727
|
+
const stagedPath = this.stagedParts.get(name);
|
|
6728
|
+
if (stagedPath) {
|
|
6729
|
+
fs6.writeFileSync(stagedPath, text, "utf8");
|
|
6730
|
+
} else {
|
|
6731
|
+
this.zip.updateFile(name, Buffer.from(text, "utf8"));
|
|
6732
|
+
}
|
|
6733
|
+
}
|
|
5881
6734
|
};
|
|
5882
6735
|
|
|
5883
6736
|
// src/ExcelConverter.ts
|
|
5884
6737
|
var fs7 = __toESM(require("fs"));
|
|
5885
|
-
var
|
|
5886
|
-
var
|
|
6738
|
+
var path8 = __toESM(require("path"));
|
|
6739
|
+
var import_crypto5 = require("crypto");
|
|
5887
6740
|
var import_child_process = require("child_process");
|
|
5888
6741
|
var POWERSHELL_CONVERSION_SCRIPT = `
|
|
5889
6742
|
$ErrorActionPreference = 'Stop'
|
|
@@ -5929,14 +6782,14 @@ function errorMessage2(error) {
|
|
|
5929
6782
|
return error instanceof Error ? error.message : String(error);
|
|
5930
6783
|
}
|
|
5931
6784
|
function validateConversionPaths(inputPath, outputPath, inputExtension, outputExtension, overwrite) {
|
|
5932
|
-
const input =
|
|
5933
|
-
const output =
|
|
5934
|
-
if (
|
|
6785
|
+
const input = path8.resolve(inputPath);
|
|
6786
|
+
const output = path8.resolve(outputPath);
|
|
6787
|
+
if (path8.extname(input).toLowerCase() !== inputExtension) {
|
|
5935
6788
|
throw new Error(
|
|
5936
6789
|
`Excel conversion: source must have the ${inputExtension} extension: ${inputPath}`
|
|
5937
6790
|
);
|
|
5938
6791
|
}
|
|
5939
|
-
if (
|
|
6792
|
+
if (path8.extname(output).toLowerCase() !== outputExtension) {
|
|
5940
6793
|
throw new Error(
|
|
5941
6794
|
`Excel conversion: destination must have the ${outputExtension} extension: ${outputPath}`
|
|
5942
6795
|
);
|
|
@@ -5947,8 +6800,8 @@ function validateConversionPaths(inputPath, outputPath, inputExtension, outputEx
|
|
|
5947
6800
|
if (!fs7.statSync(input).isFile()) {
|
|
5948
6801
|
throw new Error(`Excel conversion: source path is not a file: ${inputPath}`);
|
|
5949
6802
|
}
|
|
5950
|
-
if (!fs7.existsSync(
|
|
5951
|
-
throw new Error(`Excel conversion: destination directory not found: ${
|
|
6803
|
+
if (!fs7.existsSync(path8.dirname(output))) {
|
|
6804
|
+
throw new Error(`Excel conversion: destination directory not found: ${path8.dirname(output)}`);
|
|
5952
6805
|
}
|
|
5953
6806
|
if (fs7.existsSync(output)) {
|
|
5954
6807
|
if (fs7.statSync(output).isDirectory()) {
|
|
@@ -5973,7 +6826,7 @@ function runPowerShellConversion(inputPath, outputPath, fileFormat) {
|
|
|
5973
6826
|
JUSTYBASE_EXCEL_OUTPUT: outputPath,
|
|
5974
6827
|
JUSTYBASE_EXCEL_FORMAT: String(fileFormat)
|
|
5975
6828
|
};
|
|
5976
|
-
return new Promise((
|
|
6829
|
+
return new Promise((resolve6, reject) => {
|
|
5977
6830
|
var _a, _b, _c, _d;
|
|
5978
6831
|
let stdout = "";
|
|
5979
6832
|
let stderr = "";
|
|
@@ -6008,7 +6861,7 @@ function runPowerShellConversion(inputPath, outputPath, fileFormat) {
|
|
|
6008
6861
|
if (settled) return;
|
|
6009
6862
|
settled = true;
|
|
6010
6863
|
if (code === 0) {
|
|
6011
|
-
|
|
6864
|
+
resolve6();
|
|
6012
6865
|
return;
|
|
6013
6866
|
}
|
|
6014
6867
|
const details = (stderr || stdout).replace(/\s+/g, " ").trim();
|
|
@@ -6029,9 +6882,9 @@ async function convertExcelFile(inputPath, outputPath, inputExtension, outputExt
|
|
|
6029
6882
|
if (process.platform !== "win32") {
|
|
6030
6883
|
throw new Error("Excel conversion requires Windows with desktop Microsoft Excel and COM automation.");
|
|
6031
6884
|
}
|
|
6032
|
-
const temporaryOutput =
|
|
6033
|
-
|
|
6034
|
-
`.${
|
|
6885
|
+
const temporaryOutput = path8.join(
|
|
6886
|
+
path8.dirname(output),
|
|
6887
|
+
`.${path8.basename(output, outputExtension)}.${(0, import_crypto5.randomUUID)()}.conversion${outputExtension}`
|
|
6035
6888
|
);
|
|
6036
6889
|
try {
|
|
6037
6890
|
await runPowerShellConversion(input, temporaryOutput, fileFormat);
|
|
@@ -6063,6 +6916,9 @@ function convertXlsxToXlsb(inputPath, outputPath, options) {
|
|
|
6063
6916
|
ExcelReaderAbstract,
|
|
6064
6917
|
F,
|
|
6065
6918
|
INVALID_SHEET_NAME_CHARS,
|
|
6919
|
+
MAX_CELL_CHARS,
|
|
6920
|
+
MAX_COLS,
|
|
6921
|
+
MAX_ROWS,
|
|
6066
6922
|
ReaderFactory,
|
|
6067
6923
|
StreamingSheetState,
|
|
6068
6924
|
XlsbReader,
|
|
@@ -6073,13 +6929,25 @@ function convertXlsxToXlsb(inputPath, outputPath, options) {
|
|
|
6073
6929
|
XlsxUpdater,
|
|
6074
6930
|
XlsxWriter,
|
|
6075
6931
|
applyHeaderWidths,
|
|
6932
|
+
assertCellTextLength,
|
|
6933
|
+
assertColumnCount,
|
|
6934
|
+
assertRowCount,
|
|
6935
|
+
assertRowFitsColumns,
|
|
6936
|
+
columnIndexToLetter,
|
|
6937
|
+
columnLetterToIndex,
|
|
6076
6938
|
convertXlsbToXlsx,
|
|
6077
6939
|
convertXlsxToXlsb,
|
|
6078
6940
|
defaultColWidth,
|
|
6941
|
+
escapeXmlText,
|
|
6079
6942
|
getFormat,
|
|
6080
6943
|
initColWidths,
|
|
6081
6944
|
isFormattedCell,
|
|
6945
|
+
parseSharedStringsXml,
|
|
6946
|
+
parseTagAttributes,
|
|
6947
|
+
resolveRelTarget,
|
|
6082
6948
|
sanitizeSheetName,
|
|
6949
|
+
trimTrailingEmptyRows,
|
|
6950
|
+
unescapeXml,
|
|
6083
6951
|
unwrapCell,
|
|
6084
6952
|
updateColWidthsFromRows
|
|
6085
6953
|
});
|