@json-to-office/core-docx 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,113 +1,15 @@
1
- import { type TSchema, type Static } from '@sinclair/typebox';
1
+ import type { TSchema } from '@sinclair/typebox';
2
2
  import type { ComponentDefinition } from '@json-to-office/shared-docx';
3
- import type { AddWarningFunction } from '@json-to-office/shared-docx';
4
3
  import type { ThemeConfig } from '../styles';
4
+ import type { RenderContext as SharedRenderContext, RenderFunction as SharedRenderFunction, ComponentVersion as SharedComponentVersion, ComponentVersionMap as SharedComponentVersionMap, CustomComponent as SharedCustomComponent } from '@json-to-office/shared/plugin';
5
5
  /**
6
- * Render function context - parameters passed to render
7
- */
8
- export interface RenderContext<T> {
9
- /** The validated props for this component */
10
- props: T;
11
- /** The resolved theme configuration */
12
- theme: ThemeConfig;
13
- /** Function to add warnings during processing */
14
- addWarning: AddWarningFunction;
15
- /** For container components: processed nested children */
16
- children?: unknown[];
17
- }
18
- /**
19
- * Render function that transforms custom component props into standard components.
20
- */
21
- export type RenderFunction<TProps, TComponentDefinition = ComponentDefinition> = (context: RenderContext<TProps>) => Promise<TComponentDefinition[]>;
22
- /**
23
- * A single version entry within a versioned component.
24
- * Each version is self-contained with its own schema, render, and metadata.
25
- */
26
- export interface ComponentVersion<TComponentDefinition = ComponentDefinition, TPropsSchema extends TSchema = TSchema> {
27
- /** TypeBox schema for props validation */
28
- propsSchema: TPropsSchema;
29
- /** Async render function to transform props into standard components */
30
- render: RenderFunction<Static<TPropsSchema>, TComponentDefinition>;
31
- /** Whether this version supports nested children */
32
- hasChildren?: boolean;
33
- /** Optional description for this version */
34
- description?: string;
35
- }
36
- /**
37
- * Map of semver version strings to their version definitions.
38
- */
39
- export type ComponentVersionMap<TComponentDefinition = ComponentDefinition> = Record<string, ComponentVersion<TComponentDefinition, any>>;
40
- /**
41
- * Custom component definition with multiple semver-keyed versions.
42
- * Each version is self-contained with its own schema + render.
43
- *
44
- * @typeParam TComponentDefinition - The component definition type for render return
45
- * @typeParam TVersions - The versions map type
46
- * @typeParam TName - The literal string type for the component name
47
- */
48
- export interface CustomComponent<TComponentDefinition = ComponentDefinition, TVersions extends ComponentVersionMap<TComponentDefinition> = ComponentVersionMap<TComponentDefinition>, TName extends string = string> {
49
- /** Unique name for the component type */
50
- name: TName;
51
- /** Map of semver version strings to version definitions */
52
- versions: TVersions;
53
- }
54
- /**
55
- * Create a single version entry with full type inference for props.
56
- *
57
- * Wrapping each version with `createVersion` lets TypeScript infer the
58
- * props type from `propsSchema` and propagate it to the `render` function,
59
- * so `props` is fully typed instead of `any`.
60
- *
61
- * @example
62
- * ```typescript
63
- * createVersion({
64
- * propsSchema: Type.Object({ city: Type.String() }),
65
- * render: async ({ props }) => {
66
- * // props.city is inferred as string
67
- * return [{ name: 'paragraph', props: { text: props.city } }];
68
- * },
69
- * })
70
- * ```
6
+ * DOCX render context with ThemeConfig baked in
71
7
  */
