@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/esm/index.js
CHANGED
|
@@ -112,6 +112,143 @@ var BigBuffer = class {
|
|
|
112
112
|
}
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
+
// src/Formats.ts
|
|
116
|
+
var F = {
|
|
117
|
+
THOUSANDS_SEP: "#,##0",
|
|
118
|
+
CURRENCY_PLN: '#,##0.00 "z\u0142"',
|
|
119
|
+
CURRENCY_EUR: "#,##0.00 \u20AC",
|
|
120
|
+
PERCENTAGE: "0%",
|
|
121
|
+
SCIENTIFIC: "0.00E+00",
|
|
122
|
+
TWO_DECIMALS: "#,##0.00",
|
|
123
|
+
TEXT: "@",
|
|
124
|
+
LEADING_ZEROS: "000000000",
|
|
125
|
+
DATE_SHORT: "dd.mm.yyyy",
|
|
126
|
+
DATE_LONG: "d mmmm yyyy",
|
|
127
|
+
DATE_DAY_MONTH_YEAR: "dd-mm-yyyy",
|
|
128
|
+
DATE_ISO: "yyyy-mm-dd",
|
|
129
|
+
DATE_MONTH_YEAR: "mmmm yyyy",
|
|
130
|
+
DATE_WEEKDAY: "dddd, d mmmm yyyy",
|
|
131
|
+
DATE_DAY_MONTH: "d mmmm",
|
|
132
|
+
DATE_YEAR_ONLY: "yyyy",
|
|
133
|
+
DATETIME_SHORT: "dd.mm.yyyy hh:mm",
|
|
134
|
+
DATETIME_LONG: "d mmmm yyyy hh:mm:ss",
|
|
135
|
+
TIME_HH_MM: "hh:mm",
|
|
136
|
+
TIME_HH_MM_SS: "hh:mm:ss",
|
|
137
|
+
TIME_12H: "h:mm AM/PM",
|
|
138
|
+
DATETIME_24H: "dd.mm.yyyy hh:mm:ss",
|
|
139
|
+
DATETIME_ISO: 'yyyy-mm-dd"T"hh:mm:ss',
|
|
140
|
+
TIME_MS: "hh:mm:ss.000"
|
|
141
|
+
};
|
|
142
|
+
function isFormattedCell(val) {
|
|
143
|
+
return val !== null && val !== void 0 && typeof val === "object" && "format" in val && typeof val.format === "string";
|
|
144
|
+
}
|
|
145
|
+
function unwrapCell(val) {
|
|
146
|
+
if (isFormattedCell(val)) {
|
|
147
|
+
return val.value;
|
|
148
|
+
}
|
|
149
|
+
return val;
|
|
150
|
+
}
|
|
151
|
+
function getFormat(val) {
|
|
152
|
+
if (isFormattedCell(val)) {
|
|
153
|
+
return val.format;
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/writerHelpers.ts
|
|
159
|
+
var OA_EPOCH_1900 = Date.UTC(1899, 11, 30);
|
|
160
|
+
var MARCH_1_1900 = Date.UTC(1900, 2, 1);
|
|
161
|
+
function oaDateToTime(oaDate, epochMs) {
|
|
162
|
+
let serial = oaDate;
|
|
163
|
+
if (epochMs === OA_EPOCH_1900 && serial >= 1 && serial < 60) {
|
|
164
|
+
serial += 1;
|
|
165
|
+
}
|
|
166
|
+
return Math.round(serial * 864e5) + epochMs;
|
|
167
|
+
}
|
|
168
|
+
function timeToOaDate(timeMs, epochMs) {
|
|
169
|
+
let serial = (timeMs - epochMs) / 864e5;
|
|
170
|
+
if (epochMs === OA_EPOCH_1900 && timeMs < MARCH_1_1900) {
|
|
171
|
+
serial -= 1;
|
|
172
|
+
}
|
|
173
|
+
return serial;
|
|
174
|
+
}
|
|
175
|
+
var INVALID_SHEET_NAME_CHARS = /[\\/*?[\]:]/g;
|
|
176
|
+
function sanitizeSheetName(name, sheetCount) {
|
|
177
|
+
if (!name || typeof name !== "string") {
|
|
178
|
+
return `Sheet${sheetCount + 1}`;
|
|
179
|
+
}
|
|
180
|
+
let sanitized = name.replace(INVALID_SHEET_NAME_CHARS, "_");
|
|
181
|
+
if (sanitized.length > 31) {
|
|
182
|
+
sanitized = sanitized.substring(0, 31);
|
|
183
|
+
}
|
|
184
|
+
if (sanitized.trim().length === 0) {
|
|
185
|
+
sanitized = `Sheet${sheetCount + 1}`;
|
|
186
|
+
}
|
|
187
|
+
return sanitized;
|
|
188
|
+
}
|
|
189
|
+
var MAX_ROWS = 1048576;
|
|
190
|
+
var MAX_COLS = 16384;
|
|
191
|
+
var MAX_CELL_CHARS = 32767;
|
|
192
|
+
function assertCellTextLength(text) {
|
|
193
|
+
if (text.length > MAX_CELL_CHARS) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`Excel cell text exceeds ${MAX_CELL_CHARS} UTF-16 code units (got ${text.length}).`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function assertColumnCount(columnCount) {
|
|
200
|
+
if (!Number.isInteger(columnCount) || columnCount < 1 || columnCount > MAX_COLS) {
|
|
201
|
+
throw new Error(
|
|
202
|
+
`Excel column count must be 1..${MAX_COLS} (got ${columnCount}).`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function assertRowFitsColumns(kind, length, columnCount) {
|
|
207
|
+
if (length > columnCount) {
|
|
208
|
+
throw new Error(
|
|
209
|
+
`Excel ${kind} length ${length} exceeds columnCount ${columnCount}.`
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function assertRowCount(totalRows) {
|
|
214
|
+
if (totalRows > MAX_ROWS) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
`Excel row count exceeds ${MAX_ROWS} (got ${totalRows}).`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function initColWidths(columnCount) {
|
|
221
|
+
return new Array(columnCount).fill(-1);
|
|
222
|
+
}
|
|
223
|
+
function applyHeaderWidths(colWidths, headers, columnCount) {
|
|
224
|
+
for (let i = 0; i < columnCount; i++) {
|
|
225
|
+
const len = headers[i] ? headers[i].length + 1 : 0;
|
|
226
|
+
let width = 1.25 * len + 2;
|
|
227
|
+
if (width > 80) width = 80;
|
|
228
|
+
if (colWidths[i] < width) colWidths[i] = width;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function updateColWidthsFromRows(colWidths, rows, sampleLimit = 100) {
|
|
232
|
+
for (let r = 0; r < Math.min(rows.length, sampleLimit); r++) {
|
|
233
|
+
const row = rows[r];
|
|
234
|
+
for (let c = 0; c < row.length; c++) {
|
|
235
|
+
const raw = row[c];
|
|
236
|
+
if (raw === null || raw === void 0) continue;
|
|
237
|
+
const val = isFormattedCell(raw) ? raw.value : raw;
|
|
238
|
+
const len = val instanceof Date ? 20 : String(val).length + 1;
|
|
239
|
+
let width = 1.25 * len + 2;
|
|
240
|
+
if (width > 80) width = 80;
|
|
241
|
+
if (colWidths[c] < width) colWidths[c] = width;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function defaultColWidth(width, floor = false) {
|
|
246
|
+
if (width > 0) {
|
|
247
|
+
return floor ? Math.floor(width) : width;
|
|
248
|
+
}
|
|
249
|
+
return 10;
|
|
250
|
+
}
|
|
251
|
+
|
|
115
252
|
// src/ExcelReaderAbstract.ts
|
|
116
253
|
var ExcelReaderAbstract = class {
|
|
117
254
|
constructor() {
|
|
@@ -126,8 +263,7 @@ var ExcelReaderAbstract = class {
|
|
|
126
263
|
dispose() {
|
|
127
264
|
}
|
|
128
265
|
getDateTimeFromOaDate(oaDate) {
|
|
129
|
-
|
|
130
|
-
return new Date(ms);
|
|
266
|
+
return new Date(oaDateToTime(oaDate, this._oaEpoch));
|
|
131
267
|
}
|
|
132
268
|
};
|
|
133
269
|
|
|
@@ -387,6 +523,208 @@ var BiffReaderWriter = class {
|
|
|
387
523
|
|
|
388
524
|
// src/XlsbReader.ts
|
|
389
525
|
import AdmZip from "adm-zip";
|
|
526
|
+
|
|
527
|
+
// src/xmlUtils.ts
|
|
528
|
+
function hasInvalidControlChars(str) {
|
|
529
|
+
for (let i = 0; i < str.length; i++) {
|
|
530
|
+
const c = str.charCodeAt(i);
|
|
531
|
+
if (c < 32 && c !== 9 && c !== 10 && c !== 13) {
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
function stripInvalidControlChars(str) {
|
|
538
|
+
let out = "";
|
|
539
|
+
for (let i = 0; i < str.length; i++) {
|
|
540
|
+
const c = str.charCodeAt(i);
|
|
541
|
+
if (c === 9 || c === 10 || c === 13 || c >= 32 && c <= 55295 || c >= 57344 && c <= 65533) {
|
|
542
|
+
out += str[i];
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return out;
|
|
546
|
+
}
|
|
547
|
+
function escapeXmlText(str) {
|
|
548
|
+
if (!/&|<|>|"|'/.test(str) && !hasInvalidControlChars(str)) {
|
|
549
|
+
return str;
|
|
550
|
+
}
|
|
551
|
+
let out = str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
552
|
+
if (hasInvalidControlChars(out)) {
|
|
553
|
+
out = stripInvalidControlChars(out);
|
|
554
|
+
}
|
|
555
|
+
return out;
|
|
556
|
+
}
|
|
557
|
+
function unescapeXml(str) {
|
|
558
|
+
if (!str || str.indexOf("&") === -1) return str;
|
|
559
|
+
return str.replace(/&(?:amp|lt|gt|quot|apos|#\d+|#x[0-9a-fA-F]+);/g, (entity) => {
|
|
560
|
+
switch (entity) {
|
|
561
|
+
case "&":
|
|
562
|
+
return "&";
|
|
563
|
+
case "<":
|
|
564
|
+
return "<";
|
|
565
|
+
case ">":
|
|
566
|
+
return ">";
|
|
567
|
+
case """:
|
|
568
|
+
return '"';
|
|
569
|
+
case "'":
|
|
570
|
+
return "'";
|
|
571
|
+
default: {
|
|
572
|
+
if (entity[1] === "#") {
|
|
573
|
+
const body = entity.slice(2, -1);
|
|
574
|
+
const code = body[0] === "x" || body[0] === "X" ? parseInt(body.slice(1), 16) : parseInt(body, 10);
|
|
575
|
+
if (Number.isFinite(code)) {
|
|
576
|
+
try {
|
|
577
|
+
return String.fromCodePoint(code);
|
|
578
|
+
} catch {
|
|
579
|
+
return entity;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return entity;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
function parseTagAttributes(tag) {
|
|
589
|
+
const attrs = {};
|
|
590
|
+
const re = /([A-Za-z_][\w.:-]*)\s*=\s*("([^"]*)"|'([^']*)')/g;
|
|
591
|
+
let m;
|
|
592
|
+
while ((m = re.exec(tag)) !== null) {
|
|
593
|
+
const name = m[1];
|
|
594
|
+
const value = m[3] !== void 0 ? m[3] : m[4];
|
|
595
|
+
attrs[name] = value;
|
|
596
|
+
const colon = name.indexOf(":");
|
|
597
|
+
if (colon !== -1) {
|
|
598
|
+
const local = name.slice(colon + 1);
|
|
599
|
+
if (!(local in attrs)) attrs[local] = value;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return attrs;
|
|
603
|
+
}
|
|
604
|
+
function resolveRelTarget(target) {
|
|
605
|
+
if (!target) return target;
|
|
606
|
+
if (target.startsWith("/")) return target.slice(1);
|
|
607
|
+
if (target.startsWith("xl/")) return target;
|
|
608
|
+
return "xl/" + target;
|
|
609
|
+
}
|
|
610
|
+
function columnIndexToLetter(colIndex) {
|
|
611
|
+
let n = colIndex + 1;
|
|
612
|
+
let result = "";
|
|
613
|
+
while (n > 0) {
|
|
614
|
+
const rem = (n - 1) % 26;
|
|
615
|
+
result = String.fromCharCode(65 + rem) + result;
|
|
616
|
+
n = Math.floor((n - 1) / 26);
|
|
617
|
+
}
|
|
618
|
+
return result;
|
|
619
|
+
}
|
|
620
|
+
function columnLetterToIndex(letter) {
|
|
621
|
+
let column = 0;
|
|
622
|
+
const length = letter.length;
|
|
623
|
+
for (let i = 0; i < length; i++) {
|
|
624
|
+
column += (letter.charCodeAt(i) - 64) * Math.pow(26, length - i - 1);
|
|
625
|
+
}
|
|
626
|
+
return column - 1;
|
|
627
|
+
}
|
|
628
|
+
function trimTrailingEmptyRows(rows) {
|
|
629
|
+
let end = rows.length;
|
|
630
|
+
while (end > 0 && rows[end - 1].every((v) => v === null || v === void 0)) {
|
|
631
|
+
end--;
|
|
632
|
+
}
|
|
633
|
+
return end === rows.length ? rows : rows.slice(0, end);
|
|
634
|
+
}
|
|
635
|
+
function parseSharedStringsXml(xml) {
|
|
636
|
+
const result = [];
|
|
637
|
+
const itemRegex = /<(?:(?:[A-Za-z_][\w.-]*):)?si\b[^>]*>([\s\S]*?)<\/(?:(?:[A-Za-z_][\w.-]*):)?si\s*>|<(?:(?:[A-Za-z_][\w.-]*):)?si\b[^>]*\/>/gi;
|
|
638
|
+
let itemMatch;
|
|
639
|
+
while ((itemMatch = itemRegex.exec(xml)) !== null) {
|
|
640
|
+
const full = itemMatch[0];
|
|
641
|
+
if (full.endsWith("/>") && itemMatch[1] === void 0) {
|
|
642
|
+
result.push("");
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
let content = itemMatch[1] ?? "";
|
|
646
|
+
content = content.replace(/<(?:(?:[A-Za-z_][\w.-]*):)?rPh\b[^>]*>[\s\S]*?<\/(?:(?:[A-Za-z_][\w.-]*):)?rPh\s*>/gi, "");
|
|
647
|
+
content = content.replace(/<(?:(?:[A-Za-z_][\w.-]*):)?rPh\b[^>]*\/>/gi, "");
|
|
648
|
+
const textRegex = /<(?:(?:[A-Za-z_][\w.-]*):)?t\b[^>]*>([\s\S]*?)<\/(?:(?:[A-Za-z_][\w.-]*):)?t\s*>/gi;
|
|
649
|
+
let value = "";
|
|
650
|
+
let textMatch;
|
|
651
|
+
while ((textMatch = textRegex.exec(content)) !== null) {
|
|
652
|
+
value += textMatch[1];
|
|
653
|
+
}
|
|
654
|
+
result.push(unescapeXml(value));
|
|
655
|
+
}
|
|
656
|
+
return result;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// src/biff12Utils.ts
|
|
660
|
+
function readVlq(buf, pos) {
|
|
661
|
+
let value = 0;
|
|
662
|
+
let shift = 0;
|
|
663
|
+
let byte;
|
|
664
|
+
do {
|
|
665
|
+
byte = buf[pos++];
|
|
666
|
+
value |= (byte & 127) << shift;
|
|
667
|
+
shift += 7;
|
|
668
|
+
} while ((byte & 128) !== 0);
|
|
669
|
+
return { value, nextPos: pos };
|
|
670
|
+
}
|
|
671
|
+
function vlqBytes(value) {
|
|
672
|
+
const out = [];
|
|
673
|
+
while (value >= 128) {
|
|
674
|
+
out.push(value & 127 | 128);
|
|
675
|
+
value >>>= 7;
|
|
676
|
+
}
|
|
677
|
+
out.push(value & 127);
|
|
678
|
+
return out;
|
|
679
|
+
}
|
|
680
|
+
function readRecord(buf, pos, rec) {
|
|
681
|
+
const idRes = readVlq(buf, pos);
|
|
682
|
+
if (idRes.nextPos > buf.length) return false;
|
|
683
|
+
const lenRes = readVlq(buf, idRes.nextPos);
|
|
684
|
+
if (lenRes.nextPos + lenRes.value > buf.length) return false;
|
|
685
|
+
rec.headerStart = pos;
|
|
686
|
+
rec.dataStart = lenRes.nextPos;
|
|
687
|
+
rec.dataEnd = lenRes.nextPos + lenRes.value;
|
|
688
|
+
rec.id = idRes.value;
|
|
689
|
+
rec.len = lenRes.value;
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
function buildRecord(id, payload) {
|
|
693
|
+
const idBytes = vlqBytes(id);
|
|
694
|
+
const lenBytes = vlqBytes(payload.length);
|
|
695
|
+
const header = Buffer.alloc(idBytes.length + lenBytes.length);
|
|
696
|
+
idBytes.forEach((b, i) => header[i] = b);
|
|
697
|
+
lenBytes.forEach((b, i) => header[idBytes.length + i] = b);
|
|
698
|
+
return Buffer.concat([header, payload]);
|
|
699
|
+
}
|
|
700
|
+
function readUtf16(buf, start, charCount) {
|
|
701
|
+
const end = start + charCount * 2;
|
|
702
|
+
if (end > buf.length) return "";
|
|
703
|
+
return buf.toString("utf16le", start, end);
|
|
704
|
+
}
|
|
705
|
+
function parseSharedStringsBin(buf) {
|
|
706
|
+
const values = [];
|
|
707
|
+
let total = 0;
|
|
708
|
+
let unique = 0;
|
|
709
|
+
let endSstOffset = buf.length;
|
|
710
|
+
const rec = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
711
|
+
let pos = 0;
|
|
712
|
+
while (readRecord(buf, pos, rec)) {
|
|
713
|
+
pos = rec.dataEnd;
|
|
714
|
+
if (rec.id === 159) {
|
|
715
|
+
total = buf.readUInt32LE(rec.dataStart);
|
|
716
|
+
unique = buf.readUInt32LE(rec.dataStart + 4);
|
|
717
|
+
} else if (rec.id === 19) {
|
|
718
|
+
const cch = buf.readUInt32LE(rec.dataStart + 1);
|
|
719
|
+
values.push(readUtf16(buf, rec.dataStart + 5, cch));
|
|
720
|
+
} else if (rec.id === 160) {
|
|
721
|
+
endSstOffset = rec.headerStart;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return { values, total, unique, endSstOffset };
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// src/XlsbReader.ts
|
|
390
728
|
var XlsbReader = class extends ExcelReaderAbstract {
|
|
391
729
|
constructor() {
|
|
392
730
|
super();
|
|
@@ -398,12 +736,29 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
398
736
|
this._reader = null;
|
|
399
737
|
this._currentRow = [];
|
|
400
738
|
this._pendingRowIndex = -1;
|
|
739
|
+
this._nextExpectedRow = 0;
|
|
401
740
|
this._eof = false;
|
|
402
741
|
this.xfIdToNumFmtId = [];
|
|
403
742
|
this.customDateFormats = null;
|
|
404
743
|
}
|
|
405
|
-
async open(
|
|
406
|
-
this.
|
|
744
|
+
async open(path9, readSharedStrings = true) {
|
|
745
|
+
await this.close();
|
|
746
|
+
this.sheetNames = [];
|
|
747
|
+
this.sheets = [];
|
|
748
|
+
this.sharedStrings = [];
|
|
749
|
+
this.xfIdToNumFmtId = [];
|
|
750
|
+
this.customDateFormats = /* @__PURE__ */ new Set();
|
|
751
|
+
this.fieldCount = 0;
|
|
752
|
+
this.resultsCount = 0;
|
|
753
|
+
this.actualSheetName = "";
|
|
754
|
+
this._currentSheetIndex = -1;
|
|
755
|
+
this._currentRow = [];
|
|
756
|
+
this._pendingRowIndex = -1;
|
|
757
|
+
this._nextExpectedRow = 0;
|
|
758
|
+
this._eof = false;
|
|
759
|
+
this._reader = null;
|
|
760
|
+
this._oaEpoch = Date.UTC(1899, 11, 30);
|
|
761
|
+
this.zip = new AdmZip(path9);
|
|
407
762
|
const wbEntry = this.zip.getEntry("xl/workbook.bin");
|
|
408
763
|
if (wbEntry) {
|
|
409
764
|
const buf = wbEntry.getData();
|
|
@@ -420,20 +775,39 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
420
775
|
const relsEntry = this.zip.getEntry("xl/_rels/workbook.bin.rels");
|
|
421
776
|
if (relsEntry) {
|
|
422
777
|
const xml = relsEntry.getData().toString("utf8");
|
|
423
|
-
const
|
|
424
|
-
let
|
|
778
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
779
|
+
let tagMatch;
|
|
425
780
|
const rIdToTarget = {};
|
|
426
|
-
while ((
|
|
427
|
-
|
|
781
|
+
while ((tagMatch = relTagRe.exec(xml)) !== null) {
|
|
782
|
+
const attrs = parseTagAttributes(tagMatch[0]);
|
|
783
|
+
const id = attrs["Id"] ?? attrs["id"];
|
|
784
|
+
const target = attrs["Target"] ?? attrs["target"];
|
|
785
|
+
if (id !== void 0 && target !== void 0) {
|
|
786
|
+
rIdToTarget[id] = target;
|
|
787
|
+
}
|
|
428
788
|
}
|
|
429
789
|
for (const sheet of this.sheets) {
|
|
430
|
-
|
|
790
|
+
const target = rIdToTarget[sheet.rId];
|
|
431
791
|
if (target) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
792
|
+
sheet.path = resolveRelTarget(target);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
try {
|
|
797
|
+
const raw = wbEntry ? wbEntry.getData() : null;
|
|
798
|
+
if (raw) {
|
|
799
|
+
const rec = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
800
|
+
let pos = 0;
|
|
801
|
+
while (readRecord(raw, pos, rec)) {
|
|
802
|
+
if (rec.id === 153 && rec.len >= 4) {
|
|
803
|
+
const is1904 = (raw[rec.dataStart] & 1) !== 0;
|
|
804
|
+
this._oaEpoch = is1904 ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30);
|
|
805
|
+
break;
|
|
806
|
+
}
|
|
807
|
+
pos = rec.dataEnd;
|
|
435
808
|
}
|
|
436
809
|
}
|
|
810
|
+
} catch {
|
|
437
811
|
}
|
|
438
812
|
if (readSharedStrings) {
|
|
439
813
|
const ssPath = "xl/sharedStrings.bin";
|
|
@@ -448,8 +822,8 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
448
822
|
this.resultsCount = this.sheets.length;
|
|
449
823
|
this._currentSheetIndex = -1;
|
|
450
824
|
}
|
|
451
|
-
_readSharedStrings(
|
|
452
|
-
const entry = this.zip.getEntry(
|
|
825
|
+
_readSharedStrings(path9) {
|
|
826
|
+
const entry = this.zip.getEntry(path9);
|
|
453
827
|
if (!entry) return;
|
|
454
828
|
const reader = new BiffReaderWriter(entry.getData());
|
|
455
829
|
while (reader.readSharedStrings()) {
|
|
@@ -458,8 +832,8 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
458
832
|
}
|
|
459
833
|
}
|
|
460
834
|
}
|
|
461
|
-
_readStyles(
|
|
462
|
-
const entry = this.zip.getEntry(
|
|
835
|
+
_readStyles(path9) {
|
|
836
|
+
const entry = this.zip.getEntry(path9);
|
|
463
837
|
if (!entry) return;
|
|
464
838
|
const reader = new BiffReaderWriter(entry.getData());
|
|
465
839
|
while (reader.readStyles()) {
|
|
@@ -470,21 +844,54 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
470
844
|
getSheetNames() {
|
|
471
845
|
return this.sheetNames;
|
|
472
846
|
}
|
|
847
|
+
async selectSheet(nameOrIndex) {
|
|
848
|
+
let index = -1;
|
|
849
|
+
if (typeof nameOrIndex === "number") {
|
|
850
|
+
index = nameOrIndex;
|
|
851
|
+
} else {
|
|
852
|
+
index = this.sheetNames.indexOf(nameOrIndex);
|
|
853
|
+
if (index === -1) throw new Error(`Sheet '${nameOrIndex}' not found`);
|
|
854
|
+
}
|
|
855
|
+
if (index < 0 || index >= this.sheets.length) {
|
|
856
|
+
throw new Error(`Sheet index ${String(nameOrIndex)} out of range`);
|
|
857
|
+
}
|
|
858
|
+
this._currentSheetIndex = index;
|
|
859
|
+
if (!this._initSheet(index)) {
|
|
860
|
+
throw new Error(`XlsbReader: worksheet part missing for sheet '${this.sheetNames[index]}'.`);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
async nextResult() {
|
|
864
|
+
const next = this._currentSheetIndex + 1;
|
|
865
|
+
if (next >= this.sheets.length) return false;
|
|
866
|
+
this._currentSheetIndex = next;
|
|
867
|
+
if (!this._initSheet(next)) {
|
|
868
|
+
throw new Error(`XlsbReader: worksheet part missing for sheet '${this.sheetNames[next]}'.`);
|
|
869
|
+
}
|
|
870
|
+
return true;
|
|
871
|
+
}
|
|
473
872
|
async read() {
|
|
474
873
|
if (this._currentSheetIndex === -1) {
|
|
475
874
|
this._currentSheetIndex = 0;
|
|
476
875
|
if (!this._initSheet(0)) return false;
|
|
477
876
|
}
|
|
478
877
|
if (this._eof) return false;
|
|
878
|
+
if (this._pendingRowIndex > this._nextExpectedRow && this._pendingRowIndex < MAX_ROWS) {
|
|
879
|
+
this._currentRow = [];
|
|
880
|
+
this._nextExpectedRow++;
|
|
881
|
+
return true;
|
|
882
|
+
}
|
|
479
883
|
this._currentRow = [];
|
|
480
884
|
while (true) {
|
|
481
885
|
const hasRecord = this._reader.readWorksheet();
|
|
482
886
|
if (!hasRecord) {
|
|
483
887
|
this._eof = true;
|
|
888
|
+
this._nextExpectedRow = this._pendingRowIndex + 1;
|
|
484
889
|
return true;
|
|
485
890
|
}
|
|
486
891
|
if (this._reader._rowIndex !== -1 && this._reader._rowIndex !== this._pendingRowIndex) {
|
|
892
|
+
const completed = this._pendingRowIndex;
|
|
487
893
|
this._pendingRowIndex = this._reader._rowIndex;
|
|
894
|
+
this._nextExpectedRow = completed + 1;
|
|
488
895
|
return true;
|
|
489
896
|
}
|
|
490
897
|
if (this._reader._readCell) {
|
|
@@ -525,13 +932,17 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
525
932
|
}
|
|
526
933
|
}
|
|
527
934
|
_initSheet(index) {
|
|
528
|
-
if (index >= this.sheets.length) return false;
|
|
935
|
+
if (index < 0 || index >= this.sheets.length) return false;
|
|
529
936
|
const sheet = this.sheets[index];
|
|
530
|
-
const entry = this.zip.getEntry(sheet.path);
|
|
937
|
+
const entry = sheet.path ? this.zip.getEntry(sheet.path) : void 0;
|
|
531
938
|
if (!entry) return false;
|
|
532
939
|
this._reader = new BiffReaderWriter(entry.getData());
|
|
533
940
|
this._eof = false;
|
|
534
941
|
this._pendingRowIndex = -1;
|
|
942
|
+
this._nextExpectedRow = 0;
|
|
943
|
+
this._currentRow = [];
|
|
944
|
+
this.fieldCount = 0;
|
|
945
|
+
this.actualSheetName = sheet.name;
|
|
535
946
|
while (this._reader.readWorksheet()) {
|
|
536
947
|
if (this._reader._rowIndex !== -1) {
|
|
537
948
|
this._pendingRowIndex = this._reader._rowIndex;
|
|
@@ -539,7 +950,7 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
539
950
|
}
|
|
540
951
|
}
|
|
541
952
|
this._eof = true;
|
|
542
|
-
return
|
|
953
|
+
return true;
|
|
543
954
|
}
|
|
544
955
|
getValue(i) {
|
|
545
956
|
if (i < 0 || i >= this._currentRow.length) return null;
|
|
@@ -549,84 +960,15 @@ var XlsbReader = class extends ExcelReaderAbstract {
|
|
|
549
960
|
|
|
550
961
|
// src/XlsxReader.ts
|
|
551
962
|
import yauzl from "yauzl";
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
}
|
|
561
|
-
return false;
|
|
562
|
-
}
|
|
563
|
-
function stripInvalidControlChars(str) {
|
|
564
|
-
let out = "";
|
|
565
|
-
for (let i = 0; i < str.length; i++) {
|
|
566
|
-
const c = str.charCodeAt(i);
|
|
567
|
-
if (c === 9 || c === 10 || c === 13 || c >= 32 && c <= 55295 || c >= 57344 && c <= 65533) {
|
|
568
|
-
out += str[i];
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
return out;
|
|
572
|
-
}
|
|
573
|
-
function escapeXmlText(str) {
|
|
574
|
-
if (!/&|<|>|"|'/.test(str) && !hasInvalidControlChars(str)) {
|
|
575
|
-
return str;
|
|
576
|
-
}
|
|
577
|
-
let out = str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
578
|
-
if (hasInvalidControlChars(out)) {
|
|
579
|
-
out = stripInvalidControlChars(out);
|
|
580
|
-
}
|
|
581
|
-
return out;
|
|
582
|
-
}
|
|
583
|
-
function unescapeXml(str) {
|
|
584
|
-
if (!str || str.indexOf("&") === -1) return str;
|
|
585
|
-
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, "'");
|
|
586
|
-
}
|
|
587
|
-
function columnIndexToLetter(colIndex) {
|
|
588
|
-
let n = colIndex + 1;
|
|
589
|
-
let result = "";
|
|
590
|
-
while (n > 0) {
|
|
591
|
-
const rem = (n - 1) % 26;
|
|
592
|
-
result = String.fromCharCode(65 + rem) + result;
|
|
593
|
-
n = Math.floor((n - 1) / 26);
|
|
594
|
-
}
|
|
595
|
-
return result;
|
|
596
|
-
}
|
|
597
|
-
function columnLetterToIndex(letter) {
|
|
598
|
-
let column = 0;
|
|
599
|
-
const length = letter.length;
|
|
600
|
-
for (let i = 0; i < length; i++) {
|
|
601
|
-
column += (letter.charCodeAt(i) - 64) * Math.pow(26, length - i - 1);
|
|
602
|
-
}
|
|
603
|
-
return column - 1;
|
|
604
|
-
}
|
|
605
|
-
function trimTrailingEmptyRows(rows) {
|
|
606
|
-
let end = rows.length;
|
|
607
|
-
while (end > 0 && rows[end - 1].every((v) => v === null || v === void 0)) {
|
|
608
|
-
end--;
|
|
609
|
-
}
|
|
610
|
-
return end === rows.length ? rows : rows.slice(0, end);
|
|
611
|
-
}
|
|
612
|
-
function parseSharedStringsXml(xml) {
|
|
613
|
-
const result = [];
|
|
614
|
-
const itemRegex = /<(?:(?:[A-Za-z_][\w.-]*):)?si\b[^>]*>([\s\S]*?)<\/(?:(?:[A-Za-z_][\w.-]*):)?si\s*>/gi;
|
|
615
|
-
let itemMatch;
|
|
616
|
-
while ((itemMatch = itemRegex.exec(xml)) !== null) {
|
|
617
|
-
const content = itemMatch[1];
|
|
618
|
-
const textRegex = /<(?:(?:[A-Za-z_][\w.-]*):)?t\b[^>]*>([\s\S]*?)<\/(?:(?:[A-Za-z_][\w.-]*):)?t\s*>/gi;
|
|
619
|
-
let value = "";
|
|
620
|
-
let textMatch;
|
|
621
|
-
while ((textMatch = textRegex.exec(content)) !== null) {
|
|
622
|
-
value += textMatch[1];
|
|
623
|
-
}
|
|
624
|
-
result.push(unescapeXml(value));
|
|
625
|
-
}
|
|
626
|
-
return result;
|
|
963
|
+
function parseIsoDateUtc(value) {
|
|
964
|
+
let text = value.trim().replace(" ", "T");
|
|
965
|
+
if (!text) return null;
|
|
966
|
+
if (!/z$|[+-]\d{2}:?\d{2}$/i.test(text) && text.includes("T")) {
|
|
967
|
+
text += "Z";
|
|
968
|
+
}
|
|
969
|
+
const date = new Date(text);
|
|
970
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
627
971
|
}
|
|
628
|
-
|
|
629
|
-
// src/XlsxReader.ts
|
|
630
972
|
var XlsxReader = class extends ExcelReaderAbstract {
|
|
631
973
|
constructor() {
|
|
632
974
|
super();
|
|
@@ -638,13 +980,34 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
638
980
|
this._currentSheetIndex = -1;
|
|
639
981
|
this._sheetXml = null;
|
|
640
982
|
this._xmlPos = 0;
|
|
983
|
+
/** End of the sheetData element; row tags must not be searched past it. */
|
|
984
|
+
this._xmlEnd = 0;
|
|
641
985
|
this._currentRow = [];
|
|
986
|
+
this._expectedRowNum = 1;
|
|
987
|
+
this._pendingEmptyCount = 0;
|
|
642
988
|
this.xfIdToNumFmtId = [];
|
|
643
989
|
this.customDateFormats = /* @__PURE__ */ new Set();
|
|
644
990
|
}
|
|
645
|
-
async open(
|
|
646
|
-
|
|
647
|
-
|
|
991
|
+
async open(path9, readSharedStrings = true) {
|
|
992
|
+
await this.close();
|
|
993
|
+
this.sheetNames = [];
|
|
994
|
+
this.sheets = [];
|
|
995
|
+
this.sharedStrings = [];
|
|
996
|
+
this.xfIdToNumFmtId = [];
|
|
997
|
+
this.customDateFormats = /* @__PURE__ */ new Set();
|
|
998
|
+
this.fieldCount = 0;
|
|
999
|
+
this.resultsCount = 0;
|
|
1000
|
+
this.actualSheetName = "";
|
|
1001
|
+
this._currentSheetIndex = -1;
|
|
1002
|
+
this._sheetXml = null;
|
|
1003
|
+
this._xmlPos = 0;
|
|
1004
|
+
this._xmlEnd = 0;
|
|
1005
|
+
this._currentRow = [];
|
|
1006
|
+
this._expectedRowNum = 1;
|
|
1007
|
+
this._pendingEmptyCount = 0;
|
|
1008
|
+
this._oaEpoch = Date.UTC(1899, 11, 30);
|
|
1009
|
+
return new Promise((resolve6, reject) => {
|
|
1010
|
+
yauzl.open(path9, { lazyEntries: true, autoClose: false }, async (err, zipfile) => {
|
|
648
1011
|
if (err) return reject(err);
|
|
649
1012
|
this.zipfile = zipfile;
|
|
650
1013
|
this.entries = /* @__PURE__ */ new Map();
|
|
@@ -657,28 +1020,40 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
657
1020
|
const wbRelsContent = await this._readZipEntryContent("xl/_rels/workbook.xml.rels");
|
|
658
1021
|
const rIdToTarget = {};
|
|
659
1022
|
if (wbRelsContent) {
|
|
660
|
-
const
|
|
661
|
-
let
|
|
662
|
-
while ((
|
|
663
|
-
|
|
1023
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
1024
|
+
let tagMatch;
|
|
1025
|
+
while ((tagMatch = relTagRe.exec(wbRelsContent)) !== null) {
|
|
1026
|
+
const attrs = parseTagAttributes(tagMatch[0]);
|
|
1027
|
+
const id = attrs["Id"] ?? attrs["id"];
|
|
1028
|
+
const target = attrs["Target"] ?? attrs["target"];
|
|
1029
|
+
if (id !== void 0 && target !== void 0) {
|
|
1030
|
+
rIdToTarget[id] = target;
|
|
1031
|
+
}
|
|
664
1032
|
}
|
|
665
1033
|
}
|
|
666
1034
|
const wbContent = await this._readZipEntryContent("xl/workbook.xml");
|
|
667
1035
|
if (wbContent) {
|
|
668
|
-
const
|
|
669
|
-
let
|
|
670
|
-
while ((
|
|
671
|
-
const
|
|
672
|
-
const
|
|
673
|
-
const
|
|
1036
|
+
const sheetTagRe = /<sheet\b[^>]*>/g;
|
|
1037
|
+
let tagMatch;
|
|
1038
|
+
while ((tagMatch = sheetTagRe.exec(wbContent)) !== null) {
|
|
1039
|
+
const attrs = parseTagAttributes(tagMatch[0]);
|
|
1040
|
+
const rawName = attrs["name"];
|
|
1041
|
+
const sheetId = attrs["sheetId"] ?? "";
|
|
1042
|
+
const rId = attrs["r:id"] ?? attrs["id"] ?? attrs["rId"];
|
|
1043
|
+
if (rawName === void 0 || rId === void 0) continue;
|
|
1044
|
+
const name = unescapeXml(rawName);
|
|
674
1045
|
const target = rIdToTarget[rId];
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
fullPath = "xl/" + fullPath;
|
|
678
|
-
}
|
|
1046
|
+
if (!target) continue;
|
|
1047
|
+
const fullPath = resolveRelTarget(target);
|
|
679
1048
|
this.sheetNames.push(name);
|
|
680
1049
|
this.sheets.push({ name, sheetId, rId, path: fullPath });
|
|
681
1050
|
}
|
|
1051
|
+
const wbPrMatch = /<workbookPr\b[^>]*>/i.exec(wbContent);
|
|
1052
|
+
if (wbPrMatch) {
|
|
1053
|
+
const wbPrAttrs = parseTagAttributes(wbPrMatch[0]);
|
|
1054
|
+
const v = (wbPrAttrs["date1904"] ?? "").trim().toLowerCase();
|
|
1055
|
+
this._oaEpoch = v === "1" || v === "true" ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30);
|
|
1056
|
+
}
|
|
682
1057
|
}
|
|
683
1058
|
if (readSharedStrings) {
|
|
684
1059
|
const ssContent = await this._readZipEntryContent("xl/sharedStrings.xml");
|
|
@@ -692,7 +1067,7 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
692
1067
|
}
|
|
693
1068
|
this.resultsCount = this.sheets.length;
|
|
694
1069
|
this._currentSheetIndex = -1;
|
|
695
|
-
|
|
1070
|
+
resolve6();
|
|
696
1071
|
} catch (e) {
|
|
697
1072
|
reject(e);
|
|
698
1073
|
}
|
|
@@ -706,16 +1081,16 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
706
1081
|
this.zipfile.close();
|
|
707
1082
|
}
|
|
708
1083
|
}
|
|
709
|
-
async _readZipEntryContent(
|
|
710
|
-
const entry = this.entries.get(
|
|
1084
|
+
async _readZipEntryContent(path9) {
|
|
1085
|
+
const entry = this.entries.get(path9);
|
|
711
1086
|
if (!entry) return null;
|
|
712
|
-
return new Promise((
|
|
1087
|
+
return new Promise((resolve6, reject) => {
|
|
713
1088
|
this.zipfile.openReadStream(entry, (err, readStream) => {
|
|
714
1089
|
if (err) return reject(err);
|
|
715
1090
|
const chunks = [];
|
|
716
1091
|
readStream.on("data", (chunk) => chunks.push(chunk));
|
|
717
1092
|
readStream.on("end", () => {
|
|
718
|
-
|
|
1093
|
+
resolve6(Buffer.concat(chunks).toString("utf8"));
|
|
719
1094
|
});
|
|
720
1095
|
readStream.on("error", reject);
|
|
721
1096
|
});
|
|
@@ -763,6 +1138,27 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
763
1138
|
return false;
|
|
764
1139
|
}
|
|
765
1140
|
}
|
|
1141
|
+
async selectSheet(nameOrIndex) {
|
|
1142
|
+
let index = -1;
|
|
1143
|
+
if (typeof nameOrIndex === "number") {
|
|
1144
|
+
index = nameOrIndex;
|
|
1145
|
+
} else {
|
|
1146
|
+
index = this.sheetNames.indexOf(nameOrIndex);
|
|
1147
|
+
if (index === -1) throw new Error(`Sheet '${nameOrIndex}' not found`);
|
|
1148
|
+
}
|
|
1149
|
+
if (index < 0 || index >= this.sheets.length) {
|
|
1150
|
+
throw new Error(`Sheet index ${String(nameOrIndex)} out of range`);
|
|
1151
|
+
}
|
|
1152
|
+
this._currentSheetIndex = index;
|
|
1153
|
+
await this._initSheet(index);
|
|
1154
|
+
}
|
|
1155
|
+
async nextResult() {
|
|
1156
|
+
const next = this._currentSheetIndex + 1;
|
|
1157
|
+
if (next >= this.sheets.length) return false;
|
|
1158
|
+
this._currentSheetIndex = next;
|
|
1159
|
+
await this._initSheet(next);
|
|
1160
|
+
return true;
|
|
1161
|
+
}
|
|
766
1162
|
async _initSheet(index) {
|
|
767
1163
|
if (index >= this.sheets.length) return;
|
|
768
1164
|
const sheet = this.sheets[index];
|
|
@@ -772,23 +1168,106 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
772
1168
|
this._sheetXml = "";
|
|
773
1169
|
}
|
|
774
1170
|
this._xmlPos = 0;
|
|
1171
|
+
this._xmlEnd = this._sheetXml.length;
|
|
1172
|
+
this._expectedRowNum = 1;
|
|
1173
|
+
this._pendingEmptyCount = 0;
|
|
1174
|
+
this._currentRow = [];
|
|
1175
|
+
this.fieldCount = 0;
|
|
775
1176
|
const sd = this._sheetXml.indexOf("<sheetData>");
|
|
776
|
-
if (sd !== -1)
|
|
1177
|
+
if (sd !== -1) {
|
|
1178
|
+
this._xmlPos = sd + 11;
|
|
1179
|
+
const se = this._sheetXml.indexOf("</sheetData>", this._xmlPos);
|
|
1180
|
+
if (se !== -1) this._xmlEnd = se;
|
|
1181
|
+
} else {
|
|
1182
|
+
const m = /<(?:[A-Za-z_][\w.-]*:)?sheetData\b[^>]*>/i.exec(this._sheetXml);
|
|
1183
|
+
if (m && m.index !== void 0) {
|
|
1184
|
+
this._xmlPos = m.index + m[0].length;
|
|
1185
|
+
if (/\/>\s*$/.test(m[0])) {
|
|
1186
|
+
this._xmlEnd = this._xmlPos;
|
|
1187
|
+
} else {
|
|
1188
|
+
const se = this._sheetXml.indexOf("</sheetData>", this._xmlPos);
|
|
1189
|
+
if (se !== -1) this._xmlEnd = se;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
777
1193
|
}
|
|
778
1194
|
_readNextRow() {
|
|
779
1195
|
if (!this._sheetXml) return false;
|
|
780
|
-
|
|
1196
|
+
if (this._pendingEmptyCount > 0) {
|
|
1197
|
+
this._pendingEmptyCount--;
|
|
1198
|
+
this._currentRow = [];
|
|
1199
|
+
this._expectedRowNum++;
|
|
1200
|
+
return true;
|
|
1201
|
+
}
|
|
1202
|
+
const rowStart = this._findRowTagStart(this._xmlPos);
|
|
781
1203
|
if (rowStart === -1) return false;
|
|
1204
|
+
const rowTagEnd = this._sheetXml.indexOf(">", rowStart);
|
|
1205
|
+
if (rowTagEnd === -1) return false;
|
|
1206
|
+
const rowTag = this._sheetXml.substring(rowStart, rowTagEnd + 1);
|
|
1207
|
+
const isSelfClosing = this._sheetXml.charCodeAt(rowTagEnd - 1) === 47;
|
|
1208
|
+
const rowAttrs = parseTagAttributes(rowTag);
|
|
1209
|
+
const rRaw = rowAttrs["r"];
|
|
1210
|
+
let rowNum = this._expectedRowNum;
|
|
1211
|
+
if (rRaw !== void 0) {
|
|
1212
|
+
const parsed = parseInt(rRaw, 10);
|
|
1213
|
+
if (Number.isFinite(parsed) && parsed >= 1 && parsed <= MAX_ROWS) rowNum = parsed;
|
|
1214
|
+
}
|
|
1215
|
+
if (rowNum > this._expectedRowNum) {
|
|
1216
|
+
const gap = rowNum - this._expectedRowNum;
|
|
1217
|
+
this._pendingEmptyCount = gap - 1;
|
|
1218
|
+
this._currentRow = [];
|
|
1219
|
+
this._expectedRowNum++;
|
|
1220
|
+
return true;
|
|
1221
|
+
}
|
|
1222
|
+
if (rowNum < this._expectedRowNum) {
|
|
1223
|
+
if (isSelfClosing) {
|
|
1224
|
+
this._xmlPos = rowTagEnd + 1;
|
|
1225
|
+
this._currentRow = [];
|
|
1226
|
+
return true;
|
|
1227
|
+
}
|
|
1228
|
+
const rowEnd2 = this._sheetXml.indexOf("</row>", rowStart);
|
|
1229
|
+
if (rowEnd2 === -1) return false;
|
|
1230
|
+
this._xmlPos = rowEnd2 + 6;
|
|
1231
|
+
this._parseRowByIndex(rowStart, rowEnd2);
|
|
1232
|
+
this._expectedRowNum = Math.max(this._expectedRowNum + 1, rowNum + 1);
|
|
1233
|
+
return true;
|
|
1234
|
+
}
|
|
1235
|
+
if (isSelfClosing) {
|
|
1236
|
+
this._xmlPos = rowTagEnd + 1;
|
|
1237
|
+
this._currentRow = [];
|
|
1238
|
+
this._expectedRowNum = rowNum + 1;
|
|
1239
|
+
return true;
|
|
1240
|
+
}
|
|
782
1241
|
const rowEnd = this._sheetXml.indexOf("</row>", rowStart);
|
|
783
1242
|
if (rowEnd === -1) return false;
|
|
784
1243
|
this._xmlPos = rowEnd + 6;
|
|
785
1244
|
this._parseRowByIndex(rowStart, rowEnd);
|
|
1245
|
+
this._expectedRowNum = rowNum + 1;
|
|
786
1246
|
return true;
|
|
787
1247
|
}
|
|
1248
|
+
/**
|
|
1249
|
+
* Find the next `<row …>` start, ignoring similarly named elements such as
|
|
1250
|
+
* `<rowBreaks>` that follow `</sheetData>`.
|
|
1251
|
+
*/
|
|
1252
|
+
_findRowTagStart(from) {
|
|
1253
|
+
const xml = this._sheetXml;
|
|
1254
|
+
let pos = from;
|
|
1255
|
+
while (pos < this._xmlEnd) {
|
|
1256
|
+
const idx = xml.indexOf("<row", pos);
|
|
1257
|
+
if (idx === -1 || idx >= this._xmlEnd) return -1;
|
|
1258
|
+
const next = xml.charCodeAt(idx + 4);
|
|
1259
|
+
if (next === 62 || next === 47 || next === 32 || next === 9 || next === 10 || next === 13) {
|
|
1260
|
+
return idx;
|
|
1261
|
+
}
|
|
1262
|
+
pos = idx + 4;
|
|
1263
|
+
}
|
|
1264
|
+
return -1;
|
|
1265
|
+
}
|
|
788
1266
|
_parseRowByIndex(rowStart, rowEnd) {
|
|
789
1267
|
this._currentRow = [];
|
|
790
1268
|
const xml = this._sheetXml;
|
|
791
1269
|
let pos = xml.indexOf(">", rowStart) + 1;
|
|
1270
|
+
let nextCol = 0;
|
|
792
1271
|
while (pos < rowEnd) {
|
|
793
1272
|
const cStart = xml.indexOf("<c", pos);
|
|
794
1273
|
if (cStart === -1 || cStart >= rowEnd) break;
|
|
@@ -822,6 +1301,7 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
822
1301
|
}
|
|
823
1302
|
}
|
|
824
1303
|
let val = null;
|
|
1304
|
+
let inlineVals = null;
|
|
825
1305
|
if (xml.charCodeAt(nextTagClose - 1) === 47) {
|
|
826
1306
|
pos = nextTagClose + 1;
|
|
827
1307
|
} else {
|
|
@@ -836,20 +1316,28 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
836
1316
|
}
|
|
837
1317
|
} else {
|
|
838
1318
|
if (type === "inlineStr") {
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
1319
|
+
inlineVals = [];
|
|
1320
|
+
let tSearch = nextTagClose;
|
|
1321
|
+
while (true) {
|
|
1322
|
+
const tStart = xml.indexOf("<t", tSearch);
|
|
1323
|
+
if (tStart === -1 || tStart >= cellContentEnd) break;
|
|
1324
|
+
const tClose = xml.indexOf(">", tStart);
|
|
1325
|
+
if (tClose === -1 || tClose >= cellContentEnd) break;
|
|
1326
|
+
const tContentStart = tClose + 1;
|
|
842
1327
|
const tEnd = xml.indexOf("</t>", tContentStart);
|
|
843
|
-
if (tEnd
|
|
844
|
-
|
|
845
|
-
|
|
1328
|
+
if (tEnd === -1 || tEnd > cellContentEnd) break;
|
|
1329
|
+
inlineVals.push(xml.substring(tContentStart, tEnd));
|
|
1330
|
+
tSearch = tEnd + 4;
|
|
846
1331
|
}
|
|
1332
|
+
if (inlineVals.length === 0) inlineVals = null;
|
|
847
1333
|
}
|
|
848
1334
|
}
|
|
849
1335
|
pos = cellContentEnd + 4;
|
|
850
1336
|
}
|
|
851
1337
|
let finalVal = null;
|
|
852
|
-
if (
|
|
1338
|
+
if (inlineVals !== null) {
|
|
1339
|
+
finalVal = unescapeXml(inlineVals.join(""));
|
|
1340
|
+
} else if (val !== null) {
|
|
853
1341
|
if (type === "s") {
|
|
854
1342
|
const idx = parseInt(val, 10);
|
|
855
1343
|
finalVal = this.sharedStrings[idx];
|
|
@@ -857,6 +1345,12 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
857
1345
|
finalVal = val === "1" || val === "true";
|
|
858
1346
|
} else if (type === "inlineStr") {
|
|
859
1347
|
finalVal = unescapeXml(val);
|
|
1348
|
+
} else if (type === "str") {
|
|
1349
|
+
finalVal = unescapeXml(val);
|
|
1350
|
+
} else if (type === "e") {
|
|
1351
|
+
finalVal = unescapeXml(val);
|
|
1352
|
+
} else if (type === "d") {
|
|
1353
|
+
finalVal = parseIsoDateUtc(val) ?? unescapeXml(val);
|
|
860
1354
|
} else if (type === "n" || type === "") {
|
|
861
1355
|
finalVal = parseFloat(val);
|
|
862
1356
|
if (styleIndex > 0 && this.xfIdToNumFmtId) {
|
|
@@ -874,10 +1368,10 @@ var XlsxReader = class extends ExcelReaderAbstract {
|
|
|
874
1368
|
}
|
|
875
1369
|
}
|
|
876
1370
|
}
|
|
877
|
-
if (colIndex
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1371
|
+
if (colIndex === -1) colIndex = nextCol;
|
|
1372
|
+
nextCol = colIndex + 1;
|
|
1373
|
+
this._currentRow[colIndex] = finalVal;
|
|
1374
|
+
if (colIndex >= this.fieldCount) this.fieldCount = colIndex + 1;
|
|
881
1375
|
}
|
|
882
1376
|
}
|
|
883
1377
|
getValue(i) {
|
|
@@ -907,99 +1401,11 @@ var ReaderFactory = class {
|
|
|
907
1401
|
|
|
908
1402
|
// src/XlsbWriter.ts
|
|
909
1403
|
import * as fs from "fs";
|
|
1404
|
+
import * as path2 from "path";
|
|
1405
|
+
import { randomUUID } from "crypto";
|
|
910
1406
|
import archiver from "archiver";
|
|
911
1407
|
import { Readable } from "stream";
|
|
912
1408
|
|
|
913
|
-
// src/Formats.ts
|
|
914
|
-
var F = {
|
|
915
|
-
THOUSANDS_SEP: "#,##0",
|
|
916
|
-
CURRENCY_PLN: '#,##0.00 "z\u0142"',
|
|
917
|
-
CURRENCY_EUR: "#,##0.00 \u20AC",
|
|
918
|
-
PERCENTAGE: "0%",
|
|
919
|
-
SCIENTIFIC: "0.00E+00",
|
|
920
|
-
TWO_DECIMALS: "#,##0.00",
|
|
921
|
-
TEXT: "@",
|
|
922
|
-
LEADING_ZEROS: "000000000",
|
|
923
|
-
DATE_SHORT: "dd.mm.yyyy",
|
|
924
|
-
DATE_LONG: "d mmmm yyyy",
|
|
925
|
-
DATE_DAY_MONTH_YEAR: "dd-mm-yyyy",
|
|
926
|
-
DATE_ISO: "yyyy-mm-dd",
|
|
927
|
-
DATE_MONTH_YEAR: "mmmm yyyy",
|
|
928
|
-
DATE_WEEKDAY: "dddd, d mmmm yyyy",
|
|
929
|
-
DATE_DAY_MONTH: "d mmmm",
|
|
930
|
-
DATE_YEAR_ONLY: "yyyy",
|
|
931
|
-
DATETIME_SHORT: "dd.mm.yyyy hh:mm",
|
|
932
|
-
DATETIME_LONG: "d mmmm yyyy hh:mm:ss",
|
|
933
|
-
TIME_HH_MM: "hh:mm",
|
|
934
|
-
TIME_HH_MM_SS: "hh:mm:ss",
|
|
935
|
-
TIME_12H: "h:mm AM/PM",
|
|
936
|
-
DATETIME_24H: "dd.mm.yyyy hh:mm:ss",
|
|
937
|
-
DATETIME_ISO: 'yyyy-mm-dd"T"hh:mm:ss',
|
|
938
|
-
TIME_MS: "hh:mm:ss.000"
|
|
939
|
-
};
|
|
940
|
-
function isFormattedCell(val) {
|
|
941
|
-
return val !== null && val !== void 0 && typeof val === "object" && "format" in val && typeof val.format === "string";
|
|
942
|
-
}
|
|
943
|
-
function unwrapCell(val) {
|
|
944
|
-
if (isFormattedCell(val)) {
|
|
945
|
-
return val.value;
|
|
946
|
-
}
|
|
947
|
-
return val;
|
|
948
|
-
}
|
|
949
|
-
function getFormat(val) {
|
|
950
|
-
if (isFormattedCell(val)) {
|
|
951
|
-
return val.format;
|
|
952
|
-
}
|
|
953
|
-
return null;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
// src/writerHelpers.ts
|
|
957
|
-
var INVALID_SHEET_NAME_CHARS = /[\\/*?[\]:]/g;
|
|
958
|
-
function sanitizeSheetName(name, sheetCount) {
|
|
959
|
-
if (!name || typeof name !== "string") {
|
|
960
|
-
return `Sheet${sheetCount + 1}`;
|
|
961
|
-
}
|
|
962
|
-
let sanitized = name.replace(INVALID_SHEET_NAME_CHARS, "_");
|
|
963
|
-
if (sanitized.length > 31) {
|
|
964
|
-
sanitized = sanitized.substring(0, 31);
|
|
965
|
-
}
|
|
966
|
-
if (sanitized.trim().length === 0) {
|
|
967
|
-
sanitized = `Sheet${sheetCount + 1}`;
|
|
968
|
-
}
|
|
969
|
-
return sanitized;
|
|
970
|
-
}
|
|
971
|
-
function initColWidths(columnCount) {
|
|
972
|
-
return new Array(columnCount).fill(-1);
|
|
973
|
-
}
|
|
974
|
-
function applyHeaderWidths(colWidths, headers, columnCount) {
|
|
975
|
-
for (let i = 0; i < columnCount; i++) {
|
|
976
|
-
const len = headers[i] ? headers[i].length + 1 : 0;
|
|
977
|
-
let width = 1.25 * len + 2;
|
|
978
|
-
if (width > 80) width = 80;
|
|
979
|
-
if (colWidths[i] < width) colWidths[i] = width;
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
function updateColWidthsFromRows(colWidths, rows, sampleLimit = 100) {
|
|
983
|
-
for (let r = 0; r < Math.min(rows.length, sampleLimit); r++) {
|
|
984
|
-
const row = rows[r];
|
|
985
|
-
for (let c = 0; c < row.length; c++) {
|
|
986
|
-
const raw = row[c];
|
|
987
|
-
if (raw === null || raw === void 0) continue;
|
|
988
|
-
const val = isFormattedCell(raw) ? raw.value : raw;
|
|
989
|
-
const len = val instanceof Date ? 20 : String(val).length + 1;
|
|
990
|
-
let width = 1.25 * len + 2;
|
|
991
|
-
if (width > 80) width = 80;
|
|
992
|
-
if (colWidths[c] < width) colWidths[c] = width;
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
function defaultColWidth(width, floor = false) {
|
|
997
|
-
if (width > 0) {
|
|
998
|
-
return floor ? Math.floor(width) : width;
|
|
999
|
-
}
|
|
1000
|
-
return 10;
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
1409
|
// src/StreamingSheetState.ts
|
|
1004
1410
|
var StreamingSheetState = class {
|
|
1005
1411
|
constructor() {
|
|
@@ -1052,6 +1458,8 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
1052
1458
|
* @param filePath Destination `.xlsb` path on disk.
|
|
1053
1459
|
*/
|
|
1054
1460
|
constructor(filePath) {
|
|
1461
|
+
this._outputError = null;
|
|
1462
|
+
this._finalized = false;
|
|
1055
1463
|
this.sheetCount = 0;
|
|
1056
1464
|
this.sheetList = [];
|
|
1057
1465
|
this.sstDic = /* @__PURE__ */ new Map();
|
|
@@ -1062,8 +1470,20 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
1062
1470
|
this._nextXfIdx = 4;
|
|
1063
1471
|
this._stFmtRecords = /* @__PURE__ */ new Map();
|
|
1064
1472
|
this.stream = new StreamingSheetState();
|
|
1065
|
-
this.
|
|
1473
|
+
this._targetPath = path2.resolve(filePath);
|
|
1474
|
+
fs.mkdirSync(path2.dirname(this._targetPath), { recursive: true });
|
|
1475
|
+
this._tempPath = path2.join(
|
|
1476
|
+
path2.dirname(this._targetPath),
|
|
1477
|
+
`.${path2.basename(this._targetPath)}.${randomUUID()}.tmp`
|
|
1478
|
+
);
|
|
1479
|
+
this.output = fs.createWriteStream(this._tempPath);
|
|
1480
|
+
this.output.on("error", (err) => {
|
|
1481
|
+
this._outputError = err;
|
|
1482
|
+
});
|
|
1066
1483
|
this.archive = archiver("zip");
|
|
1484
|
+
this.archive.on("error", (err) => {
|
|
1485
|
+
this._outputError = err;
|
|
1486
|
+
});
|
|
1067
1487
|
this.archive.pipe(this.output);
|
|
1068
1488
|
this._oaEpoch = Date.UTC(1899, 11, 30);
|
|
1069
1489
|
this._sheet1Bytes = Buffer.from([
|
|
@@ -2933,6 +3353,11 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
2933
3353
|
* @param options Optional settings (hidden, doAutofilter)
|
|
2934
3354
|
*/
|
|
2935
3355
|
startSheet(sheetName, columnCount, headers, options = {}) {
|
|
3356
|
+
assertColumnCount(columnCount);
|
|
3357
|
+
if (headers !== void 0) {
|
|
3358
|
+
assertRowFitsColumns("header", headers.length, columnCount);
|
|
3359
|
+
for (const h of headers) assertCellTextLength(h);
|
|
3360
|
+
}
|
|
2936
3361
|
const { hidden = false, doAutofilter = true, sampleRows } = options;
|
|
2937
3362
|
this.addSheet(sheetName, hidden);
|
|
2938
3363
|
this.stream.begin(columnCount, doAutofilter && headers !== void 0, new BigBuffer());
|
|
@@ -2998,6 +3423,7 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
2998
3423
|
`Row length mismatch. Expected ${this.stream.endCol - this.stream.startCol} columns, got ${row.length}`
|
|
2999
3424
|
);
|
|
3000
3425
|
}
|
|
3426
|
+
assertRowCount(this.stream.rowNum + 1);
|
|
3001
3427
|
this.createRowHeader(bigBuf, this.stream.rowNum, this.stream.startCol, this.stream.endCol);
|
|
3002
3428
|
for (let c = 0; c < row.length; c++) {
|
|
3003
3429
|
const raw = row[c];
|
|
@@ -3020,7 +3446,7 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3020
3446
|
} else if (typeof val === "boolean") {
|
|
3021
3447
|
this.writeBool(bigBuf, val, c);
|
|
3022
3448
|
} else if (val instanceof Date) {
|
|
3023
|
-
const oaDate = (val.getTime()
|
|
3449
|
+
const oaDate = timeToOaDate(val.getTime(), this._oaEpoch);
|
|
3024
3450
|
this.writeDouble(bigBuf, oaDate, c, fmtString !== null ? styleNum : 1);
|
|
3025
3451
|
} else if (val !== null && val !== void 0) {
|
|
3026
3452
|
this.writeString(bigBuf, val.toString(), c, false, fmtString !== null ? styleNum : void 0);
|
|
@@ -3077,6 +3503,16 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3077
3503
|
} else if (headers) {
|
|
3078
3504
|
columnCount = headers.length;
|
|
3079
3505
|
}
|
|
3506
|
+
if (columnCount > 0) assertColumnCount(columnCount);
|
|
3507
|
+
const totalRowsCheck = rows.length + (headers ? 1 : 0);
|
|
3508
|
+
assertRowCount(totalRowsCheck);
|
|
3509
|
+
if (headers) {
|
|
3510
|
+
assertRowFitsColumns("header", headers.length, columnCount);
|
|
3511
|
+
for (const h of headers) assertCellTextLength(h);
|
|
3512
|
+
}
|
|
3513
|
+
for (let r = 0; r < rows.length; r++) {
|
|
3514
|
+
assertRowFitsColumns("row", rows[r].length, columnCount);
|
|
3515
|
+
}
|
|
3080
3516
|
const colWidths = initColWidths(columnCount);
|
|
3081
3517
|
if (headers) {
|
|
3082
3518
|
applyHeaderWidths(colWidths, headers, columnCount);
|
|
@@ -3152,7 +3588,7 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3152
3588
|
} else if (typeof val === "boolean") {
|
|
3153
3589
|
this.writeBool(bigBuf, val, c);
|
|
3154
3590
|
} else if (val instanceof Date) {
|
|
3155
|
-
const oaDate = (val.getTime()
|
|
3591
|
+
const oaDate = timeToOaDate(val.getTime(), this._oaEpoch);
|
|
3156
3592
|
this.writeDouble(bigBuf, oaDate, c, fmtString !== null ? styleNum : 1);
|
|
3157
3593
|
} else if (val !== null && val !== void 0) {
|
|
3158
3594
|
this.writeString(bigBuf, val.toString(), c, false, fmtString !== null ? styleNum : void 0);
|
|
@@ -3235,13 +3671,14 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3235
3671
|
bigBuf.writeUnsafeByte(val ? 1 : 0);
|
|
3236
3672
|
}
|
|
3237
3673
|
writeDateTime(bigBuf, date, colNum) {
|
|
3238
|
-
const oaDate = (date.getTime()
|
|
3674
|
+
const oaDate = timeToOaDate(date.getTime(), this._oaEpoch);
|
|
3239
3675
|
this.writeDouble(bigBuf, oaDate, colNum, 1);
|
|
3240
3676
|
}
|
|
3241
3677
|
_getDateWidth(_date) {
|
|
3242
3678
|
return 19;
|
|
3243
3679
|
}
|
|
3244
3680
|
writeString(bigBuf, val, colNum, bolded = false, styleOverride) {
|
|
3681
|
+
assertCellTextLength(val);
|
|
3245
3682
|
let index;
|
|
3246
3683
|
if (this.sstDic.has(val)) {
|
|
3247
3684
|
index = this.sstDic.get(val);
|
|
@@ -3267,8 +3704,8 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3267
3704
|
bigBuf.writeByte(159);
|
|
3268
3705
|
bigBuf.writeByte(1);
|
|
3269
3706
|
bigBuf.writeByte(8);
|
|
3270
|
-
bigBuf.writeInt32LE(this.sstCntUnique);
|
|
3271
3707
|
bigBuf.writeInt32LE(this.sstCntAll);
|
|
3708
|
+
bigBuf.writeInt32LE(this.sstCntUnique);
|
|
3272
3709
|
for (const [txt] of this.sstDic) {
|
|
3273
3710
|
const txtLen = txt.length;
|
|
3274
3711
|
bigBuf.writeByte(19);
|
|
@@ -3321,8 +3758,61 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3321
3758
|
* Must be awaited before the file is fully written.
|
|
3322
3759
|
*/
|
|
3323
3760
|
finalize() {
|
|
3324
|
-
return new Promise((
|
|
3761
|
+
return new Promise((resolve6, reject) => {
|
|
3762
|
+
if (this._finalized) {
|
|
3763
|
+
reject(new Error("XlsbWriter: already finalized"));
|
|
3764
|
+
return;
|
|
3765
|
+
}
|
|
3766
|
+
this._finalized = true;
|
|
3767
|
+
let settled = false;
|
|
3768
|
+
const cleanupTemp = () => {
|
|
3769
|
+
try {
|
|
3770
|
+
if (fs.existsSync(this._tempPath)) fs.rmSync(this._tempPath);
|
|
3771
|
+
} catch {
|
|
3772
|
+
}
|
|
3773
|
+
};
|
|
3774
|
+
const fail = (err) => {
|
|
3775
|
+
if (settled) return;
|
|
3776
|
+
settled = true;
|
|
3777
|
+
try {
|
|
3778
|
+
this.archive.abort();
|
|
3779
|
+
} catch {
|
|
3780
|
+
}
|
|
3781
|
+
try {
|
|
3782
|
+
this.output.destroy();
|
|
3783
|
+
} catch {
|
|
3784
|
+
}
|
|
3785
|
+
cleanupTemp();
|
|
3786
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
3787
|
+
};
|
|
3788
|
+
const succeed = () => {
|
|
3789
|
+
if (settled) return;
|
|
3790
|
+
if (this._outputError) {
|
|
3791
|
+
fail(this._outputError);
|
|
3792
|
+
return;
|
|
3793
|
+
}
|
|
3794
|
+
settled = true;
|
|
3795
|
+
try {
|
|
3796
|
+
const fd = fs.openSync(this._tempPath, "r+");
|
|
3797
|
+
try {
|
|
3798
|
+
fs.fsyncSync(fd);
|
|
3799
|
+
} finally {
|
|
3800
|
+
fs.closeSync(fd);
|
|
3801
|
+
}
|
|
3802
|
+
try {
|
|
3803
|
+
const existing = fs.statSync(this._targetPath);
|
|
3804
|
+
fs.chmodSync(this._tempPath, existing.mode & 511);
|
|
3805
|
+
} catch {
|
|
3806
|
+
}
|
|
3807
|
+
fs.renameSync(this._tempPath, this._targetPath);
|
|
3808
|
+
resolve6();
|
|
3809
|
+
} catch (err) {
|
|
3810
|
+
cleanupTemp();
|
|
3811
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
3812
|
+
}
|
|
3813
|
+
};
|
|
3325
3814
|
try {
|
|
3815
|
+
if (this._outputError) throw this._outputError;
|
|
3326
3816
|
this.saveSst();
|
|
3327
3817
|
this.archive.append(this._buildStylesBin(), { name: "xl/styles.bin" });
|
|
3328
3818
|
const wbBuffers = [];
|
|
@@ -3419,11 +3909,12 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3419
3909
|
</Relationships>`;
|
|
3420
3910
|
this.archive.append(wsRels, { name: `xl/worksheets/_rels/${sheet.nameInArchive}.rels` });
|
|
3421
3911
|
}
|
|
3422
|
-
this.output.
|
|
3423
|
-
this.
|
|
3912
|
+
this.output.once("close", succeed);
|
|
3913
|
+
this.output.once("error", fail);
|
|
3914
|
+
this.archive.once("error", fail);
|
|
3424
3915
|
this.archive.finalize();
|
|
3425
3916
|
} catch (err) {
|
|
3426
|
-
|
|
3917
|
+
fail(err);
|
|
3427
3918
|
}
|
|
3428
3919
|
});
|
|
3429
3920
|
}
|
|
@@ -3431,27 +3922,17 @@ var XlsbWriter = class _XlsbWriter {
|
|
|
3431
3922
|
|
|
3432
3923
|
// src/XlsxWriter.ts
|
|
3433
3924
|
import * as fs2 from "fs";
|
|
3925
|
+
import * as path3 from "path";
|
|
3926
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
3434
3927
|
import archiver2 from "archiver";
|
|
3435
3928
|
import { Readable as Readable2 } from "stream";
|
|
3436
|
-
var COLUMN_LETTERS = (() => {
|
|
3437
|
-
const letters = [];
|
|
3438
|
-
for (let i = 65; i < 91; i++) {
|
|
3439
|
-
letters.push(String.fromCharCode(i));
|
|
3440
|
-
}
|
|
3441
|
-
const temp = [];
|
|
3442
|
-
for (const p of letters) {
|
|
3443
|
-
for (let i = 65; i < 91; i++) {
|
|
3444
|
-
temp.push(p + String.fromCharCode(i));
|
|
3445
|
-
}
|
|
3446
|
-
}
|
|
3447
|
-
letters.push(...temp);
|
|
3448
|
-
return letters;
|
|
3449
|
-
})();
|
|
3450
3929
|
var XlsxWriter = class {
|
|
3451
3930
|
/**
|
|
3452
3931
|
* @param filePath Destination `.xlsx` path on disk.
|
|
3453
3932
|
*/
|
|
3454
3933
|
constructor(filePath) {
|
|
3934
|
+
this._outputError = null;
|
|
3935
|
+
this._finalized = false;
|
|
3455
3936
|
this.sheetCount = 0;
|
|
3456
3937
|
this.sheetList = [];
|
|
3457
3938
|
this.sstArray = [];
|
|
@@ -3465,13 +3946,25 @@ var XlsxWriter = class {
|
|
|
3465
3946
|
this._nextXfIndex = 4;
|
|
3466
3947
|
this.stream = new StreamingSheetState();
|
|
3467
3948
|
this.currentColLetters = [];
|
|
3468
|
-
this.
|
|
3949
|
+
this._targetPath = path3.resolve(filePath);
|
|
3950
|
+
fs2.mkdirSync(path3.dirname(this._targetPath), { recursive: true });
|
|
3951
|
+
this._tempPath = path3.join(
|
|
3952
|
+
path3.dirname(this._targetPath),
|
|
3953
|
+
`.${path3.basename(this._targetPath)}.${randomUUID2()}.tmp`
|
|
3954
|
+
);
|
|
3955
|
+
this.output = fs2.createWriteStream(this._tempPath);
|
|
3956
|
+
this.output.on("error", (err) => {
|
|
3957
|
+
this._outputError = err;
|
|
3958
|
+
});
|
|
3469
3959
|
this.archive = archiver2("zip");
|
|
3960
|
+
this.archive.on("error", (err) => {
|
|
3961
|
+
this._outputError = err;
|
|
3962
|
+
});
|
|
3470
3963
|
this.archive.pipe(this.output);
|
|
3471
3964
|
this._oaEpoch = Date.UTC(1899, 11, 30);
|
|
3472
3965
|
}
|
|
3473
3966
|
_getColumnLetter(colIndex) {
|
|
3474
|
-
return colIndex
|
|
3967
|
+
return columnIndexToLetter(colIndex);
|
|
3475
3968
|
}
|
|
3476
3969
|
/** @deprecated Internal; kept for unit-test access. Prefer sanitizeSheetName(). */
|
|
3477
3970
|
_sanitizeSheetName(name) {
|
|
@@ -3523,6 +4016,11 @@ var XlsxWriter = class {
|
|
|
3523
4016
|
* Start streaming a sheet. Call `writeRow` for each row, then `endSheet`.
|
|
3524
4017
|
*/
|
|
3525
4018
|
startSheet(sheetName, columnCount, headers, options = {}) {
|
|
4019
|
+
assertColumnCount(columnCount);
|
|
4020
|
+
if (headers !== void 0) {
|
|
4021
|
+
assertRowFitsColumns("header", headers.length, columnCount);
|
|
4022
|
+
for (const h of headers) assertCellTextLength(h);
|
|
4023
|
+
}
|
|
3526
4024
|
const { hidden = false, doAutofilter = true, sampleRows } = options;
|
|
3527
4025
|
this.addSheet(sheetName, hidden);
|
|
3528
4026
|
this.stream.begin(columnCount, doAutofilter && headers !== void 0, new BigBuffer());
|
|
@@ -3579,6 +4077,7 @@ var XlsxWriter = class {
|
|
|
3579
4077
|
);
|
|
3580
4078
|
}
|
|
3581
4079
|
this.stream.rowNum++;
|
|
4080
|
+
assertRowCount(this.stream.rowNum);
|
|
3582
4081
|
bigBuf.writeString(`<row r="${this.stream.rowNum}">`);
|
|
3583
4082
|
for (let c = 0; c < row.length; c++) {
|
|
3584
4083
|
const raw = row[c];
|
|
@@ -3642,6 +4141,16 @@ var XlsxWriter = class {
|
|
|
3642
4141
|
} else if (headers) {
|
|
3643
4142
|
columnCount = headers.length;
|
|
3644
4143
|
}
|
|
4144
|
+
if (columnCount > 0) assertColumnCount(columnCount);
|
|
4145
|
+
const totalRows = rows.length + (headers ? 1 : 0);
|
|
4146
|
+
assertRowCount(totalRows);
|
|
4147
|
+
if (headers) {
|
|
4148
|
+
assertRowFitsColumns("header", headers.length, columnCount);
|
|
4149
|
+
for (const h of headers) assertCellTextLength(h);
|
|
4150
|
+
}
|
|
4151
|
+
for (let r = 0; r < rows.length; r++) {
|
|
4152
|
+
assertRowFitsColumns("row", rows[r].length, columnCount);
|
|
4153
|
+
}
|
|
3645
4154
|
const colWidths = initColWidths(columnCount);
|
|
3646
4155
|
const colLetters = new Array(columnCount);
|
|
3647
4156
|
for (let i = 0; i < columnCount; i++) {
|
|
@@ -3653,7 +4162,6 @@ var XlsxWriter = class {
|
|
|
3653
4162
|
updateColWidthsFromRows(colWidths, rows);
|
|
3654
4163
|
bigBuf.writeString('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>');
|
|
3655
4164
|
bigBuf.writeString('<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">');
|
|
3656
|
-
const totalRows = rows.length + (headers ? 1 : 0);
|
|
3657
4165
|
if (totalRows > 0 && columnCount > 0) {
|
|
3658
4166
|
bigBuf.writeString(`<dimension ref="A1:${colLetters[columnCount - 1]}${totalRows}"/>`);
|
|
3659
4167
|
} else {
|
|
@@ -3739,6 +4247,7 @@ var XlsxWriter = class {
|
|
|
3739
4247
|
});
|
|
3740
4248
|
}
|
|
3741
4249
|
_writeStringCell(bigBuf, val, colRef, rowNum, styleOverride) {
|
|
4250
|
+
assertCellTextLength(val);
|
|
3742
4251
|
let index = this.sstMap.get(val);
|
|
3743
4252
|
if (index === void 0) {
|
|
3744
4253
|
index = this.sstArray.length;
|
|
@@ -3760,29 +4269,79 @@ var XlsxWriter = class {
|
|
|
3760
4269
|
return xfIndex;
|
|
3761
4270
|
}
|
|
3762
4271
|
_toOADate(date) {
|
|
3763
|
-
return (date.getTime()
|
|
4272
|
+
return timeToOaDate(date.getTime(), this._oaEpoch);
|
|
3764
4273
|
}
|
|
3765
4274
|
_getDateWidth(_date) {
|
|
3766
4275
|
return 19;
|
|
3767
4276
|
}
|
|
3768
4277
|
/** Finalize the ZIP package and close the output stream. */
|
|
3769
4278
|
finalize() {
|
|
3770
|
-
return new Promise((
|
|
4279
|
+
return new Promise((resolve6, reject) => {
|
|
4280
|
+
if (this._finalized) {
|
|
4281
|
+
reject(new Error("XlsxWriter: already finalized"));
|
|
4282
|
+
return;
|
|
4283
|
+
}
|
|
4284
|
+
this._finalized = true;
|
|
4285
|
+
let settled = false;
|
|
4286
|
+
const cleanupTemp = () => {
|
|
4287
|
+
try {
|
|
4288
|
+
if (fs2.existsSync(this._tempPath)) fs2.rmSync(this._tempPath);
|
|
4289
|
+
} catch {
|
|
4290
|
+
}
|
|
4291
|
+
};
|
|
4292
|
+
const fail = (err) => {
|
|
4293
|
+
if (settled) return;
|
|
4294
|
+
settled = true;
|
|
4295
|
+
try {
|
|
4296
|
+
this.archive.abort();
|
|
4297
|
+
} catch {
|
|
4298
|
+
}
|
|
4299
|
+
try {
|
|
4300
|
+
this.output.destroy();
|
|
4301
|
+
} catch {
|
|
4302
|
+
}
|
|
4303
|
+
cleanupTemp();
|
|
4304
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
4305
|
+
};
|
|
4306
|
+
const succeed = () => {
|
|
4307
|
+
if (settled) return;
|
|
4308
|
+
if (this._outputError) {
|
|
4309
|
+
fail(this._outputError);
|
|
4310
|
+
return;
|
|
4311
|
+
}
|
|
4312
|
+
settled = true;
|
|
4313
|
+
try {
|
|
4314
|
+
const fd = fs2.openSync(this._tempPath, "r+");
|
|
4315
|
+
try {
|
|
4316
|
+
fs2.fsyncSync(fd);
|
|
4317
|
+
} finally {
|
|
4318
|
+
fs2.closeSync(fd);
|
|
4319
|
+
}
|
|
4320
|
+
try {
|
|
4321
|
+
const existing = fs2.statSync(this._targetPath);
|
|
4322
|
+
fs2.chmodSync(this._tempPath, existing.mode & 511);
|
|
4323
|
+
} catch {
|
|
4324
|
+
}
|
|
4325
|
+
fs2.renameSync(this._tempPath, this._targetPath);
|
|
4326
|
+
resolve6();
|
|
4327
|
+
} catch (err) {
|
|
4328
|
+
cleanupTemp();
|
|
4329
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
4330
|
+
}
|
|
4331
|
+
};
|
|
3771
4332
|
try {
|
|
4333
|
+
if (this._outputError) throw this._outputError;
|
|
3772
4334
|
this._writeSharedStrings();
|
|
3773
4335
|
this._writeStyles();
|
|
3774
4336
|
this._writeWorkbook();
|
|
3775
4337
|
this._writeContentTypes();
|
|
3776
4338
|
this._writeRels();
|
|
3777
|
-
this.output.
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
this.archive.on("error", (err) => {
|
|
3781
|
-
reject(err);
|
|
3782
|
-
});
|
|
4339
|
+
this.output.once("close", succeed);
|
|
4340
|
+
this.output.once("error", fail);
|
|
4341
|
+
this.archive.once("error", fail);
|
|
3783
4342
|
this.archive.finalize();
|
|
3784
4343
|
} catch (err) {
|
|
3785
|
-
|
|
4344
|
+
fail(err);
|
|
3786
4345
|
}
|
|
3787
4346
|
});
|
|
3788
4347
|
}
|
|
@@ -3914,13 +4473,13 @@ ${xfEntries.join("\n")}
|
|
|
3914
4473
|
// src/XlsxUpdater.ts
|
|
3915
4474
|
import * as fs5 from "fs";
|
|
3916
4475
|
import * as os from "os";
|
|
3917
|
-
import * as
|
|
4476
|
+
import * as path6 from "path";
|
|
3918
4477
|
import AdmZip2 from "adm-zip";
|
|
3919
4478
|
|
|
3920
4479
|
// src/atomicFile.ts
|
|
3921
4480
|
import * as fs3 from "fs";
|
|
3922
|
-
import * as
|
|
3923
|
-
import { randomUUID } from "crypto";
|
|
4481
|
+
import * as path4 from "path";
|
|
4482
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
3924
4483
|
var realFileOperations = {
|
|
3925
4484
|
exists: (filePath) => fs3.existsSync(filePath),
|
|
3926
4485
|
rename: (sourcePath, destinationPath) => fs3.renameSync(sourcePath, destinationPath),
|
|
@@ -3943,7 +4502,7 @@ function readDestinationMode(target) {
|
|
|
3943
4502
|
}
|
|
3944
4503
|
}
|
|
3945
4504
|
function installTemporaryOutput(temporaryPath, destinationPath, overwrite, operations = realFileOperations) {
|
|
3946
|
-
const backupPath = `${destinationPath}.${
|
|
4505
|
+
const backupPath = `${destinationPath}.${randomUUID3()}.replacement-backup`;
|
|
3947
4506
|
let backupCreated = false;
|
|
3948
4507
|
let installed = false;
|
|
3949
4508
|
if (operations.exists(destinationPath)) {
|
|
@@ -3991,11 +4550,11 @@ function installTemporaryOutput(temporaryPath, destinationPath, overwrite, opera
|
|
|
3991
4550
|
}
|
|
3992
4551
|
}
|
|
3993
4552
|
function writeBufferAtomically(buffer, destinationPath) {
|
|
3994
|
-
const target =
|
|
3995
|
-
fs3.mkdirSync(
|
|
3996
|
-
const temporaryPath =
|
|
3997
|
-
|
|
3998
|
-
`.${
|
|
4553
|
+
const target = path4.resolve(destinationPath);
|
|
4554
|
+
fs3.mkdirSync(path4.dirname(target), { recursive: true });
|
|
4555
|
+
const temporaryPath = path4.join(
|
|
4556
|
+
path4.dirname(target),
|
|
4557
|
+
`.${path4.basename(target)}.${randomUUID3()}.tmp`
|
|
3999
4558
|
);
|
|
4000
4559
|
const destinationMode = readDestinationMode(target);
|
|
4001
4560
|
try {
|
|
@@ -4031,8 +4590,8 @@ function writeBufferAtomically(buffer, destinationPath) {
|
|
|
4031
4590
|
|
|
4032
4591
|
// src/streamingZip.ts
|
|
4033
4592
|
import * as fs4 from "fs";
|
|
4034
|
-
import * as
|
|
4035
|
-
import { randomUUID as
|
|
4593
|
+
import * as path5 from "path";
|
|
4594
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4036
4595
|
import { finished } from "stream/promises";
|
|
4037
4596
|
import { Readable as Readable3 } from "stream";
|
|
4038
4597
|
import archiver3 from "archiver";
|
|
@@ -4044,8 +4603,8 @@ function readDestinationMode2(target) {
|
|
|
4044
4603
|
return void 0;
|
|
4045
4604
|
}
|
|
4046
4605
|
}
|
|
4047
|
-
function appendAndWait(archive, source, name) {
|
|
4048
|
-
return new Promise((
|
|
4606
|
+
function appendAndWait(archive, source, name, output) {
|
|
4607
|
+
return new Promise((resolve6, reject) => {
|
|
4049
4608
|
const cleanup = () => {
|
|
4050
4609
|
archive.off("entry", onEntry);
|
|
4051
4610
|
archive.off("error", onError);
|
|
@@ -4053,10 +4612,13 @@ function appendAndWait(archive, source, name) {
|
|
|
4053
4612
|
if (typeof maybeEmitter2.off === "function") {
|
|
4054
4613
|
maybeEmitter2.off("error", onError);
|
|
4055
4614
|
}
|
|
4615
|
+
if (output) {
|
|
4616
|
+
output.off("error", onError);
|
|
4617
|
+
}
|
|
4056
4618
|
};
|
|
4057
4619
|
const onEntry = () => {
|
|
4058
4620
|
cleanup();
|
|
4059
|
-
|
|
4621
|
+
resolve6();
|
|
4060
4622
|
};
|
|
4061
4623
|
const onError = (error) => {
|
|
4062
4624
|
cleanup();
|
|
@@ -4064,6 +4626,9 @@ function appendAndWait(archive, source, name) {
|
|
|
4064
4626
|
};
|
|
4065
4627
|
archive.once("entry", onEntry);
|
|
4066
4628
|
archive.once("error", onError);
|
|
4629
|
+
if (output) {
|
|
4630
|
+
output.once("error", onError);
|
|
4631
|
+
}
|
|
4067
4632
|
const maybeEmitter = source;
|
|
4068
4633
|
if (typeof maybeEmitter.once === "function") {
|
|
4069
4634
|
maybeEmitter.once("error", onError);
|
|
@@ -4077,11 +4642,11 @@ function appendAndWait(archive, source, name) {
|
|
|
4077
4642
|
});
|
|
4078
4643
|
}
|
|
4079
4644
|
async function writeAdmZipStreaming(zip, stagedParts, destinationPath) {
|
|
4080
|
-
const target =
|
|
4081
|
-
fs4.mkdirSync(
|
|
4082
|
-
const temporaryPath =
|
|
4083
|
-
|
|
4084
|
-
`.${
|
|
4645
|
+
const target = path5.resolve(destinationPath);
|
|
4646
|
+
fs4.mkdirSync(path5.dirname(target), { recursive: true });
|
|
4647
|
+
const temporaryPath = path5.join(
|
|
4648
|
+
path5.dirname(target),
|
|
4649
|
+
`.${path5.basename(target)}.${randomUUID4()}.tmp`
|
|
4085
4650
|
);
|
|
4086
4651
|
const destinationMode = readDestinationMode2(target);
|
|
4087
4652
|
let output = null;
|
|
@@ -4091,18 +4656,32 @@ async function writeAdmZipStreaming(zip, stagedParts, destinationPath) {
|
|
|
4091
4656
|
temporaryPath,
|
|
4092
4657
|
destinationMode !== void 0 ? { mode: destinationMode } : {}
|
|
4093
4658
|
);
|
|
4659
|
+
let outputError = null;
|
|
4660
|
+
output.on("error", (err) => {
|
|
4661
|
+
outputError = err;
|
|
4662
|
+
try {
|
|
4663
|
+
archive == null ? void 0 : archive.abort();
|
|
4664
|
+
} catch {
|
|
4665
|
+
}
|
|
4666
|
+
});
|
|
4094
4667
|
archive = archiver3("zip");
|
|
4668
|
+
archive.on("error", (err) => {
|
|
4669
|
+
outputError = err;
|
|
4670
|
+
});
|
|
4095
4671
|
archive.pipe(output);
|
|
4096
4672
|
for (const entry of zip.getEntries()) {
|
|
4673
|
+
if (outputError) throw outputError;
|
|
4097
4674
|
const stagedPath = stagedParts.get(entry.entryName);
|
|
4098
4675
|
if (stagedPath) {
|
|
4099
|
-
await appendAndWait(archive, fs4.createReadStream(stagedPath), entry.entryName);
|
|
4676
|
+
await appendAndWait(archive, fs4.createReadStream(stagedPath), entry.entryName, output);
|
|
4100
4677
|
} else {
|
|
4101
|
-
await appendAndWait(archive, Readable3.from([entry.getData()]), entry.entryName);
|
|
4678
|
+
await appendAndWait(archive, Readable3.from([entry.getData()]), entry.entryName, output);
|
|
4102
4679
|
}
|
|
4103
4680
|
}
|
|
4681
|
+
if (outputError) throw outputError;
|
|
4104
4682
|
await archive.finalize();
|
|
4105
4683
|
await finished(output);
|
|
4684
|
+
if (outputError) throw outputError;
|
|
4106
4685
|
if (destinationMode !== void 0) {
|
|
4107
4686
|
try {
|
|
4108
4687
|
fs4.chmodSync(temporaryPath, destinationMode);
|
|
@@ -4136,6 +4715,23 @@ async function writeAdmZipStreaming(zip, stagedParts, destinationPath) {
|
|
|
4136
4715
|
}
|
|
4137
4716
|
|
|
4138
4717
|
// src/XlsxUpdater.ts
|
|
4718
|
+
function parseCellRange(ref) {
|
|
4719
|
+
const m = /^\$?([A-Za-z]+)\$?(\d+)(?::\$?([A-Za-z]+)\$?(\d+))?$/.exec(ref.trim());
|
|
4720
|
+
if (!m) return null;
|
|
4721
|
+
const c1 = columnLetterToIndex(m[1].toUpperCase());
|
|
4722
|
+
const r1 = parseInt(m[2], 10);
|
|
4723
|
+
const c2 = m[3] ? columnLetterToIndex(m[3].toUpperCase()) : c1;
|
|
4724
|
+
const r2 = m[4] ? parseInt(m[4], 10) : r1;
|
|
4725
|
+
if (!Number.isFinite(c1) || !Number.isFinite(r1) || !Number.isFinite(c2) || !Number.isFinite(r2)) {
|
|
4726
|
+
return null;
|
|
4727
|
+
}
|
|
4728
|
+
return {
|
|
4729
|
+
c1: Math.min(c1, c2),
|
|
4730
|
+
r1: Math.min(r1, r2),
|
|
4731
|
+
c2: Math.max(c1, c2),
|
|
4732
|
+
r2: Math.max(r1, r2)
|
|
4733
|
+
};
|
|
4734
|
+
}
|
|
4139
4735
|
var XlsxUpdater = class _XlsxUpdater {
|
|
4140
4736
|
/**
|
|
4141
4737
|
* @param path Path to an existing `.xlsx` file.
|
|
@@ -4222,6 +4818,8 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4222
4818
|
const dimRef = totalRows > 0 && lastCol >= 0 ? `A1:${columnIndexToLetter(lastCol)}${totalRows}` : "A1";
|
|
4223
4819
|
this._updatePivotCaches(sheetName, dimRef, rows.length);
|
|
4224
4820
|
this._addRefreshOnLoadToPivotTables();
|
|
4821
|
+
this._removeCalcChain();
|
|
4822
|
+
this._updateTableRefs(sheetName, dimRef);
|
|
4225
4823
|
}
|
|
4226
4824
|
/**
|
|
4227
4825
|
* Replace worksheet rows from a one-pass source without materialising the
|
|
@@ -4294,10 +4892,10 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4294
4892
|
if (outputError) throw outputError;
|
|
4295
4893
|
writtenBytes += data.length;
|
|
4296
4894
|
if (rowOutput.write(data)) return;
|
|
4297
|
-
await new Promise((
|
|
4895
|
+
await new Promise((resolve6, reject) => {
|
|
4298
4896
|
const onDrain = () => {
|
|
4299
4897
|
cleanup();
|
|
4300
|
-
|
|
4898
|
+
resolve6();
|
|
4301
4899
|
};
|
|
4302
4900
|
const onError = (error) => {
|
|
4303
4901
|
cleanup();
|
|
@@ -4337,8 +4935,8 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4337
4935
|
lastKeptEnd = writtenBytes;
|
|
4338
4936
|
}
|
|
4339
4937
|
}
|
|
4340
|
-
await new Promise((
|
|
4341
|
-
rowOutput.once("close",
|
|
4938
|
+
await new Promise((resolve6, reject) => {
|
|
4939
|
+
rowOutput.once("close", resolve6);
|
|
4342
4940
|
rowOutput.once("error", reject);
|
|
4343
4941
|
rowOutput.end();
|
|
4344
4942
|
});
|
|
@@ -4357,6 +4955,8 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4357
4955
|
this._commitSharedStrings();
|
|
4358
4956
|
this._updatePivotCaches(sheetName, dimRef, keptDataRows);
|
|
4359
4957
|
this._addRefreshOnLoadToPivotTables();
|
|
4958
|
+
this._removeCalcChain();
|
|
4959
|
+
this._updateTableRefs(sheetName, dimRef);
|
|
4360
4960
|
if (oldStage && oldStage !== outputPath) {
|
|
4361
4961
|
this._removeTemporaryFile(oldStage);
|
|
4362
4962
|
}
|
|
@@ -4430,7 +5030,7 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4430
5030
|
}
|
|
4431
5031
|
_ensureTemporaryDirectory() {
|
|
4432
5032
|
if (this.temporaryDirectory === null) {
|
|
4433
|
-
this.temporaryDirectory = fs5.mkdtempSync(
|
|
5033
|
+
this.temporaryDirectory = fs5.mkdtempSync(path6.join(os.tmpdir(), "spreadsheet-updater-"));
|
|
4434
5034
|
} else {
|
|
4435
5035
|
try {
|
|
4436
5036
|
fs5.mkdirSync(this.temporaryDirectory, { recursive: true });
|
|
@@ -4440,7 +5040,7 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4440
5040
|
return this.temporaryDirectory;
|
|
4441
5041
|
}
|
|
4442
5042
|
_temporaryPath(suffix) {
|
|
4443
|
-
return
|
|
5043
|
+
return path6.join(this._ensureTemporaryDirectory(), `part-${Date.now()}-${Math.random().toString(16).slice(2)}${suffix}`);
|
|
4444
5044
|
}
|
|
4445
5045
|
_removeTemporaryFile(filePath) {
|
|
4446
5046
|
try {
|
|
@@ -4480,10 +5080,15 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4480
5080
|
const rIdToTarget = {};
|
|
4481
5081
|
if (relsEntry) {
|
|
4482
5082
|
const rels = relsEntry.getData().toString("utf8");
|
|
4483
|
-
const
|
|
4484
|
-
let
|
|
4485
|
-
while ((
|
|
4486
|
-
|
|
5083
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
5084
|
+
let tagMatch;
|
|
5085
|
+
while ((tagMatch = relTagRe.exec(rels)) !== null) {
|
|
5086
|
+
const attrs = parseTagAttributes(tagMatch[0]);
|
|
5087
|
+
const id = attrs["Id"] ?? attrs["id"];
|
|
5088
|
+
const target = attrs["Target"] ?? attrs["target"];
|
|
5089
|
+
if (id !== void 0 && target !== void 0) {
|
|
5090
|
+
rIdToTarget[id] = target;
|
|
5091
|
+
}
|
|
4487
5092
|
}
|
|
4488
5093
|
}
|
|
4489
5094
|
const wbEntry = this.zip.getEntry("xl/workbook.xml");
|
|
@@ -4492,21 +5097,23 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4492
5097
|
let match;
|
|
4493
5098
|
while ((match = sheetRe.exec(wbXml)) !== null) {
|
|
4494
5099
|
const tag = match[0];
|
|
4495
|
-
const
|
|
4496
|
-
const
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
const
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
fullPath = fullPath.substring(1);
|
|
4503
|
-
} else if (!fullPath.startsWith("xl/")) {
|
|
4504
|
-
fullPath = "xl/" + fullPath;
|
|
4505
|
-
}
|
|
5100
|
+
const attrs = parseTagAttributes(tag);
|
|
5101
|
+
const rawName = attrs["name"];
|
|
5102
|
+
const rId = attrs["r:id"] ?? attrs["id"] ?? attrs["rId"];
|
|
5103
|
+
if (rawName === void 0 || rId === void 0) continue;
|
|
5104
|
+
const name = unescapeXml(rawName);
|
|
5105
|
+
const target = rIdToTarget[rId] ?? "";
|
|
5106
|
+
const fullPath = resolveRelTarget(target);
|
|
4506
5107
|
if (fullPath) {
|
|
4507
5108
|
this.sheetNameToPath.set(name, fullPath);
|
|
4508
5109
|
}
|
|
4509
5110
|
}
|
|
5111
|
+
const wbPrMatch = /<workbookPr\b[^>]*>/i.exec(wbXml);
|
|
5112
|
+
if (wbPrMatch) {
|
|
5113
|
+
const wbPrAttrs = parseTagAttributes(wbPrMatch[0]);
|
|
5114
|
+
const v = (wbPrAttrs["date1904"] ?? "").trim().toLowerCase();
|
|
5115
|
+
this._oaEpoch = v === "1" || v === "true" ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30);
|
|
5116
|
+
}
|
|
4510
5117
|
for (const entry of this.zip.getEntries()) {
|
|
4511
5118
|
const name = entry.entryName;
|
|
4512
5119
|
if (/^xl\/pivotCache\/pivotCacheDefinition\d*\.xml$/.test(name)) {
|
|
@@ -4701,7 +5308,7 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4701
5308
|
return `<c r="${colRef}${rowNum}" t="b"${colStyle > 0 ? ` s="${colStyle}"` : ""}><v>${val ? 1 : 0}</v></c>`;
|
|
4702
5309
|
}
|
|
4703
5310
|
if (val instanceof Date) {
|
|
4704
|
-
const oaDate = (val.getTime()
|
|
5311
|
+
const oaDate = timeToOaDate(val.getTime(), this._oaEpoch);
|
|
4705
5312
|
if (Number.isFinite(oaDate)) {
|
|
4706
5313
|
const style = colStyle > 0 ? colStyle : dateStyle !== null ? dateStyle : 0;
|
|
4707
5314
|
return `<c r="${colRef}${rowNum}"${style > 0 ? ` s="${style}"` : ""}><v>${oaDate}</v></c>`;
|
|
@@ -4918,8 +5525,8 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4918
5525
|
}
|
|
4919
5526
|
}
|
|
4920
5527
|
_addRefreshOnLoadToPivotTables() {
|
|
4921
|
-
for (const
|
|
4922
|
-
const entry = this.zip.getEntry(
|
|
5528
|
+
for (const path9 of this.pivotTableDefPaths) {
|
|
5529
|
+
const entry = this.zip.getEntry(path9);
|
|
4923
5530
|
if (!entry) continue;
|
|
4924
5531
|
let xml = entry.getData().toString("utf8");
|
|
4925
5532
|
const rootRe = /<pivotTableDefinition\b[^>]*>/;
|
|
@@ -4927,7 +5534,233 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4927
5534
|
if (!rootMatch || rootMatch[0].includes("refreshOnLoad")) continue;
|
|
4928
5535
|
const newTag = rootMatch[0].replace(/(\/?>)$/, ' refreshOnLoad="1"$1');
|
|
4929
5536
|
xml = xml.substring(0, rootMatch.index) + newTag + xml.substring(rootMatch.index + rootMatch[0].length);
|
|
4930
|
-
this.zip.updateFile(
|
|
5537
|
+
this.zip.updateFile(path9, Buffer.from(xml, "utf8"));
|
|
5538
|
+
}
|
|
5539
|
+
}
|
|
5540
|
+
/**
|
|
5541
|
+
* Remove the stale calculation chain (xl/calcChain.xml). Excel rebuilds it
|
|
5542
|
+
* on open; keeping stale <c> entries for the replaced sheet would force a
|
|
5543
|
+
* repair prompt. Removes the part, its Content_Types override and its
|
|
5544
|
+
* workbook rels entry (xlspy parity).
|
|
5545
|
+
*/
|
|
5546
|
+
_removeCalcChain() {
|
|
5547
|
+
const calcPath = "xl/calcChain.xml";
|
|
5548
|
+
if (!this.zip.getEntry(calcPath) && !this.stagedParts.has(calcPath)) {
|
|
5549
|
+
this._ensureCalcPrFullCalc();
|
|
5550
|
+
return;
|
|
5551
|
+
}
|
|
5552
|
+
try {
|
|
5553
|
+
this.zip.deleteFile(calcPath);
|
|
5554
|
+
} catch {
|
|
5555
|
+
}
|
|
5556
|
+
const stagedCalc = this.stagedParts.get(calcPath);
|
|
5557
|
+
if (stagedCalc) {
|
|
5558
|
+
this.stagedParts.delete(calcPath);
|
|
5559
|
+
try {
|
|
5560
|
+
fs5.rmSync(stagedCalc, { force: true });
|
|
5561
|
+
} catch {
|
|
5562
|
+
}
|
|
5563
|
+
}
|
|
5564
|
+
const ctEntry = this.zip.getEntry("[Content_Types].xml");
|
|
5565
|
+
if (ctEntry || this.stagedParts.has("[Content_Types].xml")) {
|
|
5566
|
+
try {
|
|
5567
|
+
let ct = this._entryData("[Content_Types].xml").toString("utf8");
|
|
5568
|
+
const before = ct;
|
|
5569
|
+
ct = ct.replace(/<Override\b[^>]*PartName="\/xl\/calcChain\.xml"[^>]*\/?>/gi, "");
|
|
5570
|
+
if (ct !== before) {
|
|
5571
|
+
this._writePartText("[Content_Types].xml", ct);
|
|
5572
|
+
}
|
|
5573
|
+
} catch {
|
|
5574
|
+
}
|
|
5575
|
+
}
|
|
5576
|
+
const relsPath = "xl/_rels/workbook.xml.rels";
|
|
5577
|
+
if (this.zip.getEntry(relsPath) || this.stagedParts.has(relsPath)) {
|
|
5578
|
+
try {
|
|
5579
|
+
const rels = this._entryData(relsPath).toString("utf8");
|
|
5580
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
5581
|
+
let m;
|
|
5582
|
+
let out = "";
|
|
5583
|
+
let last = 0;
|
|
5584
|
+
let changed = false;
|
|
5585
|
+
while ((m = relTagRe.exec(rels)) !== null) {
|
|
5586
|
+
const attrs = parseTagAttributes(m[0]);
|
|
5587
|
+
const type = attrs["Type"] ?? attrs["type"] ?? "";
|
|
5588
|
+
const target = attrs["Target"] ?? attrs["target"] ?? "";
|
|
5589
|
+
const isCalcChain = type.endsWith("/calcChain") && resolveRelTarget(target) === "xl/calcChain.xml";
|
|
5590
|
+
if (isCalcChain) {
|
|
5591
|
+
out += rels.substring(last, m.index);
|
|
5592
|
+
last = m.index + m[0].length;
|
|
5593
|
+
changed = true;
|
|
5594
|
+
}
|
|
5595
|
+
}
|
|
5596
|
+
if (changed) {
|
|
5597
|
+
out += rels.substring(last);
|
|
5598
|
+
this._writePartText(relsPath, out);
|
|
5599
|
+
}
|
|
5600
|
+
} catch {
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
this._ensureCalcPrFullCalc();
|
|
5604
|
+
}
|
|
5605
|
+
_writePartText(name, text) {
|
|
5606
|
+
if (this.stagedParts.has(name)) {
|
|
5607
|
+
fs5.writeFileSync(this.stagedParts.get(name), text, "utf8");
|
|
5608
|
+
} else {
|
|
5609
|
+
this.zip.updateFile(name, Buffer.from(text, "utf8"));
|
|
5610
|
+
}
|
|
5611
|
+
}
|
|
5612
|
+
_ensureCalcPrFullCalc() {
|
|
5613
|
+
try {
|
|
5614
|
+
const wbPath = "xl/workbook.xml";
|
|
5615
|
+
if (!this.zip.getEntry(wbPath) && !this.stagedParts.has(wbPath)) return;
|
|
5616
|
+
let xml = this._entryData(wbPath).toString("utf8");
|
|
5617
|
+
const calcPrRe = /<(?:[A-Za-z_][\w.-]*:)?calcPr\b[^>]*>/i;
|
|
5618
|
+
const match = calcPrRe.exec(xml);
|
|
5619
|
+
if (match && match.index !== void 0) {
|
|
5620
|
+
const newTag = _XlsxUpdater._replaceOrAddXmlAttribute(match[0], "fullCalcOnLoad", "1");
|
|
5621
|
+
xml = xml.substring(0, match.index) + newTag + xml.substring(match.index + match[0].length);
|
|
5622
|
+
} else {
|
|
5623
|
+
const insertAt = _XlsxUpdater._calcPrInsertPosition(xml);
|
|
5624
|
+
if (insertAt < 0) return;
|
|
5625
|
+
xml = xml.substring(0, insertAt) + '<calcPr fullCalcOnLoad="1"/>' + xml.substring(insertAt);
|
|
5626
|
+
}
|
|
5627
|
+
this._writePartText(wbPath, xml);
|
|
5628
|
+
} catch {
|
|
5629
|
+
}
|
|
5630
|
+
}
|
|
5631
|
+
/**
|
|
5632
|
+
* Insertion offset for a missing `<calcPr>`. The element follows
|
|
5633
|
+
* sheets/externalReferences/definedNames in the workbook schema, so insert
|
|
5634
|
+
* after the last of those that exists instead of right after `<workbook>`.
|
|
5635
|
+
*/
|
|
5636
|
+
static _calcPrInsertPosition(xml) {
|
|
5637
|
+
const prefixed = "(?:[A-Za-z_][\\w.-]*:)?";
|
|
5638
|
+
const candidates = [
|
|
5639
|
+
new RegExp(`</${prefixed}sheets\\s*>`, "i"),
|
|
5640
|
+
new RegExp(`<${prefixed}externalReferences\\b[^>]*/>`, "i"),
|
|
5641
|
+
new RegExp(`<${prefixed}externalReferences\\b[^>]*>[\\s\\S]*?</${prefixed}externalReferences\\s*>`, "i"),
|
|
5642
|
+
new RegExp(`<${prefixed}definedNames\\b[^>]*/>`, "i"),
|
|
5643
|
+
new RegExp(`<${prefixed}definedNames\\b[^>]*>[\\s\\S]*?</${prefixed}definedNames\\s*>`, "i")
|
|
5644
|
+
];
|
|
5645
|
+
let insertAt = -1;
|
|
5646
|
+
for (const re of candidates) {
|
|
5647
|
+
const m = re.exec(xml);
|
|
5648
|
+
if (m && m.index !== void 0) {
|
|
5649
|
+
insertAt = Math.max(insertAt, m.index + m[0].length);
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5652
|
+
if (insertAt >= 0) return insertAt;
|
|
5653
|
+
const close = new RegExp(`</${prefixed}workbook\\s*>`, "i").exec(xml);
|
|
5654
|
+
if (close && close.index !== void 0) return close.index;
|
|
5655
|
+
const root = new RegExp(`<${prefixed}workbook\\b[^>]*>`, "i").exec(xml);
|
|
5656
|
+
if (!root || root.index === void 0) return -1;
|
|
5657
|
+
return root.index + root[0].length;
|
|
5658
|
+
}
|
|
5659
|
+
/**
|
|
5660
|
+
* Update ListObject table ranges (xl/tables/*.xml) for the replaced sheet.
|
|
5661
|
+
* Only tables anchored at A1 are updated to the new dimension; tables
|
|
5662
|
+
* elsewhere are left untouched (moving them would corrupt the sheet).
|
|
5663
|
+
* Empty replacements (dimRef "A1") do not shrink multi-column tables.
|
|
5664
|
+
*/
|
|
5665
|
+
_updateTableRefs(sheetName, dimRef) {
|
|
5666
|
+
const sheetPath = this.sheetNameToPath.get(sheetName);
|
|
5667
|
+
if (!sheetPath) return;
|
|
5668
|
+
const slash = sheetPath.lastIndexOf("/");
|
|
5669
|
+
const dir = slash >= 0 ? sheetPath.substring(0, slash + 1) : "";
|
|
5670
|
+
const base = slash >= 0 ? sheetPath.substring(slash + 1) : sheetPath;
|
|
5671
|
+
const sheetRelsPath = `${dir}_rels/${base}.rels`;
|
|
5672
|
+
const sheetRelsEntry = this.zip.getEntry(sheetRelsPath);
|
|
5673
|
+
const hasStagedRels = this.stagedParts.has(sheetRelsPath);
|
|
5674
|
+
if (!sheetRelsEntry && !hasStagedRels) return;
|
|
5675
|
+
const tableTargets = [];
|
|
5676
|
+
try {
|
|
5677
|
+
const relsXml = this._entryData(sheetRelsPath).toString("utf8");
|
|
5678
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
5679
|
+
let m;
|
|
5680
|
+
while ((m = relTagRe.exec(relsXml)) !== null) {
|
|
5681
|
+
const attrs = parseTagAttributes(m[0]);
|
|
5682
|
+
const type = attrs["Type"] ?? attrs["type"] ?? "";
|
|
5683
|
+
const target = attrs["Target"] ?? attrs["target"] ?? "";
|
|
5684
|
+
if (!type.includes("/table") || !target) continue;
|
|
5685
|
+
let tablePath;
|
|
5686
|
+
if (target.startsWith("/")) {
|
|
5687
|
+
tablePath = resolveRelTarget(target);
|
|
5688
|
+
} else {
|
|
5689
|
+
const combined = dir + target;
|
|
5690
|
+
const parts = [];
|
|
5691
|
+
for (const seg of combined.split("/")) {
|
|
5692
|
+
if (seg === "..") parts.pop();
|
|
5693
|
+
else if (seg !== "." && seg !== "") parts.push(seg);
|
|
5694
|
+
}
|
|
5695
|
+
tablePath = parts.join("/");
|
|
5696
|
+
}
|
|
5697
|
+
tableTargets.push(tablePath);
|
|
5698
|
+
}
|
|
5699
|
+
} catch {
|
|
5700
|
+
return;
|
|
5701
|
+
}
|
|
5702
|
+
if (tableTargets.length === 0) return;
|
|
5703
|
+
const tables = [];
|
|
5704
|
+
for (const tablePath of tableTargets) {
|
|
5705
|
+
try {
|
|
5706
|
+
const hasEntry = !!this.zip.getEntry(tablePath);
|
|
5707
|
+
const hasStaged = this.stagedParts.has(tablePath);
|
|
5708
|
+
if (!hasEntry && !hasStaged) continue;
|
|
5709
|
+
const xml = this._entryData(tablePath).toString("utf8");
|
|
5710
|
+
const tableTagRe = /<(?:[A-Za-z_][\w.-]*:)?table\b[^>]*>/i;
|
|
5711
|
+
const tableMatch = tableTagRe.exec(xml);
|
|
5712
|
+
if (!tableMatch || tableMatch.index === void 0) continue;
|
|
5713
|
+
const refMatch = /\bref\s*=\s*("([^"]*)"|'([^']*)')/i.exec(tableMatch[0]);
|
|
5714
|
+
const ref = refMatch ? refMatch[2] ?? refMatch[3] ?? "" : "";
|
|
5715
|
+
tables.push({
|
|
5716
|
+
path: tablePath,
|
|
5717
|
+
xml,
|
|
5718
|
+
tag: tableMatch[0],
|
|
5719
|
+
tagIndex: tableMatch.index,
|
|
5720
|
+
ref,
|
|
5721
|
+
range: parseCellRange(ref),
|
|
5722
|
+
staged: hasStaged
|
|
5723
|
+
});
|
|
5724
|
+
} catch {
|
|
5725
|
+
}
|
|
5726
|
+
}
|
|
5727
|
+
if (tables.length === 0) return;
|
|
5728
|
+
const dimRange = parseCellRange(dimRef);
|
|
5729
|
+
if (!dimRange) return;
|
|
5730
|
+
for (const table of tables) {
|
|
5731
|
+
try {
|
|
5732
|
+
const origRange = table.range;
|
|
5733
|
+
if (!origRange) continue;
|
|
5734
|
+
if (origRange.r1 !== 1 || origRange.c1 !== 0) continue;
|
|
5735
|
+
if (dimRef === "A1" && !/^A1$/i.test(table.ref)) continue;
|
|
5736
|
+
const origCols = origRange.c2 - origRange.c1 + 1;
|
|
5737
|
+
const newCols = dimRange.c2 - dimRange.c1 + 1;
|
|
5738
|
+
if (newCols !== origCols) continue;
|
|
5739
|
+
let lastRow = dimRange.r2;
|
|
5740
|
+
for (const other of tables) {
|
|
5741
|
+
if (other === table || !other.range) continue;
|
|
5742
|
+
if (other.range.r1 <= 1) continue;
|
|
5743
|
+
if (other.range.c1 > origRange.c2 || other.range.c2 < origRange.c1) continue;
|
|
5744
|
+
lastRow = Math.min(lastRow, other.range.r1 - 1);
|
|
5745
|
+
}
|
|
5746
|
+
if (lastRow < 1) continue;
|
|
5747
|
+
const newRef = origRange.c2 === 0 && lastRow === 1 ? "A1" : `A1:${columnIndexToLetter(origRange.c2)}${lastRow}`;
|
|
5748
|
+
let xml = table.xml;
|
|
5749
|
+
const newTag = _XlsxUpdater._replaceOrAddXmlAttribute(table.tag, "ref", newRef);
|
|
5750
|
+
xml = xml.substring(0, table.tagIndex) + newTag + xml.substring(table.tagIndex + table.tag.length);
|
|
5751
|
+
const afRe = /<(?:[A-Za-z_][\w.-]*:)?autoFilter\b[^>]*>/i;
|
|
5752
|
+
const afMatch = afRe.exec(xml);
|
|
5753
|
+
if (afMatch && afMatch.index !== void 0) {
|
|
5754
|
+
const newAf = _XlsxUpdater._replaceOrAddXmlAttribute(afMatch[0], "ref", newRef);
|
|
5755
|
+
xml = xml.substring(0, afMatch.index) + newAf + xml.substring(afMatch.index + afMatch[0].length);
|
|
5756
|
+
}
|
|
5757
|
+
if (table.staged) {
|
|
5758
|
+
fs5.writeFileSync(this.stagedParts.get(table.path), xml, "utf8");
|
|
5759
|
+
} else {
|
|
5760
|
+
this.zip.updateFile(table.path, Buffer.from(xml, "utf8"));
|
|
5761
|
+
}
|
|
5762
|
+
} catch {
|
|
5763
|
+
}
|
|
4931
5764
|
}
|
|
4932
5765
|
}
|
|
4933
5766
|
};
|
|
@@ -4935,78 +5768,8 @@ var XlsxUpdater = class _XlsxUpdater {
|
|
|
4935
5768
|
// src/XlsbUpdater.ts
|
|
4936
5769
|
import * as fs6 from "fs";
|
|
4937
5770
|
import * as os2 from "os";
|
|
4938
|
-
import * as
|
|
5771
|
+
import * as path7 from "path";
|
|
4939
5772
|
import AdmZip3 from "adm-zip";
|
|
4940
|
-
|
|
4941
|
-
// src/biff12Utils.ts
|
|
4942
|
-
function readVlq(buf, pos) {
|
|
4943
|
-
let value = 0;
|
|
4944
|
-
let shift = 0;
|
|
4945
|
-
let byte;
|
|
4946
|
-
do {
|
|
4947
|
-
byte = buf[pos++];
|
|
4948
|
-
value |= (byte & 127) << shift;
|
|
4949
|
-
shift += 7;
|
|
4950
|
-
} while ((byte & 128) !== 0);
|
|
4951
|
-
return { value, nextPos: pos };
|
|
4952
|
-
}
|
|
4953
|
-
function vlqBytes(value) {
|
|
4954
|
-
const out = [];
|
|
4955
|
-
while (value >= 128) {
|
|
4956
|
-
out.push(value & 127 | 128);
|
|
4957
|
-
value >>>= 7;
|
|
4958
|
-
}
|
|
4959
|
-
out.push(value & 127);
|
|
4960
|
-
return out;
|
|
4961
|
-
}
|
|
4962
|
-
function readRecord(buf, pos, rec) {
|
|
4963
|
-
const idRes = readVlq(buf, pos);
|
|
4964
|
-
if (idRes.nextPos > buf.length) return false;
|
|
4965
|
-
const lenRes = readVlq(buf, idRes.nextPos);
|
|
4966
|
-
if (lenRes.nextPos + lenRes.value > buf.length) return false;
|
|
4967
|
-
rec.headerStart = pos;
|
|
4968
|
-
rec.dataStart = lenRes.nextPos;
|
|
4969
|
-
rec.dataEnd = lenRes.nextPos + lenRes.value;
|
|
4970
|
-
rec.id = idRes.value;
|
|
4971
|
-
rec.len = lenRes.value;
|
|
4972
|
-
return true;
|
|
4973
|
-
}
|
|
4974
|
-
function buildRecord(id, payload) {
|
|
4975
|
-
const idBytes = vlqBytes(id);
|
|
4976
|
-
const lenBytes = vlqBytes(payload.length);
|
|
4977
|
-
const header = Buffer.alloc(idBytes.length + lenBytes.length);
|
|
4978
|
-
idBytes.forEach((b, i) => header[i] = b);
|
|
4979
|
-
lenBytes.forEach((b, i) => header[idBytes.length + i] = b);
|
|
4980
|
-
return Buffer.concat([header, payload]);
|
|
4981
|
-
}
|
|
4982
|
-
function readUtf16(buf, start, charCount) {
|
|
4983
|
-
const end = start + charCount * 2;
|
|
4984
|
-
if (end > buf.length) return "";
|
|
4985
|
-
return buf.toString("utf16le", start, end);
|
|
4986
|
-
}
|
|
4987
|
-
function parseSharedStringsBin(buf) {
|
|
4988
|
-
const values = [];
|
|
4989
|
-
let total = 0;
|
|
4990
|
-
let unique = 0;
|
|
4991
|
-
let endSstOffset = buf.length;
|
|
4992
|
-
const rec = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
4993
|
-
let pos = 0;
|
|
4994
|
-
while (readRecord(buf, pos, rec)) {
|
|
4995
|
-
pos = rec.dataEnd;
|
|
4996
|
-
if (rec.id === 159) {
|
|
4997
|
-
total = buf.readUInt32LE(rec.dataStart);
|
|
4998
|
-
unique = buf.readUInt32LE(rec.dataStart + 4);
|
|
4999
|
-
} else if (rec.id === 19) {
|
|
5000
|
-
const cch = buf.readUInt32LE(rec.dataStart + 1);
|
|
5001
|
-
values.push(readUtf16(buf, rec.dataStart + 5, cch));
|
|
5002
|
-
} else if (rec.id === 160) {
|
|
5003
|
-
endSstOffset = rec.headerStart;
|
|
5004
|
-
}
|
|
5005
|
-
}
|
|
5006
|
-
return { values, total, unique, endSstOffset };
|
|
5007
|
-
}
|
|
5008
|
-
|
|
5009
|
-
// src/XlsbUpdater.ts
|
|
5010
5773
|
var CELL_RECORDS = /* @__PURE__ */ new Set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
|
5011
5774
|
var RK_INT_LOWER = -1 << 29;
|
|
5012
5775
|
var RK_INT_UPPER = (1 << 29) - 1;
|
|
@@ -5110,6 +5873,7 @@ var XlsbUpdater = class {
|
|
|
5110
5873
|
const rwLast = Math.max(0, rows.length + (headers ? 1 : 0) - 1);
|
|
5111
5874
|
const colLast = lastCol;
|
|
5112
5875
|
this._patchPivotCaches(sheetName, rwLast, colLast);
|
|
5876
|
+
this._removeCalcChain();
|
|
5113
5877
|
}
|
|
5114
5878
|
/** Replace worksheet rows from a one-pass source. */
|
|
5115
5879
|
async replaceSheetDataStream(sheetName, rows, options = {}) {
|
|
@@ -5177,10 +5941,10 @@ var XlsbUpdater = class {
|
|
|
5177
5941
|
if (outputError) throw outputError;
|
|
5178
5942
|
writtenBytes += data.length;
|
|
5179
5943
|
if (rowOutput.write(data)) return;
|
|
5180
|
-
await new Promise((
|
|
5944
|
+
await new Promise((resolve6, reject) => {
|
|
5181
5945
|
const onDrain = () => {
|
|
5182
5946
|
cleanup();
|
|
5183
|
-
|
|
5947
|
+
resolve6();
|
|
5184
5948
|
};
|
|
5185
5949
|
const onError = (error) => {
|
|
5186
5950
|
cleanup();
|
|
@@ -5231,8 +5995,8 @@ var XlsbUpdater = class {
|
|
|
5231
5995
|
lastKeptEnd = writtenBytes;
|
|
5232
5996
|
}
|
|
5233
5997
|
}
|
|
5234
|
-
await new Promise((
|
|
5235
|
-
rowOutput.once("close",
|
|
5998
|
+
await new Promise((resolve6, reject) => {
|
|
5999
|
+
rowOutput.once("close", resolve6);
|
|
5236
6000
|
rowOutput.once("error", reject);
|
|
5237
6001
|
rowOutput.end();
|
|
5238
6002
|
});
|
|
@@ -5295,6 +6059,7 @@ var XlsbUpdater = class {
|
|
|
5295
6059
|
Math.max(0, keptDataRows + (headers !== void 0 ? 1 : 0) - 1),
|
|
5296
6060
|
lastCol
|
|
5297
6061
|
);
|
|
6062
|
+
this._removeCalcChain();
|
|
5298
6063
|
if (oldStage && oldStage !== outputPath) this._removeTemporaryFile(oldStage);
|
|
5299
6064
|
createdParts.splice(createdParts.indexOf(outputPath), 1);
|
|
5300
6065
|
} catch (error) {
|
|
@@ -5365,7 +6130,7 @@ var XlsbUpdater = class {
|
|
|
5365
6130
|
}
|
|
5366
6131
|
_ensureTemporaryDirectory() {
|
|
5367
6132
|
if (this.temporaryDirectory === null) {
|
|
5368
|
-
this.temporaryDirectory = fs6.mkdtempSync(
|
|
6133
|
+
this.temporaryDirectory = fs6.mkdtempSync(path7.join(os2.tmpdir(), "spreadsheet-updater-"));
|
|
5369
6134
|
} else {
|
|
5370
6135
|
try {
|
|
5371
6136
|
fs6.mkdirSync(this.temporaryDirectory, { recursive: true });
|
|
@@ -5375,7 +6140,7 @@ var XlsbUpdater = class {
|
|
|
5375
6140
|
return this.temporaryDirectory;
|
|
5376
6141
|
}
|
|
5377
6142
|
_temporaryPath(suffix) {
|
|
5378
|
-
return
|
|
6143
|
+
return path7.join(this._ensureTemporaryDirectory(), `part-${Date.now()}-${Math.random().toString(16).slice(2)}${suffix}`);
|
|
5379
6144
|
}
|
|
5380
6145
|
_removeTemporaryFile(filePath) {
|
|
5381
6146
|
try {
|
|
@@ -5415,10 +6180,15 @@ var XlsbUpdater = class {
|
|
|
5415
6180
|
const rIdToTarget = {};
|
|
5416
6181
|
if (relsEntry) {
|
|
5417
6182
|
const rels = relsEntry.getData().toString("utf8");
|
|
5418
|
-
const
|
|
5419
|
-
let
|
|
5420
|
-
while ((
|
|
5421
|
-
|
|
6183
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
6184
|
+
let tagMatch;
|
|
6185
|
+
while ((tagMatch = relTagRe.exec(rels)) !== null) {
|
|
6186
|
+
const attrs = parseTagAttributes(tagMatch[0]);
|
|
6187
|
+
const id = attrs["Id"] ?? attrs["id"];
|
|
6188
|
+
const target = attrs["Target"] ?? attrs["target"];
|
|
6189
|
+
if (id !== void 0 && target !== void 0) {
|
|
6190
|
+
rIdToTarget[id] = target;
|
|
6191
|
+
}
|
|
5422
6192
|
}
|
|
5423
6193
|
}
|
|
5424
6194
|
const wbEntry = this.zip.getEntry("xl/workbook.bin");
|
|
@@ -5438,12 +6208,24 @@ var XlsbUpdater = class {
|
|
|
5438
6208
|
o += 4;
|
|
5439
6209
|
const name = readUtf16(wbBuf, o, nameLen);
|
|
5440
6210
|
let target = rIdToTarget[rId] ?? "";
|
|
5441
|
-
|
|
5442
|
-
else if (!target.startsWith("xl/")) target = "xl/" + target;
|
|
6211
|
+
target = resolveRelTarget(target);
|
|
5443
6212
|
if (target) {
|
|
5444
6213
|
this.sheetNameToPath.set(name, target);
|
|
5445
6214
|
}
|
|
5446
6215
|
}
|
|
6216
|
+
try {
|
|
6217
|
+
const rec1904 = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
6218
|
+
let p1904 = 0;
|
|
6219
|
+
while (readRecord(wbBuf, p1904, rec1904)) {
|
|
6220
|
+
if (rec1904.id === 153 && rec1904.len >= 4) {
|
|
6221
|
+
const is1904 = (wbBuf[rec1904.dataStart] & 1) !== 0;
|
|
6222
|
+
this._oaEpoch = is1904 ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30);
|
|
6223
|
+
break;
|
|
6224
|
+
}
|
|
6225
|
+
p1904 = rec1904.dataEnd;
|
|
6226
|
+
}
|
|
6227
|
+
} catch {
|
|
6228
|
+
}
|
|
5447
6229
|
}
|
|
5448
6230
|
for (const e of this.zip.getEntries()) {
|
|
5449
6231
|
if (/^xl\/pivotCache\/pivotCacheDefinition\d*\.bin$/.test(e.entryName)) {
|
|
@@ -5552,27 +6334,15 @@ var XlsbUpdater = class {
|
|
|
5552
6334
|
_findDateXf() {
|
|
5553
6335
|
const entry = this.zip.getEntry("xl/styles.bin");
|
|
5554
6336
|
if (!entry) return null;
|
|
5555
|
-
const
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
if (
|
|
5563
|
-
|
|
5564
|
-
xfIndex = 0;
|
|
5565
|
-
continue;
|
|
5566
|
-
}
|
|
5567
|
-
if (rec.id === 618) {
|
|
5568
|
-
inCellXfs = false;
|
|
5569
|
-
continue;
|
|
5570
|
-
}
|
|
5571
|
-
if (inCellXfs && rec.id === 47 && rec.len >= 4) {
|
|
5572
|
-
const numFmtId = stylesBuf.readUInt16LE(rec.dataStart + 2);
|
|
5573
|
-
const isDate = numFmtId >= 14 && numFmtId <= 22 || numFmtId >= 45 && numFmtId <= 47;
|
|
5574
|
-
if (isDate) return xfIndex;
|
|
5575
|
-
xfIndex++;
|
|
6337
|
+
const reader = new BiffReaderWriter(entry.getData());
|
|
6338
|
+
while (reader.readStyles()) {
|
|
6339
|
+
}
|
|
6340
|
+
const numFmtIds = reader._xfIndexToNumFmtId;
|
|
6341
|
+
const customDates = reader._customNumFmts;
|
|
6342
|
+
for (let i = 0; i < numFmtIds.length; i++) {
|
|
6343
|
+
const numFmtId = numFmtIds[i];
|
|
6344
|
+
if (numFmtId >= 14 && numFmtId <= 22 || numFmtId >= 45 && numFmtId <= 47 || customDates.has(numFmtId)) {
|
|
6345
|
+
return i;
|
|
5576
6346
|
}
|
|
5577
6347
|
}
|
|
5578
6348
|
return null;
|
|
@@ -5652,7 +6422,7 @@ var XlsbUpdater = class {
|
|
|
5652
6422
|
return buildRecord(4, payload);
|
|
5653
6423
|
}
|
|
5654
6424
|
if (val instanceof Date) {
|
|
5655
|
-
const oaDate = (val.getTime()
|
|
6425
|
+
const oaDate = timeToOaDate(val.getTime(), this._oaEpoch);
|
|
5656
6426
|
if (Number.isFinite(oaDate)) {
|
|
5657
6427
|
const style = colStyle > 0 ? colStyle : dateXf !== null ? dateXf : 0;
|
|
5658
6428
|
const payload = Buffer.alloc(16);
|
|
@@ -5719,10 +6489,10 @@ var XlsbUpdater = class {
|
|
|
5719
6489
|
const rec = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
5720
6490
|
let pos = 0;
|
|
5721
6491
|
while (readRecord(originalSheet, pos, rec)) {
|
|
5722
|
-
if (rec.id ===
|
|
6492
|
+
if (rec.id === 148 && rec.len >= 16) {
|
|
5723
6493
|
const shift = rec.dataStart >= rowsEnd ? delta : 0;
|
|
5724
|
-
patchInt32(rec.dataStart + shift +
|
|
5725
|
-
patchInt32(rec.dataStart + shift +
|
|
6494
|
+
patchInt32(rec.dataStart + shift + 4, lastRowIdx);
|
|
6495
|
+
patchInt32(rec.dataStart + shift + 12, lastCol);
|
|
5726
6496
|
break;
|
|
5727
6497
|
}
|
|
5728
6498
|
pos = rec.dataEnd;
|
|
@@ -5761,8 +6531,8 @@ var XlsbUpdater = class {
|
|
|
5761
6531
|
return { rowsStart, rowsEnd, autoFilterRanges };
|
|
5762
6532
|
}
|
|
5763
6533
|
/**
|
|
5764
|
-
* Keep the sheet dimension record (
|
|
5765
|
-
* Excel uses its rwLast (payload offset
|
|
6534
|
+
* Keep the sheet dimension record (0x94) in sync with the new data.
|
|
6535
|
+
* Excel uses its rwLast (payload offset 4) and colLast (payload offset 12)
|
|
5766
6536
|
* fields to read the full row range; stale values make the last written row
|
|
5767
6537
|
* invisible to Excel (and drop it from pivot refreshes).
|
|
5768
6538
|
*/
|
|
@@ -5770,9 +6540,9 @@ var XlsbUpdater = class {
|
|
|
5770
6540
|
const rec = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
5771
6541
|
let pos = 0;
|
|
5772
6542
|
while (readRecord(sheetBuf, pos, rec)) {
|
|
5773
|
-
if (rec.id ===
|
|
5774
|
-
sheetBuf.writeInt32LE(lastRowIdx, rec.dataStart +
|
|
5775
|
-
sheetBuf.writeInt32LE(lastCol, rec.dataStart +
|
|
6543
|
+
if (rec.id === 148 && rec.len >= 16) {
|
|
6544
|
+
sheetBuf.writeInt32LE(lastRowIdx, rec.dataStart + 4);
|
|
6545
|
+
sheetBuf.writeInt32LE(lastCol, rec.dataStart + 12);
|
|
5776
6546
|
return;
|
|
5777
6547
|
}
|
|
5778
6548
|
pos = rec.dataEnd;
|
|
@@ -5782,8 +6552,8 @@ var XlsbUpdater = class {
|
|
|
5782
6552
|
// Pivot cache integration
|
|
5783
6553
|
// ------------------------------------------------------------------
|
|
5784
6554
|
_patchPivotCaches(sheetName, rwLast, colLast) {
|
|
5785
|
-
for (const
|
|
5786
|
-
const entry = this.zip.getEntry(
|
|
6555
|
+
for (const path9 of this.pivotCacheDefPaths) {
|
|
6556
|
+
const entry = this.zip.getEntry(path9);
|
|
5787
6557
|
if (!entry) continue;
|
|
5788
6558
|
const buf = Buffer.from(entry.getData());
|
|
5789
6559
|
const rec = { headerStart: 0, dataStart: 0, dataEnd: 0, id: 0, len: 0 };
|
|
@@ -5815,16 +6585,84 @@ var XlsbUpdater = class {
|
|
|
5815
6585
|
}
|
|
5816
6586
|
}
|
|
5817
6587
|
if (patched) {
|
|
5818
|
-
this.zip.updateFile(
|
|
6588
|
+
this.zip.updateFile(path9, buf);
|
|
6589
|
+
}
|
|
6590
|
+
}
|
|
6591
|
+
}
|
|
6592
|
+
/**
|
|
6593
|
+
* Remove the stale calculation chain (xl/calcChain.bin). Excel rebuilds it
|
|
6594
|
+
* on open. Removes the part, its Content_Types override and its workbook
|
|
6595
|
+
* rels entry. XLSB tables are not patched (documented limitation).
|
|
6596
|
+
*/
|
|
6597
|
+
_removeCalcChain() {
|
|
6598
|
+
const calcPath = "xl/calcChain.bin";
|
|
6599
|
+
if (!this.zip.getEntry(calcPath) && !this.stagedParts.has(calcPath)) return;
|
|
6600
|
+
try {
|
|
6601
|
+
this.zip.deleteFile(calcPath);
|
|
6602
|
+
} catch {
|
|
6603
|
+
}
|
|
6604
|
+
const stagedCalc = this.stagedParts.get(calcPath);
|
|
6605
|
+
if (stagedCalc) {
|
|
6606
|
+
this.stagedParts.delete(calcPath);
|
|
6607
|
+
try {
|
|
6608
|
+
fs6.rmSync(stagedCalc, { force: true });
|
|
6609
|
+
} catch {
|
|
6610
|
+
}
|
|
6611
|
+
}
|
|
6612
|
+
const ctPath = "[Content_Types].xml";
|
|
6613
|
+
if (this.zip.getEntry(ctPath) || this.stagedParts.has(ctPath)) {
|
|
6614
|
+
try {
|
|
6615
|
+
const ct = this._entryData(ctPath).toString("utf8");
|
|
6616
|
+
const updated = ct.replace(
|
|
6617
|
+
/<Override\b[^>]*PartName="\/xl\/calcChain\.bin"[^>]*\/?>/gi,
|
|
6618
|
+
""
|
|
6619
|
+
);
|
|
6620
|
+
if (updated !== ct) this._writePartText(ctPath, updated);
|
|
6621
|
+
} catch {
|
|
6622
|
+
}
|
|
6623
|
+
}
|
|
6624
|
+
const relsPath = "xl/_rels/workbook.bin.rels";
|
|
6625
|
+
if (this.zip.getEntry(relsPath) || this.stagedParts.has(relsPath)) {
|
|
6626
|
+
try {
|
|
6627
|
+
const rels = this._entryData(relsPath).toString("utf8");
|
|
6628
|
+
const relTagRe = /<Relationship\b[^>]*>/g;
|
|
6629
|
+
let m;
|
|
6630
|
+
let out = "";
|
|
6631
|
+
let last = 0;
|
|
6632
|
+
let changed = false;
|
|
6633
|
+
while ((m = relTagRe.exec(rels)) !== null) {
|
|
6634
|
+
const attrs = parseTagAttributes(m[0]);
|
|
6635
|
+
const type = attrs["Type"] ?? attrs["type"] ?? "";
|
|
6636
|
+
const target = attrs["Target"] ?? attrs["target"] ?? "";
|
|
6637
|
+
if (type.endsWith("/calcChain") && resolveRelTarget(target) === "xl/calcChain.bin") {
|
|
6638
|
+
out += rels.substring(last, m.index);
|
|
6639
|
+
last = m.index + m[0].length;
|
|
6640
|
+
changed = true;
|
|
6641
|
+
}
|
|
6642
|
+
}
|
|
6643
|
+
if (changed) {
|
|
6644
|
+
out += rels.substring(last);
|
|
6645
|
+
this._writePartText(relsPath, out);
|
|
6646
|
+
}
|
|
6647
|
+
} catch {
|
|
5819
6648
|
}
|
|
5820
6649
|
}
|
|
5821
6650
|
}
|
|
6651
|
+
/** Write an XML/text part to its staged file when present, else into the ZIP. */
|
|
6652
|
+
_writePartText(name, text) {
|
|
6653
|
+
const stagedPath = this.stagedParts.get(name);
|
|
6654
|
+
if (stagedPath) {
|
|
6655
|
+
fs6.writeFileSync(stagedPath, text, "utf8");
|
|
6656
|
+
} else {
|
|
6657
|
+
this.zip.updateFile(name, Buffer.from(text, "utf8"));
|
|
6658
|
+
}
|
|
6659
|
+
}
|
|
5822
6660
|
};
|
|
5823
6661
|
|
|
5824
6662
|
// src/ExcelConverter.ts
|
|
5825
6663
|
import * as fs7 from "fs";
|
|
5826
|
-
import * as
|
|
5827
|
-
import { randomUUID as
|
|
6664
|
+
import * as path8 from "path";
|
|
6665
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
5828
6666
|
import { spawn } from "child_process";
|
|
5829
6667
|
var POWERSHELL_CONVERSION_SCRIPT = `
|
|
5830
6668
|
$ErrorActionPreference = 'Stop'
|
|
@@ -5870,14 +6708,14 @@ function errorMessage2(error) {
|
|
|
5870
6708
|
return error instanceof Error ? error.message : String(error);
|
|
5871
6709
|
}
|
|
5872
6710
|
function validateConversionPaths(inputPath, outputPath, inputExtension, outputExtension, overwrite) {
|
|
5873
|
-
const input =
|
|
5874
|
-
const output =
|
|
5875
|
-
if (
|
|
6711
|
+
const input = path8.resolve(inputPath);
|
|
6712
|
+
const output = path8.resolve(outputPath);
|
|
6713
|
+
if (path8.extname(input).toLowerCase() !== inputExtension) {
|
|
5876
6714
|
throw new Error(
|
|
5877
6715
|
`Excel conversion: source must have the ${inputExtension} extension: ${inputPath}`
|
|
5878
6716
|
);
|
|
5879
6717
|
}
|
|
5880
|
-
if (
|
|
6718
|
+
if (path8.extname(output).toLowerCase() !== outputExtension) {
|
|
5881
6719
|
throw new Error(
|
|
5882
6720
|
`Excel conversion: destination must have the ${outputExtension} extension: ${outputPath}`
|
|
5883
6721
|
);
|
|
@@ -5888,8 +6726,8 @@ function validateConversionPaths(inputPath, outputPath, inputExtension, outputEx
|
|
|
5888
6726
|
if (!fs7.statSync(input).isFile()) {
|
|
5889
6727
|
throw new Error(`Excel conversion: source path is not a file: ${inputPath}`);
|
|
5890
6728
|
}
|
|
5891
|
-
if (!fs7.existsSync(
|
|
5892
|
-
throw new Error(`Excel conversion: destination directory not found: ${
|
|
6729
|
+
if (!fs7.existsSync(path8.dirname(output))) {
|
|
6730
|
+
throw new Error(`Excel conversion: destination directory not found: ${path8.dirname(output)}`);
|
|
5893
6731
|
}
|
|
5894
6732
|
if (fs7.existsSync(output)) {
|
|
5895
6733
|
if (fs7.statSync(output).isDirectory()) {
|
|
@@ -5914,7 +6752,7 @@ function runPowerShellConversion(inputPath, outputPath, fileFormat) {
|
|
|
5914
6752
|
JUSTYBASE_EXCEL_OUTPUT: outputPath,
|
|
5915
6753
|
JUSTYBASE_EXCEL_FORMAT: String(fileFormat)
|
|
5916
6754
|
};
|
|
5917
|
-
return new Promise((
|
|
6755
|
+
return new Promise((resolve6, reject) => {
|
|
5918
6756
|
var _a, _b, _c, _d;
|
|
5919
6757
|
let stdout = "";
|
|
5920
6758
|
let stderr = "";
|
|
@@ -5949,7 +6787,7 @@ function runPowerShellConversion(inputPath, outputPath, fileFormat) {
|
|
|
5949
6787
|
if (settled) return;
|
|
5950
6788
|
settled = true;
|
|
5951
6789
|
if (code === 0) {
|
|
5952
|
-
|
|
6790
|
+
resolve6();
|
|
5953
6791
|
return;
|
|
5954
6792
|
}
|
|
5955
6793
|
const details = (stderr || stdout).replace(/\s+/g, " ").trim();
|
|
@@ -5970,9 +6808,9 @@ async function convertExcelFile(inputPath, outputPath, inputExtension, outputExt
|
|
|
5970
6808
|
if (process.platform !== "win32") {
|
|
5971
6809
|
throw new Error("Excel conversion requires Windows with desktop Microsoft Excel and COM automation.");
|
|
5972
6810
|
}
|
|
5973
|
-
const temporaryOutput =
|
|
5974
|
-
|
|
5975
|
-
`.${
|
|
6811
|
+
const temporaryOutput = path8.join(
|
|
6812
|
+
path8.dirname(output),
|
|
6813
|
+
`.${path8.basename(output, outputExtension)}.${randomUUID5()}.conversion${outputExtension}`
|
|
5976
6814
|
);
|
|
5977
6815
|
try {
|
|
5978
6816
|
await runPowerShellConversion(input, temporaryOutput, fileFormat);
|
|
@@ -6003,6 +6841,9 @@ export {
|
|
|
6003
6841
|
ExcelReaderAbstract,
|
|
6004
6842
|
F,
|
|
6005
6843
|
INVALID_SHEET_NAME_CHARS,
|
|
6844
|
+
MAX_CELL_CHARS,
|
|
6845
|
+
MAX_COLS,
|
|
6846
|
+
MAX_ROWS,
|
|
6006
6847
|
ReaderFactory,
|
|
6007
6848
|
StreamingSheetState,
|
|
6008
6849
|
XlsbReader,
|
|
@@ -6013,13 +6854,25 @@ export {
|
|
|
6013
6854
|
XlsxUpdater,
|
|
6014
6855
|
XlsxWriter,
|
|
6015
6856
|
applyHeaderWidths,
|
|
6857
|
+
assertCellTextLength,
|
|
6858
|
+
assertColumnCount,
|
|
6859
|
+
assertRowCount,
|
|
6860
|
+
assertRowFitsColumns,
|
|
6861
|
+
columnIndexToLetter,
|
|
6862
|
+
columnLetterToIndex,
|
|
6016
6863
|
convertXlsbToXlsx,
|
|
6017
6864
|
convertXlsxToXlsb,
|
|
6018
6865
|
defaultColWidth,
|
|
6866
|
+
escapeXmlText,
|
|
6019
6867
|
getFormat,
|
|
6020
6868
|
initColWidths,
|
|
6021
6869
|
isFormattedCell,
|
|
6870
|
+
parseSharedStringsXml,
|
|
6871
|
+
parseTagAttributes,
|
|
6872
|
+
resolveRelTarget,
|
|
6022
6873
|
sanitizeSheetName,
|
|
6874
|
+
trimTrailingEmptyRows,
|
|
6875
|
+
unescapeXml,
|
|
6023
6876
|
unwrapCell,
|
|
6024
6877
|
updateColWidthsFromRows
|
|
6025
6878
|
};
|