@bendyline/squisq-formats 2.0.0 → 2.1.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.
Files changed (82) hide show
  1. package/dist/{chunk-CX3LH47J.js → chunk-74GO3FVS.js} +3 -3
  2. package/dist/{chunk-4HJ6AK2Q.js → chunk-7ARKUCQT.js} +52 -13
  3. package/dist/chunk-7ARKUCQT.js.map +1 -0
  4. package/dist/{chunk-LXYLOOST.js → chunk-DTDF6QDP.js} +3 -3
  5. package/dist/chunk-DTDF6QDP.js.map +1 -0
  6. package/dist/{chunk-ZZ7D4DN6.js → chunk-EW54IRRS.js} +39 -9
  7. package/dist/chunk-EW54IRRS.js.map +1 -0
  8. package/dist/{chunk-H7T5DXUU.js → chunk-FE6OJV6O.js} +95 -14
  9. package/dist/chunk-FE6OJV6O.js.map +1 -0
  10. package/dist/{chunk-XTLQXR4U.js → chunk-KURGXM4I.js} +3 -3
  11. package/dist/{chunk-2CSZJEAL.js → chunk-NNHKUXKA.js} +292 -70
  12. package/dist/chunk-NNHKUXKA.js.map +1 -0
  13. package/dist/{chunk-2PRVLAU3.js → chunk-QFLDYKCR.js} +55 -17
  14. package/dist/chunk-QFLDYKCR.js.map +1 -0
  15. package/dist/{chunk-CVNVD7VH.js → chunk-RLU7UFYU.js} +5 -5
  16. package/dist/{chunk-X4USWOQ2.js → chunk-SC67HYQJ.js} +2 -2
  17. package/dist/{chunk-HBSCRS5P.js → chunk-UGYF5AZE.js} +3 -3
  18. package/dist/{chunk-2EFDWKC6.js → chunk-WQSHGBLN.js} +86 -23
  19. package/dist/chunk-WQSHGBLN.js.map +1 -0
  20. package/dist/container/index.d.ts +10 -4
  21. package/dist/container/index.js +9 -4
  22. package/dist/container/index.js.map +1 -1
  23. package/dist/docx/index.d.ts +2 -2
  24. package/dist/docx/index.js +5 -4
  25. package/dist/{export-Djev9BLU.d.ts → export-D2NkylDT.d.ts} +1 -1
  26. package/dist/{extract-LNOA7DBK.js → extract-OJ7ZQV6P.js} +5 -5
  27. package/dist/html/index.js +1 -1
  28. package/dist/{import-Yivc3kZM.d.ts → import-K8mfc0fz.d.ts} +1 -1
  29. package/dist/index.d.ts +8 -7
  30. package/dist/index.js +15 -13
  31. package/dist/infer/index.d.ts +4 -4
  32. package/dist/infer/index.js +5 -5
  33. package/dist/{layouts-7OUUVAGU.js → layouts-5VDIRPIJ.js} +4 -4
  34. package/dist/{layouts-CZ0bdsNL.d.ts → layouts-BHrgZ5FS.d.ts} +1 -1
  35. package/dist/ooxml/index.d.ts +4 -4
  36. package/dist/ooxml/index.js +4 -4
  37. package/dist/pptx/index.d.ts +5 -5
  38. package/dist/pptx/index.js +5 -5
  39. package/dist/{reader-Gof6OrJX.d.ts → reader-B9L8Ucbj.d.ts} +3 -3
  40. package/dist/registry/index.d.ts +20 -6
  41. package/dist/registry/index.js +6 -4
  42. package/dist/{themeReader-DPw4J5Ss.d.ts → themeReader-DJKErl_j.d.ts} +1 -1
  43. package/dist/xlsx/index.d.ts +4 -4
  44. package/dist/xlsx/index.js +4 -4
  45. package/dist/{zipLimits-bGYMS9cI.d.ts → zipLimits-BOKCB7qk.d.ts} +2 -0
  46. package/package.json +2 -2
  47. package/src/__tests__/convert.test.ts +143 -1
  48. package/src/__tests__/docxExport.test.ts +34 -0
  49. package/src/__tests__/docxImport.test.ts +121 -0
  50. package/src/__tests__/html.test.ts +4 -0
  51. package/src/__tests__/ooxml.test.ts +17 -0
  52. package/src/__tests__/ooxmlCancellation.test.ts +113 -0
  53. package/src/container/index.ts +17 -3
  54. package/src/docx/export.ts +113 -5
  55. package/src/docx/import.ts +324 -69
  56. package/src/html/htmlTemplate.ts +2 -2
  57. package/src/index.ts +3 -0
  58. package/src/infer/index.ts +35 -4
  59. package/src/ooxml/namespaces.ts +21 -0
  60. package/src/ooxml/reader.ts +55 -14
  61. package/src/pptx/layouts.ts +68 -14
  62. package/src/registry/convert.ts +115 -31
  63. package/src/registry/defaultFormats.ts +5 -2
  64. package/src/registry/index.ts +3 -1
  65. package/src/registry/types.ts +18 -1
  66. package/src/shared/boundedZipArchive.ts +49 -8
  67. package/src/shared/zipEntryCount.ts +59 -5
  68. package/src/shared/zipLimits.ts +10 -0
  69. package/dist/chunk-2CSZJEAL.js.map +0 -1
  70. package/dist/chunk-2EFDWKC6.js.map +0 -1
  71. package/dist/chunk-2PRVLAU3.js.map +0 -1
  72. package/dist/chunk-4HJ6AK2Q.js.map +0 -1
  73. package/dist/chunk-H7T5DXUU.js.map +0 -1
  74. package/dist/chunk-LXYLOOST.js.map +0 -1
  75. package/dist/chunk-ZZ7D4DN6.js.map +0 -1
  76. /package/dist/{chunk-CX3LH47J.js.map → chunk-74GO3FVS.js.map} +0 -0
  77. /package/dist/{chunk-XTLQXR4U.js.map → chunk-KURGXM4I.js.map} +0 -0
  78. /package/dist/{chunk-CVNVD7VH.js.map → chunk-RLU7UFYU.js.map} +0 -0
  79. /package/dist/{chunk-X4USWOQ2.js.map → chunk-SC67HYQJ.js.map} +0 -0
  80. /package/dist/{chunk-HBSCRS5P.js.map → chunk-UGYF5AZE.js.map} +0 -0
  81. /package/dist/{extract-LNOA7DBK.js.map → extract-OJ7ZQV6P.js.map} +0 -0
  82. /package/dist/{layouts-7OUUVAGU.js.map → layouts-5VDIRPIJ.js.map} +0 -0
