@particle-academy/holy-sheet 2.5.0 → 2.7.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 +560 -0
- package/dist/index.cjs +350 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -7
- package/dist/index.d.ts +30 -7
- package/dist/index.js +350 -67
- package/dist/index.js.map +1 -1
- package/package.json +67 -66
package/dist/index.cjs
CHANGED
|
@@ -418,13 +418,15 @@ var MergedRegion = class {
|
|
|
418
418
|
|
|
419
419
|
// src/workbook/sheet.ts
|
|
420
420
|
var Sheet = class {
|
|
421
|
-
constructor(name, cells = /* @__PURE__ */ new Map(), mergedRegions = [], columnWidths = /* @__PURE__ */ new Map(), frozenRows = 0, frozenCols = 0) {
|
|
421
|
+
constructor(name, cells = /* @__PURE__ */ new Map(), mergedRegions = [], columnWidths = /* @__PURE__ */ new Map(), frozenRows = 0, frozenCols = 0, rowHeights = /* @__PURE__ */ new Map(), print = null) {
|
|
422
422
|
this.name = name;
|
|
423
423
|
this.cells = cells;
|
|
424
424
|
this.mergedRegions = mergedRegions;
|
|
425
425
|
this.columnWidths = columnWidths;
|
|
426
426
|
this.frozenRows = frozenRows;
|
|
427
427
|
this.frozenCols = frozenCols;
|
|
428
|
+
this.rowHeights = rowHeights;
|
|
429
|
+
this.print = print;
|
|
428
430
|
}
|
|
429
431
|
/** rowNumber → (colLetter → Cell), sorted by ascending row number. */
|
|
430
432
|
rows() {
|
|
@@ -495,6 +497,54 @@ var DateConverter = {
|
|
|
495
497
|
}
|
|
496
498
|
};
|
|
497
499
|
|
|
500
|
+
// src/exceptions.ts
|
|
501
|
+
var SchemaException = class _SchemaException extends Error {
|
|
502
|
+
constructor(errors, message) {
|
|
503
|
+
super(message ?? summarize(errors));
|
|
504
|
+
this.name = "SchemaException";
|
|
505
|
+
this.errors = errors;
|
|
506
|
+
Object.setPrototypeOf(this, _SchemaException.prototype);
|
|
507
|
+
}
|
|
508
|
+
static fromErrors(errors) {
|
|
509
|
+
return new _SchemaException(errors);
|
|
510
|
+
}
|
|
511
|
+
getErrors() {
|
|
512
|
+
return this.errors;
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
function summarize(errors) {
|
|
516
|
+
if (errors.length === 1) {
|
|
517
|
+
const e = errors[0];
|
|
518
|
+
return `[holy-sheet] schema invalid at ${e.path}: expected ${e.expected}, got ${e.got}`;
|
|
519
|
+
}
|
|
520
|
+
const first2 = errors[0];
|
|
521
|
+
const rest = errors.length - 1;
|
|
522
|
+
return `[holy-sheet] schema invalid at ${first2.path}: expected ${first2.expected}, got ${first2.got} (+${rest} more)`;
|
|
523
|
+
}
|
|
524
|
+
var UnsupportedFormatException = class _UnsupportedFormatException extends Error {
|
|
525
|
+
constructor(message, path = null, mimetype = null) {
|
|
526
|
+
super(message);
|
|
527
|
+
this.name = "UnsupportedFormatException";
|
|
528
|
+
this.path = path;
|
|
529
|
+
this.mimetype = mimetype;
|
|
530
|
+
Object.setPrototypeOf(this, _UnsupportedFormatException.prototype);
|
|
531
|
+
}
|
|
532
|
+
static notAZip(path = null) {
|
|
533
|
+
return new _UnsupportedFormatException(
|
|
534
|
+
`[holy-sheet] cannot read ${path ?? "this file"}: it is not a zip archive, and xlsx and ods both are`,
|
|
535
|
+
path
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
static unknownPackage(path, mimetype) {
|
|
539
|
+
const what = mimetype !== null && mimetype !== "" ? `its mimetype is ${mimetype}` : "it has neither xl/workbook.xml nor an OpenDocument mimetype";
|
|
540
|
+
return new _UnsupportedFormatException(
|
|
541
|
+
`[holy-sheet] cannot read ${path ?? "this file"}: ${what}. Supported: xlsx, ods`,
|
|
542
|
+
path,
|
|
543
|
+
mimetype !== "" ? mimetype : null
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
|
|
498
548
|
// src/schema/theme.ts
|
|
499
549
|
var Theme = class {
|
|
500
550
|
constructor(key) {
|
|
@@ -572,7 +622,7 @@ var Normalizer = class {
|
|
|
572
622
|
const columnFormat = columnFormats.get(c) ?? null;
|
|
573
623
|
const rowBand = theme.dataFormat(r);
|
|
574
624
|
const merged = rowBand !== null ? columnFormat ? columnFormat.mergeWith(rowBand) : rowBand : columnFormat;
|
|
575
|
-
cells.set(address, this.buildCell(address, value, merged));
|
|
625
|
+
cells.set(address, this.buildCell(address, value, merged, name));
|
|
576
626
|
});
|
|
577
627
|
});
|
|
578
628
|
if (sheet.totals && isPlainObject(sheet.totals) && Object.keys(sheet.totals).length > 0 && rows.length > 0 && columns.length > 0) {
|
|
@@ -596,7 +646,7 @@ var Normalizer = class {
|
|
|
596
646
|
}
|
|
597
647
|
}
|
|
598
648
|
if (sheet.cells != null) {
|
|
599
|
-
for (const [address, cell] of this.normalizeCellMap(sheet.cells)) {
|
|
649
|
+
for (const [address, cell] of this.normalizeCellMap(sheet.cells, name)) {
|
|
600
650
|
const beneath = cells.get(address);
|
|
601
651
|
cells.set(
|
|
602
652
|
address,
|
|
@@ -617,17 +667,23 @@ var Normalizer = class {
|
|
|
617
667
|
this.normalizeMerges(sheet.mergedRegions ?? []),
|
|
618
668
|
this.normalizeWidths(sheet),
|
|
619
669
|
toInt(sheet.frozenRows ?? 0),
|
|
620
|
-
toInt(sheet.frozenCols ?? 0)
|
|
670
|
+
toInt(sheet.frozenCols ?? 0),
|
|
671
|
+
normalizeRowHeights(sheet.rowHeights),
|
|
672
|
+
normalizePrint(sheet.print)
|
|
621
673
|
);
|
|
622
674
|
}
|
|
623
|
-
normalizeCellMap(map) {
|
|
675
|
+
normalizeCellMap(map, sheetName2) {
|
|
624
676
|
const cells = /* @__PURE__ */ new Map();
|
|
625
677
|
for (const [address, cellData] of Object.entries(map)) {
|
|
626
|
-
if (!
|
|
678
|
+
if (!isCellSpec(cellData)) {
|
|
627
679
|
const [bare, formula] = promoteFormula(cellData);
|
|
628
680
|
cells.set(
|
|
629
681
|
address,
|
|
630
|
-
new Cell(
|
|
682
|
+
new Cell(
|
|
683
|
+
address,
|
|
684
|
+
formula !== null ? null : this.coerceValue(bare, null, address, sheetName2),
|
|
685
|
+
formula
|
|
686
|
+
)
|
|
631
687
|
);
|
|
632
688
|
continue;
|
|
633
689
|
}
|
|
@@ -642,7 +698,7 @@ var Normalizer = class {
|
|
|
642
698
|
address,
|
|
643
699
|
new Cell(
|
|
644
700
|
address,
|
|
645
|
-
this.coerceValue(rawValue, format),
|
|
701
|
+
this.coerceValue(rawValue, format, address, sheetName2),
|
|
646
702
|
cellData?.formula ?? null,
|
|
647
703
|
format,
|
|
648
704
|
comment,
|
|
@@ -723,8 +779,8 @@ var Normalizer = class {
|
|
|
723
779
|
return null;
|
|
724
780
|
}
|
|
725
781
|
}
|
|
726
|
-
buildCell(address, value, columnFormat) {
|
|
727
|
-
if (
|
|
782
|
+
buildCell(address, value, columnFormat, sheetName2) {
|
|
783
|
+
if (isCellSpec(value)) {
|
|
728
784
|
const cellFormat = isPlainObject(value.format) ? CellFormat.fromInput(value.format) : null;
|
|
729
785
|
const merged = columnFormat ? columnFormat.mergeWith(cellFormat) : cellFormat;
|
|
730
786
|
const comment = isPlainObject(value.comment) ? new CellComment(
|
|
@@ -735,7 +791,7 @@ var Normalizer = class {
|
|
|
735
791
|
const rawValue = value.value ?? null;
|
|
736
792
|
return new Cell(
|
|
737
793
|
address,
|
|
738
|
-
this.coerceValue(rawValue, merged),
|
|
794
|
+
this.coerceValue(rawValue, merged, address, sheetName2),
|
|
739
795
|
value.formula ?? null,
|
|
740
796
|
merged,
|
|
741
797
|
comment,
|
|
@@ -745,12 +801,12 @@ var Normalizer = class {
|
|
|
745
801
|
const [bare, formula] = promoteFormula(value);
|
|
746
802
|
return new Cell(
|
|
747
803
|
address,
|
|
748
|
-
formula !== null ? null : this.coerceValue(bare, columnFormat),
|
|
804
|
+
formula !== null ? null : this.coerceValue(bare, columnFormat, address, sheetName2),
|
|
749
805
|
formula,
|
|
750
806
|
columnFormat
|
|
751
807
|
);
|
|
752
808
|
}
|
|
753
|
-
coerceValue(value, format) {
|
|
809
|
+
coerceValue(value, format, address = "?", sheetName2) {
|
|
754
810
|
if (value === null || value === void 0) return null;
|
|
755
811
|
const df = format?.displayFormat;
|
|
756
812
|
if (df === "date" || df === "datetime") {
|
|
@@ -764,12 +820,36 @@ var Normalizer = class {
|
|
|
764
820
|
if (value instanceof Date) {
|
|
765
821
|
return DateConverter.toSerial(value, true);
|
|
766
822
|
}
|
|
767
|
-
if (typeof value
|
|
823
|
+
if (typeof value === "bigint") {
|
|
824
|
+
return String(value);
|
|
825
|
+
}
|
|
826
|
+
if (typeof value === "object" && !Array.isArray(value) && hasOwnToString(value)) {
|
|
768
827
|
return String(value);
|
|
769
828
|
}
|
|
829
|
+
if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
|
|
830
|
+
const where = sheetName2 === void 0 ? address : `${sheetName2}!${address}`;
|
|
831
|
+
const got = typeOf(value);
|
|
832
|
+
throw SchemaException.fromErrors([
|
|
833
|
+
{
|
|
834
|
+
path: where,
|
|
835
|
+
expected: "a string, number, bool, null, Date, or an object with its own toString",
|
|
836
|
+
got,
|
|
837
|
+
value,
|
|
838
|
+
hint: Array.isArray(value) ? 'This cell holds an array. The usual cause is a `rows` entry nested one level too deep: `rows: [[["x"]]]` makes cell A1 an array, where `rows: [["x"]]` makes it the string "x". For a cell with formatting use the object form: {value: "x", format: {...}}.' : "Give the cell a scalar or a Date, or define toString() on the object so the conversion is yours rather than implicit."
|
|
839
|
+
}
|
|
840
|
+
]);
|
|
841
|
+
}
|
|
770
842
|
return value;
|
|
771
843
|
}
|
|
772
844
|
};
|
|
845
|
+
function isCellSpec(value) {
|
|
846
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
847
|
+
return Object.getPrototypeOf(value) === Object.prototype;
|
|
848
|
+
}
|
|
849
|
+
function hasOwnToString(value) {
|
|
850
|
+
const fn = value.toString;
|
|
851
|
+
return typeof fn === "function" && fn !== Object.prototype.toString;
|
|
852
|
+
}
|
|
773
853
|
function promoteFormula(value) {
|
|
774
854
|
if (typeof value === "string" && value.length > 1 && value[0] === "=") {
|
|
775
855
|
return [null, value.slice(1)];
|
|
@@ -780,6 +860,34 @@ function toInt(v) {
|
|
|
780
860
|
const n = Math.trunc(Number(v));
|
|
781
861
|
return Number.isFinite(n) ? n : 0;
|
|
782
862
|
}
|
|
863
|
+
function phpNumeric(v) {
|
|
864
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : null;
|
|
865
|
+
return isNumericString(v) ? numericStringToNumber(v) : null;
|
|
866
|
+
}
|
|
867
|
+
function normalizeRowHeights(map) {
|
|
868
|
+
const out = /* @__PURE__ */ new Map();
|
|
869
|
+
if (!isPlainObject(map) && !Array.isArray(map)) return out;
|
|
870
|
+
for (const [row, px] of Object.entries(map)) {
|
|
871
|
+
const n = phpNumeric(px);
|
|
872
|
+
if (/^[1-9]\d{0,6}$/.test(row) && n !== null && n >= 0) out.set(parseInt(row, 10), n);
|
|
873
|
+
}
|
|
874
|
+
return new Map([...out.entries()].sort((a, b) => a[0] - b[0]));
|
|
875
|
+
}
|
|
876
|
+
function normalizePrint(print) {
|
|
877
|
+
if (!isPlainObject(print) && !Array.isArray(print)) return null;
|
|
878
|
+
const p = print;
|
|
879
|
+
const orientation = p.orientation === "portrait" || p.orientation === "landscape" ? p.orientation : null;
|
|
880
|
+
const fitToWidth = p.fitToWidth === true;
|
|
881
|
+
let footer = null;
|
|
882
|
+
if (isPlainObject(p.footer) || Array.isArray(p.footer)) {
|
|
883
|
+
const f = p.footer;
|
|
884
|
+
const slot = (k) => typeof f[k] === "string" ? f[k] : "";
|
|
885
|
+
footer = { left: slot("left"), center: slot("center"), right: slot("right") };
|
|
886
|
+
if (footer.left === "" && footer.center === "" && footer.right === "") footer = null;
|
|
887
|
+
}
|
|
888
|
+
if (orientation === null && !fitToWidth && footer === null) return null;
|
|
889
|
+
return { orientation, fitToWidth, footer };
|
|
890
|
+
}
|
|
783
891
|
|
|
784
892
|
// src/schema/formula-linter.ts
|
|
785
893
|
var ERR_VALUE = "#VALUE!";
|
|
@@ -1301,54 +1409,6 @@ function phpRound(x, n) {
|
|
|
1301
1409
|
return Math.sign(x) * Math.round(Math.abs(x) * factor) / factor;
|
|
1302
1410
|
}
|
|
1303
1411
|
|
|
1304
|
-
// src/exceptions.ts
|
|
1305
|
-
var SchemaException = class _SchemaException extends Error {
|
|
1306
|
-
constructor(errors, message) {
|
|
1307
|
-
super(message ?? summarize(errors));
|
|
1308
|
-
this.name = "SchemaException";
|
|
1309
|
-
this.errors = errors;
|
|
1310
|
-
Object.setPrototypeOf(this, _SchemaException.prototype);
|
|
1311
|
-
}
|
|
1312
|
-
static fromErrors(errors) {
|
|
1313
|
-
return new _SchemaException(errors);
|
|
1314
|
-
}
|
|
1315
|
-
getErrors() {
|
|
1316
|
-
return this.errors;
|
|
1317
|
-
}
|
|
1318
|
-
};
|
|
1319
|
-
function summarize(errors) {
|
|
1320
|
-
if (errors.length === 1) {
|
|
1321
|
-
const e = errors[0];
|
|
1322
|
-
return `[holy-sheet] schema invalid at ${e.path}: expected ${e.expected}, got ${e.got}`;
|
|
1323
|
-
}
|
|
1324
|
-
const first2 = errors[0];
|
|
1325
|
-
const rest = errors.length - 1;
|
|
1326
|
-
return `[holy-sheet] schema invalid at ${first2.path}: expected ${first2.expected}, got ${first2.got} (+${rest} more)`;
|
|
1327
|
-
}
|
|
1328
|
-
var UnsupportedFormatException = class _UnsupportedFormatException extends Error {
|
|
1329
|
-
constructor(message, path = null, mimetype = null) {
|
|
1330
|
-
super(message);
|
|
1331
|
-
this.name = "UnsupportedFormatException";
|
|
1332
|
-
this.path = path;
|
|
1333
|
-
this.mimetype = mimetype;
|
|
1334
|
-
Object.setPrototypeOf(this, _UnsupportedFormatException.prototype);
|
|
1335
|
-
}
|
|
1336
|
-
static notAZip(path = null) {
|
|
1337
|
-
return new _UnsupportedFormatException(
|
|
1338
|
-
`[holy-sheet] cannot read ${path ?? "this file"}: it is not a zip archive, and xlsx and ods both are`,
|
|
1339
|
-
path
|
|
1340
|
-
);
|
|
1341
|
-
}
|
|
1342
|
-
static unknownPackage(path, mimetype) {
|
|
1343
|
-
const what = mimetype !== null && mimetype !== "" ? `its mimetype is ${mimetype}` : "it has neither xl/workbook.xml nor an OpenDocument mimetype";
|
|
1344
|
-
return new _UnsupportedFormatException(
|
|
1345
|
-
`[holy-sheet] cannot read ${path ?? "this file"}: ${what}. Supported: xlsx, ods`,
|
|
1346
|
-
path,
|
|
1347
|
-
mimetype !== "" ? mimetype : null
|
|
1348
|
-
);
|
|
1349
|
-
}
|
|
1350
|
-
};
|
|
1351
|
-
|
|
1352
1412
|
// src/schema/repairer.ts
|
|
1353
1413
|
var VALID_THEMES = ["default", "minimal", "plain", "business"];
|
|
1354
1414
|
var ISO_DATE2 = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?(\.\d+)?(Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
@@ -1595,7 +1655,21 @@ var Validator = class {
|
|
|
1595
1655
|
"Each row is an array of cell values, in column order."
|
|
1596
1656
|
)
|
|
1597
1657
|
);
|
|
1658
|
+
return;
|
|
1598
1659
|
}
|
|
1660
|
+
row.forEach((cell, k) => {
|
|
1661
|
+
if (Array.isArray(cell)) {
|
|
1662
|
+
errors.push(
|
|
1663
|
+
error(
|
|
1664
|
+
`${path}.rows[${j}][${k}]`,
|
|
1665
|
+
"a cell value or a cell object",
|
|
1666
|
+
"array",
|
|
1667
|
+
cell,
|
|
1668
|
+
'This cell is an array, which usually means the row is nested one level too deep: `rows: [[["x"]]]` makes cell A1 an array, where `rows: [["x"]]` makes it "x". For a cell with formatting use the object form: {value: "x", format: {...}}.'
|
|
1669
|
+
)
|
|
1670
|
+
);
|
|
1671
|
+
}
|
|
1672
|
+
});
|
|
1599
1673
|
});
|
|
1600
1674
|
}
|
|
1601
1675
|
}
|
|
@@ -1661,6 +1735,95 @@ var Validator = class {
|
|
|
1661
1735
|
}
|
|
1662
1736
|
}
|
|
1663
1737
|
}
|
|
1738
|
+
if (sheet.rowHeights != null) {
|
|
1739
|
+
if (!isPlainObject(sheet.rowHeights) && !Array.isArray(sheet.rowHeights)) {
|
|
1740
|
+
errors.push(
|
|
1741
|
+
error(
|
|
1742
|
+
`${path}.rowHeights`,
|
|
1743
|
+
"object keyed by 1-based row number",
|
|
1744
|
+
typeOf(sheet.rowHeights),
|
|
1745
|
+
sheet.rowHeights,
|
|
1746
|
+
'Row heights map a row NUMBER to pixels: {"1": 32, "4": 28}.'
|
|
1747
|
+
)
|
|
1748
|
+
);
|
|
1749
|
+
} else {
|
|
1750
|
+
for (const [row, px] of Object.entries(sheet.rowHeights)) {
|
|
1751
|
+
const n = typeof px === "number" ? px : isNumericString(px) ? numericStringToNumber(px) : null;
|
|
1752
|
+
if (!/^[1-9]\d{0,6}$/.test(row)) {
|
|
1753
|
+
errors.push(error(
|
|
1754
|
+
`${path}.rowHeights.${row}`,
|
|
1755
|
+
"a 1-based row number",
|
|
1756
|
+
typeOf(row),
|
|
1757
|
+
row,
|
|
1758
|
+
'Keys are row numbers as Excel shows them: "1" is the first row.'
|
|
1759
|
+
));
|
|
1760
|
+
} else if (n === null || !Number.isFinite(n) || n < 0) {
|
|
1761
|
+
errors.push(error(
|
|
1762
|
+
`${path}.rowHeights.${row}`,
|
|
1763
|
+
"a non-negative number of pixels",
|
|
1764
|
+
typeOf(px),
|
|
1765
|
+
px,
|
|
1766
|
+
"A height is a number of pixels, like 28 (20 is a default row)."
|
|
1767
|
+
));
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
if (sheet.print != null) {
|
|
1773
|
+
const print = sheet.print;
|
|
1774
|
+
if (!isPlainObject(print) && !Array.isArray(print)) {
|
|
1775
|
+
errors.push(error(
|
|
1776
|
+
`${path}.print`,
|
|
1777
|
+
"object {orientation?, fitToWidth?, footer?}",
|
|
1778
|
+
typeOf(print),
|
|
1779
|
+
print,
|
|
1780
|
+
'Print setup: {"orientation": "landscape", "fitToWidth": true, "footer": {"right": "Page {page} of {pages}"}}.'
|
|
1781
|
+
));
|
|
1782
|
+
} else {
|
|
1783
|
+
if (print.orientation != null && print.orientation !== "portrait" && print.orientation !== "landscape") {
|
|
1784
|
+
errors.push(error(
|
|
1785
|
+
`${path}.print.orientation`,
|
|
1786
|
+
"one of: portrait, landscape",
|
|
1787
|
+
typeOf(print.orientation),
|
|
1788
|
+
print.orientation,
|
|
1789
|
+
"Use landscape for a sheet wider than it is tall."
|
|
1790
|
+
));
|
|
1791
|
+
}
|
|
1792
|
+
if (print.fitToWidth != null && typeof print.fitToWidth !== "boolean") {
|
|
1793
|
+
errors.push(error(
|
|
1794
|
+
`${path}.print.fitToWidth`,
|
|
1795
|
+
"boolean",
|
|
1796
|
+
typeOf(print.fitToWidth),
|
|
1797
|
+
print.fitToWidth,
|
|
1798
|
+
"true scales the sheet to one page wide when printed."
|
|
1799
|
+
));
|
|
1800
|
+
}
|
|
1801
|
+
if (print.footer != null) {
|
|
1802
|
+
if (!isPlainObject(print.footer) && !Array.isArray(print.footer)) {
|
|
1803
|
+
errors.push(error(
|
|
1804
|
+
`${path}.print.footer`,
|
|
1805
|
+
"object {left?, center?, right?}",
|
|
1806
|
+
typeOf(print.footer),
|
|
1807
|
+
print.footer,
|
|
1808
|
+
"A footer has up to three slots of text."
|
|
1809
|
+
));
|
|
1810
|
+
} else {
|
|
1811
|
+
const footer = print.footer;
|
|
1812
|
+
for (const slot of ["left", "center", "right"]) {
|
|
1813
|
+
if (footer[slot] != null && typeof footer[slot] !== "string") {
|
|
1814
|
+
errors.push(error(
|
|
1815
|
+
`${path}.print.footer.${slot}`,
|
|
1816
|
+
"string",
|
|
1817
|
+
typeOf(footer[slot]),
|
|
1818
|
+
footer[slot],
|
|
1819
|
+
"Footer text; {page} and {pages} print the page number and count."
|
|
1820
|
+
));
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1664
1827
|
return errors;
|
|
1665
1828
|
}
|
|
1666
1829
|
validateColumn(col, path) {
|
|
@@ -2750,6 +2913,22 @@ function sheetToSchema(sheet) {
|
|
|
2750
2913
|
}
|
|
2751
2914
|
if (sheet.frozenRows > 0) out["frozenRows"] = sheet.frozenRows;
|
|
2752
2915
|
if (sheet.frozenCols > 0) out["frozenCols"] = sheet.frozenCols;
|
|
2916
|
+
if (sheet.rowHeights.size > 0) {
|
|
2917
|
+
const heights = {};
|
|
2918
|
+
for (const [k, v] of sheet.rowHeights) heights[k] = v;
|
|
2919
|
+
out["rowHeights"] = heights;
|
|
2920
|
+
}
|
|
2921
|
+
if (sheet.print !== null) {
|
|
2922
|
+
const print = {};
|
|
2923
|
+
if (sheet.print.orientation !== null) print["orientation"] = sheet.print.orientation;
|
|
2924
|
+
if (sheet.print.fitToWidth) print["fitToWidth"] = true;
|
|
2925
|
+
if (sheet.print.footer !== null) {
|
|
2926
|
+
const footer = {};
|
|
2927
|
+
for (const k of ["left", "center", "right"]) if (sheet.print.footer[k] !== "") footer[k] = sheet.print.footer[k];
|
|
2928
|
+
print["footer"] = footer;
|
|
2929
|
+
}
|
|
2930
|
+
out["print"] = print;
|
|
2931
|
+
}
|
|
2753
2932
|
return out;
|
|
2754
2933
|
}
|
|
2755
2934
|
function cellToSchema(cell) {
|
|
@@ -3372,9 +3551,62 @@ var WorksheetParser = {
|
|
|
3372
3551
|
const merges = parseMerges(xml);
|
|
3373
3552
|
const columnWidths = parseColumnWidths(xml);
|
|
3374
3553
|
const [frozenRows, frozenCols] = parseFrozen(xml);
|
|
3375
|
-
return new Sheet(name, cells, merges, columnWidths, frozenRows, frozenCols);
|
|
3554
|
+
return new Sheet(name, cells, merges, columnWidths, frozenRows, frozenCols, parseRowHeights(xml), parsePrint(xml));
|
|
3376
3555
|
}
|
|
3377
3556
|
};
|
|
3557
|
+
function parseRowHeights(xml) {
|
|
3558
|
+
const out = /* @__PURE__ */ new Map();
|
|
3559
|
+
const sheetData = el(xml, "sheetData");
|
|
3560
|
+
if (!sheetData) return out;
|
|
3561
|
+
for (const row of els(sheetData, "row")) {
|
|
3562
|
+
const r = parseInt(at(row, "r") ?? "0", 10);
|
|
3563
|
+
const custom = at(row, "customHeight");
|
|
3564
|
+
const ht = at(row, "ht");
|
|
3565
|
+
if (r > 0 && ht !== void 0 && (custom === "1" || custom === "true")) {
|
|
3566
|
+
out.set(r, Math.round(parseFloat(ht) / 0.75 * 100) / 100);
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
return out;
|
|
3570
|
+
}
|
|
3571
|
+
function parsePrint(xml) {
|
|
3572
|
+
const fitToPage = ["1", "true"].includes(at(el(el(xml, "sheetPr"), "pageSetUpPr"), "fitToPage") ?? "");
|
|
3573
|
+
const setup = el(xml, "pageSetup");
|
|
3574
|
+
const o = at(setup, "orientation");
|
|
3575
|
+
const orientation = o === "portrait" || o === "landscape" ? o : null;
|
|
3576
|
+
const fitToWidth = fitToPage && setup !== void 0 && at(setup, "fitToWidth") === "1" && ["0", ""].includes(at(setup, "fitToHeight") ?? "");
|
|
3577
|
+
const oddFooter = el(el(xml, "headerFooter"), "oddFooter");
|
|
3578
|
+
const footer = oddFooter !== void 0 ? parseFooterCodes(oddFooter.text) : null;
|
|
3579
|
+
if (orientation === null && !fitToWidth && footer === null) return null;
|
|
3580
|
+
return { orientation, fitToWidth, footer };
|
|
3581
|
+
}
|
|
3582
|
+
function parseFooterCodes(codes) {
|
|
3583
|
+
const slots = { left: "", center: "", right: "" };
|
|
3584
|
+
let current = "center";
|
|
3585
|
+
const len = codes.length;
|
|
3586
|
+
for (let i = 0; i < len; i++) {
|
|
3587
|
+
const ch = codes[i];
|
|
3588
|
+
if (ch !== "&" || i + 1 >= len) {
|
|
3589
|
+
slots[current] += ch;
|
|
3590
|
+
continue;
|
|
3591
|
+
}
|
|
3592
|
+
const next = codes[++i];
|
|
3593
|
+
if (next === "&") slots[current] += "&";
|
|
3594
|
+
else if (next === "L") current = "left";
|
|
3595
|
+
else if (next === "C") current = "center";
|
|
3596
|
+
else if (next === "R") current = "right";
|
|
3597
|
+
else if (next === "P") slots[current] += "{page}";
|
|
3598
|
+
else if (next === "N") slots[current] += "{pages}";
|
|
3599
|
+
else if (next === '"') {
|
|
3600
|
+
const close = codes.indexOf('"', i + 1);
|
|
3601
|
+
i = close === -1 ? len : close;
|
|
3602
|
+
} else if (/[0-9]/.test(next)) {
|
|
3603
|
+
while (i + 1 < len && /[0-9]/.test(codes[i + 1])) i++;
|
|
3604
|
+
} else if (next === "K") {
|
|
3605
|
+
i = Math.min(len - 1, i + 6);
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
return slots.left === "" && slots.center === "" && slots.right === "" ? null : slots;
|
|
3609
|
+
}
|
|
3378
3610
|
function parseCells(xml, stylesIndex, comments, sharedStrings) {
|
|
3379
3611
|
const cells = /* @__PURE__ */ new Map();
|
|
3380
3612
|
const sheetData = el(xml, "sheetData");
|
|
@@ -3750,6 +3982,19 @@ function hexToArgb(hex2) {
|
|
|
3750
3982
|
|
|
3751
3983
|
// src/writer/xlsx-writer.ts
|
|
3752
3984
|
var encoder2 = new TextEncoder();
|
|
3985
|
+
function footerCodes(footer) {
|
|
3986
|
+
let codes = "";
|
|
3987
|
+
for (const [slot, code] of [["left", "&L"], ["center", "&C"], ["right", "&R"]]) {
|
|
3988
|
+
if (footer[slot] !== "") {
|
|
3989
|
+
codes += code + footer[slot].replace(/&/g, "&&").replace(/\{pages\}/g, "&N").replace(/\{page\}/g, "&P");
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
return codes;
|
|
3993
|
+
}
|
|
3994
|
+
function decimal(value) {
|
|
3995
|
+
const out = value.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
|
|
3996
|
+
return out === "" || out === "-0" ? "0" : out;
|
|
3997
|
+
}
|
|
3753
3998
|
var XlsxWriter = class {
|
|
3754
3999
|
toBytes(workbook) {
|
|
3755
4000
|
const styles = new StylesRegistry();
|
|
@@ -3838,14 +4083,20 @@ var XlsxWriter = class {
|
|
|
3838
4083
|
}
|
|
3839
4084
|
colsXml += "</cols>";
|
|
3840
4085
|
}
|
|
4086
|
+
const rows = sheet.rows();
|
|
4087
|
+
for (const rowIndex of sheet.rowHeights.keys()) {
|
|
4088
|
+
if (!rows.has(rowIndex)) rows.set(rowIndex, /* @__PURE__ */ new Map());
|
|
4089
|
+
}
|
|
3841
4090
|
let rowsXml = "";
|
|
3842
|
-
for (const [rowIndex, row] of
|
|
4091
|
+
for (const [rowIndex, row] of [...rows.entries()].sort((a, b) => a[0] - b[0])) {
|
|
3843
4092
|
let cellsXml = "";
|
|
3844
4093
|
const sortedCols = [...row.keys()].sort();
|
|
3845
4094
|
for (const col of sortedCols) {
|
|
3846
4095
|
cellsXml += this.cellXml(row.get(col), styles);
|
|
3847
4096
|
}
|
|
3848
|
-
|
|
4097
|
+
const height = sheet.rowHeights.get(rowIndex);
|
|
4098
|
+
const ht = height !== void 0 ? ` ht="${decimal(height * 0.75)}" customHeight="1"` : "";
|
|
4099
|
+
rowsXml += cellsXml === "" ? `<row r="${rowIndex}"${ht}/>` : `<row r="${rowIndex}"${ht}>${cellsXml}</row>`;
|
|
3849
4100
|
}
|
|
3850
4101
|
let mergesXml = "";
|
|
3851
4102
|
if (sheet.mergedRegions.length > 0) {
|
|
@@ -3854,7 +4105,14 @@ var XlsxWriter = class {
|
|
|
3854
4105
|
mergesXml += "</mergeCells>";
|
|
3855
4106
|
}
|
|
3856
4107
|
const legacyDrawing = sheet.hasComments() ? '<legacyDrawing r:id="rId1"/>' : "";
|
|
3857
|
-
|
|
4108
|
+
const print = sheet.print;
|
|
4109
|
+
const sheetPrXml = print?.fitToWidth ? '<sheetPr><pageSetUpPr fitToPage="1"/></sheetPr>' : "";
|
|
4110
|
+
let pageSetupXml = "";
|
|
4111
|
+
if (print !== null && (print.orientation !== null || print.fitToWidth)) {
|
|
4112
|
+
pageSetupXml = "<pageSetup" + (print.orientation !== null ? ` orientation="${print.orientation}"` : "") + (print.fitToWidth ? ' fitToWidth="1" fitToHeight="0"' : "") + "/>";
|
|
4113
|
+
}
|
|
4114
|
+
const headerFooterXml = print?.footer ? `<headerFooter><oddFooter>${this.escape(footerCodes(print.footer))}</oddFooter></headerFooter>` : "";
|
|
4115
|
+
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">' + sheetPrXml + sheetViewsXml + colsXml + `<sheetData>${rowsXml}</sheetData>` + mergesXml + pageSetupXml + headerFooterXml + legacyDrawing + "</worksheet>";
|
|
3858
4116
|
}
|
|
3859
4117
|
cellXml(cell, styles) {
|
|
3860
4118
|
const ref = cell.address;
|
|
@@ -4971,6 +5229,31 @@ var holy_sheet_schema_default = {
|
|
|
4971
5229
|
type: "integer",
|
|
4972
5230
|
minimum: 0,
|
|
4973
5231
|
description: "How many columns stay visible while the sheet scrolls sideways. Set to 1 when the first column is the label for the row."
|
|
5232
|
+
},
|
|
5233
|
+
rowHeights: {
|
|
5234
|
+
type: "object",
|
|
5235
|
+
description: 'Row heights in pixels, keyed by 1-based row NUMBER as Excel shows it: {"1": 32, "4": 28}. A default row is 20; give a title row or a header row room (28-36) instead of packing text against its fill.',
|
|
5236
|
+
propertyNames: { pattern: "^[1-9][0-9]*$" },
|
|
5237
|
+
additionalProperties: { type: "number", minimum: 0 }
|
|
5238
|
+
},
|
|
5239
|
+
print: {
|
|
5240
|
+
type: "object",
|
|
5241
|
+
additionalProperties: false,
|
|
5242
|
+
description: "How the sheet prints. A sheet wider than a page splits its columns across pages unless fitToWidth is set.",
|
|
5243
|
+
properties: {
|
|
5244
|
+
orientation: { type: "string", enum: ["portrait", "landscape"] },
|
|
5245
|
+
fitToWidth: { type: "boolean", description: "Scale the sheet to one page wide (as many pages tall as it needs)." },
|
|
5246
|
+
footer: {
|
|
5247
|
+
type: "object",
|
|
5248
|
+
additionalProperties: false,
|
|
5249
|
+
description: 'A printed footer: up to three slots. {page} and {pages} print the page number and count: {"left": "Q3 Revenue Review", "right": "Page {page} of {pages}"}.',
|
|
5250
|
+
properties: {
|
|
5251
|
+
left: { type: "string" },
|
|
5252
|
+
center: { type: "string" },
|
|
5253
|
+
right: { type: "string" }
|
|
5254
|
+
}
|
|
5255
|
+
}
|
|
5256
|
+
}
|
|
4974
5257
|
}
|
|
4975
5258
|
}
|
|
4976
5259
|
},
|
|
@@ -5179,7 +5462,7 @@ var holy_sheet_schema_default = {
|
|
|
5179
5462
|
};
|
|
5180
5463
|
|
|
5181
5464
|
// src/agent.ts
|
|
5182
|
-
var VERSION = "2.
|
|
5465
|
+
var VERSION = "2.7.0";
|
|
5183
5466
|
function toU8(input) {
|
|
5184
5467
|
return input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
5185
5468
|
}
|