8
+ export type RenderContext<T> = SharedRenderContext<T, ThemeConfig>;
9
+ export type RenderFunction<TProps, TComponentDefinition = ComponentDefinition> = SharedRenderFunction<TProps, TComponentDefinition, ThemeConfig>;
10
+ export type ComponentVersion<TComponentDefinition = ComponentDefinition, TPropsSchema extends TSchema = TSchema> = SharedComponentVersion<TComponentDefinition, TPropsSchema, ThemeConfig>;
11
+ export type ComponentVersionMap<TComponentDefinition = ComponentDefinition> = SharedComponentVersionMap<TComponentDefinition, ThemeConfig>;
12
+ export type CustomComponent<TComponentDefinition = ComponentDefinition, TVersions extends ComponentVersionMap<TComponentDefinition> = ComponentVersionMap<TComponentDefinition>, TName extends string = string> = SharedCustomComponent<TComponentDefinition, TVersions, TName>;
72
13
  export declare function createVersion<TPropsSchema extends TSchema, TComponentDefinition = ComponentDefinition>(version: ComponentVersion<TComponentDefinition, TPropsSchema>): ComponentVersion<TComponentDefinition, TPropsSchema>;
73
- /**
74
- * Create a custom component with multiple semver-keyed versions.
75
- *
76
- * Use {@link createVersion} for each version entry to get full type inference
77
- * for `props` in render functions.
78
- *
79
- * @example Basic single-version usage
80
- * ```typescript
81
- * const weatherComponent = createComponent({
82
- * name: 'weather' as const,
83
- * versions: {
84
- * '1.0.0': createVersion({
85
- * propsSchema: Type.Object({ city: Type.String() }),
86
- * render: async ({ props }) => [{
87
- * name: 'paragraph',
88
- * props: { text: `Weather in ${props.city}` }
89
- * }]
90
- * })
91
- * }
92
- * });
93
- * ```
94
- *
95
- * @example Multi-version with breaking changes
96
- * ```typescript
97
- * const weatherComponent = createComponent({
98
- * name: 'weather' as const,
99
- * versions: {
100
- * '1.0.0': createVersion({
101
- * propsSchema: WeatherV1PropsSchema,
102
- * render: async ({ props }) => [...],
103
- * }),
104
- * '2.0.0': createVersion({
105
- * propsSchema: WeatherV2PropsSchema,
106
- * render: async ({ props }) => [...],
107
- * }),
108
- * },
109
- * });
110
- * ```
111
- */
112
14
  export declare function createComponent<TComponentDefinition = ComponentDefinition, TVersions extends ComponentVersionMap<TComponentDefinition> = ComponentVersionMap<TComponentDefinition>, TName extends string = string>(component: CustomComponent<TComponentDefinition, TVersions, TName>): CustomComponent<TComponentDefinition, TVersions, TName>;
113
15
  //# sourceMappingURL=createComponent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createComponent.d.ts","sourceRoot":"","sources":["../../src/plugin/createComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,6CAA6C;IAC7C,KAAK,EAAE,CAAC,CAAC;IACT,uCAAuC;IACvC,KAAK,EAAE,WAAW,CAAC;IACnB,iDAAiD;IACjD,UAAU,EAAE,kBAAkB,CAAC;IAC/B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,CACxB,MAAM,EACN,oBAAoB,GAAG,mBAAmB,IACxC,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;AAExE;;;GAGG;AACH,MAAM,WAAW,gBAAgB,CAC/B,oBAAoB,GAAG,mBAAmB,EAC1C,YAAY,SAAS,OAAO,GAAG,OAAO;IAEtC,0CAA0C;IAC1C,WAAW,EAAE,YAAY,CAAC;IAC1B,wEAAwE;IACxE,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,oBAAoB,CAAC,CAAC;IACnE,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,CAAC,oBAAoB,GAAG,mBAAmB,IACxE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC,CAAC;AAE9D;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe,CAC9B,oBAAoB,GAAG,mBAAmB,EAC1C,SAAS,SACP,mBAAmB,CAAC,oBAAoB,CAAC,GAAG,mBAAmB,CAAC,oBAAoB,CAAC,EACvF,KAAK,SAAS,MAAM,GAAG,MAAM;IAE7B,yCAAyC;IACzC,IAAI,EAAE,KAAK,CAAC;IACZ,2DAA2D;IAC3D,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAC3B,YAAY,SAAS,OAAO,EAC5B,oBAAoB,GAAG,mBAAmB,EAE1C,OAAO,EAAE,gBAAgB,CAAC,oBAAoB,EAAE,YAAY,CAAC,GAC5D,gBAAgB,CAAC,oBAAoB,EAAE,YAAY,CAAC,CAEtD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,eAAe,CAC7B,oBAAoB,GAAG,mBAAmB,EAC1C,SAAS,SACP,mBAAmB,CAAC,oBAAoB,CAAC,GAAG,mBAAmB,CAAC,oBAAoB,CAAC,EACvF,KAAK,SAAS,MAAM,GAAG,MAAM,EAE7B,SAAS,EAAE,eAAe,CAAC,oBAAoB,EAAE,SAAS,EAAE,KAAK,CAAC,GACjE,eAAe,CAAC,oBAAoB,EAAE,SAAS,EAAE,KAAK,CAAC,CAoCzD"}
