@devmm/puredocs-excel 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +909 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -2
- package/dist/index.d.ts +84 -2
- package/dist/index.js +910 -121
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -196,6 +196,124 @@ function listEntries(entries, prefix) {
|
|
|
196
196
|
}
|
|
197
197
|
return results.sort();
|
|
198
198
|
}
|
|
199
|
+
|
|
200
|
+
// src/io/zip-container.ts
|
|
201
|
+
var LOCAL_HEADER = 67324752;
|
|
202
|
+
var CENTRAL_HEADER = 33639248;
|
|
203
|
+
var END_OF_CENTRAL_DIR = 101010256;
|
|
204
|
+
var DOS_DATE = 33;
|
|
205
|
+
var DOS_TIME = 0;
|
|
206
|
+
var MAX_ENTRIES = 65535;
|
|
207
|
+
var MAX_BYTES = 4294967295;
|
|
208
|
+
var CRC_TABLE = (() => {
|
|
209
|
+
const table = new Int32Array(256);
|
|
210
|
+
for (let i = 0; i < 256; i++) {
|
|
211
|
+
let c = i;
|
|
212
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
213
|
+
table[i] = c;
|
|
214
|
+
}
|
|
215
|
+
return table;
|
|
216
|
+
})();
|
|
217
|
+
function crc32(data) {
|
|
218
|
+
let c = -1;
|
|
219
|
+
for (let i = 0; i < data.length; i++) {
|
|
220
|
+
c = c >>> 8 ^ CRC_TABLE[(c ^ data[i]) & 255];
|
|
221
|
+
}
|
|
222
|
+
return (c ^ -1) >>> 0;
|
|
223
|
+
}
|
|
224
|
+
function exceedsZipLimits(parts) {
|
|
225
|
+
if (parts.length > MAX_ENTRIES) return true;
|
|
226
|
+
let total = 0;
|
|
227
|
+
for (const p of parts) {
|
|
228
|
+
total += 30 + utf8Length(p.name) * 2 + 46 + p.deflated.length;
|
|
229
|
+
if (total > MAX_BYTES || p.rawSize > MAX_BYTES) return true;
|
|
230
|
+
}
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
function buildZipContainer(parts) {
|
|
234
|
+
const names = parts.map((p) => encodeName(p.name));
|
|
235
|
+
let localSize = 0;
|
|
236
|
+
let centralSize = 0;
|
|
237
|
+
for (let i = 0; i < parts.length; i++) {
|
|
238
|
+
localSize += 30 + names[i].bytes.length + parts[i].deflated.length;
|
|
239
|
+
centralSize += 46 + names[i].bytes.length;
|
|
240
|
+
}
|
|
241
|
+
const out = new Uint8Array(localSize + centralSize + 22);
|
|
242
|
+
const view = new DataView(out.buffer);
|
|
243
|
+
const offsets = [];
|
|
244
|
+
let at = 0;
|
|
245
|
+
for (let i = 0; i < parts.length; i++) {
|
|
246
|
+
const part = parts[i];
|
|
247
|
+
const name = names[i];
|
|
248
|
+
offsets.push(at);
|
|
249
|
+
view.setUint32(at, LOCAL_HEADER, true);
|
|
250
|
+
view.setUint16(at + 4, 20, true);
|
|
251
|
+
view.setUint16(at + 6, name.utf8Flag, true);
|
|
252
|
+
view.setUint16(at + 8, 8, true);
|
|
253
|
+
view.setUint16(at + 10, DOS_TIME, true);
|
|
254
|
+
view.setUint16(at + 12, DOS_DATE, true);
|
|
255
|
+
view.setUint32(at + 14, part.crc, true);
|
|
256
|
+
view.setUint32(at + 18, part.deflated.length, true);
|
|
257
|
+
view.setUint32(at + 22, part.rawSize, true);
|
|
258
|
+
view.setUint16(at + 26, name.bytes.length, true);
|
|
259
|
+
view.setUint16(at + 28, 0, true);
|
|
260
|
+
at += 30;
|
|
261
|
+
out.set(name.bytes, at);
|
|
262
|
+
at += name.bytes.length;
|
|
263
|
+
out.set(part.deflated, at);
|
|
264
|
+
at += part.deflated.length;
|
|
265
|
+
}
|
|
266
|
+
const centralStart = at;
|
|
267
|
+
for (let i = 0; i < parts.length; i++) {
|
|
268
|
+
const part = parts[i];
|
|
269
|
+
const name = names[i];
|
|
270
|
+
view.setUint32(at, CENTRAL_HEADER, true);
|
|
271
|
+
view.setUint16(at + 4, 20, true);
|
|
272
|
+
view.setUint16(at + 6, 20, true);
|
|
273
|
+
view.setUint16(at + 8, name.utf8Flag, true);
|
|
274
|
+
view.setUint16(at + 10, 8, true);
|
|
275
|
+
view.setUint16(at + 12, DOS_TIME, true);
|
|
276
|
+
view.setUint16(at + 14, DOS_DATE, true);
|
|
277
|
+
view.setUint32(at + 16, part.crc, true);
|
|
278
|
+
view.setUint32(at + 20, part.deflated.length, true);
|
|
279
|
+
view.setUint32(at + 24, part.rawSize, true);
|
|
280
|
+
view.setUint16(at + 28, name.bytes.length, true);
|
|
281
|
+
view.setUint16(at + 30, 0, true);
|
|
282
|
+
view.setUint16(at + 32, 0, true);
|
|
283
|
+
view.setUint16(at + 34, 0, true);
|
|
284
|
+
view.setUint16(at + 36, 0, true);
|
|
285
|
+
view.setUint32(at + 38, 0, true);
|
|
286
|
+
view.setUint32(at + 42, offsets[i], true);
|
|
287
|
+
at += 46;
|
|
288
|
+
out.set(name.bytes, at);
|
|
289
|
+
at += name.bytes.length;
|
|
290
|
+
}
|
|
291
|
+
view.setUint32(at, END_OF_CENTRAL_DIR, true);
|
|
292
|
+
view.setUint16(at + 4, 0, true);
|
|
293
|
+
view.setUint16(at + 6, 0, true);
|
|
294
|
+
view.setUint16(at + 8, parts.length, true);
|
|
295
|
+
view.setUint16(at + 10, parts.length, true);
|
|
296
|
+
view.setUint32(at + 12, at - centralStart, true);
|
|
297
|
+
view.setUint32(at + 16, centralStart, true);
|
|
298
|
+
view.setUint16(at + 20, 0, true);
|
|
299
|
+
return out;
|
|
300
|
+
}
|
|
301
|
+
function encodeName(name) {
|
|
302
|
+
const bytes = new TextEncoder().encode(name);
|
|
303
|
+
let ascii = true;
|
|
304
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
305
|
+
if (bytes[i] > 127) {
|
|
306
|
+
ascii = false;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return { bytes, utf8Flag: ascii ? 0 : 2048 };
|
|
311
|
+
}
|
|
312
|
+
function utf8Length(name) {
|
|
313
|
+
return new TextEncoder().encode(name).length;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/io/zip-writer.ts
|
|
199
317
|
function zipXlsx(entries) {
|
|
200
318
|
const zippable = {};
|
|
201
319
|
for (const [path, content] of entries) {
|
|
@@ -210,6 +328,95 @@ function zipXlsx(entries) {
|
|
|
210
328
|
);
|
|
211
329
|
}
|
|
212
330
|
}
|
|
331
|
+
var ASYNC_MIN_BYTES = 512 * 1024;
|
|
332
|
+
function createDeflateCache() {
|
|
333
|
+
return /* @__PURE__ */ new Map();
|
|
334
|
+
}
|
|
335
|
+
function compress(name, source, bytes) {
|
|
336
|
+
return {
|
|
337
|
+
name,
|
|
338
|
+
source,
|
|
339
|
+
deflated: fflate.deflateSync(bytes, { level: 6 }),
|
|
340
|
+
crc: crc32(bytes),
|
|
341
|
+
rawSize: bytes.length
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
function zipXlsxCached(entries, cache) {
|
|
345
|
+
const parts = collectParts(entries, cache);
|
|
346
|
+
pruneCache(entries, cache);
|
|
347
|
+
return exceedsZipLimits(parts) ? zipXlsx(entries) : buildZipContainer(parts);
|
|
348
|
+
}
|
|
349
|
+
async function zipXlsxCachedAsync(entries, cache) {
|
|
350
|
+
const order = [];
|
|
351
|
+
const ready = /* @__PURE__ */ new Map();
|
|
352
|
+
const jobs = [];
|
|
353
|
+
const todo = [];
|
|
354
|
+
let todoBytes = 0;
|
|
355
|
+
for (const [path, content] of entries) {
|
|
356
|
+
order.push(path);
|
|
357
|
+
const cached = cache.get(path);
|
|
358
|
+
if (cached && cached.source === content) {
|
|
359
|
+
ready.set(path, cached);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const bytes = typeof content === "string" ? encodeUtf8(content) : content;
|
|
363
|
+
todo.push({ path, content, bytes });
|
|
364
|
+
todoBytes += bytes.length;
|
|
365
|
+
}
|
|
366
|
+
if (todoBytes <= ASYNC_MIN_BYTES) {
|
|
367
|
+
for (const { path, content, bytes } of todo) {
|
|
368
|
+
const compressed = compress(path, content, bytes);
|
|
369
|
+
cache.set(path, compressed);
|
|
370
|
+
ready.set(path, compressed);
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
for (const { path, content, bytes } of todo) {
|
|
374
|
+
jobs.push(deflateAsync(bytes).then((deflated) => ({
|
|
375
|
+
name: path,
|
|
376
|
+
source: content,
|
|
377
|
+
deflated,
|
|
378
|
+
crc: crc32(bytes),
|
|
379
|
+
rawSize: bytes.length
|
|
380
|
+
})));
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
for (const part of await Promise.all(jobs)) {
|
|
384
|
+
cache.set(part.name, part);
|
|
385
|
+
ready.set(part.name, part);
|
|
386
|
+
}
|
|
387
|
+
pruneCache(entries, cache);
|
|
388
|
+
const parts = order.map((path) => ready.get(path));
|
|
389
|
+
return exceedsZipLimits(parts) ? zipXlsx(entries) : buildZipContainer(parts);
|
|
390
|
+
}
|
|
391
|
+
function deflateAsync(bytes) {
|
|
392
|
+
return new Promise((resolve, reject) => {
|
|
393
|
+
fflate.deflate(bytes, { level: 6 }, (err, data) => {
|
|
394
|
+
if (err) reject(new Error(`Failed to create xlsx: ${err.message}`));
|
|
395
|
+
else resolve(data);
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
function collectParts(entries, cache) {
|
|
400
|
+
const parts = [];
|
|
401
|
+
for (const [path, content] of entries) {
|
|
402
|
+
const cached = cache.get(path);
|
|
403
|
+
if (cached && cached.source === content) {
|
|
404
|
+
parts.push(cached);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
const bytes = typeof content === "string" ? encodeUtf8(content) : content;
|
|
408
|
+
const part = compress(path, content, bytes);
|
|
409
|
+
cache.set(path, part);
|
|
410
|
+
parts.push(part);
|
|
411
|
+
}
|
|
412
|
+
return parts;
|
|
413
|
+
}
|
|
414
|
+
function pruneCache(entries, cache) {
|
|
415
|
+
if (cache.size === entries.size) return;
|
|
416
|
+
for (const path of [...cache.keys()]) {
|
|
417
|
+
if (!entries.has(path)) cache.delete(path);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
213
420
|
function setXmlEntry(entries, path, xml2) {
|
|
214
421
|
entries.set(path, xml2);
|
|
215
422
|
}
|
|
@@ -302,7 +509,8 @@ var REL_TYPES = {
|
|
|
302
509
|
drawing: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
|
|
303
510
|
hyperlink: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
|
304
511
|
comments: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
|
|
305
|
-
vmlDrawing: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
|
|
512
|
+
vmlDrawing: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing",
|
|
513
|
+
calcChain: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/calcChain"
|
|
306
514
|
};
|
|
307
515
|
var CONTENT_TYPES = {
|
|
308
516
|
workbook: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
|
|
@@ -314,7 +522,8 @@ var CONTENT_TYPES = {
|
|
|
314
522
|
relationships: "application/vnd.openxmlformats-package.relationships+xml",
|
|
315
523
|
coreProperties: "application/vnd.openxmlformats-package.core-properties+xml",
|
|
316
524
|
comments: "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
|
|
317
|
-
vmlDrawing: "application/vnd.openxmlformats-officedocument.vmlDrawing"
|
|
525
|
+
vmlDrawing: "application/vnd.openxmlformats-officedocument.vmlDrawing",
|
|
526
|
+
calcChain: "application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml"
|
|
318
527
|
};
|
|
319
528
|
|
|
320
529
|
// src/io/ooxml-templates.ts
|
|
@@ -347,18 +556,20 @@ var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
|
347
556
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
348
557
|
<Relationship Id="rId1" Type="${REL_TYPES.officeDocument}" Target="xl/workbook.xml"/>
|
|
349
558
|
</Relationships>`;
|
|
350
|
-
function buildWorkbookRels(sheetCount) {
|
|
559
|
+
function buildWorkbookRels(sheetCount, includeCalcChain = false) {
|
|
351
560
|
const sheetRels = Array.from(
|
|
352
561
|
{ length: sheetCount },
|
|
353
562
|
(_, i) => ` <Relationship Id="rId${i + 1}" Type="${REL_TYPES.worksheet}" Target="worksheets/sheet${i + 1}.xml"/>`
|
|
354
563
|
).join("\n");
|
|
355
564
|
const stylesId = sheetCount + 1;
|
|
356
565
|
const sharedId = sheetCount + 2;
|
|
566
|
+
const calcChainRel = includeCalcChain ? `
|
|
567
|
+
<Relationship Id="rId${sheetCount + 3}" Type="${REL_TYPES.calcChain}" Target="calcChain.xml"/>` : "";
|
|
357
568
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
358
569
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
359
570
|
${sheetRels}
|
|
360
571
|
<Relationship Id="rId${stylesId}" Type="${REL_TYPES.styles}" Target="styles.xml"/>
|
|
361
|
-
<Relationship Id="rId${sharedId}" Type="${REL_TYPES.sharedStrings}" Target="sharedStrings.xml"
|
|
572
|
+
<Relationship Id="rId${sharedId}" Type="${REL_TYPES.sharedStrings}" Target="sharedStrings.xml"/>${calcChainRel}
|
|
362
573
|
</Relationships>`;
|
|
363
574
|
}
|
|
364
575
|
function buildWorkbookXml(sheets, definedNames = []) {
|
|
@@ -963,8 +1174,12 @@ var ExcelBorderEdge = class _ExcelBorderEdge {
|
|
|
963
1174
|
cacheKey() {
|
|
964
1175
|
return `${this.style}:${this.color?.toString() ?? "null"}`;
|
|
965
1176
|
}
|
|
1177
|
+
/** An independent copy. ExcelColor is immutable, so it is shared. */
|
|
1178
|
+
clone() {
|
|
1179
|
+
return new _ExcelBorderEdge(this.style, this.color);
|
|
1180
|
+
}
|
|
966
1181
|
};
|
|
967
|
-
var
|
|
1182
|
+
var ExcelBorder = class _ExcelBorder {
|
|
968
1183
|
constructor() {
|
|
969
1184
|
this.left = new ExcelBorderEdge();
|
|
970
1185
|
this.right = new ExcelBorderEdge();
|
|
@@ -1004,6 +1219,33 @@ var _ExcelBorder = class _ExcelBorder {
|
|
|
1004
1219
|
b.right = new ExcelBorderEdge(style, color);
|
|
1005
1220
|
return b;
|
|
1006
1221
|
}
|
|
1222
|
+
/**
|
|
1223
|
+
* A border with no edges set.
|
|
1224
|
+
*
|
|
1225
|
+
* A fresh instance per access, not a shared singleton. `ExcelBorder` is mutable
|
|
1226
|
+
* (`border.left.style = …` is the normal way to use it), so a shared instance
|
|
1227
|
+
* handed to two styles let an edit through one of them reach the other — and,
|
|
1228
|
+
* once anything had assigned `none` and then modified it, every later
|
|
1229
|
+
* `ExcelBorder.none` came back already carrying that edge.
|
|
1230
|
+
*/
|
|
1231
|
+
static get none() {
|
|
1232
|
+
return new _ExcelBorder();
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* A deep copy: the edges are new objects, so mutating the copy cannot reach the
|
|
1236
|
+
* original. {@link CellStyle.clone} relies on this.
|
|
1237
|
+
*/
|
|
1238
|
+
clone() {
|
|
1239
|
+
const b = new _ExcelBorder();
|
|
1240
|
+
b.left = this.left.clone();
|
|
1241
|
+
b.right = this.right.clone();
|
|
1242
|
+
b.top = this.top.clone();
|
|
1243
|
+
b.bottom = this.bottom.clone();
|
|
1244
|
+
b.diagonal = this.diagonal.clone();
|
|
1245
|
+
b.diagonalDown = this.diagonalDown;
|
|
1246
|
+
b.diagonalUp = this.diagonalUp;
|
|
1247
|
+
return b;
|
|
1248
|
+
}
|
|
1007
1249
|
// ── Serialisation ─────────────────────────────────────────────────────────
|
|
1008
1250
|
toXml() {
|
|
1009
1251
|
const dd = this.diagonalDown ? ' diagonalDown="1"' : "";
|
|
@@ -1049,8 +1291,6 @@ var _ExcelBorder = class _ExcelBorder {
|
|
|
1049
1291
|
].join("|");
|
|
1050
1292
|
}
|
|
1051
1293
|
};
|
|
1052
|
-
_ExcelBorder.none = new _ExcelBorder();
|
|
1053
|
-
var ExcelBorder = _ExcelBorder;
|
|
1054
1294
|
|
|
1055
1295
|
// src/style/excel-alignment.ts
|
|
1056
1296
|
var ExcelHorizontalAlignment = /* @__PURE__ */ ((ExcelHorizontalAlignment3) => {
|
|
@@ -1343,7 +1583,7 @@ var _CellStyle = class _CellStyle {
|
|
|
1343
1583
|
const s = new _CellStyle();
|
|
1344
1584
|
if (this.font) s.font = this.font.clone();
|
|
1345
1585
|
s.fill = this.fill;
|
|
1346
|
-
s.border = this.border;
|
|
1586
|
+
s.border = this.border?.clone();
|
|
1347
1587
|
s.alignment = this.alignment ? Object.assign(new ExcelAlignment(), this.alignment) : void 0;
|
|
1348
1588
|
s.numberFormat = this.numberFormat;
|
|
1349
1589
|
s.isLocked = this.isLocked;
|
|
@@ -1366,8 +1606,46 @@ ensureAlignment_fn = function() {
|
|
|
1366
1606
|
};
|
|
1367
1607
|
var CellStyle = _CellStyle;
|
|
1368
1608
|
|
|
1609
|
+
// src/model/oa-date.ts
|
|
1610
|
+
var OA_EPOCH_MS = Date.UTC(1899, 11, 30, 0, 0, 0, 0);
|
|
1611
|
+
var MS_PER_DAY = 864e5;
|
|
1612
|
+
function toOADate(date) {
|
|
1613
|
+
const utcMs = Date.UTC(
|
|
1614
|
+
date.getFullYear(),
|
|
1615
|
+
date.getMonth(),
|
|
1616
|
+
date.getDate(),
|
|
1617
|
+
date.getHours(),
|
|
1618
|
+
date.getMinutes(),
|
|
1619
|
+
date.getSeconds(),
|
|
1620
|
+
date.getMilliseconds()
|
|
1621
|
+
);
|
|
1622
|
+
return (utcMs - OA_EPOCH_MS) / MS_PER_DAY;
|
|
1623
|
+
}
|
|
1624
|
+
function fromOADate(oaDate) {
|
|
1625
|
+
const ms = OA_EPOCH_MS + oaDate * MS_PER_DAY;
|
|
1626
|
+
const d = new Date(ms);
|
|
1627
|
+
return new Date(
|
|
1628
|
+
d.getUTCFullYear(),
|
|
1629
|
+
d.getUTCMonth(),
|
|
1630
|
+
d.getUTCDate(),
|
|
1631
|
+
d.getUTCHours(),
|
|
1632
|
+
d.getUTCMinutes(),
|
|
1633
|
+
d.getUTCSeconds(),
|
|
1634
|
+
d.getUTCMilliseconds()
|
|
1635
|
+
);
|
|
1636
|
+
}
|
|
1637
|
+
var DATE_FORMAT_IDS = /* @__PURE__ */ new Set([14, 15, 16, 17, 18, 19, 20, 21, 22, 45, 46, 47]);
|
|
1638
|
+
function isDateFormatId(formatId) {
|
|
1639
|
+
return DATE_FORMAT_IDS.has(formatId);
|
|
1640
|
+
}
|
|
1641
|
+
function isDateFormatCode(formatCode) {
|
|
1642
|
+
const lower = formatCode.toLowerCase();
|
|
1643
|
+
return lower.includes("y") || lower.includes("d") || lower.includes("m") && !lower.includes("mm:") || // m = month unless part of mm:ss
|
|
1644
|
+
lower.includes("h") || lower.includes("am") || lower.includes("pm");
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1369
1647
|
// src/style/style-manager.ts
|
|
1370
|
-
var _StyleManager_instances, initDefaults_fn, getOrCreateFont_fn, getOrCreateFill_fn, getOrCreateBorder_fn, getOrCreateNumFmt_fn, cellFormatKey_fn, cfToXml_fn;
|
|
1648
|
+
var _numFmtMapCache, _dateStyleMemo, _StyleManager_instances, numFmtMap_fn, invalidateFormatCaches_fn, initDefaults_fn, getOrCreateFont_fn, getOrCreateFill_fn, getOrCreateBorder_fn, getOrCreateNumFmt_fn, cellFormatKey_fn, cfToXml_fn;
|
|
1371
1649
|
var StyleManager = class {
|
|
1372
1650
|
constructor() {
|
|
1373
1651
|
__privateAdd(this, _StyleManager_instances);
|
|
@@ -1386,6 +1664,21 @@ var StyleManager = class {
|
|
|
1386
1664
|
this.cellXfCache = /* @__PURE__ */ new Map();
|
|
1387
1665
|
this.nextCustomNumFmtId = 164;
|
|
1388
1666
|
this.isDirty = false;
|
|
1667
|
+
/**
|
|
1668
|
+
* `numFmtId` → format code for the custom (≥164) formats, materialised once.
|
|
1669
|
+
* Rebuilding it per lookup made every numeric cell read O(custom formats),
|
|
1670
|
+
* and reading a cell's value has to consult it to tell dates from numbers.
|
|
1671
|
+
* Invalidated whenever `numFmts` changes — see {@link #invalidateFormatCaches}.
|
|
1672
|
+
*/
|
|
1673
|
+
__privateAdd(this, _numFmtMapCache);
|
|
1674
|
+
/**
|
|
1675
|
+
* cellXf index → does its number format render a date? Memoised because
|
|
1676
|
+
* {@link Cell.getValue} asks this for every numeric cell, and answering it
|
|
1677
|
+
* from scratch reconstructs a whole CellStyle. Entries stay valid because
|
|
1678
|
+
* `cellXfs` and `numFmts` are append-only: an existing index never changes
|
|
1679
|
+
* meaning.
|
|
1680
|
+
*/
|
|
1681
|
+
__privateAdd(this, _dateStyleMemo, /* @__PURE__ */ new Map());
|
|
1389
1682
|
__privateMethod(this, _StyleManager_instances, initDefaults_fn).call(this);
|
|
1390
1683
|
}
|
|
1391
1684
|
// ── Public API ─────────────────────────────────────────────────────────────
|
|
@@ -1437,10 +1730,7 @@ var StyleManager = class {
|
|
|
1437
1730
|
const borderEntry = this.borders[cf.borderId];
|
|
1438
1731
|
if (borderEntry && cf.applyBorder) s.border = borderEntry.border;
|
|
1439
1732
|
if (cf.applyNumFmt) {
|
|
1440
|
-
|
|
1441
|
-
this.numFmts.map((n) => [n.id, n.code])
|
|
1442
|
-
);
|
|
1443
|
-
s.numberFormat = ExcelNumberFormat.fromFormatId(cf.numFmtId, customMap);
|
|
1733
|
+
s.numberFormat = ExcelNumberFormat.fromFormatId(cf.numFmtId, __privateMethod(this, _StyleManager_instances, numFmtMap_fn).call(this));
|
|
1444
1734
|
}
|
|
1445
1735
|
if (cf.alignment && cf.applyAlign) s.alignment = cf.alignment;
|
|
1446
1736
|
if (cf.applyProt) {
|
|
@@ -1449,6 +1739,29 @@ var StyleManager = class {
|
|
|
1449
1739
|
}
|
|
1450
1740
|
return s;
|
|
1451
1741
|
}
|
|
1742
|
+
/**
|
|
1743
|
+
* Whether the cellXf at `styleIndex` formats its value as a date. This is the
|
|
1744
|
+
* one style question the value-reading path has to ask, so it is answered
|
|
1745
|
+
* without reconstructing a CellStyle: the result is derived from the stored
|
|
1746
|
+
* format id/code and memoised per index.
|
|
1747
|
+
*
|
|
1748
|
+
* Equivalent to `getCellStyle(i).numberFormat` being date-like, by
|
|
1749
|
+
* construction — both read the same `applyNumFmt`/`numFmtId` pair through
|
|
1750
|
+
* {@link ExcelNumberFormat.fromFormatId}.
|
|
1751
|
+
*/
|
|
1752
|
+
isDateStyle(styleIndex) {
|
|
1753
|
+
const memo = __privateGet(this, _dateStyleMemo).get(styleIndex);
|
|
1754
|
+
if (memo !== void 0) return memo;
|
|
1755
|
+
const cf = this.cellXfs[styleIndex];
|
|
1756
|
+
if (!cf) return false;
|
|
1757
|
+
let isDate = false;
|
|
1758
|
+
if (cf.applyNumFmt) {
|
|
1759
|
+
const fmt = ExcelNumberFormat.fromFormatId(cf.numFmtId, __privateMethod(this, _StyleManager_instances, numFmtMap_fn).call(this));
|
|
1760
|
+
isDate = isDateFormatId(fmt.formatId) || isDateFormatCode(fmt.formatCode);
|
|
1761
|
+
}
|
|
1762
|
+
__privateGet(this, _dateStyleMemo).set(styleIndex, isDate);
|
|
1763
|
+
return isDate;
|
|
1764
|
+
}
|
|
1452
1765
|
// ── XML serialisation ──────────────────────────────────────────────────────
|
|
1453
1766
|
/** Builds the complete xl/styles.xml string. */
|
|
1454
1767
|
buildStylesXml() {
|
|
@@ -1478,6 +1791,7 @@ ${cellXfsXml}
|
|
|
1478
1791
|
this.numFmtCache.clear();
|
|
1479
1792
|
this.cellXfCache.clear();
|
|
1480
1793
|
this.nextCustomNumFmtId = 164;
|
|
1794
|
+
__privateMethod(this, _StyleManager_instances, invalidateFormatCaches_fn).call(this);
|
|
1481
1795
|
const root = parseXml(stylesXml);
|
|
1482
1796
|
for (const el of getElementsByTagName(root, "numFmt")) {
|
|
1483
1797
|
const id = parseInt(getAttr(el, "numFmtId") ?? "0", 10);
|
|
@@ -1536,9 +1850,23 @@ ${cellXfsXml}
|
|
|
1536
1850
|
}
|
|
1537
1851
|
if (this.fonts.length === 0) __privateMethod(this, _StyleManager_instances, initDefaults_fn).call(this);
|
|
1538
1852
|
if (this.cellXfs.length === 0) __privateMethod(this, _StyleManager_instances, initDefaults_fn).call(this);
|
|
1853
|
+
__privateMethod(this, _StyleManager_instances, invalidateFormatCaches_fn).call(this);
|
|
1539
1854
|
}
|
|
1540
1855
|
};
|
|
1856
|
+
_numFmtMapCache = new WeakMap();
|
|
1857
|
+
_dateStyleMemo = new WeakMap();
|
|
1541
1858
|
_StyleManager_instances = new WeakSet();
|
|
1859
|
+
numFmtMap_fn = function() {
|
|
1860
|
+
if (!__privateGet(this, _numFmtMapCache)) {
|
|
1861
|
+
__privateSet(this, _numFmtMapCache, new Map(this.numFmts.map((n) => [n.id, n.code])));
|
|
1862
|
+
}
|
|
1863
|
+
return __privateGet(this, _numFmtMapCache);
|
|
1864
|
+
};
|
|
1865
|
+
/** Drops the derived format caches after `numFmts` or `cellXfs` changed. */
|
|
1866
|
+
invalidateFormatCaches_fn = function() {
|
|
1867
|
+
__privateSet(this, _numFmtMapCache, void 0);
|
|
1868
|
+
__privateGet(this, _dateStyleMemo).clear();
|
|
1869
|
+
};
|
|
1542
1870
|
// ── Private helpers ────────────────────────────────────────────────────────
|
|
1543
1871
|
initDefaults_fn = function() {
|
|
1544
1872
|
const defaultFont = new ExcelFont({ name: "Calibri", size: 11 });
|
|
@@ -1611,6 +1939,7 @@ getOrCreateNumFmt_fn = function(fmt) {
|
|
|
1611
1939
|
const id = this.nextCustomNumFmtId++;
|
|
1612
1940
|
this.numFmts.push({ id, code: fmt.formatCode });
|
|
1613
1941
|
this.numFmtCache.set(fmt.formatCode, id);
|
|
1942
|
+
__privateMethod(this, _StyleManager_instances, invalidateFormatCaches_fn).call(this);
|
|
1614
1943
|
this.isDirty = true;
|
|
1615
1944
|
return id;
|
|
1616
1945
|
};
|
|
@@ -2023,56 +2352,54 @@ function parseRangeRef(rangeReference) {
|
|
|
2023
2352
|
};
|
|
2024
2353
|
}
|
|
2025
2354
|
|
|
2026
|
-
// src/model/
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
);
|
|
2053
|
-
}
|
|
2054
|
-
var DATE_FORMAT_IDS = /* @__PURE__ */ new Set([14, 15, 16, 17, 18, 19, 20, 21, 22, 45, 46, 47]);
|
|
2055
|
-
function isDateFormatId(formatId) {
|
|
2056
|
-
return DATE_FORMAT_IDS.has(formatId);
|
|
2355
|
+
// src/model/cell.ts
|
|
2356
|
+
function readCellValue(data, sharedStrings, styles) {
|
|
2357
|
+
const raw = data.rawValue;
|
|
2358
|
+
if (raw === void 0 || raw === "") return null;
|
|
2359
|
+
switch (data.dataType) {
|
|
2360
|
+
case "s": {
|
|
2361
|
+
const idx = parseInt(raw, 10);
|
|
2362
|
+
return sharedStrings.tryGetString(idx) ?? raw;
|
|
2363
|
+
}
|
|
2364
|
+
case "b":
|
|
2365
|
+
return raw === "1";
|
|
2366
|
+
case "str":
|
|
2367
|
+
return raw;
|
|
2368
|
+
default: {
|
|
2369
|
+
const num = parseFloat(raw);
|
|
2370
|
+
if (isNaN(num)) return raw;
|
|
2371
|
+
if (styles.isDateStyle(data.styleIndex)) {
|
|
2372
|
+
try {
|
|
2373
|
+
return fromOADate(num);
|
|
2374
|
+
} catch {
|
|
2375
|
+
return num;
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
return num;
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2057
2381
|
}
|
|
2058
|
-
function
|
|
2059
|
-
const
|
|
2060
|
-
|
|
2061
|
-
|
|
2382
|
+
function buildCellXml(data) {
|
|
2383
|
+
const style = data.styleIndex > 0 ? ` s="${data.styleIndex}"` : "";
|
|
2384
|
+
const formulaXml = data.formula ? data.arrayRef ? `<f t="array" ref="${data.arrayRef}" aca="1" ca="1">${escXml(data.formula)}</f>` : `<f>${escXml(data.formula)}</f>` : "";
|
|
2385
|
+
const valueXml = data.rawValue !== void 0 && data.rawValue !== "" ? `<v>${escXml(data.rawValue)}</v>` : "";
|
|
2386
|
+
const type = data.dataType && (formulaXml || valueXml) ? ` t="${data.dataType}"` : "";
|
|
2387
|
+
if (!formulaXml && !valueXml && data.styleIndex === 0) return "";
|
|
2388
|
+
return `<c r="${data.reference}"${style}${type}>${formulaXml}${valueXml}</c>`;
|
|
2062
2389
|
}
|
|
2063
|
-
|
|
2064
|
-
// src/model/cell.ts
|
|
2065
|
-
var _data, _sharedStrings, _styles, _Cell_instances, isDateStyle_fn;
|
|
2390
|
+
var _data, _sharedStrings, _styles, _version, _Cell_instances, touch_fn;
|
|
2066
2391
|
var Cell = class {
|
|
2067
2392
|
/** @internal */
|
|
2068
|
-
constructor(data, sharedStrings, styles) {
|
|
2393
|
+
constructor(data, sharedStrings, styles, version) {
|
|
2069
2394
|
__privateAdd(this, _Cell_instances);
|
|
2070
2395
|
__privateAdd(this, _data);
|
|
2071
2396
|
__privateAdd(this, _sharedStrings);
|
|
2072
2397
|
__privateAdd(this, _styles);
|
|
2398
|
+
__privateAdd(this, _version);
|
|
2073
2399
|
__privateSet(this, _data, data);
|
|
2074
2400
|
__privateSet(this, _sharedStrings, sharedStrings);
|
|
2075
2401
|
__privateSet(this, _styles, styles);
|
|
2402
|
+
__privateSet(this, _version, version);
|
|
2076
2403
|
}
|
|
2077
2404
|
// ── Identity ───────────────────────────────────────────────────────────────
|
|
2078
2405
|
/** Cell reference string e.g. "A1". */
|
|
@@ -2088,6 +2415,7 @@ var Cell = class {
|
|
|
2088
2415
|
return parseCellRef(__privateGet(this, _data).reference).column;
|
|
2089
2416
|
}
|
|
2090
2417
|
setValue(value) {
|
|
2418
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2091
2419
|
__privateGet(this, _data).formula = void 0;
|
|
2092
2420
|
if (value === null || value === void 0) {
|
|
2093
2421
|
__privateGet(this, _data).dataType = void 0;
|
|
@@ -2125,29 +2453,7 @@ var Cell = class {
|
|
|
2125
2453
|
* Date detection is based on the cell's number format.
|
|
2126
2454
|
*/
|
|
2127
2455
|
getValue() {
|
|
2128
|
-
|
|
2129
|
-
switch (__privateGet(this, _data).dataType) {
|
|
2130
|
-
case "s": {
|
|
2131
|
-
const idx = parseInt(__privateGet(this, _data).rawValue, 10);
|
|
2132
|
-
return __privateGet(this, _sharedStrings).tryGetString(idx) ?? __privateGet(this, _data).rawValue;
|
|
2133
|
-
}
|
|
2134
|
-
case "b":
|
|
2135
|
-
return __privateGet(this, _data).rawValue === "1";
|
|
2136
|
-
case "str":
|
|
2137
|
-
return __privateGet(this, _data).rawValue;
|
|
2138
|
-
default: {
|
|
2139
|
-
const num = parseFloat(__privateGet(this, _data).rawValue);
|
|
2140
|
-
if (isNaN(num)) return __privateGet(this, _data).rawValue;
|
|
2141
|
-
if (__privateMethod(this, _Cell_instances, isDateStyle_fn).call(this)) {
|
|
2142
|
-
try {
|
|
2143
|
-
return fromOADate(num);
|
|
2144
|
-
} catch {
|
|
2145
|
-
return num;
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
return num;
|
|
2149
|
-
}
|
|
2150
|
-
}
|
|
2456
|
+
return readCellValue(__privateGet(this, _data), __privateGet(this, _sharedStrings), __privateGet(this, _styles));
|
|
2151
2457
|
}
|
|
2152
2458
|
/**
|
|
2153
2459
|
* Returns the display text of the cell value (always a string).
|
|
@@ -2185,6 +2491,7 @@ var Cell = class {
|
|
|
2185
2491
|
*/
|
|
2186
2492
|
setFormula(formula) {
|
|
2187
2493
|
if (!formula) throw new Error("Formula cannot be empty.");
|
|
2494
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2188
2495
|
__privateGet(this, _data).formula = formula.startsWith("=") ? formula.slice(1) : formula;
|
|
2189
2496
|
__privateGet(this, _data).rawValue = void 0;
|
|
2190
2497
|
__privateGet(this, _data).dataType = void 0;
|
|
@@ -2197,6 +2504,7 @@ var Cell = class {
|
|
|
2197
2504
|
* plain scalar formula. Serialises as `<f t="array" ref="…">`.
|
|
2198
2505
|
*/
|
|
2199
2506
|
setArrayRef(spillRef) {
|
|
2507
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2200
2508
|
__privateGet(this, _data).arrayRef = spillRef ?? void 0;
|
|
2201
2509
|
return this;
|
|
2202
2510
|
}
|
|
@@ -2214,6 +2522,7 @@ var Cell = class {
|
|
|
2214
2522
|
* added to the shared-string table.
|
|
2215
2523
|
*/
|
|
2216
2524
|
setComputedValue(value) {
|
|
2525
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2217
2526
|
if (value === null || value === void 0) {
|
|
2218
2527
|
__privateGet(this, _data).dataType = void 0;
|
|
2219
2528
|
__privateGet(this, _data).rawValue = void 0;
|
|
@@ -2253,6 +2562,7 @@ var Cell = class {
|
|
|
2253
2562
|
}
|
|
2254
2563
|
/** Replaces the entire cell style. */
|
|
2255
2564
|
set style(value) {
|
|
2565
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2256
2566
|
__privateGet(this, _data).styleIndex = __privateGet(this, _styles).getOrCreateCellFormatIndex(value);
|
|
2257
2567
|
}
|
|
2258
2568
|
/**
|
|
@@ -2263,6 +2573,7 @@ var Cell = class {
|
|
|
2263
2573
|
* cell.applyStyle(s => s.setBold(true).setFontColor(ExcelColor.red))
|
|
2264
2574
|
*/
|
|
2265
2575
|
applyStyle(action) {
|
|
2576
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2266
2577
|
const s = __privateGet(this, _styles).getCellStyle(__privateGet(this, _data).styleIndex).clone();
|
|
2267
2578
|
action(s);
|
|
2268
2579
|
__privateGet(this, _data).styleIndex = __privateGet(this, _styles).getOrCreateCellFormatIndex(s);
|
|
@@ -2305,6 +2616,7 @@ var Cell = class {
|
|
|
2305
2616
|
// ── Clear ──────────────────────────────────────────────────────────────────
|
|
2306
2617
|
/** Clears the cell value and formula, preserving style. */
|
|
2307
2618
|
clear() {
|
|
2619
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2308
2620
|
__privateGet(this, _data).rawValue = void 0;
|
|
2309
2621
|
__privateGet(this, _data).dataType = void 0;
|
|
2310
2622
|
__privateGet(this, _data).formula = void 0;
|
|
@@ -2312,19 +2624,14 @@ var Cell = class {
|
|
|
2312
2624
|
}
|
|
2313
2625
|
/** Clears the cell value, formula, AND style. */
|
|
2314
2626
|
clearAll() {
|
|
2627
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2315
2628
|
this.clear();
|
|
2316
2629
|
__privateGet(this, _data).styleIndex = 0;
|
|
2317
2630
|
}
|
|
2318
2631
|
// ── XML serialisation ──────────────────────────────────────────────────────
|
|
2319
2632
|
/** @internal Builds the <c> XML element string for this cell. */
|
|
2320
2633
|
toXml() {
|
|
2321
|
-
|
|
2322
|
-
const style = __privateGet(this, _data).styleIndex > 0 ? ` s="${__privateGet(this, _data).styleIndex}"` : "";
|
|
2323
|
-
const formulaXml = __privateGet(this, _data).formula ? __privateGet(this, _data).arrayRef ? `<f t="array" ref="${__privateGet(this, _data).arrayRef}" aca="1" ca="1">${escXml(__privateGet(this, _data).formula)}</f>` : `<f>${escXml(__privateGet(this, _data).formula)}</f>` : "";
|
|
2324
|
-
const valueXml = __privateGet(this, _data).rawValue !== void 0 && __privateGet(this, _data).rawValue !== "" ? `<v>${escXml(__privateGet(this, _data).rawValue)}</v>` : "";
|
|
2325
|
-
const type = __privateGet(this, _data).dataType && (formulaXml || valueXml) ? ` t="${__privateGet(this, _data).dataType}"` : "";
|
|
2326
|
-
if (!formulaXml && !valueXml && __privateGet(this, _data).styleIndex === 0) return "";
|
|
2327
|
-
return `<c r="${ref}"${style}${type}>${formulaXml}${valueXml}</c>`;
|
|
2634
|
+
return buildCellXml(__privateGet(this, _data));
|
|
2328
2635
|
}
|
|
2329
2636
|
/** @internal Parses a <c> XML element into CellData. */
|
|
2330
2637
|
static fromXmlElement(el, ref) {
|
|
@@ -2333,29 +2640,35 @@ var Cell = class {
|
|
|
2333
2640
|
let rawValue;
|
|
2334
2641
|
let formula;
|
|
2335
2642
|
let arrayRef;
|
|
2643
|
+
let sharedIndex;
|
|
2336
2644
|
for (const child of el.children) {
|
|
2337
2645
|
const tag = child.tagName.replace(/^.*:/, "");
|
|
2338
2646
|
if (tag === "v") rawValue = child.textContent;
|
|
2339
2647
|
if (tag === "f") {
|
|
2340
2648
|
formula = child.textContent;
|
|
2341
|
-
|
|
2342
|
-
|
|
2649
|
+
const attrs = child.attributes;
|
|
2650
|
+
const type = attrs?.["t"];
|
|
2651
|
+
if (type === "array" && attrs?.["ref"]) {
|
|
2652
|
+
arrayRef = attrs["ref"];
|
|
2653
|
+
}
|
|
2654
|
+
if (type === "shared" && attrs?.["si"] !== void 0) {
|
|
2655
|
+
const si = parseInt(attrs["si"], 10);
|
|
2656
|
+
if (!Number.isNaN(si)) sharedIndex = si;
|
|
2343
2657
|
}
|
|
2344
2658
|
}
|
|
2345
2659
|
}
|
|
2346
|
-
|
|
2660
|
+
if (formula === "") formula = void 0;
|
|
2661
|
+
return { reference: ref, dataType, rawValue, formula, arrayRef, styleIndex, sharedIndex };
|
|
2347
2662
|
}
|
|
2348
2663
|
};
|
|
2349
2664
|
_data = new WeakMap();
|
|
2350
2665
|
_sharedStrings = new WeakMap();
|
|
2351
2666
|
_styles = new WeakMap();
|
|
2667
|
+
_version = new WeakMap();
|
|
2352
2668
|
_Cell_instances = new WeakSet();
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
if (!style.numberFormat) return false;
|
|
2357
|
-
if (isDateFormatId(style.numberFormat.formatId)) return true;
|
|
2358
|
-
return isDateFormatCode(style.numberFormat.formatCode);
|
|
2669
|
+
/** Records that this cell's stored data changed. */
|
|
2670
|
+
touch_fn = function() {
|
|
2671
|
+
if (__privateGet(this, _version)) __privateGet(this, _version).v++;
|
|
2359
2672
|
};
|
|
2360
2673
|
function escXml(s) {
|
|
2361
2674
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -2622,6 +2935,219 @@ function shiftFormulaRefs(formula, shift, formulaSheet) {
|
|
|
2622
2935
|
return { text: out + formula.slice(flushed), changed: true, hasRefError };
|
|
2623
2936
|
}
|
|
2624
2937
|
|
|
2938
|
+
// src/model/formula-ref-translate.ts
|
|
2939
|
+
function isWordChar2(code) {
|
|
2940
|
+
return code >= 65 && code <= 90 || // A-Z
|
|
2941
|
+
code >= 97 && code <= 122 || // a-z
|
|
2942
|
+
code >= 48 && code <= 57 || // 0-9
|
|
2943
|
+
code === 95 || // _
|
|
2944
|
+
code === 46 || // .
|
|
2945
|
+
code === 36;
|
|
2946
|
+
}
|
|
2947
|
+
function parseCellComponent2(word) {
|
|
2948
|
+
const m = /^(\$?)([A-Za-z]{1,3})(\$?)(\d+)$/.exec(word);
|
|
2949
|
+
if (!m) return null;
|
|
2950
|
+
const col = columnNumber(m[2]);
|
|
2951
|
+
const row = parseInt(m[4], 10);
|
|
2952
|
+
if (col > EXCEL_MAX_COLUMNS || row < 1 || row > EXCEL_MAX_ROWS) return null;
|
|
2953
|
+
return { colAbs: m[1] === "$", col, rowAbs: m[3] === "$", row };
|
|
2954
|
+
}
|
|
2955
|
+
function parseColComponent2(word) {
|
|
2956
|
+
const m = /^(\$?)([A-Za-z]{1,3})$/.exec(word);
|
|
2957
|
+
if (!m) return null;
|
|
2958
|
+
const col = columnNumber(m[2]);
|
|
2959
|
+
return col > EXCEL_MAX_COLUMNS ? null : { abs: m[1] === "$", col };
|
|
2960
|
+
}
|
|
2961
|
+
function parseRowComponent2(word) {
|
|
2962
|
+
const m = /^(\$?)(\d+)$/.exec(word);
|
|
2963
|
+
if (!m) return null;
|
|
2964
|
+
const row = parseInt(m[2], 10);
|
|
2965
|
+
return row < 1 || row > EXCEL_MAX_ROWS ? null : { abs: m[1] === "$", row };
|
|
2966
|
+
}
|
|
2967
|
+
function move(index, delta, abs, max) {
|
|
2968
|
+
if (abs || delta === 0) return index;
|
|
2969
|
+
const moved = index + delta;
|
|
2970
|
+
return moved < 1 || moved > max ? null : moved;
|
|
2971
|
+
}
|
|
2972
|
+
function translateFormulaRefs(formula, dRow, dCol) {
|
|
2973
|
+
if (dRow === 0 && dCol === 0) return { text: formula, hasRefError: false };
|
|
2974
|
+
const n = formula.length;
|
|
2975
|
+
let out = "";
|
|
2976
|
+
let flushed = 0;
|
|
2977
|
+
let hasRefError = false;
|
|
2978
|
+
let i = 0;
|
|
2979
|
+
const replace = (start, text) => {
|
|
2980
|
+
if (text.length === i - start && formula.startsWith(text, start)) return;
|
|
2981
|
+
out += formula.slice(flushed, start) + text;
|
|
2982
|
+
flushed = i;
|
|
2983
|
+
};
|
|
2984
|
+
const readWord = () => {
|
|
2985
|
+
let j = i;
|
|
2986
|
+
while (j < n && isWordChar2(formula.charCodeAt(j))) j++;
|
|
2987
|
+
const w = formula.slice(i, j);
|
|
2988
|
+
i = j;
|
|
2989
|
+
return w;
|
|
2990
|
+
};
|
|
2991
|
+
const readQuoted = () => {
|
|
2992
|
+
let j = i + 1;
|
|
2993
|
+
while (j < n) {
|
|
2994
|
+
if (formula[j] === "'") {
|
|
2995
|
+
if (formula[j + 1] === "'") {
|
|
2996
|
+
j += 2;
|
|
2997
|
+
continue;
|
|
2998
|
+
}
|
|
2999
|
+
j++;
|
|
3000
|
+
break;
|
|
3001
|
+
}
|
|
3002
|
+
j++;
|
|
3003
|
+
}
|
|
3004
|
+
const q = formula.slice(i, j);
|
|
3005
|
+
i = j;
|
|
3006
|
+
return q;
|
|
3007
|
+
};
|
|
3008
|
+
const emitCell2 = (c, row, col) => `${c.colAbs ? "$" : ""}${columnLetter(col)}${c.rowAbs ? "$" : ""}${row}`;
|
|
3009
|
+
const consumeRef = (refStart, rewrite = true) => {
|
|
3010
|
+
const first = readWord();
|
|
3011
|
+
const firstCell = parseCellComponent2(first);
|
|
3012
|
+
const firstCol = firstCell ? null : parseColComponent2(first);
|
|
3013
|
+
const firstRow = firstCell || firstCol ? null : parseRowComponent2(first);
|
|
3014
|
+
if (!firstCell && !firstCol && !firstRow) return;
|
|
3015
|
+
let secondCell = null;
|
|
3016
|
+
let secondCol = null;
|
|
3017
|
+
let secondRow = null;
|
|
3018
|
+
let isRange = false;
|
|
3019
|
+
if (formula[i] === ":" && i + 1 < n && isWordChar2(formula.charCodeAt(i + 1))) {
|
|
3020
|
+
const save = i;
|
|
3021
|
+
i++;
|
|
3022
|
+
const w = readWord();
|
|
3023
|
+
if (firstCell) secondCell = parseCellComponent2(w);
|
|
3024
|
+
else if (firstCol) secondCol = parseColComponent2(w);
|
|
3025
|
+
else if (firstRow) secondRow = parseRowComponent2(w);
|
|
3026
|
+
if (secondCell || secondCol || secondRow) isRange = true;
|
|
3027
|
+
else i = save;
|
|
3028
|
+
}
|
|
3029
|
+
if (!isRange && formula[i] === "(") return;
|
|
3030
|
+
if (!rewrite) {
|
|
3031
|
+
if (formula[i] === "#") i++;
|
|
3032
|
+
return;
|
|
3033
|
+
}
|
|
3034
|
+
const dead = () => {
|
|
3035
|
+
if (formula[i] === "#") i++;
|
|
3036
|
+
replace(refStart, "#REF!");
|
|
3037
|
+
hasRefError = true;
|
|
3038
|
+
};
|
|
3039
|
+
if (isRange) {
|
|
3040
|
+
if (firstCell && secondCell) {
|
|
3041
|
+
const r1 = move(firstCell.row, dRow, firstCell.rowAbs, EXCEL_MAX_ROWS);
|
|
3042
|
+
const c1 = move(firstCell.col, dCol, firstCell.colAbs, EXCEL_MAX_COLUMNS);
|
|
3043
|
+
const r2 = move(secondCell.row, dRow, secondCell.rowAbs, EXCEL_MAX_ROWS);
|
|
3044
|
+
const c2 = move(secondCell.col, dCol, secondCell.colAbs, EXCEL_MAX_COLUMNS);
|
|
3045
|
+
if (r1 === null || c1 === null || r2 === null || c2 === null) {
|
|
3046
|
+
dead();
|
|
3047
|
+
return;
|
|
3048
|
+
}
|
|
3049
|
+
replace(refStart, `${emitCell2(firstCell, r1, c1)}:${emitCell2(secondCell, r2, c2)}`);
|
|
3050
|
+
return;
|
|
3051
|
+
}
|
|
3052
|
+
if (firstCol && secondCol) {
|
|
3053
|
+
const c1 = move(firstCol.col, dCol, firstCol.abs, EXCEL_MAX_COLUMNS);
|
|
3054
|
+
const c2 = move(secondCol.col, dCol, secondCol.abs, EXCEL_MAX_COLUMNS);
|
|
3055
|
+
if (c1 === null || c2 === null) {
|
|
3056
|
+
dead();
|
|
3057
|
+
return;
|
|
3058
|
+
}
|
|
3059
|
+
replace(refStart, `${firstCol.abs ? "$" : ""}${columnLetter(c1)}:${secondCol.abs ? "$" : ""}${columnLetter(c2)}`);
|
|
3060
|
+
return;
|
|
3061
|
+
}
|
|
3062
|
+
if (firstRow && secondRow) {
|
|
3063
|
+
const r1 = move(firstRow.row, dRow, firstRow.abs, EXCEL_MAX_ROWS);
|
|
3064
|
+
const r2 = move(secondRow.row, dRow, secondRow.abs, EXCEL_MAX_ROWS);
|
|
3065
|
+
if (r1 === null || r2 === null) {
|
|
3066
|
+
dead();
|
|
3067
|
+
return;
|
|
3068
|
+
}
|
|
3069
|
+
replace(refStart, `${firstRow.abs ? "$" : ""}${r1}:${secondRow.abs ? "$" : ""}${r2}`);
|
|
3070
|
+
return;
|
|
3071
|
+
}
|
|
3072
|
+
return;
|
|
3073
|
+
}
|
|
3074
|
+
if (!firstCell) return;
|
|
3075
|
+
const row = move(firstCell.row, dRow, firstCell.rowAbs, EXCEL_MAX_ROWS);
|
|
3076
|
+
const col = move(firstCell.col, dCol, firstCell.colAbs, EXCEL_MAX_COLUMNS);
|
|
3077
|
+
if (row === null || col === null) {
|
|
3078
|
+
dead();
|
|
3079
|
+
return;
|
|
3080
|
+
}
|
|
3081
|
+
replace(refStart, emitCell2(firstCell, row, col));
|
|
3082
|
+
};
|
|
3083
|
+
while (i < n) {
|
|
3084
|
+
const ch = formula[i];
|
|
3085
|
+
if (ch === '"') {
|
|
3086
|
+
let j = i + 1;
|
|
3087
|
+
while (j < n) {
|
|
3088
|
+
if (formula[j] === '"') {
|
|
3089
|
+
if (formula[j + 1] === '"') {
|
|
3090
|
+
j += 2;
|
|
3091
|
+
continue;
|
|
3092
|
+
}
|
|
3093
|
+
j++;
|
|
3094
|
+
break;
|
|
3095
|
+
}
|
|
3096
|
+
j++;
|
|
3097
|
+
}
|
|
3098
|
+
i = j;
|
|
3099
|
+
continue;
|
|
3100
|
+
}
|
|
3101
|
+
if (ch === "'") {
|
|
3102
|
+
readQuoted();
|
|
3103
|
+
if (formula[i] === "!") {
|
|
3104
|
+
i++;
|
|
3105
|
+
consumeRef(i);
|
|
3106
|
+
}
|
|
3107
|
+
continue;
|
|
3108
|
+
}
|
|
3109
|
+
if (isWordChar2(formula.charCodeAt(i))) {
|
|
3110
|
+
const start = i;
|
|
3111
|
+
readWord();
|
|
3112
|
+
if (formula[i] === "!") {
|
|
3113
|
+
i++;
|
|
3114
|
+
consumeRef(i);
|
|
3115
|
+
continue;
|
|
3116
|
+
}
|
|
3117
|
+
if (formula[i] === ":") {
|
|
3118
|
+
const save = i;
|
|
3119
|
+
i++;
|
|
3120
|
+
const w2 = i < n && isWordChar2(formula.charCodeAt(i)) ? readWord() : "";
|
|
3121
|
+
if (w2 && formula[i] === "!") {
|
|
3122
|
+
i++;
|
|
3123
|
+
consumeRef(i, false);
|
|
3124
|
+
continue;
|
|
3125
|
+
}
|
|
3126
|
+
i = save;
|
|
3127
|
+
}
|
|
3128
|
+
if (formula[i] === "[") {
|
|
3129
|
+
let depth = 0;
|
|
3130
|
+
let j = i;
|
|
3131
|
+
while (j < n) {
|
|
3132
|
+
if (formula[j] === "[") depth++;
|
|
3133
|
+
else if (formula[j] === "]" && --depth === 0) {
|
|
3134
|
+
j++;
|
|
3135
|
+
break;
|
|
3136
|
+
}
|
|
3137
|
+
j++;
|
|
3138
|
+
}
|
|
3139
|
+
i = j;
|
|
3140
|
+
continue;
|
|
3141
|
+
}
|
|
3142
|
+
i = start;
|
|
3143
|
+
consumeRef(start);
|
|
3144
|
+
continue;
|
|
3145
|
+
}
|
|
3146
|
+
i++;
|
|
3147
|
+
}
|
|
3148
|
+
return { text: out + formula.slice(flushed), hasRefError };
|
|
3149
|
+
}
|
|
3150
|
+
|
|
2625
3151
|
// src/model/range.ts
|
|
2626
3152
|
var _sheet, _startRow, _startColumn, _endRow, _endColumn, _address, _Range_instances, forEach_fn, forEachExisting_fn;
|
|
2627
3153
|
var Range = class {
|
|
@@ -2852,7 +3378,7 @@ var PROTECTION_ATTRS = [
|
|
|
2852
3378
|
["allowEditObjects", "objects"],
|
|
2853
3379
|
["allowEditScenarios", "scenarios"]
|
|
2854
3380
|
];
|
|
2855
|
-
var _sharedStrings2, _styles2, _name, _rows, _cols, _mergeCells, _pane, _autoFilter, _dataValidations, _visibility, _drawings, _protection, _protectionCredentials, _hyperlinks, _comments, _commentsDirty, _pendingHyperlinkRels, _preservedTail, _preservedRels, _Worksheet_instances, takeCellData_fn, takeCellDataAt_fn, deleteCellData_fn, applyStructural_fn, shiftRowsAndCells_fn, shiftRows_fn, shiftColumns_fn, maxPopulatedRow_fn, shiftSpillRanges_fn, shiftSpillRange_fn, rewriteFormulas_fn, shiftMerges_fn, shiftAutoFilter_fn, shiftValidations_fn, shiftHyperlinks_fn, shiftComments_fn, shiftColDefs_fn, relIdBase_fn, activeTail_fn, buildTailXml_fn, buildSheetProtectionXml_fn, buildHyperlinksXml_fn, buildColsXml_fn, buildSheetDataXml_fn, buildMergeCellsXml_fn, buildDataValidationsXml_fn, buildSheetViewXml_fn, splitColSpan_fn, insertColDef_fn, findColDefIndex_fn, findColDef_fn, pruneColDefs_fn, getOrCreateRow_fn;
|
|
3381
|
+
var _sharedStrings2, _styles2, _name, _rows, _cols, _mergeCells, _pane, _autoFilter, _dataValidations, _visibility, _drawings, _protection, _protectionCredentials, _hyperlinks, _comments, _commentsDirty, _pendingHyperlinkRels, _preservedTail, _preservedRels, _version2, _usedBoundsMemo, _xmlMemo, _Worksheet_instances, touch_fn2, findCellData_fn, computeUsedBounds_fn, takeCellData_fn, takeCellDataAt_fn, deleteCellData_fn, applyStructural_fn, shiftRowsAndCells_fn, shiftRows_fn, shiftColumns_fn, maxPopulatedRow_fn, shiftSpillRanges_fn, shiftSpillRange_fn, rewriteFormulas_fn, shiftMerges_fn, shiftAutoFilter_fn, shiftValidations_fn, shiftHyperlinks_fn, shiftComments_fn, shiftColDefs_fn, relIdBase_fn, activeTail_fn, buildTailXml_fn, buildSheetProtectionXml_fn, buildHyperlinksXml_fn, resolveSharedFormulas_fn, buildColsXml_fn, buildSheetDataXml_fn, buildMergeCellsXml_fn, buildDataValidationsXml_fn, buildSheetViewXml_fn, splitColSpan_fn, insertColDef_fn, findColDefIndex_fn, findColDef_fn, pruneColDefs_fn, getOrCreateRow_fn;
|
|
2856
3382
|
var Worksheet = class {
|
|
2857
3383
|
/** @internal */
|
|
2858
3384
|
constructor(name, sharedStrings, styles) {
|
|
@@ -2894,10 +3420,26 @@ var Worksheet = class {
|
|
|
2894
3420
|
// comment anchors survive open→save. See io/preserved-parts.ts.
|
|
2895
3421
|
__privateAdd(this, _preservedTail, []);
|
|
2896
3422
|
__privateAdd(this, _preservedRels, []);
|
|
3423
|
+
/**
|
|
3424
|
+
* Mutation counter for this sheet, shared with every {@link Cell} view it
|
|
3425
|
+
* hands out (a Cell writes directly into the stored CellData). Everything
|
|
3426
|
+
* derived from the sheet's contents — the used-range memo, the cached
|
|
3427
|
+
* worksheet XML — is keyed on it, so every mutator must bump it. The test
|
|
3428
|
+
* `worksheet-version.test.ts` walks the public surface to keep that true.
|
|
3429
|
+
*/
|
|
3430
|
+
__privateAdd(this, _version2, { v: 0 });
|
|
3431
|
+
/** Memoised {@link usedBounds}, valid while `#version.v` is unchanged. */
|
|
3432
|
+
__privateAdd(this, _usedBoundsMemo);
|
|
3433
|
+
/** Memoised {@link buildXml} output, valid while `#version.v` is unchanged. */
|
|
3434
|
+
__privateAdd(this, _xmlMemo);
|
|
2897
3435
|
__privateSet(this, _name, name);
|
|
2898
3436
|
__privateSet(this, _sharedStrings2, sharedStrings);
|
|
2899
3437
|
__privateSet(this, _styles2, styles);
|
|
2900
3438
|
}
|
|
3439
|
+
/** @internal Current mutation count — see {@link #version}. */
|
|
3440
|
+
get version() {
|
|
3441
|
+
return __privateGet(this, _version2).v;
|
|
3442
|
+
}
|
|
2901
3443
|
// ── Identity ───────────────────────────────────────────────────────────────
|
|
2902
3444
|
get name() {
|
|
2903
3445
|
return __privateGet(this, _name);
|
|
@@ -2919,10 +3461,11 @@ var Worksheet = class {
|
|
|
2919
3461
|
const rowData = __privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, row);
|
|
2920
3462
|
let cellData = rowData.cells.get(ref);
|
|
2921
3463
|
if (!cellData) {
|
|
3464
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
2922
3465
|
cellData = { reference: ref, styleIndex: 0 };
|
|
2923
3466
|
rowData.cells.set(ref, cellData);
|
|
2924
3467
|
}
|
|
2925
|
-
return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2));
|
|
3468
|
+
return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2), __privateGet(this, _version2));
|
|
2926
3469
|
}
|
|
2927
3470
|
tryGetCell(refOrRow, column) {
|
|
2928
3471
|
let rowData;
|
|
@@ -2938,7 +3481,7 @@ var Worksheet = class {
|
|
|
2938
3481
|
}
|
|
2939
3482
|
const cellData = rowData.cells.get(ref);
|
|
2940
3483
|
if (!cellData) return null;
|
|
2941
|
-
return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2));
|
|
3484
|
+
return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2), __privateGet(this, _version2));
|
|
2942
3485
|
}
|
|
2943
3486
|
cell(refOrRow, column) {
|
|
2944
3487
|
return typeof refOrRow === "string" ? this.getCell(refOrRow) : this.getCell(refOrRow, column);
|
|
@@ -2950,15 +3493,28 @@ var Worksheet = class {
|
|
|
2950
3493
|
}
|
|
2951
3494
|
/** Gets the raw value of a cell (for formula evaluation). */
|
|
2952
3495
|
getCellValue(reference) {
|
|
2953
|
-
|
|
3496
|
+
const cd = __privateMethod(this, _Worksheet_instances, findCellData_fn).call(this, reference);
|
|
3497
|
+
return cd ? readCellValue(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)) : null;
|
|
3498
|
+
}
|
|
3499
|
+
/**
|
|
3500
|
+
* Value of a cell addressed by 1-based row/column. The reference-string
|
|
3501
|
+
* overload has to build (and the callee re-parse) a string per cell; a range
|
|
3502
|
+
* read walking a rectangle already knows the coordinates, and on a real
|
|
3503
|
+
* workbook that is tens of millions of strings per recalculation.
|
|
3504
|
+
*/
|
|
3505
|
+
getCellValueAt(row, column) {
|
|
3506
|
+
const rowData = __privateGet(this, _rows).get(row);
|
|
3507
|
+
if (!rowData) return null;
|
|
3508
|
+
const cd = rowData.cells.get(cellRefFromRowCol(row, column));
|
|
3509
|
+
return cd ? readCellValue(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)) : null;
|
|
2954
3510
|
}
|
|
2955
3511
|
/** Formula text (without leading '=') of a cell, or null if it has none. */
|
|
2956
3512
|
getCellFormula(reference) {
|
|
2957
|
-
return this.
|
|
3513
|
+
return __privateMethod(this, _Worksheet_instances, findCellData_fn).call(this, reference)?.formula ?? null;
|
|
2958
3514
|
}
|
|
2959
3515
|
/** Spill range (e.g. "A1:A3") of a dynamic-array anchor, or null. Supports A1#. */
|
|
2960
3516
|
getSpillRange(reference) {
|
|
2961
|
-
return this.
|
|
3517
|
+
return __privateMethod(this, _Worksheet_instances, findCellData_fn).call(this, reference)?.arrayRef ?? null;
|
|
2962
3518
|
}
|
|
2963
3519
|
/**
|
|
2964
3520
|
* Iterates the populated rows of the sheet in ascending row order, yielding
|
|
@@ -2974,7 +3530,7 @@ var Worksheet = class {
|
|
|
2974
3530
|
*rows() {
|
|
2975
3531
|
const populated = [...__privateGet(this, _rows).values()].filter((row) => row.cells.size > 0).sort((a, b) => a.rowIndex - b.rowIndex);
|
|
2976
3532
|
for (const row of populated) {
|
|
2977
|
-
const cells = [...row.cells.values()].map((cd) => ({ cd, col: columnFromRef(cd.reference, refSplit(cd.reference)) })).sort((a, b) => a.col - b.col).map(({ cd }) => new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)));
|
|
3533
|
+
const cells = [...row.cells.values()].map((cd) => ({ cd, col: columnFromRef(cd.reference, refSplit(cd.reference)) })).sort((a, b) => a.col - b.col).map(({ cd }) => new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2), __privateGet(this, _version2)));
|
|
2978
3534
|
yield { rowIndex: row.rowIndex, cells };
|
|
2979
3535
|
}
|
|
2980
3536
|
}
|
|
@@ -2999,7 +3555,7 @@ var Worksheet = class {
|
|
|
2999
3555
|
for (const cd of row.cells.values()) {
|
|
3000
3556
|
const col = columnFromRef(cd.reference, refSplit(cd.reference));
|
|
3001
3557
|
if (col < startColumn || col > endColumn) continue;
|
|
3002
|
-
action(new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)), row.rowIndex, col);
|
|
3558
|
+
action(new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2), __privateGet(this, _version2)), row.rowIndex, col);
|
|
3003
3559
|
}
|
|
3004
3560
|
};
|
|
3005
3561
|
if (endRow - startRow + 1 <= __privateGet(this, _rows).size) {
|
|
@@ -3018,6 +3574,7 @@ var Worksheet = class {
|
|
|
3018
3574
|
* the public {@link moveCell} has to do. Used by bulk range moves.
|
|
3019
3575
|
*/
|
|
3020
3576
|
moveCellRC(fromRow, fromColumn, toRow, toColumn) {
|
|
3577
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3021
3578
|
if (fromRow === toRow && fromColumn === toColumn) return;
|
|
3022
3579
|
const fromRef = cellRefFromRowCol(fromRow, fromColumn);
|
|
3023
3580
|
const toRef = cellRefFromRowCol(toRow, toColumn);
|
|
@@ -3039,21 +3596,16 @@ var Worksheet = class {
|
|
|
3039
3596
|
* cheaper when a dense shape is not actually required.
|
|
3040
3597
|
*/
|
|
3041
3598
|
get usedBounds() {
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
for (const ref of row.cells.keys()) {
|
|
3048
|
-
const col = columnFromRef(ref, refSplit(ref));
|
|
3049
|
-
if (col > columnCount) columnCount = col;
|
|
3050
|
-
}
|
|
3051
|
-
}
|
|
3052
|
-
return { rowCount, columnCount };
|
|
3599
|
+
const memo = __privateGet(this, _usedBoundsMemo);
|
|
3600
|
+
if (memo && memo.at === __privateGet(this, _version2).v) return memo.value;
|
|
3601
|
+
const value = __privateMethod(this, _Worksheet_instances, computeUsedBounds_fn).call(this);
|
|
3602
|
+
__privateSet(this, _usedBoundsMemo, { at: __privateGet(this, _version2).v, value });
|
|
3603
|
+
return value;
|
|
3053
3604
|
}
|
|
3054
3605
|
// ── Layout ─────────────────────────────────────────────────────────────────
|
|
3055
3606
|
/** Sets the width of a column (1-based index). */
|
|
3056
3607
|
setColumnWidth(columnIndex, width) {
|
|
3608
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3057
3609
|
if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
|
|
3058
3610
|
if (width <= 0) throw new RangeError("Width must be > 0.");
|
|
3059
3611
|
const def = __privateMethod(this, _Worksheet_instances, splitColSpan_fn).call(this, columnIndex) ?? __privateMethod(this, _Worksheet_instances, insertColDef_fn).call(this, { min: columnIndex, max: columnIndex, customWidth: false });
|
|
@@ -3066,6 +3618,7 @@ var Worksheet = class {
|
|
|
3066
3618
|
* {@link getColumnWidth} returns `undefined`. A hidden column stays hidden.
|
|
3067
3619
|
*/
|
|
3068
3620
|
resetColumnWidth(columnIndex) {
|
|
3621
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3069
3622
|
if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
|
|
3070
3623
|
const def = __privateMethod(this, _Worksheet_instances, splitColSpan_fn).call(this, columnIndex);
|
|
3071
3624
|
if (!def) return;
|
|
@@ -3078,6 +3631,7 @@ var Worksheet = class {
|
|
|
3078
3631
|
* file as `<col hidden="1">` and keep any explicit width.
|
|
3079
3632
|
*/
|
|
3080
3633
|
setColumnHidden(columnIndex, hidden = true) {
|
|
3634
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3081
3635
|
if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
|
|
3082
3636
|
const def = __privateMethod(this, _Worksheet_instances, splitColSpan_fn).call(this, columnIndex);
|
|
3083
3637
|
if (def) {
|
|
@@ -3104,6 +3658,7 @@ var Worksheet = class {
|
|
|
3104
3658
|
}
|
|
3105
3659
|
/** Auto-fits column width based on content (approximate). */
|
|
3106
3660
|
autoFitColumn(columnIndex) {
|
|
3661
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3107
3662
|
this.autoFitColumns(columnIndex, columnIndex);
|
|
3108
3663
|
}
|
|
3109
3664
|
/**
|
|
@@ -3111,6 +3666,7 @@ var Worksheet = class {
|
|
|
3111
3666
|
* populated cells, instead of re-scanning the sheet once per column.
|
|
3112
3667
|
*/
|
|
3113
3668
|
autoFitColumns(firstColumn, lastColumn) {
|
|
3669
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3114
3670
|
if (firstColumn < 1) throw new RangeError("Column index must be >= 1.");
|
|
3115
3671
|
if (lastColumn < firstColumn) return;
|
|
3116
3672
|
const maxLen = /* @__PURE__ */ new Map();
|
|
@@ -3125,6 +3681,7 @@ var Worksheet = class {
|
|
|
3125
3681
|
}
|
|
3126
3682
|
/** Sets the height of a row (1-based index). */
|
|
3127
3683
|
setRowHeight(rowIndex, height) {
|
|
3684
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3128
3685
|
if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
|
|
3129
3686
|
if (height <= 0) throw new RangeError("Height must be > 0.");
|
|
3130
3687
|
const row = __privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, rowIndex);
|
|
@@ -3145,6 +3702,7 @@ var Worksheet = class {
|
|
|
3145
3702
|
* {@link getRowHeight} returns `undefined`. A hidden row stays hidden.
|
|
3146
3703
|
*/
|
|
3147
3704
|
resetRowHeight(rowIndex) {
|
|
3705
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3148
3706
|
if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
|
|
3149
3707
|
const row = __privateGet(this, _rows).get(rowIndex);
|
|
3150
3708
|
if (!row) return;
|
|
@@ -3154,6 +3712,7 @@ var Worksheet = class {
|
|
|
3154
3712
|
}
|
|
3155
3713
|
/** Shows or hides a row (1-based index). Hidden rows affect SUBTOTAL(101-111). */
|
|
3156
3714
|
setRowHidden(rowIndex, hidden = true) {
|
|
3715
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3157
3716
|
if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
|
|
3158
3717
|
__privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, rowIndex).hidden = hidden;
|
|
3159
3718
|
}
|
|
@@ -3204,6 +3763,7 @@ var Worksheet = class {
|
|
|
3204
3763
|
* ws.insertRows(3, 2); // pushes row 3 and below down by two rows
|
|
3205
3764
|
*/
|
|
3206
3765
|
insertRows(at, count = 1) {
|
|
3766
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3207
3767
|
return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "row", "insert", at, count);
|
|
3208
3768
|
}
|
|
3209
3769
|
/**
|
|
@@ -3213,6 +3773,7 @@ var Worksheet = class {
|
|
|
3213
3773
|
* for the shifting rules and known limitations.
|
|
3214
3774
|
*/
|
|
3215
3775
|
deleteRows(at, count = 1) {
|
|
3776
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3216
3777
|
return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "row", "delete", at, count);
|
|
3217
3778
|
}
|
|
3218
3779
|
/**
|
|
@@ -3221,6 +3782,7 @@ var Worksheet = class {
|
|
|
3221
3782
|
* shift with the columns.
|
|
3222
3783
|
*/
|
|
3223
3784
|
insertColumns(at, count = 1) {
|
|
3785
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3224
3786
|
return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "col", "insert", at, count);
|
|
3225
3787
|
}
|
|
3226
3788
|
/**
|
|
@@ -3228,6 +3790,7 @@ var Worksheet = class {
|
|
|
3228
3790
|
* Column-level counterpart to {@link deleteRows}.
|
|
3229
3791
|
*/
|
|
3230
3792
|
deleteColumns(at, count = 1) {
|
|
3793
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3231
3794
|
return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "col", "delete", at, count);
|
|
3232
3795
|
}
|
|
3233
3796
|
/**
|
|
@@ -3236,6 +3799,7 @@ var Worksheet = class {
|
|
|
3236
3799
|
* like `'Data'!A5` stay correct. Cell positions here do not change.
|
|
3237
3800
|
*/
|
|
3238
3801
|
applyExternalShift(shift) {
|
|
3802
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3239
3803
|
return { refErrors: __privateMethod(this, _Worksheet_instances, rewriteFormulas_fn).call(this, shift) };
|
|
3240
3804
|
}
|
|
3241
3805
|
/**
|
|
@@ -3248,6 +3812,7 @@ var Worksheet = class {
|
|
|
3248
3812
|
* ws.moveCell('A1', 'C5');
|
|
3249
3813
|
*/
|
|
3250
3814
|
moveCell(from, to) {
|
|
3815
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3251
3816
|
const fromRef = normalizeRef(from);
|
|
3252
3817
|
const toRef = normalizeRef(to);
|
|
3253
3818
|
if (fromRef === toRef) return;
|
|
@@ -3279,6 +3844,7 @@ var Worksheet = class {
|
|
|
3279
3844
|
* ws.copyCell('A1', 'C5');
|
|
3280
3845
|
*/
|
|
3281
3846
|
copyCell(from, to) {
|
|
3847
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3282
3848
|
const fromRef = normalizeRef(from);
|
|
3283
3849
|
const toRef = normalizeRef(to);
|
|
3284
3850
|
if (fromRef === toRef) return;
|
|
@@ -3294,11 +3860,13 @@ var Worksheet = class {
|
|
|
3294
3860
|
}
|
|
3295
3861
|
/** Merges cells in the given range (e.g. "A1:B2"). */
|
|
3296
3862
|
mergeCells(rangeReference) {
|
|
3863
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3297
3864
|
if (!rangeReference) throw new Error("Range reference cannot be empty.");
|
|
3298
3865
|
__privateGet(this, _mergeCells).push({ ref: rangeReference });
|
|
3299
3866
|
}
|
|
3300
3867
|
/** Removes a merge for the given range reference. */
|
|
3301
3868
|
unmergeCells(rangeReference) {
|
|
3869
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3302
3870
|
__privateSet(this, _mergeCells, __privateGet(this, _mergeCells).filter((m) => m.ref !== rangeReference));
|
|
3303
3871
|
}
|
|
3304
3872
|
/**
|
|
@@ -3315,12 +3883,14 @@ var Worksheet = class {
|
|
|
3315
3883
|
* @param column Number of columns to freeze (0 = none)
|
|
3316
3884
|
*/
|
|
3317
3885
|
freezePanes(row, column) {
|
|
3886
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3318
3887
|
if (row < 0) throw new RangeError("Row must be >= 0.");
|
|
3319
3888
|
if (column < 0) throw new RangeError("Column must be >= 0.");
|
|
3320
3889
|
__privateSet(this, _pane, row === 0 && column === 0 ? void 0 : { row, column });
|
|
3321
3890
|
}
|
|
3322
3891
|
/** Sets auto-filter on the given range. */
|
|
3323
3892
|
setAutoFilter(rangeReference) {
|
|
3893
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3324
3894
|
__privateSet(this, _autoFilter, { ref: rangeReference });
|
|
3325
3895
|
}
|
|
3326
3896
|
/**
|
|
@@ -3329,10 +3899,12 @@ var Worksheet = class {
|
|
|
3329
3899
|
* @param listFormula Formula for the dropdown list (e.g. "'Sheet2'!$A$1:$A$10")
|
|
3330
3900
|
*/
|
|
3331
3901
|
addDropdownValidation(cellRange, listFormula) {
|
|
3902
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3332
3903
|
__privateGet(this, _dataValidations).push({ sqref: cellRange, formula: listFormula });
|
|
3333
3904
|
}
|
|
3334
3905
|
/** Sets the visibility state of this worksheet. */
|
|
3335
3906
|
setVisibility(state) {
|
|
3907
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3336
3908
|
__privateSet(this, _visibility, state);
|
|
3337
3909
|
}
|
|
3338
3910
|
get visibility() {
|
|
@@ -3354,10 +3926,12 @@ var Worksheet = class {
|
|
|
3354
3926
|
* ws.protect({ allowSort: true, allowAutoFilter: true });
|
|
3355
3927
|
*/
|
|
3356
3928
|
protect(options) {
|
|
3929
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3357
3930
|
__privateSet(this, _protection, { ...PROTECTION_DEFAULTS, ...options });
|
|
3358
3931
|
}
|
|
3359
3932
|
/** Turns off sheet protection, discarding any password read from a file. */
|
|
3360
3933
|
unprotect() {
|
|
3934
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3361
3935
|
__privateSet(this, _protection, void 0);
|
|
3362
3936
|
__privateSet(this, _protectionCredentials, "");
|
|
3363
3937
|
}
|
|
@@ -3388,6 +3962,7 @@ var Worksheet = class {
|
|
|
3388
3962
|
if (!link.target && !link.location) {
|
|
3389
3963
|
throw new Error("A hyperlink needs a target (external) or a location (in-workbook).");
|
|
3390
3964
|
}
|
|
3965
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3391
3966
|
const normalized = normalizeRangeRef(ref);
|
|
3392
3967
|
__privateGet(this, _hyperlinks).set(normalized, { ref: normalized, ...link });
|
|
3393
3968
|
}
|
|
@@ -3397,6 +3972,7 @@ var Worksheet = class {
|
|
|
3397
3972
|
}
|
|
3398
3973
|
/** Removes the hyperlink on this ref. Returns true if one was there. */
|
|
3399
3974
|
removeHyperlink(ref) {
|
|
3975
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3400
3976
|
return __privateGet(this, _hyperlinks).delete(normalizeRangeRef(ref));
|
|
3401
3977
|
}
|
|
3402
3978
|
/** Every hyperlink on the sheet, including ones read from a file. */
|
|
@@ -3417,6 +3993,7 @@ var Worksheet = class {
|
|
|
3417
3993
|
* ws.setComment('B4', 'Check this figure', { author: 'Bill' });
|
|
3418
3994
|
*/
|
|
3419
3995
|
setComment(ref, text, options) {
|
|
3996
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3420
3997
|
const normalized = normalizeRef(ref);
|
|
3421
3998
|
__privateGet(this, _comments).set(normalized, {
|
|
3422
3999
|
ref: normalized,
|
|
@@ -3431,6 +4008,7 @@ var Worksheet = class {
|
|
|
3431
4008
|
}
|
|
3432
4009
|
/** Removes the comment on a cell. Returns true if one was there. */
|
|
3433
4010
|
removeComment(ref) {
|
|
4011
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3434
4012
|
const removed = __privateGet(this, _comments).delete(normalizeRef(ref));
|
|
3435
4013
|
if (removed) __privateSet(this, _commentsDirty, true);
|
|
3436
4014
|
return removed;
|
|
@@ -3458,6 +4036,7 @@ var Worksheet = class {
|
|
|
3458
4036
|
* save pipeline turns registered providers into valid OOXML drawing/chart parts.
|
|
3459
4037
|
*/
|
|
3460
4038
|
addDrawingProvider(provider) {
|
|
4039
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3461
4040
|
__privateGet(this, _drawings).push(provider);
|
|
3462
4041
|
}
|
|
3463
4042
|
/**
|
|
@@ -3469,6 +4048,7 @@ var Worksheet = class {
|
|
|
3469
4048
|
* deliberately drop a chart that came from the source file.
|
|
3470
4049
|
*/
|
|
3471
4050
|
clearDrawings() {
|
|
4051
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3472
4052
|
__privateSet(this, _drawings, []);
|
|
3473
4053
|
__privateSet(this, _preservedTail, __privateGet(this, _preservedTail).filter((t) => t.tag !== "drawing"));
|
|
3474
4054
|
}
|
|
@@ -3535,6 +4115,7 @@ var Worksheet = class {
|
|
|
3535
4115
|
}
|
|
3536
4116
|
/** @internal Captures round-trip state from the source package. */
|
|
3537
4117
|
loadPreservedState(worksheetXml, relationships) {
|
|
4118
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3538
4119
|
__privateSet(this, _preservedRels, relationships.filter((r) => r.type !== REL_TYPES.hyperlink));
|
|
3539
4120
|
const known = new Set(__privateGet(this, _preservedRels).map((r) => r.id));
|
|
3540
4121
|
__privateSet(this, _preservedTail, parseTailElements(worksheetXml).filter((t) => known.has(t.relId)));
|
|
@@ -3575,6 +4156,8 @@ var Worksheet = class {
|
|
|
3575
4156
|
// ── XML serialisation ──────────────────────────────────────────────────────
|
|
3576
4157
|
/** Builds the xl/worksheets/sheetN.xml string. */
|
|
3577
4158
|
buildXml() {
|
|
4159
|
+
const memo = __privateGet(this, _xmlMemo);
|
|
4160
|
+
if (memo && memo.at === __privateGet(this, _version2).v) return memo.xml;
|
|
3578
4161
|
const rels = this.generatedRels();
|
|
3579
4162
|
const sheetViewXml = __privateMethod(this, _Worksheet_instances, buildSheetViewXml_fn).call(this);
|
|
3580
4163
|
const colsXml = __privateMethod(this, _Worksheet_instances, buildColsXml_fn).call(this);
|
|
@@ -3585,15 +4168,18 @@ var Worksheet = class {
|
|
|
3585
4168
|
const validXml = __privateMethod(this, _Worksheet_instances, buildDataValidationsXml_fn).call(this);
|
|
3586
4169
|
const linksXml = __privateMethod(this, _Worksheet_instances, buildHyperlinksXml_fn).call(this, rels.hyperlinks);
|
|
3587
4170
|
const tailXml = __privateMethod(this, _Worksheet_instances, buildTailXml_fn).call(this, rels);
|
|
3588
|
-
|
|
4171
|
+
const xml2 = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3589
4172
|
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
|
3590
4173
|
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
3591
4174
|
<sheetViews>${sheetViewXml}</sheetViews>
|
|
3592
4175
|
${colsXml}${sheetDataXml}${protectionXml}${filterXml}${mergeXml}${validXml}${linksXml}${tailXml}
|
|
3593
4176
|
</worksheet>`;
|
|
4177
|
+
__privateSet(this, _xmlMemo, { at: __privateGet(this, _version2).v, xml: xml2 });
|
|
4178
|
+
return xml2;
|
|
3594
4179
|
}
|
|
3595
4180
|
/** Parses xl/worksheets/sheetN.xml and loads cells into memory. */
|
|
3596
4181
|
loadFromXml(xml2) {
|
|
4182
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3597
4183
|
__privateGet(this, _rows).clear();
|
|
3598
4184
|
__privateSet(this, _cols, []);
|
|
3599
4185
|
__privateSet(this, _mergeCells, []);
|
|
@@ -3615,6 +4201,8 @@ ${colsXml}${sheetDataXml}${protectionXml}${filterXml}${mergeXml}${validXml}${lin
|
|
|
3615
4201
|
hidden: getAttr(colEl, "hidden") === "1"
|
|
3616
4202
|
});
|
|
3617
4203
|
}
|
|
4204
|
+
const sharedMasters = /* @__PURE__ */ new Map();
|
|
4205
|
+
const sharedInstances = [];
|
|
3618
4206
|
for (const rowEl of getElementsByTagName(root, "row")) {
|
|
3619
4207
|
const rowIndex = parseInt(getAttr(rowEl, "r") ?? "0", 10);
|
|
3620
4208
|
if (rowIndex < 1) continue;
|
|
@@ -3635,9 +4223,17 @@ ${colsXml}${sheetDataXml}${protectionXml}${filterXml}${mergeXml}${validXml}${lin
|
|
|
3635
4223
|
ref.toUpperCase()
|
|
3636
4224
|
);
|
|
3637
4225
|
rowData.cells.set(ref.toUpperCase(), cellData);
|
|
4226
|
+
if (cellData.sharedIndex !== void 0) {
|
|
4227
|
+
if (cellData.formula !== void 0) {
|
|
4228
|
+
sharedMasters.set(cellData.sharedIndex, { formula: cellData.formula, ref: cellData.reference });
|
|
4229
|
+
} else {
|
|
4230
|
+
sharedInstances.push(cellData);
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
3638
4233
|
}
|
|
3639
4234
|
__privateGet(this, _rows).set(rowIndex, rowData);
|
|
3640
4235
|
}
|
|
4236
|
+
__privateMethod(this, _Worksheet_instances, resolveSharedFormulas_fn).call(this, sharedMasters, sharedInstances);
|
|
3641
4237
|
for (const mcEl of getElementsByTagName(root, "mergeCell")) {
|
|
3642
4238
|
const ref = getAttr(mcEl, "ref");
|
|
3643
4239
|
if (ref) __privateGet(this, _mergeCells).push({ ref });
|
|
@@ -3693,6 +4289,7 @@ ${colsXml}${sheetDataXml}${protectionXml}${filterXml}${mergeXml}${validXml}${lin
|
|
|
3693
4289
|
}
|
|
3694
4290
|
/** @internal Parses an `xl/comments*.xml` part into this sheet's comments. */
|
|
3695
4291
|
loadCommentsXml(xml2) {
|
|
4292
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3696
4293
|
const root = parseXml(xml2);
|
|
3697
4294
|
const authors = getElementsByTagName(root, "author").map((a) => a.textContent);
|
|
3698
4295
|
for (const cEl of getElementsByTagName(root, "comment")) {
|
|
@@ -3752,7 +4349,47 @@ _commentsDirty = new WeakMap();
|
|
|
3752
4349
|
_pendingHyperlinkRels = new WeakMap();
|
|
3753
4350
|
_preservedTail = new WeakMap();
|
|
3754
4351
|
_preservedRels = new WeakMap();
|
|
4352
|
+
_version2 = new WeakMap();
|
|
4353
|
+
_usedBoundsMemo = new WeakMap();
|
|
4354
|
+
_xmlMemo = new WeakMap();
|
|
3755
4355
|
_Worksheet_instances = new WeakSet();
|
|
4356
|
+
/** Records a change to this sheet's contents or layout. */
|
|
4357
|
+
touch_fn2 = function() {
|
|
4358
|
+
__privateGet(this, _version2).v++;
|
|
4359
|
+
};
|
|
4360
|
+
/**
|
|
4361
|
+
* The stored record for a cell, without creating it — the read paths below
|
|
4362
|
+
* work on this directly instead of wrapping it in a {@link Cell}. Formula
|
|
4363
|
+
* evaluation reads far more cells than it writes, and one wrapper per read is
|
|
4364
|
+
* pure garbage.
|
|
4365
|
+
*/
|
|
4366
|
+
findCellData_fn = function(reference) {
|
|
4367
|
+
const ref = normalizeRefFast(reference);
|
|
4368
|
+
return __privateGet(this, _rows).get(parseCellRef(ref).row)?.cells.get(ref);
|
|
4369
|
+
};
|
|
4370
|
+
/**
|
|
4371
|
+
* The extent, computed without touching the memo.
|
|
4372
|
+
*
|
|
4373
|
+
* Internal callers must use this. A mutator bumps the version once, at its
|
|
4374
|
+
* start, and then does its work; if it read the memoised getter part-way
|
|
4375
|
+
* through, the half-finished extent would be cached against the *new* version
|
|
4376
|
+
* and served as current for the rest of that version's life. That is precisely
|
|
4377
|
+
* how `insertColumns` came to report the extent it had before the insert —
|
|
4378
|
+
* caught by the round-trip fuzz test, not by any of the hand-written ones.
|
|
4379
|
+
*/
|
|
4380
|
+
computeUsedBounds_fn = function() {
|
|
4381
|
+
let rowCount = 0;
|
|
4382
|
+
let columnCount = 0;
|
|
4383
|
+
for (const row of __privateGet(this, _rows).values()) {
|
|
4384
|
+
if (row.cells.size === 0) continue;
|
|
4385
|
+
if (row.rowIndex > rowCount) rowCount = row.rowIndex;
|
|
4386
|
+
for (const ref of row.cells.keys()) {
|
|
4387
|
+
const col = columnFromRef(ref, refSplit(ref));
|
|
4388
|
+
if (col > columnCount) columnCount = col;
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
return { rowCount, columnCount };
|
|
4392
|
+
};
|
|
3756
4393
|
/** Removes and returns a cell's data record, pruning an emptied bare row. */
|
|
3757
4394
|
takeCellData_fn = function(ref) {
|
|
3758
4395
|
return __privateMethod(this, _Worksheet_instances, takeCellDataAt_fn).call(this, parseCellRef(ref).row, ref);
|
|
@@ -3778,7 +4415,7 @@ applyStructural_fn = function(dim, kind, at, count) {
|
|
|
3778
4415
|
const shift = { dim, kind, at, count, sheetName: __privateGet(this, _name) };
|
|
3779
4416
|
if (kind === "insert") {
|
|
3780
4417
|
const limit = dim === "row" ? EXCEL_MAX_ROWS : EXCEL_MAX_COLUMNS;
|
|
3781
|
-
const used = dim === "row" ? __privateMethod(this, _Worksheet_instances, maxPopulatedRow_fn).call(this) : this.
|
|
4418
|
+
const used = dim === "row" ? __privateMethod(this, _Worksheet_instances, maxPopulatedRow_fn).call(this) : __privateMethod(this, _Worksheet_instances, computeUsedBounds_fn).call(this).columnCount;
|
|
3782
4419
|
if (used >= at && used + count > limit) {
|
|
3783
4420
|
throw new RangeError(`Insert would push populated cells past the sheet limit of ${limit}.`);
|
|
3784
4421
|
}
|
|
@@ -3994,6 +4631,34 @@ buildHyperlinksXml_fn = function(relIds) {
|
|
|
3994
4631
|
}).join("");
|
|
3995
4632
|
return `<hyperlinks>${inner}</hyperlinks>`;
|
|
3996
4633
|
};
|
|
4634
|
+
/**
|
|
4635
|
+
* Materialises the formula of every shared-formula instance cell.
|
|
4636
|
+
*
|
|
4637
|
+
* Excel writes a fill-down once: the group's master carries the text, the rest
|
|
4638
|
+
* carry `<f t="shared" si="n"/>` and are understood to hold the master's
|
|
4639
|
+
* formula with its relative references moved by the offset between the cells.
|
|
4640
|
+
* Read literally — the way this used to be read — an instance cell has no
|
|
4641
|
+
* formula at all, and the next save turns it into the constant that happened to
|
|
4642
|
+
* be cached in `<v>`. On a real workbook that is tens of thousands of formulas
|
|
4643
|
+
* quietly replaced by numbers.
|
|
4644
|
+
*
|
|
4645
|
+
* Each cell ends up owning its own text, so nothing downstream (evaluation,
|
|
4646
|
+
* structural edits, serialisation) needs to know the group ever existed.
|
|
4647
|
+
*/
|
|
4648
|
+
resolveSharedFormulas_fn = function(masters, instances) {
|
|
4649
|
+
if (instances.length === 0) return;
|
|
4650
|
+
for (const cd of instances) {
|
|
4651
|
+
const master = masters.get(cd.sharedIndex);
|
|
4652
|
+
if (!master) continue;
|
|
4653
|
+
const from = parseCellRef(master.ref);
|
|
4654
|
+
const to = parseCellRef(cd.reference);
|
|
4655
|
+
cd.formula = translateFormulaRefs(
|
|
4656
|
+
master.formula,
|
|
4657
|
+
to.row - from.row,
|
|
4658
|
+
to.column - from.column
|
|
4659
|
+
).text;
|
|
4660
|
+
}
|
|
4661
|
+
};
|
|
3997
4662
|
// ── Private XML builders ───────────────────────────────────────────────────
|
|
3998
4663
|
buildColsXml_fn = function() {
|
|
3999
4664
|
if (__privateGet(this, _cols).length === 0) return "";
|
|
@@ -4011,7 +4676,7 @@ buildSheetDataXml_fn = function() {
|
|
|
4011
4676
|
const ht = row.height !== void 0 && row.customHeight ? ` ht="${row.height}" customHeight="1"` : "";
|
|
4012
4677
|
const hidden = row.hidden ? ' hidden="1"' : "";
|
|
4013
4678
|
const sortedCells = [...row.cells.values()].map((cd) => ({ cd, col: columnFromRef(cd.reference, refSplit(cd.reference)) })).sort((a, b) => a.col - b.col);
|
|
4014
|
-
const cellsXml = sortedCells.map(({ cd }) =>
|
|
4679
|
+
const cellsXml = sortedCells.map(({ cd }) => buildCellXml(cd)).filter(Boolean).join("");
|
|
4015
4680
|
if (!cellsXml && !ht && !hidden) return "";
|
|
4016
4681
|
return `<row r="${row.rowIndex}"${ht}${hidden}>${cellsXml}</row>`;
|
|
4017
4682
|
}).filter(Boolean).join("");
|
|
@@ -4225,7 +4890,7 @@ indexOf_fn = function(name, scope) {
|
|
|
4225
4890
|
};
|
|
4226
4891
|
|
|
4227
4892
|
// src/model/workbook.ts
|
|
4228
|
-
var _sharedStrings3, _styles3, _sheets, _definedNames, _closed, _preservedParts, _preservedContentTypes, _orphanedTargets, _Workbook_instances, structuralEdit_fn, assertOpen_fn, seedPreservedParts_fn, preservedContentTypeDeclarations_fn, buildZipEntries_fn;
|
|
4893
|
+
var _sharedStrings3, _styles3, _sheets, _definedNames, _closed, _calcChain, _calcChainFingerprint, _deflateCache, _preservedParts, _preservedContentTypes, _orphanedTargets, _Workbook_instances, structuralEdit_fn, sheetFingerprint_fn, calcChainStillValid_fn, assertOpen_fn, seedPreservedParts_fn, preservedContentTypeDeclarations_fn, buildZipEntries_fn;
|
|
4229
4894
|
var _Workbook = class _Workbook {
|
|
4230
4895
|
constructor(sharedStrings, styles) {
|
|
4231
4896
|
__privateAdd(this, _Workbook_instances);
|
|
@@ -4234,6 +4899,24 @@ var _Workbook = class _Workbook {
|
|
|
4234
4899
|
__privateAdd(this, _sheets, []);
|
|
4235
4900
|
__privateAdd(this, _definedNames, new DefinedNameManager());
|
|
4236
4901
|
__privateAdd(this, _closed, false);
|
|
4902
|
+
/**
|
|
4903
|
+
* The calculation order Excel stored in `xl/calcChain.xml`, in file order, or
|
|
4904
|
+
* undefined when the source had no such part. Read-only to consumers: it is a
|
|
4905
|
+
* record of what the source file said, not a live view of the model.
|
|
4906
|
+
*/
|
|
4907
|
+
__privateAdd(this, _calcChain);
|
|
4908
|
+
/**
|
|
4909
|
+
* Fingerprint of the sheet set taken right after the calc chain was parsed.
|
|
4910
|
+
* The chain is only written back out when this still matches — see
|
|
4911
|
+
* {@link #calcChainStillValid}.
|
|
4912
|
+
*/
|
|
4913
|
+
__privateAdd(this, _calcChainFingerprint);
|
|
4914
|
+
/**
|
|
4915
|
+
* Compressed form of each package part from the last save. Compression is where
|
|
4916
|
+
* a save spends its time, and an autosave changes one part, so the rest are
|
|
4917
|
+
* reused verbatim. Keyed on content, so a part rebuilt identically still hits.
|
|
4918
|
+
*/
|
|
4919
|
+
__privateAdd(this, _deflateCache, createDeflateCache());
|
|
4237
4920
|
/** Parts carried over verbatim from the file this workbook was opened from. */
|
|
4238
4921
|
__privateAdd(this, _preservedParts, /* @__PURE__ */ new Map());
|
|
4239
4922
|
__privateAdd(this, _preservedContentTypes);
|
|
@@ -4258,6 +4941,7 @@ var _Workbook = class _Workbook {
|
|
|
4258
4941
|
* Mirrors Workbook.Open(stream) in C#.
|
|
4259
4942
|
*/
|
|
4260
4943
|
static fromBuffer(buffer) {
|
|
4944
|
+
var _a;
|
|
4261
4945
|
const entries = unzipXlsx(buffer);
|
|
4262
4946
|
const ss = new SharedStringManager();
|
|
4263
4947
|
const sm = new StyleManager();
|
|
@@ -4294,6 +4978,11 @@ var _Workbook = class _Workbook {
|
|
|
4294
4978
|
}
|
|
4295
4979
|
__privateGet(wb, _sheets).push({ worksheet: ws, sheetId, relId });
|
|
4296
4980
|
}
|
|
4981
|
+
const calcChainXml = readXmlEntry(entries, CALC_CHAIN_PATH);
|
|
4982
|
+
if (calcChainXml) {
|
|
4983
|
+
__privateSet(wb, _calcChain, parseCalcChain(calcChainXml, __privateGet(wb, _sheets)));
|
|
4984
|
+
__privateSet(wb, _calcChainFingerprint, __privateMethod(_a = wb, _Workbook_instances, sheetFingerprint_fn).call(_a));
|
|
4985
|
+
}
|
|
4297
4986
|
__privateSet(wb, _preservedParts, collectPreservedParts(entries));
|
|
4298
4987
|
const ctXml = readXmlEntry(entries, "[Content_Types].xml");
|
|
4299
4988
|
if (ctXml) __privateSet(wb, _preservedContentTypes, parseContentTypes(ctXml));
|
|
@@ -4340,8 +5029,8 @@ var _Workbook = class _Workbook {
|
|
|
4340
5029
|
if (__privateGet(this, _sheets).some((e) => e.worksheet.name === name)) {
|
|
4341
5030
|
throw new Error(`Worksheet "${name}" already exists.`);
|
|
4342
5031
|
}
|
|
4343
|
-
const sheetId = __privateGet(this, _sheets).
|
|
4344
|
-
const relId = `rId${
|
|
5032
|
+
const sheetId = __privateGet(this, _sheets).reduce((max, e) => Math.max(max, e.sheetId), 0) + 1;
|
|
5033
|
+
const relId = `rId${__privateGet(this, _sheets).length + 1}`;
|
|
4345
5034
|
const ws = new Worksheet(name, __privateGet(this, _sharedStrings3), __privateGet(this, _styles3));
|
|
4346
5035
|
__privateGet(this, _sheets).push({ worksheet: ws, sheetId, relId });
|
|
4347
5036
|
return ws;
|
|
@@ -4450,7 +5139,21 @@ var _Workbook = class _Workbook {
|
|
|
4450
5139
|
saveAsBuffer() {
|
|
4451
5140
|
__privateMethod(this, _Workbook_instances, assertOpen_fn).call(this);
|
|
4452
5141
|
const entries = __privateMethod(this, _Workbook_instances, buildZipEntries_fn).call(this);
|
|
4453
|
-
return
|
|
5142
|
+
return zipXlsxCached(entries, __privateGet(this, _deflateCache));
|
|
5143
|
+
}
|
|
5144
|
+
/**
|
|
5145
|
+
* Same output as {@link saveAsBuffer}, without blocking the calling thread on
|
|
5146
|
+
* compression — for an editor that autosaves, this is the difference between a
|
|
5147
|
+
* visible stall on every save and none.
|
|
5148
|
+
*
|
|
5149
|
+
* The XML for sheets that have not changed since the last save is reused, so a
|
|
5150
|
+
* save after a one-cell edit re-serialises one sheet rather than all of them.
|
|
5151
|
+
* That reuse applies to the synchronous path too; only the compression differs.
|
|
5152
|
+
*/
|
|
5153
|
+
async saveAsBufferAsync() {
|
|
5154
|
+
__privateMethod(this, _Workbook_instances, assertOpen_fn).call(this);
|
|
5155
|
+
const entries = __privateMethod(this, _Workbook_instances, buildZipEntries_fn).call(this);
|
|
5156
|
+
return zipXlsxCachedAsync(entries, __privateGet(this, _deflateCache));
|
|
4454
5157
|
}
|
|
4455
5158
|
/**
|
|
4456
5159
|
* Saves the workbook to a file (Node.js only).
|
|
@@ -4458,7 +5161,7 @@ var _Workbook = class _Workbook {
|
|
|
4458
5161
|
*/
|
|
4459
5162
|
async saveToFile(filePath) {
|
|
4460
5163
|
__privateMethod(this, _Workbook_instances, assertOpen_fn).call(this);
|
|
4461
|
-
const buffer = this.
|
|
5164
|
+
const buffer = await this.saveAsBufferAsync();
|
|
4462
5165
|
const fs = await import('fs/promises');
|
|
4463
5166
|
const path = await import('path');
|
|
4464
5167
|
const dir = path.dirname(filePath);
|
|
@@ -4474,12 +5177,29 @@ var _Workbook = class _Workbook {
|
|
|
4474
5177
|
[Symbol.dispose]() {
|
|
4475
5178
|
this.close();
|
|
4476
5179
|
}
|
|
5180
|
+
// ── Calculation order ──────────────────────────────────────────────────────
|
|
5181
|
+
/**
|
|
5182
|
+
* The calculation order Excel recorded for this workbook, oldest-first as
|
|
5183
|
+
* stored in `xl/calcChain.xml`, or an empty array when the source file had
|
|
5184
|
+
* none (a freshly created workbook, or one saved by a tool that omits it).
|
|
5185
|
+
*
|
|
5186
|
+
* Excel has already done the topological sort that produced this order, so
|
|
5187
|
+
* evaluating in it avoids repeating that work. Treat it as a hint, not a
|
|
5188
|
+
* guarantee: it reflects the file as opened, and edits made since are not
|
|
5189
|
+
* reflected here.
|
|
5190
|
+
*/
|
|
5191
|
+
get calcChain() {
|
|
5192
|
+
return __privateGet(this, _calcChain) ?? EMPTY_CALC_CHAIN;
|
|
5193
|
+
}
|
|
4477
5194
|
};
|
|
4478
5195
|
_sharedStrings3 = new WeakMap();
|
|
4479
5196
|
_styles3 = new WeakMap();
|
|
4480
5197
|
_sheets = new WeakMap();
|
|
4481
5198
|
_definedNames = new WeakMap();
|
|
4482
5199
|
_closed = new WeakMap();
|
|
5200
|
+
_calcChain = new WeakMap();
|
|
5201
|
+
_calcChainFingerprint = new WeakMap();
|
|
5202
|
+
_deflateCache = new WeakMap();
|
|
4483
5203
|
_preservedParts = new WeakMap();
|
|
4484
5204
|
_preservedContentTypes = new WeakMap();
|
|
4485
5205
|
_orphanedTargets = new WeakMap();
|
|
@@ -4501,6 +5221,28 @@ structuralEdit_fn = function(sheetName, dim, kind, at, count) {
|
|
|
4501
5221
|
}
|
|
4502
5222
|
return { refErrors };
|
|
4503
5223
|
};
|
|
5224
|
+
/**
|
|
5225
|
+
* Identity of the sheet set: names, durable ids and per-sheet mutation counts.
|
|
5226
|
+
* The calc chain is a statement about exactly this set — which cells hold
|
|
5227
|
+
* formulas, on which sheet, under which id — so any change to it invalidates
|
|
5228
|
+
* the chain wholesale.
|
|
5229
|
+
*/
|
|
5230
|
+
sheetFingerprint_fn = function() {
|
|
5231
|
+
return __privateGet(this, _sheets).map((e) => `${e.sheetId}:${e.worksheet.name}:${e.worksheet.version}`).join("|");
|
|
5232
|
+
};
|
|
5233
|
+
/**
|
|
5234
|
+
* Whether the parsed calc chain may be written back out.
|
|
5235
|
+
*
|
|
5236
|
+
* Deliberately conservative: any edit at all (a value, a formula, a sheet
|
|
5237
|
+
* added or removed) and the chain is dropped instead of patched. An absent
|
|
5238
|
+
* calcChain is a documented no-op — Excel rebuilds it on open — whereas a
|
|
5239
|
+
* chain that disagrees with the sheets is the failure mode this whole change
|
|
5240
|
+
* exists to remove. Rebuilding a correct chain would mean redoing Excel's
|
|
5241
|
+
* dependency analysis, which belongs in the formula package, not here.
|
|
5242
|
+
*/
|
|
5243
|
+
calcChainStillValid_fn = function() {
|
|
5244
|
+
return __privateGet(this, _calcChain) !== void 0 && __privateGet(this, _calcChain).length > 0 && __privateGet(this, _calcChainFingerprint) === __privateMethod(this, _Workbook_instances, sheetFingerprint_fn).call(this);
|
|
5245
|
+
};
|
|
4504
5246
|
// ── Private helpers ────────────────────────────────────────────────────────
|
|
4505
5247
|
assertOpen_fn = function() {
|
|
4506
5248
|
if (__privateGet(this, _closed)) throw new Error("Workbook has been closed.");
|
|
@@ -4561,7 +5303,7 @@ buildZipEntries_fn = function() {
|
|
|
4561
5303
|
entries.set("_rels/.rels", ROOT_RELS);
|
|
4562
5304
|
const sheetMetas = __privateGet(this, _sheets).map((e, i) => ({
|
|
4563
5305
|
name: e.worksheet.name,
|
|
4564
|
-
sheetId:
|
|
5306
|
+
sheetId: e.sheetId,
|
|
4565
5307
|
relationshipId: `rId${i + 1}`,
|
|
4566
5308
|
state: e.worksheet.visibility !== "visible" ? e.worksheet.visibility : void 0
|
|
4567
5309
|
}));
|
|
@@ -4574,7 +5316,15 @@ buildZipEntries_fn = function() {
|
|
|
4574
5316
|
return { name: dn.name, refersTo: dn.refersTo, localSheetId };
|
|
4575
5317
|
});
|
|
4576
5318
|
entries.set("xl/workbook.xml", buildWorkbookXml(sheetMetas, definedNameMetas));
|
|
4577
|
-
|
|
5319
|
+
const keepCalcChain = __privateMethod(this, _Workbook_instances, calcChainStillValid_fn).call(this);
|
|
5320
|
+
if (keepCalcChain) {
|
|
5321
|
+
const idBySheet = new Map(__privateGet(this, _sheets).map((e) => [e.worksheet.name, e.sheetId]));
|
|
5322
|
+
entries.set(CALC_CHAIN_PATH, buildCalcChainXml(__privateGet(this, _calcChain), idBySheet));
|
|
5323
|
+
} else {
|
|
5324
|
+
entries.delete(CALC_CHAIN_PATH);
|
|
5325
|
+
preservedPaths.delete(CALC_CHAIN_PATH);
|
|
5326
|
+
}
|
|
5327
|
+
entries.set("xl/_rels/workbook.xml.rels", buildWorkbookRels(sheetCount, keepCalcChain));
|
|
4578
5328
|
entries.set("xl/styles.xml", __privateGet(this, _styles3).buildStylesXml());
|
|
4579
5329
|
entries.set("xl/sharedStrings.xml", __privateGet(this, _sharedStrings3).buildXml());
|
|
4580
5330
|
for (let i = 0; i < __privateGet(this, _sheets).length; i++) {
|
|
@@ -4582,6 +5332,9 @@ buildZipEntries_fn = function() {
|
|
|
4582
5332
|
}
|
|
4583
5333
|
const extraOverrides = [];
|
|
4584
5334
|
const extraDefaults = [];
|
|
5335
|
+
if (keepCalcChain) {
|
|
5336
|
+
extraOverrides.push({ partName: `/${CALC_CHAIN_PATH}`, contentType: CONTENT_TYPES.calcChain });
|
|
5337
|
+
}
|
|
4585
5338
|
let drawingCounter = 0;
|
|
4586
5339
|
let partCounter = 0;
|
|
4587
5340
|
let commentsCounter = 0;
|
|
@@ -4657,6 +5410,42 @@ buildZipEntries_fn = function() {
|
|
|
4657
5410
|
return entries;
|
|
4658
5411
|
};
|
|
4659
5412
|
var Workbook = _Workbook;
|
|
5413
|
+
var CALC_CHAIN_PATH = "xl/calcChain.xml";
|
|
5414
|
+
var EMPTY_CALC_CHAIN = [];
|
|
5415
|
+
function parseCalcChain(xml2, sheets) {
|
|
5416
|
+
const nameById = new Map(sheets.map((e) => [e.sheetId, e.worksheet.name]));
|
|
5417
|
+
const out = [];
|
|
5418
|
+
let currentId;
|
|
5419
|
+
try {
|
|
5420
|
+
const root = parseXml(xml2);
|
|
5421
|
+
for (const el of getElementsByTagName(root, "c")) {
|
|
5422
|
+
const ref = getAttr(el, "r");
|
|
5423
|
+
if (!ref) continue;
|
|
5424
|
+
const iAttr = getAttr(el, "i");
|
|
5425
|
+
if (iAttr !== void 0) {
|
|
5426
|
+
const parsed = parseInt(iAttr, 10);
|
|
5427
|
+
if (!Number.isNaN(parsed)) currentId = parsed;
|
|
5428
|
+
}
|
|
5429
|
+
if (currentId === void 0) continue;
|
|
5430
|
+
const sheet = nameById.get(currentId);
|
|
5431
|
+
if (sheet === void 0) continue;
|
|
5432
|
+
out.push({ sheet, ref: ref.toUpperCase() });
|
|
5433
|
+
}
|
|
5434
|
+
} catch {
|
|
5435
|
+
return [];
|
|
5436
|
+
}
|
|
5437
|
+
return out;
|
|
5438
|
+
}
|
|
5439
|
+
function buildCalcChainXml(chain, idBySheet) {
|
|
5440
|
+
const cells = [];
|
|
5441
|
+
for (const { sheet, ref } of chain) {
|
|
5442
|
+
const id = idBySheet.get(sheet);
|
|
5443
|
+
if (id === void 0) continue;
|
|
5444
|
+
cells.push(`<c r="${ref}" i="${id}"/>`);
|
|
5445
|
+
}
|
|
5446
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
5447
|
+
<calcChain xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">${cells.join("")}</calcChain>`;
|
|
5448
|
+
}
|
|
4660
5449
|
function resolveWorksheetPath(relsXml, relId) {
|
|
4661
5450
|
if (!relsXml) return void 0;
|
|
4662
5451
|
try {
|