@cj-tech-master/excelts 1.4.5-canary.20251212064440.3eb099f → 1.4.5-canary.20251212160853.7621827
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 +259 -20
- package/dist/browser/excelts.iife.js.map +1 -1
- package/dist/browser/excelts.iife.min.js +3 -3
- package/dist/cjs/doc/column.js +36 -7
- package/dist/cjs/doc/row.js +48 -18
- package/dist/cjs/doc/workbook.js +25 -2
- package/dist/cjs/doc/worksheet.js +153 -23
- 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/column.js +36 -7
- package/dist/esm/doc/row.js +48 -18
- package/dist/esm/doc/workbook.js +25 -2
- package/dist/esm/doc/worksheet.js +153 -23
- 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/column.d.ts +37 -2
- package/dist/types/doc/row.d.ts +50 -5
- package/dist/types/doc/workbook.d.ts +24 -1
- package/dist/types/doc/worksheet.d.ts +158 -11
- 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
package/dist/esm/doc/workbook.js
CHANGED
|
@@ -26,12 +26,18 @@ class Workbook {
|
|
|
26
26
|
this.pivotTables = [];
|
|
27
27
|
this._definedNames = new DefinedNames();
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* xlsx file format operations
|
|
31
|
+
*/
|
|
29
32
|
get xlsx() {
|
|
30
33
|
if (!this._xlsx) {
|
|
31
34
|
this._xlsx = new XLSX(this);
|
|
32
35
|
}
|
|
33
36
|
return this._xlsx;
|
|
34
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* csv file format operations
|
|
40
|
+
*/
|
|
35
41
|
get csv() {
|
|
36
42
|
if (!this._csv) {
|
|
37
43
|
this._csv = new CSV(this);
|
|
@@ -47,6 +53,9 @@ class Workbook {
|
|
|
47
53
|
}
|
|
48
54
|
return this._worksheets.length || 1;
|
|
49
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Add a new worksheet and return a reference to it
|
|
58
|
+
*/
|
|
50
59
|
addWorksheet(name, options) {
|
|
51
60
|
const id = this.nextId;
|
|
52
61
|
const lastOrderNo = this._worksheets.reduce((acc, ws) => ((ws && ws.orderNo) > acc ? ws.orderNo : acc), 0);
|
|
@@ -70,6 +79,9 @@ class Workbook {
|
|
|
70
79
|
worksheet.destroy();
|
|
71
80
|
}
|
|
72
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Fetch sheet by name or id
|
|
84
|
+
*/
|
|
73
85
|
getWorksheet(id) {
|
|
74
86
|
if (id === undefined) {
|
|
75
87
|
return this._worksheets.find(Boolean);
|
|
@@ -82,6 +94,9 @@ class Workbook {
|
|
|
82
94
|
}
|
|
83
95
|
return undefined;
|
|
84
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Return a clone of worksheets in order
|
|
99
|
+
*/
|
|
85
100
|
get worksheets() {
|
|
86
101
|
// return a clone of _worksheets
|
|
87
102
|
return this._worksheets
|
|
@@ -89,9 +104,14 @@ class Workbook {
|
|
|
89
104
|
.sort((a, b) => a.orderNo - b.orderNo)
|
|
90
105
|
.filter(Boolean);
|
|
91
106
|
}
|
|
92
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Iterate over all sheets.
|
|
109
|
+
*
|
|
110
|
+
* Note: `workbook.worksheets.forEach` will still work but this is better.
|
|
111
|
+
*/
|
|
112
|
+
eachSheet(callback) {
|
|
93
113
|
this.worksheets.forEach(sheet => {
|
|
94
|
-
|
|
114
|
+
callback(sheet, sheet.id);
|
|
95
115
|
});
|
|
96
116
|
}
|
|
97
117
|
get definedNames() {
|
|
@@ -101,6 +121,9 @@ class Workbook {
|
|
|
101
121
|
// Note: themes are not an exposed feature, meddle at your peril!
|
|
102
122
|
this._themes = undefined;
|
|
103
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Add Image to Workbook and return the id
|
|
126
|
+
*/
|
|
104
127
|
addImage(image) {
|
|
105
128
|
// TODO: validation?
|
|
106
129
|
const id = this.media.length;
|
|
@@ -130,14 +130,21 @@ class Worksheet {
|
|
|
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,12 +159,18 @@ 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) => {
|
|
@@ -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,6 +226,17 @@ 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;
|
|
@@ -250,9 +276,15 @@ class Worksheet {
|
|
|
250
276
|
// account for defined names
|
|
251
277
|
this.workbook.definedNames.spliceColumns(this.name, start, count, inserts.length);
|
|
252
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* Get the last column in a worksheet
|
|
281
|
+
*/
|
|
253
282
|
get lastColumn() {
|
|
254
283
|
return this.getColumn(this.columnCount);
|
|
255
284
|
}
|
|
285
|
+
/**
|
|
286
|
+
* The total column size of the document. Equal to the maximum cell count from all of the rows
|
|
287
|
+
*/
|
|
256
288
|
get columnCount() {
|
|
257
289
|
let maxCount = 0;
|
|
258
290
|
this.eachRow(row => {
|
|
@@ -260,6 +292,9 @@ class Worksheet {
|
|
|
260
292
|
});
|
|
261
293
|
return maxCount;
|
|
262
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* A count of the number of columns that have values
|
|
297
|
+
*/
|
|
263
298
|
get actualColumnCount() {
|
|
264
299
|
// performance nightmare - for each row, counts all the columns used
|
|
265
300
|
const counts = [];
|
|
@@ -291,23 +326,41 @@ class Worksheet {
|
|
|
291
326
|
get _nextRow() {
|
|
292
327
|
return this._lastRowNumber + 1;
|
|
293
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Get the last editable row in a worksheet (or undefined if there are none)
|
|
331
|
+
*/
|
|
294
332
|
get lastRow() {
|
|
295
333
|
if (this._rows.length) {
|
|
296
334
|
return this._rows[this._rows.length - 1];
|
|
297
335
|
}
|
|
298
336
|
return undefined;
|
|
299
337
|
}
|
|
300
|
-
|
|
338
|
+
/**
|
|
339
|
+
* Tries to find and return row for row number, else undefined
|
|
340
|
+
*
|
|
341
|
+
* @param r - The 1-indexed row number
|
|
342
|
+
*/
|
|
301
343
|
findRow(r) {
|
|
302
344
|
return this._rows[r - 1];
|
|
303
345
|
}
|
|
304
|
-
|
|
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
|
+
*/
|
|
305
352
|
findRows(start, length) {
|
|
306
353
|
return this._rows.slice(start - 1, start - 1 + length);
|
|
307
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* The total row size of the document. Equal to the row number of the last row that has values.
|
|
357
|
+
*/
|
|
308
358
|
get rowCount() {
|
|
309
359
|
return this._lastRowNumber;
|
|
310
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
|
+
*/
|
|
311
364
|
get actualRowCount() {
|
|
312
365
|
// counts actual rows that have actual data
|
|
313
366
|
let count = 0;
|
|
@@ -316,7 +369,9 @@ class Worksheet {
|
|
|
316
369
|
});
|
|
317
370
|
return count;
|
|
318
371
|
}
|
|
319
|
-
|
|
372
|
+
/**
|
|
373
|
+
* Get or create row by 1-based index
|
|
374
|
+
*/
|
|
320
375
|
getRow(r) {
|
|
321
376
|
let row = this._rows[r - 1];
|
|
322
377
|
if (!row) {
|
|
@@ -324,7 +379,9 @@ class Worksheet {
|
|
|
324
379
|
}
|
|
325
380
|
return row;
|
|
326
381
|
}
|
|
327
|
-
|
|
382
|
+
/**
|
|
383
|
+
* Get or create rows by 1-based index
|
|
384
|
+
*/
|
|
328
385
|
getRows(start, length) {
|
|
329
386
|
if (length < 1) {
|
|
330
387
|
return undefined;
|
|
@@ -335,6 +392,10 @@ class Worksheet {
|
|
|
335
392
|
}
|
|
336
393
|
return rows;
|
|
337
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
|
+
*/
|
|
338
399
|
addRow(value, style = "n") {
|
|
339
400
|
const rowNo = this._nextRow;
|
|
340
401
|
const row = this.getRow(rowNo);
|
|
@@ -342,6 +403,9 @@ class Worksheet {
|
|
|
342
403
|
this._setStyleOption(rowNo, style[0] === "i" ? style : "n");
|
|
343
404
|
return row;
|
|
344
405
|
}
|
|
406
|
+
/**
|
|
407
|
+
* Add multiple rows by providing an array of arrays or key-value pairs
|
|
408
|
+
*/
|
|
345
409
|
addRows(value, style = "n") {
|
|
346
410
|
const rows = [];
|
|
347
411
|
value.forEach(row => {
|
|
@@ -349,11 +413,19 @@ class Worksheet {
|
|
|
349
413
|
});
|
|
350
414
|
return rows;
|
|
351
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
|
+
*/
|
|
352
420
|
insertRow(pos, value, style = "n") {
|
|
353
421
|
this.spliceRows(pos, 0, value);
|
|
354
422
|
this._setStyleOption(pos, style);
|
|
355
423
|
return this.getRow(pos);
|
|
356
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
|
+
*/
|
|
357
429
|
insertRows(pos, values, style = "n") {
|
|
358
430
|
this.spliceRows(pos, 0, ...values);
|
|
359
431
|
if (style !== "n") {
|
|
@@ -387,6 +459,9 @@ class Worksheet {
|
|
|
387
459
|
});
|
|
388
460
|
rDst.height = rSrc.height;
|
|
389
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Duplicate rows and insert new rows
|
|
464
|
+
*/
|
|
390
465
|
duplicateRow(rowNum, count, insert = false) {
|
|
391
466
|
// create count duplicates of rowNum
|
|
392
467
|
// either inserting new or overwriting existing rows
|
|
@@ -403,6 +478,12 @@ class Worksheet {
|
|
|
403
478
|
});
|
|
404
479
|
}
|
|
405
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
|
+
*/
|
|
406
487
|
spliceRows(start, count, ...inserts) {
|
|
407
488
|
// same problem as row.splice, except worse.
|
|
408
489
|
const nKeep = start + count;
|
|
@@ -467,26 +548,33 @@ class Worksheet {
|
|
|
467
548
|
// account for defined names
|
|
468
549
|
this.workbook.definedNames.spliceRows(this.name, start, count, nInserts);
|
|
469
550
|
}
|
|
470
|
-
eachRow(
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
551
|
+
eachRow(optOrCallback, maybeCallback) {
|
|
552
|
+
let options;
|
|
553
|
+
let callback;
|
|
554
|
+
if (typeof optOrCallback === "function") {
|
|
555
|
+
callback = optOrCallback;
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
options = optOrCallback;
|
|
559
|
+
callback = maybeCallback;
|
|
474
560
|
}
|
|
475
561
|
if (options && options.includeEmpty) {
|
|
476
562
|
const n = this._rows.length;
|
|
477
563
|
for (let i = 1; i <= n; i++) {
|
|
478
|
-
|
|
564
|
+
callback(this.getRow(i), i);
|
|
479
565
|
}
|
|
480
566
|
}
|
|
481
567
|
else {
|
|
482
568
|
this._rows.forEach(row => {
|
|
483
569
|
if (row && row.hasValues) {
|
|
484
|
-
|
|
570
|
+
callback(row, row.number);
|
|
485
571
|
}
|
|
486
572
|
});
|
|
487
573
|
}
|
|
488
574
|
}
|
|
489
|
-
|
|
575
|
+
/**
|
|
576
|
+
* Return all rows as sparse array
|
|
577
|
+
*/
|
|
490
578
|
getSheetValues() {
|
|
491
579
|
const rows = [];
|
|
492
580
|
this._rows.forEach(row => {
|
|
@@ -498,13 +586,17 @@ class Worksheet {
|
|
|
498
586
|
}
|
|
499
587
|
// =========================================================================
|
|
500
588
|
// Cells
|
|
501
|
-
|
|
589
|
+
/**
|
|
590
|
+
* Returns the cell at [r,c] or address given by r. If not found, return undefined
|
|
591
|
+
*/
|
|
502
592
|
findCell(r, c) {
|
|
503
593
|
const address = colCache.getAddress(r, c);
|
|
504
594
|
const row = this._rows[address.row - 1];
|
|
505
595
|
return row ? row.findCell(address.col) : undefined;
|
|
506
596
|
}
|
|
507
|
-
|
|
597
|
+
/**
|
|
598
|
+
* Get or create cell at [r,c] or address given by r
|
|
599
|
+
*/
|
|
508
600
|
getCell(r, c) {
|
|
509
601
|
const address = colCache.getAddress(r, c);
|
|
510
602
|
const row = this.getRow(address.row);
|
|
@@ -512,7 +604,15 @@ class Worksheet {
|
|
|
512
604
|
}
|
|
513
605
|
// =========================================================================
|
|
514
606
|
// Merge
|
|
515
|
-
|
|
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
|
+
*/
|
|
516
616
|
mergeCells(...cells) {
|
|
517
617
|
const dimensions = new Range(cells);
|
|
518
618
|
this._mergeCellsInternal(dimensions);
|
|
@@ -557,9 +657,11 @@ class Worksheet {
|
|
|
557
657
|
// return true if this._merges has a merge object
|
|
558
658
|
return Object.values(this._merges).some(Boolean);
|
|
559
659
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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
|
+
*/
|
|
563
665
|
unMergeCells(...cells) {
|
|
564
666
|
const dimensions = new Range(cells);
|
|
565
667
|
// find any cells in that range and unmerge them
|
|
@@ -631,6 +733,10 @@ class Worksheet {
|
|
|
631
733
|
}
|
|
632
734
|
// =========================================================================
|
|
633
735
|
// Images
|
|
736
|
+
/**
|
|
737
|
+
* Using the image id from `Workbook.addImage`,
|
|
738
|
+
* embed an image within the worksheet to cover a range
|
|
739
|
+
*/
|
|
634
740
|
addImage(imageId, range) {
|
|
635
741
|
const model = {
|
|
636
742
|
type: "image",
|
|
@@ -642,6 +748,9 @@ class Worksheet {
|
|
|
642
748
|
getImages() {
|
|
643
749
|
return this._media.filter(m => m.type === "image");
|
|
644
750
|
}
|
|
751
|
+
/**
|
|
752
|
+
* Using the image id from `Workbook.addImage`, set the background to the worksheet
|
|
753
|
+
*/
|
|
645
754
|
addBackgroundImage(imageId) {
|
|
646
755
|
const model = {
|
|
647
756
|
type: "background",
|
|
@@ -655,6 +764,9 @@ class Worksheet {
|
|
|
655
764
|
}
|
|
656
765
|
// =========================================================================
|
|
657
766
|
// Worksheet Protection
|
|
767
|
+
/**
|
|
768
|
+
* Protect the worksheet with optional password and options
|
|
769
|
+
*/
|
|
658
770
|
protect(password, options) {
|
|
659
771
|
// TODO: make this function truly async
|
|
660
772
|
// perhaps marshal to worker thread or something
|
|
@@ -689,17 +801,29 @@ class Worksheet {
|
|
|
689
801
|
}
|
|
690
802
|
// =========================================================================
|
|
691
803
|
// Tables
|
|
804
|
+
/**
|
|
805
|
+
* Add a new table and return a reference to it
|
|
806
|
+
*/
|
|
692
807
|
addTable(model) {
|
|
693
808
|
const table = new Table(this, model);
|
|
694
809
|
this.tables[model.name] = table;
|
|
695
810
|
return table;
|
|
696
811
|
}
|
|
812
|
+
/**
|
|
813
|
+
* Fetch table by name
|
|
814
|
+
*/
|
|
697
815
|
getTable(name) {
|
|
698
816
|
return this.tables[name];
|
|
699
817
|
}
|
|
818
|
+
/**
|
|
819
|
+
* Delete table by name
|
|
820
|
+
*/
|
|
700
821
|
removeTable(name) {
|
|
701
822
|
delete this.tables[name];
|
|
702
823
|
}
|
|
824
|
+
/**
|
|
825
|
+
* Fetch all tables in the worksheet
|
|
826
|
+
*/
|
|
703
827
|
getTables() {
|
|
704
828
|
return Object.values(this.tables);
|
|
705
829
|
}
|
|
@@ -715,9 +839,15 @@ Please leave feedback at https://github.com/excelts/excelts/discussions/2575`);
|
|
|
715
839
|
}
|
|
716
840
|
// ===========================================================================
|
|
717
841
|
// Conditional Formatting
|
|
842
|
+
/**
|
|
843
|
+
* Add conditional formatting rules
|
|
844
|
+
*/
|
|
718
845
|
addConditionalFormatting(cf) {
|
|
719
846
|
this.conditionalFormattings.push(cf);
|
|
720
847
|
}
|
|
848
|
+
/**
|
|
849
|
+
* Delete conditional formatting rules
|
|
850
|
+
*/
|
|
721
851
|
removeConditionalFormatting(filter) {
|
|
722
852
|
if (typeof filter === "number") {
|
|
723
853
|
this.conditionalFormattings.splice(filter, 1);
|
|
@@ -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";
|