zodToJsonSchema.mjs 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import { parseDef } from "./parseDef.mjs";
  2. import { getRefs } from "./Refs.mjs";
  3. import { zodDef, isEmptyObj } from "./util.mjs";
  4. const zodToJsonSchema = (schema, options) => {
  5. const refs = getRefs(options);
  6. const name = typeof options === 'string' ? options
  7. : options?.nameStrategy === 'title' ? undefined
  8. : options?.name;
  9. const main = parseDef(schema._def, name === undefined ? refs : ({
  10. ...refs,
  11. currentPath: [...refs.basePath, refs.definitionPath, name],
  12. }), false) ?? {};
  13. const title = typeof options === 'object' && options.name !== undefined && options.nameStrategy === 'title' ?
  14. options.name
  15. : undefined;
  16. if (title !== undefined) {
  17. main.title = title;
  18. }
  19. const definitions = (() => {
  20. if (isEmptyObj(refs.definitions)) {
  21. return undefined;
  22. }
  23. const definitions = {};
  24. const processedDefinitions = new Set();
  25. // the call to `parseDef()` here might itself add more entries to `.definitions`
  26. // so we need to continually evaluate definitions until we've resolved all of them
  27. //
  28. // we have a generous iteration limit here to avoid blowing up the stack if there
  29. // are any bugs that would otherwise result in us iterating indefinitely
  30. for (let i = 0; i < 500; i++) {
  31. const newDefinitions = Object.entries(refs.definitions).filter(([key]) => !processedDefinitions.has(key));
  32. if (newDefinitions.length === 0)
  33. break;
  34. for (const [key, schema] of newDefinitions) {
  35. definitions[key] =
  36. parseDef(zodDef(schema), { ...refs, currentPath: [...refs.basePath, refs.definitionPath, key] }, true) ?? {};
  37. processedDefinitions.add(key);
  38. }
  39. }
  40. return definitions;
  41. })();
  42. const combined = name === undefined ?
  43. definitions ?
  44. {
  45. ...main,
  46. [refs.definitionPath]: definitions,
  47. }
  48. : main
  49. : refs.nameStrategy === 'duplicate-ref' ?
  50. {
  51. ...main,
  52. ...(definitions || refs.seenRefs.size ?
  53. {
  54. [refs.definitionPath]: {
  55. ...definitions,
  56. // only actually duplicate the schema definition if it was ever referenced
  57. // otherwise the duplication is completely pointless
  58. ...(refs.seenRefs.size ? { [name]: main } : undefined),
  59. },
  60. }
  61. : undefined),
  62. }
  63. : {
  64. $ref: [...(refs.$refStrategy === 'relative' ? [] : refs.basePath), refs.definitionPath, name].join('/'),
  65. [refs.definitionPath]: {
  66. ...definitions,
  67. [name]: main,
  68. },
  69. };
  70. if (refs.target === 'jsonSchema7') {
  71. combined.$schema = 'http://json-schema.org/draft-07/schema#';
  72. }
  73. else if (refs.target === 'jsonSchema2019-09') {
  74. combined.$schema = 'https://json-schema.org/draft/2019-09/schema#';
  75. }
  76. return combined;
  77. };
  78. export { zodToJsonSchema };
  79. //# sourceMappingURL=zodToJsonSchema.mjs.map