@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.
package/dist/index.js CHANGED
@@ -8454,210 +8454,6 @@ function emptyToUndefined(formatting) {
8454
8454
  return entries.length === 0 ? void 0 : Object.fromEntries(entries);
8455
8455
  }
8456
8456
 
8457
- // src/blocks/index.ts
8458
- import { KEY_TAKEAWAYS_BUDGET } from "@json-to-office/shared-docx";
8459
-
8460
- // src/blocks/keyTakeaways.ts
8461
- var MIN_RULE_PT = 0.25;
8462
- var MAX_RULE_PT = 12;
8463
- var KEY_TAKEAWAYS_DEFAULT_LABEL = "Key takeaways";
8464
- var FALLBACK_RECIPE = {
8465
- ruleWeightPt: 1.5,
8466
- ruleColor: "accent",
8467
- padPt: 6,
8468
- type: "label"
8469
- };
8470
- function compileKeyTakeaways(props, theme) {
8471
- const recipe = theme.chrome?.keyTakeaways;
8472
- const labelRole = recipe?.type ?? FALLBACK_RECIPE.type;
8473
- const labelStyle = theme.styles?.[labelRole];
8474
- const ruleWeightPt = Math.min(
8475
- MAX_RULE_PT,
8476
- Math.max(
8477
- MIN_RULE_PT,
8478
- recipe?.rule?.weightPt ?? FALLBACK_RECIPE.ruleWeightPt
8479
- )
8480
- );
8481
- const ruleColor = recipe?.rule?.color ?? recipe?.color ?? FALLBACK_RECIPE.ruleColor;
8482
- const padPt = recipe?.padPt ?? FALLBACK_RECIPE.padPt;
8483
- const children = [
8484
- {
8485
- name: "divider",
8486
- props: {
8487
- thickness: ruleWeightPt,
8488
- color: ruleColor,
8489
- spacing: { before: 12, after: padPt }
8490
- }
8491
- },
8492
- {
8493
- name: "paragraph",
8494
- props: {
8495
- text: props.label ?? KEY_TAKEAWAYS_DEFAULT_LABEL,
8496
- keepNext: true,
8497
- spacing: { after: 4 },
8498
- // The label role when the theme resolves one; a bold run in the
8499
- // recipe's colour otherwise, so the label still reads as a label.
8500
- ...labelStyle ? { themeStyle: labelRole } : {
8501
- font: {
8502
- bold: true,
8503
- ...recipe?.color !== void 0 && { color: recipe.color }
8504
- }
8505
- }
8506
- }
8507
- },
8508
- {
8509
- name: "list",
8510
- props: { items: [...props.items] }
8511
- },
8512
- {
8513
- name: "divider",
8514
- props: {
8515
- thickness: 0.5,
8516
- color: "borderPrimary",
8517
- spacing: { before: padPt, after: 12 }
8518
- }
8519
- }
8520
- ];
8521
- return {
8522
- children,
8523
- sourceMap: {
8524
- "/children/0": "",
8525
- "/children/1": "",
8526
- "/children/1/props/text": "/props/label",
8527
- "/children/2": "",
8528
- "/children/2/props/items": "/props/items",
8529
- "/children/3": ""
8530
- }
8531
- };
8532
- }
8533
-
8534
- // src/blocks/index.ts
8535
- var BLOCK_NAMES = ["key-takeaways"];
8536
- var BLOCKS = {
8537
- "key-takeaways": {
8538
- compile: (props, theme) => compileKeyTakeaways(props, theme),
8539
- budgets: (props, pointer) => (Array.isArray(props.items) ? props.items : []).flatMap(
8540
- (item, index) => typeof item === "string" ? [
8541
- {
8542
- block: "key-takeaways",
8543
- slot: "items",
8544
- path: `${pointer}/props/items/${index}`,
8545
- words: wordCount(item),
8546
- maxWords: KEY_TAKEAWAYS_BUDGET.items.maxWords
8547
- }
8548
- ] : []
8549
- )
8550
- }
8551
- };
8552
- function isBlockName(name) {
8553
- return BLOCK_NAMES.includes(name);
8554
- }
8555
- function isRecord(value) {
8556
- return typeof value === "object" && value !== null && !Array.isArray(value);
8557
- }
8558
- function expandBlocks(document, theme) {
8559
- const sourceMap = {};
8560
- const blocks = [];
8561
- const walk2 = (node, pointer) => {
8562
- if (Array.isArray(node)) {
8563
- return node.map((child, index) => walk2(child, `${pointer}/${index}`));
8564
- }
8565
- if (!isRecord(node)) return node;
8566
- if (typeof node.name === "string" && isRecord(node.props)) {
8567
- if (node.enabled !== false && isBlockName(node.name)) {
8568
- const compiled = BLOCKS[node.name].compile(node.props, theme);
8569
- blocks.push(pointer);
8570
- for (const [emitted, authored] of Object.entries(compiled.sourceMap)) {
8571
- sourceMap[`${pointer}${emitted}`] = `${pointer}${authored}`;
8572
- }
8573
- return { ...node, children: compiled.children };
8574
- }
8575
- }
8576
- const next = { ...node };
8577
- if (isRecord(node.props)) {
8578
- const props = { ...node.props };
8579
- let changed = false;
8580
- for (const key of ["header", "footer"]) {
8581
- if (Array.isArray(props[key])) {
8582
- props[key] = walk2(props[key], `${pointer}/props/${key}`);
8583
- changed = true;
8584
- }
8585
- }
8586
- if (Array.isArray(props.columns)) {
8587
- props.columns = props.columns.map((column, index) => {
8588
- if (!isRecord(column)) return column;
8589
- const base = `${pointer}/props/columns/${index}`;
8590
- const out = { ...column };
8591
- if (isRecord(column.header) && "content" in column.header) {
8592
- out.header = {
8593
- ...column.header,
8594
- content: walk2(column.header.content, `${base}/header/content`)
8595
- };
8596
- }
8597
- if (Array.isArray(column.cells)) {
8598
- out.cells = column.cells.map(
8599
- (cell, cellIndex) => isRecord(cell) && "content" in cell ? {
8600
- ...cell,
8601
- content: walk2(
8602
- cell.content,
8603
- `${base}/cells/${cellIndex}/content`
8604
- )
8605
- } : cell
8606
- );
8607
- }
8608
- return out;
8609
- });
8610
- changed = true;
8611
- }
8612
- if (changed) next.props = props;
8613
- }
8614
- if (Array.isArray(node.children)) {
8615
- next.children = walk2(node.children, `${pointer}/children`);
8616
- }
8617
- return next;
8618
- };
8619
- const expanded = walk2(document, "");
8620
- return {
8621
- document: blocks.length > 0 ? expanded : document,
8622
- sourceMap,
8623
- blocks
8624
- };
8625
- }
8626
- function toAuthoredPointer(sourceMap, pointer) {
8627
- let best;
8628
- for (const emitted of Object.keys(sourceMap)) {
8629
- if ((pointer === emitted || pointer.startsWith(`${emitted}/`)) && (best === void 0 || emitted.length > best.length)) {
8630
- best = emitted;
8631
- }
8632
- }
8633
- if (best === void 0) return pointer;
8634
- return `${sourceMap[best]}${pointer.slice(best.length)}`;
8635
- }
8636
- function blockSlotBudgets(document, blocks) {
8637
- return blocks.flatMap((pointer) => {
8638
- const node = nodeAt(document, pointer);
8639
- if (!isRecord(node) || !isRecord(node.props) || !isBlockName(node.name)) {
8640
- return [];
8641
- }
8642
- return BLOCKS[node.name].budgets(node.props, pointer);
8643
- });
8644
- }
8645
- function wordCount(text) {
8646
- const trimmed = text.trim();
8647
- return trimmed === "" ? 0 : trimmed.split(/\s+/).length;
8648
- }
8649
- function nodeAt(root, pointer) {
8650
- if (pointer === "") return root;
8651
- let current = root;
8652
- for (const segment of pointer.slice(1).split("/")) {
8653
- const key = segment.replace(/~1/g, "/").replace(/~0/g, "~");
8654
- if (Array.isArray(current)) current = current[Number(key)];
8655
- else if (isRecord(current)) current = current[key];
8656
- else return void 0;
8657
- }
8658
- return current;
8659
- }
8660
-
8661
8457
  // src/ir/compiler.ts
