@justybase/spreadsheet-tasks 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +46 -2
- package/dist/cjs/index.d.ts +71 -3
- package/dist/cjs/index.js +1204 -89
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +1201 -89
- package/dist/esm/index.js.map +1 -1
- package/docs/API.md +83 -2
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `convertXlsbToXlsx` and `convertXlsxToXlsb` — Windows/Excel COM conversions
|
|
13
|
+
with protected destinations and optional explicit overwriting.
|
|
14
|
+
- `replaceSheetDataStream()` and `saveStreaming()` for XLSX/XLSB updaters,
|
|
15
|
+
accepting synchronous or asynchronous row sources without collecting the
|
|
16
|
+
complete result set in memory.
|
|
17
|
+
- `XlsmUpdater` as an explicit alias for `XlsxUpdater`; VBA package members are
|
|
18
|
+
preserved as opaque ZIP parts.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Destination replacement during Excel conversion now preserves files created
|
|
23
|
+
concurrently and rolls back safely when the final rename fails.
|
|
24
|
+
- Shared-string `count` values now reflect the replaced worksheet rather than
|
|
25
|
+
accumulating stale references across repeated updates. Pending strings are
|
|
26
|
+
committed only once.
|
|
27
|
+
- Updater saves now install the destination atomically through a same-directory
|
|
28
|
+
temporary file.
|
|
29
|
+
|
|
8
30
|
## [2.1.0] - 2026-08-07
|
|
9
31
|
|
|
10
32
|
### Added
|
package/README.md
CHANGED
|
@@ -31,7 +31,8 @@ High-performance TypeScript library for reading and writing Excel files in XLSB
|
|
|
31
31
|
- **Streaming Support** — Efficient memory usage for large files
|
|
32
32
|
- **Multiple Sheets** — Support for multiple worksheets per workbook
|
|
33
33
|
- **Auto-filter** — Automatic filter headers support
|
|
34
|
-
- **
|
|
34
|
+
- **Excel COM Conversion** — Convert XLSB and XLSX through desktop Microsoft Excel on Windows
|
|
35
|
+
- **In-place Updates** — Replace the data of a worksheet inside an existing XLSX, XLSM or XLSB while keeping pivot tables, other sheets and formatting untouched
|
|
35
36
|
|
|
36
37
|
## Benchmark Results
|
|
37
38
|
|
|
@@ -46,6 +47,7 @@ Measured with `npm run benchmark` (50K rows). Your results may vary by hardware
|
|
|
46
47
|
## Requirements
|
|
47
48
|
|
|
48
49
|
- **Node.js** 16 or newer ([`engines`](./package.json) in `package.json`)
|
|
50
|
+
- XLSB/XLSX conversion additionally requires Windows, PowerShell and desktop Microsoft Excel with COM automation
|
|
49
51
|
|
|
50
52
|
## Installation
|
|
51
53
|
|
|
@@ -211,7 +213,7 @@ Excel file (like a report with pivot tables wired to a data sheet) and only
|
|
|
211
213
|
refresh its data:
|
|
212
214
|
|
|
213
215
|
```typescript
|
|
214
|
-
import { XlsxUpdater, XlsbUpdater } from '@justybase/spreadsheet-tasks';
|
|
216
|
+
import { XlsxUpdater, XlsbUpdater, XlsmUpdater } from '@justybase/spreadsheet-tasks';
|
|
215
217
|
|
|
216
218
|
// XLSX
|
|
217
219
|
const xlsx = new XlsxUpdater('report.xlsx');
|
|
@@ -222,8 +224,26 @@ xlsx.save(); // overwrite in place, or xlsx.save('report_new.xls
|
|
|
222
224
|
const xlsb = new XlsbUpdater('report.xlsb');
|
|
223
225
|
xlsb.replaceSheetData('data1', rows, { headers: ['ID', 'NAME', 'AMOUNT'] });
|
|
224
226
|
xlsb.save('report_new.xlsb');
|
|
227
|
+
|
|
228
|
+
// XLSM uses the XLSX updater and keeps opaque VBA parts intact.
|
|
229
|
+
const xlsm = new XlsmUpdater('report.xlsm');
|
|
230
|
+
xlsm.replaceSheetData('data1', rows, { headers: ['ID', 'NAME', 'AMOUNT'] });
|
|
231
|
+
xlsm.save('report_new.xlsm');
|
|
225
232
|
```
|
|
226
233
|
|
|
234
|
+
For a result set that should not be collected into a 2D array, use the additive
|
|
235
|
+
streaming API. It accepts either a synchronous iterable or an async iterable and
|
|
236
|
+
writes the final ZIP through a temporary file before installing it atomically:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
const updater = new XlsxUpdater('large-report.xlsx');
|
|
240
|
+
await updater.replaceSheetDataStream('data1', databaseRows(), { headers });
|
|
241
|
+
await updater.saveStreaming('large-report-updated.xlsx');
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`save()` remains available for the existing synchronous API. Both save methods
|
|
245
|
+
replace their destination atomically after a successful write.
|
|
246
|
+
|
|
227
247
|
**What happens under the hood:**
|
|
228
248
|
|
|
229
249
|
- The target sheet's cell data is cleared and replaced with the new rows.
|
|
@@ -242,6 +262,30 @@ xlsb.save('report_new.xlsb');
|
|
|
242
262
|
> headers), so the pivot's columns would be replaced by the first record's
|
|
243
263
|
> values.
|
|
244
264
|
|
|
265
|
+
### Converting XLSB and XLSX with Excel
|
|
266
|
+
|
|
267
|
+
On Windows, existing workbooks can be converted by asking the installed desktop
|
|
268
|
+
Microsoft Excel to perform `SaveAs` through COM. The source file is opened
|
|
269
|
+
read-only and the destination is written separately:
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import {
|
|
273
|
+
convertXlsbToXlsx,
|
|
274
|
+
convertXlsxToXlsb,
|
|
275
|
+
} from '@justybase/spreadsheet-tasks';
|
|
276
|
+
|
|
277
|
+
await convertXlsbToXlsx('report.xlsb', 'report.xlsx');
|
|
278
|
+
await convertXlsxToXlsb('report.xlsx', 'report.xlsb');
|
|
279
|
+
|
|
280
|
+
// Existing destinations are protected by default.
|
|
281
|
+
await convertXlsbToXlsx('report.xlsb', 'report.xlsx', { overwrite: true });
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Conversion is a Node.js-only API and requires Windows plus desktop Microsoft
|
|
285
|
+
Excel. Unsupported files, unavailable COM automation, and Excel conversion
|
|
286
|
+
errors are reported as rejected promises. VBA/macros are not guaranteed when
|
|
287
|
+
the target format does not support them.
|
|
288
|
+
|
|
245
289
|
## API Documentation
|
|
246
290
|
|
|
247
291
|
See [API Documentation](./docs/API.md) for detailed API reference.
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -367,6 +367,8 @@ interface ReplaceSheetDataOptions$1 {
|
|
|
367
367
|
*/
|
|
368
368
|
styleFallback?: 'inherit' | 'general';
|
|
369
369
|
}
|
|
370
|
+
/** A one-pass synchronous or asynchronous source of worksheet rows. */
|
|
371
|
+
type RowSource$1 = Iterable<ReadonlyArray<CellValue>> | AsyncIterable<ReadonlyArray<CellValue>>;
|
|
370
372
|
/**
|
|
371
373
|
* Update the data of a worksheet inside an existing XLSX file without rebuilding
|
|
372
374
|
* the workbook. Everything outside the target sheet's cell data — pivot tables
|
|
@@ -383,6 +385,8 @@ interface ReplaceSheetDataOptions$1 {
|
|
|
383
385
|
declare class XlsxUpdater {
|
|
384
386
|
private readonly zip;
|
|
385
387
|
private readonly sourcePath;
|
|
388
|
+
private temporaryDirectory;
|
|
389
|
+
private readonly stagedParts;
|
|
386
390
|
private sheetNameToPath;
|
|
387
391
|
private pivotCacheDefs;
|
|
388
392
|
private pivotTableDefPaths;
|
|
@@ -390,12 +394,14 @@ declare class XlsxUpdater {
|
|
|
390
394
|
private sharedStringsValues;
|
|
391
395
|
private sharedStringsOriginalLength;
|
|
392
396
|
private sharedStringsCount;
|
|
397
|
+
private sharedStringsPending;
|
|
398
|
+
private sharedStringsDirty;
|
|
393
399
|
private stringIndexMap;
|
|
394
400
|
private readonly _oaEpoch;
|
|
395
401
|
/**
|
|
396
402
|
* @param path Path to an existing `.xlsx` file.
|
|
397
403
|
*/
|
|
398
|
-
constructor(
|
|
404
|
+
constructor(filePath: string);
|
|
399
405
|
/** Worksheet names in workbook order. */
|
|
400
406
|
getSheetNames(): string[];
|
|
401
407
|
/**
|
|
@@ -407,13 +413,36 @@ declare class XlsxUpdater {
|
|
|
407
413
|
* @param options See {@link ReplaceSheetDataOptions}.
|
|
408
414
|
*/
|
|
409
415
|
replaceSheetData(sheetName: string, rows: CellValue[][], options?: ReplaceSheetDataOptions$1): void;
|
|
416
|
+
/**
|
|
417
|
+
* Replace worksheet rows from a one-pass source without materialising the
|
|
418
|
+
* complete result set. Use {@link saveStreaming} to keep the final ZIP
|
|
419
|
+
* write streaming as well.
|
|
420
|
+
*/
|
|
421
|
+
replaceSheetDataStream(sheetName: string, rows: RowSource$1, options?: ReplaceSheetDataOptions$1): Promise<void>;
|
|
410
422
|
/**
|
|
411
423
|
* Write the updated workbook to disk.
|
|
412
424
|
* @param outputPath Destination path; defaults to the source file (overwrites in place).
|
|
413
425
|
*/
|
|
414
426
|
save(outputPath?: string): void;
|
|
427
|
+
/** Save staged streaming replacements without materialising the ZIP. */
|
|
428
|
+
saveStreaming(outputPath?: string): Promise<void>;
|
|
415
429
|
/** The updated workbook as an in-memory ZIP buffer. */
|
|
416
430
|
toBuffer(): Buffer;
|
|
431
|
+
/**
|
|
432
|
+
* Release the staging directory and any unsaved staged parts.
|
|
433
|
+
* The updater remains usable; a new staging directory is created lazily
|
|
434
|
+
* on the next streaming replacement.
|
|
435
|
+
*/
|
|
436
|
+
dispose(): void;
|
|
437
|
+
/** Support `using` declarations for automatic staging cleanup. */
|
|
438
|
+
[Symbol.dispose](): void;
|
|
439
|
+
private _ensureTemporaryDirectory;
|
|
440
|
+
private _temporaryPath;
|
|
441
|
+
private _removeTemporaryFile;
|
|
442
|
+
private _entryData;
|
|
443
|
+
private _stagePart;
|
|
444
|
+
private _discardStagedPart;
|
|
445
|
+
private _materializeStagedParts;
|
|
417
446
|
private _loadWorkbookStructure;
|
|
418
447
|
private _ensureSharedStringsLoaded;
|
|
419
448
|
private _commitSharedStrings;
|
|
@@ -422,7 +451,10 @@ declare class XlsxUpdater {
|
|
|
422
451
|
private _buildSheetData;
|
|
423
452
|
private _valueCell;
|
|
424
453
|
private _stringCell;
|
|
454
|
+
private static _replaceOrAddXmlAttribute;
|
|
455
|
+
private _sharedStringRefCount;
|
|
425
456
|
private _patchSheetXml;
|
|
457
|
+
private _patchSheetFile;
|
|
426
458
|
private _updatePivotCaches;
|
|
427
459
|
private _addRefreshOnLoadToPivotTables;
|
|
428
460
|
}
|
|
@@ -445,6 +477,8 @@ interface ReplaceSheetDataOptions {
|
|
|
445
477
|
*/
|
|
446
478
|
styleFallback?: 'inherit' | 'general';
|
|
447
479
|
}
|
|
480
|
+
/** A one-pass synchronous or asynchronous source of worksheet rows. */
|
|
481
|
+
type RowSource = Iterable<ReadonlyArray<CellValue>> | AsyncIterable<ReadonlyArray<CellValue>>;
|
|
448
482
|
/**
|
|
449
483
|
* Update the data of a worksheet inside an existing XLSB file without rebuilding
|
|
450
484
|
* the workbook. Everything outside the target sheet's rows — pivot tables and
|
|
@@ -460,6 +494,8 @@ interface ReplaceSheetDataOptions {
|
|
|
460
494
|
declare class XlsbUpdater {
|
|
461
495
|
private readonly zip;
|
|
462
496
|
private readonly sourcePath;
|
|
497
|
+
private temporaryDirectory;
|
|
498
|
+
private readonly stagedParts;
|
|
463
499
|
private sheetNameToPath;
|
|
464
500
|
private pivotCacheDefPaths;
|
|
465
501
|
private sharedStringsBuf;
|
|
@@ -468,11 +504,12 @@ declare class XlsbUpdater {
|
|
|
468
504
|
private sharedStringsUnique;
|
|
469
505
|
private sharedStringsEndSst;
|
|
470
506
|
private stringIndexMap;
|
|
507
|
+
private sharedStringsDirty;
|
|
471
508
|
private readonly _oaEpoch;
|
|
472
509
|
/**
|
|
473
510
|
* @param path Path to an existing `.xlsb` file.
|
|
474
511
|
*/
|
|
475
|
-
constructor(
|
|
512
|
+
constructor(filePath: string);
|
|
476
513
|
/** Worksheet names in workbook order. */
|
|
477
514
|
getSheetNames(): string[];
|
|
478
515
|
/**
|
|
@@ -484,13 +521,32 @@ declare class XlsbUpdater {
|
|
|
484
521
|
* @param options See {@link ReplaceSheetDataOptions}.
|
|
485
522
|
*/
|
|
486
523
|
replaceSheetData(sheetName: string, rows: CellValue[][], options?: ReplaceSheetDataOptions): void;
|
|
524
|
+
/** Replace worksheet rows from a one-pass source. */
|
|
525
|
+
replaceSheetDataStream(sheetName: string, rows: RowSource, options?: ReplaceSheetDataOptions): Promise<void>;
|
|
487
526
|
/**
|
|
488
527
|
* Write the updated workbook to disk.
|
|
489
528
|
* @param outputPath Destination path; defaults to the source file (overwrites in place).
|
|
490
529
|
*/
|
|
491
530
|
save(outputPath?: string): void;
|
|
531
|
+
/** Save staged streaming replacements without materialising the ZIP. */
|
|
532
|
+
saveStreaming(outputPath?: string): Promise<void>;
|
|
492
533
|
/** The updated workbook as an in-memory ZIP buffer. */
|
|
493
534
|
toBuffer(): Buffer;
|
|
535
|
+
/**
|
|
536
|
+
* Release the staging directory and any unsaved staged parts.
|
|
537
|
+
* The updater remains usable; a new staging directory is created lazily
|
|
538
|
+
* on the next streaming replacement.
|
|
539
|
+
*/
|
|
540
|
+
dispose(): void;
|
|
541
|
+
/** Support `using` declarations for automatic staging cleanup. */
|
|
542
|
+
[Symbol.dispose](): void;
|
|
543
|
+
private _ensureTemporaryDirectory;
|
|
544
|
+
private _temporaryPath;
|
|
545
|
+
private _removeTemporaryFile;
|
|
546
|
+
private _entryData;
|
|
547
|
+
private _stagePart;
|
|
548
|
+
private _discardStagedPart;
|
|
549
|
+
private _materializeStagedParts;
|
|
494
550
|
private _loadWorkbookStructure;
|
|
495
551
|
private _appendCount;
|
|
496
552
|
private _ensureSharedStringsLoaded;
|
|
@@ -500,6 +556,8 @@ declare class XlsbUpdater {
|
|
|
500
556
|
private _buildRows;
|
|
501
557
|
private _valueCellBytes;
|
|
502
558
|
private _stringCellBytes;
|
|
559
|
+
private _sharedStringRefCount;
|
|
560
|
+
private _patchSheetMetadataFile;
|
|
503
561
|
private _findRowsRegion;
|
|
504
562
|
/**
|
|
505
563
|
* Keep the sheet dimension record (0x98) in sync with the new data.
|
|
@@ -511,6 +569,16 @@ declare class XlsbUpdater {
|
|
|
511
569
|
private _patchPivotCaches;
|
|
512
570
|
}
|
|
513
571
|
|
|
572
|
+
/** Options for an Excel format conversion. */
|
|
573
|
+
interface ExcelConversionOptions {
|
|
574
|
+
/** Allow replacing an existing destination file. Defaults to false. */
|
|
575
|
+
overwrite?: boolean;
|
|
576
|
+
}
|
|
577
|
+
/** Convert an Excel Binary Workbook (.xlsb) to an Open XML Workbook (.xlsx). */
|
|
578
|
+
declare function convertXlsbToXlsx(inputPath: string, outputPath: string, options?: ExcelConversionOptions): Promise<void>;
|
|
579
|
+
/** Convert an Open XML Workbook (.xlsx) to an Excel Binary Workbook (.xlsb). */
|
|
580
|
+
declare function convertXlsxToXlsb(inputPath: string, outputPath: string, options?: ExcelConversionOptions): Promise<void>;
|
|
581
|
+
|
|
514
582
|
/** Excel sheet name characters that must be replaced. */
|
|
515
583
|
declare const INVALID_SHEET_NAME_CHARS: RegExp;
|
|
516
584
|
/**
|
|
@@ -556,4 +624,4 @@ declare class StreamingSheetState {
|
|
|
556
624
|
end(): void;
|
|
557
625
|
}
|
|
558
626
|
|
|
559
|
-
export { BiffReaderWriter, BigBuffer, type CellValue, ExcelReaderAbstract, F, type FormattedCell, INVALID_SHEET_NAME_CHARS, type PrimitiveCellValue, ReaderFactory, type ReplaceSheetDataOptions$1 as ReplaceSheetDataOptions, StreamingSheetState, XlsbReader, type ReplaceSheetDataOptions as XlsbReplaceSheetDataOptions, XlsbUpdater, XlsbWriter, XlsxReader, XlsxUpdater, XlsxWriter, applyHeaderWidths, defaultColWidth, getFormat, initColWidths, isFormattedCell, sanitizeSheetName, unwrapCell, updateColWidthsFromRows };
|
|
627
|
+
export { BiffReaderWriter, BigBuffer, type CellValue, type ExcelConversionOptions, ExcelReaderAbstract, F, type FormattedCell, INVALID_SHEET_NAME_CHARS, type PrimitiveCellValue, ReaderFactory, type ReplaceSheetDataOptions$1 as ReplaceSheetDataOptions, type RowSource$1 as RowSource, StreamingSheetState, XlsbReader, type ReplaceSheetDataOptions as XlsbReplaceSheetDataOptions, XlsbUpdater, XlsbWriter, XlsxUpdater as XlsmUpdater, XlsxReader, XlsxUpdater, XlsxWriter, applyHeaderWidths, convertXlsbToXlsx, convertXlsxToXlsb, defaultColWidth, getFormat, initColWidths, isFormattedCell, sanitizeSheetName, unwrapCell, updateColWidthsFromRows };
|