@@ -62,12 +62,16 @@ import {
62
62
  REL_HYPERLINK,
63
63
  REL_IMAGE,
64
64
  REL_FOOTNOTES,
65
+ REL_HEADER,
66
+ REL_FOOTER,
65
67
  CONTENT_TYPE_DOCX_DOCUMENT,
66
68
  CONTENT_TYPE_DOCX_STYLES,
67
69
  CONTENT_TYPE_DOCX_NUMBERING,
68
70
  CONTENT_TYPE_DOCX_SETTINGS,
69
71
  CONTENT_TYPE_DOCX_FONT_TABLE,
70
72
  CONTENT_TYPE_DOCX_FOOTNOTES,
73
+ CONTENT_TYPE_DOCX_HEADER,
74
+ CONTENT_TYPE_DOCX_FOOTER,
71
75
  } from '../ooxml/namespaces.js';
72
76
  import {
73
77
  DEPTH_TO_STYLE_ID,
@@ -135,8 +139,35 @@ export async function markdownDocToDocx(
135
139
  ? options
136
140
  : { ...options, themeId: readFrontmatterThemeId(doc.frontmatter) };
137
141
  const ctx = new ExportContext(resolvedOptions, doc);
138
- const bodyXml = convertBlocks(doc.children, ctx);
139
- return buildDocxPackage(bodyXml, ctx, resolvedOptions);
142
+ const stories = partitionDocumentStories(doc.children);
143
+ ctx.setRelationshipSource('document');
144
+ const bodyXml = convertBlocks(stories.body, ctx);
145
+ ctx.setRelationshipSource('header');
146
+ const headerXml = convertBlocks(stories.header, ctx);
147
+ ctx.setRelationshipSource('footer');
148
+ const footerXml = convertBlocks(stories.footer, ctx);
149
+ ctx.setRelationshipSource('document');
150
+ return buildDocxPackage(bodyXml, headerXml, footerXml, ctx, resolvedOptions);
151
+ }
152
+
153
+ interface DocumentStories {
154
+ body: MarkdownBlockNode[];
155
+ header: MarkdownBlockNode[];
156
+ footer: MarkdownBlockNode[];
157
+ }
158
+
159
+ function partitionDocumentStories(nodes: MarkdownBlockNode[]): DocumentStories {
160
+ const stories: DocumentStories = { body: [], header: [], footer: [] };
161
+ for (const node of nodes) {
162
+ if (node.type === 'containerDirective' && node.name === 'docx-header') {
163
+ stories.header.push(...node.children);
164
+ } else if (node.type === 'containerDirective' && node.name === 'docx-footer') {
165
+ stories.footer.push(...node.children);
166
+ } else {
167
+ stories.body.push(node);
168
+ }
169
+ }
170
+ return stories;
140
171
  }
141
172
 
142
173
  /**
@@ -172,8 +203,11 @@ class ExportContext {
172
203
  type: string;
173
204
  target: string;
174
205
  targetMode?: 'External';
206
+ source: DocxStoryPart;
175
207
  }> = [];
176
208
 
209
+ private relationshipSource: DocxStoryPart = 'document';
210
+
177
211
  /** Numbering definitions (abstract + num) */
178
212
  readonly numberingDefs: NumberingDef[] = [];
179
213
 
@@ -257,6 +291,10 @@ class ExportContext {
257
291
  return `rId${this.nextRelId++}`;
258
292
  }
259
293
 
294
+ setRelationshipSource(source: DocxStoryPart): void {
295
+ this.relationshipSource = source;
296
+ }
297
+
260
298
  /** Add a hyperlink relationship and return the rId */
261
299
  addHyperlink(url: string): string {
262
300
  const id = this.allocRelId();
@@ -265,6 +303,7 @@ class ExportContext {
265
303
  type: REL_HYPERLINK,
266
304
  target: url,
267
305
  targetMode: 'External',
306
+ source: this.relationshipSource,
268
307
  });
269
308
  return id;
270
309
  }