8662
8458
  init_types();
8663
8459
  init_units();
@@ -8955,33 +8751,47 @@ function compileChromeParagraph(component, scope) {
8955
8751
  const text = String(props.text ?? "");
8956
8752
  if (reportUnlowered(component, props, text, scope)) return [];
8957
8753
  const font = props.font ?? {};
8958
- const normal = getNormalStyle(ctx.theme);
8754
+ const named = chromeStyle(ctx.theme, props.themeStyle);
8755
+ const base = { ...getNormalStyle(ctx.theme), ...named };
8756
+ const styleId = named ? paragraphStyleId(props.themeStyle) ?? "Normal" : "Normal";
8757
+ if (styleId !== "Normal" && !ctx.styleIds.has(styleId)) {
8758
+ ctx.styleIds.add(styleId);
8759
+ }
8959
8760
  const chromeProps = {
8960
8761
  text,
8961
- ...props.alignment ? { alignment: props.alignment } : {},
8762
+ ...named ? { themeStyle: props.themeStyle } : {},
8763
+ ...props.alignment ? { alignment: props.alignment } : named?.alignment ? { alignment: named.alignment } : {},
8962
8764
  ...props.spacing ? { spacing: props.spacing } : {},
8765
+ ...props.indent ? { indent: props.indent } : {},
8766
+ ...props.tabStops ? { tabStops: props.tabStops } : {},
8963
8767
  ...props.boldColor ? { boldColor: props.boldColor } : {},
8964
8768
  font: {
8965
- family: font.family || resolveFontFamily(ctx.theme, normal.font) || getThemeFonts(ctx.theme).body.family,
8966
- size: font.size ?? normal.size ?? 11,
8967
- color: font.color || normal.color || "textPrimary",
8968
- bold: font.bold ?? false,
8969
- italic: font.italic ?? false,
8769
+ family: font.family || resolveFontFamily(ctx.theme, base.font) || getThemeFonts(ctx.theme).body.family,
8770
+ size: font.size ?? base.size ?? 11,
8771
+ color: font.color || base.color || "textPrimary",
8772
+ bold: font.bold ?? base.bold ?? false,
8773
+ italic: font.italic ?? base.italic ?? false,
8970
8774
  ...font.underline !== void 0 ? { underline: font.underline } : {},
8971
- ...font.fontWeight !== void 0 ? { fontWeight: font.fontWeight } : {},
8972
- ...font.case !== void 0 ? { case: font.case } : {},
8775
+ ...(font.fontWeight ?? base.fontWeight) !== void 0 ? { fontWeight: font.fontWeight ?? base.fontWeight } : {},
8776
+ ...(font.case ?? base.case) !== void 0 ? { case: font.case ?? base.case } : {},
8777
+ ...(font.characterSpacing ?? base.characterSpacing) !== void 0 ? { characterSpacing: font.characterSpacing ?? base.characterSpacing } : {},
8973
8778
  ...font.lineSpacing !== void 0 ? { lineSpacing: font.lineSpacing } : {}
8974
8779
  }
8975
8780
  };
8976
8781
  return [
8977
8782
  paragraphNode(scope, compileRuns(chromeProps, text, ctx, scope.path), {
8978
- styleId: "Normal",
8783
+ styleId,
8979
8784
  formatting: paragraphFormatting(chromeProps, ctx, {
8980
8785
  alwaysSpacing: true
8981
8786
  })
8982
8787
  })
8983
8788
  ];
8984
8789
  }
