@bendyline/squisq-formats 1.4.0 → 1.4.2

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.
Files changed (83) hide show
  1. package/README.md +180 -43
  2. package/dist/{chunk-CRTC6DEX.js → chunk-46FMDOWZ.js} +2 -2
  3. package/dist/{chunk-ON232HJR.js → chunk-5LSSWZPU.js} +2 -2
  4. package/dist/{chunk-APSIAEXL.js → chunk-D7IFPWNZ.js} +87 -43
  5. package/dist/chunk-D7IFPWNZ.js.map +1 -0
  6. package/dist/chunk-DQAZR57U.js +33 -0
  7. package/dist/chunk-DQAZR57U.js.map +1 -0
  8. package/dist/chunk-EHLGMSTE.js +273 -0
  9. package/dist/chunk-EHLGMSTE.js.map +1 -0
  10. package/dist/{chunk-A3FHLTY5.js → chunk-MJGRI6XR.js} +6 -19
  11. package/dist/chunk-MJGRI6XR.js.map +1 -0
  12. package/dist/{chunk-JBNVE2GV.js → chunk-RFAPOKHJ.js} +9 -2
  13. package/dist/chunk-RFAPOKHJ.js.map +1 -0
  14. package/dist/chunk-RTS5XBZ7.js +526 -0
  15. package/dist/chunk-RTS5XBZ7.js.map +1 -0
  16. package/dist/{chunk-FMOIGA5T.js → chunk-SHLPSADL.js} +178 -5
  17. package/dist/chunk-SHLPSADL.js.map +1 -0
  18. package/dist/chunk-SSUPBUF5.js +15 -0
  19. package/dist/chunk-SSUPBUF5.js.map +1 -0
  20. package/dist/{chunk-BJQIJ4S4.js → chunk-WWBIKRNP.js} +17 -41
  21. package/dist/chunk-WWBIKRNP.js.map +1 -0
  22. package/dist/{chunk-I2GFTEOV.js → chunk-YESS7VY3.js} +77 -15
  23. package/dist/{chunk-I2GFTEOV.js.map → chunk-YESS7VY3.js.map} +1 -1
  24. package/dist/csv/index.d.ts +14 -1
  25. package/dist/csv/index.js +1 -1
  26. package/dist/docx/index.js +4 -3
  27. package/dist/epub/index.js +3 -2
  28. package/dist/html/index.d.ts +3 -0
  29. package/dist/html/index.js +3 -2
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.js +26 -11
  32. package/dist/ooxml/index.d.ts +5 -1
  33. package/dist/ooxml/index.js +16 -12
  34. package/dist/pdf/index.js +2 -1
  35. package/dist/pptx/index.d.ts +18 -3
  36. package/dist/pptx/index.js +7 -4
  37. package/dist/registry/index.d.ts +167 -0
  38. package/dist/registry/index.js +17 -0
  39. package/dist/registry/index.js.map +1 -0
  40. package/dist/xlsx/index.d.ts +44 -14
  41. package/dist/xlsx/index.js +4 -2
  42. package/package.json +7 -2
  43. package/src/__tests__/convert.test.ts +186 -0
  44. package/src/__tests__/csvImport.test.ts +34 -1
  45. package/src/__tests__/formatRegistry.test.ts +174 -0
  46. package/src/__tests__/lossyWarnings.test.ts +105 -0
  47. package/src/__tests__/pdfImport.test.ts +95 -1
  48. package/src/__tests__/pptxImport.test.ts +46 -1
  49. package/src/__tests__/roundTripAssets.test.ts +50 -0
  50. package/src/__tests__/roundTripMatrix.test.ts +10 -4
  51. package/src/__tests__/sharedContainer.test.ts +41 -0
  52. package/src/__tests__/sharedImages.test.ts +61 -0
  53. package/src/__tests__/xlsxExport.test.ts +164 -0
  54. package/src/csv/index.ts +25 -2
  55. package/src/docx/import.ts +6 -37
  56. package/src/html/imageUtils.ts +5 -19
  57. package/src/index.ts +29 -5
  58. package/src/ooxml/index.ts +3 -0
  59. package/src/ooxml/namespaces.ts +10 -0
  60. package/src/pdf/import.ts +145 -50
  61. package/src/pptx/import.ts +127 -10
  62. package/src/pptx/index.ts +1 -1
  63. package/src/registry/convert.ts +318 -0
  64. package/src/registry/defaultFormats.ts +323 -0
  65. package/src/registry/errors.ts +46 -0
  66. package/src/registry/index.ts +38 -0
  67. package/src/registry/registry.ts +48 -0
  68. package/src/registry/types.ts +107 -0
  69. package/src/shared/container.ts +28 -0
  70. package/src/shared/images.ts +44 -0
  71. package/src/xlsx/export.ts +253 -0
  72. package/src/xlsx/index.ts +10 -36
  73. package/dist/chunk-A3FHLTY5.js.map +0 -1
  74. package/dist/chunk-APSIAEXL.js.map +0 -1
  75. package/dist/chunk-BJQIJ4S4.js.map +0 -1
  76. package/dist/chunk-FMOIGA5T.js.map +0 -1
  77. package/dist/chunk-JBNVE2GV.js.map +0 -1
  78. package/dist/chunk-L53YIGYS.js +0 -141
  79. package/dist/chunk-L53YIGYS.js.map +0 -1
  80. package/dist/chunk-QSE7EWE7.js +0 -181
  81. package/dist/chunk-QSE7EWE7.js.map +0 -1
  82. /package/dist/{chunk-CRTC6DEX.js.map → chunk-46FMDOWZ.js.map} +0 -0
  83. /package/dist/{chunk-ON232HJR.js.map → chunk-5LSSWZPU.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/csv/index.ts"],"sourcesContent":["/**\n * @bendyline/squisq-formats CSV Module\n *\n * Bridges CSV ↔ the squisq markdown table model. CSV isn't OOXML, so this\n * module is self-contained (no jszip / DOMParser): a small RFC-4180 parser on\n * the import side and a serializer on the export side. The first row is treated\n * as the table header by default.\n *\n * @example\n * ```ts\n * import { csvToMarkdownDoc, markdownDocToCsv } from '@bendyline/squisq-formats/csv';\n * ```\n */\n\nimport { markdownToDoc } from '@bendyline/squisq/doc';\nimport type {\n MarkdownDocument,\n MarkdownTable,\n MarkdownTableCell,\n MarkdownTableRow,\n} from '@bendyline/squisq/markdown';\nimport type { Doc } from '@bendyline/squisq/schemas';\n\nexport interface CsvImportOptions {\n /** Field delimiter. Default `,`. */\n delimiter?: string;\n /** Treat the first row as a header row. Default true. */\n hasHeader?: boolean;\n}\n\nexport interface CsvExportOptions {\n /** Field delimiter. Default `,`. */\n delimiter?: string;\n}\n\nasync function toText(data: ArrayBuffer | Blob | string): Promise<string> {\n if (typeof data === 'string') return data;\n if (typeof Blob !== 'undefined' && data instanceof Blob) return data.text();\n return new TextDecoder().decode(new Uint8Array(data as ArrayBuffer));\n}\n\n/** Parse CSV text into a grid of string cells (RFC 4180: quotes, escaped quotes). */\nexport function parseCsv(text: string, delimiter = ','): string[][] {\n const rows: string[][] = [];\n let row: string[] = [];\n let field = '';\n let inQuotes = false;\n let sawAny = false;\n const pushField = () => {\n row.push(field);\n field = '';\n };\n const pushRow = () => {\n pushField();\n rows.push(row);\n row = [];\n };\n for (let i = 0; i < text.length; i++) {\n const ch = text[i]!;\n if (inQuotes) {\n if (ch === '\"') {\n if (text[i + 1] === '\"') {\n field += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n field += ch;\n }\n continue;\n }\n if (ch === '\"') {\n inQuotes = true;\n sawAny = true;\n continue;\n }\n if (ch === delimiter) {\n sawAny = true;\n pushField();\n continue;\n }\n if (ch === '\\n' || ch === '\\r') {\n if (ch === '\\r' && text[i + 1] === '\\n') i++;\n pushRow();\n sawAny = false;\n continue;\n }\n sawAny = true;\n field += ch;\n }\n // Flush a trailing field/row only if the last line wasn't terminated.\n if (field !== '' || row.length > 0 || sawAny) pushRow();\n return rows;\n}\n\nfunction rowsToTable(rows: string[][], hasHeader: boolean): MarkdownTable {\n const maxCols = rows.reduce((m, r) => Math.max(m, r.length), 1);\n const mdRows: MarkdownTableRow[] = rows.map((cells, rowIdx) => {\n const children: MarkdownTableCell[] = [];\n for (let c = 0; c < maxCols; c++) {\n const value = cells[c] ?? '';\n children.push({\n type: 'tableCell',\n ...(hasHeader && rowIdx === 0 ? { isHeader: true } : {}),\n children: value ? [{ type: 'text', value }] : [],\n });\n }\n return { type: 'tableRow', children };\n });\n return { type: 'table', children: mdRows };\n}\n\n/** Convert CSV to a MarkdownDocument containing a single table. */\nexport async function csvToMarkdownDoc(\n data: ArrayBuffer | Blob | string,\n options: CsvImportOptions = {},\n): Promise<MarkdownDocument> {\n const text = await toText(data);\n const rows = parseCsv(text, options.delimiter ?? ',');\n const hasHeader = options.hasHeader ?? true;\n const children = rows.length > 0 ? [rowsToTable(rows, hasHeader)] : [];\n return { type: 'document', children };\n}\n\n/** Convert CSV to a squisq Doc. */\nexport async function csvToDoc(\n data: ArrayBuffer | Blob | string,\n options: CsvImportOptions = {},\n): Promise<Doc> {\n return markdownToDoc(await csvToMarkdownDoc(data, options));\n}\n\nfunction escapeCsvField(value: string, delimiter: string): string {\n if (value.includes('\"') || value.includes(delimiter) || /[\\r\\n]/.test(value)) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\nfunction cellText(cell: MarkdownTableCell): string {\n // Flatten inline children to plain text (CSV has no formatting).\n const walk = (nodes: unknown[]): string =>\n nodes\n .map((n) => {\n const node = n as { type?: string; value?: string; children?: unknown[] };\n if (node.value !== undefined) return node.value;\n if (Array.isArray(node.children)) return walk(node.children);\n return '';\n })\n .join('');\n return walk(cell.children);\n}\n\n/** Serialize the first table in a MarkdownDocument to CSV text. */\nexport function markdownDocToCsv(doc: MarkdownDocument, options: CsvExportOptions = {}): string {\n const delimiter = options.delimiter ?? ',';\n const table = doc.children.find((n) => n.type === 'table') as MarkdownTable | undefined;\n if (!table) return '';\n return table.children\n .map((row) =>\n row.children.map((cell) => escapeCsvField(cellText(cell), delimiter)).join(delimiter),\n )\n .join('\\r\\n');\n}\n"],"mappings":"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","names":[]}
@@ -1,141 +0,0 @@
1
- import {
2
- NS_R,
3
- NS_SML,
4
- getPartRelationships,
5
- getPartXml,
6
- openPackage
7
- } from "./chunk-FMOIGA5T.js";
8
-
9
- // src/xlsx/index.ts
10
- import { markdownToDoc } from "@bendyline/squisq/doc";
11
-
12
- // src/xlsx/import.ts
13
- function attrNS(el, ns, local, fallback) {
14
- return el.getAttributeNS(ns, local) ?? el.getAttribute(fallback);
15
- }
16
- function resolveTarget(baseDir, target) {
17
- if (target.startsWith("/")) return target.replace(/^\//, "");
18
- const stack = baseDir ? baseDir.split("/") : [];
19
- for (const seg of target.split("/")) {
20
- if (seg === "..") stack.pop();
21
- else if (seg !== ".") stack.push(seg);
22
- }
23
- return stack.join("/");
24
- }
25
- async function readSheets(pkg) {
26
- const wb = await getPartXml(pkg, "xl/workbook.xml");
27
- if (!wb) return [];
28
- const rels = await getPartRelationships(pkg, "xl/workbook.xml");
29
- const relById = new Map(rels.map((r) => [r.id, r.target]));
30
- const out = [];
31
- const sheetEls = wb.getElementsByTagNameNS(NS_SML, "sheet");
32
- for (let i = 0; i < sheetEls.length; i++) {
33
- const el = sheetEls[i];
34
- const name = el.getAttribute("name") ?? `Sheet${i + 1}`;
35
- const rid = attrNS(el, NS_R, "id", "r:id");
36
- const target = rid ? relById.get(rid) : void 0;
37
- if (target) out.push({ name, path: resolveTarget("xl", target) });
38
- }
39
- return out;
40
- }
41
- async function readSharedStrings(pkg) {
42
- const doc = await getPartXml(pkg, "xl/sharedStrings.xml");
43
- if (!doc) return [];
44
- const siEls = doc.getElementsByTagNameNS(NS_SML, "si");
45
- const out = [];
46
- for (let i = 0; i < siEls.length; i++) out.push(siEls[i].textContent ?? "");
47
- return out;
48
- }
49
- function colIndex(ref) {
50
- const m = /^([A-Za-z]+)/.exec(ref);
51
- if (!m) return 0;
52
- let n = 0;
53
- for (const ch of m[1].toUpperCase()) n = n * 26 + (ch.charCodeAt(0) - 64);
54
- return n - 1;
55
- }
56
- function cellText(cell, shared) {
57
- const t = cell.getAttribute("t");
58
- if (t === "inlineStr") {
59
- const is = cell.getElementsByTagNameNS(NS_SML, "t");
60
- return is.length ? is[0].textContent ?? "" : "";
61
- }
62
- const vEls = cell.getElementsByTagNameNS(NS_SML, "v");
63
- const v = vEls.length ? vEls[0].textContent ?? "" : "";
64
- if (t === "s") return shared[Number.parseInt(v, 10)] ?? "";
65
- if (t === "b") return v === "1" ? "TRUE" : "FALSE";
66
- return v;
67
- }
68
- async function sheetToGrid(pkg, path, shared) {
69
- const doc = await getPartXml(pkg, path);
70
- if (!doc) return [];
71
- const rowEls = doc.getElementsByTagNameNS(NS_SML, "row");
72
- const grid = [];
73
- for (let r = 0; r < rowEls.length; r++) {
74
- const cells = rowEls[r].getElementsByTagNameNS(NS_SML, "c");
75
- const rowArr = [];
76
- for (let c = 0; c < cells.length; c++) {
77
- const cell = cells[c];
78
- const ref = cell.getAttribute("r");
79
- const idx = ref ? colIndex(ref) : rowArr.length;
80
- while (rowArr.length < idx) rowArr.push("");
81
- rowArr[idx] = cellText(cell, shared);
82
- }
83
- grid.push(rowArr);
84
- }
85
- return grid;
86
- }
87
- function gridToTable(grid) {
88
- const maxCols = grid.reduce((m, r) => Math.max(m, r.length), 1);
89
- const rows = grid.map((cells, rowIdx) => {
90
- const children = [];
91
- for (let c = 0; c < maxCols; c++) {
92
- const value = cells[c] ?? "";
93
- children.push({
94
- type: "tableCell",
95
- ...rowIdx === 0 ? { isHeader: true } : {},
96
- children: value ? [{ type: "text", value }] : []
97
- });
98
- }
99
- return { type: "tableRow", children };
100
- });
101
- return { type: "table", children: rows };
102
- }
103
- async function xlsxToMarkdownDoc(data, options = {}) {
104
- const pkg = await openPackage(data);
105
- const sheets = await readSheets(pkg);
106
- const shared = await readSharedStrings(pkg);
107
- let selected = sheets;
108
- if (options.sheet !== void 0) {
109
- const picked = typeof options.sheet === "number" ? sheets[options.sheet] : sheets.find((s) => s.name === options.sheet);
110
- selected = picked ? [picked] : [];
111
- }
112
- const children = [];
113
- const single = selected.length === 1 && options.sheet !== void 0;
114
- for (const sheet of selected) {
115
- const grid = await sheetToGrid(pkg, sheet.path, shared);
116
- if (!single) {
117
- children.push({ type: "heading", depth: 1, children: [{ type: "text", value: sheet.name }] });
118
- }
119
- if (grid.length > 0) children.push(gridToTable(grid));
120
- }
121
- return { type: "document", children };
122
- }
123
-
124
- // src/xlsx/index.ts
125
- async function markdownDocToXlsx(_doc, _options) {
126
- throw new Error("XLSX export is not yet implemented");
127
- }
128
- async function docToXlsx(_doc, _options) {
129
- throw new Error("XLSX export is not yet implemented");
130
- }
131
- async function xlsxToDoc(data, options) {
132
- return markdownToDoc(await xlsxToMarkdownDoc(data, options));
133
- }
134
-
135
- export {
136
- xlsxToMarkdownDoc,
137
- markdownDocToXlsx,
138
- docToXlsx,
139
- xlsxToDoc
140
- };
141
- //# sourceMappingURL=chunk-L53YIGYS.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/xlsx/index.ts","../src/xlsx/import.ts"],"sourcesContent":["/**\n * @bendyline/squisq-formats XLSX Module (Stub)\n *\n * Placeholder for Excel .xlsx import/export support.\n * Will use SpreadsheetML (`<spreadsheet>`, `<worksheet>`, `<sheetData>`)\n * via the shared ooxml/ infrastructure.\n *\n * @example\n * ```ts\n * import { markdownDocToXlsx } from '@bendyline/squisq-formats/xlsx';\n * ```\n */\n\nimport { markdownToDoc } from '@bendyline/squisq/doc';\nimport type { MarkdownDocument } from '@bendyline/squisq/markdown';\nimport type { Doc } from '@bendyline/squisq/schemas';\nimport { type XlsxImportOptions, xlsxToMarkdownDoc } from './import.js';\n\nexport type { XlsxImportOptions } from './import.js';\nexport { xlsxToMarkdownDoc } from './import.js';\n\n/**\n * Options for XLSX export (placeholder).\n */\nexport interface XlsxExportOptions {\n /** Workbook title */\n title?: string;\n /** Workbook author */\n author?: string;\n}\n\n/**\n * Convert a MarkdownDocument to a .xlsx Blob.\n *\n * @throws Error — XLSX export is not yet implemented\n */\nexport async function markdownDocToXlsx(\n _doc: MarkdownDocument,\n _options?: XlsxExportOptions,\n): Promise<Blob> {\n throw new Error('XLSX export is not yet implemented');\n}\n\n/**\n * Convert a squisq Doc to a .xlsx Blob.\n *\n * @throws Error — XLSX export is not yet implemented\n */\nexport async function docToXlsx(_doc: Doc, _options?: XlsxExportOptions): Promise<Blob> {\n throw new Error('XLSX export is not yet implemented');\n}\n\n/**\n * Convert a .xlsx file to a squisq Doc (via the markdown table model).\n */\nexport async function xlsxToDoc(\n data: ArrayBuffer | Blob,\n options?: XlsxImportOptions,\n): Promise<Doc> {\n return markdownToDoc(await xlsxToMarkdownDoc(data, options));\n}\n","/**\n * XLSX import — SpreadsheetML (.xlsx) → MarkdownDocument.\n *\n * Reuses the shared ooxml/ reader (zip + DOMParser). Reads the workbook's\n * sheet list, resolves each sheet part via relationships, pulls shared strings,\n * and turns each worksheet's cell grid into a markdown table (first row treated\n * as the header). By default every sheet is imported, each preceded by an H1 of\n * the sheet name; pass `options.sheet` (index or name) to import just one.\n */\n\nimport type {\n MarkdownBlockNode,\n MarkdownDocument,\n MarkdownTable,\n MarkdownTableCell,\n MarkdownTableRow,\n} from '@bendyline/squisq/markdown';\nimport { getPartXml, getPartRelationships, openPackage } from '../ooxml/reader.js';\nimport type { OoxmlPackage } from '../ooxml/types.js';\nimport { NS_R, NS_SML } from '../ooxml/namespaces.js';\n\nexport interface XlsxImportOptions {\n /** Which sheet to import (0-based index or sheet name). Default: all sheets. */\n sheet?: number | string;\n}\n\ninterface SheetRef {\n name: string;\n path: string;\n}\n\nfunction attrNS(el: Element, ns: string, local: string, fallback: string): string | null {\n return el.getAttributeNS(ns, local) ?? el.getAttribute(fallback);\n}\n\n/** Resolve a relationship target (relative to its part's dir) to a zip path. */\nfunction resolveTarget(baseDir: string, target: string): string {\n if (target.startsWith('/')) return target.replace(/^\\//, '');\n const stack = baseDir ? baseDir.split('/') : [];\n for (const seg of target.split('/')) {\n if (seg === '..') stack.pop();\n else if (seg !== '.') stack.push(seg);\n }\n return stack.join('/');\n}\n\nasync function readSheets(pkg: OoxmlPackage): Promise<SheetRef[]> {\n const wb = await getPartXml(pkg, 'xl/workbook.xml');\n if (!wb) return [];\n const rels = await getPartRelationships(pkg, 'xl/workbook.xml');\n const relById = new Map(rels.map((r) => [r.id, r.target]));\n const out: SheetRef[] = [];\n const sheetEls = wb.getElementsByTagNameNS(NS_SML, 'sheet');\n for (let i = 0; i < sheetEls.length; i++) {\n const el = sheetEls[i]!;\n const name = el.getAttribute('name') ?? `Sheet${i + 1}`;\n const rid = attrNS(el, NS_R, 'id', 'r:id');\n const target = rid ? relById.get(rid) : undefined;\n if (target) out.push({ name, path: resolveTarget('xl', target) });\n }\n return out;\n}\n\nasync function readSharedStrings(pkg: OoxmlPackage): Promise<string[]> {\n const doc = await getPartXml(pkg, 'xl/sharedStrings.xml');\n if (!doc) return [];\n const siEls = doc.getElementsByTagNameNS(NS_SML, 'si');\n const out: string[] = [];\n for (let i = 0; i < siEls.length; i++) out.push(siEls[i]!.textContent ?? '');\n return out;\n}\n\n/** Column letters of a cell ref (\"B7\" → 1, \"AA1\" → 26). */\nfunction colIndex(ref: string): number {\n const m = /^([A-Za-z]+)/.exec(ref);\n if (!m) return 0;\n let n = 0;\n for (const ch of m[1]!.toUpperCase()) n = n * 26 + (ch.charCodeAt(0) - 64);\n return n - 1;\n}\n\nfunction cellText(cell: Element, shared: string[]): string {\n const t = cell.getAttribute('t');\n if (t === 'inlineStr') {\n const is = cell.getElementsByTagNameNS(NS_SML, 't');\n return is.length ? (is[0]!.textContent ?? '') : '';\n }\n const vEls = cell.getElementsByTagNameNS(NS_SML, 'v');\n const v = vEls.length ? (vEls[0]!.textContent ?? '') : '';\n if (t === 's') return shared[Number.parseInt(v, 10)] ?? '';\n if (t === 'b') return v === '1' ? 'TRUE' : 'FALSE';\n return v;\n}\n\nasync function sheetToGrid(pkg: OoxmlPackage, path: string, shared: string[]): Promise<string[][]> {\n const doc = await getPartXml(pkg, path);\n if (!doc) return [];\n const rowEls = doc.getElementsByTagNameNS(NS_SML, 'row');\n const grid: string[][] = [];\n for (let r = 0; r < rowEls.length; r++) {\n const cells = rowEls[r]!.getElementsByTagNameNS(NS_SML, 'c');\n const rowArr: string[] = [];\n for (let c = 0; c < cells.length; c++) {\n const cell = cells[c]!;\n const ref = cell.getAttribute('r');\n const idx = ref ? colIndex(ref) : rowArr.length;\n while (rowArr.length < idx) rowArr.push('');\n rowArr[idx] = cellText(cell, shared);\n }\n grid.push(rowArr);\n }\n return grid;\n}\n\nfunction gridToTable(grid: string[][]): MarkdownTable {\n const maxCols = grid.reduce((m, r) => Math.max(m, r.length), 1);\n const rows: MarkdownTableRow[] = grid.map((cells, rowIdx) => {\n const children: MarkdownTableCell[] = [];\n for (let c = 0; c < maxCols; c++) {\n const value = cells[c] ?? '';\n children.push({\n type: 'tableCell',\n ...(rowIdx === 0 ? { isHeader: true } : {}),\n children: value ? [{ type: 'text', value }] : [],\n });\n }\n return { type: 'tableRow', children };\n });\n return { type: 'table', children: rows };\n}\n\nexport async function xlsxToMarkdownDoc(\n data: ArrayBuffer | Blob,\n options: XlsxImportOptions = {},\n): Promise<MarkdownDocument> {\n const pkg = await openPackage(data);\n const sheets = await readSheets(pkg);\n const shared = await readSharedStrings(pkg);\n\n let selected = sheets;\n if (options.sheet !== undefined) {\n const picked =\n typeof options.sheet === 'number'\n ? sheets[options.sheet]\n : sheets.find((s) => s.name === options.sheet);\n selected = picked ? [picked] : [];\n }\n\n const children: MarkdownBlockNode[] = [];\n const single = selected.length === 1 && options.sheet !== undefined;\n for (const sheet of selected) {\n const grid = await sheetToGrid(pkg, sheet.path, shared);\n if (!single) {\n children.push({ type: 'heading', depth: 1, children: [{ type: 'text', value: sheet.name }] });\n }\n if (grid.length > 0) children.push(gridToTable(grid));\n }\n return { type: 'document', children };\n}\n"],"mappings":"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","names":[]}
@@ -1,181 +0,0 @@
1
- import {
2
- escapeXml,
3
- xmlDeclaration
4
- } from "./chunk-U4MRIFKL.js";
5
- import {
6
- CONTENT_TYPE_CORE_PROPERTIES,
7
- CONTENT_TYPE_RELATIONSHIPS,
8
- NS_CONTENT_TYPES,
9
- NS_CORE_PROPERTIES,
10
- NS_DC,
11
- NS_DCTERMS,
12
- NS_RELATIONSHIPS,
13
- NS_XSI,
14
- REL_CORE_PROPERTIES
15
- } from "./chunk-FMOIGA5T.js";
16
-
17
- // src/ooxml/writer.ts
18
- import JSZip from "jszip";
19
- function createPackage() {
20
- const parts = [];
21
- const relationships = [];
22
- let coreProps;
23
- return {
24
- addPart(path, content, contentType) {
25
- parts.push({ path, content, contentType });
26
- },
27
- addBinaryPart(path, data, contentType) {
28
- parts.push({ path, binaryContent: data, contentType });
29
- },
30
- addRelationship(sourcePart, rel) {
31
- relationships.push({ sourcePart, relationship: rel });
32
- },
33
- setCoreProperties(props) {
34
- coreProps = props;
35
- },
36
- async toBlob() {
37
- const zip = assemble(parts, relationships, coreProps);
38
- return zip.generateAsync({ type: "blob" });
39
- },
40
- async toArrayBuffer() {
41
- const zip = assemble(parts, relationships, coreProps);
42
- return zip.generateAsync({ type: "arraybuffer" });
43
- }
44
- };
45
- }
46
- function assemble(parts, relationships, coreProps) {
47
- const zip = new JSZip();
48
- for (const part of parts) {
49
- if (part.content !== void 0) {
50
- zip.file(part.path, part.content);
51
- } else if (part.binaryContent !== void 0) {
52
- zip.file(part.path, part.binaryContent);
53
- }
54
- }
55
- if (coreProps) {
56
- const coreXml = buildCorePropertiesXml(coreProps);
57
- zip.file("docProps/core.xml", coreXml);
58
- relationships.push({
59
- sourcePart: "",
60
- relationship: {
61
- id: `rId${relationships.length + 100}`,
62
- type: REL_CORE_PROPERTIES,
63
- target: "docProps/core.xml"
64
- }
65
- });
66
- }
67
- zip.file("[Content_Types].xml", buildContentTypesXml(parts, coreProps));
68
- const relsBySource = groupRelationshipsBySource(relationships);
69
- for (const [sourcePart, rels] of relsBySource) {
70
- const relsPath = sourcePart === "" ? "_rels/.rels" : buildRelsPath(sourcePart);
71
- zip.file(relsPath, buildRelationshipsXml(rels));
72
- }
73
- return zip;
74
- }
75
- function buildContentTypesXml(parts, coreProps) {
76
- const lines = [xmlDeclaration()];
77
- lines.push(`<Types xmlns="${NS_CONTENT_TYPES}">`);
78
- const extensionTypes = /* @__PURE__ */ new Map();
79
- extensionTypes.set("rels", CONTENT_TYPE_RELATIONSHIPS);
80
- extensionTypes.set("xml", "application/xml");
81
- for (const part of parts) {
82
- if (part.binaryContent !== void 0) {
83
- const ext = getExtension(part.path);
84
- if (ext && !extensionTypes.has(ext)) {
85
- extensionTypes.set(ext, part.contentType);
86
- }
87
- }
88
- }
89
- for (const [ext, ct] of extensionTypes) {
90
- lines.push(` <Default Extension="${escapeXml(ext)}" ContentType="${escapeXml(ct)}"/>`);
91
- }
92
- for (const part of parts) {
93
- if (part.content !== void 0) {
94
- lines.push(
95
- ` <Override PartName="/${escapeXml(part.path)}" ContentType="${escapeXml(part.contentType)}"/>`
96
- );
97
- }
98
- }
99
- if (coreProps) {
100
- lines.push(
101
- ` <Override PartName="/docProps/core.xml" ContentType="${CONTENT_TYPE_CORE_PROPERTIES}"/>`
102
- );
103
- }
104
- lines.push("</Types>");
105
- return lines.join("\n");
106
- }
107
- function buildRelationshipsXml(rels) {
108
- const lines = [xmlDeclaration()];
109
- lines.push(`<Relationships xmlns="${NS_RELATIONSHIPS}">`);
110
- for (const rel of rels) {
111
- const attrs = [
112
- `Id="${escapeXml(rel.id)}"`,
113
- `Type="${escapeXml(rel.type)}"`,
114
- `Target="${escapeXml(rel.target)}"`
115
- ];
116
- if (rel.targetMode) {
117
- attrs.push(`TargetMode="${escapeXml(rel.targetMode)}"`);
118
- }
119
- lines.push(` <Relationship ${attrs.join(" ")}/>`);
120
- }
121
- lines.push("</Relationships>");
122
- return lines.join("\n");
123
- }
124
- function buildCorePropertiesXml(props) {
125
- const lines = [xmlDeclaration()];
126
- lines.push(
127
- `<cp:coreProperties xmlns:cp="${NS_CORE_PROPERTIES}" xmlns:dc="${NS_DC}" xmlns:dcterms="${NS_DCTERMS}" xmlns:xsi="${NS_XSI}">`
128
- );
129
- if (props.title) lines.push(` <dc:title>${escapeXml(props.title)}</dc:title>`);
130
- if (props.subject) lines.push(` <dc:subject>${escapeXml(props.subject)}</dc:subject>`);
131
- if (props.creator) lines.push(` <dc:creator>${escapeXml(props.creator)}</dc:creator>`);
132
- if (props.description)
133
- lines.push(` <dc:description>${escapeXml(props.description)}</dc:description>`);
134
- if (props.keywords) lines.push(` <cp:keywords>${escapeXml(props.keywords)}</cp:keywords>`);
135
- if (props.lastModifiedBy)
136
- lines.push(` <cp:lastModifiedBy>${escapeXml(props.lastModifiedBy)}</cp:lastModifiedBy>`);
137
- if (props.revision) lines.push(` <cp:revision>${escapeXml(props.revision)}</cp:revision>`);
138
- if (props.created) {
139
- lines.push(
140
- ` <dcterms:created xsi:type="dcterms:W3CDTF">${escapeXml(props.created)}</dcterms:created>`
141
- );
142
- }
143
- if (props.modified) {
144
- lines.push(
145
- ` <dcterms:modified xsi:type="dcterms:W3CDTF">${escapeXml(props.modified)}</dcterms:modified>`
146
- );
147
- }
148
- lines.push("</cp:coreProperties>");
149
- return lines.join("\n");
150
- }
151
- function getExtension(path) {
152
- const dot = path.lastIndexOf(".");
153
- if (dot === -1) return void 0;
154
- return path.substring(dot + 1).toLowerCase();
155
- }
156
- function buildRelsPath(partPath) {
157
- const lastSlash = partPath.lastIndexOf("/");
158
- if (lastSlash === -1) {
159
- return `_rels/${partPath}.rels`;
160
- }
161
- const dir = partPath.substring(0, lastSlash);
162
- const file = partPath.substring(lastSlash + 1);
163
- return `${dir}/_rels/${file}.rels`;
164
- }
165
- function groupRelationshipsBySource(pending) {
166
- const grouped = /* @__PURE__ */ new Map();
167
- for (const { sourcePart, relationship } of pending) {
168
- let list = grouped.get(sourcePart);
169
- if (!list) {
170
- list = [];
171
- grouped.set(sourcePart, list);
172
- }
173
- list.push(relationship);
174
- }
175
- return grouped;
176
- }
177
-
178
- export {
179
- createPackage
180
- };
181
- //# sourceMappingURL=chunk-QSE7EWE7.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/ooxml/writer.ts"],"sourcesContent":["/**\n * OOXML Package Writer\n *\n * Builds OOXML archives (.docx, .pptx, .xlsx) from parts.\n * Handles automatic generation of [Content_Types].xml and\n * _rels/*.rels files from the registered parts and relationships.\n */\n\nimport JSZip from 'jszip';\nimport type { CoreProperties, PackagePart, PendingRelationship, Relationship } from './types.js';\nimport {\n NS_CONTENT_TYPES,\n NS_RELATIONSHIPS,\n NS_CORE_PROPERTIES,\n NS_DC,\n NS_DCTERMS,\n NS_XSI,\n CONTENT_TYPE_RELATIONSHIPS,\n CONTENT_TYPE_CORE_PROPERTIES,\n REL_CORE_PROPERTIES,\n} from './namespaces.js';\nimport { xmlDeclaration, escapeXml } from './xmlUtils.js';\n\n// ============================================\n// Package Builder\n// ============================================\n\n/**\n * Mutable builder for constructing an OOXML package.\n *\n * Add parts, relationships, and core properties, then call `toBlob()`\n * or `toArrayBuffer()` to produce the final ZIP archive.\n *\n * @example\n * ```ts\n * const builder = createPackage();\n * builder.addPart('word/document.xml', documentXml, CONTENT_TYPE_DOCX_DOCUMENT);\n * builder.addRelationship('', {\n * id: 'rId1',\n * type: REL_OFFICE_DOCUMENT,\n * target: 'word/document.xml',\n * });\n * const blob = await builder.toBlob();\n * ```\n */\nexport interface OoxmlPackageBuilder {\n /**\n * Add an XML or text part to the package.\n *\n * @param path - Path within the archive (e.g., \"word/document.xml\")\n * @param content - XML string content\n * @param contentType - MIME content type for [Content_Types].xml\n */\n addPart(path: string, content: string, contentType: string): void;\n\n /**\n * Add a binary part to the package (e.g., an image).\n *\n * @param path - Path within the archive (e.g., \"word/media/image1.png\")\n * @param data - Binary content\n * @param contentType - MIME content type (e.g., \"image/png\")\n */\n addBinaryPart(path: string, data: ArrayBuffer | Uint8Array, contentType: string): void;\n\n /**\n * Register a relationship.\n *\n * @param sourcePart - The part this relationship belongs to (e.g., \"word/document.xml\").\n * Use \"\" (empty string) for root-level relationships (_rels/.rels).\n * @param rel - The relationship entry\n */\n addRelationship(sourcePart: string, rel: Relationship): void;\n\n /**\n * Set core document properties (docProps/core.xml).\n * Calling this multiple times overwrites previous values.\n */\n setCoreProperties(props: CoreProperties): void;\n\n /**\n * Assemble the final OOXML package as a Blob.\n * Generates [Content_Types].xml and all _rels/*.rels files automatically.\n */\n toBlob(): Promise<Blob>;\n\n /**\n * Assemble the final OOXML package as an ArrayBuffer.\n */\n toArrayBuffer(): Promise<ArrayBuffer>;\n}\n\n/**\n * Create a new OOXML package builder.\n */\nexport function createPackage(): OoxmlPackageBuilder {\n const parts: PackagePart[] = [];\n const relationships: PendingRelationship[] = [];\n let coreProps: CoreProperties | undefined;\n\n return {\n addPart(path, content, contentType) {\n parts.push({ path, content, contentType });\n },\n\n addBinaryPart(path, data, contentType) {\n parts.push({ path, binaryContent: data, contentType });\n },\n\n addRelationship(sourcePart, rel) {\n relationships.push({ sourcePart, relationship: rel });\n },\n\n setCoreProperties(props) {\n coreProps = props;\n },\n\n async toBlob() {\n const zip = assemble(parts, relationships, coreProps);\n return zip.generateAsync({ type: 'blob' });\n },\n\n async toArrayBuffer() {\n const zip = assemble(parts, relationships, coreProps);\n return zip.generateAsync({ type: 'arraybuffer' });\n },\n };\n}\n\n// ============================================\n// Assembly\n// ============================================\n\n/**\n * Assemble a JSZip archive from parts, relationships, and core properties.\n */\nfunction assemble(\n parts: PackagePart[],\n relationships: PendingRelationship[],\n coreProps?: CoreProperties,\n): JSZip {\n const zip = new JSZip();\n\n // Write content parts\n for (const part of parts) {\n if (part.content !== undefined) {\n zip.file(part.path, part.content);\n } else if (part.binaryContent !== undefined) {\n zip.file(part.path, part.binaryContent);\n }\n }\n\n // Add core properties if set\n if (coreProps) {\n const coreXml = buildCorePropertiesXml(coreProps);\n zip.file('docProps/core.xml', coreXml);\n\n // Add relationship to core properties\n relationships.push({\n sourcePart: '',\n relationship: {\n id: `rId${relationships.length + 100}`,\n type: REL_CORE_PROPERTIES,\n target: 'docProps/core.xml',\n },\n });\n }\n\n // Build and write [Content_Types].xml\n zip.file('[Content_Types].xml', buildContentTypesXml(parts, coreProps));\n\n // Build and write _rels/*.rels files\n const relsBySource = groupRelationshipsBySource(relationships);\n for (const [sourcePart, rels] of relsBySource) {\n const relsPath = sourcePart === '' ? '_rels/.rels' : buildRelsPath(sourcePart);\n zip.file(relsPath, buildRelationshipsXml(rels));\n }\n\n return zip;\n}\n\n// ============================================\n// [Content_Types].xml\n// ============================================\n\nfunction buildContentTypesXml(parts: PackagePart[], coreProps?: CoreProperties): string {\n const lines: string[] = [xmlDeclaration()];\n lines.push(`<Types xmlns=\"${NS_CONTENT_TYPES}\">`);\n\n // Default extension types\n const extensionTypes = new Map<string, string>();\n extensionTypes.set('rels', CONTENT_TYPE_RELATIONSHIPS);\n extensionTypes.set('xml', 'application/xml');\n\n // Collect extensions from binary parts (e.g., png, jpeg)\n for (const part of parts) {\n if (part.binaryContent !== undefined) {\n const ext = getExtension(part.path);\n if (ext && !extensionTypes.has(ext)) {\n extensionTypes.set(ext, part.contentType);\n }\n }\n }\n\n for (const [ext, ct] of extensionTypes) {\n lines.push(` <Default Extension=\"${escapeXml(ext)}\" ContentType=\"${escapeXml(ct)}\"/>`);\n }\n\n // Override entries for each XML part\n for (const part of parts) {\n if (part.content !== undefined) {\n lines.push(\n ` <Override PartName=\"/${escapeXml(part.path)}\" ContentType=\"${escapeXml(part.contentType)}\"/>`,\n );\n }\n }\n\n // Core properties override\n if (coreProps) {\n lines.push(\n ` <Override PartName=\"/docProps/core.xml\" ContentType=\"${CONTENT_TYPE_CORE_PROPERTIES}\"/>`,\n );\n }\n\n lines.push('</Types>');\n return lines.join('\\n');\n}\n\n// ============================================\n// Relationships XML\n// ============================================\n\nfunction buildRelationshipsXml(rels: Relationship[]): string {\n const lines: string[] = [xmlDeclaration()];\n lines.push(`<Relationships xmlns=\"${NS_RELATIONSHIPS}\">`);\n\n for (const rel of rels) {\n const attrs: string[] = [\n `Id=\"${escapeXml(rel.id)}\"`,\n `Type=\"${escapeXml(rel.type)}\"`,\n `Target=\"${escapeXml(rel.target)}\"`,\n ];\n if (rel.targetMode) {\n attrs.push(`TargetMode=\"${escapeXml(rel.targetMode)}\"`);\n }\n lines.push(` <Relationship ${attrs.join(' ')}/>`);\n }\n\n lines.push('</Relationships>');\n return lines.join('\\n');\n}\n\n// ============================================\n// Core Properties XML\n// ============================================\n\nfunction buildCorePropertiesXml(props: CoreProperties): string {\n const lines: string[] = [xmlDeclaration()];\n lines.push(\n `<cp:coreProperties` +\n ` xmlns:cp=\"${NS_CORE_PROPERTIES}\"` +\n ` xmlns:dc=\"${NS_DC}\"` +\n ` xmlns:dcterms=\"${NS_DCTERMS}\"` +\n ` xmlns:xsi=\"${NS_XSI}\">`,\n );\n\n if (props.title) lines.push(` <dc:title>${escapeXml(props.title)}</dc:title>`);\n if (props.subject) lines.push(` <dc:subject>${escapeXml(props.subject)}</dc:subject>`);\n if (props.creator) lines.push(` <dc:creator>${escapeXml(props.creator)}</dc:creator>`);\n if (props.description)\n lines.push(` <dc:description>${escapeXml(props.description)}</dc:description>`);\n if (props.keywords) lines.push(` <cp:keywords>${escapeXml(props.keywords)}</cp:keywords>`);\n if (props.lastModifiedBy)\n lines.push(` <cp:lastModifiedBy>${escapeXml(props.lastModifiedBy)}</cp:lastModifiedBy>`);\n if (props.revision) lines.push(` <cp:revision>${escapeXml(props.revision)}</cp:revision>`);\n if (props.created) {\n lines.push(\n ` <dcterms:created xsi:type=\"dcterms:W3CDTF\">${escapeXml(props.created)}</dcterms:created>`,\n );\n }\n if (props.modified) {\n lines.push(\n ` <dcterms:modified xsi:type=\"dcterms:W3CDTF\">${escapeXml(props.modified)}</dcterms:modified>`,\n );\n }\n\n lines.push('</cp:coreProperties>');\n return lines.join('\\n');\n}\n\n// ============================================\n// Helpers\n// ============================================\n\nfunction getExtension(path: string): string | undefined {\n const dot = path.lastIndexOf('.');\n if (dot === -1) return undefined;\n return path.substring(dot + 1).toLowerCase();\n}\n\nfunction buildRelsPath(partPath: string): string {\n const lastSlash = partPath.lastIndexOf('/');\n if (lastSlash === -1) {\n return `_rels/${partPath}.rels`;\n }\n const dir = partPath.substring(0, lastSlash);\n const file = partPath.substring(lastSlash + 1);\n return `${dir}/_rels/${file}.rels`;\n}\n\nfunction groupRelationshipsBySource(pending: PendingRelationship[]): Map<string, Relationship[]> {\n const grouped = new Map<string, Relationship[]>();\n for (const { sourcePart, relationship } of pending) {\n let list = grouped.get(sourcePart);\n if (!list) {\n list = [];\n grouped.set(sourcePart, list);\n }\n list.push(relationship);\n }\n return grouped;\n}\n"],"mappings":"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","names":[]}