1
+ {"version":3,"file":"createComponent.d.ts","sourceRoot":"","sources":["../../src/plugin/createComponent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAK7C,OAAO,KAAK,EACV,aAAa,IAAI,mBAAmB,EACpC,cAAc,IAAI,oBAAoB,EACtC,gBAAgB,IAAI,sBAAsB,EAC1C,mBAAmB,IAAI,yBAAyB,EAChD,eAAe,IAAI,qBAAqB,EACzC,MAAM,+BAA+B,CAAC;AAIvC;;GAEG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,mBAAmB,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;AAEnE,MAAM,MAAM,cAAc,CACxB,MAAM,EACN,oBAAoB,GAAG,mBAAmB,IACxC,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,EAAE,WAAW,CAAC,CAAC;AAEpE,MAAM,MAAM,gBAAgB,CAC1B,oBAAoB,GAAG,mBAAmB,EAC1C,YAAY,SAAS,OAAO,GAAG,OAAO,IACpC,sBAAsB,CAAC,oBAAoB,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;AAE5E,MAAM,MAAM,mBAAmB,CAAC,oBAAoB,GAAG,mBAAmB,IACxE,yBAAyB,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;AAE/D,MAAM,MAAM,eAAe,CACzB,oBAAoB,GAAG,mBAAmB,EAC1C,SAAS,SACP,mBAAmB,CAAC,oBAAoB,CAAC,GAAG,mBAAmB,CAAC,oBAAoB,CAAC,EACvF,KAAK,SAAS,MAAM,GAAG,MAAM,IAC3B,qBAAqB,CAAC,oBAAoB,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;AAIlE,wBAAgB,aAAa,CAC3B,YAAY,SAAS,OAAO,EAC5B,oBAAoB,GAAG,mBAAmB,EAE1C,OAAO,EAAE,gBAAgB,CAAC,oBAAoB,EAAE,YAAY,CAAC,GAC5D,gBAAgB,CAAC,oBAAoB,EAAE,YAAY,CAAC,CAEtD;AAED,wBAAgB,eAAe,CAC7B,oBAAoB,GAAG,mBAAmB,EAC1C,SAAS,SACP,mBAAmB,CAAC,oBAAoB,CAAC,GAAG,mBAAmB,CAAC,oBAAoB,CAAC,EACvF,KAAK,SAAS,MAAM,GAAG,MAAM,EAE7B,SAAS,EAAE,eAAe,CAAC,oBAAoB,EAAE,SAAS,EAAE,KAAK,CAAC,GACjE,eAAe,CAAC,oBAAoB,EAAE,SAAS,EAAE,KAAK,CAAC,CAEzD"}
@@ -1,4 +1,4 @@
1
- import type { ThemeConfig } from '../styles';
1
+ import { type ThemeConfig } from '../styles';
2
2
  import type { DocumentGeneratorBuilder } from './types';
3
3
  /**
4
4
  * Options for creating a document generator
@@ -6,6 +6,8 @@ import type { DocumentGeneratorBuilder } from './types';
6
6
  export interface DocumentGeneratorOptions {
7
7
  /** Theme configuration */
8
8
  theme: ThemeConfig;
9
+ /** Custom themes keyed by name, resolved per-document via document.props.theme */
10
+ customThemes?: Record<string, ThemeConfig>;
9
11
  /** Enable caching for better performance */
10
12
  enableCache?: boolean;
11
13
  /** Enable debug logging */
@@ -1 +1 @@
1
- {"version":3,"file":"createDocumentGenerator.d.ts","sourceRoot":"","sources":["../../src/plugin/createDocumentGenerator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,KAAK,EAEV,wBAAwB,EAKzB,MAAM,SAAS,CAAC;AAgBjB;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,0BAA0B;IAC1B,KAAK,EAAE,WAAW,CAAC;IACnB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAoZD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,wBAAwB,GAChC,wBAAwB,CAAC,SAAS,EAAE,CAAC,CAUvC"}
1
+ {"version":3,"file":"createDocumentGenerator.d.ts","sourceRoot":"","sources":["../../src/plugin/createDocumentGenerator.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAwB,MAAM,WAAW,CAAC;AAEnE,OAAO,KAAK,EAEV,wBAAwB,EAKzB,MAAM,SAAS,CAAC;AAcjB;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,0BAA0B;IAC1B,KAAK,EAAE,WAAW,CAAC;IACnB,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC3C,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA2bD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,wBAAwB,GAChC,wBAAwB,CAAC,SAAS,EAAE,CAAC,CAWvC"}
@@ -678,7 +678,7 @@ function loadThemesFromJson() {
678
678
  _themesCache = themes2;
679
679
  return themes2;
680
680
  }
681
- var _themesCache, themes, getTheme, minimalTheme, corporateTheme, modernTheme;
681
+ var _themesCache, themes, getTheme, getThemeWithFallback, minimalTheme, corporateTheme, modernTheme;
682
682
  var init_themes = __esm({
683
683
  "src/templates/themes/index.ts"() {
684
684
  "use strict";
@@ -691,6 +691,15 @@ var init_themes = __esm({
691
691
  getTheme = (themeName) => {
692
692
  return themes[themeName];
693
693
  };
694
+ getThemeWithFallback = (themeName, fallbackTheme = "minimal") => {
695
+ const theme = getTheme(themeName) || getTheme(fallbackTheme);
696
+ if (!theme) {
697
+ throw new Error(
698
+ `Failed to load theme: ${themeName}. Fallback theme '${fallbackTheme}' also not found.`
699
+ );
700
+ }
701
+ return theme;
702
+ };
694
703
  minimalTheme = themes["minimal"];
695
704
  corporateTheme = themes["corporate"];
696
705
  modernTheme = themes["modern"];
@@ -1333,7 +1342,6 @@ var init_styles = __esm({
1333
1342
  // src/utils/widthUtils.ts
1334
1343
  var widthUtils_exports = {};
1335
1344
  __export(widthUtils_exports, {
1336
- __widthUtils: () => __widthUtils,
1337
1345
  getAvailableHeightTwips: () => getAvailableHeightTwips,
1338
1346
  getAvailableWidthTwips: () => getAvailableWidthTwips,
1339
1347
  getPageHeightTwips: () => getPageHeightTwips,
@@ -1378,13 +1386,11 @@ function relativeLengthToTwips(value, availableWidthTwips) {
1378
1386
  if (fraction === void 0) return 0;
1379
1387
  return Math.round(availableWidthTwips * fraction);
1380
1388
  }
1381
- var __widthUtils;
1382
1389
  var init_widthUtils = __esm({
1383
1390
  "src/utils/widthUtils.ts"() {
1384
1391
  "use strict";
1385
1392
  init_styles();
1386
1393
  init_styleHelpers();
1387
- __widthUtils = true;
1388
1394
  }
1389
1395
  });
1390
1396
 
@@ -2036,62 +2042,29 @@ var init_numberingConfig = __esm({
2036
2042
  import { Packer as Packer2 } from "docx";
2037
2043
 
2038
2044
  // src/plugin/createDocumentGenerator.ts
2045
+ init_styles();
2039
2046
  import { Packer } from "docx";
2040
2047
 
2041
2048
  // src/plugin/version-resolver.ts
2042
- import { latestVersion } from "@json-to-office/shared-docx";
2043
- function resolveComponentVersion(componentName, versions, requestedVersion) {
2044
- const versionKeys = Object.keys(versions);
2045
- if (requestedVersion) {
2046
- const entry = versions[requestedVersion];
2047
- if (!entry) {
2048
- throw new Error(
2049
- `Component "${componentName}" does not have version "${requestedVersion}". Available versions: ${versionKeys.join(", ")}`
2050
- );
2051
- }
2052
- return entry;
2053
- }
2054
- const latest = latestVersion(versionKeys);
2055
- return versions[latest];
2056
- }
2049
+ import { resolveComponentVersion } from "@json-to-office/shared/plugin";
2057
2050
 
2058
2051
  // src/plugin/validation.ts
2059
2052
  import {
2060
2053
  validateCustomComponentProps,
2054
+ ComponentValidationError
2055
+ } from "@json-to-office/shared/plugin";
2056
+ import {
2061
2057
  validateDocument as validateDocumentUnified
2062
2058
  } from "@json-to-office/shared-docx/validation/unified";
2063
- var DuplicateComponentError = class _DuplicateComponentError extends Error {
2064
- componentName;
2065
- code = "DUPLICATE_COMPONENT";
2066
- constructor(componentName) {
2067
- super(
2068
- `Cannot register component "${componentName}": a component with this name is already registered. Component names must be unique within a document generator.`
2069
- );
2070
- this.name = "DuplicateComponentError";
2071
- this.componentName = componentName;
2072
- if (Error.captureStackTrace) {
2073
- Error.captureStackTrace(this, _DuplicateComponentError);
2074
- }
2075
- }
2076
- };
2077
- var ComponentValidationError = class extends Error {
2078
- errors;
2079
- props;
2080
- constructor(errors, props) {
2081
- const propsStr = props !== void 0 ? `
2082
- Props: ${JSON.stringify(props, null, 2)}` : "";
2083
- const message = `Document validation failed:
2084
- ${errors.map((e) => ` ${e.path}: ${e.message}`).join("\n")}${propsStr}`;
2085
- super(message);
2086
- this.name = "ComponentValidationError";
2087
- this.errors = errors;
2088
- this.props = props;
2089
- }
2090
- };
2091
- function validateComponentProps(schema, props) {
2059
+ import {
2060
+ DuplicateComponentError,
2061
+ ComponentValidationError as ComponentValidationError2
2062
+ } from "@json-to-office/shared/plugin";
2063
+ function validateComponentProps(schema, props, componentName) {
2092
2064
  return validateCustomComponentProps(schema.propsSchema, props, {
2093
2065
  clean: true,
2094
- applyDefaults: true
2066
+ applyDefaults: true,
2067
+ componentName
2095
2068
  });
2096
2069
  }
2097
2070
  function validateDocument(document, customComponents) {
@@ -2118,7 +2091,8 @@ function validateDocument(document, customComponents) {
2118
2091
  );
2119
2092
  const validation = validateComponentProps(
2120
2093
  versionEntry,
2121
- componentData.props
2094
+ componentData.props,
2095
+ customComponent.name
2122
2096
  );
2123
2097
  if (!validation.valid && validation.errors) {
2124
2098
  const indexedErrors = validation.errors.map(
@@ -2153,7 +2127,7 @@ var cleanComponentProps = getValidatedProps;
2153
2127
 
2154
2128
  // src/plugin/schema.ts
2155
2129
  import { Type } from "@sinclair/typebox";
2156
- import { latestVersion as latestVersion2 } from "@json-to-office/shared-docx";
2130
+ import { latestVersion } from "@json-to-office/shared-docx";
2157
2131
  import {
2158
2132
  ReportPropsSchema,
2159
2133
  SectionPropsSchema,
@@ -5070,15 +5044,21 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5070
5044
  let columnWidths;
5071
5045
  let tableWidth;
5072
5046
  if (hasExplicitWidths) {
5073
- const { getAvailableWidthTwips: getAvailableWidthTwips2 } = await Promise.resolve().then(() => (init_widthUtils(), widthUtils_exports));
5047
+ const widthUtils = await Promise.resolve().then(() => (init_widthUtils(), widthUtils_exports));
5048
+ const { getAvailableWidthTwips: getAvailableWidthTwips2, relativeLengthToTwips: relativeLengthToTwips2 } = widthUtils;
5074
5049
  const availableTableWidth = getAvailableWidthTwips2(theme, themeName);
5075
5050
  const columnsWithExplicitWidthTwips = columns.map(
5076
- (col) => col.width !== void 0 ? pointsToTwips(col.width) : void 0
5051
+ (col) => col.width !== void 0 ? relativeLengthToTwips2(col.width, availableTableWidth) : void 0
5077
5052
  );
5078
5053
  const totalExplicitWidth = columnsWithExplicitWidthTwips.reduce(
5079
5054
  (sum, w) => sum + (w || 0),
5080
5055
  0
5081
5056
  );
5057
+ if (totalExplicitWidth > availableTableWidth) {
5058
+ console.warn(
5059
+ `[json-to-office] Column widths total (${totalExplicitWidth} twips) exceeds available table width (${availableTableWidth} twips). Table may overflow.`
5060
+ );
5061
+ }
5082
5062
  const columnsWithoutWidth = columns.filter(
5083
5063
  (col) => col.width === void 0
5084
5064
  ).length;
@@ -6182,42 +6162,15 @@ async function renderHighchartsComponent(component, theme, _themeName) {
6182
6162
  import { Type as Type2 } from "@sinclair/typebox";
6183
6163
 
6184
6164
  // src/plugin/createComponent.ts
6185
- import { isValidSemver } from "@json-to-office/shared";
6165
+ import {
6166
+ createVersion as sharedCreateVersion,
6167
+ createComponent as sharedCreateComponent
6168
+ } from "@json-to-office/shared/plugin";
6186
6169
  function createVersion(version) {
6187
- return version;
6170
+ return sharedCreateVersion(version);
6188
6171
  }
6189
6172
  function createComponent(component) {
6190
- if (!component.name) {
6191
- throw new Error("Component name is required");
6192
- }
6193
- if (!component.versions || typeof component.versions !== "object") {
6194
- throw new Error(`Component "${component.name}" requires a versions map`);
6195
- }
6196
- const versionKeys = Object.keys(component.versions);
6197
- if (versionKeys.length === 0) {
6198
- throw new Error(
6199
- `Component "${component.name}" must have at least one version`
6200
- );
6201
- }
6202
- for (const key of versionKeys) {
6203
- if (!isValidSemver(key)) {
6204
- throw new Error(
6205
- `Component "${component.name}": invalid semver key "${key}". Expected format: major.minor.patch`
6206
- );
6207
- }
6208
- const entry = component.versions[key];
6209
- if (!entry.propsSchema) {
6210
- throw new Error(
6211
- `Component "${component.name}" version "${key}" requires a propsSchema`
6212
- );
6213
- }
6214
- if (!entry.render || typeof entry.render !== "function") {
6215
- throw new Error(
6216
- `Component "${component.name}" version "${key}" requires a render function`
6217
- );
6218
- }
6219
- }
6220
- return component;
6173
+ return sharedCreateComponent(component);
6221
6174
  }
6222
6175
 
6223
6176
  // src/components/text-space-after.ts
@@ -6691,45 +6644,29 @@ function normalizeDocument(document) {
6691
6644
  return [normalized];
6692
6645
  }
6693
6646
 
6694
- // src/styles/theme-resolver.ts
6695
- init_themes();
6696
- function resolveTheme2(theme) {
6697
- if (typeof theme === "string") {
6698
- const resolvedTheme = getTheme(theme);
6699
- if (resolvedTheme) {
6700
- return resolvedTheme;
6701
- }
6702
- return getTheme("minimal");
6703
- }
6704
- if (theme) {
6705
- return theme;
6706
- }
6707
- return getTheme("minimal");
6708
- }
6709
-
6710
- // src/styles/theme-validator.ts
6711
- import { validateTheme as validateThemeUnified } from "@json-to-office/shared-docx/validation/unified";
6712
- function validateTheme(theme) {
6713
- if (!theme) {
6714
- return void 0;
6715
- }
6716
- if (typeof theme === "string") {
6717
- return theme;
6718
- }
6719
- const result = validateThemeUnified(theme);
6720
- if (!result.valid) {
6721
- const errorSummary = result.errors?.map((e) => e.message).join(", ") || "Invalid theme";
6722
- throw new Error(`Invalid theme: ${errorSummary}`);
6723
- }
6724
- return result.data;
6725
- }
6726
-
6727
6647
  // src/plugin/createDocumentGenerator.ts
6728
6648
  function createBuilderImpl(state) {
6729
- const validatedTheme = validateTheme(state.theme);
6730
- const fullTheme = resolveTheme2(validatedTheme);
6731
6649
  const componentMap = new Map(state.components.map((c) => [c.name, c]));
6732
- async function processDocumentComponents(components, warningsCollector) {
6650
+ function resolveDocumentTheme(themeName) {
6651
+ if (state.customThemes) {
6652
+ if (state.customThemes[themeName]) {
6653
+ return state.customThemes[themeName];
6654
+ }
6655
+ const key = Object.keys(state.customThemes).find(
6656
+ (k) => k.toLowerCase() === themeName.toLowerCase()
6657
+ );
6658
+ if (key) {
6659
+ return state.customThemes[key];
6660
+ }
6661
+ }
6662
+ return getThemeWithFallback(themeName);
6663
+ }
6664
+ async function processDocumentComponents(components, warningsCollector, resolvedTheme, depth = 0) {
6665
+ if (depth > 20) {
6666
+ throw new Error(
6667
+ "Maximum component nesting depth exceeded (20). Check for circular component references."
6668
+ );
6669
+ }
6733
6670
  const processedComponents = [];
6734
6671
  for (const componentData of components) {
6735
6672
  const componentName = componentData?.name;
@@ -6759,7 +6696,9 @@ function createBuilderImpl(state) {
6759
6696
  if (componentWithName.children && Array.isArray(componentWithName.children)) {
6760
6697
  nestedChildren = await processDocumentComponents(
6761
6698
  componentWithName.children,
6762
- warningsCollector
6699
+ warningsCollector,
6700
+ resolvedTheme,
6701
+ depth + 1
6763
6702
  );
6764
6703
  }
6765
6704
  const versionLabel = componentWithName.version ? `${customComponent.name}@${componentWithName.version}` : customComponent.name;
@@ -6773,14 +6712,16 @@ function createBuilderImpl(state) {
6773
6712
  };
6774
6713
  const result = await versionEntry.render({
6775
6714
  props: cleanedProps,
6776
- theme: fullTheme,
6715
+ theme: resolvedTheme,
6777
6716
  addWarning,
6778
6717
  children: nestedChildren
6779
6718
  });
6780
6719
  const resultComponents = Array.isArray(result) ? result : [result];
6781
6720
  const processedResult = await processDocumentComponents(
6782
6721
  resultComponents,
6783
- warningsCollector
6722
+ warningsCollector,
6723
+ resolvedTheme,
6724
+ depth + 1
6784
6725
  );
6785
6726
  processedComponents.push(...processedResult);
6786
6727
  if (state.debug) {
@@ -6790,7 +6731,7 @@ function createBuilderImpl(state) {
6790
6731
  );
6791
6732
  }
6792
6733
  } catch (error) {
6793
- if (error instanceof ComponentValidationError) {
6734
+ if (error instanceof ComponentValidationError2) {
6794
6735
  throw error;
6795
6736
  }
6796
6737
  throw new Error(
@@ -6801,7 +6742,9 @@ function createBuilderImpl(state) {
6801
6742
  if ("children" in componentData && Array.isArray(componentData.children)) {
6802
6743
  const processedNested = await processDocumentComponents(
6803
6744
  componentData.children,
6804
- warningsCollector
6745
+ warningsCollector,
6746
+ resolvedTheme,
6747
+ depth + 1
6805
6748
  );
6806
6749
  processedComponents.push({
6807
6750
  ...componentData,
@@ -6815,18 +6758,19 @@ function createBuilderImpl(state) {
6815
6758
  return processedComponents;
6816
6759
  }
6817
6760
  function addComponent(component) {
6818
- if (state.componentNames.has(component.name)) {
6819
- throw new DuplicateComponentError(component.name);
6820
- }
6821
6761
  if (!component.name) {
6822
6762
  throw new Error("Component name is required");
6823
6763
  }
6764
+ if (state.componentNames.has(component.name)) {
6765
+ throw new DuplicateComponentError(component.name);
6766
+ }
6824
6767
  const newComponentNames = new Set(state.componentNames);
6825
6768
  newComponentNames.add(component.name);
6826
6769
  const newState = {
6827
6770
  components: [...state.components, component],
6828
6771
  componentNames: newComponentNames,
6829
6772
  theme: state.theme,
6773
+ customThemes: state.customThemes,
6830
6774
  debug: state.debug,
6831
6775
  enableCache: state.enableCache
6832
6776
  };
@@ -6841,10 +6785,13 @@ function createBuilderImpl(state) {
6841
6785
  internalDocument,
6842
6786
  state.components
6843
6787
  );
6788
+ const themeName = internalDocument.props.theme || "minimal";
6789
+ const docTheme = resolveDocumentTheme(themeName);
6844
6790
  const warnings = [];
6845
6791
  const processedComponents = await processDocumentComponents(
6846
6792
  internalDocument.children || [],
6847
- warnings
6793
+ warnings,
6794
+ docTheme
6848
6795
  );
6849
6796
  const processedDocument = {
6850
6797
  ...internalDocument,
@@ -6853,10 +6800,10 @@ function createBuilderImpl(state) {
6853
6800
  const [finalReportComponent] = normalizeDocument(processedDocument);
6854
6801
  const structure = await processDocument(
6855
6802
  finalReportComponent,
6856
- fullTheme,
6857
- "custom"
6803
+ docTheme,
6804
+ themeName
6858
6805
  );
6859
- const layout = applyLayout(structure.sections, fullTheme, "custom");
6806
+ const layout = applyLayout(structure.sections, docTheme, themeName);
6860
6807
  const generatedDocument = await renderDocument(structure, layout);
6861
6808
  return {
6862
6809
  document: generatedDocument,
@@ -6892,7 +6839,7 @@ function createBuilderImpl(state) {
6892
6839
  );
6893
6840
  return { valid: true };
6894
6841
  } catch (error) {
6895
- if (error instanceof ComponentValidationError) {
6842
+ if (error instanceof ComponentValidationError2) {
6896
6843
  return {
6897
6844
  valid: false,
6898
6845
  errors: error.errors.map((e) => ({
@@ -6932,10 +6879,13 @@ function createBuilderImpl(state) {
6932
6879
  internalDocument,
6933
6880
  state.components
6934
6881
  );
6882
+ const themeName = internalDocument.props.theme || "minimal";
6883
+ const docTheme = resolveDocumentTheme(themeName);
6935
6884
  const warnings = [];
6936
6885
  const processedComponents = await processDocumentComponents(
6937
6886
  internalDocument.children || [],
6938
- warnings
6887
+ warnings,
6888
+ docTheme
6939
6889
  );
6940
6890
  const processedDocument = {
6941
6891
  ...internalDocument,
@@ -6967,6 +6917,7 @@ function createDocumentGenerator(options) {
6967
6917
  components: [],
6968
6918
  componentNames: /* @__PURE__ */ new Set(),
6969
6919
  theme: options.theme,
6920
+ customThemes: options.customThemes,
6970
6921
  debug: options.debug ?? false,
6971
6922
  enableCache: options.enableCache ?? false
6972
6923
  };