8790
+ function chromeStyle(theme, themeStyle) {
8791
+ if (typeof themeStyle !== "string" || !themeStyle) return void 0;
8792
+ const styles = getThemeStyles(theme);
8793
+ return styles[themeStyle] ?? styles[themeStyle.toLowerCase()];
8794
+ }
8985
8795
  function compileChromeImage(component, scope) {
8986
8796
  const { ctx, path: path4 } = scope;
8987
8797
  const props = component.props ?? {};
@@ -9069,7 +8879,7 @@ function compileComponent(component, scope) {
9069
8879
  case "table":
9070
8880
  return compileTable(component, scope);
9071
8881
  default:
9072
- if (isBlockName(component.name)) {
8882
+ if (component.name === "group") {
9073
8883
  if (component.enabled === false) return [];
9074
8884
  return compileBlock(component, scope);
9075
8885
  }
@@ -9081,8 +8891,8 @@ function compileComponent(component, scope) {
9081
8891
  }
9082
8892
  }
9083
8893
  function compileBlock(component, scope) {
9084
- const children = component.children ?? [];
9085
- if (children.length === 0) {
8894
+ const children = component.children;
8895
+ if (!Array.isArray(children)) {
9086
8896
  scope.ctx.unsupported.push({ name: component.name, path: scope.path });
9087
8897
  return [];
9088
8898
  }
@@ -12274,6 +12084,204 @@ function normalizeDocument(document) {
12274
12084
  return [normalized];
12275
12085
  }
12276
12086
 
12087
+ // src/blocks/document.ts
12088
+ init_styles();
12089
+ init_widthUtils();
12090
+ import {
12091
+ JsonBlockEvaluator,
12092
+ BlockEvaluationError,
12093
+ composeBlocksWithPlugins,
12094
+ readBlockDefinitions,
12095
+ blockValueAt,
12096
+ isBlockRecord,
12097
+ toAuthoredBlockPointer
12098
+ } from "@json-to-office/shared";
12099
+ import { blockSlotBudgets } from "@json-to-office/shared";
12100
+ import { validateDocument } from "@json-to-office/shared-docx";
12101
+ var toAuthoredPointer = toAuthoredBlockPointer;
12102
+ function docxBlockContext(document, theme, path4 = "") {
12103
+ const index = /^\/children\/(\d+)/.exec(path4)?.[1];
12104
+ const section2 = index === void 0 ? void 0 : blockValueAt(document, `/children/${index}`);
12105
+ const page = isBlockRecord(section2) && isBlockRecord(section2.props) && isBlockRecord(section2.props.page) ? section2.props.page : {};
12106
+ const margins = getDocumentMargins(theme);
12107
+ const dimensions = getPageDimensions(
12108
+ page.size ?? theme.page.size
12109
+ );
12110
+ const overrides = isBlockRecord(page.margins) ? page.margins : {};
12111
+ const width = Object.keys(page).length ? Math.max(
12112
+ 0,
12113
+ dimensions.width - Number(overrides.left ?? margins.left ?? 1440) - Number(overrides.right ?? margins.right ?? 1440)
12114
+ ) : getAvailableWidthTwips(theme);
12115
+ const height = Object.keys(page).length ? Math.max(
12116
+ 0,
12117
+ dimensions.height - Number(overrides.top ?? margins.top ?? 1440) - Number(overrides.bottom ?? margins.bottom ?? 1440)
12118
+ ) : getAvailableHeightTwips(theme);
12119
+ return {
12120
+ document: blockValueAt(document, "/props/metadata") ?? {},
12121
+ page: { width, height },
12122
+ section: {}
12123
+ };
12124
+ }
12125
+ function createDocxBlockEvaluator(document, theme, extra = {}) {
12126
+ return new JsonBlockEvaluator(readBlockDefinitions(document), {
12127
+ format: "docx",
12128
+ theme,
12129
+ contextAt: (path4) => docxBlockContext(document, theme, path4),
12130
+ contextSources: { "/document": "/props/metadata" },
12131
+ measure: (axis, unit, context) => Number(blockValueAt(context, `/page/${axis}`)) / (unit === "twip" ? 1 : unit === "in" ? 1440 : 20),
12132
+ ...extra
12133
+ });
12134
+ }
12135
+ function expandBlocks(document, theme) {
12136
+ const effects = [];
12137
+ const evaluator = createDocxBlockEvaluator(document, theme, {
12138
+ onSection: (effect) => effects.push(effect)
12139
+ });
12140
+ const expanded = evaluator.expand(document);
12141
+ return finishDocxBlocks(expanded, document, theme, evaluator, effects);
12142
+ }
12143
+ function validateEffects(document, effects) {
12144
+ for (const effect of effects) {
12145
+ const parts = effect.path.split("/").slice(1);
12146
+ const section2 = blockValueAt(document, "/" + parts.slice(0, 2).join("/"));
12147
+ let valid = parts.length >= 4 && parts[0] === "children" && parts[2] === "children" && isBlockRecord(section2) && section2.name === "section";
12148
+ for (let i = 4; i < parts.length; i += 2) {
12149
+ const parent = blockValueAt(document, "/" + parts.slice(0, i).join("/"));
12150
+ valid &&= parts[i] === "children" && isBlockRecord(parent) && parent.name === "group";
12151
+ }
12152
+ if (!valid)
12153
+ throw new BlockEvaluationError([
12154
+ {
12155
+ path: effect.environment.source,
12156
+ code: "invalid_placement",
12157
+ message: "Section effects require a block in a top-level section body, optionally nested in transparent groups."
12158
+ }
12159
+ ]);
12160
+ }
12161
+ }
12162
+ function finishDocxBlocks(expanded, document, theme, evaluator, effects, validate = true) {
12163
+ if (isBlockRecord(expanded) && Array.isArray(expanded.children)) {
12164
+ let inherited;
12165
+ expanded.children.forEach((section2, index) => {
12166
+ if (!isBlockRecord(section2) || section2.name !== "section" || section2.enabled === false)
12167
+ return;
12168
+ const path4 = `/children/${index}`;
12169
+ const local = effects.filter(
12170
+ (e) => e.path.startsWith(`${path4}/children/`)
12171
+ );
12172
+ let tracker;
12173
+ let trackerSource = path4;
12174
+ for (const effect of local) {
12175
+ if (effect.settings.tracker !== void 0) {
12176
+ tracker = evaluator.evaluate(
12177
+ effect.settings.tracker,
12178
+ effect.environment,
12179
+ `${path4}/props/tracker`,
12180
+ `${effect.environment.definition}/section/tracker`
12181
+ );
12182
+ trackerSource = toAuthoredPointer(
12183
+ evaluator.sourceMap,
12184
+ `${path4}/props/tracker`
12185
+ );
12186
+ }
12187
+ }
12188
+ let chrome = inherited;
12189
+ for (const effect of local) {
12190
+ if (effect.settings.header !== void 0 || effect.settings.footer !== void 0) {
12191
+ chrome = effect;
12192
+ if (effect.settings.scope === "following") inherited = effect;
12193
+ }
12194
+ }
12195
+ const props = isBlockRecord(section2.props) ? { ...section2.props } : {};
12196
+ if (chrome) {
12197
+ const environment = {
12198
+ ...chrome.environment,
12199
+ context: {
12200
+ ...docxBlockContext(document, theme, path4),
12201
+ section: { tracker }
12202
+ },
12203
+ contextSources: {
12204
+ "/document": "/props/metadata",
12205
+ "/section/tracker": trackerSource
12206
+ }
12207
+ };
12208
+ for (const part of ["header", "footer"]) {
12209
+ if (props[part] !== void 0 || chrome.settings[part] === void 0)
12210
+ continue;
12211
+ const compiled = evaluator.evaluate(
12212
+ chrome.settings[part],
12213
+ environment,
12214
+ `${path4}/props/${part}`,
12215
+ `${environment.definition}/section/${part}`
12216
+ );
12217
+ props[part] = evaluator.expand(compiled, `${path4}/props/${part}`);
12218
+ }
12219
+ if (props.pageBreak === void 0)
12220
+ props.pageBreak = chrome.settings.pageBreak ?? true;
12221
+ }
12222
+ for (const effect of local)
12223
+ if (effect.settings.pageBreak !== void 0 && props.pageBreak === void 0)
12224
+ props.pageBreak = effect.settings.pageBreak;
12225
+ section2.props = props;
12226
+ });
12227
+ }
12228
+ validateEffects(expanded, effects);
12229
+ if (evaluator.blocks.length && validate) {
12230
+ const result = validateDocument(expanded);
12231
+ if (!result.valid)
12232
+ throw new BlockEvaluationError(
12233
+ result.errors.map((issue) => ({
12234
+ path: toAuthoredPointer(evaluator.sourceMap, issue.path),
12235
+ code: issue.code ?? "block_invalid_output",
12236
+ message: issue.message
12237
+ }))
12238
+ );
12239
+ }
12240
+ return {
12241
+ document: expanded,
12242
+ sourceMap: evaluator.sourceMap,
12243
+ blocks: evaluator.blocks
12244
+ };
12245
+ }
12246
+ async function expandBlocksWithPlugins(document, theme, plugins, render, preserve = /* @__PURE__ */ new Set()) {
12247
+ const effects = [];
12248
+ const evaluator = createDocxBlockEvaluator(document, theme, {
12249
+ reservedNames: [...plugins],
12250
+ onSection: (effect) => effects.push(effect)
12251
+ });
12252
+ const walk2 = (value) => composeBlocksWithPlugins(evaluator, value, {
12253
+ plugins,
12254
+ render,
12255
+ preserve
12256
+ });
12257
+ const first = await walk2(document);
12258
+ const finished = finishDocxBlocks(
12259
+ first.standard,
12260
+ document,
12261
+ theme,
12262
+ evaluator,
12263
+ effects,
12264
+ false
12265
+ );
12266
+ const second = await walk2(finished.document);
12267
+ validateEffects(second.standard, effects);
12268
+ const result = validateDocument(second.standard);
12269
+ if (!result.valid)
12270
+ throw new BlockEvaluationError(
12271
+ result.errors.map((issue) => ({
12272
+ path: toAuthoredPointer(evaluator.sourceMap, issue.path),
12273
+ code: issue.code ?? "block_invalid_output",
12274
+ message: issue.message
12275
+ }))
12276
+ );
12277
+ return {
12278
+ document: second.standard,
12279
+ preserved: first.preserved,
12280
+ sourceMap: evaluator.sourceMap,
12281
+ blocks: evaluator.blocks
12282
+ };
12283
+ }
12284
+
12277
12285
  // src/quality/facts.ts
12278
12286
  init_styleHelpers();
12279
12287
  init_defaults();
@@ -12716,7 +12724,10 @@ function prepareDocxQualityDocument(document, options = {}) {
12716
12724
  const facts = [];
12717
12725
  const provenance = {};
12718
12726
  let previousHeadingLevel;
12719
- const authoredPath = (path4) => toAuthoredPointer(expanded.sourceMap, path4);
12727
+ const authoredPath = (path4) => toAuthoredPointer(
12728
+ themed.sourceMap ?? {},
12729
+ toAuthoredPointer(expanded.sourceMap, path4)
12730
+ );
12720
12731
  const addFact = (raw) => {
12721
12732
  const fact = {
12722
12733
  ...raw,
@@ -12731,7 +12742,7 @@ function prepareDocxQualityDocument(document, options = {}) {
12731
12742
  ...fact.relatedPaths && { relatedPaths: fact.relatedPaths }
12732
12743
  };
12733
12744
  };
12734
- for (const budget of blockSlotBudgets(context.document, expanded.blocks)) {
12745
+ for (const budget of blockSlotBudgets(themed.document, expanded.blocks)) {
12735
12746
  addFact({
12736
12747
  id: `docx:block-slot:${budget.path}`,
12737
12748
  kind: "docx/block-slot",
@@ -12881,11 +12892,19 @@ function prepareDocxQualityDocument(document, options = {}) {
12881
12892
  facts,
12882
12893
  provenance,
12883
12894
  renderer: options.renderer ?? DEFAULT_DOCX_RENDERER_ID2,
12884
- ...expanded.blocks.length > 0 && {
12895
+ ...(expanded.blocks.length > 0 || (themed.blockPaths?.length ?? 0) > 0) && {
12885
12896
  metadata: {
12886
12897
  blocks: {
12887
- sourceMap: expanded.sourceMap,
12888
- blocks: expanded.blocks,
12898
+ sourceMap: {
12899
+ ...themed.sourceMap,
12900
+ ...Object.fromEntries(
12901
+ Object.entries(expanded.sourceMap).map(([key, value]) => [
12902
+ key,
12903
+ toAuthoredPointer(themed.sourceMap ?? {}, value)
12904
+ ])
12905
+ )
12906
+ },
12907
+ blocks: [...themed.blockPaths ?? [], ...expanded.blocks],
12889
12908
  document: context.document
12890
12909
  }
12891
12910
  }
@@ -14315,7 +14334,7 @@ function validateComponentProps(schema, props, componentName, opts) {
14315
14334
  componentName
14316
14335
  });
14317
14336
  }
14318
- function validateDocument(document, customComponents, options) {
14337
+ function validateDocument2(document, customComponents, options) {
14319
14338
  const knownCustomNames = new Set(customComponents.map((c) => c.name));
14320
14339
  const documentResult = validateDocumentUnified(document, {
14321
14340
  knownCustomNames,
@@ -14719,7 +14738,7 @@ function createBuilderImpl(state) {
14719
14738
  ...options?.validation
14720
14739
  };
14721
14740
  if (vOpts.enabled !== false) {
14722
- const result = validateDocument(
14741
+ const result = validateDocument2(
14723
14742
  validationDocument,
14724
14743
  state.components,
14725
14744
  { allowUnknownFields: vOpts.allowUnknownFields }
@@ -14734,12 +14753,12 @@ function createBuilderImpl(state) {
14734
14753
  );
14735
14754
  }
14736
14755
  }
14737
- const validateEmitted = vOpts.enabled === false ? void 0 : (emitted, componentLabel) => {
14738
- const result = validateDocument(
14756
+ const validateEmitted = vOpts.enabled === false ? void 0 : (emitted, componentLabel, sourcePath) => {
14757
+ const result = validateDocument2(
14739
14758
  {
14740
14759
  ...internalDocument,
14741
14760
  ...renderer !== void 0 ? { renderer } : {},
14742
- children: emitted
14761
+ children: sourcePath === void 0 ? emitted : [{ name: "section", props: {}, children: emitted }]
14743
14762
  },
14744
14763
  state.components,
14745
14764
  { allowUnknownFields: vOpts.allowUnknownFields }
@@ -14747,7 +14766,10 @@ function createBuilderImpl(state) {
14747
14766
  if (!result.valid) {
14748
14767
  throw new ComponentValidationError2(
14749
14768
  (result.errors ?? []).map((e) => ({
14750
- path: e.path ?? "",
14769
+ path: sourcePath === void 0 ? e.path ?? "" : (e.path ?? "").replace(
14770
+ /^\/children\/0\/children\/\d+/,
14771
+ sourcePath
14772
+ ),
14751
14773
  message: `custom component '${componentLabel}' emitted invalid output \u2014 ${e.message}`
14752
14774
  })),
14753
14775
  emitted
@@ -14765,6 +14787,62 @@ function createBuilderImpl(state) {
14765
14787
  warnings,
14766
14788
  resolveNamedTheme: resolveDocumentTheme
14767
14789
  });
14790
+ if (modedRoot.props.blocks !== void 0) {
14791
+ const expanded = await expandBlocksWithPlugins(
14792
+ modedRoot,
14793
+ modedTheme,
14794
+ new Set(componentMap.keys()),
14795
+ async (component, path4) => {
14796
+ const custom = componentMap.get(String(component.name));
14797
+ const version = resolveComponentVersion(
14798
+ custom.name,
14799
+ custom.versions,
14800
+ component.version
14801
+ );
14802
+ const checked = validateComponentProps(
14803
+ version,
14804
+ component.props ?? {},
14805
+ custom.name,
14806
+ { clean: vOpts.allowUnknownFields === true }
14807
+ );
14808
+ if (!checked.valid)
14809
+ throw new ComponentValidationError2(
14810
+ (checked.errors ?? []).map((e) => ({ path: path4, message: e.message })),
14811
+ component
14812
+ );
14813
+ const output = await version.render({
14814
+ props: cleanComponentProps(version, component.props ?? {}),
14815
+ theme: modedTheme,
14816
+ children: component.children,
14817
+ addWarning: (message, context) => warnings.push({
14818
+ component: custom.name,
14819
+ message,
14820
+ severity: "warning",
14821
+ context
14822
+ })
14823
+ });
14824
+ const emitted = Array.isArray(output) ? output : [output];
14825
+ validateEmitted?.(emitted, custom.name, path4);
14826
+ return emitted;
14827
+ },
14828
+ preserveSet
14829
+ );
14830
+ const [modedDoc2] = normalizeDocument(expanded.document);
14831
+ return {
14832
+ modedRoot,
14833
+ modedTheme,
14834
+ themeName,
14835
+ modedDoc: modedDoc2,
14836
+ sourceMap: expanded.sourceMap,
14837
+ blockPaths: expanded.blocks,
14838
+ warnings,
14839
+ preserveSet,
14840
+ processed: {
14841
+ standard: modedDoc2.children ?? [],
14842
+ preserved: expanded.preserved.children ?? []
14843
+ }
14844
+ };
14845
+ }
14768
14846
  const processed = await processDocumentComponents(
14769
14847
  modedRoot.children || [],
14770
14848
  preserveSet,
@@ -14789,9 +14867,13 @@ function createBuilderImpl(state) {
14789
14867
  }
14790
14868
  async function expandStandardDefinition(document, options) {
14791
14869
  try {
14792
- const { modedDoc, warnings } = await expandDocument(document, options);
14870
+ const { modedDoc, warnings, sourceMap } = await expandDocument(
14871
+ document,
14872
+ options
14873
+ );
14793
14874
  return {
14794
14875
  standardDefinition: modedDoc,
14876
+ ...sourceMap && { sourceMap },
14795
14877
  warnings: warnings.length > 0 ? warnings : null
14796
14878
  };
14797
14879
  } catch (error) {
@@ -14809,6 +14891,8 @@ function createBuilderImpl(state) {
14809
14891
  themeName,
14810
14892
  processed,
14811
14893
  modedDoc,
14894
+ sourceMap,
14895
+ blockPaths,
14812
14896
  warnings,
14813
14897
  preserveSet
14814
14898
  } = await expandDocument(document, options);
@@ -14819,7 +14903,13 @@ function createBuilderImpl(state) {
14819
14903
  const { buffer } = await generateBufferViaIr(modedDoc, {
14820
14904
  // The prologue already ran: the theme had to be resolved before custom
14821
14905
  // components expanded, because each one's `render` is handed it.
14822
- context: { document: modedDoc, theme: modedTheme, themeName },
14906
+ context: {
14907
+ document: modedDoc,
14908
+ theme: modedTheme,
14909
+ themeName,
14910
+ ...sourceMap !== void 0 && { sourceMap },
14911
+ ...blockPaths !== void 0 && { blockPaths }
14912
+ },
14823
14913
  ...state.services ? { services: state.services } : {},
14824
14914
  ...state.fonts ? { fonts: state.fonts } : {},
14825
14915
  ...baseDir !== void 0 ? { baseDir } : {},
@@ -14876,7 +14966,7 @@ function createBuilderImpl(state) {
14876
14966
  function validate(document) {
14877
14967
  try {
14878
14968
  const internalDocument = document;
14879
- const result = validateDocument(
14969
+ const result = validateDocument2(
14880
14970
  internalDocument,
14881
14971
  state.components
14882
14972
  );
@@ -14955,7 +15045,6 @@ function getCoreVersion() {
14955
15045
  return "Core v1.0.0";
14956
15046
  }
14957
15047
  export {
14958
- BLOCK_NAMES,
14959
15048
  ComponentValidationError2 as ComponentValidationError,
14960
15049
  DocumentGenerator as CoreDocumentGenerator,
14961
15050
  DEFAULT_DOCX_RENDERER_ID,
@@ -14970,7 +15059,6 @@ export {
14970
15059
  analyzeDocxQuality,
14971
15060
  blockSlotBudgets,
14972
15061
  cleanComponentProps,
14973
- compileKeyTakeaways,
14974
15062
  consultingTheme,
14975
15063
  createComponent,
14976
15064
  createDocumentGenerator,
@@ -15027,7 +15115,7 @@ export {
15027
15115
  themes,
15028
15116
  toAuthoredPointer,
15029
15117
  validateComponentProps,
15030
- validateDocument,
15118
+ validateDocument2 as validateDocument,
15031
15119
  validateJsonSchema,
15032
15120
  validateThemeJsonString,
15033
15121
  vermilionTheme