@json-to-office/core-docx 2.6.0 → 3.0.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 +1 @@
1
- {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/ir/compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AA6C3D,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,wBAAwB,CAAC;AAuC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAEL,KAAK,MAAM,EAqCZ,MAAM,SAAS,CAAC;AAiBjB,2DAA2D;AAC3D,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,2EAA2E;IAC3E,QAAQ,EAAE,SAAS,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC;IACrD,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B;;;;OAIG;IACH,WAAW,EAAE,oBAAoB,EAAE,CAAC;CACrC;AA8FD,wBAAgB,eAAe,CAC7B,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,UAAU,EAClB,QAAQ,GAAE,iBAAiB,EAAO,EAClC,MAAM,GAAE,cAA0B,EAClC,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,iBAAiB,CAgHnB"}
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/ir/compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAiD3D,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,wBAAwB,CAAC;AAuC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAEL,KAAK,MAAM,EAqCZ,MAAM,SAAS,CAAC;AAiBjB,2DAA2D;AAC3D,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,2EAA2E;IAC3E,QAAQ,EAAE,SAAS,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC;IACrD,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B;;;;OAIG;IACH,WAAW,EAAE,oBAAoB,EAAE,CAAC;CACrC;AA8FD,wBAAgB,eAAe,CAC7B,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,UAAU,EAClB,QAAQ,GAAE,iBAAiB,EAAO,EAClC,MAAM,GAAE,cAA0B,EAClC,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,iBAAiB,CAgHnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"createDocumentGenerator.d.ts","sourceRoot":"","sources":["../../src/plugin/createDocumentGenerator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAK7C,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,KAAK,EAEV,wBAAwB,EAOxB,2BAA2B,EAC5B,MAAM,SAAS,CAAC;AAajB;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,kEAAkE;IAClE,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC3C,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,oFAAoF;IACpF,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB;;;;OAIG;IACH,UAAU,CAAC,EAAE,2BAA2B,CAAC;IACzC,gFAAgF;IAChF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AA4sBD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,wBAAwB,GAChC,wBAAwB,CAAC,SAAS,EAAE,CAAC,CAkBvC"}
1
+ {"version":3,"file":"createDocumentGenerator.d.ts","sourceRoot":"","sources":["../../src/plugin/createDocumentGenerator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAK7C,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,KAAK,EAEV,wBAAwB,EAOxB,2BAA2B,EAC5B,MAAM,SAAS,CAAC;AAejB;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,kEAAkE;IAClE,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC3C,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,oFAAoF;IACpF,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB;;;;OAIG;IACH,UAAU,CAAC,EAAE,2BAA2B,CAAC;IACzC,gFAAgF;IAChF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAoyBD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,wBAAwB,GAChC,wBAAwB,CAAC,SAAS,EAAE,CAAC,CAkBvC"}
@@ -8702,210 +8702,6 @@ function emptyToUndefined(formatting) {
8702
8702
  return entries.length === 0 ? void 0 : Object.fromEntries(entries);
8703
8703
  }
8704
8704
 
8705
- // src/blocks/index.ts
8706
- import { KEY_TAKEAWAYS_BUDGET } from "@json-to-office/shared-docx";
8707
-
8708
- // src/blocks/keyTakeaways.ts
8709
- var MIN_RULE_PT = 0.25;
8710
- var MAX_RULE_PT = 12;
8711
- var KEY_TAKEAWAYS_DEFAULT_LABEL = "Key takeaways";
8712
- var FALLBACK_RECIPE = {
8713
- ruleWeightPt: 1.5,
8714
- ruleColor: "accent",
8715
- padPt: 6,
8716
- type: "label"
8717
- };
8718
- function compileKeyTakeaways(props, theme) {
8719
- const recipe = theme.chrome?.keyTakeaways;
8720
- const labelRole = recipe?.type ?? FALLBACK_RECIPE.type;
8721
- const labelStyle = theme.styles?.[labelRole];
8722
- const ruleWeightPt = Math.min(
8723
- MAX_RULE_PT,
8724
- Math.max(
8725
- MIN_RULE_PT,
8726
- recipe?.rule?.weightPt ?? FALLBACK_RECIPE.ruleWeightPt
8727
- )
8728
- );
8729
- const ruleColor = recipe?.rule?.color ?? recipe?.color ?? FALLBACK_RECIPE.ruleColor;
8730
- const padPt = recipe?.padPt ?? FALLBACK_RECIPE.padPt;
8731
- const children = [
8732
- {
8733
- name: "divider",
8734
- props: {
8735
- thickness: ruleWeightPt,
8736
- color: ruleColor,
8737
- spacing: { before: 12, after: padPt }
8738
- }
8739
- },
8740
- {
8741
- name: "paragraph",
8742
- props: {
8743
- text: props.label ?? KEY_TAKEAWAYS_DEFAULT_LABEL,
8744
- keepNext: true,
8745
- spacing: { after: 4 },
8746
- // The label role when the theme resolves one; a bold run in the
8747
- // recipe's colour otherwise, so the label still reads as a label.
8748
- ...labelStyle ? { themeStyle: labelRole } : {
8749
- font: {
8750
- bold: true,
8751
- ...recipe?.color !== void 0 && { color: recipe.color }
8752
- }
8753
- }
8754
- }
8755
- },
8756
- {
8757
- name: "list",
8758
- props: { items: [...props.items] }
8759
- },
8760
- {
8761
- name: "divider",
8762
- props: {
8763
- thickness: 0.5,
8764
- color: "borderPrimary",
8765
- spacing: { before: padPt, after: 12 }
8766
- }
8767
- }
8768
- ];
8769
- return {
8770
- children,
8771
- sourceMap: {
8772
- "/children/0": "",
8773
- "/children/1": "",
8774
- "/children/1/props/text": "/props/label",
8775
- "/children/2": "",
8776
- "/children/2/props/items": "/props/items",
8777
- "/children/3": ""
8778
- }
8779
- };
8780
- }
8781
-
8782
- // src/blocks/index.ts
8783
- var BLOCK_NAMES = ["key-takeaways"];
8784
- var BLOCKS = {
8785
- "key-takeaways": {
8786
- compile: (props, theme) => compileKeyTakeaways(props, theme),
8787
- budgets: (props, pointer) => (Array.isArray(props.items) ? props.items : []).flatMap(
8788
- (item, index) => typeof item === "string" ? [
8789
- {
8790
- block: "key-takeaways",
8791
- slot: "items",
8792
- path: `${pointer}/props/items/${index}`,
8793
- words: wordCount(item),
8794
- maxWords: KEY_TAKEAWAYS_BUDGET.items.maxWords
8795
- }
8796
- ] : []
8797
- )
8798
- }
8799
- };
8800
- function isBlockName(name) {
8801
- return BLOCK_NAMES.includes(name);
8802
- }
8803
- function isRecord(value) {
8804
- return typeof value === "object" && value !== null && !Array.isArray(value);
8805
- }
8806
- function expandBlocks(document, theme) {
8807
- const sourceMap = {};
8808
- const blocks = [];
8809
- const walk2 = (node, pointer) => {
8810
- if (Array.isArray(node)) {
8811
- return node.map((child, index) => walk2(child, `${pointer}/${index}`));
8812
- }
8813
- if (!isRecord(node)) return node;
8814
- if (typeof node.name === "string" && isRecord(node.props)) {
8815
- if (node.enabled !== false && isBlockName(node.name)) {
8816
- const compiled = BLOCKS[node.name].compile(node.props, theme);
8817
- blocks.push(pointer);
8818
- for (const [emitted, authored] of Object.entries(compiled.sourceMap)) {
8819
- sourceMap[`${pointer}${emitted}`] = `${pointer}${authored}`;
8820
- }
8821
- return { ...node, children: compiled.children };
8822
- }
8823
- }
8824
- const next = { ...node };
8825
- if (isRecord(node.props)) {
8826
- const props = { ...node.props };
8827
- let changed = false;
8828
- for (const key of ["header", "footer"]) {
8829
- if (Array.isArray(props[key])) {
8830
- props[key] = walk2(props[key], `${pointer}/props/${key}`);
8831
- changed = true;
8832
- }
8833
- }
8834
- if (Array.isArray(props.columns)) {
8835
- props.columns = props.columns.map((column, index) => {
8836
- if (!isRecord(column)) return column;
8837
- const base = `${pointer}/props/columns/${index}`;
8838
- const out = { ...column };
8839
- if (isRecord(column.header) && "content" in column.header) {
8840
- out.header = {
8841
- ...column.header,
8842
- content: walk2(column.header.content, `${base}/header/content`)
8843
- };
8844
- }
8845
- if (Array.isArray(column.cells)) {
8846
- out.cells = column.cells.map(
8847
- (cell, cellIndex) => isRecord(cell) && "content" in cell ? {
8848
- ...cell,
8849
- content: walk2(
8850
- cell.content,
8851
- `${base}/cells/${cellIndex}/content`
8852
- )
8853
- } : cell
8854
- );
8855
- }
8856
- return out;
8857
- });
8858
- changed = true;
8859
- }
8860
- if (changed) next.props = props;
8861
- }
8862
- if (Array.isArray(node.children)) {
8863
- next.children = walk2(node.children, `${pointer}/children`);
8864
- }
8865
- return next;
8866
- };
8867
- const expanded = walk2(document, "");
8868
- return {
8869
- document: blocks.length > 0 ? expanded : document,
8870
- sourceMap,
8871
- blocks
8872
- };
8873
- }
8874
- function toAuthoredPointer(sourceMap, pointer) {
8875
- let best;
8876
- for (const emitted of Object.keys(sourceMap)) {
8877
- if ((pointer === emitted || pointer.startsWith(`${emitted}/`)) && (best === void 0 || emitted.length > best.length)) {
8878
- best = emitted;
8879
- }
8880
- }
8881
- if (best === void 0) return pointer;
8882
- return `${sourceMap[best]}${pointer.slice(best.length)}`;
8883
- }
8884
- function blockSlotBudgets(document, blocks) {
8885
- return blocks.flatMap((pointer) => {
8886
- const node = nodeAt(document, pointer);
8887
- if (!isRecord(node) || !isRecord(node.props) || !isBlockName(node.name)) {
8888
- return [];
8889
- }
8890
- return BLOCKS[node.name].budgets(node.props, pointer);
8891
- });
8892
- }
8893
- function wordCount(text) {
8894
- const trimmed = text.trim();
8895
- return trimmed === "" ? 0 : trimmed.split(/\s+/).length;
8896
- }
8897
- function nodeAt(root, pointer) {
8898
- if (pointer === "") return root;
8899
- let current = root;
8900
- for (const segment of pointer.slice(1).split("/")) {
8901
- const key = segment.replace(/~1/g, "/").replace(/~0/g, "~");
8902
- if (Array.isArray(current)) current = current[Number(key)];
8903
- else if (isRecord(current)) current = current[key];
8904
- else return void 0;
8905
- }
8906
- return current;
8907
- }
8908
-
8909
8705
  // src/ir/compiler.ts
8910
8706
  init_types();
8911
8707
  init_units();
@@ -9203,33 +8999,47 @@ function compileChromeParagraph(component, scope) {
9203
8999
  const text = String(props.text ?? "");
9204
9000
  if (reportUnlowered(component, props, text, scope)) return [];
9205
9001
  const font = props.font ?? {};
9206
- const normal = getNormalStyle(ctx.theme);
9002
+ const named = chromeStyle(ctx.theme, props.themeStyle);
9003
+ const base = { ...getNormalStyle(ctx.theme), ...named };
9004
+ const styleId = named ? paragraphStyleId(props.themeStyle) ?? "Normal" : "Normal";
9005
+ if (styleId !== "Normal" && !ctx.styleIds.has(styleId)) {
9006
+ ctx.styleIds.add(styleId);
9007
+ }
9207
9008
  const chromeProps = {
9208
9009
  text,
9209
- ...props.alignment ? { alignment: props.alignment } : {},
9010
+ ...named ? { themeStyle: props.themeStyle } : {},
9011
+ ...props.alignment ? { alignment: props.alignment } : named?.alignment ? { alignment: named.alignment } : {},
9210
9012
  ...props.spacing ? { spacing: props.spacing } : {},
9013
+ ...props.indent ? { indent: props.indent } : {},
9014
+ ...props.tabStops ? { tabStops: props.tabStops } : {},
9211
9015
  ...props.boldColor ? { boldColor: props.boldColor } : {},
9212
9016
  font: {
9213
- family: font.family || resolveFontFamily(ctx.theme, normal.font) || getThemeFonts(ctx.theme).body.family,
9214
- size: font.size ?? normal.size ?? 11,
9215
- color: font.color || normal.color || "textPrimary",
9216
- bold: font.bold ?? false,
9217
- italic: font.italic ?? false,
9017
+ family: font.family || resolveFontFamily(ctx.theme, base.font) || getThemeFonts(ctx.theme).body.family,
9018
+ size: font.size ?? base.size ?? 11,
9019
+ color: font.color || base.color || "textPrimary",
9020
+ bold: font.bold ?? base.bold ?? false,
9021
+ italic: font.italic ?? base.italic ?? false,
9218
9022
  ...font.underline !== void 0 ? { underline: font.underline } : {},
9219
- ...font.fontWeight !== void 0 ? { fontWeight: font.fontWeight } : {},
9220
- ...font.case !== void 0 ? { case: font.case } : {},
9023
+ ...(font.fontWeight ?? base.fontWeight) !== void 0 ? { fontWeight: font.fontWeight ?? base.fontWeight } : {},
9024
+ ...(font.case ?? base.case) !== void 0 ? { case: font.case ?? base.case } : {},
9025
+ ...(font.characterSpacing ?? base.characterSpacing) !== void 0 ? { characterSpacing: font.characterSpacing ?? base.characterSpacing } : {},
9221
9026
  ...font.lineSpacing !== void 0 ? { lineSpacing: font.lineSpacing } : {}
9222
9027
  }
9223
9028
  };
9224
9029
  return [
9225
9030
  paragraphNode(scope, compileRuns(chromeProps, text, ctx, scope.path), {
9226
- styleId: "Normal",
9031
+ styleId,
9227
9032
  formatting: paragraphFormatting(chromeProps, ctx, {
9228
9033
  alwaysSpacing: true
9229
9034
  })
9230
9035
  })
9231
9036
  ];
9232
9037
  }
9038
+ function chromeStyle(theme, themeStyle) {
9039
+ if (typeof themeStyle !== "string" || !themeStyle) return void 0;
9040
+ const styles = getThemeStyles(theme);
9041
+ return styles[themeStyle] ?? styles[themeStyle.toLowerCase()];
9042
+ }
9233
9043
  function compileChromeImage(component, scope) {
9234
9044
  const { ctx, path: path3 } = scope;
9235
9045
  const props = component.props ?? {};
@@ -9317,7 +9127,7 @@ function compileComponent(component, scope) {
9317
9127
  case "table":
9318
9128
  return compileTable(component, scope);
9319
9129
  default:
9320
- if (isBlockName(component.name)) {
9130
+ if (component.name === "group") {
9321
9131
  if (component.enabled === false) return [];
9322
9132
  return compileBlock(component, scope);
9323
9133
  }
@@ -9329,8 +9139,8 @@ function compileComponent(component, scope) {
9329
9139
  }
9330
9140
  }
9331
9141
  function compileBlock(component, scope) {
9332
- const children = component.children ?? [];
9333
- if (children.length === 0) {
9142
+ const children = component.children;
9143
+ if (!Array.isArray(children)) {
9334
9144
  scope.ctx.unsupported.push({ name: component.name, path: scope.path });
9335
9145
  return [];
9336
9146
  }
@@ -12360,6 +12170,204 @@ function normalizeDocument(document) {
12360
12170
  return [normalized];
12361
12171
  }
12362
12172
 
12173
+ // src/blocks/document.ts
12174
+ init_styles();
12175
+ init_widthUtils();
12176
+ import {
12177
+ JsonBlockEvaluator,
12178
+ BlockEvaluationError,
12179
+ composeBlocksWithPlugins,
12180
+ readBlockDefinitions,
12181
+ blockValueAt,
12182
+ isBlockRecord,
12183
+ toAuthoredBlockPointer
12184
+ } from "@json-to-office/shared";
12185
+ import { blockSlotBudgets } from "@json-to-office/shared";
12186
+ import { validateDocument as validateDocument2 } from "@json-to-office/shared-docx";
12187
+ var toAuthoredPointer = toAuthoredBlockPointer;
12188
+ function docxBlockContext(document, theme, path3 = "") {
12189
+ const index = /^\/children\/(\d+)/.exec(path3)?.[1];
12190
+ const section2 = index === void 0 ? void 0 : blockValueAt(document, `/children/${index}`);
12191
+ const page = isBlockRecord(section2) && isBlockRecord(section2.props) && isBlockRecord(section2.props.page) ? section2.props.page : {};
12192
+ const margins = getDocumentMargins(theme);
12193
+ const dimensions = getPageDimensions(
12194
+ page.size ?? theme.page.size
12195
+ );
12196
+ const overrides = isBlockRecord(page.margins) ? page.margins : {};
12197
+ const width = Object.keys(page).length ? Math.max(
12198
+ 0,
12199
+ dimensions.width - Number(overrides.left ?? margins.left ?? 1440) - Number(overrides.right ?? margins.right ?? 1440)
12200
+ ) : getAvailableWidthTwips(theme);
12201
+ const height = Object.keys(page).length ? Math.max(
12202
+ 0,
12203
+ dimensions.height - Number(overrides.top ?? margins.top ?? 1440) - Number(overrides.bottom ?? margins.bottom ?? 1440)
12204
+ ) : getAvailableHeightTwips(theme);
12205
+ return {
12206
+ document: blockValueAt(document, "/props/metadata") ?? {},
12207
+ page: { width, height },
12208
+ section: {}
12209
+ };
12210
+ }
12211
+ function createDocxBlockEvaluator(document, theme, extra = {}) {
12212
+ return new JsonBlockEvaluator(readBlockDefinitions(document), {
12213
+ format: "docx",
12214
+ theme,
12215
+ contextAt: (path3) => docxBlockContext(document, theme, path3),
12216
+ contextSources: { "/document": "/props/metadata" },
12217
+ measure: (axis, unit, context) => Number(blockValueAt(context, `/page/${axis}`)) / (unit === "twip" ? 1 : unit === "in" ? 1440 : 20),
12218
+ ...extra
12219
+ });
12220
+ }
12221
+ function expandBlocks(document, theme) {
12222
+ const effects = [];
12223
+ const evaluator = createDocxBlockEvaluator(document, theme, {
12224
+ onSection: (effect) => effects.push(effect)
12225
+ });
12226
+ const expanded = evaluator.expand(document);
12227
+ return finishDocxBlocks(expanded, document, theme, evaluator, effects);
12228
+ }
12229
+ function validateEffects(document, effects) {
12230
+ for (const effect of effects) {
12231
+ const parts = effect.path.split("/").slice(1);
12232
+ const section2 = blockValueAt(document, "/" + parts.slice(0, 2).join("/"));
12233
+ let valid = parts.length >= 4 && parts[0] === "children" && parts[2] === "children" && isBlockRecord(section2) && section2.name === "section";
12234
+ for (let i = 4; i < parts.length; i += 2) {
12235
+ const parent = blockValueAt(document, "/" + parts.slice(0, i).join("/"));
12236
+ valid &&= parts[i] === "children" && isBlockRecord(parent) && parent.name === "group";
12237
+ }
12238
+ if (!valid)
12239
+ throw new BlockEvaluationError([
12240
+ {
12241
+ path: effect.environment.source,
12242
+ code: "invalid_placement",
12243
+ message: "Section effects require a block in a top-level section body, optionally nested in transparent groups."
12244
+ }
12245
+ ]);
12246
+ }
12247
+ }
12248
+ function finishDocxBlocks(expanded, document, theme, evaluator, effects, validate = true) {
12249
+ if (isBlockRecord(expanded) && Array.isArray(expanded.children)) {
12250
+ let inherited;
12251
+ expanded.children.forEach((section2, index) => {
12252
+ if (!isBlockRecord(section2) || section2.name !== "section" || section2.enabled === false)
12253
+ return;
12254
+ const path3 = `/children/${index}`;
12255
+ const local = effects.filter(
12256
+ (e) => e.path.startsWith(`${path3}/children/`)
12257
+ );
12258
+ let tracker;
12259
+ let trackerSource = path3;
12260
+ for (const effect of local) {
12261
+ if (effect.settings.tracker !== void 0) {
12262
+ tracker = evaluator.evaluate(
12263
+ effect.settings.tracker,
12264
+ effect.environment,
12265
+ `${path3}/props/tracker`,
12266
+ `${effect.environment.definition}/section/tracker`
12267
+ );
12268
+ trackerSource = toAuthoredPointer(
12269
+ evaluator.sourceMap,
12270
+ `${path3}/props/tracker`
12271
+ );
12272
+ }
12273
+ }
12274
+ let chrome = inherited;
12275
+ for (const effect of local) {
12276
+ if (effect.settings.header !== void 0 || effect.settings.footer !== void 0) {
12277
+ chrome = effect;
12278
+ if (effect.settings.scope === "following") inherited = effect;
12279
+ }
12280
+ }
12281
+ const props = isBlockRecord(section2.props) ? { ...section2.props } : {};
12282
+ if (chrome) {
12283
+ const environment = {
12284
+ ...chrome.environment,
12285
+ context: {
12286
+ ...docxBlockContext(document, theme, path3),
12287
+ section: { tracker }
12288
+ },
12289
+ contextSources: {
12290
+ "/document": "/props/metadata",
12291
+ "/section/tracker": trackerSource
12292
+ }
12293
+ };
12294
+ for (const part of ["header", "footer"]) {
12295
+ if (props[part] !== void 0 || chrome.settings[part] === void 0)
12296
+ continue;
12297
+ const compiled = evaluator.evaluate(
12298
+ chrome.settings[part],
12299
+ environment,
12300
+ `${path3}/props/${part}`,
12301
+ `${environment.definition}/section/${part}`
12302
+ );
12303
+ props[part] = evaluator.expand(compiled, `${path3}/props/${part}`);
12304
+ }
12305
+ if (props.pageBreak === void 0)
12306
+ props.pageBreak = chrome.settings.pageBreak ?? true;
12307
+ }
12308
+ for (const effect of local)
12309
+ if (effect.settings.pageBreak !== void 0 && props.pageBreak === void 0)
12310
+ props.pageBreak = effect.settings.pageBreak;
12311
+ section2.props = props;
12312
+ });
12313
+ }
12314
+ validateEffects(expanded, effects);
12315
+ if (evaluator.blocks.length && validate) {
12316
+ const result = validateDocument2(expanded);
12317
+ if (!result.valid)
12318
+ throw new BlockEvaluationError(
12319
+ result.errors.map((issue) => ({
12320
+ path: toAuthoredPointer(evaluator.sourceMap, issue.path),
12321
+ code: issue.code ?? "block_invalid_output",
12322
+ message: issue.message
12323
+ }))
12324
+ );
12325
+ }
12326
+ return {
12327
+ document: expanded,
12328
+ sourceMap: evaluator.sourceMap,
12329
+ blocks: evaluator.blocks
12330
+ };
12331
+ }
12332
+ async function expandBlocksWithPlugins(document, theme, plugins, render, preserve = /* @__PURE__ */ new Set()) {
12333
+ const effects = [];
12334
+ const evaluator = createDocxBlockEvaluator(document, theme, {
12335
+ reservedNames: [...plugins],
12336
+ onSection: (effect) => effects.push(effect)
12337
+ });
12338
+ const walk2 = (value) => composeBlocksWithPlugins(evaluator, value, {
12339
+ plugins,
12340
+ render,
12341
+ preserve
12342
+ });
12343
+ const first = await walk2(document);
12344
+ const finished = finishDocxBlocks(
12345
+ first.standard,
12346
+ document,
12347
+ theme,
12348
+ evaluator,
12349
+ effects,
12350
+ false
12351
+ );
12352
+ const second = await walk2(finished.document);
12353
+ validateEffects(second.standard, effects);
12354
+ const result = validateDocument2(second.standard);
12355
+ if (!result.valid)
12356
+ throw new BlockEvaluationError(
12357
+ result.errors.map((issue) => ({
12358
+ path: toAuthoredPointer(evaluator.sourceMap, issue.path),
12359
+ code: issue.code ?? "block_invalid_output",
12360
+ message: issue.message
12361
+ }))
12362
+ );
12363
+ return {
12364
+ document: second.standard,
12365
+ preserved: first.preserved,
12366
+ sourceMap: evaluator.sourceMap,
12367
+ blocks: evaluator.blocks
12368
+ };
12369
+ }
12370
+
12363
12371
  // src/quality/facts.ts
12364
12372
  init_styleHelpers();
12365
12373
  init_defaults();
@@ -12802,7 +12810,10 @@ function prepareDocxQualityDocument(document, options = {}) {
12802
12810
  const facts = [];
12803
12811
  const provenance = {};
12804
12812
  let previousHeadingLevel;
12805
- const authoredPath = (path3) => toAuthoredPointer(expanded.sourceMap, path3);
12813
+ const authoredPath = (path3) => toAuthoredPointer(
12814
+ themed.sourceMap ?? {},
12815
+ toAuthoredPointer(expanded.sourceMap, path3)
12816
+ );
12806
12817
  const addFact = (raw) => {
12807
12818
  const fact = {
12808
12819
  ...raw,
@@ -12817,7 +12828,7 @@ function prepareDocxQualityDocument(document, options = {}) {
12817
12828
  ...fact.relatedPaths && { relatedPaths: fact.relatedPaths }
12818
12829
  };
12819
12830
  };
12820
- for (const budget of blockSlotBudgets(context.document, expanded.blocks)) {
12831
+ for (const budget of blockSlotBudgets(themed.document, expanded.blocks)) {
12821
12832
  addFact({
12822
12833
  id: `docx:block-slot:${budget.path}`,
12823
12834
  kind: "docx/block-slot",
@@ -12967,11 +12978,19 @@ function prepareDocxQualityDocument(document, options = {}) {
12967
12978
  facts,
12968
12979
  provenance,
12969
12980
  renderer: options.renderer ?? DEFAULT_DOCX_RENDERER_ID2,
12970
- ...expanded.blocks.length > 0 && {
12981
+ ...(expanded.blocks.length > 0 || (themed.blockPaths?.length ?? 0) > 0) && {
12971
12982
  metadata: {
12972
12983
  blocks: {
12973
- sourceMap: expanded.sourceMap,
12974
- blocks: expanded.blocks,
12984
+ sourceMap: {
12985
+ ...themed.sourceMap,
12986
+ ...Object.fromEntries(
12987
+ Object.entries(expanded.sourceMap).map(([key, value]) => [
12988
+ key,
12989
+ toAuthoredPointer(themed.sourceMap ?? {}, value)
12990
+ ])
12991
+ )
12992
+ },
12993
+ blocks: [...themed.blockPaths ?? [], ...expanded.blocks],
12975
12994
  document: context.document
12976
12995
  }
12977
12996
  }
@@ -13309,12 +13328,12 @@ function createBuilderImpl(state) {
13309
13328
  );
13310
13329
  }
13311
13330
  }
13312
- const validateEmitted = vOpts.enabled === false ? void 0 : (emitted, componentLabel) => {
13331
+ const validateEmitted = vOpts.enabled === false ? void 0 : (emitted, componentLabel, sourcePath) => {
13313
13332
  const result = validateDocument(
13314
13333
  {
13315
13334
  ...internalDocument,
13316
13335
  ...renderer !== void 0 ? { renderer } : {},
13317
- children: emitted
13336
+ children: sourcePath === void 0 ? emitted : [{ name: "section", props: {}, children: emitted }]
13318
13337
  },
13319
13338
  state.components,
13320
13339
  { allowUnknownFields: vOpts.allowUnknownFields }
@@ -13322,7 +13341,10 @@ function createBuilderImpl(state) {
13322
13341
  if (!result.valid) {
13323
13342
  throw new ComponentValidationError2(
13324
13343
  (result.errors ?? []).map((e) => ({
13325
- path: e.path ?? "",
13344
+ path: sourcePath === void 0 ? e.path ?? "" : (e.path ?? "").replace(
13345
+ /^\/children\/0\/children\/\d+/,
13346
+ sourcePath
13347
+ ),
13326
13348
  message: `custom component '${componentLabel}' emitted invalid output \u2014 ${e.message}`
13327
13349
  })),
13328
13350
  emitted
@@ -13340,6 +13362,62 @@ function createBuilderImpl(state) {
13340
13362
  warnings,
13341
13363
  resolveNamedTheme: resolveDocumentTheme
13342
13364
  });
13365
+ if (modedRoot.props.blocks !== void 0) {
13366
+ const expanded = await expandBlocksWithPlugins(
13367
+ modedRoot,
13368
+ modedTheme,
13369
+ new Set(componentMap.keys()),
13370
+ async (component, path3) => {
13371
+ const custom = componentMap.get(String(component.name));
13372
+ const version = resolveComponentVersion(
13373
+ custom.name,
13374
+ custom.versions,
13375
+ component.version
13376
+ );
13377
+ const checked = validateComponentProps(
13378
+ version,
13379
+ component.props ?? {},
13380
+ custom.name,
13381
+ { clean: vOpts.allowUnknownFields === true }
13382
+ );
13383
+ if (!checked.valid)
13384
+ throw new ComponentValidationError2(
13385
+ (checked.errors ?? []).map((e) => ({ path: path3, message: e.message })),
13386
+ component
13387
+ );
13388
+ const output = await version.render({
13389
+ props: cleanComponentProps(version, component.props ?? {}),
13390
+ theme: modedTheme,
13391
+ children: component.children,
13392
+ addWarning: (message, context) => warnings.push({
13393
+ component: custom.name,
13394
+ message,
13395
+ severity: "warning",
13396
+ context
13397
+ })
13398
+ });
13399
+ const emitted = Array.isArray(output) ? output : [output];
13400
+ validateEmitted?.(emitted, custom.name, path3);
13401
+ return emitted;
13402
+ },
13403
+ preserveSet
13404
+ );
13405
+ const [modedDoc2] = normalizeDocument(expanded.document);
13406
+ return {
13407
+ modedRoot,
13408
+ modedTheme,
13409
+ themeName,
13410
+ modedDoc: modedDoc2,
13411
+ sourceMap: expanded.sourceMap,
13412
+ blockPaths: expanded.blocks,
13413
+ warnings,
13414
+ preserveSet,
13415
+ processed: {
13416
+ standard: modedDoc2.children ?? [],
13417
+ preserved: expanded.preserved.children ?? []
13418
+ }
13419
+ };
13420
+ }
13343
13421
  const processed = await processDocumentComponents(
13344
13422
  modedRoot.children || [],
13345
13423
  preserveSet,
@@ -13364,9 +13442,13 @@ function createBuilderImpl(state) {
13364
13442
  }
13365
13443
  async function expandStandardDefinition(document, options) {
13366
13444
  try {
13367
- const { modedDoc, warnings } = await expandDocument(document, options);
13445
+ const { modedDoc, warnings, sourceMap } = await expandDocument(
13446
+ document,
13447
+ options
13448
+ );
13368
13449
  return {
13369
13450
  standardDefinition: modedDoc,
13451
+ ...sourceMap && { sourceMap },
13370
13452
  warnings: warnings.length > 0 ? warnings : null
13371
13453
  };
13372
13454
  } catch (error) {
@@ -13384,6 +13466,8 @@ function createBuilderImpl(state) {
13384
13466
  themeName,
13385
13467
  processed,
13386
13468
  modedDoc,
13469
+ sourceMap,
13470
+ blockPaths,
13387
13471
  warnings,
13388
13472
  preserveSet
13389
13473
  } = await expandDocument(document, options);
@@ -13394,7 +13478,13 @@ function createBuilderImpl(state) {
13394
13478
  const { buffer } = await generateBufferViaIr(modedDoc, {
13395
13479
  // The prologue already ran: the theme had to be resolved before custom
13396
13480
  // components expanded, because each one's `render` is handed it.
13397
- context: { document: modedDoc, theme: modedTheme, themeName },
13481
+ context: {
13482
+ document: modedDoc,
13483
+ theme: modedTheme,
13484
+ themeName,
13485
+ ...sourceMap !== void 0 && { sourceMap },
13486
+ ...blockPaths !== void 0 && { blockPaths }
13487
+ },
13398
13488
  ...state.services ? { services: state.services } : {},
13399
13489
  ...state.fonts ? { fonts: state.fonts } : {},
13400
13490
  ...baseDir !== void 0 ? { baseDir } : {},