@office-open/xlsx 0.8.0 → 0.9.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.
@@ -0,0 +1,42 @@
1
+ import { Ct as VolTypeOptions, D as WorksheetOptions, Et as WorkbookProtectionOptions, Tt as WebPublishingOptions, Y as ExternalLinkOptions, bt as FileRecoveryPrOptions, dt as DxfOptions, rt as ChartsheetOptions, vt as CorePropertiesOptions, wt as WebPublishObjectOptions, xt as FileSharingOptions, yt as CustomWorkbookViewOptions } from "./worksheet-BbJpRvw2.mjs";
2
+ import { Element } from "@office-open/xml";
3
+ import { DataType, ParsedArchive, parseArchive } from "@office-open/core";
4
+
5
+ //#region src/shared/file.d.ts
6
+ interface WorkbookOptions extends CorePropertiesOptions {
7
+ worksheets?: WorksheetOptions[];
8
+ chartsheets?: ChartsheetOptions[];
9
+ dxfs?: DxfOptions[];
10
+ workbookProtection?: WorkbookProtectionOptions;
11
+ externalLinks?: ExternalLinkOptions[];
12
+ customWorkbookViews?: CustomWorkbookViewOptions[];
13
+ fileRecoveryPr?: FileRecoveryPrOptions;
14
+ functionGroups?: string[];
15
+ webPublishing?: WebPublishingOptions;
16
+ fileSharing?: FileSharingOptions;
17
+ volTypes?: VolTypeOptions[];
18
+ webPublishObjects?: WebPublishObjectOptions[];
19
+ }
20
+ //#endregion
21
+ //#region src/parse.d.ts
22
+ interface XlsxPartRefs {
23
+ worksheets: string[];
24
+ charts: string[];
25
+ media: string[];
26
+ drawings: string[];
27
+ }
28
+ interface XlsxDocument {
29
+ doc: ParsedArchive;
30
+ workbook?: Element;
31
+ worksheets: string[];
32
+ styles?: Element;
33
+ sharedStrings?: Element;
34
+ partRefs: XlsxPartRefs;
35
+ coreProps?: string;
36
+ appProps?: string;
37
+ }
38
+ declare function parseXlsx(data: DataType): XlsxDocument;
39
+ declare function parseWorkbook(data: DataType): WorkbookOptions;
40
+ //#endregion
41
+ export { parseXlsx as a, parseWorkbook as i, XlsxPartRefs as n, parseArchive as r, XlsxDocument as t };
42
+ //# sourceMappingURL=parse-CZQQuAH5.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-CZQQuAH5.d.mts","names":[],"sources":["../src/shared/file.ts","../src/parse.ts"],"mappings":";;;;;UAkBiB,eAAA,SAAwB,qBAAA;EACvC,UAAA,GAAa,gBAAA;EAEb,WAAA,GAAc,iBAAA;EAEd,IAAA,GAAO,UAAA;EAEP,kBAAA,GAAqB,yBAAA;EAErB,aAAA,GAAgB,mBAAA;EAEhB,mBAAA,GAFmC,yBAAA;EAInC,cAAA,GAAiB,qBAAA;EAEjB,cAAA;EAEA,aAAA,GAAgB,oBAAA;EAEhB,WAAA,GAAc,kBAAA;EAEd,QAAA,GAFgC,cAAA;EAIhC,iBAAA,GAFmD,uBAAA;AAAA;;;UClBpC,YAAA;EACf,UAAA;EACA,MAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,UAGe,YAAA;EACf,GAAA,EAAK,aAAA;EAEL,QAAA,GAAW,OAAA;EAEX,UAAA;EAEA,MAAA,GAAS,OAAA;EAET,aAAA,GAAgB,OAAA;EAChB,QAAA,EAAU,YAAA;EAEV,SAAA;EAEA,QAAA;AAAA;AAAA,iBAcc,SAAA,CAAU,IAAA,EAAM,QAAA,GAAW,YAAY;AAAA,iBA4FvC,aAAA,CAAc,IAAA,EAAM,QAAA,GAAW,eAAe"}
@@ -0,0 +1,2 @@
1
+ import { a as parseXlsx, i as parseWorkbook, n as XlsxPartRefs, r as parseArchive, t as XlsxDocument } from "./parse-CZQQuAH5.mjs";
2
+ export { XlsxDocument, XlsxPartRefs, parseArchive, parseWorkbook, parseXlsx };
package/dist/parse.mjs ADDED
@@ -0,0 +1,131 @@
1
+ import { i as worksheetDesc, t as XlsxReadContext } from "./context-a1lzscdy.mjs";
2
+ import { attr, findChild, textOf } from "@office-open/xml";
3
+ import { parseArchive, parseCorePropsElement, toUint8Array } from "@office-open/core";
4
+ //#region src/parse.ts
5
+ /**
6
+ * XLSX parsing — parse .xlsx files into structured data.
7
+ *
8
+ * @module
9
+ */
10
+ function sortByNumber(paths) {
11
+ return paths.sort((a, b) => {
12
+ return parseInt(a.match(/(\d+)/)?.[1] ?? "0", 10) - parseInt(b.match(/(\d+)/)?.[1] ?? "0", 10);
13
+ });
14
+ }
15
+ /**
16
+ * Parse raw .xlsx data into a low-level XlsxDocument.
17
+ */
18
+ function parseXlsx(data) {
19
+ const doc = parseArchive(toUint8Array(data));
20
+ const workbook = doc.get("xl/workbook.xml");
21
+ const styles = doc.get("xl/styles.xml");
22
+ const sharedStrings = doc.get("xl/sharedStrings.xml");
23
+ const worksheets = [];
24
+ const charts = [];
25
+ const drawings = [];
26
+ const media = [];
27
+ const wbRels = doc.get("xl/_rels/workbook.xml.rels");
28
+ if (wbRels) for (const child of wbRels.elements ?? []) {
29
+ if (child.name !== "Relationship") continue;
30
+ const type = attr(child, "Type") ?? "";
31
+ const target = attr(child, "Target") ?? "";
32
+ if (!target) continue;
33
+ if (type.includes("/worksheet")) worksheets.push(target.startsWith("/") ? target.slice(1) : `xl/${target}`);
34
+ }
35
+ sortByNumber(worksheets);
36
+ drawings.push(...doc.keys("xl/drawings/").filter((k) => k.endsWith(".xml")));
37
+ charts.push(...doc.keys("xl/charts/").filter((k) => k.endsWith(".xml")));
38
+ media.push(...doc.keys("xl/media/"));
39
+ sortByNumber(drawings);
40
+ sortByNumber(charts);
41
+ let coreProps;
42
+ let appProps;
43
+ const rootRels = doc.get("_rels/.rels");
44
+ if (rootRels) for (const child of rootRels.elements ?? []) {
45
+ if (child.name !== "Relationship") continue;
46
+ const type = attr(child, "Type") ?? "";
47
+ const target = attr(child, "Target") ?? "";
48
+ if (type.includes("/core-properties")) coreProps = target;
49
+ else if (type.includes("/extended-properties")) appProps = target;
50
+ }
51
+ return {
52
+ doc,
53
+ workbook,
54
+ worksheets,
55
+ styles,
56
+ sharedStrings,
57
+ partRefs: {
58
+ worksheets,
59
+ charts,
60
+ media,
61
+ drawings
62
+ },
63
+ coreProps,
64
+ appProps
65
+ };
66
+ }
67
+ function parseSharedStrings(el) {
68
+ if (!el) return [];
69
+ const strings = [];
70
+ for (const si of el.elements ?? []) {
71
+ if (si.name !== "si") continue;
72
+ const t = findChild(si, "t");
73
+ if (t) strings.push(textOf(t) ?? "");
74
+ else {
75
+ const parts = [];
76
+ for (const r of si.elements ?? []) {
77
+ if (r.name !== "r") continue;
78
+ const rt = findChild(r, "t");
79
+ if (rt) parts.push(textOf(rt) ?? "");
80
+ }
81
+ strings.push(parts.join(""));
82
+ }
83
+ }
84
+ return strings;
85
+ }
86
+ /**
87
+ * Parse a .xlsx file and convert it into WorkbookOptions.
88
+ *
89
+ * The returned options can be passed to `new Workbook(parsed)`.
90
+ */
91
+ function parseWorkbook(data) {
92
+ const xlsx = parseXlsx(data);
93
+ const opts = {};
94
+ if (xlsx.coreProps) {
95
+ const corePropsEl = xlsx.doc.get(xlsx.coreProps);
96
+ if (corePropsEl) {
97
+ const cp = parseCorePropsElement(corePropsEl);
98
+ if (cp.title) opts.title = cp.title;
99
+ if (cp.subject) opts.subject = cp.subject;
100
+ if (cp.creator) opts.creator = cp.creator;
101
+ if (cp.keywords) opts.keywords = cp.keywords;
102
+ if (cp.description) opts.description = cp.description;
103
+ if (cp.lastModifiedBy) opts.lastModifiedBy = cp.lastModifiedBy;
104
+ if (cp.revision) opts.revision = parseInt(cp.revision, 10);
105
+ }
106
+ }
107
+ const readContext = new XlsxReadContext(xlsx, parseSharedStrings(xlsx.sharedStrings));
108
+ const sheetNames = [];
109
+ if (xlsx.workbook) {
110
+ const sheetsEl = findChild(xlsx.workbook, "sheets");
111
+ if (sheetsEl) for (const s of sheetsEl.elements ?? []) {
112
+ if (s.name !== "sheet") continue;
113
+ sheetNames.push(attr(s, "name") ?? "");
114
+ }
115
+ }
116
+ const worksheets = [];
117
+ for (let i = 0; i < xlsx.worksheets.length; i++) {
118
+ const wsPath = xlsx.worksheets[i];
119
+ const wsEl = xlsx.doc.get(wsPath);
120
+ if (!wsEl) continue;
121
+ const wsOpts = worksheetDesc.parse(wsEl, readContext);
122
+ if (sheetNames[i]) wsOpts.name = sheetNames[i];
123
+ worksheets.push(wsOpts);
124
+ }
125
+ opts.worksheets = worksheets;
126
+ return opts;
127
+ }
128
+ //#endregion
129
+ export { parseArchive, parseWorkbook, parseXlsx };
130
+
131
+ //# sourceMappingURL=parse.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.mjs","names":[],"sources":["../src/parse.ts"],"sourcesContent":["/**\n * XLSX parsing — parse .xlsx files into structured data.\n *\n * @module\n */\nimport { parseArchive, parseCorePropsElement } from \"@office-open/core\";\nimport type { ParsedArchive } from \"@office-open/core\";\nimport { toUint8Array } from \"@office-open/core\";\nimport type { DataType } from \"@office-open/core\";\nimport type { Element } from \"@office-open/xml\";\nimport { attr, findChild, textOf } from \"@office-open/xml\";\nimport { worksheetDesc } from \"@parts/worksheet\";\nimport type { WorksheetOptions } from \"@parts/worksheet\";\nimport type { WorkbookOptions } from \"@shared/file\";\n\nimport { XlsxReadContext } from \"./context\";\n\nexport { parseArchive };\n\n// ── Low-level parse result ──\n\nexport interface XlsxPartRefs {\n worksheets: string[];\n charts: string[];\n media: string[];\n drawings: string[];\n}\n\nexport interface XlsxDocument {\n doc: ParsedArchive;\n /** xl/workbook.xml root element */\n workbook?: Element;\n /** Worksheet paths (xl/worksheets/sheet{n}.xml) */\n worksheets: string[];\n /** xl/styles.xml root element */\n styles?: Element;\n /** xl/sharedStrings.xml root element */\n sharedStrings?: Element;\n partRefs: XlsxPartRefs;\n /** docProps/core.xml path */\n coreProps?: string;\n /** docProps/app.xml path */\n appProps?: string;\n}\n\nfunction sortByNumber(paths: string[]): string[] {\n return paths.sort((a, b) => {\n const numA = parseInt(a.match(/(\\d+)/)?.[1] ?? \"0\", 10);\n const numB = parseInt(b.match(/(\\d+)/)?.[1] ?? \"0\", 10);\n return numA - numB;\n });\n}\n\n/**\n * Parse raw .xlsx data into a low-level XlsxDocument.\n */\nexport function parseXlsx(data: DataType): XlsxDocument {\n const uint8 = toUint8Array(data);\n const doc = parseArchive(uint8);\n\n const workbook = doc.get(\"xl/workbook.xml\");\n const styles = doc.get(\"xl/styles.xml\");\n const sharedStrings = doc.get(\"xl/sharedStrings.xml\");\n\n // Resolve worksheet paths from workbook rels\n const worksheets: string[] = [];\n const charts: string[] = [];\n const drawings: string[] = [];\n const media: string[] = [];\n\n const wbRels = doc.get(\"xl/_rels/workbook.xml.rels\");\n if (wbRels) {\n for (const child of wbRels.elements ?? []) {\n if (child.name !== \"Relationship\") continue;\n const type = attr(child, \"Type\") ?? \"\";\n const target = attr(child, \"Target\") ?? \"\";\n if (!target) continue;\n\n if (type.includes(\"/worksheet\")) {\n worksheets.push(target.startsWith(\"/\") ? target.slice(1) : `xl/${target}`);\n }\n }\n }\n sortByNumber(worksheets);\n\n // Scan for drawings, charts, media\n drawings.push(...doc.keys(\"xl/drawings/\").filter((k) => k.endsWith(\".xml\")));\n charts.push(...doc.keys(\"xl/charts/\").filter((k) => k.endsWith(\".xml\")));\n media.push(...doc.keys(\"xl/media/\"));\n sortByNumber(drawings);\n sortByNumber(charts);\n\n // Root rels → core/app props\n let coreProps: string | undefined;\n let appProps: string | undefined;\n const rootRels = doc.get(\"_rels/.rels\");\n if (rootRels) {\n for (const child of rootRels.elements ?? []) {\n if (child.name !== \"Relationship\") continue;\n const type = attr(child, \"Type\") ?? \"\";\n const target = attr(child, \"Target\") ?? \"\";\n if (type.includes(\"/core-properties\")) coreProps = target;\n else if (type.includes(\"/extended-properties\")) appProps = target;\n }\n }\n\n return {\n doc,\n workbook,\n worksheets,\n styles,\n sharedStrings,\n partRefs: { worksheets, charts, media, drawings },\n coreProps,\n appProps,\n };\n}\n\n// ── Shared strings helper ──\n\nfunction parseSharedStrings(el: Element | undefined): string[] {\n if (!el) return [];\n const strings: string[] = [];\n for (const si of el.elements ?? []) {\n if (si.name !== \"si\") continue;\n // Handle both <si><t>text</t></si> and <si><r><t>text</t></r>...</si>\n const t = findChild(si, \"t\");\n if (t) {\n strings.push(textOf(t) ?? \"\");\n } else {\n // Rich text: concatenate all <r><t> segments\n const parts: string[] = [];\n for (const r of si.elements ?? []) {\n if (r.name !== \"r\") continue;\n const rt = findChild(r, \"t\");\n if (rt) parts.push(textOf(rt) ?? \"\");\n }\n strings.push(parts.join(\"\"));\n }\n }\n return strings;\n}\n\n/**\n * Parse a .xlsx file and convert it into WorkbookOptions.\n *\n * The returned options can be passed to `new Workbook(parsed)`.\n */\nexport function parseWorkbook(data: DataType): WorkbookOptions {\n const xlsx = parseXlsx(data);\n\n const opts: Record<string, unknown> = {};\n\n // Core properties\n if (xlsx.coreProps) {\n const corePropsEl = xlsx.doc.get(xlsx.coreProps);\n if (corePropsEl) {\n const cp = parseCorePropsElement(corePropsEl);\n if (cp.title) opts.title = cp.title;\n if (cp.subject) opts.subject = cp.subject;\n if (cp.creator) opts.creator = cp.creator;\n if (cp.keywords) opts.keywords = cp.keywords;\n if (cp.description) opts.description = cp.description;\n if (cp.lastModifiedBy) opts.lastModifiedBy = cp.lastModifiedBy;\n if (cp.revision) opts.revision = parseInt(cp.revision, 10);\n }\n }\n\n // Shared strings\n const strings = parseSharedStrings(xlsx.sharedStrings);\n\n // Create read context for descriptor pipeline\n const readContext = new XlsxReadContext(xlsx, strings);\n\n // Sheet names from workbook\n const sheetNames: string[] = [];\n if (xlsx.workbook) {\n const sheetsEl = findChild(xlsx.workbook, \"sheets\");\n if (sheetsEl) {\n for (const s of sheetsEl.elements ?? []) {\n if (s.name !== \"sheet\") continue;\n sheetNames.push(attr(s, \"name\") ?? \"\");\n }\n }\n }\n\n // Parse worksheets using descriptor pipeline\n const worksheets: WorksheetOptions[] = [];\n for (let i = 0; i < xlsx.worksheets.length; i++) {\n const wsPath = xlsx.worksheets[i];\n const wsEl = xlsx.doc.get(wsPath);\n if (!wsEl) continue;\n\n const wsOpts = worksheetDesc.parse(wsEl, readContext) as Record<string, unknown>;\n if (sheetNames[i]) wsOpts.name = sheetNames[i];\n worksheets.push(wsOpts as WorksheetOptions);\n }\n\n opts.worksheets = worksheets;\n return opts as WorkbookOptions;\n}\n"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { OutputByType, OutputType } from "@office-open/core";
2
+ import { Buffer } from "\u0000polyfill-node.buffer";
3
+
4
+ //#region src/patch.d.ts
5
+ type InputDataType = Buffer | string | number[] | Uint8Array | ArrayBuffer | Blob;
6
+ type PatchDocumentOutputType = OutputType;
7
+ declare const PatchType: {
8
+ readonly CELL: "cell";
9
+ };
10
+ interface CellPatch {
11
+ value: string | number | boolean | Date;
12
+ }
13
+ type IPatch = CellPatch;
14
+ interface PatchWorkbookOptions<T extends PatchDocumentOutputType = PatchDocumentOutputType> {
15
+ outputType: T;
16
+ data: InputDataType;
17
+ patches: Readonly<Record<string, IPatch>>;
18
+ placeholderDelimiters?: Readonly<{
19
+ start: string;
20
+ end: string;
21
+ }>;
22
+ }
23
+ declare const patchWorkbook: <T extends PatchDocumentOutputType = PatchDocumentOutputType>({
24
+ outputType,
25
+ data,
26
+ patches,
27
+ placeholderDelimiters
28
+ }: PatchWorkbookOptions<T>) => Promise<OutputByType[T]>;
29
+ //#endregion
30
+ export { CellPatch, IPatch, InputDataType, PatchDocumentOutputType, PatchType, PatchWorkbookOptions, patchWorkbook };
31
+ //# sourceMappingURL=patch.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.d.mts","names":[],"sources":["../src/patch.ts"],"mappings":";;;;KAkBY,aAAA,GAAgB,MAAA,uBAA6B,UAAA,GAAa,WAAA,GAAc,IAAA;AAAA,KAExE,uBAAA,GAA0B,UAAU;AAAA,cAEnC,SAAA;EAAA,SAEH,IAAA;AAAA;AAAA,UAEO,SAAA;EAEf,KAAA,8BAAmC,IAAI;AAAA;AAAA,KAG7B,MAAA,GAAS,SAAS;AAAA,UAEb,oBAAA,WAA+B,uBAAA,GAA0B,uBAAA;EACxE,UAAA,EAAY,CAAA;EACZ,IAAA,EAAM,aAAA;EACN,OAAA,EAAS,QAAA,CAAS,MAAA,SAAe,MAAA;EACjC,qBAAA,GAAwB,QAAA;IACtB,KAAA;IACA,GAAA;EAAA;AAAA;AAAA,cAWS,aAAA,aAAiC,uBAAA,GAA0B,uBAAA;EAAyB,UAAA;EAAA,IAAA;EAAA,OAAA;EAAA;AAAA,GAK9F,oBAAA,CAAqB,CAAA,MAAK,OAAA,CAAQ,YAAA,CAAa,CAAA"}
package/dist/patch.mjs ADDED
@@ -0,0 +1,101 @@
1
+ import { js2xml } from "@office-open/xml";
2
+ import { OoxmlMimeType, strFromU8, toJson, toUint8Array, unzipSync, zipAndConvert } from "@office-open/core";
3
+ //#region src/patch.ts
4
+ /**
5
+ * XLSX patching — replace placeholders in existing .xlsx files.
6
+ *
7
+ * Unlike DOCX/PPTX (paragraph-based), XLSX patching targets cell values:
8
+ * - Replaces placeholders in the shared strings table (most common)
9
+ * - Replaces placeholders in inline strings within worksheet cells
10
+ *
11
+ * @module
12
+ */
13
+ /** Reusable TextEncoder (stateless, safe to share). */
14
+ const encoder = new TextEncoder();
15
+ const PatchType = { CELL: "cell" };
16
+ /**
17
+ * Patch an existing .xlsx workbook by replacing placeholder text in cells.
18
+ *
19
+ * Placeholders are matched in shared strings and inline strings.
20
+ * For string replacements, the shared string value is updated in-place.
21
+ * For non-string replacements, the cell type and value are updated.
22
+ */
23
+ const patchWorkbook = async ({ outputType, data, patches, placeholderDelimiters = {
24
+ start: "{{",
25
+ end: "}}"
26
+ } }) => {
27
+ const zipContent = unzipSync(toUint8Array(data));
28
+ const xmlMap = /* @__PURE__ */ new Map();
29
+ const binaryMap = /* @__PURE__ */ new Map();
30
+ for (const [key, value] of Object.entries(zipContent)) if (key.endsWith(".xml") || key.endsWith(".rels")) xmlMap.set(key, toJson(strFromU8(value)));
31
+ else binaryMap.set(key, value);
32
+ const { start, end } = placeholderDelimiters;
33
+ const patchMap = /* @__PURE__ */ new Map();
34
+ for (const [key, patch] of Object.entries(patches)) patchMap.set(`${start}${key}${end}`, patch);
35
+ const sst = xmlMap.get("xl/sharedStrings.xml");
36
+ if (sst) patchSharedStrings(sst, patchMap);
37
+ const worksheetKeys = Object.keys(zipContent).filter((k) => k.startsWith("xl/worksheets/sheet") && k.endsWith(".xml"));
38
+ for (const wsKey of worksheetKeys) {
39
+ const ws = xmlMap.get(wsKey);
40
+ if (ws) patchWorksheetInlineStrings(ws, patchMap);
41
+ }
42
+ const files = {};
43
+ for (const [key, value] of xmlMap) files[key] = encoder.encode(js2xml(value));
44
+ for (const [key, value] of binaryMap) files[key] = value;
45
+ return await zipAndConvert(files, outputType, OoxmlMimeType.XLSX);
46
+ };
47
+ function patchSharedStrings(sst, patchMap) {
48
+ const root = sst.name ? sst : sst.elements?.[0];
49
+ if (!root) return;
50
+ for (const si of root.elements ?? []) {
51
+ if (si.name !== "si") continue;
52
+ const t = findLocalChild(si, "t");
53
+ if (t) patchTextElement(t, patchMap);
54
+ else for (const r of si.elements ?? []) {
55
+ if (r.name !== "r") continue;
56
+ const rt = findLocalChild(r, "t");
57
+ if (rt) patchTextElement(rt, patchMap);
58
+ }
59
+ }
60
+ }
61
+ function patchWorksheetInlineStrings(ws, patchMap) {
62
+ const root = ws.name ? ws : ws.elements?.[0];
63
+ if (!root) return;
64
+ const sheetData = findLocalChild(root, "sheetData");
65
+ if (!sheetData) return;
66
+ for (const row of sheetData.elements ?? []) {
67
+ if (localName(row) !== "row") continue;
68
+ for (const cell of row.elements ?? []) {
69
+ if (localName(cell) !== "c") continue;
70
+ const isEl = findLocalChild(cell, "is");
71
+ if (isEl) {
72
+ const t = findLocalChild(isEl, "t");
73
+ if (t) patchTextElement(t, patchMap);
74
+ }
75
+ }
76
+ }
77
+ }
78
+ function patchTextElement(tEl, patchMap) {
79
+ const text = tEl.elements?.[0]?.text;
80
+ if (typeof text !== "string") return;
81
+ for (const [placeholder, patch] of patchMap) if (text.includes(placeholder)) {
82
+ const newValue = String(patch.value);
83
+ const replaced = text.replace(placeholder, newValue);
84
+ if (tEl.elements) tEl.elements[0] = {
85
+ ...tEl.elements[0],
86
+ text: replaced
87
+ };
88
+ }
89
+ }
90
+ function localName(el) {
91
+ const name = el.name ?? "";
92
+ const colonIdx = name.indexOf(":");
93
+ return colonIdx >= 0 ? name.slice(colonIdx + 1) : name;
94
+ }
95
+ function findLocalChild(parent, name) {
96
+ return (parent.elements ?? []).find((el) => localName(el) === name);
97
+ }
98
+ //#endregion
99
+ export { PatchType, patchWorkbook };
100
+
101
+ //# sourceMappingURL=patch.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.mjs","names":[],"sources":["../src/patch.ts"],"sourcesContent":["/**\n * XLSX patching — replace placeholders in existing .xlsx files.\n *\n * Unlike DOCX/PPTX (paragraph-based), XLSX patching targets cell values:\n * - Replaces placeholders in the shared strings table (most common)\n * - Replaces placeholders in inline strings within worksheet cells\n *\n * @module\n */\nimport { OoxmlMimeType, unzipSync, zipAndConvert, strFromU8, toJson } from \"@office-open/core\";\nimport type { OutputByType, OutputType } from \"@office-open/core\";\nimport { toUint8Array } from \"@office-open/core\";\nimport { js2xml } from \"@office-open/xml\";\nimport type { Element } from \"@office-open/xml\";\n\n/** Reusable TextEncoder (stateless, safe to share). */\nconst encoder = new TextEncoder();\n\nexport type InputDataType = Buffer | string | number[] | Uint8Array | ArrayBuffer | Blob;\n\nexport type PatchDocumentOutputType = OutputType;\n\nexport const PatchType = {\n CELL: \"cell\",\n} as const;\n\nexport interface CellPatch {\n /** Replacement value (string, number, boolean, or Date) */\n value: string | number | boolean | Date;\n}\n\nexport type IPatch = CellPatch;\n\nexport interface PatchWorkbookOptions<T extends PatchDocumentOutputType = PatchDocumentOutputType> {\n outputType: T;\n data: InputDataType;\n patches: Readonly<Record<string, IPatch>>;\n placeholderDelimiters?: Readonly<{\n start: string;\n end: string;\n }>;\n}\n\n/**\n * Patch an existing .xlsx workbook by replacing placeholder text in cells.\n *\n * Placeholders are matched in shared strings and inline strings.\n * For string replacements, the shared string value is updated in-place.\n * For non-string replacements, the cell type and value are updated.\n */\nexport const patchWorkbook = async <T extends PatchDocumentOutputType = PatchDocumentOutputType>({\n outputType,\n data,\n patches,\n placeholderDelimiters = { start: \"{{\", end: \"}}\" } as const,\n}: PatchWorkbookOptions<T>): Promise<OutputByType[T]> => {\n const zipContent = unzipSync(toUint8Array(data));\n\n const xmlMap = new Map<string, Element>();\n const binaryMap = new Map<string, Uint8Array>();\n\n // Separate XML files from binary files\n for (const [key, value] of Object.entries(zipContent)) {\n if (key.endsWith(\".xml\") || key.endsWith(\".rels\")) {\n xmlMap.set(key, toJson(strFromU8(value)));\n } else {\n binaryMap.set(key, value);\n }\n }\n\n const { start, end } = placeholderDelimiters;\n\n // Build placeholder → patch map\n const patchMap = new Map<string, IPatch>();\n for (const [key, patch] of Object.entries(patches)) {\n patchMap.set(`${start}${key}${end}`, patch);\n }\n\n // 1. Patch shared strings\n const sst = xmlMap.get(\"xl/sharedStrings.xml\");\n if (sst) {\n patchSharedStrings(sst, patchMap);\n }\n\n // 2. Patch inline strings in worksheets\n const worksheetKeys = Object.keys(zipContent).filter(\n (k) => k.startsWith(\"xl/worksheets/sheet\") && k.endsWith(\".xml\"),\n );\n for (const wsKey of worksheetKeys) {\n const ws = xmlMap.get(wsKey);\n if (ws) patchWorksheetInlineStrings(ws, patchMap);\n }\n\n // Rebuild ZIP\n const files: Record<string, Uint8Array> = {};\n for (const [key, value] of xmlMap) {\n files[key] = encoder.encode(js2xml(value));\n }\n for (const [key, value] of binaryMap) {\n files[key] = value;\n }\n\n return await zipAndConvert(files, outputType, OoxmlMimeType.XLSX);\n};\n\nfunction patchSharedStrings(sst: Element, patchMap: Map<string, IPatch>): void {\n // toJson returns {elements: [{name: \"sst\", ...}]} — unwrap to actual root\n const root = sst.name ? sst : sst.elements?.[0];\n if (!root) return;\n\n for (const si of root.elements ?? []) {\n if (si.name !== \"si\") continue;\n\n // Simple: <si><t>text</t></si>\n const t = findLocalChild(si, \"t\");\n if (t) {\n patchTextElement(t, patchMap);\n } else {\n // Rich text: <si><r><t>text</t></r>...</si>\n for (const r of si.elements ?? []) {\n if (r.name !== \"r\") continue;\n const rt = findLocalChild(r, \"t\");\n if (rt) patchTextElement(rt, patchMap);\n }\n }\n }\n}\n\nfunction patchWorksheetInlineStrings(ws: Element, patchMap: Map<string, IPatch>): void {\n const root = ws.name ? ws : ws.elements?.[0];\n if (!root) return;\n\n const sheetData = findLocalChild(root, \"sheetData\");\n if (!sheetData) return;\n\n for (const row of sheetData.elements ?? []) {\n if (localName(row) !== \"row\") continue;\n for (const cell of row.elements ?? []) {\n if (localName(cell) !== \"c\") continue;\n\n // Check inline strings: <c t=\"inlineStr\"><is><t>text</t></is></c>\n const isEl = findLocalChild(cell, \"is\");\n if (isEl) {\n const t = findLocalChild(isEl, \"t\");\n if (t) patchTextElement(t, patchMap);\n }\n }\n }\n}\n\nfunction patchTextElement(tEl: Element, patchMap: Map<string, IPatch>): void {\n const text = tEl.elements?.[0]?.text;\n if (typeof text !== \"string\") return;\n\n for (const [placeholder, patch] of patchMap) {\n if (text.includes(placeholder)) {\n const newValue = String(patch.value);\n const replaced = text.replace(placeholder, newValue);\n if (tEl.elements) {\n tEl.elements[0] = { ...tEl.elements[0], text: replaced };\n }\n }\n }\n}\n\nfunction localName(el: Element): string {\n const name = el.name ?? \"\";\n const colonIdx = name.indexOf(\":\");\n return colonIdx >= 0 ? name.slice(colonIdx + 1) : name;\n}\n\nfunction findLocalChild(parent: Element, name: string): Element | undefined {\n return (parent.elements ?? []).find((el) => localName(el) === name);\n}\n"],"mappings":"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"}