@bendyline/squisq-formats 2.5.1 → 2.6.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/NOTICE.md +10 -9
- package/dist/{chunk-TVUX3RUC.js → chunk-2OCP46K5.js} +10 -3
- package/dist/{chunk-2LT3JL7U.js → chunk-3ITGQRL5.js} +8 -8
- package/dist/{chunk-FMQWNPCV.js → chunk-AGGNLRHV.js} +6 -6
- package/dist/{chunk-AONELFLA.js → chunk-B7GEAZBI.js} +6 -1
- package/dist/chunk-CKSTGNVZ.js +724 -0
- package/dist/chunk-DXYWKZ52.js +57 -0
- package/dist/chunk-FLR2ARKC.js +240 -0
- package/dist/{chunk-5JQ5NDRC.js → chunk-JPU5GPGG.js} +64 -12
- package/dist/{chunk-RX55T5HO.js → chunk-OBADJV7C.js} +136 -415
- package/dist/{chunk-T4PX33AG.js → chunk-POO3PJFH.js} +23 -3
- package/dist/{chunk-AD2WT564.js → chunk-Q77KNJIN.js} +45 -0
- package/dist/{chunk-6RQOV3B3.js → chunk-VPWPEMZJ.js} +1 -1
- package/dist/{chunk-FIOSE4BO.js → chunk-XJNOZTAY.js} +6 -6
- package/dist/csv/index.d.ts +67 -1
- package/dist/csv/index.js +10 -3
- package/dist/data/index.d.ts +78 -0
- package/dist/data/index.js +30 -0
- package/dist/docx/index.js +3 -3
- package/dist/epub/index.js +4 -4
- package/dist/{export-Boq78GMq.d.ts → export-6iQXd-lQ.d.ts} +98 -2
- package/dist/html/index.d.ts +2 -8
- package/dist/html/index.js +3 -3
- package/dist/{images-ESPQKVTW.js → images-JLBFCDD4.js} +1 -1
- package/dist/{import-B0gBYUmd.d.ts → import-D59rxNTj.d.ts} +11 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +29 -26
- package/dist/infer/index.js +6 -6
- package/dist/materialize-A34OZGEU.js +11 -0
- package/dist/outside-in/index.d.ts +4 -4
- package/dist/outside-in/index.js +2 -2
- package/dist/pdf/index.js +2 -2
- package/dist/pptx/index.js +3 -3
- package/dist/registry/index.d.ts +4 -4
- package/dist/registry/index.js +1 -1
- package/dist/{types-bwP9PBSk.d.ts → types-BD23kHNG.d.ts} +5 -5
- package/dist/xlsx/index.d.ts +85 -4
- package/dist/xlsx/index.js +21 -4
- package/package.json +17 -2
- package/dist/{chunk-KMBBO5H7.js → chunk-7DQP2I57.js} +4 -4
- package/dist/{chunk-M7XPXGXW.js → chunk-ROF7SSQP.js} +3 -3
|
@@ -1,23 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from "./chunk-AVOZAKGP.js";
|
|
2
|
+
planDataSidecar
|
|
3
|
+
} from "./chunk-DXYWKZ52.js";
|
|
5
4
|
import {
|
|
6
|
-
createPackage
|
|
7
|
-
} from "./chunk-ILCJ3WFD.js";
|
|
8
|
-
import {
|
|
9
|
-
escapeXml,
|
|
10
|
-
xmlDeclaration
|
|
11
|
-
} from "./chunk-JU2RHXUB.js";
|
|
12
|
-
import {
|
|
13
|
-
CONTENT_TYPE_XLSX_STYLES,
|
|
14
|
-
CONTENT_TYPE_XLSX_WORKBOOK,
|
|
15
|
-
CONTENT_TYPE_XLSX_WORKSHEET,
|
|
16
5
|
NS_R,
|
|
17
6
|
NS_SML,
|
|
18
|
-
REL_OFFICE_DOCUMENT,
|
|
19
|
-
REL_STYLES,
|
|
20
|
-
REL_WORKSHEET,
|
|
21
7
|
baseDirOf,
|
|
22
8
|
getPartRelationships,
|
|
23
9
|
getPartXml,
|
|
@@ -25,9 +11,6 @@ import {
|
|
|
25
11
|
requireMainPartPath
|
|
26
12
|
} from "./chunk-S5PCVMKU.js";
|
|
27
13
|
|
|
28
|
-
// src/xlsx/index.ts
|
|
29
|
-
import { markdownToDoc } from "@bendyline/squisq/doc";
|
|
30
|
-
|
|
31
14
|
// src/xlsx/cells.ts
|
|
32
15
|
var MAX_COL_INDEX = 16383;
|
|
33
16
|
var MAX_ROW_INDEX = 1048575;
|
|
@@ -495,6 +478,8 @@ function gridToTables(sheet, grid, merges = [], options = {}) {
|
|
|
495
478
|
}
|
|
496
479
|
|
|
497
480
|
// src/xlsx/import.ts
|
|
481
|
+
import { stringifyMarkdown } from "@bendyline/squisq/markdown";
|
|
482
|
+
import { MemoryContentContainer } from "@bendyline/squisq/storage";
|
|
498
483
|
var XLSX_MAIN_PART = "xl/workbook.xml";
|
|
499
484
|
function attrNS(el, ns, local, fallback) {
|
|
500
485
|
return el.getAttributeNS(ns, local) ?? el.getAttribute(fallback);
|
|
@@ -508,7 +493,7 @@ function resolveTarget(baseDir, target) {
|
|
|
508
493
|
}
|
|
509
494
|
return stack.join("/");
|
|
510
495
|
}
|
|
511
|
-
async function
|
|
496
|
+
async function listSheetParts(pkg, mainPart) {
|
|
512
497
|
const wb = await getPartXml(pkg, mainPart);
|
|
513
498
|
if (!wb) {
|
|
514
499
|
throw new Error(`Invalid XLSX package: workbook part "${mainPart}" could not be parsed.`);
|
|
@@ -524,11 +509,22 @@ async function readWorkbook(pkg, mainPart) {
|
|
|
524
509
|
const target = rid ? relById.get(rid) : void 0;
|
|
525
510
|
if (target) out.push({ name, path: resolveTarget(baseDirOf(mainPart), target) });
|
|
526
511
|
}
|
|
512
|
+
return out;
|
|
513
|
+
}
|
|
514
|
+
async function readWorkbook(pkg, mainPart) {
|
|
515
|
+
const wb = await getPartXml(pkg, mainPart);
|
|
516
|
+
if (!wb) {
|
|
517
|
+
throw new Error(`Invalid XLSX package: workbook part "${mainPart}" could not be parsed.`);
|
|
518
|
+
}
|
|
519
|
+
const sheets = await listSheetParts(pkg, mainPart);
|
|
527
520
|
const workbookPr = wb.getElementsByTagNameNS(NS_SML, "workbookPr")[0];
|
|
528
521
|
const date1904Value = workbookPr?.getAttribute("date1904");
|
|
522
|
+
const calcPr = wb.getElementsByTagNameNS(NS_SML, "calcPr")[0];
|
|
523
|
+
const fullCalcValue = calcPr?.getAttribute("fullCalcOnLoad");
|
|
529
524
|
return {
|
|
530
|
-
sheets
|
|
531
|
-
date1904: date1904Value === "1" || date1904Value === "true"
|
|
525
|
+
sheets,
|
|
526
|
+
date1904: date1904Value === "1" || date1904Value === "true",
|
|
527
|
+
fullCalcOnLoad: fullCalcValue === "1" || fullCalcValue === "true"
|
|
532
528
|
};
|
|
533
529
|
}
|
|
534
530
|
function isPhonetic(el, root) {
|
|
@@ -698,28 +694,32 @@ function formattedNumberText(raw, style, date1904) {
|
|
|
698
694
|
function readFormula(cell, row, col, ctx) {
|
|
699
695
|
const fEls = cell.getElementsByTagNameNS(NS_SML, "f");
|
|
700
696
|
const f = fEls.length ? fEls[0] : null;
|
|
701
|
-
if (!f) return "";
|
|
697
|
+
if (!f) return { text: "" };
|
|
702
698
|
const text = (f.textContent ?? "").trim();
|
|
703
|
-
if (f.getAttribute("t") !== "shared") return text;
|
|
699
|
+
if (f.getAttribute("t") !== "shared") return { text };
|
|
704
700
|
const si = f.getAttribute("si");
|
|
705
|
-
if (si === null) return text;
|
|
701
|
+
if (si === null) return { text };
|
|
706
702
|
if (text !== "") {
|
|
707
703
|
ctx.sharedFormulas.set(si, { text, row, col });
|
|
708
|
-
return text;
|
|
704
|
+
return { text, sharedRole: "master" };
|
|
709
705
|
}
|
|
710
706
|
const master = ctx.sharedFormulas.get(si);
|
|
711
|
-
if (!master) return "";
|
|
707
|
+
if (!master) return { text: "", sharedRole: "follower" };
|
|
712
708
|
try {
|
|
713
|
-
return
|
|
709
|
+
return {
|
|
710
|
+
text: translateFormula(master.text, row - master.row, col - master.col),
|
|
711
|
+
sharedRole: "follower"
|
|
712
|
+
};
|
|
714
713
|
} catch {
|
|
715
|
-
return "";
|
|
714
|
+
return { text: "", sharedRole: "follower" };
|
|
716
715
|
}
|
|
717
716
|
}
|
|
718
717
|
function readCell(cell, row, col, ctx) {
|
|
719
|
-
const formula = readFormula(cell, row, col, ctx);
|
|
718
|
+
const { text: formula, sharedRole } = readFormula(cell, row, col, ctx);
|
|
720
719
|
const withFormula = (text2, kind2, value) => {
|
|
721
720
|
const out = { text: text2, kind: text2 === "" ? "empty" : kind2 };
|
|
722
721
|
if (formula !== "") out.formula = formula;
|
|
722
|
+
if (sharedRole !== void 0) out.sharedFormulaRole = sharedRole;
|
|
723
723
|
if (value !== void 0 && out.kind !== "empty") out.value = value;
|
|
724
724
|
return out;
|
|
725
725
|
};
|
|
@@ -866,7 +866,50 @@ async function xlsxToTables(data, options = {}) {
|
|
|
866
866
|
}
|
|
867
867
|
return out;
|
|
868
868
|
}
|
|
869
|
+
async function xlsxToCellGrids(data, options = {}) {
|
|
870
|
+
const pkg = await openPackage(data, options);
|
|
871
|
+
const mainPart = requireMainPartPath(pkg, XLSX_MAIN_PART, "XLSX");
|
|
872
|
+
const [{ sheets, date1904, fullCalcOnLoad }, shared, styles] = await Promise.all([
|
|
873
|
+
readWorkbook(pkg, mainPart),
|
|
874
|
+
readSharedStrings(pkg),
|
|
875
|
+
readCellStyles(pkg)
|
|
876
|
+
]);
|
|
877
|
+
let selected = sheets;
|
|
878
|
+
if (options.sheet !== void 0) {
|
|
879
|
+
const picked = typeof options.sheet === "number" ? sheets[options.sheet] : sheets.find((s) => s.name === options.sheet);
|
|
880
|
+
selected = picked ? [picked] : [];
|
|
881
|
+
}
|
|
882
|
+
const out = [];
|
|
883
|
+
for (const sheet of selected) {
|
|
884
|
+
const { cells, merges } = await sheetToCells(pkg, sheet.path, shared, styles, date1904);
|
|
885
|
+
out.push({ name: sheet.name, cells, merges });
|
|
886
|
+
}
|
|
887
|
+
return { sheets: out, date1904, fullCalcOnLoad };
|
|
888
|
+
}
|
|
869
889
|
async function xlsxToMarkdownDoc(data, options = {}) {
|
|
890
|
+
return workbookToMarkdown(data, options, void 0);
|
|
891
|
+
}
|
|
892
|
+
function shouldSpillRegion(spill, rect, hasHeader) {
|
|
893
|
+
if (spill.mode === "always") return true;
|
|
894
|
+
const height = rect.bottom - rect.top + 1;
|
|
895
|
+
const width = rect.right - rect.left + 1;
|
|
896
|
+
const dataRows = height - (hasHeader ? 1 : 0);
|
|
897
|
+
return dataRows > spill.maxInlineRows || height * width > spill.maxInlineCells;
|
|
898
|
+
}
|
|
899
|
+
function shouldSpillGrid(spill, cells) {
|
|
900
|
+
if (spill.mode === "always") return true;
|
|
901
|
+
const width = cells.reduce((max, row) => Math.max(max, row.length), 0);
|
|
902
|
+
return cells.length - 1 > spill.maxInlineRows || cells.length * width > spill.maxInlineCells;
|
|
903
|
+
}
|
|
904
|
+
function sidecarLinkParagraph(spill) {
|
|
905
|
+
return {
|
|
906
|
+
type: "paragraph",
|
|
907
|
+
children: [
|
|
908
|
+
{ type: "link", url: spill.src, children: [{ type: "text", value: spill.fileName }] }
|
|
909
|
+
]
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
async function workbookToMarkdown(data, options, spill) {
|
|
870
913
|
const pkg = await openPackage(data, options);
|
|
871
914
|
const mainPart = requireMainPartPath(pkg, XLSX_MAIN_PART, "XLSX");
|
|
872
915
|
const [{ sheets, date1904 }, shared, styles] = await Promise.all([
|
|
@@ -890,6 +933,14 @@ async function xlsxToMarkdownDoc(data, options = {}) {
|
|
|
890
933
|
}
|
|
891
934
|
if (cells.length === 0) continue;
|
|
892
935
|
if (!useRegions) {
|
|
936
|
+
if (spill && shouldSpillGrid(spill, cells)) {
|
|
937
|
+
children.push(
|
|
938
|
+
annotatedHeading(single ? 1 : 2, sheet.name, { src: spill.src, sheet: sheet.name }),
|
|
939
|
+
sidecarLinkParagraph(spill)
|
|
940
|
+
);
|
|
941
|
+
spill.used = true;
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
893
944
|
children.push(cellsToTable(cells));
|
|
894
945
|
continue;
|
|
895
946
|
}
|
|
@@ -900,6 +951,14 @@ async function xlsxToMarkdownDoc(data, options = {}) {
|
|
|
900
951
|
});
|
|
901
952
|
for (const warning of plan.warnings) console.warn(`XLSX import: ${sheet.name}: ${warning}`);
|
|
902
953
|
if (plan.degraded || plan.regions.length === 0 && plan.strays.length === 0) {
|
|
954
|
+
if (spill && shouldSpillGrid(spill, cells)) {
|
|
955
|
+
children.push(
|
|
956
|
+
annotatedHeading(single ? 1 : 2, sheet.name, { src: spill.src, sheet: sheet.name }),
|
|
957
|
+
sidecarLinkParagraph(spill)
|
|
958
|
+
);
|
|
959
|
+
spill.used = true;
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
903
962
|
children.push(cellsToTable(cells));
|
|
904
963
|
continue;
|
|
905
964
|
}
|
|
@@ -908,6 +967,21 @@ async function xlsxToMarkdownDoc(data, options = {}) {
|
|
|
908
967
|
const slice = sliceRect(cells, region.rect, EMPTY_CELL);
|
|
909
968
|
const anchor = formatCellRef(region.rect.top, region.rect.left);
|
|
910
969
|
const title = region.title ?? `${sheet.name} \u2014 ${anchor}`;
|
|
970
|
+
const hasHeaderRow = inferHeader(slice);
|
|
971
|
+
if (spill && shouldSpillRegion(spill, region.rect, hasHeaderRow)) {
|
|
972
|
+
children.push(
|
|
973
|
+
annotatedHeading(depth, title, {
|
|
974
|
+
src: spill.src,
|
|
975
|
+
sheet: sheet.name,
|
|
976
|
+
anchor,
|
|
977
|
+
...hasHeaderRow ? {} : { headerRow: "false" },
|
|
978
|
+
...region.titleCell ? { titleAnchor: formatCellRef(region.titleCell.row, region.titleCell.col) } : {}
|
|
979
|
+
}),
|
|
980
|
+
sidecarLinkParagraph(spill)
|
|
981
|
+
);
|
|
982
|
+
spill.used = true;
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
911
985
|
children.push(
|
|
912
986
|
annotatedHeading(depth, title, {
|
|
913
987
|
sheet: sheet.name,
|
|
@@ -916,7 +990,7 @@ async function xlsxToMarkdownDoc(data, options = {}) {
|
|
|
916
990
|
// says. (`headerRow`, not `header` — `dataTable` already declares a
|
|
917
991
|
// `headers` input, and two params one letter apart in the same
|
|
918
992
|
// annotation is a trap for anyone reading or editing the markdown.)
|
|
919
|
-
...
|
|
993
|
+
...hasHeaderRow ? {} : { headerRow: "false" },
|
|
920
994
|
// A caption promoted into the heading has left its cell behind;
|
|
921
995
|
// record where, so the reverse path can put the text back.
|
|
922
996
|
...region.titleCell ? { titleAnchor: formatCellRef(region.titleCell.row, region.titleCell.col) } : {}
|
|
@@ -947,396 +1021,43 @@ async function xlsxToMarkdownDoc(data, options = {}) {
|
|
|
947
1021
|
}
|
|
948
1022
|
return { type: "document", children };
|
|
949
1023
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
function cellKey(row, col) {
|
|
962
|
-
return row * KEY_STRIDE + col;
|
|
963
|
-
}
|
|
964
|
-
function keyRow(key) {
|
|
965
|
-
return Math.floor(key / KEY_STRIDE);
|
|
966
|
-
}
|
|
967
|
-
function keyCol(key) {
|
|
968
|
-
return key - keyRow(key) * KEY_STRIDE;
|
|
969
|
-
}
|
|
970
|
-
function tableToGrid(table) {
|
|
971
|
-
return table.children.map(
|
|
972
|
-
(row) => row.children.map((cell) => extractPlainText(cell.children))
|
|
973
|
-
);
|
|
974
|
-
}
|
|
975
|
-
function tableToRichGrid(table) {
|
|
976
|
-
return table.children.map(
|
|
977
|
-
(row) => row.children.map((cell) => hasRichCellContent(cell.children) ? cell.children : void 0)
|
|
978
|
-
);
|
|
979
|
-
}
|
|
980
|
-
function readRole(raw) {
|
|
981
|
-
return raw === "formulas" || raw === "loose" ? raw : "values";
|
|
982
|
-
}
|
|
983
|
-
function collectEntries(nodes) {
|
|
984
|
-
const entries = [];
|
|
985
|
-
let pending = null;
|
|
986
|
-
for (const node of nodes) {
|
|
987
|
-
if (node.type === "heading") {
|
|
988
|
-
pending = node;
|
|
989
|
-
continue;
|
|
990
|
-
}
|
|
991
|
-
if (node.type !== "table") continue;
|
|
992
|
-
const params = pending?.templateAnnotation?.params ?? {};
|
|
993
|
-
const sheetKey = typeof params.sheet === "string" && params.sheet !== "" ? params.sheet : null;
|
|
994
|
-
entries.push({
|
|
995
|
-
table: node,
|
|
996
|
-
headingText: pending ? extractPlainText(pending.children) : "",
|
|
997
|
-
params,
|
|
998
|
-
sheetKey,
|
|
999
|
-
role: readRole(params.role)
|
|
1000
|
-
});
|
|
1001
|
-
pending = null;
|
|
1002
|
-
}
|
|
1003
|
-
return entries;
|
|
1004
|
-
}
|
|
1005
|
-
function groupEntries(entries) {
|
|
1006
|
-
const groups = [];
|
|
1007
|
-
const bySheet = /* @__PURE__ */ new Map();
|
|
1008
|
-
for (const entry of entries) {
|
|
1009
|
-
if (entry.sheetKey !== null) {
|
|
1010
|
-
let group = bySheet.get(entry.sheetKey);
|
|
1011
|
-
if (!group) {
|
|
1012
|
-
group = { candidate: entry.sheetKey, entries: [], anchored: true };
|
|
1013
|
-
bySheet.set(entry.sheetKey, group);
|
|
1014
|
-
groups.push(group);
|
|
1015
|
-
}
|
|
1016
|
-
group.entries.push(entry);
|
|
1017
|
-
continue;
|
|
1018
|
-
}
|
|
1019
|
-
groups.push({ candidate: entry.headingText || null, entries: [entry], anchored: false });
|
|
1020
|
-
}
|
|
1021
|
-
return groups;
|
|
1022
|
-
}
|
|
1023
|
-
function place(cells, row, col, cell, clashes) {
|
|
1024
|
-
const key = cellKey(row, col);
|
|
1025
|
-
if (cells.has(key)) clashes.push(formatCellRef(row, col));
|
|
1026
|
-
cells.set(key, cell);
|
|
1027
|
-
}
|
|
1028
|
-
function overlayFormula(cells, row, col, formula) {
|
|
1029
|
-
const key = cellKey(row, col);
|
|
1030
|
-
const existing = cells.get(key);
|
|
1031
|
-
cells.set(key, { text: existing?.text ?? "", formula });
|
|
1032
|
-
}
|
|
1033
|
-
function formulaSource(raw) {
|
|
1034
|
-
const trimmed = raw.trim();
|
|
1035
|
-
return trimmed.startsWith("=") ? trimmed.slice(1) : trimmed;
|
|
1036
|
-
}
|
|
1037
|
-
function placeValues(entry, cells, warnings, sheetName, clashes) {
|
|
1038
|
-
const grid = tableToGrid(entry.table);
|
|
1039
|
-
const richGrid = tableToRichGrid(entry.table);
|
|
1040
|
-
const anchorRaw = entry.params.anchor ?? "A1";
|
|
1041
|
-
let anchor = parseCellRef(anchorRaw);
|
|
1042
|
-
if (!anchor) {
|
|
1043
|
-
warnings.push(
|
|
1044
|
-
`Sheet "${sheetName}": anchor "${anchorRaw}" is not a valid cell reference; placed at A1 instead.`
|
|
1045
|
-
);
|
|
1046
|
-
anchor = { row: 0, col: 0 };
|
|
1047
|
-
}
|
|
1048
|
-
const height = grid.length;
|
|
1049
|
-
const width = grid.reduce((m, r) => Math.max(m, r.length), 0);
|
|
1050
|
-
if (anchor.row + height - 1 > MAX_ROW_INDEX || anchor.col + width - 1 > MAX_COL_INDEX) {
|
|
1051
|
-
warnings.push(
|
|
1052
|
-
`Sheet "${sheetName}": a table anchored at ${anchorRaw} runs past the end of the sheet; placed at A1 instead.`
|
|
1053
|
-
);
|
|
1054
|
-
anchor = { row: 0, col: 0 };
|
|
1055
|
-
}
|
|
1056
|
-
const titleRef = entry.params.titleAnchor;
|
|
1057
|
-
if (titleRef !== void 0 && entry.headingText !== "") {
|
|
1058
|
-
const at = parseCellRef(titleRef);
|
|
1059
|
-
if (at) place(cells, at.row, at.col, { text: entry.headingText }, clashes);
|
|
1060
|
-
}
|
|
1061
|
-
for (let r = 0; r < grid.length; r++) {
|
|
1062
|
-
const row = grid[r];
|
|
1063
|
-
for (let c = 0; c < row.length; c++) {
|
|
1064
|
-
const text = row[c];
|
|
1065
|
-
if (text === "") continue;
|
|
1066
|
-
const rich = richGrid[r]?.[c];
|
|
1067
|
-
place(cells, anchor.row + r, anchor.col + c, rich ? { text, rich } : { text }, clashes);
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
function placeFormulas(entry, cells, warnings, sheetName) {
|
|
1072
|
-
const grid = tableToGrid(entry.table);
|
|
1073
|
-
if (grid.length < 2) return;
|
|
1074
|
-
const anchorRaw = entry.params.anchor ?? "A1";
|
|
1075
|
-
const anchor = parseCellRef(anchorRaw);
|
|
1076
|
-
if (!anchor) {
|
|
1077
|
-
warnings.push(
|
|
1078
|
-
`Sheet "${sheetName}": formulas block anchor "${anchorRaw}" is not a valid cell reference; skipped.`
|
|
1079
|
-
);
|
|
1080
|
-
return;
|
|
1081
|
-
}
|
|
1082
|
-
const columns = grid[0].map((letters) => {
|
|
1083
|
-
const trimmed = letters.trim();
|
|
1084
|
-
if (!/^[A-Za-z]{1,3}$/.test(trimmed)) return -1;
|
|
1085
|
-
const col = columnIndexFromLetters(trimmed);
|
|
1086
|
-
return col >= 0 && col <= MAX_COL_INDEX ? col : -1;
|
|
1087
|
-
});
|
|
1088
|
-
for (let r = 1; r < grid.length; r++) {
|
|
1089
|
-
const row = grid[r];
|
|
1090
|
-
for (let c = 0; c < row.length; c++) {
|
|
1091
|
-
const raw = row[c];
|
|
1092
|
-
if (raw.trim() === "") continue;
|
|
1093
|
-
const mapped = columns[c];
|
|
1094
|
-
const col = mapped !== void 0 && mapped >= 0 ? mapped : anchor.col + c;
|
|
1095
|
-
const targetRow = anchor.row + r - 1;
|
|
1096
|
-
if (targetRow > MAX_ROW_INDEX) continue;
|
|
1097
|
-
overlayFormula(cells, targetRow, col, formulaSource(raw));
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
function placeLoose(entry, cells, warnings, sheetName, clashes) {
|
|
1102
|
-
const grid = tableToGrid(entry.table);
|
|
1103
|
-
const richGrid = tableToRichGrid(entry.table);
|
|
1104
|
-
let skipped = 0;
|
|
1105
|
-
for (let r = 1; r < grid.length; r++) {
|
|
1106
|
-
const row = grid[r];
|
|
1107
|
-
const ref = (row[0] ?? "").trim();
|
|
1108
|
-
if (ref === "") continue;
|
|
1109
|
-
const at = parseCellRef(ref);
|
|
1110
|
-
if (!at) {
|
|
1111
|
-
skipped++;
|
|
1112
|
-
continue;
|
|
1113
|
-
}
|
|
1114
|
-
const text = row[1] ?? "";
|
|
1115
|
-
const formula = formulaSource(row[2] ?? "");
|
|
1116
|
-
const rich = richGrid[r]?.[1];
|
|
1117
|
-
const planned = { text };
|
|
1118
|
-
if (formula !== "") planned.formula = formula;
|
|
1119
|
-
if (rich) planned.rich = rich;
|
|
1120
|
-
place(cells, at.row, at.col, planned, clashes);
|
|
1121
|
-
}
|
|
1122
|
-
if (skipped > 0) {
|
|
1123
|
-
warnings.push(
|
|
1124
|
-
`Sheet "${sheetName}": ${skipped} loose-cell row(s) had an invalid cell reference and were skipped.`
|
|
1125
|
-
);
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
function planWorkbook(doc, options) {
|
|
1129
|
-
const warnings = [];
|
|
1130
|
-
const groups = groupEntries(collectEntries(doc.children));
|
|
1131
|
-
const used = /* @__PURE__ */ new Set();
|
|
1132
|
-
const sheets = [];
|
|
1133
|
-
groups.forEach((group, index) => {
|
|
1134
|
-
const fallback = `${options.sheetNamePrefix}${index + 1}`;
|
|
1135
|
-
const name = options.sanitize(group.candidate ?? fallback, used, fallback);
|
|
1136
|
-
const cells = /* @__PURE__ */ new Map();
|
|
1137
|
-
const clashes = [];
|
|
1138
|
-
for (const entry of group.entries) {
|
|
1139
|
-
if (entry.role === "values") placeValues(entry, cells, warnings, name, clashes);
|
|
1140
|
-
}
|
|
1141
|
-
for (const entry of group.entries) {
|
|
1142
|
-
if (entry.role === "formulas") placeFormulas(entry, cells, warnings, name);
|
|
1143
|
-
}
|
|
1144
|
-
for (const entry of group.entries) {
|
|
1145
|
-
if (entry.role === "loose") placeLoose(entry, cells, warnings, name, clashes);
|
|
1146
|
-
}
|
|
1147
|
-
if (clashes.length > 0) {
|
|
1148
|
-
const shown = clashes.slice(0, 5).join(", ");
|
|
1149
|
-
const rest = clashes.length > 5 ? ", and more" : "";
|
|
1150
|
-
warnings.push(
|
|
1151
|
-
`Sheet "${name}": ${clashes.length} overlapping cell(s) (${shown}${rest}); the later block won.`
|
|
1152
|
-
);
|
|
1153
|
-
}
|
|
1154
|
-
sheets.push({ name, cells, anchored: group.anchored });
|
|
1155
|
-
});
|
|
1156
|
-
let cellCount = 0;
|
|
1157
|
-
for (const sheet of sheets) cellCount += sheet.cells.size;
|
|
1158
|
-
return { sheets, warnings, cellCount };
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
// src/xlsx/export.ts
|
|
1162
|
-
var NUMERIC_RE = /^-?\d+(\.\d+)?$/;
|
|
1163
|
-
function isSafeNumericCell(value) {
|
|
1164
|
-
if (!NUMERIC_RE.test(value)) return false;
|
|
1165
|
-
const unsigned = value.startsWith("-") ? value.slice(1) : value;
|
|
1166
|
-
const [integer] = unsigned.split(".");
|
|
1167
|
-
if (integer.length > 1 && integer.startsWith("0")) return false;
|
|
1168
|
-
const significantDigits = unsigned.replace(".", "").replace(/^0+/, "");
|
|
1169
|
-
if (significantDigits.length > 15) return false;
|
|
1170
|
-
return Number.isFinite(Number(value));
|
|
1171
|
-
}
|
|
1172
|
-
function cleanSheetName(raw) {
|
|
1173
|
-
return raw.replace(/[[\]:*?/\\]/g, "").trim().slice(0, 31).trim().replace(/^'+|'+$/g, "");
|
|
1174
|
-
}
|
|
1175
|
-
function sanitizeSheetName(candidate, used, fallback) {
|
|
1176
|
-
let base = cleanSheetName(candidate) || cleanSheetName(fallback) || "Sheet";
|
|
1177
|
-
let name = base;
|
|
1178
|
-
let n = 2;
|
|
1179
|
-
while (used.has(name.toLocaleLowerCase("en-US"))) {
|
|
1180
|
-
const suffix = String(n++);
|
|
1181
|
-
base = base.slice(0, 31 - suffix.length);
|
|
1182
|
-
name = `${base}${suffix}`;
|
|
1183
|
-
}
|
|
1184
|
-
used.add(name.toLocaleLowerCase("en-US"));
|
|
1185
|
-
return name;
|
|
1186
|
-
}
|
|
1187
|
-
var ERROR_VALUE_RE = /^#[A-Z0-9_/]+[!?]?$/;
|
|
1188
|
-
function cellXml(cell, ref, inferNumericCells) {
|
|
1189
|
-
const { text, formula } = cell;
|
|
1190
|
-
if (formula !== void 0 && formula !== "") {
|
|
1191
|
-
const f = `<f>${escapeXml(formula)}</f>`;
|
|
1192
|
-
if (text === "") return `<c r="${ref}">${f}</c>`;
|
|
1193
|
-
if (isSafeNumericCell(text)) return `<c r="${ref}">${f}<v>${escapeXml(text)}</v></c>`;
|
|
1194
|
-
if (ERROR_VALUE_RE.test(text)) return `<c r="${ref}" t="e">${f}<v>${escapeXml(text)}</v></c>`;
|
|
1195
|
-
return `<c r="${ref}" t="str">${f}<v>${escapeXml(text)}</v></c>`;
|
|
1196
|
-
}
|
|
1197
|
-
if (inferNumericCells && isSafeNumericCell(text)) {
|
|
1198
|
-
return `<c r="${ref}"><v>${escapeXml(text)}</v></c>`;
|
|
1199
|
-
}
|
|
1200
|
-
if (cell.rich) {
|
|
1201
|
-
return `<c r="${ref}" t="inlineStr"><is>${richRunsXml(cell.rich)}</is></c>`;
|
|
1202
|
-
}
|
|
1203
|
-
return `<c r="${ref}" t="inlineStr"><is><t xml:space="preserve">${escapeXml(text)}</t></is></c>`;
|
|
1204
|
-
}
|
|
1205
|
-
function richRunsXml(nodes) {
|
|
1206
|
-
const runs = [];
|
|
1207
|
-
const emit = (text, vertAlign) => {
|
|
1208
|
-
if (text === "") return;
|
|
1209
|
-
const rPr = vertAlign ? `<rPr><vertAlign val="${vertAlign}"/></rPr>` : "";
|
|
1210
|
-
runs.push(`<r>${rPr}<t xml:space="preserve">${escapeXml(text)}</t></r>`);
|
|
1024
|
+
var XLSX_MIME = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
1025
|
+
async function xlsxToContainer(data, options = {}) {
|
|
1026
|
+
const plan = planDataSidecar(options.sourceName, "workbook.xlsx");
|
|
1027
|
+
const mode = options.sidecar ?? "auto";
|
|
1028
|
+
const spill = mode === "never" ? void 0 : {
|
|
1029
|
+
src: plan.sidecarPath,
|
|
1030
|
+
fileName: plan.fileName,
|
|
1031
|
+
mode,
|
|
1032
|
+
maxInlineRows: options.maxInlineRows ?? 100,
|
|
1033
|
+
maxInlineCells: options.maxInlineCells ?? 2e3,
|
|
1034
|
+
used: false
|
|
1211
1035
|
};
|
|
1212
|
-
const
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
} else {
|
|
1219
|
-
emit(inlineToPlainText(node), vertAlign);
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
};
|
|
1223
|
-
walk(nodes, null);
|
|
1224
|
-
return runs.join("");
|
|
1225
|
-
}
|
|
1226
|
-
function worksheetXml(sheet, inferNumericCells) {
|
|
1227
|
-
const keys = [...sheet.cells.keys()].sort((a, b) => a - b);
|
|
1228
|
-
const rows = [];
|
|
1229
|
-
let maxRow = 0;
|
|
1230
|
-
let maxCol = 0;
|
|
1231
|
-
let i = 0;
|
|
1232
|
-
while (i < keys.length) {
|
|
1233
|
-
const rowIdx = keyRow(keys[i]);
|
|
1234
|
-
let cellsXml = "";
|
|
1235
|
-
while (i < keys.length && keyRow(keys[i]) === rowIdx) {
|
|
1236
|
-
const key = keys[i];
|
|
1237
|
-
const col = keyCol(key);
|
|
1238
|
-
if (col > maxCol) maxCol = col;
|
|
1239
|
-
cellsXml += cellXml(
|
|
1240
|
-
sheet.cells.get(key),
|
|
1241
|
-
`${columnLetter(col)}${rowIdx + 1}`,
|
|
1242
|
-
inferNumericCells
|
|
1243
|
-
);
|
|
1244
|
-
i++;
|
|
1245
|
-
}
|
|
1246
|
-
if (rowIdx > maxRow) maxRow = rowIdx;
|
|
1247
|
-
rows.push(`<row r="${rowIdx + 1}">${cellsXml}</row>`);
|
|
1036
|
+
const markdownDoc = await workbookToMarkdown(data, options, spill);
|
|
1037
|
+
const container = new MemoryContentContainer();
|
|
1038
|
+
await container.writeDocument(stringifyMarkdown(markdownDoc), plan.markdownFilename);
|
|
1039
|
+
if (spill?.used) {
|
|
1040
|
+
const bytes = data instanceof Blob ? await data.arrayBuffer() : data;
|
|
1041
|
+
await container.writeFile(plan.sidecarPath, bytes, XLSX_MIME);
|
|
1248
1042
|
}
|
|
1249
|
-
|
|
1250
|
-
return `${xmlDeclaration()}
|
|
1251
|
-
<worksheet xmlns="${NS_SML}" xmlns:r="${NS_R}"><dimension ref="${dimension}"/><sheetData>${rows.join("")}</sheetData></worksheet>`;
|
|
1252
|
-
}
|
|
1253
|
-
function workbookXml(sheets, hasFormulas) {
|
|
1254
|
-
const sheetEls = sheets.map(
|
|
1255
|
-
(sheet, i) => `<sheet name="${escapeXml(sheet.name)}" sheetId="${i + 1}" r:id="rId${i + 1}"/>`
|
|
1256
|
-
).join("");
|
|
1257
|
-
const calcPr = hasFormulas ? `<calcPr calcId="0" fullCalcOnLoad="1"/>` : "";
|
|
1258
|
-
return `${xmlDeclaration()}
|
|
1259
|
-
<workbook xmlns="${NS_SML}" xmlns:r="${NS_R}"><sheets>${sheetEls}</sheets>${calcPr}</workbook>`;
|
|
1260
|
-
}
|
|
1261
|
-
function stylesXml() {
|
|
1262
|
-
return `${xmlDeclaration()}
|
|
1263
|
-
<styleSheet xmlns="${NS_SML}"><fonts count="1"><font><sz val="11"/><name val="Calibri"/></font></fonts><fills count="1"><fill><patternFill patternType="none"/></fill></fills><borders count="1"><border/></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/></cellXfs></styleSheet>`;
|
|
1264
|
-
}
|
|
1265
|
-
async function markdownDocToXlsx(doc, options = {}) {
|
|
1266
|
-
options.signal?.throwIfAborted();
|
|
1267
|
-
const prefix = cleanSheetName(options.sheetNamePrefix ?? "Sheet") || "Sheet";
|
|
1268
|
-
const maxCells = options.maxCells ?? 1e5;
|
|
1269
|
-
if (!Number.isSafeInteger(maxCells) || maxCells < 0) {
|
|
1270
|
-
throw new RangeError("maxCells must be a non-negative safe integer");
|
|
1271
|
-
}
|
|
1272
|
-
const plan = planWorkbook(doc, { sheetNamePrefix: prefix, sanitize: sanitizeSheetName });
|
|
1273
|
-
for (const warning of plan.warnings) options.onWarning?.(warning);
|
|
1274
|
-
if (plan.cellCount > maxCells) {
|
|
1275
|
-
throw new RangeError(`XLSX export exceeds the ${maxCells}-cell safety limit`);
|
|
1276
|
-
}
|
|
1277
|
-
let sheets = plan.sheets;
|
|
1278
|
-
if (sheets.length === 0) {
|
|
1279
|
-
sheets = [
|
|
1280
|
-
{
|
|
1281
|
-
name: sanitizeSheetName(`${prefix}1`, /* @__PURE__ */ new Set(), "Sheet1"),
|
|
1282
|
-
cells: /* @__PURE__ */ new Map(),
|
|
1283
|
-
anchored: false
|
|
1284
|
-
}
|
|
1285
|
-
];
|
|
1286
|
-
}
|
|
1287
|
-
const anchored = sheets.some((sheet) => sheet.anchored);
|
|
1288
|
-
const inferNumericCells = options.inferNumericCells ?? anchored;
|
|
1289
|
-
const hasFormulas = sheets.some((sheet) => {
|
|
1290
|
-
for (const cell of sheet.cells.values()) if (cell.formula) return true;
|
|
1291
|
-
return false;
|
|
1292
|
-
});
|
|
1293
|
-
const pkg = createPackage();
|
|
1294
|
-
sheets.forEach((sheet, i) => {
|
|
1295
|
-
if ((i & 31) === 0) options.signal?.throwIfAborted();
|
|
1296
|
-
const sheetPath = `xl/worksheets/sheet${i + 1}.xml`;
|
|
1297
|
-
pkg.addPart(sheetPath, worksheetXml(sheet, inferNumericCells), CONTENT_TYPE_XLSX_WORKSHEET);
|
|
1298
|
-
pkg.addRelationship("xl/workbook.xml", {
|
|
1299
|
-
id: `rId${i + 1}`,
|
|
1300
|
-
type: REL_WORKSHEET,
|
|
1301
|
-
target: `worksheets/sheet${i + 1}.xml`
|
|
1302
|
-
});
|
|
1303
|
-
});
|
|
1304
|
-
pkg.addPart("xl/styles.xml", stylesXml(), CONTENT_TYPE_XLSX_STYLES);
|
|
1305
|
-
pkg.addRelationship("xl/workbook.xml", {
|
|
1306
|
-
id: `rId${sheets.length + 1}`,
|
|
1307
|
-
type: REL_STYLES,
|
|
1308
|
-
target: "styles.xml"
|
|
1309
|
-
});
|
|
1310
|
-
pkg.addPart("xl/workbook.xml", workbookXml(sheets, hasFormulas), CONTENT_TYPE_XLSX_WORKBOOK);
|
|
1311
|
-
pkg.addRelationship("", {
|
|
1312
|
-
id: "rId1",
|
|
1313
|
-
type: REL_OFFICE_DOCUMENT,
|
|
1314
|
-
target: "xl/workbook.xml"
|
|
1315
|
-
});
|
|
1316
|
-
if (options.title || options.author) {
|
|
1317
|
-
pkg.setCoreProperties({
|
|
1318
|
-
title: options.title,
|
|
1319
|
-
creator: options.author,
|
|
1320
|
-
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1321
|
-
modified: (/* @__PURE__ */ new Date()).toISOString()
|
|
1322
|
-
});
|
|
1323
|
-
}
|
|
1324
|
-
return pkg.toArrayBuffer();
|
|
1325
|
-
}
|
|
1326
|
-
async function docToXlsx(doc, options) {
|
|
1327
|
-
return markdownDocToXlsx(docToMarkdown(doc), options);
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
|
-
// src/xlsx/index.ts
|
|
1331
|
-
async function xlsxToDoc(data, options) {
|
|
1332
|
-
return markdownToDoc(await xlsxToMarkdownDoc(data, options));
|
|
1043
|
+
return container;
|
|
1333
1044
|
}
|
|
1334
1045
|
|
|
1335
1046
|
export {
|
|
1047
|
+
MAX_COL_INDEX,
|
|
1048
|
+
MAX_ROW_INDEX,
|
|
1049
|
+
columnLetter,
|
|
1050
|
+
columnIndexFromLetters,
|
|
1051
|
+
colIndex,
|
|
1052
|
+
parseCellRef,
|
|
1053
|
+
formatCellRef,
|
|
1054
|
+
columnLetter2,
|
|
1336
1055
|
gridToTables,
|
|
1056
|
+
listSheetParts,
|
|
1057
|
+
readCellStyles,
|
|
1058
|
+
numberFormatKind,
|
|
1337
1059
|
xlsxToTables,
|
|
1060
|
+
xlsxToCellGrids,
|
|
1338
1061
|
xlsxToMarkdownDoc,
|
|
1339
|
-
|
|
1340
|
-
docToXlsx,
|
|
1341
|
-
xlsxToDoc
|
|
1062
|
+
xlsxToContainer
|
|
1342
1063
|
};
|