docx-kit 0.1.0 → 0.2.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.
@@ -1,6 +1,6 @@
1
+ import { n as dataUrlToUint8Array, t as createImageRun } from "./image-CdfLh9GO.js";
1
2
  import { t as DocxKitError } from "./errors-c9CC63Ti.js";
2
- import { n as dataUrlToUint8Array, t as createImageRun } from "./image-6yS25Ggr.js";
3
- import { AlignmentType, Document, HeadingLevel, PageBreak, Paragraph, ShadingType, Table, TableCell, TableRow, TextRun, VerticalAlign, WidthType } from "docx";
3
+ import { AlignmentType, BorderStyle, Document, ExternalHyperlink, Footer, Header, HeadingLevel, HighlightColor, LevelFormat, PageBreak, Paragraph, ShadingType, Table, TableCell, TableRow, TextRun, VerticalAlign, WidthType } from "docx";
4
4
  //#region src/style/normalizeStyle.ts
5
5
  /**
6
6
  * Style normalization — merge multiple style sources into one resolved rule.
@@ -251,9 +251,40 @@ function toTwip(value) {
251
251
  * @module compiler/compileStyle
252
252
  */
253
253
  /**
254
+ * Compile all border properties from a `DocxStyleRule`.
255
+ *
256
+ * Returns `undefined` if no borders are specified.
257
+ *
258
+ * @param style - — The resolved style rule
259
+ * @returns Border options for `docx` or `undefined`
260
+ *
261
+ * @example
262
+ * ```ts
263
+ * compileBorder({ border: { style: 'single', color: '#333', width: '1pt' } })
264
+ * // => { top: {...}, right: {...}, bottom: {...}, left: {...} }
265
+ * ```
266
+ */
267
+ function compileBorder(style) {
268
+ const b = style.border;
269
+ const bt = style.borderTop ?? b;
270
+ const br = style.borderRight ?? b;
271
+ const bb = style.borderBottom ?? b;
272
+ const bl = style.borderLeft ?? b;
273
+ if (!bt && !br && !bb && !bl) return;
274
+ return {
275
+ bottom: bb ? compileSingleBorder(bb) : void 0,
276
+ left: bl ? compileSingleBorder(bl) : void 0,
277
+ right: br ? compileSingleBorder(br) : void 0,
278
+ top: bt ? compileSingleBorder(bt) : void 0
279
+ };
280
+ }
281
+ /**
254
282
  * Compile a `DocxStyleRule` into `docx` table cell options
255
283
  * (vertical alignment, margins, shading).
256
284
  *
285
+ * Applies sensible default cell padding (5 pt left/right, 2 pt top/bottom)
286
+ * when no margins are explicitly set.
287
+ *
257
288
  * @param style - — The resolved style rule
258
289
  * @returns Options object suitable for `new TableCell(...)`
259
290
  *
@@ -265,13 +296,23 @@ function toTwip(value) {
265
296
  */
