@kubb/studio 5.5.0 → 5.5.2

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.cjs CHANGED
@@ -725,7 +725,7 @@ function resolveAgentCapacity(env = process.env) {
725
725
  }
726
726
  //#endregion
727
727
  //#region package.json
728
- var version = "5.5.0";
728
+ var version = "5.5.2";
729
729
  //#endregion
730
730
  //#region src/operations/resolveConfig.ts
731
731
  /**
@@ -838,8 +838,46 @@ async function resolvePlugins(plugins) {
838
838
  }));
839
839
  }
840
840
  /**
841
+ * Whether `value` comes back unchanged from a JSON round trip. A function, a `RegExp` or another
842
+ * class instance does not: the agent reports options to Studio as JSON, so what Studio sends back
843
+ * for such a value is a lossy copy (`macros: [{ name }]` without its hooks, `pattern: {}`).
844
+ */
845
+ function survivesJson(value) {
846
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
847
+ if (typeof value === "number") return Number.isFinite(value);
848
+ if (Array.isArray(value)) return value.every(survivesJson);
849
+ if ((0, remeda.isPlainObject)(value)) return Object.values(value).every(survivesJson);
850
+ return false;
851
+ }
852
+ /**
853
+ * Deep-merges Studio's options over the disk options, with Studio taking priority, except where the
854
+ * disk value cannot survive JSON. Studio only ever saw a lossy copy of such a value, so the disk
855
+ * value is kept rather than replaced by what Studio echoes back. Plain objects merge key by key, so
856
+ * Studio can still change a literal that sits next to a function (e.g. `group.type` beside `group.name`).
857
+ *
858
+ * @example
859
+ * ```ts
860
+ * mergeOptions({ macros: [camelCaseMacro], syntaxType: 'type' }, { macros: [{ name: 'camelCase' }], syntaxType: 'interface' })
861
+ * // { macros: [camelCaseMacro], syntaxType: 'interface' }
862
+ * ```
863
+ */
864
+ function mergeOptions(disk, studio) {
865
+ const merged = { ...disk };
866
+ for (const [key, value] of Object.entries(studio)) {
867
+ const current = disk[key];
868
+ if ((0, remeda.isPlainObject)(current) && (0, remeda.isPlainObject)(value)) {
869
+ merged[key] = mergeOptions(current, value);
870
+ continue;
871
+ }
872
+ if (current !== void 0 && !survivesJson(current)) continue;
873
+ merged[key] = value;
874
+ }
875
+ return merged;
876
+ }
877
+ /**
841
878
  * Merges studio plugin options with disk config plugins.
842
- * Studio takes priority: options from studio win over disk, and a plugin Studio explicitly
879
+ * Studio takes priority: options from studio win over disk (except values only the disk config
880
+ * can express, see {@link mergeOptions}), and a plugin Studio explicitly
843
881
  * disabled is dropped even when the disk config still lists it. Disk plugins without a studio
844
882
  * counterpart are kept as-is. Studio plugins not present on disk are appended.
845
883
  *
@@ -858,7 +896,7 @@ async function mergePlugins(diskPlugins, studioPlugins) {
858
896
  const merged = await Promise.all(activeDiskPlugins.map(async (diskPlugin) => {
859
897
  const studioEntry = studioEntryByName.get(diskPlugin.name);
860
898
  if (!studioEntry) return diskPlugin;
861
- const options = (0, remeda.mergeDeep)(diskPlugin.options ?? {}, studioEntry.options ?? {});
899
+ const options = mergeOptions(diskPlugin.options ?? {}, studioEntry.options ?? {});
862
900
  const [resolved] = await resolvePlugins([{
863
901
  name: studioEntry.name,
864
902
  options
@@ -882,7 +920,7 @@ async function mergeAdapter(diskAdapter, studioOptions) {
882
920
  const packageName = `@kubb/adapter-${diskAdapter.name}`;
883
921
  const factory = (await importFromProject(packageName))[toExportName(packageName)];
884
922
  if (typeof factory !== "function") return diskAdapter;
885
- return factory((0, remeda.mergeDeep)(diskAdapter.options ?? {}, studioOptions));
923
+ return factory(mergeOptions(diskAdapter.options ?? {}, studioOptions));
886
924
  }
887
925
  //#endregion
888
926
  //#region src/operations/configFile.ts