@@ -284,6 +323,7 @@ class ExportContext {
284
323
  id: relId,
285
324
  type: REL_IMAGE,
286
325
  target: `media/${filename}`,
326
+ source: this.relationshipSource,
287
327
  });
288
328
 
289
329
  return { relId, docPrId };
@@ -309,6 +349,8 @@ class ExportContext {
309
349
  }
310
350
  }
311
351
 
352
+ type DocxStoryPart = 'document' | 'header' | 'footer';
353
+
312
354
  interface NumberingDef {
313
355
  numId: number;
314
356
  ordered: boolean;
@@ -384,6 +426,10 @@ function convertBlock(node: MarkdownBlockNode, ctx: ExportContext, listDepth: nu
384
426
  return convertMathBlock(node);
385
427
  case 'footnoteDefinition':
386
428
  return convertFootnoteDefinition(node, ctx);
429
+ case 'containerDirective':
430
+ // Non-DOCX directives have no native Word wrapper. Preserve their
431
+ // contents rather than silently dropping the entire subtree.
432
+ return convertBlocks(node.children, ctx);
387
433
  default:
388
434
  // Definition lists, directives, link definitions — skip or emit as plain text
389
435
  return '';
@@ -858,6 +904,8 @@ function convertFootnoteRef(node: MarkdownFootnoteReference, ctx: ExportContext)
858
904
 
859
905
  async function buildDocxPackage(
860
906
  bodyXml: string,
907
+ headerXml: string,
908
+ footerXml: string,
861
909
  ctx: ExportContext,
862
910
  options: DocxExportOptions,
863
911
  ): Promise<ArrayBuffer> {
@@ -870,11 +918,27 @@ async function buildDocxPackage(
870
918
  const settingsRelId = `rId${relCounter++}`;
871
919
  const fontTableRelId = `rId${relCounter++}`;
872
920
  const footnotesRelId = `rId${relCounter++}`;
921
+ const headerRelId = `rId${relCounter++}`;
922
+ const footerRelId = `rId${relCounter++}`;
923
+ const hasHeader = headerXml.trim().length > 0;
924
+ const hasFooter = footerXml.trim().length > 0;
873
925
 
874
926
  // --- word/document.xml ---
875
- const documentXml = buildDocumentXml(bodyXml, ctx.backgroundColor);
927
+ const documentXml = buildDocumentXml(
928
+ bodyXml,
929
+ ctx.backgroundColor,
930
+ hasHeader ? headerRelId : undefined,
931
+ hasFooter ? footerRelId : undefined,
932
+ );
876
933
  pkg.addPart('word/document.xml', documentXml, CONTENT_TYPE_DOCX_DOCUMENT);
877
934
 
935
+ if (hasHeader) {
936
+ pkg.addPart('word/header1.xml', buildStoryXml('header', headerXml), CONTENT_TYPE_DOCX_HEADER);
937
+ }
938
+ if (hasFooter) {
939
+ pkg.addPart('word/footer1.xml', buildStoryXml('footer', footerXml), CONTENT_TYPE_DOCX_FOOTER);
940
+ }
941
+
878
942
  // --- word/styles.xml ---
879
943
  const stylesXml = buildStylesXml(options, ctx);
880
944
  pkg.addPart('word/styles.xml', stylesXml, CONTENT_TYPE_DOCX_STYLES);
@@ -942,9 +1006,31 @@ async function buildDocxPackage(
942
1006
  });
943
1007
  }
944
1008
 
1009
+ if (hasHeader) {
1010
+ pkg.addRelationship('word/document.xml', {
1011
+ id: headerRelId,
1012
+ type: REL_HEADER,
1013
+ target: 'header1.xml',
1014
+ });
1015
+ }
1016
+
1017
+ if (hasFooter) {
1018
+ pkg.addRelationship('word/document.xml', {
1019
+ id: footerRelId,
1020
+ type: REL_FOOTER,
1021
+ target: 'footer1.xml',
1022
+ });
1023
+ }
1024
+
945
1025
  // --- Dynamic relationships (hyperlinks, images) ---
946
1026
  for (const rel of ctx.relationships) {
947
- pkg.addRelationship('word/document.xml', {
1027
+ const sourcePart =
1028
+ rel.source === 'header'
1029
+ ? 'word/header1.xml'
1030
+ : rel.source === 'footer'
1031
+ ? 'word/footer1.xml'
1032
+ : 'word/document.xml';
1033
+ pkg.addRelationship(sourcePart, {
948
1034
  id: rel.id,
949
1035
  type: rel.type,
950
1036
  target: rel.target,
@@ -975,11 +1061,19 @@ async function buildDocxPackage(
975
1061
  // XML Part Generators
976
1062
  // ============================================
977
1063
 
978
- function buildDocumentXml(bodyXml: string, backgroundColor?: string): string {
1064
+ function buildDocumentXml(
1065
+ bodyXml: string,
1066
+ backgroundColor?: string,
1067
+ headerRelId?: string,
1068
+ footerRelId?: string,
1069
+ ): string {
979
1070
  // `<w:background>` must be the FIRST child of `<w:document>` per the
980
1071
  // WordprocessingML schema. Word also requires `<w:displayBackgroundShape/>`
981
1072
  // in settings.xml before the bg actually paints (see buildSettingsXml).
982
1073
  const backgroundEl = backgroundColor ? `<w:background w:color="${backgroundColor}"/>` : '';
1074
+ const storyReferences =
1075
+ (headerRelId ? `<w:headerReference w:type="default" r:id="${headerRelId}"/>` : '') +
1076
+ (footerRelId ? `<w:footerReference w:type="default" r:id="${footerRelId}"/>` : '');
983
1077
  return (
984
1078
  xmlDeclaration() +
985
1079
  `<w:document` +
@@ -997,6 +1091,7 @@ function buildDocumentXml(bodyXml: string, backgroundColor?: string): string {
997
1091
  `<w:body>` +
998
1092
  bodyXml +
999
1093
  `<w:sectPr>` +
1094
+ storyReferences +
1000
1095
  `<w:pgSz w:w="12240" w:h="15840"/>` +
1001
1096
  `<w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/>` +
1002
1097
  `<w:cols w:space="720"/>` +
@@ -1006,6 +1101,19 @@ function buildDocumentXml(bodyXml: string, backgroundColor?: string): string {
1006
1101
  );
1007
1102
  }
1008
1103
 
1104
+ function buildStoryXml(kind: 'header' | 'footer', bodyXml: string): string {
1105
+ const element = kind === 'header' ? 'hdr' : 'ftr';
1106
+ return (
1107
+ xmlDeclaration() +
1108
+ `<w:${element}` +
1109
+ ` xmlns:w="${NS_WML}"` +
1110
+ ` xmlns:r="${NS_R}"` +
1111
+ ` xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing">` +
1112
+ bodyXml +
1113
+ `</w:${element}>`
1114
+ );
1115
+ }
1116
+
1009
1117
  function buildStylesXml(options: DocxExportOptions, ctx: ExportContext): string {
1010
1118
  const font = ctx.font;
1011
1119
  const headingFont = ctx.headingFont;