266
297
  function compileCellStyle(style) {
267
298
  const shorthand = style.margin == null ? void 0 : parseShorthandTwip(style.margin);
299
+ const bottom = toTwip(style.marginBottom) ?? shorthand?.bottom;
300
+ const left = toTwip(style.marginLeft) ?? shorthand?.left;
301
+ const right = toTwip(style.marginRight) ?? shorthand?.right;
302
+ const top = toTwip(style.marginTop) ?? shorthand?.top;
303
+ const hasAnyMargin = bottom != null || left != null || right != null || top != null;
268
304
  return {
269
305
  verticalAlign: compileVerticalAlign(style.verticalAlign),
270
- margins: {
271
- bottom: toTwip(style.marginBottom) ?? shorthand?.bottom,
272
- left: toTwip(style.marginLeft) ?? shorthand?.left,
273
- right: toTwip(style.marginRight) ?? shorthand?.right,
274
- top: toTwip(style.marginTop) ?? shorthand?.top
306
+ margins: hasAnyMargin ? {
307
+ bottom: bottom ?? 0,
308
+ left: left ?? 0,
309
+ right: right ?? 0,
310
+ top: top ?? 0
311
+ } : {
312
+ bottom: 0,
313
+ left: 100,
314
+ right: 100,
315
+ top: 0
275
316
  },
276
317
  shading: style.backgroundColor == null ? void 0 : {
277
318
  fill: normalizeColor(style.backgroundColor),
@@ -300,6 +341,12 @@ function compileColumnWidth(width) {
300
341
  type: WidthType.PERCENTAGE
301
342
  };
302
343
  }
344
+ /** Compile paragraph-level borders (separate from cell borders). */
345
+ function compileParagraphBorder(style) {
346
+ const borders = compileBorder(style);
347
+ if (!borders) return {};
348
+ return { border: borders };
349
+ }
303
350
  /**
304
351
  * Compile a `DocxStyleRule` into `docx` paragraph-level options
305
352
  * (alignment, indent, spacing).
@@ -318,7 +365,11 @@ function compileParagraphStyle(style) {
318
365
  const indent = resolveIndent(style);
319
366
  return {
320
367
  alignment: compileAlignment(style.textAlign),
368
+ ...compileParagraphBorder(style),
321
369
  indent,
370
+ keepLines: style.keepLines,
371
+ keepNext: style.keepNext,
372
+ pageBreakBefore: style.pageBreakBefore,
322
373
  spacing
323
374
  };
324
375
  }
@@ -341,11 +392,16 @@ function compileTextStyle(style) {
341
392
  const result = {
342
393
  allCaps: style.allCaps,
343
394
  bold: style.fontWeight === "bold" || Number(style.fontWeight) >= 600,
395
+ characterSpacing: style.characterSpacing,
344
396
  color: normalizeColor(style.color),
345
397
  font: style.fontFamily,
398
+ highlight: compileHighlight(style.highlight),
346
399
  italics: style.fontStyle === "italic",
347
400
  size: toPtHalf(style.fontSize),
401
+ smallCaps: style.smallCaps,
348
402
  strike: style.strike,
403
+ subScript: style.subScript,
404
+ superScript: style.superScript,
349
405
  underline: style.underline ? {} : void 0
350
406
  };
351
407
  if (style.docx) Object.assign(result, style.docx);
@@ -360,12 +416,39 @@ function compileAlignment(value) {
360
416
  if (value === "justify") return AlignmentType.JUSTIFIED;
361
417
  return AlignmentType.LEFT;
362
418
  }
419
+ /**
420
+ * Map a CSS-like border-style string to a `docx` `BorderStyle` enum value.
421
+ */
422
+ function compileBorderStyle(style) {
423
+ if (style === "dashed") return BorderStyle.DASHED;
424
+ if (style === "dotted") return BorderStyle.DOTTED;
425
+ if (style === "double") return BorderStyle.DOUBLE;
426
+ if (style === "none") return BorderStyle.NONE;
427
+ return BorderStyle.SINGLE;
428
+ }
429
+ /**
430
+ * Map a highlight color name to `docx` `HighlightColor`.
431
+ */
432
+ function compileHighlight(color) {
433
+ if (!color || color === "none") return;
434
+ return HighlightColor[color.toUpperCase()];
435
+ }
363
436
  /** Convert line-height multiplier to twips. 1 → 240 (single-spacing). */
364
437
  function compileLineHeight(value) {
365
438
  if (typeof value === "number") return Math.round(value * 240);
366
439
  return toTwip(value);
367
440
  }
368
441
  /**
442
+ * Compile a single border side into `docx` border options.
443
+ */
444
+ function compileSingleBorder(rule) {
445
+ return {
446
+ color: normalizeColor(rule.color),
447
+ size: rule.width == null ? 1 : toTwip(rule.width),
448
+ style: compileBorderStyle(rule.style)
449
+ };
450
+ }
451
+ /**
369
452
  * Map the CSS-like vertical-align to a `docx` `VerticalAlign` enum value.
370
453
  */
371
454
  function compileVerticalAlign(value) {
@@ -449,9 +532,13 @@ const HEADING_MAP = {
449
532
  */
450
533
  async function compileNode(ctx) {
451
534
  switch (ctx.node.type) {
535
+ case "bulletList": return compileBulletList(ctx.node, ctx.config);
452
536
  case "heading": return compileHeading(ctx.node, ctx.config);
537
+ case "hyperlink": return compileHyperlink(ctx.node, ctx.config);
453
538
  case "image": return compileImage(ctx.node, ctx.config);
539
+ case "numberedList": return compileNumberedList(ctx.node, ctx.config);
454
540
  case "pageBreak": return new Paragraph({ children: [new PageBreak()] });
541
+ case "sectionBreak": throw new DocxKitError("UNKNOWN_NODE_TYPE", "Section break nodes must be handled at the document compilation level");
455
542
  case "paragraph": return compileParagraph(ctx.node, ctx.config);
456
543
  case "table": return compileTable(ctx.node, ctx.config);
457
544
  case "plugin": {
@@ -498,22 +585,58 @@ function compileHeading(node, config) {
498
585
  });
499
586
  }
500
587
  /**
588
+ * Compile a hyperlink node into an `ExternalHyperlink` containing `TextRun`s.
589
+ */
590
+ function compileHyperlink(node, config) {
591
+ const style = resolveStyle({
592
+ className: node.className,
593
+ inline: node.style,
594
+ styles: config.styles
595
+ });
596
+ return new Paragraph({ children: [new ExternalHyperlink({
597
+ children: node.children.map((child) => {
598
+ if (typeof child === "string") return new TextRun({
599
+ text: child,
600
+ ...compileTextStyle(style)
601
+ });
602
+ return new TextRun({
603
+ text: child.text,
604
+ ...compileTextStyle(resolveStyle({
605
+ base: style,
606
+ className: child.className,
607
+ inline: child.style,
608
+ styles: config.styles
609
+ }))
610
+ });
611
+ }),
612
+ link: node.url
613
+ })] });
614
+ }
615
+ /**
501
616
  * Compile an image node into a `docx` Paragraph containing an `ImageRun`.
502
617
  *
503
618
  * Handles image data normalization (Blob → Uint8Array), auto format detection,
504
619
  * floating layout, and size transformations.
505
620
  */
506
- async function compileImage(node, _config) {
621
+ async function compileImage(node, config) {
507
622
  if (node.data == null || typeof node.data === "string" && node.data.length === 0) throw new DocxKitError("IMAGE_INVALID_DATA", "Image data is empty or null");
508
623
  const data = await normalizeImageData(node.data);
509
624
  const imageType = node.imageType ?? "png";
510
- return new Paragraph({ children: [createImageRun({
511
- data,
512
- floating: compileFloating(node.floating),
513
- height: toPx(node.height),
514
- type: imageType,
515
- width: toPx(node.width)
516
- })] });
625
+ return new Paragraph({
626
+ ...compileParagraphStyle(resolveStyle({
627
+ base: config.defaults?.image,
628
+ className: node.className,
629
+ inline: node.style,
630
+ styles: config.styles
631
+ })),
632
+ children: [createImageRun({
633
+ data,
634
+ floating: compileFloating(node.floating),
635
+ height: toPx(node.height),
636
+ type: imageType,
637
+ width: toPx(node.width)
638
+ })]
639
+ });
517
640
  }
518
641
  /**
519
642
  * Compile a paragraph node into a `docx` Paragraph.
@@ -546,6 +669,104 @@ function compileParagraph(node, config) {
546
669
  children
547
670
  });
548
671
  }
672
+ const numberingConfigMap = /* @__PURE__ */ new Map();
673
+ let numberingCounter = 0;
674
+ /** Reset numbering state between compilations. */
675
+ function resetNumberingState() {
676
+ numberingConfigMap.clear();
677
+ numberingCounter = 0;
678
+ }
679
+ /**
680
+ * Generate a numbering config entry for a bullet list
681
+ * and return Paragraphs with numbering references.
682
+ */
683
+ function compileBulletList(node, config) {
684
+ const ref = `bullet-${++numberingCounter}`;
685
+ const bullet = node.bullet ?? "•";
686
+ numberingConfigMap.set(ref, {
687
+ reference: ref,
688
+ levels: [{
689
+ alignment: AlignmentType.LEFT,
690
+ format: LevelFormat.BULLET,
691
+ level: node.level ?? 0,
692
+ text: bullet
693
+ }]
694
+ });
695
+ const style = resolveStyle({
696
+ className: node.className,
697
+ inline: node.style,
698
+ styles: config.styles
699
+ });
700
+ return node.items.map((item) => {
701
+ const text = typeof item === "string" ? item : item.text;
702
+ const itemStyle = typeof item === "object" ? resolveStyle({
703
+ base: style,
704
+ className: item.className,
705
+ inline: item.style,
706
+ styles: config.styles
707
+ }) : style;
708
+ return new Paragraph({
709
+ ...compileParagraphStyle(itemStyle),
710
+ numbering: {
711
+ level: node.level ?? 0,
712
+ reference: ref
713
+ },
714
+ children: [new TextRun({
715
+ text,
716
+ ...compileTextStyle(itemStyle)
717
+ })]
718
+ });
719
+ });
720
+ }
721
+ /**
722
+ * Generate a numbering config entry for an ordered list
723
+ * and return Paragraphs with numbering references.
724
+ */
725
+ function compileNumberedList(node, config) {
726
+ const ref = `numbered-${++numberingCounter}`;
727
+ const formatMap = {
728
+ decimal: LevelFormat.DECIMAL,
729
+ lowerLetter: LevelFormat.LOWER_LETTER,
730
+ lowerRoman: LevelFormat.LOWER_ROMAN,
731
+ upperLetter: LevelFormat.UPPER_LETTER,
732
+ upperRoman: LevelFormat.UPPER_ROMAN
733
+ };
734
+ numberingConfigMap.set(ref, {
735
+ reference: ref,
736
+ levels: [{
737
+ alignment: AlignmentType.LEFT,
738
+ level: node.level ?? 0,
739
+ start: node.start ?? 1,
740
+ text: "%1.",
741
+ format: formatMap[node.numberingFormat ?? "decimal"] ?? LevelFormat.DECIMAL
742
+ }]
743
+ });
744
+ const style = resolveStyle({
745
+ className: node.className,
746
+ inline: node.style,
747
+ styles: config.styles
748
+ });
749
+ return node.items.map((item) => {
750
+ const text = typeof item === "string" ? item : item.text;
751
+ const itemStyle = typeof item === "object" ? resolveStyle({
752
+ base: style,
753
+ className: item.className,
754
+ inline: item.style,
755
+ styles: config.styles
756
+ }) : style;
757
+ return new Paragraph({
758
+ ...compileParagraphStyle(itemStyle),
759
+ numbering: {
760
+ level: node.level ?? 0,
761
+ reference: ref
762
+ },
763
+ children: [new TextRun({
764
+ text,
765
+ ...compileTextStyle(itemStyle)
766
+ })]
767
+ });
768
+ });
769
+ }
549
770
  /**
550
771
  * Compile a table node into a `docx` Table.
551
772
  *
@@ -560,15 +781,24 @@ function compileTable(node, _config) {
560
781
  children: node.columns.map((col) => new TableCell({
561
782
  ...compileCellStyle(node.headerCellStyle ?? {}),
562
783
  children: [new Paragraph(String(col.title))],
784
+ ...col.colSpan && col.colSpan > 1 ? { columnSpan: col.colSpan } : {},
563
785
  width: compileColumnWidth(col.width)
564
786
  }))
565
787
  }));
566
788
  rows.push(...node.data.map((row, rowIndex) => new TableRow({ children: node.columns.map((col) => {
567
789
  const raw = row[col.key];
568
790
  const rendered = col.render ? col.render(raw, row, rowIndex) : String(raw ?? "");
791
+ const cellColSpan = row[`_${col.key}_colSpan`] ?? col.colSpan;
792
+ const isEvenRow = rowIndex % 2 === 1;
793
+ const baseCellStyle = node.cellStyle ? { ...compileCellStyle(node.cellStyle) } : {};
569
794
  return new TableCell({
570
- ...compileCellStyle(node.cellStyle ?? {}),
795
+ ...baseCellStyle,
796
+ ...node.striped && isEvenRow && baseCellStyle.shading ? { shading: {
797
+ ...baseCellStyle.shading,
798
+ fill: "F2F2F2"
799
+ } } : {},
571
800
  children: [new Paragraph(String(rendered))],
801
+ ...cellColSpan && cellColSpan > 1 ? { columnSpan: cellColSpan } : {},
572
802
  width: compileColumnWidth(col.width)
573
803
  });
574
804
  }) })));
@@ -616,7 +846,10 @@ async function normalizeImageData(data) {
616
846
  *
617
847
  * This is the top-level compilation entry point. It iterates over all block
618
848
  * nodes, compiles each one via {@link compileNode}, and wraps them in a
619
- * single-section `Document` with page properties and metadata.
849
+ * `Document` with page properties and metadata.
850
+ *
851
+ * Multi-section support: {@link SectionBreakNode} markers split content
852
+ * into separate sections, each with its own page setup, headers, and footers.
620
853
  *
621
854
  * @module compiler/compileDocument
622
855
  */
@@ -626,6 +859,9 @@ async function normalizeImageData(data) {
626
859
  * This is the primary compilation pipeline — nodes flow through
627
860
  * `compileNode` → individual sub-compilers → `docx` objects → `Document`.
628
861
  *
862
+ * If `SectionBreakNode` markers are present, content is split into
863
+ * multiple sections, each with its own page setup, headers, and footers.
864
+ *
629
865
  * @param options - — Compilation options with config, nodes, and plugins
630
866
  * @returns A `docx` `Document` ready for packaging
631
867
  *
@@ -642,16 +878,35 @@ async function normalizeImageData(data) {
642
878
  * ```
643
879
  */
644
880
  async function compileDocument(options) {
645
- const children = [];
646
- for (const node of options.nodes) {
647
- const compiled = await compileNode({
648
- config: options.config,
649
- node,
650
- plugins: options.plugins
881
+ resetNumberingState();
882
+ const sectionGroups = [{ nodes: [] }];
883
+ for (const node of options.nodes) if (node.type === "sectionBreak") sectionGroups.push({
884
+ config: node.config,
885
+ nodes: []
886
+ });
887
+ else sectionGroups[sectionGroups.length - 1].nodes.push(node);
888
+ const sections = [];
889
+ for (const [i, group] of sectionGroups.entries()) {
890
+ const children = [];
891
+ for (const node of group.nodes) {
892
+ const compiled = await compileNode({
893
+ config: options.config,
894
+ node,
895
+ plugins: options.plugins
896
+ });
897
+ if (Array.isArray(compiled)) children.push(...compiled);
898
+ else children.push(compiled);
899
+ }
900
+ const sectionConfig = group.config;
901
+ const pageConfig = i === 0 ? options.config : mergePageConfig(options.config, sectionConfig);
902
+ sections.push({
903
+ ...compileSectionProperties(pageConfig),
904
+ children,
905
+ footers: compileFooters(i === 0 ? void 0 : sectionConfig?.footer, options.config),
906
+ headers: compileHeaders(i === 0 ? void 0 : sectionConfig?.header, options.config)
651
907
  });
652
- if (Array.isArray(compiled)) children.push(...compiled);
653
- else children.push(compiled);
654
908
  }
909
+ const numberingConfig = numberingConfigMap.size > 0 ? Array.from(numberingConfigMap.values()) : void 0;
655
910
  return new Document({
656
911
  creator: options.config.metadata?.creator,
657
912
  description: options.config.metadata?.description,
@@ -659,13 +914,43 @@ async function compileDocument(options) {
659
914
  lastModifiedBy: options.config.metadata?.lastModifiedBy,
660
915
  subject: options.config.metadata?.subject,
661
916
  title: options.config.metadata?.title,
662
- sections: [{
663
- ...compileSectionProperties(options.config),
664
- children
665
- }]
917
+ ...numberingConfig ? { numbering: { config: numberingConfig } } : {},
918
+ sections
666
919
  });
667
920
  }
668
921
  /**
922
+ * Compile section footers into `docx` `Footer` objects.
923
+ *
924
+ * @returns `{ default?, first?, even? }` or `undefined` if no footers defined
925
+ */
926
+ function compileFooters(config, _docConfig) {
927
+ if (!config) return;
928
+ const result = {};
929
+ if (config.default) result.default = new Footer({ children: compileHeaderFooterChildren(config.default) });
930
+ if (config.first) result.first = new Footer({ children: compileHeaderFooterChildren(config.first) });
931
+ if (config.even) result.even = new Footer({ children: compileHeaderFooterChildren(config.even) });
932
+ return Object.keys(result).length > 0 ? result : void 0;
933
+ }
934
+ /**
935
+ * Compile a `HeaderFooterContent` into a list of `docx` `Paragraph`s.
936
+ */
937
+ function compileHeaderFooterChildren(content) {
938
+ return content.children.map((text) => new Paragraph(text));
939
+ }
940
+ /**
941
+ * Compile section headers into `docx` `Header` objects.
942
+ *
943
+ * @returns `{ default?, first?, even? }` or `undefined` if no headers defined
944
+ */
945
+ function compileHeaders(config, _docConfig) {
946
+ if (!config) return;
947
+ const result = {};
948
+ if (config.default) result.default = new Header({ children: compileHeaderFooterChildren(config.default) });
949
+ if (config.first) result.first = new Header({ children: compileHeaderFooterChildren(config.first) });
950
+ if (config.even) result.even = new Header({ children: compileHeaderFooterChildren(config.even) });
951
+ return Object.keys(result).length > 0 ? result : void 0;
952
+ }
953
+ /**
669
954
  * Compile a single page margin value (shorthand or explicit) into twips.
670
955
  */
671
956
  function compilePageMargin(margin) {
@@ -689,6 +974,22 @@ function compileSectionProperties(config) {
689
974
  } } };
690
975
  }
691
976
  /**
977
+ * Merge document-level page config with per-section overrides.
978
+ *
979
+ * Section config wins when both are specified; falls back to document
980
+ * defaults for any property not set in the section.
981
+ */
982
+ function mergePageConfig(docConfig, sectionConfig) {
983
+ if (!sectionConfig?.page) return docConfig;
984
+ return {
985
+ ...docConfig,
986
+ page: {
987
+ ...docConfig.page,
988
+ ...sectionConfig.page
989
+ }
990
+ };
991
+ }
992
+ /**
692
993
  * Page size presets in twips (width × height).
693
994
  *
694
995
  * Values sourced from the OOXML specification.
@@ -0,0 +1,63 @@
1
+ //#region src/errors.ts
2
+ /**
3
+ * Error handling types and classes for docx-kit.
4
+ *
5
+ * @module errors
6
+ */
7
+ /**
8
+ * Well-known error codes used throughout the library.
9
+ *
10
+ * Consumers can match against these codes for structured error handling.
11
+ */
12
+ const ERROR_CODES = {
13
+ /** Document export failed. */
14
+ EXPORT_FAILED: "EXPORT_FAILED",
15
+ /** Image data is empty, corrupt, or unsupported. */
16
+ IMAGE_INVALID_DATA: "IMAGE_INVALID_DATA",
17
+ /** A plugin node referenced an unregistered plugin. */
18
+ PLUGIN_NOT_REGISTERED: "PLUGIN_NOT_REGISTERED",
19
+ /** A plugin's `render()` method threw an error. */
20
+ PLUGIN_RENDER_FAILED: "PLUGIN_RENDER_FAILED",
21
+ /** A `className` referenced a stylesheet key that doesn't exist. */
22
+ STYLE_UNKNOWN_CLASS: "STYLE_UNKNOWN_CLASS",
23
+ /** Table was created with no columns. */
24
+ TABLE_INVALID_COLUMNS: "TABLE_INVALID_COLUMNS",
25
+ /** Encountered a node type that has no registered compiler. */
26
+ UNKNOWN_NODE_TYPE: "UNKNOWN_NODE_TYPE"
27
+ };
28
+ /**
29
+ * Structured error class for docx-kit.
30
+ *
31
+ * Carries a machine-readable `code`, a human-readable `message`,
32
+ * and optionally the underlying `cause`.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * try {
37
+ * await doc.save('output.docx')
38
+ * } catch (err) {
39
+ * if (err instanceof DocxKitError && err.code === ERROR_CODES.EXPORT_FAILED) {
40
+ * console.error('Export failed:', err.message)
41
+ * }
42
+ * }
43
+ * ```
44
+ */
45
+ var DocxKitError = class extends Error {
46
+ /** The underlying error that caused this failure (if any). */
47
+ cause;
48
+ /** Machine-readable error code. */
49
+ code;
50
+ /**
51
+ * @param code - — Known error code from {@link ERROR_CODES}
52
+ * @param message - — Human-readable description
53
+ * @param cause - — Optional underlying error
54
+ */
55
+ constructor(code, message, cause) {
56
+ super(message);
57
+ this.name = "DocxKitError";
58
+ this.code = code;
59
+ this.cause = cause;
60
+ }
61
+ };
62
+ //#endregion
63
+ export { ERROR_CODES as n, DocxKitError as t };
@@ -0,0 +1,30 @@
1
+ import { Packer } from "docx";
2
+ //#region src/node/fs.ts
3
+ /**
4
+ * Node.js filesystem utilities for saving documents to disk.
5
+ *
6
+ * @module node/fs
7
+ */
8
+ /**
9
+ * Save a compiled document to a file on disk.
10
+ *
11
+ * Uses `Packer.toBuffer()` to produce the .docx binary, then
12
+ * writes via `node:fs/promises`. This function is **Node.js only**.
13
+ *
14
+ * @param doc - — A compiled `docx` `Document` instance
15
+ * @param filename - — Output file path (e.g. `"report.docx"`)
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { saveDocument } from 'docx-kit/node'
20
+ *
21
+ * const doc = await compileDocument({ ... })
22
+ * await saveDocument(doc, 'report.docx')
23
+ * ```
24
+ */
25
+ async function saveDocument(doc, filename) {
26
+ const { writeFile } = await import("node:fs/promises");
27
+ await writeFile(filename, await Packer.toBuffer(doc));
28
+ }
29
+ //#endregion
30
+ export { saveDocument };
@@ -46,8 +46,8 @@ async function dataUrlToUint8Array(dataUrl) {
46
46
  /**
47
47
  * Shared image utilities for internal use by compilers and plugins.
48
48
  *
49
- * Centralizes the `as any` casts required by the `docx` library's
50
- * strict `ImageRun` constructor types into one audited location.
49
+ * Bridges the `docx` library's `IImageOptions` with `docx-kit`'s
50
+ * own `CreateImageRunOptions`, confining type casts to this single module.
51
51
  *
52
52
  * @module utils/image
53
53
  */
@@ -56,7 +56,7 @@ async function dataUrlToUint8Array(dataUrl) {
56
56
  *
57
57
  * All paths that construct an `ImageRun` should use this factory
58
58
  * instead of calling `new ImageRun(...)` directly, ensuring that
59
- * all `as any` casts are confined to a single audited function.
59
+ * type casts are confined to a single audited function.
60
60
  *
61
61
  * @param options - — Image data and layout options
62
62
  * @returns A configured `ImageRun` instance
@@ -0,0 +1,76 @@
1
+ import { ImageRun } from "docx";
2
+ //#region src/utils/dataUrl.ts
3
+ /**
4
+ * Cross-platform base64 data-URL decoder.
5
+ *
6
+ * Auto-detects the runtime environment and uses the appropriate
7
+ * implementation:
8
+ * - **Node.js**: `Buffer.from(base64, 'base64')`
9
+ * - **Browser**: `atob(base64)` + manual byte population
10
+ *
11
+ * This is the internal shared version used by the compiler and
12
+ * built-in plugins. For platform-specific direct access, import
13
+ * from `'docx-kit/node'` or `'docx-kit/browser'`.
14
+ *
15
+ * @module utils/dataUrl
16
+ */
17
+ /**
18
+ * Decode a base64 data-URL to raw bytes (works in both browser & Node.js).
19
+ *
20
+ * Strips the `"data:*;base64,"` prefix and decodes using the appropriate
21
+ * runtime API.
22
+ *
23
+ * @param dataUrl - — A base64 data-URI string (e.g. `"data:image/png;base64,iVBO..."`)
24
+ * @returns Raw bytes as `Uint8Array`
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * import { dataUrlToUint8Array } from 'docx-kit'
29
+ *
30
+ * const bytes = await dataUrlToUint8Array('data:image/png;base64,iVBORw...')
31
+ * ```
32
+ */
33
+ async function dataUrlToUint8Array(dataUrl) {
34
+ const base64 = dataUrl.split(",")[1];
35
+ if (typeof atob === "function") {
36
+ const binary = atob(base64);
37
+ const bytes = new Uint8Array(binary.length);
38
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
39
+ return bytes;
40
+ }
41
+ const { Buffer } = await import("node:buffer");
42
+ return new Uint8Array(Buffer.from(base64, "base64"));
43
+ }
44
+ //#endregion
45
+ //#region src/utils/image.ts
46
+ /**
47
+ * Shared image utilities for internal use by compilers and plugins.
48
+ *
49
+ * Bridges the `docx` library's `IImageOptions` with `docx-kit`'s
50
+ * own `CreateImageRunOptions`, confining type casts to this single module.
51
+ *
52
+ * @module utils/image
53
+ */
54
+ /**
55
+ * Create an `ImageRun` with type-safe defaults.
56
+ *
57
+ * All paths that construct an `ImageRun` should use this factory
58
+ * instead of calling `new ImageRun(...)` directly, ensuring that
59
+ * type casts are confined to a single audited function.
60
+ *
61
+ * @param options - — Image data and layout options
62
+ * @returns A configured `ImageRun` instance
63
+ */
64
+ function createImageRun(options) {
65
+ return new ImageRun({
66
+ data: options.data,
67
+ floating: options.floating,
68
+ type: options.type,
69
+ transformation: {
70
+ height: options.height ?? 180,
71
+ width: options.width ?? 300
72
+ }
73
+ });
74
+ }
75
+ //#endregion
76
+ export { dataUrlToUint8Array as n, createImageRun as t };