@cj-tech-master/excelts 1.4.5 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/excelts.iife.js +454 -159
- package/dist/browser/excelts.iife.js.map +1 -1
- package/dist/browser/excelts.iife.min.js +28 -28
- package/dist/cjs/doc/anchor.js +25 -11
- package/dist/cjs/doc/cell.js +75 -43
- package/dist/cjs/doc/column.js +74 -22
- package/dist/cjs/doc/defined-names.js +53 -7
- package/dist/cjs/doc/image.js +11 -8
- package/dist/cjs/doc/range.js +64 -28
- package/dist/cjs/doc/row.js +72 -31
- package/dist/cjs/doc/table.js +3 -5
- package/dist/cjs/doc/workbook.js +30 -6
- package/dist/cjs/doc/worksheet.js +165 -41
- package/dist/cjs/utils/sheet-utils.js +3 -1
- package/dist/cjs/utils/unzip/extract.js +30 -82
- package/dist/cjs/utils/unzip/index.js +18 -2
- package/dist/cjs/utils/unzip/zip-parser.js +458 -0
- package/dist/esm/doc/anchor.js +25 -11
- package/dist/esm/doc/cell.js +75 -43
- package/dist/esm/doc/column.js +74 -22
- package/dist/esm/doc/defined-names.js +53 -7
- package/dist/esm/doc/image.js +11 -8
- package/dist/esm/doc/range.js +64 -28
- package/dist/esm/doc/row.js +72 -31
- package/dist/esm/doc/table.js +3 -5
- package/dist/esm/doc/workbook.js +30 -6
- package/dist/esm/doc/worksheet.js +165 -41
- package/dist/esm/utils/sheet-utils.js +3 -1
- package/dist/esm/utils/unzip/extract.js +28 -82
- package/dist/esm/utils/unzip/index.js +17 -2
- package/dist/esm/utils/unzip/zip-parser.js +451 -0
- package/dist/types/doc/anchor.d.ts +14 -7
- package/dist/types/doc/cell.d.ts +78 -37
- package/dist/types/doc/column.d.ts +72 -36
- package/dist/types/doc/defined-names.d.ts +11 -8
- package/dist/types/doc/image.d.ts +29 -12
- package/dist/types/doc/pivot-table.d.ts +1 -1
- package/dist/types/doc/range.d.ts +15 -4
- package/dist/types/doc/row.d.ts +78 -40
- package/dist/types/doc/table.d.ts +21 -36
- package/dist/types/doc/workbook.d.ts +54 -34
- package/dist/types/doc/worksheet.d.ts +255 -83
- package/dist/types/stream/xlsx/worksheet-reader.d.ts +3 -5
- package/dist/types/types.d.ts +86 -26
- package/dist/types/utils/col-cache.d.ts +11 -8
- package/dist/types/utils/unzip/extract.d.ts +16 -14
- package/dist/types/utils/unzip/index.d.ts +15 -1
- package/dist/types/utils/unzip/zip-parser.d.ts +92 -0
- package/package.json +1 -1
|
@@ -125,19 +125,26 @@ class Worksheet {
|
|
|
125
125
|
}
|
|
126
126
|
name = name.substring(0, 31);
|
|
127
127
|
}
|
|
128
|
-
if (this._workbook.
|
|
128
|
+
if (this._workbook.worksheets.find(ws => ws && ws.name.toLowerCase() === name.toLowerCase())) {
|
|
129
129
|
throw new Error(`Worksheet name already exists: ${name}`);
|
|
130
130
|
}
|
|
131
131
|
this._name = name;
|
|
132
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* The workbook that contains this worksheet
|
|
135
|
+
*/
|
|
133
136
|
get workbook() {
|
|
134
137
|
return this._workbook;
|
|
135
138
|
}
|
|
136
|
-
|
|
139
|
+
/**
|
|
140
|
+
* When you're done with this worksheet, call this to remove from workbook
|
|
141
|
+
*/
|
|
137
142
|
destroy() {
|
|
138
143
|
this._workbook.removeWorksheetEx(this);
|
|
139
144
|
}
|
|
140
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Get the bounding range of the cells in this worksheet
|
|
147
|
+
*/
|
|
141
148
|
get dimensions() {
|
|
142
149
|
const dimensions = new Range();
|
|
143
150
|
this._rows.forEach(row => {
|
|
@@ -152,16 +159,22 @@ class Worksheet {
|
|
|
152
159
|
}
|
|
153
160
|
// =========================================================================
|
|
154
161
|
// Columns
|
|
155
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Get the current columns array
|
|
164
|
+
*/
|
|
156
165
|
get columns() {
|
|
157
166
|
return this._columns;
|
|
158
167
|
}
|
|
159
|
-
|
|
160
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Add column headers and define column keys and widths.
|
|
170
|
+
*
|
|
171
|
+
* Note: these column structures are a workbook-building convenience only,
|
|
172
|
+
* apart from the column width, they will not be fully persisted.
|
|
173
|
+
*/
|
|
161
174
|
set columns(value) {
|
|
162
175
|
// calculate max header row count
|
|
163
176
|
this._headerRowCount = value.reduce((pv, cv) => {
|
|
164
|
-
const headerCount = (cv.header
|
|
177
|
+
const headerCount = Array.isArray(cv.header) ? cv.header.length : cv.header ? 1 : 0;
|
|
165
178
|
return Math.max(pv, headerCount);
|
|
166
179
|
}, 0);
|
|
167
180
|
// construct Column objects
|
|
@@ -185,7 +198,9 @@ class Worksheet {
|
|
|
185
198
|
eachColumnKey(f) {
|
|
186
199
|
Object.keys(this._keys).forEach(key => f(this._keys[key], key));
|
|
187
200
|
}
|
|
188
|
-
|
|
201
|
+
/**
|
|
202
|
+
* Access an individual column by key, letter and 1-based column number
|
|
203
|
+
*/
|
|
189
204
|
getColumn(c) {
|
|
190
205
|
let colNum;
|
|
191
206
|
if (typeof c === "string") {
|
|
@@ -211,18 +226,26 @@ class Worksheet {
|
|
|
211
226
|
}
|
|
212
227
|
return this._columns[colNum - 1];
|
|
213
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Cut one or more columns (columns to the right are shifted left)
|
|
231
|
+
* and optionally insert more
|
|
232
|
+
*
|
|
233
|
+
* If column properties have been defined, they will be cut or moved accordingly
|
|
234
|
+
*
|
|
235
|
+
* Known Issue: If a splice causes any merged cells to move, the results may be unpredictable
|
|
236
|
+
*
|
|
237
|
+
* Also: If the worksheet has more rows than values in the column inserts,
|
|
238
|
+
* the rows will still be shifted as if the values existed
|
|
239
|
+
*/
|
|
214
240
|
spliceColumns(start, count, ...inserts) {
|
|
215
241
|
const rows = this._rows;
|
|
216
242
|
const nRows = rows.length;
|
|
217
243
|
if (inserts.length > 0) {
|
|
218
244
|
// must iterate over all rows whether they exist yet or not
|
|
219
245
|
for (let i = 0; i < nRows; i++) {
|
|
220
|
-
const
|
|
221
|
-
inserts.forEach(insert => {
|
|
222
|
-
rowArguments.push(insert[i] || null);
|
|
223
|
-
});
|
|
246
|
+
const insertValues = inserts.map(insert => insert[i] || null);
|
|
224
247
|
const row = this.getRow(i + 1);
|
|
225
|
-
row.splice(...
|
|
248
|
+
row.splice(start, count, ...insertValues);
|
|
226
249
|
}
|
|
227
250
|
}
|
|
228
251
|
else {
|
|
@@ -248,26 +271,35 @@ class Worksheet {
|
|
|
248
271
|
}
|
|
249
272
|
}
|
|
250
273
|
for (let i = start; i < start + inserts.length; i++) {
|
|
251
|
-
this.getColumn(i).defn =
|
|
274
|
+
this.getColumn(i).defn = undefined;
|
|
252
275
|
}
|
|
253
276
|
// account for defined names
|
|
254
277
|
this.workbook.definedNames.spliceColumns(this.name, start, count, inserts.length);
|
|
255
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* Get the last column in a worksheet
|
|
281
|
+
*/
|
|
256
282
|
get lastColumn() {
|
|
257
283
|
return this.getColumn(this.columnCount);
|
|
258
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* The total column size of the document. Equal to the maximum cell count from all of the rows
|
|
287
|
+
*/
|
|
259
288
|
get columnCount() {
|
|
260
289
|
let maxCount = 0;
|
|
261
|
-
this.eachRow(
|
|
290
|
+
this.eachRow(row => {
|
|
262
291
|
maxCount = Math.max(maxCount, row.cellCount);
|
|
263
292
|
});
|
|
264
293
|
return maxCount;
|
|
265
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* A count of the number of columns that have values
|
|
297
|
+
*/
|
|
266
298
|
get actualColumnCount() {
|
|
267
299
|
// performance nightmare - for each row, counts all the columns used
|
|
268
300
|
const counts = [];
|
|
269
301
|
let count = 0;
|
|
270
|
-
this.eachRow(
|
|
302
|
+
this.eachRow(row => {
|
|
271
303
|
row.eachCell(({ col }) => {
|
|
272
304
|
if (!counts[col]) {
|
|
273
305
|
counts[col] = true;
|
|
@@ -294,23 +326,41 @@ class Worksheet {
|
|
|
294
326
|
get _nextRow() {
|
|
295
327
|
return this._lastRowNumber + 1;
|
|
296
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Get the last editable row in a worksheet (or undefined if there are none)
|
|
331
|
+
*/
|
|
297
332
|
get lastRow() {
|
|
298
333
|
if (this._rows.length) {
|
|
299
334
|
return this._rows[this._rows.length - 1];
|
|
300
335
|
}
|
|
301
336
|
return undefined;
|
|
302
337
|
}
|
|
303
|
-
|
|
338
|
+
/**
|
|
339
|
+
* Tries to find and return row for row number, else undefined
|
|
340
|
+
*
|
|
341
|
+
* @param r - The 1-indexed row number
|
|
342
|
+
*/
|
|
304
343
|
findRow(r) {
|
|
305
344
|
return this._rows[r - 1];
|
|
306
345
|
}
|
|
307
|
-
|
|
346
|
+
/**
|
|
347
|
+
* Tries to find and return rows for row number start and length, else undefined
|
|
348
|
+
*
|
|
349
|
+
* @param start - The 1-indexed starting row number
|
|
350
|
+
* @param length - The length of the expected array
|
|
351
|
+
*/
|
|
308
352
|
findRows(start, length) {
|
|
309
353
|
return this._rows.slice(start - 1, start - 1 + length);
|
|
310
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* The total row size of the document. Equal to the row number of the last row that has values.
|
|
357
|
+
*/
|
|
311
358
|
get rowCount() {
|
|
312
359
|
return this._lastRowNumber;
|
|
313
360
|
}
|
|
361
|
+
/**
|
|
362
|
+
* A count of the number of rows that have values. If a mid-document row is empty, it will not be included in the count.
|
|
363
|
+
*/
|
|
314
364
|
get actualRowCount() {
|
|
315
365
|
// counts actual rows that have actual data
|
|
316
366
|
let count = 0;
|
|
@@ -319,7 +369,9 @@ class Worksheet {
|
|
|
319
369
|
});
|
|
320
370
|
return count;
|
|
321
371
|
}
|
|
322
|
-
|
|
372
|
+
/**
|
|
373
|
+
* Get or create row by 1-based index
|
|
374
|
+
*/
|
|
323
375
|
getRow(r) {
|
|
324
376
|
let row = this._rows[r - 1];
|
|
325
377
|
if (!row) {
|
|
@@ -327,7 +379,9 @@ class Worksheet {
|
|
|
327
379
|
}
|
|
328
380
|
return row;
|
|
329
381
|
}
|
|
330
|
-
|
|
382
|
+
/**
|
|
383
|
+
* Get or create rows by 1-based index
|
|
384
|
+
*/
|
|
331
385
|
getRows(start, length) {
|
|
332
386
|
if (length < 1) {
|
|
333
387
|
return undefined;
|
|
@@ -338,6 +392,10 @@ class Worksheet {
|
|
|
338
392
|
}
|
|
339
393
|
return rows;
|
|
340
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* Add a couple of Rows by key-value, after the last current row, using the column keys,
|
|
397
|
+
* or add a row by contiguous Array (assign to columns A, B & C)
|
|
398
|
+
*/
|
|
341
399
|
addRow(value, style = "n") {
|
|
342
400
|
const rowNo = this._nextRow;
|
|
343
401
|
const row = this.getRow(rowNo);
|
|
@@ -345,6 +403,9 @@ class Worksheet {
|
|
|
345
403
|
this._setStyleOption(rowNo, style[0] === "i" ? style : "n");
|
|
346
404
|
return row;
|
|
347
405
|
}
|
|
406
|
+
/**
|
|
407
|
+
* Add multiple rows by providing an array of arrays or key-value pairs
|
|
408
|
+
*/
|
|
348
409
|
addRows(value, style = "n") {
|
|
349
410
|
const rows = [];
|
|
350
411
|
value.forEach(row => {
|
|
@@ -352,11 +413,19 @@ class Worksheet {
|
|
|
352
413
|
});
|
|
353
414
|
return rows;
|
|
354
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* Insert a Row by key-value, at the position (shifting down all rows from position),
|
|
418
|
+
* using the column keys, or add a row by contiguous Array (assign to columns A, B & C)
|
|
419
|
+
*/
|
|
355
420
|
insertRow(pos, value, style = "n") {
|
|
356
421
|
this.spliceRows(pos, 0, value);
|
|
357
422
|
this._setStyleOption(pos, style);
|
|
358
423
|
return this.getRow(pos);
|
|
359
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* Insert multiple rows at position (shifting down all rows from position)
|
|
427
|
+
* by providing an array of arrays or key-value pairs
|
|
428
|
+
*/
|
|
360
429
|
insertRows(pos, values, style = "n") {
|
|
361
430
|
this.spliceRows(pos, 0, ...values);
|
|
362
431
|
if (style !== "n") {
|
|
@@ -390,6 +459,9 @@ class Worksheet {
|
|
|
390
459
|
});
|
|
391
460
|
rDst.height = rSrc.height;
|
|
392
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Duplicate rows and insert new rows
|
|
464
|
+
*/
|
|
393
465
|
duplicateRow(rowNum, count, insert = false) {
|
|
394
466
|
// create count duplicates of rowNum
|
|
395
467
|
// either inserting new or overwriting existing rows
|
|
@@ -406,6 +478,12 @@ class Worksheet {
|
|
|
406
478
|
});
|
|
407
479
|
}
|
|
408
480
|
}
|
|
481
|
+
/**
|
|
482
|
+
* Cut one or more rows (rows below are shifted up)
|
|
483
|
+
* and optionally insert more
|
|
484
|
+
*
|
|
485
|
+
* Known Issue: If a splice causes any merged cells to move, the results may be unpredictable
|
|
486
|
+
*/
|
|
409
487
|
spliceRows(start, count, ...inserts) {
|
|
410
488
|
// same problem as row.splice, except worse.
|
|
411
489
|
const nKeep = start + count;
|
|
@@ -448,10 +526,10 @@ class Worksheet {
|
|
|
448
526
|
rSrc.eachCell({ includeEmpty: true }, (cell, colNumber) => {
|
|
449
527
|
rDst.getCell(colNumber).style = cell.style;
|
|
450
528
|
// remerge cells accounting for insert offset
|
|
451
|
-
if (cell.
|
|
452
|
-
const cellToBeMerged = this.getRow(cell.
|
|
453
|
-
const prevMaster = cell.
|
|
454
|
-
const newMaster = this.getRow(prevMaster.
|
|
529
|
+
if (cell.type === Enums.ValueType.Merge) {
|
|
530
|
+
const cellToBeMerged = this.getRow(cell.row + nInserts).getCell(colNumber);
|
|
531
|
+
const prevMaster = cell.master;
|
|
532
|
+
const newMaster = this.getRow(prevMaster.row + nInserts).getCell(prevMaster.col);
|
|
455
533
|
cellToBeMerged.merge(newMaster);
|
|
456
534
|
}
|
|
457
535
|
});
|
|
@@ -470,31 +548,33 @@ class Worksheet {
|
|
|
470
548
|
// account for defined names
|
|
471
549
|
this.workbook.definedNames.spliceRows(this.name, start, count, nInserts);
|
|
472
550
|
}
|
|
473
|
-
eachRow(
|
|
551
|
+
eachRow(optOrCallback, maybeCallback) {
|
|
474
552
|
let options;
|
|
475
|
-
let
|
|
476
|
-
if (typeof
|
|
477
|
-
|
|
553
|
+
let callback;
|
|
554
|
+
if (typeof optOrCallback === "function") {
|
|
555
|
+
callback = optOrCallback;
|
|
478
556
|
}
|
|
479
557
|
else {
|
|
480
|
-
options =
|
|
481
|
-
|
|
558
|
+
options = optOrCallback;
|
|
559
|
+
callback = maybeCallback;
|
|
482
560
|
}
|
|
483
561
|
if (options && options.includeEmpty) {
|
|
484
562
|
const n = this._rows.length;
|
|
485
563
|
for (let i = 1; i <= n; i++) {
|
|
486
|
-
|
|
564
|
+
callback(this.getRow(i), i);
|
|
487
565
|
}
|
|
488
566
|
}
|
|
489
567
|
else {
|
|
490
568
|
this._rows.forEach(row => {
|
|
491
569
|
if (row && row.hasValues) {
|
|
492
|
-
|
|
570
|
+
callback(row, row.number);
|
|
493
571
|
}
|
|
494
572
|
});
|
|
495
573
|
}
|
|
496
574
|
}
|
|
497
|
-
|
|
575
|
+
/**
|
|
576
|
+
* Return all rows as sparse array
|
|
577
|
+
*/
|
|
498
578
|
getSheetValues() {
|
|
499
579
|
const rows = [];
|
|
500
580
|
this._rows.forEach(row => {
|
|
@@ -506,13 +586,17 @@ class Worksheet {
|
|
|
506
586
|
}
|
|
507
587
|
// =========================================================================
|
|
508
588
|
// Cells
|
|
509
|
-
|
|
589
|
+
/**
|
|
590
|
+
* Returns the cell at [r,c] or address given by r. If not found, return undefined
|
|
591
|
+
*/
|
|
510
592
|
findCell(r, c) {
|
|
511
593
|
const address = colCache.getAddress(r, c);
|
|
512
594
|
const row = this._rows[address.row - 1];
|
|
513
595
|
return row ? row.findCell(address.col) : undefined;
|
|
514
596
|
}
|
|
515
|
-
|
|
597
|
+
/**
|
|
598
|
+
* Get or create cell at [r,c] or address given by r
|
|
599
|
+
*/
|
|
516
600
|
getCell(r, c) {
|
|
517
601
|
const address = colCache.getAddress(r, c);
|
|
518
602
|
const row = this.getRow(address.row);
|
|
@@ -520,7 +604,15 @@ class Worksheet {
|
|
|
520
604
|
}
|
|
521
605
|
// =========================================================================
|
|
522
606
|
// Merge
|
|
523
|
-
|
|
607
|
+
/**
|
|
608
|
+
* Merge cells, either:
|
|
609
|
+
*
|
|
610
|
+
* tlbr string, e.g. `'A4:B5'`
|
|
611
|
+
*
|
|
612
|
+
* tl string, br string, e.g. `'G10', 'H11'`
|
|
613
|
+
*
|
|
614
|
+
* t, l, b, r numbers, e.g. `10,11,12,13`
|
|
615
|
+
*/
|
|
524
616
|
mergeCells(...cells) {
|
|
525
617
|
const dimensions = new Range(cells);
|
|
526
618
|
this._mergeCellsInternal(dimensions);
|
|
@@ -565,9 +657,11 @@ class Worksheet {
|
|
|
565
657
|
// return true if this._merges has a merge object
|
|
566
658
|
return Object.values(this._merges).some(Boolean);
|
|
567
659
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
660
|
+
/**
|
|
661
|
+
* Scan the range and if any cell is part of a merge, un-merge the group.
|
|
662
|
+
* Note this function can affect multiple merges and merge-blocks are
|
|
663
|
+
* atomic - either they're all merged or all un-merged.
|
|
664
|
+
*/
|
|
571
665
|
unMergeCells(...cells) {
|
|
572
666
|
const dimensions = new Range(cells);
|
|
573
667
|
// find any cells in that range and unmerge them
|
|
@@ -616,12 +710,14 @@ class Worksheet {
|
|
|
616
710
|
for (let r = top; r <= bottom; r++) {
|
|
617
711
|
for (let c = left; c <= right; c++) {
|
|
618
712
|
if (first) {
|
|
619
|
-
this.getCell(r, c)
|
|
713
|
+
const cell = this.getCell(r, c);
|
|
714
|
+
const formulaValue = {
|
|
620
715
|
shareType,
|
|
621
716
|
formula,
|
|
622
717
|
ref: range,
|
|
623
718
|
result: getResult(r, c)
|
|
624
719
|
};
|
|
720
|
+
cell.value = formulaValue;
|
|
625
721
|
first = false;
|
|
626
722
|
}
|
|
627
723
|
else {
|
|
@@ -637,6 +733,10 @@ class Worksheet {
|
|
|
637
733
|
}
|
|
638
734
|
// =========================================================================
|
|
639
735
|
// Images
|
|
736
|
+
/**
|
|
737
|
+
* Using the image id from `Workbook.addImage`,
|
|
738
|
+
* embed an image within the worksheet to cover a range
|
|
739
|
+
*/
|
|
640
740
|
addImage(imageId, range) {
|
|
641
741
|
const model = {
|
|
642
742
|
type: "image",
|
|
@@ -648,6 +748,9 @@ class Worksheet {
|
|
|
648
748
|
getImages() {
|
|
649
749
|
return this._media.filter(m => m.type === "image");
|
|
650
750
|
}
|
|
751
|
+
/**
|
|
752
|
+
* Using the image id from `Workbook.addImage`, set the background to the worksheet
|
|
753
|
+
*/
|
|
651
754
|
addBackgroundImage(imageId) {
|
|
652
755
|
const model = {
|
|
653
756
|
type: "background",
|
|
@@ -661,6 +764,9 @@ class Worksheet {
|
|
|
661
764
|
}
|
|
662
765
|
// =========================================================================
|
|
663
766
|
// Worksheet Protection
|
|
767
|
+
/**
|
|
768
|
+
* Protect the worksheet with optional password and options
|
|
769
|
+
*/
|
|
664
770
|
protect(password, options) {
|
|
665
771
|
// TODO: make this function truly async
|
|
666
772
|
// perhaps marshal to worker thread or something
|
|
@@ -695,17 +801,29 @@ class Worksheet {
|
|
|
695
801
|
}
|
|
696
802
|
// =========================================================================
|
|
697
803
|
// Tables
|
|
804
|
+
/**
|
|
805
|
+
* Add a new table and return a reference to it
|
|
806
|
+
*/
|
|
698
807
|
addTable(model) {
|
|
699
808
|
const table = new Table(this, model);
|
|
700
809
|
this.tables[model.name] = table;
|
|
701
810
|
return table;
|
|
702
811
|
}
|
|
812
|
+
/**
|
|
813
|
+
* Fetch table by name
|
|
814
|
+
*/
|
|
703
815
|
getTable(name) {
|
|
704
816
|
return this.tables[name];
|
|
705
817
|
}
|
|
818
|
+
/**
|
|
819
|
+
* Delete table by name
|
|
820
|
+
*/
|
|
706
821
|
removeTable(name) {
|
|
707
822
|
delete this.tables[name];
|
|
708
823
|
}
|
|
824
|
+
/**
|
|
825
|
+
* Fetch all tables in the worksheet
|
|
826
|
+
*/
|
|
709
827
|
getTables() {
|
|
710
828
|
return Object.values(this.tables);
|
|
711
829
|
}
|
|
@@ -721,9 +839,15 @@ Please leave feedback at https://github.com/excelts/excelts/discussions/2575`);
|
|
|
721
839
|
}
|
|
722
840
|
// ===========================================================================
|
|
723
841
|
// Conditional Formatting
|
|
842
|
+
/**
|
|
843
|
+
* Add conditional formatting rules
|
|
844
|
+
*/
|
|
724
845
|
addConditionalFormatting(cf) {
|
|
725
846
|
this.conditionalFormattings.push(cf);
|
|
726
847
|
}
|
|
848
|
+
/**
|
|
849
|
+
* Delete conditional formatting rules
|
|
850
|
+
*/
|
|
727
851
|
removeConditionalFormatting(filter) {
|
|
728
852
|
if (typeof filter === "number") {
|
|
729
853
|
this.conditionalFormattings.splice(filter, 1);
|
|
@@ -757,7 +881,7 @@ Please leave feedback at https://github.com/excelts/excelts/discussions/2575`);
|
|
|
757
881
|
};
|
|
758
882
|
// =================================================
|
|
759
883
|
// columns
|
|
760
|
-
model.cols = Column.toModel(this.columns);
|
|
884
|
+
model.cols = Column.toModel(this.columns || []);
|
|
761
885
|
// ==========================================================
|
|
762
886
|
// Rows
|
|
763
887
|
const rows = (model.rows = []);
|
|
@@ -112,7 +112,9 @@ function formatValue(value, fmt, dateFormat) {
|
|
|
112
112
|
*/
|
|
113
113
|
function getCellDisplayText(cell, dateFormat) {
|
|
114
114
|
const value = cell.value;
|
|
115
|
-
const
|
|
115
|
+
const numFmt = cell.numFmt;
|
|
116
|
+
// Extract format code string from numFmt (which can be string or NumFmt object)
|
|
117
|
+
const fmt = typeof numFmt === "string" ? numFmt : (numFmt?.formatCode ?? "General");
|
|
116
118
|
// Null/undefined
|
|
117
119
|
if (value == null) {
|
|
118
120
|
return "";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Simple ZIP extraction utilities
|
|
3
3
|
* Provides easy-to-use Promise-based API for extracting ZIP files
|
|
4
|
+
* Works in both Node.js and browser environments
|
|
4
5
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import { createParse } from "./parse.js";
|
|
6
|
+
import { ZipParser } from "./zip-parser.js";
|
|
7
7
|
/**
|
|
8
8
|
* Extract all files from a ZIP buffer
|
|
9
9
|
*
|
|
10
|
-
* @param zipData - ZIP file data as Buffer or
|
|
10
|
+
* @param zipData - ZIP file data as Buffer, Uint8Array, or ArrayBuffer
|
|
11
11
|
* @returns Map of file paths to their content
|
|
12
12
|
*
|
|
13
13
|
* @example
|
|
@@ -24,40 +24,24 @@ import { createParse } from "./parse.js";
|
|
|
24
24
|
*/
|
|
25
25
|
export async function extractAll(zipData) {
|
|
26
26
|
const files = new Map();
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
path: zipEntry.path,
|
|
37
|
-
data: Buffer.alloc(0),
|
|
38
|
-
isDirectory: true,
|
|
39
|
-
size: 0
|
|
40
|
-
});
|
|
41
|
-
zipEntry.autodrain();
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
const data = await zipEntry.buffer();
|
|
45
|
-
files.set(zipEntry.path, {
|
|
46
|
-
path: zipEntry.path,
|
|
47
|
-
data,
|
|
48
|
-
isDirectory: false,
|
|
49
|
-
size: data.length
|
|
50
|
-
});
|
|
51
|
-
}
|
|
27
|
+
const parser = new ZipParser(zipData);
|
|
28
|
+
for (const entry of parser.getEntries()) {
|
|
29
|
+
const data = await parser.extract(entry.path);
|
|
30
|
+
files.set(entry.path, {
|
|
31
|
+
path: entry.path,
|
|
32
|
+
data: data || new Uint8Array(0),
|
|
33
|
+
isDirectory: entry.isDirectory,
|
|
34
|
+
size: entry.uncompressedSize
|
|
35
|
+
});
|
|
52
36
|
}
|
|
53
37
|
return files;
|
|
54
38
|
}
|
|
55
39
|
/**
|
|
56
40
|
* Extract a single file from a ZIP buffer
|
|
57
41
|
*
|
|
58
|
-
* @param zipData - ZIP file data as Buffer or
|
|
42
|
+
* @param zipData - ZIP file data as Buffer, Uint8Array, or ArrayBuffer
|
|
59
43
|
* @param filePath - Path of the file to extract
|
|
60
|
-
* @returns File content as
|
|
44
|
+
* @returns File content as Uint8Array, or null if not found
|
|
61
45
|
*
|
|
62
46
|
* @example
|
|
63
47
|
* ```ts
|
|
@@ -66,31 +50,18 @@ export async function extractAll(zipData) {
|
|
|
66
50
|
* const zipData = fs.readFileSync("archive.zip");
|
|
67
51
|
* const content = await extractFile(zipData, "readme.txt");
|
|
68
52
|
* if (content) {
|
|
69
|
-
* console.log(
|
|
53
|
+
* console.log(new TextDecoder().decode(content));
|
|
70
54
|
* }
|
|
71
55
|
* ```
|
|
72
56
|
*/
|
|
73
57
|
export async function extractFile(zipData, filePath) {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
const stream = Readable.from([buffer]);
|
|
77
|
-
stream.pipe(parse);
|
|
78
|
-
for await (const entry of parse) {
|
|
79
|
-
const zipEntry = entry;
|
|
80
|
-
if (zipEntry.path === filePath) {
|
|
81
|
-
if (zipEntry.type === "Directory") {
|
|
82
|
-
return Buffer.alloc(0);
|
|
83
|
-
}
|
|
84
|
-
return zipEntry.buffer();
|
|
85
|
-
}
|
|
86
|
-
zipEntry.autodrain();
|
|
87
|
-
}
|
|
88
|
-
return null;
|
|
58
|
+
const parser = new ZipParser(zipData);
|
|
59
|
+
return parser.extract(filePath);
|
|
89
60
|
}
|
|
90
61
|
/**
|
|
91
62
|
* List all file paths in a ZIP buffer (without extracting content)
|
|
92
63
|
*
|
|
93
|
-
* @param zipData - ZIP file data as Buffer or
|
|
64
|
+
* @param zipData - ZIP file data as Buffer, Uint8Array, or ArrayBuffer
|
|
94
65
|
* @returns Array of file paths
|
|
95
66
|
*
|
|
96
67
|
* @example
|
|
@@ -103,22 +74,13 @@ export async function extractFile(zipData, filePath) {
|
|
|
103
74
|
* ```
|
|
104
75
|
*/
|
|
105
76
|
export async function listFiles(zipData) {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
const parse = createParse({ forceStream: true });
|
|
109
|
-
const stream = Readable.from([buffer]);
|
|
110
|
-
stream.pipe(parse);
|
|
111
|
-
for await (const entry of parse) {
|
|
112
|
-
const zipEntry = entry;
|
|
113
|
-
paths.push(zipEntry.path);
|
|
114
|
-
zipEntry.autodrain();
|
|
115
|
-
}
|
|
116
|
-
return paths;
|
|
77
|
+
const parser = new ZipParser(zipData);
|
|
78
|
+
return parser.listFiles();
|
|
117
79
|
}
|
|
118
80
|
/**
|
|
119
81
|
* Iterate over ZIP entries with a callback (memory efficient for large ZIPs)
|
|
120
82
|
*
|
|
121
|
-
* @param zipData - ZIP file data as Buffer or
|
|
83
|
+
* @param zipData - ZIP file data as Buffer, Uint8Array, or ArrayBuffer
|
|
122
84
|
* @param callback - Async callback for each entry, return false to stop iteration
|
|
123
85
|
*
|
|
124
86
|
* @example
|
|
@@ -128,33 +90,17 @@ export async function listFiles(zipData) {
|
|
|
128
90
|
* await forEachEntry(zipData, async (path, getData) => {
|
|
129
91
|
* if (path.endsWith(".xml")) {
|
|
130
92
|
* const content = await getData();
|
|
131
|
-
* console.log(
|
|
93
|
+
* console.log(new TextDecoder().decode(content));
|
|
132
94
|
* }
|
|
133
95
|
* return true; // continue iteration
|
|
134
96
|
* });
|
|
135
97
|
* ```
|
|
136
98
|
*/
|
|
137
99
|
export async function forEachEntry(zipData, callback) {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
for await (const entry of parse) {
|
|
143
|
-
const zipEntry = entry;
|
|
144
|
-
let dataPromise = null;
|
|
145
|
-
const getData = () => {
|
|
146
|
-
if (!dataPromise) {
|
|
147
|
-
dataPromise = zipEntry.buffer();
|
|
148
|
-
}
|
|
149
|
-
return dataPromise;
|
|
150
|
-
};
|
|
151
|
-
const shouldContinue = await callback(zipEntry.path, getData, zipEntry);
|
|
152
|
-
// If callback didn't read data, drain it
|
|
153
|
-
if (!dataPromise) {
|
|
154
|
-
zipEntry.autodrain();
|
|
155
|
-
}
|
|
156
|
-
if (shouldContinue === false) {
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
100
|
+
const parser = new ZipParser(zipData);
|
|
101
|
+
await parser.forEach(async (entry, getData) => {
|
|
102
|
+
return callback(entry.path, getData, entry);
|
|
103
|
+
});
|
|
160
104
|
}
|
|
105
|
+
// Re-export ZipParser for advanced usage
|
|
106
|
+
export { ZipParser } from "./zip-parser.js";
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unzip utilities for parsing ZIP archives
|
|
3
|
+
*
|
|
4
|
+
* Two APIs are provided:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Stream-based API** (Node.js only):
|
|
7
|
+
* - `Parse`, `createParse` - Parse ZIP files as a stream
|
|
8
|
+
* - Best for large files where you don't want to load entire file into memory
|
|
9
|
+
* - Requires Node.js `stream` module
|
|
10
|
+
*
|
|
11
|
+
* 2. **Buffer-based API** (Browser + Node.js):
|
|
12
|
+
* - `extractAll`, `extractFile`, `listFiles`, `forEachEntry`, `ZipParser`
|
|
13
|
+
* - Works in both Node.js and browser environments
|
|
14
|
+
* - Uses native `DecompressionStream` in browser, `zlib` in Node.js
|
|
15
|
+
* - Best for files already loaded into memory (ArrayBuffer, Uint8Array)
|
|
16
|
+
*
|
|
3
17
|
* Original source: https://github.com/ZJONSSON/node-unzipper
|
|
4
18
|
* License: MIT
|
|
5
19
|
*/
|
|
20
|
+
// Stream-based API (Node.js only - requires stream module)
|
|
6
21
|
export { Parse, createParse } from "./parse.js";
|
|
7
22
|
export { PullStream } from "./pull-stream.js";
|
|
8
23
|
export { NoopStream } from "./noop-stream.js";
|
|
@@ -10,5 +25,5 @@ export { bufferStream } from "./buffer-stream.js";
|
|
|
10
25
|
export { parse as parseBuffer } from "./parse-buffer.js";
|
|
11
26
|
export { parseDateTime } from "./parse-datetime.js";
|
|
12
27
|
export { parseExtraField } from "./parse-extra-field.js";
|
|
13
|
-
//
|
|
14
|
-
export { extractAll, extractFile, listFiles, forEachEntry } from "./extract.js";
|
|
28
|
+
// Buffer-based API (Browser + Node.js - cross-platform)
|
|
29
|
+
export { extractAll, extractFile, listFiles, forEachEntry, ZipParser } from "./extract.js";
|