@xlvane/compat 1.0.1 → 1.1.1
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/README.md +4 -3
- package/dist/index.d.ts +22 -6
- package/dist/index.js +149 -23
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -56,9 +56,9 @@ In CommonJS, `const ExcelJS = require('exceljs');` gives the same object; call `
|
|
|
56
56
|
The full list, with the exceljs source of each behaviour, is in [docs/exceljs-compat.md](https://github.com/DevXord/xlvane/blob/main/docs/exceljs-compat.md) (also in `node_modules/xlvane/docs/exceljs-compat.md` after installing). The ones most likely to matter:
|
|
57
57
|
|
|
58
58
|
- **Not offered:** row and column operations on a streamed worksheet (`insertRow(s)`, `spliceRows`, `duplicateRow`, `spliceColumns`; exceljs's streaming worksheet does not have them either) throw an `XlvError` with code `XLV_E_NOT_SUPPORTED` and the name of the method. Deep imports of exceljs internals (`exceljs/lib/...`) do not resolve; `exceljs/dist/es5` and the `exceljs/dist/exceljs*.js` files do. Nothing is dropped silently.
|
|
59
|
-
- **CSV:** text that starts with `=`, `+`, `-`, `@`, a tab, CR or
|
|
60
|
-
- **Sheet ids:**
|
|
61
|
-
- **Views:** only the first view of a worksheet is written; a
|
|
59
|
+
- **CSV:** text that starts with `=`, `+`, `-`, `@`, a tab, CR, LF or another control character, or with a full-width or small form of these signs, is written with a leading `'`, so spreadsheet applications do not run it as a formula. `formulaInjection: "off"` writes it unchanged, as exceljs does.
|
|
60
|
+
- **Sheet ids:** `worksheet.id` is written as `sheetId` and kept on loading, as in exceljs; an id outside 1 to 65534 is written as the lowest unused one, and the streaming `WorkbookReader` numbers worksheets 1, 2, 3… in file order.
|
|
61
|
+
- **Views:** only the first view of a worksheet is written; a frozen view always gets the cell right of and below the panes as `topLeftCell`.
|
|
62
62
|
- **Notes:** `protection.locked` and `lockText` read back as `'True'`/`'False'`, the values Excel writes.
|
|
63
63
|
- **Module object:** it is frozen, so adding or replacing its properties (monkey-patching) has no effect.
|
|
64
64
|
- **Streaming writer speed:** `stream.xlsx.WorkbookWriter` compresses in JavaScript (fflate) and took about twice as long as exceljs, which uses Node.js zlib (300 000 rows: 6.1 s against 2.8 s).
|
|
@@ -66,6 +66,7 @@ The full list, with the exceljs source of each behaviour, is in [docs/exceljs-co
|
|
|
66
66
|
|
|
67
67
|
## Links
|
|
68
68
|
|
|
69
|
+
- Website: [xlvane.github.io](https://xlvane.github.io)
|
|
69
70
|
- Changes in each version: [CHANGELOG.md](https://github.com/DevXord/xlvane/blob/main/CHANGELOG.md)
|
|
70
71
|
- Bugs and questions: [GitHub issues](https://github.com/DevXord/xlvane/issues)
|
|
71
72
|
- Reporting a vulnerability: [SECURITY.md](https://github.com/DevXord/xlvane/blob/main/SECURITY.md)
|
package/dist/index.d.ts
CHANGED
|
@@ -295,6 +295,8 @@ export declare class Workbook {
|
|
|
295
295
|
get nextId(): number;
|
|
296
296
|
/** Adds a worksheet. Names follow exceljs rules (31 characters at most; longer names are cut). */
|
|
297
297
|
addWorksheet(name?: string, options?: WorksheetOptions): Worksheet;
|
|
298
|
+
/** @internal Adds a worksheet with a given id (a loaded file's sheetId, a model's id; D-F03). */
|
|
299
|
+
_addWorksheetWithId(id: number, name?: string, options?: WorksheetOptions): Worksheet;
|
|
298
300
|
removeWorksheet(id: number | string): void;
|
|
299
301
|
/** Removes a worksheet by id or name, as `removeWorksheet` (lib/doc/workbook.js L77). */
|
|
300
302
|
removeWorksheetEx(worksheet: Worksheet): void;
|
|
@@ -2698,6 +2700,20 @@ export declare class Column {
|
|
|
2698
2700
|
get protection(): Partial<Protection> | undefined;
|
|
2699
2701
|
set protection(v: Partial<Protection> | undefined);
|
|
2700
2702
|
}
|
|
2703
|
+
/** A merged range of a worksheet. */
|
|
2704
|
+
type MergeRange = {
|
|
2705
|
+
top: number;
|
|
2706
|
+
left: number;
|
|
2707
|
+
bottom: number;
|
|
2708
|
+
right: number;
|
|
2709
|
+
};
|
|
2710
|
+
/** The merged ranges of a worksheet by master address, with a counter of changes (D-H01). */
|
|
2711
|
+
declare class MergeMap extends Map<string, MergeRange> {
|
|
2712
|
+
version: number;
|
|
2713
|
+
override set(key: string, value: MergeRange): this;
|
|
2714
|
+
override delete(key: string): boolean;
|
|
2715
|
+
override clear(): void;
|
|
2716
|
+
}
|
|
2701
2717
|
/**
|
|
2702
2718
|
* A worksheet, as exceljs `Worksheet` (lib/doc/worksheet.js).
|
|
2703
2719
|
*
|
|
@@ -2759,12 +2775,10 @@ export declare class Worksheet {
|
|
|
2759
2775
|
/** @internal */
|
|
2760
2776
|
_columns: Column[];
|
|
2761
2777
|
/** @internal */
|
|
2762
|
-
_merges:
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
right: number;
|
|
2767
|
-
}>;
|
|
2778
|
+
_merges: MergeMap;
|
|
2779
|
+
/** The cells covered by `_merges` (row × 16 385 + column), valid while `_mergedVersion` equals `_merges.version`. */
|
|
2780
|
+
private _mergedCells;
|
|
2781
|
+
private _mergedVersion;
|
|
2768
2782
|
private readonly _keys;
|
|
2769
2783
|
private _name;
|
|
2770
2784
|
/** @internal Use `workbook.addWorksheet()`. */
|
|
@@ -2927,6 +2941,8 @@ export declare class Worksheet {
|
|
|
2927
2941
|
mergeCellsWithoutStyle(...args: unknown[]): void;
|
|
2928
2942
|
private _mergeKeepStyle;
|
|
2929
2943
|
mergeCells(...args: unknown[]): void;
|
|
2944
|
+
/** The index of merged cells, rebuilt from `_merges` after any other change to it. */
|
|
2945
|
+
private _mergedIndex;
|
|
2930
2946
|
unMergeCells(...args: unknown[]): void;
|
|
2931
2947
|
get hasMerges(): boolean;
|
|
2932
2948
|
/** Adds conditional formatting for a range (exceljs `addConditionalFormatting`). */
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { XlvError, adjustFormula, adjustRange, createWorkbook, hashPassword, imageFormat, openWorkbookStream, readWorkbook, shiftFormula, writeWorkbook, writeWorkbookStream } from "xlvane";
|
|
1
|
+
import { DEFAULT_READ_LIMITS, XlvError, adjustFormula, adjustRange, createWorkbook, hashPassword, imageFormat, openWorkbookStream, readWorkbook, shiftFormula, writeWorkbook, writeWorkbookStream } from "xlvane";
|
|
2
2
|
// src/media.ts
|
|
3
3
|
const MAX_COLS = 16384;
|
|
4
4
|
const MAX_ROWS = 1048576;
|
|
@@ -1228,9 +1228,10 @@ function noteBoxOut(box) {
|
|
|
1228
1228
|
* The first exceljs view and the tab colour (X4-11, X4-12). xlvane writes one view: frozen panes
|
|
1229
1229
|
* (`xSplit`/`ySplit` in columns and rows) or a split window (`xSplit`/`ySplit` in 1/20 point, as
|
|
1230
1230
|
* exceljs), plus `zoomScale`, `showGridLines`, `showRowColHeaders`, `rightToLeft` and `style`.
|
|
1231
|
-
* `activeCell` is written for every view; `topLeftCell`
|
|
1232
|
-
*
|
|
1233
|
-
* follows from the panes
|
|
1231
|
+
* `activeCell` is written for every view; `topLeftCell` for a normal view as
|
|
1232
|
+
* `sheetView@topLeftCell` and for a split view as `pane@topLeftCell`, as exceljs writes them
|
|
1233
|
+
* (sheet-view-xform.js L72-85; D-F02); a frozen view's `topLeftCell` follows from the panes;
|
|
1234
|
+
* `activePane` follows from the panes.
|
|
1234
1235
|
*/
|
|
1235
1236
|
function layoutIn(ws) {
|
|
1236
1237
|
const v = ws.views[0];
|
|
@@ -1251,10 +1252,14 @@ function layoutIn(ws) {
|
|
|
1251
1252
|
rows: v.ySplit ?? 0,
|
|
1252
1253
|
cols: v.xSplit ?? 0
|
|
1253
1254
|
};
|
|
1254
|
-
else if (v.state === "split" && ((v.xSplit ?? 0) > 0 || (v.ySplit ?? 0) > 0))
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1255
|
+
else if (v.state === "split" && ((v.xSplit ?? 0) > 0 || (v.ySplit ?? 0) > 0)) {
|
|
1256
|
+
const corner = typeof v["topLeftCell"] === "string" && v["topLeftCell"] !== "" ? { topLeftCell: v["topLeftCell"] } : {};
|
|
1257
|
+
view.split = {
|
|
1258
|
+
x: (v.xSplit ?? 0) / 20,
|
|
1259
|
+
y: (v.ySplit ?? 0) / 20,
|
|
1260
|
+
...corner
|
|
1261
|
+
};
|
|
1262
|
+
}
|
|
1258
1263
|
}
|
|
1259
1264
|
let tab = ws.properties["tabColor"];
|
|
1260
1265
|
if (tab && typeof tab.argb === "string" && /^[0-9A-Fa-f]{7}$/.test(tab.argb)) tab = {
|
|
@@ -1356,7 +1361,10 @@ function toNewWorkbook(sheets, date1904, meta = {}) {
|
|
|
1356
1361
|
if (meta.views && meta.views.length > 0) book.views(bookViewsIn(meta.views));
|
|
1357
1362
|
if (meta.calcProperties?.["fullCalcOnLoad"]) book.calculation({ fullCalcOnLoad: true });
|
|
1358
1363
|
for (const ws of sheets) at(ws.name, void 0, () => {
|
|
1359
|
-
const sheet = book.addSheet(ws.name, {
|
|
1364
|
+
const sheet = book.addSheet(ws.name, {
|
|
1365
|
+
state: ws.state,
|
|
1366
|
+
...sheetIdOf(ws.id)
|
|
1367
|
+
});
|
|
1360
1368
|
const useStyles = meta.useStyles !== false;
|
|
1361
1369
|
const sharedRanges = sharedFormulaRanges(ws);
|
|
1362
1370
|
const props = sheetPropertiesIn(ws.properties);
|
|
@@ -1487,8 +1495,9 @@ function fontOut(f) {
|
|
|
1487
1495
|
/**
|
|
1488
1496
|
* An exceljs view from the view read by xlvane, shaped as exceljs reads one (sheet-view-xform.js
|
|
1489
1497
|
* L145–192; X4-11): a missing `zoomScaleNormal` reads as 100; a frozen view gets the default
|
|
1490
|
-
* `topLeftCell`; a split view gets `xSplit`/`ySplit` in 1/20 point
|
|
1491
|
-
*
|
|
1498
|
+
* `topLeftCell`; a split view gets `xSplit`/`ySplit` in 1/20 point, `pane@topLeftCell` as
|
|
1499
|
+
* `topLeftCell` (sheet-view-xform.js L118-124) and the active pane xlvane writes; a normal view
|
|
1500
|
+
* has no `topLeftCell`, as in exceljs.
|
|
1492
1501
|
*/
|
|
1493
1502
|
function viewOut(view, freeze) {
|
|
1494
1503
|
const common = {
|
|
@@ -1516,14 +1525,14 @@ function viewOut(view, freeze) {
|
|
|
1516
1525
|
}
|
|
1517
1526
|
const active = view.activeCell !== null ? { activeCell: view.activeCell } : {};
|
|
1518
1527
|
if (view.split) {
|
|
1519
|
-
const { x, y } = view.split;
|
|
1528
|
+
const { x, y, topLeftCell } = view.split;
|
|
1520
1529
|
const activePane = x > 0 && y > 0 ? "bottomRight" : y > 0 ? "bottomLeft" : "topRight";
|
|
1521
1530
|
return {
|
|
1522
1531
|
...common,
|
|
1523
1532
|
state: "split",
|
|
1524
1533
|
xSplit: Math.round(x * 20),
|
|
1525
1534
|
ySplit: Math.round(y * 20),
|
|
1526
|
-
topLeftCell
|
|
1535
|
+
topLeftCell,
|
|
1527
1536
|
activePane,
|
|
1528
1537
|
...active,
|
|
1529
1538
|
...style
|
|
@@ -1665,6 +1674,10 @@ function styleOut(s, numFmt) {
|
|
|
1665
1674
|
function readBytes(bytes) {
|
|
1666
1675
|
return readWorkbook(bytes);
|
|
1667
1676
|
}
|
|
1677
|
+
/** `{ sheetId }` for a worksheet id that Office accepts (1 to 65534, W2-20), otherwise nothing (D-F03). */
|
|
1678
|
+
function sheetIdOf(id) {
|
|
1679
|
+
return Number.isInteger(id) && id >= 1 && id <= 65534 ? { sheetId: id } : {};
|
|
1680
|
+
}
|
|
1668
1681
|
// src/sheet-rules.ts
|
|
1669
1682
|
const MAX_ROW = 1048576;
|
|
1670
1683
|
const MAX_COL = 16384;
|
|
@@ -3615,6 +3628,22 @@ function parseRangeArgs(args) {
|
|
|
3615
3628
|
right: Math.max(s.col, e.col)
|
|
3616
3629
|
};
|
|
3617
3630
|
}
|
|
3631
|
+
/** The merged ranges of a worksheet by master address, with a counter of changes (D-H01). */
|
|
3632
|
+
var MergeMap = class extends Map {
|
|
3633
|
+
version = 0;
|
|
3634
|
+
set(key, value) {
|
|
3635
|
+
this.version++;
|
|
3636
|
+
return super.set(key, value);
|
|
3637
|
+
}
|
|
3638
|
+
delete(key) {
|
|
3639
|
+
this.version++;
|
|
3640
|
+
return super.delete(key);
|
|
3641
|
+
}
|
|
3642
|
+
clear() {
|
|
3643
|
+
this.version++;
|
|
3644
|
+
super.clear();
|
|
3645
|
+
}
|
|
3646
|
+
};
|
|
3618
3647
|
/**
|
|
3619
3648
|
* A worksheet, as exceljs `Worksheet` (lib/doc/worksheet.js).
|
|
3620
3649
|
*
|
|
@@ -3660,7 +3689,10 @@ var Worksheet = class {
|
|
|
3660
3689
|
/** @internal */
|
|
3661
3690
|
_columns = [];
|
|
3662
3691
|
/** @internal */
|
|
3663
|
-
_merges =
|
|
3692
|
+
_merges = new MergeMap();
|
|
3693
|
+
/** The cells covered by `_merges` (row × 16 385 + column), valid while `_mergedVersion` equals `_merges.version`. */
|
|
3694
|
+
_mergedCells = /* @__PURE__ */ new Set();
|
|
3695
|
+
_mergedVersion = -1;
|
|
3664
3696
|
_keys = /* @__PURE__ */ new Map();
|
|
3665
3697
|
_name = "";
|
|
3666
3698
|
/** @internal Use `workbook.addWorksheet()`. */
|
|
@@ -4365,7 +4397,8 @@ var Worksheet = class {
|
|
|
4365
4397
|
_mergeKeepStyle = false;
|
|
4366
4398
|
mergeCells(...args) {
|
|
4367
4399
|
const range = parseRangeArgs(args);
|
|
4368
|
-
|
|
4400
|
+
const merged = this._mergedIndex();
|
|
4401
|
+
for (let r = range.top; r <= range.bottom; r++) for (let c = range.left; c <= range.right; c++) if (merged.has(r * 16385 + c)) throw new Error("Cannot merge already merged cells");
|
|
4369
4402
|
const master = this.getCell(range.top, range.left);
|
|
4370
4403
|
for (let r = range.top; r <= range.bottom; r++) for (let c = range.left; c <= range.right; c++) {
|
|
4371
4404
|
if (r === range.top && c === range.left) continue;
|
|
@@ -4375,6 +4408,17 @@ var Worksheet = class {
|
|
|
4375
4408
|
}
|
|
4376
4409
|
master._mergeCount++;
|
|
4377
4410
|
this._merges.set(`${columnLetter(range.left)}${String(range.top)}`, range);
|
|
4411
|
+
for (let r = range.top; r <= range.bottom; r++) for (let c = range.left; c <= range.right; c++) merged.add(r * 16385 + c);
|
|
4412
|
+
this._mergedVersion = this._merges.version;
|
|
4413
|
+
}
|
|
4414
|
+
/** The index of merged cells, rebuilt from `_merges` after any other change to it. */
|
|
4415
|
+
_mergedIndex() {
|
|
4416
|
+
if (this._mergedVersion !== this._merges.version) {
|
|
4417
|
+
this._mergedCells = /* @__PURE__ */ new Set();
|
|
4418
|
+
for (const m of this._merges.values()) for (let r = m.top; r <= m.bottom; r++) for (let c = m.left; c <= m.right; c++) this._mergedCells.add(r * 16385 + c);
|
|
4419
|
+
this._mergedVersion = this._merges.version;
|
|
4420
|
+
}
|
|
4421
|
+
return this._mergedCells;
|
|
4378
4422
|
}
|
|
4379
4423
|
unMergeCells(...args) {
|
|
4380
4424
|
const range = parseRangeArgs(args);
|
|
@@ -4562,7 +4606,11 @@ const INJECTION_START = /* @__PURE__ */ new Set([
|
|
|
4562
4606
|
"=",
|
|
4563
4607
|
"+",
|
|
4564
4608
|
"-",
|
|
4565
|
-
"@"
|
|
4609
|
+
"@",
|
|
4610
|
+
"﹦",
|
|
4611
|
+
"﹢",
|
|
4612
|
+
"﹣",
|
|
4613
|
+
"﹫"
|
|
4566
4614
|
]);
|
|
4567
4615
|
function invalid(name, message, hint) {
|
|
4568
4616
|
return new XlvError("XLV_E_INVALID_OPTION", message, {
|
|
@@ -5019,7 +5067,7 @@ function worksheetCsv(sheet, options) {
|
|
|
5019
5067
|
const escape = (out) => {
|
|
5020
5068
|
if (injection !== "escape" || typeof out !== "string") return [out, false];
|
|
5021
5069
|
const first = out.replace(/\0/g, "")[0];
|
|
5022
|
-
return first !== void 0 && INJECTION_START.has(first) ? [`'${out}`, f.quote !== ""] : [out, false];
|
|
5070
|
+
return first !== void 0 && (INJECTION_START.has(first) || first.charCodeAt(0) < 32) ? [`'${out}`, f.quote !== ""] : [out, false];
|
|
5023
5071
|
};
|
|
5024
5072
|
if (f.headers) lines.push(f.headers.map((h, i) => {
|
|
5025
5073
|
const [out, force] = escape(h);
|
|
@@ -5844,7 +5892,10 @@ var Workbook = class {
|
|
|
5844
5892
|
}
|
|
5845
5893
|
/** Adds a worksheet. Names follow exceljs rules (31 characters at most; longer names are cut). */
|
|
5846
5894
|
addWorksheet(name, options) {
|
|
5847
|
-
|
|
5895
|
+
return this._addWorksheetWithId(this.nextId, name, options);
|
|
5896
|
+
}
|
|
5897
|
+
/** @internal Adds a worksheet with a given id (a loaded file's sheetId, a model's id; D-F03). */
|
|
5898
|
+
_addWorksheetWithId(id, name, options) {
|
|
5848
5899
|
const sheet = new Worksheet(this, id, name, options);
|
|
5849
5900
|
sheet.orderNo = this._sheets.reduce((acc, ws) => ws?.orderNo !== void 0 && ws.orderNo > acc ? ws.orderNo : acc, 0) + 1;
|
|
5850
5901
|
this._sheets[id] = sheet;
|
|
@@ -5916,8 +5967,10 @@ var Workbook = class {
|
|
|
5916
5967
|
this.views = Array.isArray(value["views"]) ? [...value["views"]] : [];
|
|
5917
5968
|
this._media = Array.isArray(value["media"]) ? [...value["media"]] : [];
|
|
5918
5969
|
for (const ws of this.worksheets) this.removeWorksheet(ws.id);
|
|
5919
|
-
|
|
5920
|
-
|
|
5970
|
+
const models = Array.isArray(value["worksheets"]) ? value["worksheets"] : [];
|
|
5971
|
+
const ids = freeIds(models.map((m) => m["id"]));
|
|
5972
|
+
for (const [i, m] of models.entries()) {
|
|
5973
|
+
const ws = this._addWorksheetWithId(ids[i] ?? this.nextId, String(m["name"] ?? ""), { state: m["state"] ?? "visible" });
|
|
5921
5974
|
ws.model = m;
|
|
5922
5975
|
}
|
|
5923
5976
|
this._definedNames.model = Array.isArray(value["definedNames"]) ? value["definedNames"] : [];
|
|
@@ -5967,6 +6020,24 @@ var Workbook = class {
|
|
|
5967
6020
|
_fill(read, options = {}) {
|
|
5968
6021
|
const ignore = new Set(options.ignoreNodes ?? []);
|
|
5969
6022
|
const use = (node) => !ignore.has(node);
|
|
6023
|
+
let cellsRead = 0;
|
|
6024
|
+
for (const s of read.sheets) cellsRead += s.cellCount;
|
|
6025
|
+
const budget = Math.max(DEFAULT_READ_LIMITS.maxCells, 16 * cellsRead);
|
|
6026
|
+
let spent = 0;
|
|
6027
|
+
const spend = (positions, what, sheet) => {
|
|
6028
|
+
spent += positions;
|
|
6029
|
+
if (spent <= budget) return;
|
|
6030
|
+
throw new XlvError("XLV_E_LIMIT_EXCEEDED", `${what} of worksheet "${sheet}" would create more than ${String(budget)} cells or columns for ${String(cellsRead)} cells in the file.`, {
|
|
6031
|
+
location: { sheet },
|
|
6032
|
+
hint: "The file declares ranges far larger than its data. Load it with ignoreNodes for that element (for example ignoreNodes: [\"mergeCells\"]), or read it with xlvane's readWorkbook, which does not create these objects.",
|
|
6033
|
+
details: {
|
|
6034
|
+
limit: "maxCells",
|
|
6035
|
+
value: budget
|
|
6036
|
+
}
|
|
6037
|
+
});
|
|
6038
|
+
};
|
|
6039
|
+
const rowBudget = Math.max(67108864, 16 * cellsRead);
|
|
6040
|
+
let rowsLooked = 0;
|
|
5970
6041
|
for (const s of read.sheets) {
|
|
5971
6042
|
if (s.kind !== "worksheet" || options.maxRows === void 0 && options.maxCols === void 0 || !use("sheetData")) continue;
|
|
5972
6043
|
const perRow = /* @__PURE__ */ new Map();
|
|
@@ -6030,12 +6101,14 @@ var Workbook = class {
|
|
|
6030
6101
|
name: d.name,
|
|
6031
6102
|
ranges: extractRanges(d.refersTo)
|
|
6032
6103
|
}));
|
|
6104
|
+
const ids = freeIds(read.sheets.map((s) => s.kind === "worksheet" ? s.sheetId : void 0));
|
|
6033
6105
|
for (const [position, s] of read.sheets.entries()) {
|
|
6034
6106
|
if (s.kind !== "worksheet") continue;
|
|
6035
|
-
const ws = this.
|
|
6107
|
+
const ws = this._addWorksheetWithId(ids[position] ?? this.nextId, s.name, { state: s.state });
|
|
6036
6108
|
ws.orderNo = position;
|
|
6037
6109
|
for (const c of use("cols") ? s.columns : []) {
|
|
6038
6110
|
const last = Math.min(c.max, 16384);
|
|
6111
|
+
spend(Math.max(0, last - c.min + 1), "The column definitions", s.name);
|
|
6039
6112
|
for (let n = c.min; n <= last; n++) {
|
|
6040
6113
|
const column = ws.getColumn(n);
|
|
6041
6114
|
if (c.width !== null) column.width = c.width;
|
|
@@ -6089,6 +6162,15 @@ var Workbook = class {
|
|
|
6089
6162
|
}
|
|
6090
6163
|
const dim = /(\d+)$/.exec(s.dimension ?? "");
|
|
6091
6164
|
lastRow = use("sheetData") ? Math.min(Math.max(lastRow, dim ? Number(dim[1]) : 0), 1048576) : 0;
|
|
6165
|
+
rowsLooked += lastRow;
|
|
6166
|
+
if (rowsLooked > rowBudget) throw new XlvError("XLV_E_LIMIT_EXCEEDED", `The rows of worksheet "${s.name}" up to its dimension or last cell would bring the rows looked at for their settings past ${String(rowBudget)} for ${String(cellsRead)} cells in the file.`, {
|
|
6167
|
+
location: { sheet: s.name },
|
|
6168
|
+
hint: "The sheets declare far more rows than they hold. Load the file with ignoreNodes: [\"sheetData\"] to skip cells and rows, or read it with xlvane's readWorkbook.",
|
|
6169
|
+
details: {
|
|
6170
|
+
limit: "maxCells",
|
|
6171
|
+
value: rowBudget
|
|
6172
|
+
}
|
|
6173
|
+
});
|
|
6092
6174
|
for (let r = 1; r <= lastRow; r++) {
|
|
6093
6175
|
const info = s.rowInfo(r);
|
|
6094
6176
|
if (!info) continue;
|
|
@@ -6098,7 +6180,11 @@ var Workbook = class {
|
|
|
6098
6180
|
if (info.outlineLevel) row.outlineLevel = info.outlineLevel;
|
|
6099
6181
|
}
|
|
6100
6182
|
for (const note of s.notes) ws.getCell(note.ref).note = noteOut(note);
|
|
6101
|
-
for (const m of use("mergeCells") ? s.merges : [])
|
|
6183
|
+
for (const m of use("mergeCells") ? s.merges : []) {
|
|
6184
|
+
if (!m.includes(":")) continue;
|
|
6185
|
+
spend(areaOf(m), "A merged range", s.name);
|
|
6186
|
+
ws.mergeCells(m);
|
|
6187
|
+
}
|
|
6102
6188
|
if (use("sheetViews")) ws.views = [viewOut(s.view, s.freeze)];
|
|
6103
6189
|
const tab = colorOut(s.tabColor ?? void 0);
|
|
6104
6190
|
if (tab && use("sheetPr")) ws.properties["tabColor"] = tab;
|
|
@@ -6109,7 +6195,10 @@ var Workbook = class {
|
|
|
6109
6195
|
if (use("pageSetup")) ws.pageSetup = pageSetupOut(s.pageSetup, s.printArea, s.printTitles);
|
|
6110
6196
|
if (use("headerFooter")) ws.headerFooter = headerFooterOut(s.headerFooter);
|
|
6111
6197
|
if (use("rowBreaks")) ws.rowBreaks = rowBreaksOut(s.pageBreaks.rows);
|
|
6112
|
-
for (const def of use("tableParts") ? s.tables : [])
|
|
6198
|
+
for (const def of use("tableParts") ? s.tables : []) {
|
|
6199
|
+
spend(areaOf(def.ref), "A table", s.name);
|
|
6200
|
+
ws.tables[def.name] = tableOut(ws, def);
|
|
6201
|
+
}
|
|
6113
6202
|
for (const img of use("drawing") ? s.images : []) {
|
|
6114
6203
|
const id = img.image === null ? void 0 : mediaIds.get(img.image);
|
|
6115
6204
|
const media = id === void 0 ? null : imageOut(ws, img, id);
|
|
@@ -6123,6 +6212,42 @@ var Workbook = class {
|
|
|
6123
6212
|
}
|
|
6124
6213
|
}
|
|
6125
6214
|
};
|
|
6215
|
+
/**
|
|
6216
|
+
* Worksheet ids for a list of wanted ids: each whole number from 1 to 65534 (the sheet ids Office
|
|
6217
|
+
* uses, W2-20) that is not wanted by an earlier entry is kept; other entries (missing, invalid,
|
|
6218
|
+
* larger or repeated ids) get the lowest ids no entry wants, in order. The limit keeps the
|
|
6219
|
+
* `_sheets` array small: an id from an untrusted file such as 4294967295 is not an array index and
|
|
6220
|
+
* 20000000 makes every scan of the array slow (found by review, D-F03). `undefined` entries
|
|
6221
|
+
* (sheets that are not worksheets) get no id.
|
|
6222
|
+
*/
|
|
6223
|
+
function freeIds(wanted) {
|
|
6224
|
+
const taken = /* @__PURE__ */ new Set();
|
|
6225
|
+
const kept = wanted.map((w) => {
|
|
6226
|
+
if (typeof w !== "number" || !Number.isInteger(w) || w < 1 || w > 65534 || taken.has(w)) return null;
|
|
6227
|
+
taken.add(w);
|
|
6228
|
+
return w;
|
|
6229
|
+
});
|
|
6230
|
+
let next = 1;
|
|
6231
|
+
return kept.map((k, i) => {
|
|
6232
|
+
if (wanted[i] === void 0) return void 0;
|
|
6233
|
+
if (k !== null) return k;
|
|
6234
|
+
while (taken.has(next)) next++;
|
|
6235
|
+
taken.add(next);
|
|
6236
|
+
return next;
|
|
6237
|
+
});
|
|
6238
|
+
}
|
|
6239
|
+
/** Cells in a range such as "A1:C3" (1 for a cell, 0 for anything else). */
|
|
6240
|
+
function areaOf(ref) {
|
|
6241
|
+
const m = /^([A-Z]{1,3})(\d{1,7})(?::([A-Z]{1,3})(\d{1,7}))?$/.exec(ref.replace(/\$/g, ""));
|
|
6242
|
+
if (!m) return 0;
|
|
6243
|
+
const col = (letters) => {
|
|
6244
|
+
let n = 0;
|
|
6245
|
+
for (const ch of letters) n = n * 26 + ch.charCodeAt(0) - 64;
|
|
6246
|
+
return n;
|
|
6247
|
+
};
|
|
6248
|
+
const [, c1 = "A", r1 = "1", c2 = c1, r2 = r1] = m;
|
|
6249
|
+
return (Math.abs(Number(r2) - Number(r1)) + 1) * (Math.abs(col(c2) - col(c1)) + 1);
|
|
6250
|
+
}
|
|
6126
6251
|
// src/stream.ts
|
|
6127
6252
|
/**
|
|
6128
6253
|
* A cell value for the streaming writer. Rows are written as they are committed, before later
|
|
@@ -6284,6 +6409,7 @@ var WorksheetWriter = class extends Worksheet {
|
|
|
6284
6409
|
return {
|
|
6285
6410
|
name: this.name,
|
|
6286
6411
|
state: this.state,
|
|
6412
|
+
...sheetIdOf(this.id),
|
|
6287
6413
|
columns: trimTrailing(this.columns.map((c) => columnIn(c, this.writer._useStyles))),
|
|
6288
6414
|
...layout.freeze ? { freeze: layout.freeze } : {},
|
|
6289
6415
|
...Object.keys(layout.view).length > 0 ? { view: layout.view } : {},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xlvane/compat",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "The commonly used exceljs 4.4.0 API on top of xlvane, for switching with one line in package.json.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"xlsx",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"build": "tsdown"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"xlvane": "^1.
|
|
75
|
+
"xlvane": "^1.1.1"
|
|
76
76
|
},
|
|
77
77
|
"publishConfig": {
|
|
78
78
|
"access": "public",
|