@json-to-office/core-docx 0.34.0 → 0.36.1

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
@@ -2665,10 +2665,68 @@ function resolveFromBaseDir(filePath) {
2665
2665
  if (!base || isAbsolute(filePath)) return filePath;
2666
2666
  return resolve(base, filePath);
2667
2667
  }
2668
+ var warningsStorage = new AsyncLocalStorage();
2669
+ function runWithWarnings(warnings, callback) {
2670
+ return warnings === void 0 ? callback() : warningsStorage.run(warnings, callback);
2671
+ }
2672
+ function reportWarning(component, code, message, context) {
2673
+ const warnings = warningsStorage.getStore();
2674
+ if (warnings) {
2675
+ warnings.push({
2676
+ component,
2677
+ message,
2678
+ severity: "warning",
2679
+ context: { code, ...context }
2680
+ });
2681
+ return;
2682
+ }
2683
+ console.warn(`[json-to-docx] [${code}] ${message}`);
2684
+ }
2668
2685
 
2669
2686
  // src/utils/imageUtils.ts
2670
2687
  init_widthUtils();
2671
2688
  import { ImageRun } from "docx";
2689
+ var SVG_RASTER_SCALE = 3;
2690
+ var SVG_MAX_EDGE_PX = 4096;
2691
+ var SVG_MIN_EDGE_PX = 16;
2692
+ var resvgModule;
2693
+ function loadResvg() {
2694
+ resvgModule ??= import("@resvg/resvg-js");
2695
+ return resvgModule;
2696
+ }
2697
+ function toRasterPx(px) {
2698
+ return Math.min(
2699
+ SVG_MAX_EDGE_PX,
2700
+ Math.max(SVG_MIN_EDGE_PX, Math.round(px * SVG_RASTER_SCALE))
2701
+ );
2702
+ }
2703
+ async function rasterizeSvgFallback(svg, transformation) {
2704
+ let Resvg;
2705
+ try {
2706
+ ({ Resvg } = await loadResvg());
2707
+ } catch (error) {
2708
+ reportWarning(
2709
+ "image",
2710
+ "IMAGE_SVG_RASTER_FAILED",
2711
+ `Could not load the SVG rasterizer, so inline SVG keeps a fallback that only Word 2016+ can draw: ${String(error)}`
2712
+ );
2713
+ return void 0;
2714
+ }
2715
+ try {
2716
+ const markup = svg.toString("utf-8");
2717
+ const probeImage = new Resvg(markup);
2718
+ const wide = probeImage.width / probeImage.height > transformation.width / transformation.height;
2719
+ const fitTo = wide ? { mode: "height", value: toRasterPx(transformation.height) } : { mode: "width", value: toRasterPx(transformation.width) };
2720
+ return Buffer.from(new Resvg(markup, { fitTo }).render().asPng());
2721
+ } catch (error) {
2722
+ reportWarning(
2723
+ "image",
2724
+ "IMAGE_SVG_RASTER_FAILED",
2725
+ `Could not rasterize inline SVG, so its fallback only renders in Word 2016+: ${String(error)}`
2726
+ );
2727
+ return void 0;
2728
+ }
2729
+ }
2672
2730
  function parseWidthValue(width, availableWidthPx) {
2673
2731
  if (typeof width === "number") {
2674
2732
  return width;
@@ -2778,17 +2836,18 @@ function detectImageType(imagePath, responseContentType) {
2778
2836
  if (typeFromExtension) return typeFromExtension;
2779
2837
  return "png";
2780
2838
  }
2781
- function createTypedImageRun(opts) {
2839
+ async function createTypedImageRun(opts) {
2782
2840
  const base = {
2783
2841
  data: opts.data,
2784
2842
  transformation: opts.transformation,
2785
2843
  ...opts.floating && { floating: opts.floating }
2786
2844
  };
2787
2845
  if (opts.type === "svg") {
2846
+ const raster = await rasterizeSvgFallback(opts.data, opts.transformation);
2788
2847
  return new ImageRun({
2789
2848
  type: "svg",
2790
2849
  ...base,
2791
- fallback: { type: "png", data: opts.data }
2850
+ fallback: { type: "png", data: raster ?? opts.data }
2792
2851
  });
2793
2852
  }
2794
2853
  return new ImageRun({ type: opts.type, ...base });
@@ -5217,7 +5276,7 @@ async function createImage(path4, theme, themeName, options = {}) {
5217
5276
  const { mapFloatingOptions: mapFloatingOptions2 } = await Promise.resolve().then(() => (init_docxImagePositioning(), docxImagePositioning_exports));
5218
5277
  const floating = isFloating ? mapFloatingOptions2(options.floating, theme, themeName) : void 0;
5219
5278
  const imageType = detectImageType(imagePath, responseContentType);
5220
- const imageRun = createTypedImageRun({
5279
+ const imageRun = await createTypedImageRun({
5221
5280
  type: imageType,
5222
5281
  data: imageBuffer,
5223
5282
  transformation: { width: dimensions.width, height: dimensions.height },
@@ -5762,7 +5821,7 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
5762
5821
  // fallback height
5763
5822
  );
5764
5823
  const imgType = detectImageType(imageSource, imageResult.contentType);
5765
- const imageRun = createTypedImageRun({
5824
+ const imageRun = await createTypedImageRun({
5766
5825
  type: imgType,
5767
5826
  data: imageResult.buffer,
5768
5827
  transformation: {
@@ -7610,14 +7669,19 @@ function effectiveVisualServerUrl(props, serviceConfig) {
7610
7669
  if (serviceConfig?.render) return void 0;
7611
7670
  return props.serverUrl;
7612
7671
  }
7613
- async function rasterizeVisualSlide(presentation, dpi, propsServerUrl, serviceConfig, baseDir) {
7672
+ async function rasterizeVisualSlide(presentation, dpi, propsServerUrl, serviceConfig, baseDir, fonts) {
7614
7673
  if (!isNodeEnvironment()) {
7615
7674
  throw new Error(
7616
7675
  "Visual rasterization requires a Node.js environment. It is not available in browser environments."
7617
7676
  );
7618
7677
  }
7619
7678
  if (serviceConfig?.render) {
7620
- return serviceConfig.render({ presentation, dpi, baseDir });
7679
+ return serviceConfig.render({
7680
+ presentation,
7681
+ dpi,
7682
+ baseDir,
7683
+ ...fonts?.length ? { fonts: [...fonts] } : {}
7684
+ });
7621
7685
  }
7622
7686
  const serverUrl = resolveServiceUrl(
7623
7687
  propsServerUrl,
@@ -7627,7 +7691,12 @@ async function rasterizeVisualSlide(presentation, dpi, propsServerUrl, serviceCo
7627
7691
  const response = await postJsonToService({
7628
7692
  url: serverUrl,
7629
7693
  path: "/rasterize",
7630
- body: { presentation, dpi, ...baseDir !== void 0 && { baseDir } },
7694
+ body: {
7695
+ presentation,
7696
+ dpi,
7697
+ ...baseDir !== void 0 && { baseDir },
7698
+ ...fonts?.length ? { fonts } : {}
7699
+ },
7631
7700
  headers: serviceConfig?.headers,
7632
7701
  serviceLabel: "PPTX rasterization service",
7633
7702
  onUnreachable: (url, cause) => `PPTX rasterization service is not reachable at ${url}. Configure services.pptx with a \`render\` callback or a running \`serverUrl\`.
@@ -7673,7 +7742,8 @@ async function renderVisualComponent(component, theme, themeName, context) {
7673
7742
  dpi,
7674
7743
  props.serverUrl,
7675
7744
  serviceConfig,
7676
- getBaseDir()
7745
+ getBaseDir(),
7746
+ context?.visualFonts
7677
7747
  );
7678
7748
  }
7679
7749
  return await createImage(
@@ -7807,6 +7877,11 @@ function collectVisualProps(root) {
7807
7877
  function toErrorMessage(error) {
7808
7878
  return error instanceof Error ? error.message : String(error);
7809
7879
  }
7880
+ var HTTP_STATUS_IN_MESSAGE = /\breturned (\d{3})\b/;
7881
+ function isSchemaRejection(error) {
7882
+ const match = HTTP_STATUS_IN_MESSAGE.exec(toErrorMessage(error));
7883
+ return match !== null && match[1] === "400";
7884
+ }
7810
7885
  function* chunksOf(items, size) {
7811
7886
  for (let i = 0; i < items.length; i += size) yield items.slice(i, i + size);
7812
7887
  }
@@ -7865,22 +7940,38 @@ async function prerasterizeVisuals(root, serviceConfig, options = {}) {
7865
7940
  const limit = createLimiter(
7866
7941
  Math.max(1, options.concurrency ?? DEFAULT_FALLBACK_CONCURRENCY)
7867
7942
  );
7943
+ let fontsRejected = false;
7944
+ const requestFonts = () => fontsRejected || !options.fonts?.length ? void 0 : options.fonts;
7945
+ const rasterizeOne = async (target) => {
7946
+ const fonts = requestFonts();
7947
+ const call = (withFonts) => rasterizeVisualSlide(
7948
+ target.presentation,
7949
+ target.dpi,
7950
+ void 0,
7951
+ serviceConfig,
7952
+ options.baseDir,
7953
+ withFonts
7954
+ );
7955
+ try {
7956
+ return { ok: true, ...await call(fonts) };
7957
+ } catch (error) {
7958
+ if (!fonts || !isSchemaRejection(error)) {
7959
+ return { ok: false, error: toErrorMessage(error) };
7960
+ }
7961
+ try {
7962
+ const result = await call(void 0);
7963
+ fontsRejected = true;
7964
+ return { ok: true, ...result };
7965
+ } catch {
7966
+ return { ok: false, error: toErrorMessage(error) };
7967
+ }
7968
+ }
7969
+ };
7868
7970
  const rasterizeIndividually = async (chunk) => {
7869
7971
  await Promise.all(
7870
7972
  chunk.map(
7871
7973
  (target) => limit(async () => {
7872
- try {
7873
- const result = await rasterizeVisualSlide(
7874
- target.presentation,
7875
- target.dpi,
7876
- void 0,
7877
- serviceConfig,
7878
- options.baseDir
7879
- );
7880
- map.set(target.key, { ok: true, ...result });
7881
- } catch (error) {
7882
- map.set(target.key, { ok: false, error: toErrorMessage(error) });
7883
- }
7974
+ map.set(target.key, await rasterizeOne(target));
7884
7975
  })
7885
7976
  )
7886
7977
  );
@@ -7893,7 +7984,8 @@ async function prerasterizeVisuals(root, serviceConfig, options = {}) {
7893
7984
  presentation: target.presentation,
7894
7985
  dpi: target.dpi
7895
7986
  })),
7896
- ...options.baseDir !== void 0 && { baseDir: options.baseDir }
7987
+ ...options.baseDir !== void 0 && { baseDir: options.baseDir },
7988
+ ...options.fonts?.length ? { fonts: [...options.fonts] } : {}
7897
7989
  });
7898
7990
  if (!applyBatchResponse(chunk, response, map)) {
7899
7991
  throw new Error(
@@ -7922,10 +8014,10 @@ async function prerasterizeVisuals(root, serviceConfig, options = {}) {
7922
8014
  serviceConfig?.serverUrl,
7923
8015
  DEFAULT_RASTERIZE_SERVER_URL
7924
8016
  );
7925
- for (const chunk of chunksOf(unique, MAX_RASTERIZE_BATCH_SLIDES)) {
7926
- let applied = false;
8017
+ const postBatch = async (chunk, fonts) => {
8018
+ let response;
7927
8019
  try {
7928
- const response = await postJsonToService({
8020
+ response = await postJsonToService({
7929
8021
  url: serverUrl,
7930
8022
  path: "/rasterize/batch",
7931
8023
  body: {
@@ -7933,18 +8025,36 @@ async function prerasterizeVisuals(root, serviceConfig, options = {}) {
7933
8025
  presentation: target.presentation,
7934
8026
  dpi: target.dpi
7935
8027
  })),
7936
- ...options.baseDir !== void 0 && { baseDir: options.baseDir }
8028
+ ...options.baseDir !== void 0 && { baseDir: options.baseDir },
8029
+ ...fonts?.length ? { fonts } : {}
7937
8030
  },
7938
8031
  headers: serviceConfig?.headers,
7939
8032
  timeoutMs: BATCH_TIMEOUT_BASE_MS + BATCH_TIMEOUT_PER_SLIDE_MS * chunk.length,
7940
8033
  serviceLabel: "PPTX batch rasterization service",
7941
8034
  onUnreachable: (url, cause) => `PPTX rasterization service is not reachable at ${url}. Cause: ${cause}`
7942
8035
  });
7943
- applied = applyBatchResponse(chunk, await response.json(), map);
8036
+ } catch (error) {
8037
+ return { applied: false, schemaRejected: isSchemaRejection(error) };
8038
+ }
8039
+ try {
8040
+ if (applyBatchResponse(chunk, await response.json(), map)) {
8041
+ return { applied: true };
8042
+ }
7944
8043
  } catch {
7945
- applied = false;
7946
8044
  }
7947
- if (!applied) await rasterizeIndividually(chunk);
8045
+ return { applied: false, schemaRejected: false };
8046
+ };
8047
+ for (const chunk of chunksOf(unique, MAX_RASTERIZE_BATCH_SLIDES)) {
8048
+ const fonts = requestFonts();
8049
+ let outcome = await postBatch(chunk, fonts);
8050
+ if (!outcome.applied && fonts && outcome.schemaRejected) {
8051
+ const retry = await postBatch(chunk, void 0);
8052
+ if (retry.applied) {
8053
+ fontsRejected = true;
8054
+ outcome = retry;
8055
+ }
8056
+ }
8057
+ if (!outcome.applied) await rasterizeIndividually(chunk);
7948
8058
  }
7949
8059
  return map;
7950
8060
  }
@@ -8053,23 +8163,26 @@ function coreProperties(metadata) {
8053
8163
  async function renderDocument(structure, layout, options) {
8054
8164
  return runWithGenerationDate(
8055
8165
  structure.metadata.date,
8056
- () => runWithBaseDir(
8057
- options?.baseDir,
8058
- () => globalBookmarkRegistry.runScoped(
8059
- () => globalRevisionIdRegistry.runScoped(
8060
- () => globalNumberingRegistry.runScoped(
8061
- () => globalSectionBookmarkRegistry.runScoped(
8062
- () => (
8063
- // Comment ids are a separate OOXML namespace from w:ins/w:del,
8064
- // but they need the same per-render isolation: outside this nest
8065
- // concurrent generations would interleave counters and an anchor
8066
- // would point at another document's comment body.
8067
- globalCommentRegistry.runScoped(
8068
- () => (
8069
- // Footnote ids are document-scoped too: a reference resolved
8070
- // against another render's counter points at the wrong body.
8071
- globalNoteRegistry.runScoped(
8072
- () => renderDocumentScoped(structure, layout, options)
8166
+ () => runWithWarnings(
8167
+ options?.warnings,
8168
+ () => runWithBaseDir(
8169
+ options?.baseDir,
8170
+ () => globalBookmarkRegistry.runScoped(
8171
+ () => globalRevisionIdRegistry.runScoped(
8172
+ () => globalNumberingRegistry.runScoped(
8173
+ () => globalSectionBookmarkRegistry.runScoped(
8174
+ () => (
8175
+ // Comment ids are a separate OOXML namespace from w:ins/w:del,
8176
+ // but they need the same per-render isolation: outside this nest
8177
+ // concurrent generations would interleave counters and an anchor
8178
+ // would point at another document's comment body.
8179
+ globalCommentRegistry.runScoped(
8180
+ () => (
8181
+ // Footnote ids are document-scoped too: a reference resolved
8182
+ // against another render's counter points at the wrong body.
8183
+ globalNoteRegistry.runScoped(
8184
+ () => renderDocumentScoped(structure, layout, options)
8185
+ )
8073
8186
  )
8074
8187
  )
8075
8188
  )
@@ -8094,11 +8207,12 @@ async function renderDocumentScoped(structure, layout, options) {
8094
8207
  structure.themeName
8095
8208
  );
8096
8209
  context.services = options?.services;
8210
+ context.visualFonts = options?.visualFonts;
8097
8211
  try {
8098
8212
  const visualRasterResults = await prerasterizeVisuals(
8099
8213
  layout.sections,
8100
8214
  options?.services?.pptx,
8101
- { baseDir: getBaseDir() }
8215
+ { baseDir: getBaseDir(), fonts: options?.visualFonts }
8102
8216
  );
8103
8217
  if (visualRasterResults.size > 0) {
8104
8218
  context.visualRasterResults = visualRasterResults;
@@ -8288,7 +8402,7 @@ async function renderHeaderFooterComponents(components, theme, themeName, contex
8288
8402
  themeName
8289
8403
  );
8290
8404
  const imageType = detectImageType(imageSource, responseContentType);
8291
- const imageRun = createTypedImageRun({
8405
+ const imageRun = await createTypedImageRun({
8292
8406
  type: imageType,
8293
8407
  data: imageBuffer,
8294
8408
  transformation: {
@@ -8468,14 +8582,17 @@ async function renderComponent(component, theme, themeName, context) {
8468
8582
  import {
8469
8583
  collectFontNamesFromDocx,
8470
8584
  validateFontReferences,
8471
- FontRegistry
8585
+ FontRegistry,
8586
+ documentFontRegistry,
8587
+ themeFontRegistry,
8588
+ mergeFontRegistries
8472
8589
  } from "@json-to-office/shared";
8473
8590
  import {
8474
8591
  loadFileFontSource,
8475
8592
  FontDiskCache,
8476
8593
  fetchVariableFontSource
8477
8594
  } from "@json-to-office/shared/fonts/node";
8478
- async function resolveDocumentFonts(document, theme, fonts, warnings) {
8595
+ async function resolveDocumentFonts(document, theme, fonts, warnings, forceMaterialize = false) {
8479
8596
  const emit = (code, message) => {
8480
8597
  if (warnings) {
8481
8598
  warnings.push({
@@ -8492,9 +8609,14 @@ async function resolveDocumentFonts(document, theme, fonts, warnings) {
8492
8609
  for (const n of collectFontNamesFromDocx(document)) names.add(n);
8493
8610
  for (const n of collectFontNamesFromDocx(theme)) names.add(n);
8494
8611
  if (names.size === 0) return [];
8612
+ const registryEntries = mergeFontRegistries(
8613
+ themeFontRegistry(theme),
8614
+ documentFontRegistry(document),
8615
+ fonts?.extraEntries
8616
+ );
8495
8617
  const validation = validateFontReferences({
8496
8618
  referencedNames: names,
8497
- registeredEntries: fonts?.extraEntries
8619
+ registeredEntries: registryEntries
8498
8620
  });
8499
8621
  if (validation.warnings.length > 0) {
8500
8622
  if (fonts?.strict) {
@@ -8507,9 +8629,11 @@ async function resolveDocumentFonts(document, theme, fonts, warnings) {
8507
8629
  emit(w.code, w.message);
8508
8630
  }
8509
8631
  }
8510
- if (!fonts?.onResolved) return [];
8632
+ if (!fonts?.onResolved && !forceMaterialize) return [];
8511
8633
  const registry = new FontRegistry({
8512
- opts: fonts,
8634
+ // Spread keeps baseDir/googleFonts/mode/substitution intact for
8635
+ // materializeSource; extraEntries carries the merged registry.
8636
+ opts: { ...fonts, extraEntries: registryEntries },
8513
8637
  fileLoader: loadFileFontSource,
8514
8638
  variableLoader: fetchVariableFontSource,
8515
8639
  diskCache: fonts?.googleFonts?.cacheDir ? new FontDiskCache(fonts.googleFonts.cacheDir) : void 0
@@ -8520,10 +8644,13 @@ async function resolveDocumentFonts(document, theme, fonts, warnings) {
8520
8644
  emit("FONT_UNRESOLVED", msg);
8521
8645
  }
8522
8646
  }
8523
- fonts.onResolved(resolved);
8647
+ fonts?.onResolved?.(resolved);
8524
8648
  return resolved;
8525
8649
  }
8526
8650
 
8651
+ // src/core/generator.ts
8652
+ import { toRasterizeFontFaces } from "@json-to-office/shared/fonts/node";
8653
+
8527
8654
  // src/utils/packageDocument.ts
8528
8655
  import AdmZip2 from "adm-zip";
8529
8656
  import { Packer } from "docx";
@@ -8783,7 +8910,15 @@ async function generateDocumentWithCustomThemes(documentIn, customThemes, servic
8783
8910
  fonts,
8784
8911
  warnings
8785
8912
  });
8786
- await resolveDocumentFonts(document, theme, fonts, warnings);
8913
+ const hasVisual = collectVisualProps(document).length > 0;
8914
+ const resolvedFonts = await resolveDocumentFonts(
8915
+ document,
8916
+ theme,
8917
+ fonts,
8918
+ warnings,
8919
+ hasVisual
8920
+ );
8921
+ const visualFonts = hasVisual ? toRasterizeFontFaces(resolvedFonts, warnings) : [];
8787
8922
  const structure = await processDocument(
8788
8923
  document,
8789
8924
  theme,
@@ -8794,7 +8929,9 @@ async function generateDocumentWithCustomThemes(documentIn, customThemes, servic
8794
8929
  const renderedDocument = await renderDocument(structure, layout, {
8795
8930
  bypassCache: false,
8796
8931
  services,
8797
- baseDir
8932
+ baseDir,
8933
+ warnings,
8934
+ ...visualFonts.length > 0 && { visualFonts }
8798
8935
  });
8799
8936
  return renderedDocument;
8800
8937
  }
@@ -8908,7 +9045,11 @@ async function flattenVisuals(doc, options) {
8908
9045
  renderBatch: options.rasterizeBatch,
8909
9046
  dpi: options.dpi
8910
9047
  },
8911
- { baseDir: options.baseDir, concurrency: options.concurrency }
9048
+ {
9049
+ baseDir: options.baseDir,
9050
+ concurrency: options.concurrency,
9051
+ fonts: options.fonts
9052
+ }
8912
9053
  ).catch(() => /* @__PURE__ */ new Map());
8913
9054
  rasterize = async (request) => {
8914
9055
  const hit = preRasterized.get(
@@ -8929,6 +9070,7 @@ async function flattenVisuals(doc, options) {
8929
9070
  rasterize,
8930
9071
  dpi: options.dpi,
8931
9072
  baseDir: options.baseDir,
9073
+ fonts: options.fonts,
8932
9074
  limit: createLimiter(
8933
9075
  Math.max(1, options.concurrency ?? DEFAULT_CONCURRENCY)
8934
9076
  )
@@ -8942,7 +9084,8 @@ async function rasterizeVisual(obj, ctx) {
8942
9084
  () => ctx.rasterize({
8943
9085
  presentation: buildVisualPresentation(props),
8944
9086
  dpi,
8945
- baseDir: ctx.baseDir
9087
+ baseDir: ctx.baseDir,
9088
+ ...ctx.fonts?.length ? { fonts: [...ctx.fonts] } : {}
8946
9089
  })
8947
9090
  );
8948
9091
  const image = {
@@ -9218,6 +9361,7 @@ async function runExample(example, options = {}) {
9218
9361
 
9219
9362
  // src/plugin/createDocumentGenerator.ts
9220
9363
  init_themes();
9364
+ import { toRasterizeFontFaces as toRasterizeFontFaces2 } from "@json-to-office/shared/fonts/node";
9221
9365
 
9222
9366
  // src/plugin/version-resolver.ts
9223
9367
  import { resolveComponentVersion } from "@json-to-office/shared/plugin";
@@ -9745,7 +9889,15 @@ function createBuilderImpl(state) {
9745
9889
  warnings,
9746
9890
  preserveSet
9747
9891
  } = await expandDocument(document, options);
9748
- await resolveDocumentFonts(modedDoc, modedTheme, state.fonts, warnings);
9892
+ const hasVisual = collectVisualProps(modedDoc).length > 0;
9893
+ const resolvedFonts = await resolveDocumentFonts(
9894
+ modedDoc,
9895
+ modedTheme,
9896
+ state.fonts,
9897
+ warnings,
9898
+ hasVisual
9899
+ );
9900
+ const visualFonts = hasVisual ? toRasterizeFontFaces2(resolvedFonts, warnings) : [];
9749
9901
  const packageOptions = {
9750
9902
  deterministic: options?.deterministic ?? state.deterministic,
9751
9903
  generatedAt: options?.generatedAt ?? state.generatedAt
@@ -9760,7 +9912,9 @@ function createBuilderImpl(state) {
9760
9912
  const generatedDocument = await renderDocument(structure, layout, {
9761
9913
  services: state.services,
9762
9914
  bypassCache: !state.enableCache,
9763
- baseDir: options?.baseDir ?? state.baseDir
9915
+ baseDir: options?.baseDir ?? state.baseDir,
9916
+ warnings,
9917
+ ...visualFonts.length > 0 && { visualFonts }
9764
9918
  });
9765
9919
  const preservedDefinition = preserveSet ? {
9766
9920
  ...modedRoot,