@xlvane/compat 1.3.0 → 1.3.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/dist/index.d.ts +23 -6
- package/dist/index.js +405 -106
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StreamSheetSpec, Workbook as Workbook$1, WorkbookProperties as WorkbookProperties$1 } from "xlvane";
|
|
1
|
+
import { FontStyle, StreamSheetSpec, Workbook as Workbook$1, WorkbookProperties as WorkbookProperties$1 } from "xlvane";
|
|
2
2
|
/**
|
|
3
3
|
* A cell location of a defined name, as exceljs keeps it.
|
|
4
4
|
*
|
|
@@ -150,6 +150,8 @@ interface WorkbookMeta {
|
|
|
150
150
|
readonly views?: readonly Record<string, unknown>[];
|
|
151
151
|
/** `workbook.calcProperties`. */
|
|
152
152
|
readonly calcProperties?: Record<string, unknown>;
|
|
153
|
+
/** The default font (font 0) of a loaded workbook (F4). */
|
|
154
|
+
readonly defaultFont?: FontStyle;
|
|
153
155
|
}
|
|
154
156
|
/**
|
|
155
157
|
* Options of `xlsx.load`, `read` and `readFile`, as exceljs `XlsxReadOptions` (index.d.ts L1460;
|
|
@@ -287,6 +289,8 @@ export declare class Workbook {
|
|
|
287
289
|
get definedNames(): DefinedNames;
|
|
288
290
|
/** @internal The document properties and defined names to write, as exceljs `workbook.model`. */
|
|
289
291
|
_meta(): WorkbookMeta;
|
|
292
|
+
/** @internal The default font (font 0) of the file this workbook was loaded from, written again (F4); `null` for a workbook made in code. */
|
|
293
|
+
_defaultFont: FontStyle | null;
|
|
290
294
|
/** @internal */
|
|
291
295
|
get _date1904(): boolean;
|
|
292
296
|
private _byName;
|
|
@@ -405,7 +409,7 @@ interface CsvWriteOptions {
|
|
|
405
409
|
map?(value: unknown, index: number): unknown;
|
|
406
410
|
/** Blank lines for missing rows (default `true`). */
|
|
407
411
|
includeEmptyRows?: boolean;
|
|
408
|
-
/** fast-csv formatter options: `delimiter`, `rowDelimiter`, `quote`, `escape`, `quoteColumns`, `headers`, `writeHeaders`, `includeEndRowDelimiter`, `writeBOM`. */
|
|
412
|
+
/** fast-csv formatter options: `delimiter`, `rowDelimiter`, `quote`, `escape`, `quoteColumns`, `quoteHeaders`, `headers`, `writeHeaders`, `alwaysWriteHeaders`, `includeEndRowDelimiter`, `writeBOM`, `transform`. */
|
|
409
413
|
formatterOptions?: Record<string, unknown>;
|
|
410
414
|
/**
|
|
411
415
|
* `"escape"` (default): text that starts with `=`, `+`, `-`, `@`, tab, CR, LF or another
|
|
@@ -2460,6 +2464,8 @@ export declare class Cell {
|
|
|
2460
2464
|
private _moved;
|
|
2461
2465
|
/** @internal Use `worksheet.getCell()`. */
|
|
2462
2466
|
constructor(_row: Row, _col: number);
|
|
2467
|
+
/** The style a new cell takes: row style wins over column style, part by part (lib/doc/cell.js L99-119 `_mergeStyle`). */
|
|
2468
|
+
private _rowColumnStyle;
|
|
2463
2469
|
get worksheet(): Worksheet;
|
|
2464
2470
|
get row(): number;
|
|
2465
2471
|
get col(): number;
|
|
@@ -2482,7 +2488,12 @@ export declare class Cell {
|
|
|
2482
2488
|
* this cell, as exceljs translates it (lib/doc/cell.js `_getTranslatedFormula`; X4-29).
|
|
2483
2489
|
*/
|
|
2484
2490
|
get formula(): string | undefined;
|
|
2485
|
-
/**
|
|
2491
|
+
/**
|
|
2492
|
+
* `FormulaType.Shared` for a `{ sharedFormula }` value, `Master` for other formulas, and
|
|
2493
|
+
* `undefined` for a cell without a formula and for a merged cell other than the top-left one,
|
|
2494
|
+
* as in exceljs, where only the formula value has a `formulaType` (lib/doc/cell.js L264-266,
|
|
2495
|
+
* L807-815; exceljs's declaration says `FormulaType` all the same).
|
|
2496
|
+
*/
|
|
2486
2497
|
get formulaType(): FormulaType;
|
|
2487
2498
|
get result(): unknown;
|
|
2488
2499
|
get numFmt(): string | undefined;
|
|
@@ -2559,6 +2570,11 @@ export declare class Cell {
|
|
|
2559
2570
|
_merge(master: Cell): void;
|
|
2560
2571
|
/** @internal */
|
|
2561
2572
|
_unmerge(): void;
|
|
2573
|
+
/**
|
|
2574
|
+
* @internal As exceljs `cell.unmerge()` (lib/doc/cell.js L177-183): a cell merged into another
|
|
2575
|
+
* one gets the row and column style again, instead of the master's.
|
|
2576
|
+
*/
|
|
2577
|
+
_unmergeResetStyle(): void;
|
|
2562
2578
|
}
|
|
2563
2579
|
/**
|
|
2564
2580
|
* A row, as exceljs `Row` (lib/doc/row.js).
|
|
@@ -2708,7 +2724,7 @@ export declare class Column {
|
|
|
2708
2724
|
equivalentTo(other: Column): boolean;
|
|
2709
2725
|
get defn(): ColumnDefn;
|
|
2710
2726
|
set defn(value: ColumnDefn | undefined);
|
|
2711
|
-
/** Cell values of this column by row number (index 0 unused). */
|
|
2727
|
+
/** Cell values of this column by row number (index 0 unused); empty cells are left out (lib/doc/column.js L179-187). */
|
|
2712
2728
|
get values(): CellValue[];
|
|
2713
2729
|
set values(values: CellValue[]);
|
|
2714
2730
|
eachCell(options: {
|
|
@@ -2913,8 +2929,9 @@ export declare class Worksheet {
|
|
|
2913
2929
|
}[], change: SpliceChange): void;
|
|
2914
2930
|
/**
|
|
2915
2931
|
* @internal Merges a range without the overlap check of `mergeCells`. Cells keep their own
|
|
2916
|
-
* style
|
|
2917
|
-
*
|
|
2932
|
+
* style object (the master's, when `mergeCells` shared it with them), except those for which
|
|
2933
|
+
* `takeMasterStyle` returns true (cells inserted inside the range), which get a copy of the
|
|
2934
|
+
* master's style.
|
|
2918
2935
|
*/
|
|
2919
2936
|
_mergeRange(range: {
|
|
2920
2937
|
top: number;
|
package/dist/index.js
CHANGED
|
@@ -773,6 +773,67 @@ const BUILTIN_CODES = /* @__PURE__ */ new Map([
|
|
|
773
773
|
["##0.0E+0", 48],
|
|
774
774
|
["@", 49]
|
|
775
775
|
]);
|
|
776
|
+
const DAY_MS = 864e5;
|
|
777
|
+
/** exceljs's serial 0 in the 1900 date system (lib/utils/utils.js L55-61: 25569 days before 1970). */
|
|
778
|
+
const EPOCH_1899_12_30 = Date.UTC(1899, 11, 30);
|
|
779
|
+
/** Serial 0 of the core's Transitional 1900 system for 1900-01-01 to 1900-02-28 (D-112). */
|
|
780
|
+
const EPOCH_1899_12_31 = Date.UTC(1899, 11, 31);
|
|
781
|
+
const EPOCH_1904_01_01 = Date.UTC(1904, 0, 1);
|
|
782
|
+
const TIME_1900_01_01 = Date.UTC(1900, 0, 1);
|
|
783
|
+
const MIN_DATE_TIME = (/* @__PURE__ */ new Date(0)).setUTCFullYear(1, 0, 1);
|
|
784
|
+
const MAX_DATE_TIME = Date.UTC(9999, 11, 31, 23, 59, 59, 999);
|
|
785
|
+
/**
|
|
786
|
+
* The serial exceljs writes for a Date from 1899-12-30 to the end of 1899 in the 1900 date system
|
|
787
|
+
* (time-only values such as `new Date(Date.UTC(1899, 11, 30, 13, 20))`): `25569 + ms / 86400000`
|
|
788
|
+
* (lib/utils/utils.js L55-57 `dateToExcel`). The core refuses these dates, so they are written
|
|
789
|
+
* as numbers with the cell's date format. `undefined` for other values.
|
|
790
|
+
*/
|
|
791
|
+
function earlyDateSerial(v, date1904) {
|
|
792
|
+
if (date1904 || !(v instanceof Date)) return void 0;
|
|
793
|
+
const t = v.getTime();
|
|
794
|
+
return t >= EPOCH_1899_12_30 && t < TIME_1900_01_01 ? (t - EPOCH_1899_12_30) / DAY_MS : void 0;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* exceljs's test for a date format (lib/utils/utils.js L140-151 `isDateFmt`): after removing the
|
|
798
|
+
* parts in brackets and in double quotes, the format has one of the letters y, m, d, h, M, s, b.
|
|
799
|
+
* It also takes upper-case formats such as `DD.MM.YYYY`, which the core does not.
|
|
800
|
+
*/
|
|
801
|
+
function isExcelJsDateFormat(fmt) {
|
|
802
|
+
if (!fmt) return false;
|
|
803
|
+
return /[ymdhMsb]/.test(fmt.replace(/\[[^\]]*]/g, "").replace(/"[^"]*"/g, ""));
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* A serial as a Date, as the core converts it (D-112), except that a serial below 1 in the
|
|
807
|
+
* Transitional 1900 system counts from 1899-12-30 as in exceljs (F6). `null` where the core has no
|
|
808
|
+
* Date either (serial 60, negative serials, outside 0001-9999).
|
|
809
|
+
*/
|
|
810
|
+
function serialToDate(serial, dates) {
|
|
811
|
+
if (!Number.isFinite(serial) || Math.abs(serial) > 3e6) return null;
|
|
812
|
+
const ms = Math.round(serial * DAY_MS);
|
|
813
|
+
let time;
|
|
814
|
+
if (dates.date1904) time = EPOCH_1904_01_01 + ms;
|
|
815
|
+
else if (dates.strict) time = EPOCH_1899_12_30 + ms;
|
|
816
|
+
else if (ms < 0) return null;
|
|
817
|
+
else if (ms < DAY_MS) time = EPOCH_1899_12_30 + ms;
|
|
818
|
+
else if (ms < 60 * DAY_MS) time = EPOCH_1899_12_31 + ms;
|
|
819
|
+
else if (ms < 61 * DAY_MS) return null;
|
|
820
|
+
else time = EPOCH_1899_12_30 + ms;
|
|
821
|
+
return time < MIN_DATE_TIME || time > MAX_DATE_TIME ? null : new Date(time);
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* A plain value read by the core as exceljs gives it: a time-only Date of the Transitional 1900
|
|
825
|
+
* system (which the core places on 1899-12-31, D-112) on 1899-12-30, as exceljs's `excelToDate`
|
|
826
|
+
* (lib/utils/utils.js L58-61) does (F6); and, when `dateFormat` is set, a number whose format
|
|
827
|
+
* exceljs takes for a date (`isExcelJsDateFormat`) as a Date (F7).
|
|
828
|
+
*/
|
|
829
|
+
function readValueOut(value, numFmt, dates, dateFormat = true) {
|
|
830
|
+
if (value instanceof Date) {
|
|
831
|
+
const t = value.getTime();
|
|
832
|
+
return !dates.date1904 && !dates.strict && t < TIME_1900_01_01 ? /* @__PURE__ */ new Date(t - DAY_MS) : value;
|
|
833
|
+
}
|
|
834
|
+
if (dateFormat && typeof value === "number" && isExcelJsDateFormat(numFmt)) return serialToDate(value, dates) ?? value;
|
|
835
|
+
return value;
|
|
836
|
+
}
|
|
776
837
|
function colorIn(c, where) {
|
|
777
838
|
if (c === void 0 || c === null) return void 0;
|
|
778
839
|
if (typeof c.argb === "string") return c.argb;
|
|
@@ -973,14 +1034,16 @@ function isRecord$1(v) {
|
|
|
973
1034
|
return typeof v === "object" && v !== null && !(v instanceof Date) && !Array.isArray(v);
|
|
974
1035
|
}
|
|
975
1036
|
/** The `result` part of a formula value for the writer. */
|
|
976
|
-
function resultIn(r) {
|
|
1037
|
+
function resultIn(r, date1904 = false) {
|
|
977
1038
|
if (r === void 0 || r === null) return {};
|
|
1039
|
+
const serial = earlyDateSerial(r, date1904);
|
|
1040
|
+
if (serial !== void 0) return { result: serial };
|
|
978
1041
|
if (isRecord$1(r) && typeof r["error"] === "string") return { result: { error: r["error"] } };
|
|
979
1042
|
return { result: r };
|
|
980
1043
|
}
|
|
981
1044
|
/** The cached result of a formula as a plain value (`null` without one). */
|
|
982
|
-
function resultValue(r) {
|
|
983
|
-
return resultIn(r).result ?? null;
|
|
1045
|
+
function resultValue(r, date1904) {
|
|
1046
|
+
return resultIn(r, date1904).result ?? null;
|
|
984
1047
|
}
|
|
985
1048
|
/**
|
|
986
1049
|
* The range of each shared formula of a worksheet, by master address, as exceljs computes it on
|
|
@@ -1095,9 +1158,11 @@ function overlapCounts(queries, set) {
|
|
|
1095
1158
|
return counts;
|
|
1096
1159
|
}
|
|
1097
1160
|
/** Converts a cell value for the writer; a `{ text, hyperlink }` value gives its text (the link is {@link linkIn}). */
|
|
1098
|
-
function valueIn(cell, sharedRanges) {
|
|
1161
|
+
function valueIn(cell, sharedRanges, date1904 = false) {
|
|
1099
1162
|
const v = cell.value;
|
|
1100
1163
|
if (v === null || v === void 0) return null;
|
|
1164
|
+
const serial = earlyDateSerial(v, date1904);
|
|
1165
|
+
if (serial !== void 0) return serial;
|
|
1101
1166
|
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean" || v instanceof Date) return v;
|
|
1102
1167
|
if (isRecord$1(v)) {
|
|
1103
1168
|
if (cell.type === ValueType.Hyperlink) {
|
|
@@ -1109,19 +1174,19 @@ function valueIn(cell, sharedRanges) {
|
|
|
1109
1174
|
const master = v["sharedFormula"].replace(/\$/g, "").toUpperCase();
|
|
1110
1175
|
if (sharedRanges === void 0 || sharedRanges.has(master)) return {
|
|
1111
1176
|
sharedFormula: master,
|
|
1112
|
-
...resultIn(v["result"])
|
|
1177
|
+
...resultIn(v["result"], date1904)
|
|
1113
1178
|
};
|
|
1114
1179
|
const text = cell.formula;
|
|
1115
1180
|
if (text !== void 0) return {
|
|
1116
1181
|
formula: text,
|
|
1117
|
-
...resultIn(v["result"])
|
|
1182
|
+
...resultIn(v["result"], date1904)
|
|
1118
1183
|
};
|
|
1119
|
-
return resultValue(v["result"]);
|
|
1184
|
+
return resultValue(v["result"], date1904);
|
|
1120
1185
|
}
|
|
1121
1186
|
if (typeof v["formula"] === "string") {
|
|
1122
1187
|
const base = {
|
|
1123
1188
|
formula: v["formula"],
|
|
1124
|
-
...resultIn(v["result"])
|
|
1189
|
+
...resultIn(v["result"], date1904)
|
|
1125
1190
|
};
|
|
1126
1191
|
if (v["shareType"] === "array" && typeof v["ref"] === "string") return {
|
|
1127
1192
|
...base,
|
|
@@ -1379,7 +1444,8 @@ function sameBytes(a, b) {
|
|
|
1379
1444
|
function toNewWorkbook(sheets, date1904, meta = {}) {
|
|
1380
1445
|
const book = createWorkbook({
|
|
1381
1446
|
...date1904 ? { dateSystem: "1904" } : {},
|
|
1382
|
-
...meta.properties ? { properties: meta.properties } : {}
|
|
1447
|
+
...meta.properties ? { properties: meta.properties } : {},
|
|
1448
|
+
...meta.defaultFont ? { defaultFont: meta.defaultFont } : {}
|
|
1383
1449
|
});
|
|
1384
1450
|
if (sheets.length === 0) notSupported("Writing a workbook without worksheets");
|
|
1385
1451
|
const coreImages = /* @__PURE__ */ new Map();
|
|
@@ -1426,19 +1492,46 @@ function toNewWorkbook(sheets, date1904, meta = {}) {
|
|
|
1426
1492
|
if (options) sheet.column(column.number, options);
|
|
1427
1493
|
});
|
|
1428
1494
|
const styleCache = /* @__PURE__ */ new Map();
|
|
1495
|
+
const partsCache = /* @__PURE__ */ new Map();
|
|
1496
|
+
const byParts = (s) => {
|
|
1497
|
+
const parts = [
|
|
1498
|
+
s.numFmt,
|
|
1499
|
+
s.font,
|
|
1500
|
+
s.fill,
|
|
1501
|
+
s.border,
|
|
1502
|
+
s.alignment,
|
|
1503
|
+
s.protection
|
|
1504
|
+
];
|
|
1505
|
+
let level = partsCache;
|
|
1506
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
1507
|
+
let next = level.get(parts[i]);
|
|
1508
|
+
if (!next) level.set(parts[i], next = /* @__PURE__ */ new Map());
|
|
1509
|
+
level = next;
|
|
1510
|
+
}
|
|
1511
|
+
let c = level.get(parts[parts.length - 1]);
|
|
1512
|
+
if (!c) level.set(parts[parts.length - 1], c = styleIn(s));
|
|
1513
|
+
return c;
|
|
1514
|
+
};
|
|
1429
1515
|
const styleOf = (s) => {
|
|
1430
1516
|
let c = styleCache.get(s);
|
|
1431
1517
|
if (!c) {
|
|
1432
|
-
c =
|
|
1518
|
+
c = byParts(s);
|
|
1433
1519
|
styleCache.set(s, c);
|
|
1434
1520
|
}
|
|
1435
1521
|
return c;
|
|
1436
1522
|
};
|
|
1437
|
-
const styleFor = (cell) =>
|
|
1438
|
-
|
|
1523
|
+
const styleFor = (cell) => {
|
|
1524
|
+
if (!useStyles) return isDateCell(cell) ? { numFmt: 14 } : {};
|
|
1525
|
+
const master = cell._master;
|
|
1526
|
+
const style = master !== null && cell.style !== master.style && sameStyle(cell.style, master.style) ? master.style : cell.style;
|
|
1527
|
+
return cellStyleIn(cell, styleOf(style));
|
|
1528
|
+
};
|
|
1529
|
+
const blocks = mergeBlocks(ws);
|
|
1439
1530
|
ws._rows.forEach((row) => {
|
|
1440
1531
|
if (!row) return;
|
|
1441
|
-
|
|
1532
|
+
const style = rowStyleIn(row, useStyles, styleOf);
|
|
1533
|
+
if (row.height !== void 0 || row.hidden || row.outlineLevel || style) sheet.row(row.number, {
|
|
1534
|
+
...style ? { style } : {},
|
|
1442
1535
|
...row.height === void 0 ? {} : { height: row.height },
|
|
1443
1536
|
...row.hidden ? { hidden: true } : {},
|
|
1444
1537
|
...row.outlineLevel ? { outlineLevel: row.outlineLevel } : {},
|
|
@@ -1446,7 +1539,7 @@ function toNewWorkbook(sheets, date1904, meta = {}) {
|
|
|
1446
1539
|
});
|
|
1447
1540
|
row._cells.forEach((cell) => {
|
|
1448
1541
|
if (!cell) return;
|
|
1449
|
-
if (cell._master !== null &&
|
|
1542
|
+
if (cell._master !== null && blocks.covers(cell)) {
|
|
1450
1543
|
if (cell.note !== void 0) at(ws.name, cell.address, () => {
|
|
1451
1544
|
const note = noteIn(cell);
|
|
1452
1545
|
if (note) sheet.note(cell.address, note);
|
|
@@ -1454,10 +1547,8 @@ function toNewWorkbook(sheets, date1904, meta = {}) {
|
|
|
1454
1547
|
} else at(ws.name, cell.address, () => writeCell(cell));
|
|
1455
1548
|
});
|
|
1456
1549
|
});
|
|
1457
|
-
for (const m of
|
|
1458
|
-
const
|
|
1459
|
-
if (!master) continue;
|
|
1460
|
-
const style = styleFor(master);
|
|
1550
|
+
for (const m of blocks.blocks) {
|
|
1551
|
+
const style = at(ws.name, m.cell.address, () => styleFor(m.cell));
|
|
1461
1552
|
if (Object.keys(style).length === 0) continue;
|
|
1462
1553
|
const width = m.right - m.left + 1;
|
|
1463
1554
|
const step = Math.max(1, Math.floor(MERGE_STYLE_BLOCK / width));
|
|
@@ -1470,7 +1561,7 @@ function toNewWorkbook(sheets, date1904, meta = {}) {
|
|
|
1470
1561
|
const note = noteIn(cell);
|
|
1471
1562
|
if (note) sheet.note(cell.address, note);
|
|
1472
1563
|
const style = styleFor(cell);
|
|
1473
|
-
const value = cell.type === ValueType.Merge ? null : valueIn(cell, sharedRanges);
|
|
1564
|
+
const value = cell.type === ValueType.Merge ? null : valueIn(cell, sharedRanges, date1904);
|
|
1474
1565
|
if (value === null && Object.keys(style).length === 0) return;
|
|
1475
1566
|
sheet.set(cell.address, value, style);
|
|
1476
1567
|
const link = cell.type === ValueType.Merge ? null : linkIn(cell);
|
|
@@ -1505,53 +1596,125 @@ function toNewWorkbook(sheets, date1904, meta = {}) {
|
|
|
1505
1596
|
for (const d of meta.definedNames ?? []) book.defineName(d.name, d.ranges.join(","));
|
|
1506
1597
|
return book;
|
|
1507
1598
|
}
|
|
1599
|
+
/**
|
|
1600
|
+
* The part of a file's default font (font 0) that the writer accepts as `defaultFont`: parts it
|
|
1601
|
+
* refuses (a name of more than 31 characters or an empty one, a size outside 1 to 409.55 points)
|
|
1602
|
+
* are left out, so that a loaded workbook can always be written again, as with 1.3.0 and exceljs,
|
|
1603
|
+
* which writes its own Calibri 11 as font 0. `null` when nothing of it is accepted.
|
|
1604
|
+
*/
|
|
1605
|
+
function writableDefaultFont(font) {
|
|
1606
|
+
if (!font) return null;
|
|
1607
|
+
const out = { ...font };
|
|
1608
|
+
for (let tries = Object.keys(out).length; tries >= 0; tries--) {
|
|
1609
|
+
if (Object.keys(out).length === 0) return null;
|
|
1610
|
+
try {
|
|
1611
|
+
createWorkbook({ defaultFont: out });
|
|
1612
|
+
return out;
|
|
1613
|
+
} catch (error) {
|
|
1614
|
+
const property = error instanceof XlvError ? error.details["property"] : void 0;
|
|
1615
|
+
const key = typeof property === "string" && property.startsWith("font.") ? property.slice(5).split(".")[0] : void 0;
|
|
1616
|
+
if (error instanceof XlvError && error.code === "XLV_E_INVALID_STYLE" && key !== void 0 && key in out) delete out[key];
|
|
1617
|
+
else return null;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
return null;
|
|
1621
|
+
}
|
|
1622
|
+
/**
|
|
1623
|
+
* The row style to write for a row (F3), as exceljs writes it: row@s with customFormat="1"
|
|
1624
|
+
* (row-xform.js L19-23, L44-47), with styles only, and only for a row that exceljs writes at all,
|
|
1625
|
+
* one with a height or cells (lib/doc/row.js L333-364). `undefined` when there is none.
|
|
1626
|
+
*/
|
|
1627
|
+
function rowStyleIn(row, useStyles, convert = styleIn) {
|
|
1628
|
+
if (!useStyles || row.height === void 0 && !row._cells.some((c) => c !== void 0)) return void 0;
|
|
1629
|
+
const style = convert(row.style);
|
|
1630
|
+
return Object.keys(style).length > 0 ? style : void 0;
|
|
1631
|
+
}
|
|
1508
1632
|
/** Cells styled by one `sheet.style` call for a merged range: at most what the core styles at once. */
|
|
1509
1633
|
const MERGE_STYLE_BLOCK = 1048576;
|
|
1510
|
-
/**
|
|
1511
|
-
|
|
1634
|
+
/**
|
|
1635
|
+
* Whether two exceljs styles are equal: the same object (as `mergeCells` shares the master's),
|
|
1636
|
+
* parts that are the same objects (a column style given to new cells), or equal plain data (styles
|
|
1637
|
+
* read from a file). Parts that are `undefined` count as missing.
|
|
1638
|
+
*/
|
|
1639
|
+
function sameStyle(a, b) {
|
|
1512
1640
|
if (a === b) return true;
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1641
|
+
const x = a;
|
|
1642
|
+
const y = b;
|
|
1643
|
+
for (const k in x) if (!samePlain(x[k], y[k], 0)) return false;
|
|
1644
|
+
for (const k in y) if (x[k] === void 0 && y[k] !== void 0) return false;
|
|
1645
|
+
return true;
|
|
1646
|
+
}
|
|
1647
|
+
/** Whether two styles read by the core are the same data (F3, F4: a row or column style against xf 0). */
|
|
1648
|
+
function sameStyleData(a, b) {
|
|
1649
|
+
return samePlain(a, b, 0);
|
|
1650
|
+
}
|
|
1651
|
+
/** Deep equality of plain style data (objects, arrays and primitives), to a bounded depth. */
|
|
1652
|
+
function samePlain(a, b, depth) {
|
|
1653
|
+
if (a === b) return true;
|
|
1654
|
+
if (depth > 8 || a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
|
|
1655
|
+
if (Array.isArray(a) !== Array.isArray(b) || a instanceof Date || b instanceof Date) return false;
|
|
1656
|
+
const x = a;
|
|
1657
|
+
const y = b;
|
|
1658
|
+
const keys = Object.keys(x);
|
|
1659
|
+
if (keys.length !== Object.keys(y).length) return false;
|
|
1660
|
+
for (const k of keys) if (!Object.prototype.hasOwnProperty.call(y, k) || !samePlain(x[k], y[k], depth + 1)) return false;
|
|
1661
|
+
return true;
|
|
1520
1662
|
}
|
|
1521
1663
|
/**
|
|
1522
|
-
* The
|
|
1523
|
-
*
|
|
1524
|
-
*
|
|
1525
|
-
*
|
|
1664
|
+
* The other cells of the merged ranges, styled in blocks instead of one by one (K2, F2): in each
|
|
1665
|
+
* column of a range, runs of rows whose cells exist, belong to the range's master and have equal
|
|
1666
|
+
* styles (the object `mergeCells` shares with them, as in exceljs, a column's or row's style
|
|
1667
|
+
* parts, or equal data read from a file), joined across columns while the runs are the same
|
|
1668
|
+
* (review of 1.3.1: cells of a styled column or row that differ from the master were written one
|
|
1669
|
+
* by one). The master itself is written as a cell. `covers` tells whether a cell is styled by a
|
|
1670
|
+
* block; only its note is then written on its own (K3).
|
|
1526
1671
|
*/
|
|
1527
|
-
function
|
|
1528
|
-
const
|
|
1672
|
+
function mergeBlocks(ws) {
|
|
1673
|
+
const blocks = [];
|
|
1529
1674
|
const masters = /* @__PURE__ */ new Set();
|
|
1530
|
-
const mixed = /* @__PURE__ */ new Set();
|
|
1531
1675
|
for (const m of ws._merges.values()) {
|
|
1532
1676
|
const master = ws.findCell(m.top, m.left);
|
|
1533
1677
|
if (!master) continue;
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
for (
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1678
|
+
masters.add(master);
|
|
1679
|
+
let open = [];
|
|
1680
|
+
const close = (right) => {
|
|
1681
|
+
for (const o of open) blocks.push({
|
|
1682
|
+
cell: o.run.cell,
|
|
1683
|
+
top: o.run.top,
|
|
1684
|
+
bottom: o.run.bottom,
|
|
1685
|
+
left: o.left,
|
|
1686
|
+
right
|
|
1687
|
+
});
|
|
1688
|
+
};
|
|
1689
|
+
for (let c = m.left; c <= m.right; c++) {
|
|
1690
|
+
const runs = [];
|
|
1691
|
+
let run = null;
|
|
1692
|
+
for (let r = m.top; r <= m.bottom; r++) {
|
|
1693
|
+
const cell = r === m.top && c === m.left ? void 0 : ws.findRow(r)?.findCell(c);
|
|
1694
|
+
if (!cell || cell._master !== master) {
|
|
1695
|
+
run = null;
|
|
1696
|
+
continue;
|
|
1543
1697
|
}
|
|
1698
|
+
if (run && sameStyle(cell.style, run.cell.style)) run.bottom = r;
|
|
1699
|
+
else runs.push(run = {
|
|
1700
|
+
top: r,
|
|
1701
|
+
bottom: r,
|
|
1702
|
+
cell
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
if (!(open.length === runs.length && runs.every((x, i) => x.top === open[i].run.top && x.bottom === open[i].run.bottom && sameStyle(x.cell.style, open[i].run.cell.style)))) {
|
|
1706
|
+
close(c - 1);
|
|
1707
|
+
open = runs.map((x) => ({
|
|
1708
|
+
run: x,
|
|
1709
|
+
left: c
|
|
1710
|
+
}));
|
|
1544
1711
|
}
|
|
1545
1712
|
}
|
|
1546
|
-
|
|
1547
|
-
ranges.push(m);
|
|
1548
|
-
masters.add(master);
|
|
1549
|
-
} else mixed.add(master);
|
|
1713
|
+
close(m.right);
|
|
1550
1714
|
}
|
|
1551
|
-
for (const master of mixed) masters.delete(master);
|
|
1552
1715
|
return {
|
|
1553
|
-
|
|
1554
|
-
masters
|
|
1716
|
+
blocks,
|
|
1717
|
+
covers: (cell) => cell._master !== null && masters.has(cell._master)
|
|
1555
1718
|
};
|
|
1556
1719
|
}
|
|
1557
1720
|
/**
|
|
@@ -3088,10 +3251,13 @@ var Cell = class {
|
|
|
3088
3251
|
constructor(_row, _col) {
|
|
3089
3252
|
this._row = _row;
|
|
3090
3253
|
this._col = _col;
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3254
|
+
this.style = this._rowColumnStyle();
|
|
3255
|
+
}
|
|
3256
|
+
/** The style a new cell takes: row style wins over column style, part by part (lib/doc/cell.js L99-119 `_mergeStyle`). */
|
|
3257
|
+
_rowColumnStyle() {
|
|
3258
|
+
return {
|
|
3259
|
+
...this._row.worksheet.findColumn(this._col)?.style,
|
|
3260
|
+
...withoutEmpty(this._row.style)
|
|
3095
3261
|
};
|
|
3096
3262
|
}
|
|
3097
3263
|
get worksheet() {
|
|
@@ -3204,12 +3370,17 @@ var Cell = class {
|
|
|
3204
3370
|
return moved;
|
|
3205
3371
|
}
|
|
3206
3372
|
}
|
|
3207
|
-
/**
|
|
3373
|
+
/**
|
|
3374
|
+
* `FormulaType.Shared` for a `{ sharedFormula }` value, `Master` for other formulas, and
|
|
3375
|
+
* `undefined` for a cell without a formula and for a merged cell other than the top-left one,
|
|
3376
|
+
* as in exceljs, where only the formula value has a `formulaType` (lib/doc/cell.js L264-266,
|
|
3377
|
+
* L807-815; exceljs's declaration says `FormulaType` all the same).
|
|
3378
|
+
*/
|
|
3208
3379
|
get formulaType() {
|
|
3209
|
-
const v = this.value;
|
|
3210
|
-
if (!isRecord(v)) return
|
|
3380
|
+
const v = this._master ? null : this.value;
|
|
3381
|
+
if (!isRecord(v)) return void 0;
|
|
3211
3382
|
if (typeof v["sharedFormula"] === "string") return FormulaType.Shared;
|
|
3212
|
-
return typeof v["formula"] === "string" ? FormulaType.Master :
|
|
3383
|
+
return typeof v["formula"] === "string" ? FormulaType.Master : void 0;
|
|
3213
3384
|
}
|
|
3214
3385
|
get result() {
|
|
3215
3386
|
const v = this.value;
|
|
@@ -3384,6 +3555,15 @@ var Cell = class {
|
|
|
3384
3555
|
_unmerge() {
|
|
3385
3556
|
this._master = null;
|
|
3386
3557
|
}
|
|
3558
|
+
/**
|
|
3559
|
+
* @internal As exceljs `cell.unmerge()` (lib/doc/cell.js L177-183): a cell merged into another
|
|
3560
|
+
* one gets the row and column style again, instead of the master's.
|
|
3561
|
+
*/
|
|
3562
|
+
_unmergeResetStyle() {
|
|
3563
|
+
if (this._master === null) return;
|
|
3564
|
+
this._master = null;
|
|
3565
|
+
this.style = this._rowColumnStyle();
|
|
3566
|
+
}
|
|
3387
3567
|
};
|
|
3388
3568
|
function withoutEmpty(style) {
|
|
3389
3569
|
const out = {};
|
|
@@ -3761,11 +3941,11 @@ var Column = class {
|
|
|
3761
3941
|
this._hidden = !!value.hidden;
|
|
3762
3942
|
this._outlineLevel = value.outlineLevel ?? 0;
|
|
3763
3943
|
}
|
|
3764
|
-
/** Cell values of this column by row number (index 0 unused). */
|
|
3944
|
+
/** Cell values of this column by row number (index 0 unused); empty cells are left out (lib/doc/column.js L179-187). */
|
|
3765
3945
|
get values() {
|
|
3766
3946
|
const out = [];
|
|
3767
3947
|
this.eachCell((cell, r) => {
|
|
3768
|
-
out[r] = cell.value;
|
|
3948
|
+
if (cell.type !== ValueType.Null) out[r] = cell.value;
|
|
3769
3949
|
});
|
|
3770
3950
|
return out;
|
|
3771
3951
|
}
|
|
@@ -3780,8 +3960,7 @@ var Column = class {
|
|
|
3780
3960
|
const includeEmpty = typeof options === "object" && options !== null && options.includeEmpty === true;
|
|
3781
3961
|
if (!cb) return;
|
|
3782
3962
|
this.worksheet.eachRow({ includeEmpty }, (row, r) => {
|
|
3783
|
-
|
|
3784
|
-
if (cell && (includeEmpty || cell.type !== ValueType.Null)) cb(cell, r);
|
|
3963
|
+
cb(row.getCell(this.number), r);
|
|
3785
3964
|
});
|
|
3786
3965
|
}
|
|
3787
3966
|
applyStyle(name, value) {
|
|
@@ -4344,8 +4523,9 @@ var Worksheet = class {
|
|
|
4344
4523
|
}
|
|
4345
4524
|
/**
|
|
4346
4525
|
* @internal Merges a range without the overlap check of `mergeCells`. Cells keep their own
|
|
4347
|
-
* style
|
|
4348
|
-
*
|
|
4526
|
+
* style object (the master's, when `mergeCells` shared it with them), except those for which
|
|
4527
|
+
* `takeMasterStyle` returns true (cells inserted inside the range), which get a copy of the
|
|
4528
|
+
* master's style.
|
|
4349
4529
|
*/
|
|
4350
4530
|
_mergeRange(range, takeMasterStyle = () => false) {
|
|
4351
4531
|
const master = this.getCell(range.top, range.left);
|
|
@@ -4657,7 +4837,7 @@ var Worksheet = class {
|
|
|
4657
4837
|
if (r === range.top && c === range.left) continue;
|
|
4658
4838
|
const cell = this.getCell(r, c);
|
|
4659
4839
|
cell._merge(master);
|
|
4660
|
-
if (!this._mergeKeepStyle) cell.style =
|
|
4840
|
+
if (!this._mergeKeepStyle) cell.style = master.style;
|
|
4661
4841
|
}
|
|
4662
4842
|
master._mergeCount++;
|
|
4663
4843
|
this._merges.set(`${columnLetter(range.left)}${String(range.top)}`, range);
|
|
@@ -4676,7 +4856,7 @@ var Worksheet = class {
|
|
|
4676
4856
|
unMergeCells(...args) {
|
|
4677
4857
|
const range = parseRangeArgs(args);
|
|
4678
4858
|
for (const [key, m] of this._merges) if (range.top <= m.bottom && m.top <= range.bottom && range.left <= m.right && m.left <= range.right) {
|
|
4679
|
-
for (let r = m.top; r <= m.bottom; r++) for (let c = m.left; c <= m.right; c++) this.findCell(r, c)?.
|
|
4859
|
+
for (let r = m.top; r <= m.bottom; r++) for (let c = m.left; c <= m.right; c++) this.findCell(r, c)?._unmergeResetStyle();
|
|
4680
4860
|
const master = this.findCell(m.top, m.left);
|
|
4681
4861
|
if (master) master._mergeCount = Math.max(0, master._mergeCount - 1);
|
|
4682
4862
|
this._merges.delete(key);
|
|
@@ -5215,6 +5395,7 @@ function* csvRows(text, o) {
|
|
|
5215
5395
|
}
|
|
5216
5396
|
const rowLine = line;
|
|
5217
5397
|
const fields = [];
|
|
5398
|
+
let quoted = false;
|
|
5218
5399
|
let extra = 0;
|
|
5219
5400
|
let extraText = false;
|
|
5220
5401
|
const push = (field) => {
|
|
@@ -5228,6 +5409,7 @@ function* csvRows(text, o) {
|
|
|
5228
5409
|
let start = at;
|
|
5229
5410
|
while (start < n && (text[start] === " " || text[start] === " ") && text[start] !== o.delimiter) start++;
|
|
5230
5411
|
if (o.quote !== "" && text[start] === o.quote) {
|
|
5412
|
+
quoted = true;
|
|
5231
5413
|
let value = "";
|
|
5232
5414
|
let i = start + 1;
|
|
5233
5415
|
let from = i;
|
|
@@ -5267,7 +5449,7 @@ function* csvRows(text, o) {
|
|
|
5267
5449
|
}
|
|
5268
5450
|
at = skipLineEnd(at);
|
|
5269
5451
|
line++;
|
|
5270
|
-
const row = fields.length === 1 && fields[0].trim() === "" ? [
|
|
5452
|
+
const row = fields.length === 1 && !quoted && fields[0].trim() === "" ? [] : fields;
|
|
5271
5453
|
if (o.ignoreEmpty && !extraText && row.every((f) => f.trim() === "")) continue;
|
|
5272
5454
|
kept++;
|
|
5273
5455
|
if (o.skipHeader && kept === 1) continue;
|
|
@@ -5319,13 +5501,24 @@ function formatterOptions(raw, escapeFormulas) {
|
|
|
5319
5501
|
const quote = q === false ? "" : q === true || q === void 0 ? "\"" : String(q);
|
|
5320
5502
|
const escape = typeof o["escape"] === "string" ? o["escape"] : quote;
|
|
5321
5503
|
const escapeRe = (s) => s.replace(/[\\^$.*+?()[\]{}|-]/g, "\\$&");
|
|
5504
|
+
const transform = o["transform"];
|
|
5505
|
+
if (transform && typeof transform !== "function") throw invalid("formatterOptions.transform", "The option \"transform\" must be a function.", "Pass a function that takes a row and returns the row to write.");
|
|
5506
|
+
if (o["BOM"] !== void 0 && o["BOM"] !== "") throw new XlvError("XLV_E_NOT_SUPPORTED", "formatterOptions.BOM other than \"\\uFEFF\" is not supported.", {
|
|
5507
|
+
hint: "Leave BOM out; writeBOM: true writes the byte order mark U+FEFF.",
|
|
5508
|
+
details: { option: "BOM" }
|
|
5509
|
+
});
|
|
5510
|
+
const quoteColumns = o["quoteColumns"] ?? false;
|
|
5322
5511
|
return {
|
|
5323
5512
|
delimiter,
|
|
5324
5513
|
rowDelimiter,
|
|
5325
5514
|
quote,
|
|
5326
5515
|
escapedQuote: `${escape}${quote}`,
|
|
5327
|
-
quoteColumns
|
|
5328
|
-
|
|
5516
|
+
quoteColumns,
|
|
5517
|
+
quoteHeaders: o["quoteHeaders"] === void 0 ? quoteColumns : o["quoteHeaders"],
|
|
5518
|
+
headers: Array.isArray(o["headers"]) ? o["headers"].map(String) : null,
|
|
5519
|
+
shouldWriteHeaders: !!o["headers"] && !!(o["writeHeaders"] ?? true),
|
|
5520
|
+
alwaysWriteHeaders: o["alwaysWriteHeaders"] === true,
|
|
5521
|
+
transform: typeof transform === "function" ? transform : void 0,
|
|
5329
5522
|
includeEndRowDelimiter: o["includeEndRowDelimiter"] === true,
|
|
5330
5523
|
writeBOM: o["writeBOM"] === true,
|
|
5331
5524
|
escapeChars: new RegExp(`[${escapeRe(delimiter)}${escapeRe(rowDelimiter)}|\\r|\\n${escapeFormulas ? escapeRe(COMMON_DELIMITERS) : ""}]`),
|
|
@@ -5349,8 +5542,8 @@ function splitStartsFormula(text) {
|
|
|
5349
5542
|
}
|
|
5350
5543
|
return false;
|
|
5351
5544
|
}
|
|
5352
|
-
/** One field as fast-csv FieldFormatter.format writes it. */
|
|
5353
|
-
function formatField(value, index, f,
|
|
5545
|
+
/** One field as fast-csv FieldFormatter.format writes it; `quoteThis`: quoteColumns or quoteHeaders asks for quotes, or the escape. */
|
|
5546
|
+
function formatField(value, index, f, quoteThis) {
|
|
5354
5547
|
const text = `${value === null || value === void 0 ? "" : String(value)}`.replace(/\0/g, "");
|
|
5355
5548
|
if (f.quote === "" && (/[\r\n]/.test(text) || f.delimiter !== "" && text.includes(f.delimiter))) throw new XlvError("XLV_E_INVALID_OPTION", `Field ${String(index + 1)} contains a line break or the delimiter, which cannot be written with quote: false.`, {
|
|
5356
5549
|
hint: "Leave formatterOptions.quote at its default (\") so such fields are quoted, or remove the line breaks from the value.",
|
|
@@ -5367,8 +5560,6 @@ function formatField(value, index, f, forceQuote) {
|
|
|
5367
5560
|
}
|
|
5368
5561
|
});
|
|
5369
5562
|
if (f.quote !== "" && text.includes(f.quote)) return `${f.quote}${text.split(f.quote).join(f.escapedQuote)}${f.quote}`;
|
|
5370
|
-
const q = f.quoteColumns;
|
|
5371
|
-
const quoteThis = forceQuote || q === true || Array.isArray(q) && q[index] === true;
|
|
5372
5563
|
if (text.search(f.escapeChars) !== -1 || quoteThis) return `${f.quote}${text}${f.quote}`;
|
|
5373
5564
|
return text;
|
|
5374
5565
|
}
|
|
@@ -5551,7 +5742,7 @@ function columnName(n) {
|
|
|
5551
5742
|
*
|
|
5552
5743
|
* @internal
|
|
5553
5744
|
*/
|
|
5554
|
-
function worksheetCsv(sheet, options, encoding = "utf8") {
|
|
5745
|
+
async function worksheetCsv(sheet, options, encoding = "utf8") {
|
|
5555
5746
|
const injection = options.formulaInjection ?? "escape";
|
|
5556
5747
|
if (injection !== "escape" && injection !== "off") throw invalid("formulaInjection", "The option \"formulaInjection\" must be \"escape\" or \"off\".", "Use \"escape\" (default) or \"off\".");
|
|
5557
5748
|
const f = formatterOptions(options.formatterOptions, injection === "escape");
|
|
@@ -5641,29 +5832,87 @@ function worksheetCsv(sheet, options, encoding = "utf8") {
|
|
|
5641
5832
|
if (first === void 0) return [out, false];
|
|
5642
5833
|
return INJECTION_START.has(first) || first.charCodeAt(0) < 32 || encodedStartIsUnsafe(text, encoding, atStart && f.writeBOM && latin1 ? [255] : []) || utf16 && utf16StartsFormula(text) ? [`'${out}`, f.quote !== ""] : [out, false];
|
|
5643
5834
|
};
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5835
|
+
/** RowFormatter `headers`: given, or taken from the first row written (L109-124). */
|
|
5836
|
+
let headers = f.headers;
|
|
5837
|
+
/** FieldFormatter `_headers`, by which an object of quoteColumns or quoteHeaders is looked up. */
|
|
5838
|
+
let fieldHeaders = f.headers;
|
|
5839
|
+
let hasWrittenHeaders = false;
|
|
5840
|
+
const own = (o, key) => Object.prototype.hasOwnProperty.call(o, key);
|
|
5841
|
+
const shouldQuote = (index, isHeader) => {
|
|
5842
|
+
const config = isHeader ? f.quoteHeaders : f.quoteColumns;
|
|
5843
|
+
if (typeof config === "boolean") return config;
|
|
5844
|
+
if (Array.isArray(config)) return Boolean(config[index]);
|
|
5845
|
+
if (fieldHeaders !== null && config !== null && typeof config === "object") {
|
|
5846
|
+
const key = String(fieldHeaders[index]);
|
|
5847
|
+
return own(config, key) && Boolean(config[key]);
|
|
5848
|
+
}
|
|
5849
|
+
return false;
|
|
5850
|
+
};
|
|
5851
|
+
const formatColumns = (columns, isHeader, rowNumber) => {
|
|
5852
|
+
const fields = [];
|
|
5853
|
+
for (let i = 0; i < columns.length; i++) {
|
|
5854
|
+
if (!(i in columns)) {
|
|
5855
|
+
fields.push("");
|
|
5856
|
+
continue;
|
|
5857
|
+
}
|
|
5858
|
+
const atStart = lines.length === 0 && i === 0;
|
|
5859
|
+
const [out, force] = escape(columns[i], atStart);
|
|
5860
|
+
fields.push(field(out, i, force || shouldQuote(i, isHeader), isHeader ? 0 : rowNumber, atStart));
|
|
5861
|
+
}
|
|
5862
|
+
lines.push(fields.join(f.delimiter));
|
|
5863
|
+
};
|
|
5864
|
+
const isHashArray = (row) => Array.isArray(row) && Array.isArray(row[0]) && row[0].length === 2;
|
|
5865
|
+
const gatherHeaders = (row) => isHashArray(row) ? row.map((it) => it[0]) : Array.isArray(row) ? row : Object.keys(Object(row));
|
|
5866
|
+
const gatherColumns = (row, names) => {
|
|
5867
|
+
if (!Array.isArray(row)) {
|
|
5868
|
+
const o = Object(row);
|
|
5869
|
+
return names.map((h) => own(o, String(h)) ? o[String(h)] : void 0);
|
|
5870
|
+
}
|
|
5871
|
+
if (isHashArray(row)) return names.map((_h, i) => row[i] ? row[i][1] : "");
|
|
5872
|
+
if (!f.shouldWriteHeaders) return row;
|
|
5873
|
+
return names.map((_h, i) => row[i]);
|
|
5874
|
+
};
|
|
5875
|
+
const formatRow = (row, rowNumber) => {
|
|
5876
|
+
if (!row) return;
|
|
5877
|
+
let formatRowColumns = true;
|
|
5878
|
+
if (headers === null) {
|
|
5879
|
+
headers = gatherHeaders(row);
|
|
5880
|
+
fieldHeaders = headers;
|
|
5881
|
+
if (f.shouldWriteHeaders) formatRowColumns = !(Array.isArray(row) && !isHashArray(row));
|
|
5882
|
+
}
|
|
5883
|
+
if (f.shouldWriteHeaders && !hasWrittenHeaders) {
|
|
5884
|
+
formatColumns(headers, true, 0);
|
|
5885
|
+
hasWrittenHeaders = true;
|
|
5886
|
+
}
|
|
5887
|
+
if (formatRowColumns) formatColumns(gatherColumns(row, headers), false, rowNumber);
|
|
5888
|
+
};
|
|
5889
|
+
const transform = f.transform;
|
|
5890
|
+
const transformed = (row) => {
|
|
5891
|
+
if (!transform) return row;
|
|
5892
|
+
if (transform.length === 1) return transform(row);
|
|
5893
|
+
return new Promise((resolve, reject) => {
|
|
5894
|
+
transform(row, (error, out) => error ? reject(error instanceof Error ? error : new Error(String(error))) : resolve(out));
|
|
5895
|
+
});
|
|
5896
|
+
};
|
|
5648
5897
|
if (sheet) {
|
|
5649
|
-
|
|
5898
|
+
const rows = [];
|
|
5650
5899
|
sheet.eachRow((row, rowNumber) => {
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
continue;
|
|
5659
|
-
}
|
|
5660
|
-
const atStart = lines.length === 0 && i === 0;
|
|
5661
|
-
const [out, force] = escape(map(values[i], i), atStart);
|
|
5662
|
-
fields.push(field(out, i, force, rowNumber, atStart));
|
|
5900
|
+
rows.push([rowNumber, row]);
|
|
5901
|
+
});
|
|
5902
|
+
let lastRow = f.shouldWriteHeaders && f.headers === null && rows.length > 0 ? rows[0][0] - 1 : 0;
|
|
5903
|
+
for (const [rowNumber, row] of rows) {
|
|
5904
|
+
if (includeEmptyRows) while (++lastRow < rowNumber) {
|
|
5905
|
+
const out = transformed([]);
|
|
5906
|
+
formatRow(out instanceof Promise ? await out : out, lastRow);
|
|
5663
5907
|
}
|
|
5664
|
-
|
|
5908
|
+
const out = transformed(row.values.slice(1).map((v, i) => map(v, i)));
|
|
5909
|
+
formatRow(out instanceof Promise ? await out : out, rowNumber);
|
|
5665
5910
|
lastRow = rowNumber;
|
|
5666
|
-
}
|
|
5911
|
+
}
|
|
5912
|
+
}
|
|
5913
|
+
if (f.alwaysWriteHeaders && lines.length === 0) {
|
|
5914
|
+
if (!headers) throw invalid("formatterOptions.alwaysWriteHeaders", "alwaysWriteHeaders is set, but no header names are given in formatterOptions.headers.", "Pass formatterOptions.headers as an array of names, or leave alwaysWriteHeaders out.");
|
|
5915
|
+
formatColumns(headers, true, 0);
|
|
5667
5916
|
}
|
|
5668
5917
|
let text = lines.join(f.rowDelimiter);
|
|
5669
5918
|
if (f.includeEndRowDelimiter) text += f.rowDelimiter;
|
|
@@ -5688,9 +5937,22 @@ function encode(text, encoding) {
|
|
|
5688
5937
|
}
|
|
5689
5938
|
throw invalid("encoding", `The encoding "${encoding ?? ""}" is not supported.`, "Use \"utf8\" (default), \"utf16le\" or \"latin1\".");
|
|
5690
5939
|
}
|
|
5940
|
+
/**
|
|
5941
|
+
* Node.js's 'latin1' (alias 'binary'), which fast-csv's StringDecoder uses
|
|
5942
|
+
* (`@fast-csv/parse/build/src/CsvParserStream.js` L20): each byte is the code point of the same
|
|
5943
|
+
* value. The WHATWG label 'latin1' of `TextDecoder` is windows-1252 instead (bytes 80-9F become
|
|
5944
|
+
* other characters, for example 80 the euro sign), so it is not used (F10).
|
|
5945
|
+
*/
|
|
5946
|
+
const latin1Decoder = { decode(input) {
|
|
5947
|
+
if (!input) return "";
|
|
5948
|
+
let out = "";
|
|
5949
|
+
for (let i = 0; i < input.length; i += 8192) out += String.fromCharCode(...input.subarray(i, i + 8192));
|
|
5950
|
+
return out;
|
|
5951
|
+
} };
|
|
5691
5952
|
function decoderFor(encoding) {
|
|
5692
5953
|
const e = encoding.toLowerCase();
|
|
5693
|
-
|
|
5954
|
+
if (e === "latin1" || e === "binary") return latin1Decoder;
|
|
5955
|
+
const label = e === "utf8" ? "utf-8" : e === "ucs2" || e === "ucs-2" || e === "utf16le" ? "utf-16le" : e;
|
|
5694
5956
|
try {
|
|
5695
5957
|
return new TextDecoder(label);
|
|
5696
5958
|
} catch {
|
|
@@ -5842,11 +6104,11 @@ var CsvIO = class {
|
|
|
5842
6104
|
}
|
|
5843
6105
|
/** Writes a worksheet as CSV to a stream and ends it. */
|
|
5844
6106
|
async write(stream, options = {}) {
|
|
5845
|
-
await writeTo(stream, encode(this.text(options), "utf8"));
|
|
6107
|
+
await writeTo(stream, encode(await this.text(options), "utf8"));
|
|
5846
6108
|
}
|
|
5847
6109
|
/** Writes a worksheet to a CSV file (Node.js, Bun, Deno), replacing it. */
|
|
5848
6110
|
async writeFile(path, options = {}) {
|
|
5849
|
-
const bytes = encode(this.text(options, outputEncoding(options.encoding)), options.encoding);
|
|
6111
|
+
const bytes = encode(await this.text(options, outputEncoding(options.encoding)), options.encoding);
|
|
5850
6112
|
const fs = await nodeFs("writeFile");
|
|
5851
6113
|
try {
|
|
5852
6114
|
await fs.writeFile(path, bytes);
|
|
@@ -5864,13 +6126,13 @@ var CsvIO = class {
|
|
|
5864
6126
|
}
|
|
5865
6127
|
/** The CSV of a worksheet as bytes (a Buffer in Node.js), UTF-8 encoded. */
|
|
5866
6128
|
async writeBuffer(options = {}) {
|
|
5867
|
-
return asBuffer$1(encode(this.text(options), "utf8"));
|
|
6129
|
+
return asBuffer$1(encode(await this.text(options), "utf8"));
|
|
5868
6130
|
}
|
|
5869
6131
|
/** Removed in exceljs 4 (it throws there too); use `read`. */
|
|
5870
6132
|
createInputStream() {
|
|
5871
6133
|
throw new Error("`CSV#createInputStream` is deprecated. You should use `CSV#read` instead. This method will be removed in version 5.0. Please follow upgrade instruction: https://github.com/exceljs/exceljs/blob/master/UPGRADE-4.0.md");
|
|
5872
6134
|
}
|
|
5873
|
-
text(options, encoding = "utf8") {
|
|
6135
|
+
async text(options, encoding = "utf8") {
|
|
5874
6136
|
const key = options.sheetName || options.sheetId;
|
|
5875
6137
|
const sheet = this.workbook.getWorksheet(key);
|
|
5876
6138
|
if (key !== void 0 && !sheet) throw new XlvError("XLV_E_SHEET_NOT_FOUND", `The workbook has no worksheet ${typeof key === "string" ? `named "${key}"` : `with id ${String(key)}`}.`, {
|
|
@@ -6407,9 +6669,11 @@ var XlsxIO = class {
|
|
|
6407
6669
|
}
|
|
6408
6670
|
book(options) {
|
|
6409
6671
|
const useStyles = options?.useStyles !== false;
|
|
6672
|
+
const font = this.workbook._defaultFont;
|
|
6410
6673
|
return toNewWorkbook(this.workbook.worksheets, this.workbook._date1904, {
|
|
6411
6674
|
...this.workbook._meta(),
|
|
6412
|
-
useStyles
|
|
6675
|
+
useStyles,
|
|
6676
|
+
...font ? { defaultFont: font } : {}
|
|
6413
6677
|
});
|
|
6414
6678
|
}
|
|
6415
6679
|
bytes(options) {
|
|
@@ -6500,6 +6764,8 @@ var Workbook = class {
|
|
|
6500
6764
|
calcProperties: this.calcProperties
|
|
6501
6765
|
};
|
|
6502
6766
|
}
|
|
6767
|
+
/** @internal The default font (font 0) of the file this workbook was loaded from, written again (F4); `null` for a workbook made in code. */
|
|
6768
|
+
_defaultFont = null;
|
|
6503
6769
|
/** @internal */
|
|
6504
6770
|
get _date1904() {
|
|
6505
6771
|
return this.properties.date1904 === true;
|
|
@@ -6727,6 +6993,10 @@ var Workbook = class {
|
|
|
6727
6993
|
const use = (node) => !ignore.has(node);
|
|
6728
6994
|
let cellsRead = 0;
|
|
6729
6995
|
for (const s of read.sheets) cellsRead += s.cellCount;
|
|
6996
|
+
const dates = {
|
|
6997
|
+
date1904: read.dateSystem === "1904",
|
|
6998
|
+
strict: read.conformance === "strict"
|
|
6999
|
+
};
|
|
6730
7000
|
const budget = Math.max(DEFAULT_READ_LIMITS.maxCells, 16 * cellsRead);
|
|
6731
7001
|
let spent = 0;
|
|
6732
7002
|
const spend = (positions, what, sheet) => {
|
|
@@ -6783,6 +7053,27 @@ var Workbook = class {
|
|
|
6783
7053
|
}
|
|
6784
7054
|
}
|
|
6785
7055
|
this._clearSheets();
|
|
7056
|
+
const font0 = writableDefaultFont(read.defaultFont);
|
|
7057
|
+
this._defaultFont = font0;
|
|
7058
|
+
const fileStyle = (style, numFmt, styled = Object.keys(style).length > 0) => styleOut(styled && font0 && !style.font ? {
|
|
7059
|
+
...style,
|
|
7060
|
+
font: font0
|
|
7061
|
+
} : style, numFmt);
|
|
7062
|
+
const lineFormat = (style) => typeof style.numFmt === "string" ? style.numFmt : null;
|
|
7063
|
+
let xf0;
|
|
7064
|
+
const defaultXf = () => {
|
|
7065
|
+
if (xf0 !== void 0) return xf0;
|
|
7066
|
+
xf0 = null;
|
|
7067
|
+
for (const sheet of read.sheets) {
|
|
7068
|
+
if (sheet.kind !== "worksheet") continue;
|
|
7069
|
+
for (const cell of sheet.cells()) if (cell.styleIndex === 0) return xf0 = cell.style;
|
|
7070
|
+
}
|
|
7071
|
+
return xf0;
|
|
7072
|
+
};
|
|
7073
|
+
const lineStyle = (style) => {
|
|
7074
|
+
const zero = defaultXf();
|
|
7075
|
+
return zero && sameStyleData(style, zero) ? {} : fileStyle(style, lineFormat(style));
|
|
7076
|
+
};
|
|
6786
7077
|
if (read.dateSystem === "1904") this.properties.date1904 = true;
|
|
6787
7078
|
else delete this.properties.date1904;
|
|
6788
7079
|
const p = read.properties;
|
|
@@ -6844,7 +7135,7 @@ var Workbook = class {
|
|
|
6844
7135
|
if (c.width !== null) column.width = c.width;
|
|
6845
7136
|
if (c.hidden) column.hidden = true;
|
|
6846
7137
|
if (c.outlineLevel) column.outlineLevel = c.outlineLevel;
|
|
6847
|
-
if (c.style) column.style =
|
|
7138
|
+
if (c.style) column.style = lineStyle(c.style);
|
|
6848
7139
|
}
|
|
6849
7140
|
}
|
|
6850
7141
|
if (use("sheetFormatPr") || use("sheetPr")) {
|
|
@@ -6864,13 +7155,13 @@ var Workbook = class {
|
|
|
6864
7155
|
}
|
|
6865
7156
|
let lastRow = 0;
|
|
6866
7157
|
for (const cell of use("sheetData") ? s.styledCells() : []) {
|
|
6867
|
-
ws.getCell(cell.row, cell.col).style =
|
|
7158
|
+
ws.getCell(cell.row, cell.col).style = fileStyle(cell.style, cell.numFmt, cell.styleIndex > 0);
|
|
6868
7159
|
if (cell.row > lastRow) lastRow = cell.row;
|
|
6869
7160
|
}
|
|
6870
7161
|
for (const cell of use("sheetData") ? s.cells() : []) {
|
|
6871
7162
|
const target = ws.getCell(cell.row, cell.col);
|
|
6872
7163
|
let value;
|
|
6873
|
-
const plain = cell.type === "error" ? { error: cell.value } : cell.value;
|
|
7164
|
+
const plain = cell.type === "error" ? { error: cell.value } : readValueOut(cell.value, cell.numFmt, dates);
|
|
6874
7165
|
if (cell.formula && hasFormulaText(cell.formula)) value = formulaOut(cell.formula, plain, cell.ref, masters);
|
|
6875
7166
|
else value = cell.richText ? richTextOut(cell.richText) : plain;
|
|
6876
7167
|
const h = cell.hyperlink;
|
|
@@ -6887,7 +7178,7 @@ var Workbook = class {
|
|
|
6887
7178
|
};
|
|
6888
7179
|
}
|
|
6889
7180
|
target.value = value;
|
|
6890
|
-
target.style =
|
|
7181
|
+
target.style = fileStyle(cell.style, cell.numFmt, cell.styleIndex > 0);
|
|
6891
7182
|
if (cell.row > lastRow) lastRow = cell.row;
|
|
6892
7183
|
}
|
|
6893
7184
|
const dim = /(\d+)$/.exec(s.dimension ?? "");
|
|
@@ -6905,6 +7196,7 @@ var Workbook = class {
|
|
|
6905
7196
|
const info = s.rowInfo(r);
|
|
6906
7197
|
if (!info) continue;
|
|
6907
7198
|
const row = ws.getRow(r);
|
|
7199
|
+
if (info.style && use("sheetData")) row.style = lineStyle(info.style);
|
|
6908
7200
|
if (info.height !== null) row.height = Math.min(info.height, 409.5);
|
|
6909
7201
|
if (info.hidden) row.hidden = true;
|
|
6910
7202
|
if (info.outlineLevel) row.outlineLevel = info.outlineLevel;
|
|
@@ -6915,7 +7207,7 @@ var Workbook = class {
|
|
|
6915
7207
|
spend(areaOf(m), "A merged range", s.name);
|
|
6916
7208
|
spendMerge(areaOf(m), m, s.name);
|
|
6917
7209
|
try {
|
|
6918
|
-
ws.
|
|
7210
|
+
ws.mergeCellsWithoutStyle(m);
|
|
6919
7211
|
} catch (error) {
|
|
6920
7212
|
if (error instanceof XlvError || !(error instanceof Error) || error.message !== "Cannot merge already merged cells") throw error;
|
|
6921
7213
|
const other = mergeOverlapping(ws, m);
|
|
@@ -7281,8 +7573,10 @@ var WorksheetWriter = class extends Worksheet {
|
|
|
7281
7573
|
if (styles[i] !== void 0) styled = true;
|
|
7282
7574
|
});
|
|
7283
7575
|
for (let i = 0; i < values.length; i++) if (values[i] === void 0) values[i] = null;
|
|
7576
|
+
const rowStyle = rowStyleIn(row, this.writer._useStyles);
|
|
7284
7577
|
this.rowsOut.push({
|
|
7285
7578
|
values,
|
|
7579
|
+
...rowStyle ? { rowStyle } : {},
|
|
7286
7580
|
...styled ? { styles } : {},
|
|
7287
7581
|
...links.length > 0 ? { hyperlinks: links } : {},
|
|
7288
7582
|
...notes.length > 0 ? { notes } : {},
|
|
@@ -7727,6 +8021,10 @@ var WorkbookReader = class extends Emitter {
|
|
|
7727
8021
|
async *parse(input, options) {
|
|
7728
8022
|
if (options) this.options = readerOptions(options);
|
|
7729
8023
|
const book = await this.open(input ?? this.input);
|
|
8024
|
+
const dates = {
|
|
8025
|
+
date1904: book.dateSystem === "1904",
|
|
8026
|
+
strict: book.conformance === "strict"
|
|
8027
|
+
};
|
|
7730
8028
|
if (this.options.entries === "emit") this.emit("entry", { type: "workbook" });
|
|
7731
8029
|
if (this.options.worksheets === "ignore") return;
|
|
7732
8030
|
const host = new Workbook();
|
|
@@ -7747,8 +8045,9 @@ var WorkbookReader = class extends Emitter {
|
|
|
7747
8045
|
const rows = async function* () {
|
|
7748
8046
|
for await (const r of book.rows(name)) {
|
|
7749
8047
|
const row = sheet.getRow(r.number);
|
|
8048
|
+
if (styles && r.style) row.style = styleOut(r.style, typeof r.style.numFmt === "string" ? r.style.numFmt : null);
|
|
7750
8049
|
for (const cell of r.cells) {
|
|
7751
|
-
const plain = cell.type === "error" ? { error: cell.value } : cell.value;
|
|
8050
|
+
const plain = cell.type === "error" ? { error: cell.value } : readValueOut(cell.value, cell.numFmt, dates, !cell.formula);
|
|
7752
8051
|
const target = row.getCell(cell.col);
|
|
7753
8052
|
const f = cell.formula;
|
|
7754
8053
|
const result = plain === null ? {} : { result: plain };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xlvane/compat",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.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.3.
|
|
75
|
+
"xlvane": "^1.3.1"
|
|
76
76
|
},
|
|
77
77
|
"publishConfig": {
|
|
78
78
|
"access": "public",
|