@xlvane/compat 1.0.1 → 1.1.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/README.md +2 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +60 -16
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -57,8 +57,8 @@ The full list, with the exceljs source of each behaviour, is in [docs/exceljs-co
|
|
|
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
59
|
- **CSV:** text that starts with `=`, `+`, `-`, `@`, a tab, CR or LF 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:**
|
|
61
|
-
- **Views:** only the first view of a worksheet is written; a
|
|
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).
|
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;
|
package/dist/index.js
CHANGED
|
@@ -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;
|
|
@@ -5844,7 +5857,10 @@ var Workbook = class {
|
|
|
5844
5857
|
}
|
|
5845
5858
|
/** Adds a worksheet. Names follow exceljs rules (31 characters at most; longer names are cut). */
|
|
5846
5859
|
addWorksheet(name, options) {
|
|
5847
|
-
|
|
5860
|
+
return this._addWorksheetWithId(this.nextId, name, options);
|
|
5861
|
+
}
|
|
5862
|
+
/** @internal Adds a worksheet with a given id (a loaded file's sheetId, a model's id; D-F03). */
|
|
5863
|
+
_addWorksheetWithId(id, name, options) {
|
|
5848
5864
|
const sheet = new Worksheet(this, id, name, options);
|
|
5849
5865
|
sheet.orderNo = this._sheets.reduce((acc, ws) => ws?.orderNo !== void 0 && ws.orderNo > acc ? ws.orderNo : acc, 0) + 1;
|
|
5850
5866
|
this._sheets[id] = sheet;
|
|
@@ -5916,8 +5932,10 @@ var Workbook = class {
|
|
|
5916
5932
|
this.views = Array.isArray(value["views"]) ? [...value["views"]] : [];
|
|
5917
5933
|
this._media = Array.isArray(value["media"]) ? [...value["media"]] : [];
|
|
5918
5934
|
for (const ws of this.worksheets) this.removeWorksheet(ws.id);
|
|
5919
|
-
|
|
5920
|
-
|
|
5935
|
+
const models = Array.isArray(value["worksheets"]) ? value["worksheets"] : [];
|
|
5936
|
+
const ids = freeIds(models.map((m) => m["id"]));
|
|
5937
|
+
for (const [i, m] of models.entries()) {
|
|
5938
|
+
const ws = this._addWorksheetWithId(ids[i] ?? this.nextId, String(m["name"] ?? ""), { state: m["state"] ?? "visible" });
|
|
5921
5939
|
ws.model = m;
|
|
5922
5940
|
}
|
|
5923
5941
|
this._definedNames.model = Array.isArray(value["definedNames"]) ? value["definedNames"] : [];
|
|
@@ -6030,9 +6048,10 @@ var Workbook = class {
|
|
|
6030
6048
|
name: d.name,
|
|
6031
6049
|
ranges: extractRanges(d.refersTo)
|
|
6032
6050
|
}));
|
|
6051
|
+
const ids = freeIds(read.sheets.map((s) => s.kind === "worksheet" ? s.sheetId : void 0));
|
|
6033
6052
|
for (const [position, s] of read.sheets.entries()) {
|
|
6034
6053
|
if (s.kind !== "worksheet") continue;
|
|
6035
|
-
const ws = this.
|
|
6054
|
+
const ws = this._addWorksheetWithId(ids[position] ?? this.nextId, s.name, { state: s.state });
|
|
6036
6055
|
ws.orderNo = position;
|
|
6037
6056
|
for (const c of use("cols") ? s.columns : []) {
|
|
6038
6057
|
const last = Math.min(c.max, 16384);
|
|
@@ -6123,6 +6142,30 @@ var Workbook = class {
|
|
|
6123
6142
|
}
|
|
6124
6143
|
}
|
|
6125
6144
|
};
|
|
6145
|
+
/**
|
|
6146
|
+
* Worksheet ids for a list of wanted ids: each whole number from 1 to 65534 (the sheet ids Office
|
|
6147
|
+
* uses, W2-20) that is not wanted by an earlier entry is kept; other entries (missing, invalid,
|
|
6148
|
+
* larger or repeated ids) get the lowest ids no entry wants, in order. The limit keeps the
|
|
6149
|
+
* `_sheets` array small: an id from an untrusted file such as 4294967295 is not an array index and
|
|
6150
|
+
* 20000000 makes every scan of the array slow (found by review, D-F03). `undefined` entries
|
|
6151
|
+
* (sheets that are not worksheets) get no id.
|
|
6152
|
+
*/
|
|
6153
|
+
function freeIds(wanted) {
|
|
6154
|
+
const taken = /* @__PURE__ */ new Set();
|
|
6155
|
+
const kept = wanted.map((w) => {
|
|
6156
|
+
if (typeof w !== "number" || !Number.isInteger(w) || w < 1 || w > 65534 || taken.has(w)) return null;
|
|
6157
|
+
taken.add(w);
|
|
6158
|
+
return w;
|
|
6159
|
+
});
|
|
6160
|
+
let next = 1;
|
|
6161
|
+
return kept.map((k, i) => {
|
|
6162
|
+
if (wanted[i] === void 0) return void 0;
|
|
6163
|
+
if (k !== null) return k;
|
|
6164
|
+
while (taken.has(next)) next++;
|
|
6165
|
+
taken.add(next);
|
|
6166
|
+
return next;
|
|
6167
|
+
});
|
|
6168
|
+
}
|
|
6126
6169
|
// src/stream.ts
|
|
6127
6170
|
/**
|
|
6128
6171
|
* A cell value for the streaming writer. Rows are written as they are committed, before later
|
|
@@ -6284,6 +6327,7 @@ var WorksheetWriter = class extends Worksheet {
|
|
|
6284
6327
|
return {
|
|
6285
6328
|
name: this.name,
|
|
6286
6329
|
state: this.state,
|
|
6330
|
+
...sheetIdOf(this.id),
|
|
6287
6331
|
columns: trimTrailing(this.columns.map((c) => columnIn(c, this.writer._useStyles))),
|
|
6288
6332
|
...layout.freeze ? { freeze: layout.freeze } : {},
|
|
6289
6333
|
...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.0
|
|
3
|
+
"version": "1.1.0",
|
|
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.0
|
|
75
|
+
"xlvane": "^1.1.0"
|
|
76
76
|
},
|
|
77
77
|
"publishConfig": {
|
|
78
78
|
"access": "public",
|