@gabrielbryk/json-schema-to-zod 2.12.0 → 2.13.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.
Files changed (53) hide show
  1. package/.github/RELEASE_SETUP.md +120 -0
  2. package/.github/TOOLING_GUIDE.md +169 -0
  3. package/.github/dependabot.yml +52 -0
  4. package/.github/workflows/ci.yml +33 -0
  5. package/.github/workflows/release.yml +12 -4
  6. package/.github/workflows/security.yml +40 -0
  7. package/.husky/commit-msg +1 -0
  8. package/.husky/pre-commit +1 -0
  9. package/.lintstagedrc.json +3 -0
  10. package/.prettierrc +20 -0
  11. package/AGENTS.md +7 -0
  12. package/CHANGELOG.md +13 -4
  13. package/README.md +9 -9
  14. package/commitlint.config.js +24 -0
  15. package/createIndex.ts +4 -4
  16. package/dist/cli.js +3 -4
  17. package/dist/core/analyzeSchema.js +28 -5
  18. package/dist/core/emitZod.js +11 -4
  19. package/dist/generators/generateBundle.js +67 -92
  20. package/dist/parsers/parseAllOf.js +11 -12
  21. package/dist/parsers/parseAnyOf.js +2 -2
  22. package/dist/parsers/parseArray.js +38 -12
  23. package/dist/parsers/parseMultipleType.js +2 -2
  24. package/dist/parsers/parseNumber.js +44 -102
  25. package/dist/parsers/parseObject.js +138 -393
  26. package/dist/parsers/parseOneOf.js +57 -100
  27. package/dist/parsers/parseSchema.js +132 -55
  28. package/dist/parsers/parseSimpleDiscriminatedOneOf.js +2 -2
  29. package/dist/parsers/parseString.js +113 -253
  30. package/dist/types/Types.d.ts +22 -1
  31. package/dist/types/core/analyzeSchema.d.ts +1 -0
  32. package/dist/types/generators/generateBundle.d.ts +1 -1
  33. package/dist/utils/cliTools.js +1 -2
  34. package/dist/utils/esmEmitter.js +6 -2
  35. package/dist/utils/extractInlineObject.js +1 -3
  36. package/dist/utils/jsdocs.js +1 -4
  37. package/dist/utils/liftInlineObjects.js +76 -15
  38. package/dist/utils/resolveRef.js +35 -10
  39. package/dist/utils/schemaRepresentation.js +35 -66
  40. package/dist/zodToJsonSchema.js +1 -2
  41. package/docs/IMPROVEMENT-PLAN.md +30 -12
  42. package/docs/ZOD-V4-RECURSIVE-TYPE-LIMITATIONS.md +70 -25
  43. package/docs/proposals/allof-required-merging.md +10 -4
  44. package/docs/proposals/bundle-refactor.md +10 -4
  45. package/docs/proposals/discriminated-union-with-default.md +18 -14
  46. package/docs/proposals/inline-object-lifting.md +15 -5
  47. package/docs/proposals/ref-anchor-support.md +11 -0
  48. package/output.txt +67 -0
  49. package/package.json +18 -5
  50. package/scripts/generateWorkflowSchema.ts +5 -14
  51. package/scripts/regenerate_bundle.ts +25 -0
  52. package/tsc_output.txt +542 -0
  53. package/tsc_output_2.txt +489 -0
@@ -3,246 +3,114 @@ import { parseSchema } from "./parseSchema.js";
3
3
  export const parseString = (schema, refs) => {
4
4
  const formatError = schema.errorMessage?.format;
5
5
  const refContext = ensureRefs(refs);
6
- // Map formats to top-level Zod functions and their return types
6
+ // Map formats to Zod string methods
7
7
  const topLevelFormatMap = {
8
- email: { fn: "z.email", zodType: "z.ZodEmail" },
9
- ipv4: { fn: "z.ipv4", zodType: "z.ZodIPv4" },
10
- ipv6: { fn: "z.ipv6", zodType: "z.ZodIPv6" },
11
- uri: { fn: "z.url", zodType: "z.ZodURL" },
12
- uuid: { fn: "z.uuid", zodType: "z.ZodUUID" },
13
- cuid: { fn: "z.cuid", zodType: "z.ZodCUID" },
14
- cuid2: { fn: "z.cuid2", zodType: "z.ZodCUID2" },
15
- nanoid: { fn: "z.nanoid", zodType: "z.ZodNanoID" },
16
- ulid: { fn: "z.ulid", zodType: "z.ZodULID" },
17
- jwt: { fn: "z.jwt", zodType: "z.ZodJWT" },
18
- e164: { fn: "z.e164", zodType: "z.ZodE164" },
19
- base64url: { fn: "z.base64url", zodType: "z.ZodBase64URL" },
20
- base64: { fn: "z.base64", zodType: "z.ZodBase64" },
21
- emoji: { fn: "z.emoji", zodType: "z.ZodEmoji" },
22
- "idn-email": { fn: "z.email", zodType: "z.ZodEmail" },
8
+ email: { fn: "z.email", zodType: "z.ZodString" },
9
+ ipv4: { fn: "z.ipv4", zodType: "z.ZodString" },
10
+ ipv6: { fn: "z.ipv6", zodType: "z.ZodString" },
11
+ uri: { fn: "z.url", zodType: "z.ZodString" },
12
+ uuid: { fn: "z.uuid", zodType: "z.ZodString" },
13
+ cuid: { fn: "z.cuid", zodType: "z.ZodString" },
14
+ cuid2: { fn: "z.cuid2", zodType: "z.ZodString" },
15
+ nanoid: { fn: "z.nanoid", zodType: "z.ZodString" },
16
+ ulid: { fn: "z.ulid", zodType: "z.ZodString" },
17
+ jwt: { fn: "z.jwt", zodType: "z.ZodString" },
18
+ e164: { fn: "z.e164", zodType: "z.ZodString" },
19
+ base64url: { fn: "z.base64url", zodType: "z.ZodString" },
20
+ base64: { fn: "z.base64", zodType: "z.ZodString" },
21
+ emoji: { fn: "z.emoji", zodType: "z.ZodString" },
22
+ "idn-email": { fn: "z.email", zodType: "z.ZodString" },
23
+ "date-time": { fn: "z.iso.datetime", zodType: "z.ZodString" },
24
+ date: { fn: "z.iso.date", zodType: "z.ZodString" },
25
+ time: { fn: "z.iso.time", zodType: "z.ZodString" },
26
+ duration: { fn: "z.iso.duration", zodType: "z.ZodString" },
23
27
  };
24
28
  const formatInfo = schema.format ? topLevelFormatMap[schema.format] : undefined;
25
29
  const formatFn = formatInfo?.fn;
26
- const formatParam = formatError !== undefined ? `{ error: ${JSON.stringify(formatError)} }` : "";
27
- let r = formatFn ? `${formatFn}(${formatParam})` : "z.string()";
28
- const formatHandled = Boolean(formatFn);
29
- let formatWasHandled = formatHandled;
30
- if (!formatHandled) {
31
- r += withMessage(schema, "format", ({ value }) => {
32
- switch (value) {
33
- case "email":
34
- formatWasHandled = true;
35
- return {
36
- opener: ".email(",
37
- closer: ")",
38
- messagePrefix: "{ error: ",
39
- messageCloser: " })",
40
- };
41
- case "ip":
42
- formatWasHandled = true;
43
- return {
44
- opener: ".ip(",
45
- closer: ")",
46
- messagePrefix: "{ error: ",
47
- messageCloser: " })",
48
- };
49
- case "ipv4":
50
- formatWasHandled = true;
51
- return {
52
- opener: '.ip({ version: "v4"',
53
- closer: " })",
54
- messagePrefix: ", error: ",
55
- messageCloser: " })",
56
- };
57
- case "ipv6":
58
- formatWasHandled = true;
59
- return {
60
- opener: '.ip({ version: "v6"',
61
- closer: " })",
62
- messagePrefix: ", error: ",
63
- messageCloser: " })",
64
- };
65
- case "uri":
66
- formatWasHandled = true;
67
- return {
68
- opener: ".url(",
69
- closer: ")",
70
- messagePrefix: "{ error: ",
71
- messageCloser: " })",
72
- };
73
- case "uuid":
74
- formatWasHandled = true;
75
- return {
76
- opener: ".uuid(",
77
- closer: ")",
78
- messagePrefix: "{ error: ",
79
- messageCloser: " })",
80
- };
81
- case "cuid":
82
- formatWasHandled = true;
83
- return {
84
- opener: ".cuid(",
85
- closer: ")",
86
- messagePrefix: "{ error: ",
87
- messageCloser: " })",
88
- };
89
- case "cuid2":
90
- formatWasHandled = true;
91
- return {
92
- opener: ".cuid2(",
93
- closer: ")",
94
- messagePrefix: "{ error: ",
95
- messageCloser: " })",
96
- };
97
- case "nanoid":
98
- formatWasHandled = true;
99
- return {
100
- opener: ".nanoid(",
101
- closer: ")",
102
- messagePrefix: "{ error: ",
103
- messageCloser: " })",
104
- };
105
- case "ulid":
106
- formatWasHandled = true;
107
- return {
108
- opener: ".ulid(",
109
- closer: ")",
110
- messagePrefix: "{ error: ",
111
- messageCloser: " })",
112
- };
113
- case "jwt":
114
- formatWasHandled = true;
115
- return {
116
- opener: ".jwt(",
117
- closer: ")",
118
- messagePrefix: "{ error: ",
119
- messageCloser: " })",
120
- };
121
- case "e164":
122
- formatWasHandled = true;
123
- return {
124
- opener: ".e164(",
125
- closer: ")",
126
- messagePrefix: "{ error: ",
127
- messageCloser: " })",
128
- };
129
- case "base64url":
130
- formatWasHandled = true;
131
- return {
132
- opener: ".base64url(",
133
- closer: ")",
134
- messagePrefix: "{ error: ",
135
- messageCloser: " })",
136
- };
137
- case "emoji":
138
- formatWasHandled = true;
139
- return {
140
- opener: ".emoji(",
141
- closer: ")",
142
- messagePrefix: "{ error: ",
143
- messageCloser: " })",
144
- };
145
- case "date-time":
146
- formatWasHandled = true;
147
- return {
148
- opener: ".datetime({ offset: true",
149
- closer: " })",
150
- messagePrefix: ", error: ",
151
- messageCloser: " })",
152
- };
153
- case "time":
154
- formatWasHandled = true;
155
- return {
156
- opener: ".time(",
157
- closer: ")",
158
- messagePrefix: "{ error: ",
159
- messageCloser: " })",
160
- };
161
- case "date":
162
- formatWasHandled = true;
163
- return {
164
- opener: ".date(",
165
- closer: ")",
166
- messagePrefix: "{ error: ",
167
- messageCloser: " })",
168
- };
169
- case "binary":
170
- formatWasHandled = true;
171
- return {
172
- opener: ".base64(",
173
- closer: ")",
174
- messagePrefix: "{ error: ",
175
- messageCloser: " })",
176
- };
177
- case "duration":
178
- formatWasHandled = true;
179
- return {
180
- opener: ".duration(",
181
- closer: ")",
182
- messagePrefix: "{ error: ",
183
- messageCloser: " })",
184
- };
185
- case "hostname":
186
- case "idn-hostname":
187
- formatWasHandled = true;
188
- return {
189
- opener: ".refine((val) => { if (typeof val !== \"string\" || val.length === 0 || val.length > 253) return false; return val.split(\".\").every((label) => label.length > 0 && label.length <= 63 && /^[A-Za-z0-9-]+$/.test(label) && label[0] !== \"-\" && label[label.length - 1] !== \"-\"); }",
190
- closer: ")",
191
- messagePrefix: ", { error: ",
192
- messageCloser: " })",
193
- };
194
- case "idn-email":
195
- formatWasHandled = true;
196
- return {
197
- opener: ".email(",
198
- closer: ")",
199
- messagePrefix: "{ error: ",
200
- messageCloser: " })",
201
- };
202
- case "uri-reference":
203
- case "iri":
204
- case "iri-reference":
205
- formatWasHandled = true;
206
- return {
207
- opener: '.refine((val) => { try { new URL(val, "http://example.com"); return true; } catch { return false; } }',
208
- closer: ")",
209
- messagePrefix: ", { error: ",
210
- messageCloser: " })",
211
- };
212
- case "json-pointer":
213
- formatWasHandled = true;
214
- return {
215
- opener: ".refine((val) => typeof val === \"string\" && /^(?:\\/(?:[^/~]|~[01])*)*$/.test(val)",
216
- closer: ")",
217
- messagePrefix: ", { error: ",
218
- messageCloser: " })",
219
- };
220
- case "relative-json-pointer":
221
- formatWasHandled = true;
222
- return {
223
- opener: ".refine((val) => typeof val === \"string\" && /^(?:0|[1-9][0-9]*)(?:#|(?:\\/(?:[^/~]|~[01])*))*$/.test(val)",
224
- closer: ")",
225
- messagePrefix: ", { error: ",
226
- messageCloser: " })",
227
- };
228
- case "uri-template":
229
- formatWasHandled = true;
230
- return {
231
- opener: ".refine((val) => { if (typeof val !== \"string\") return false; const opens = (val.match(/\\{/g) || []).length; const closes = (val.match(/\\}/g) || []).length; return opens === closes; }",
232
- closer: ")",
233
- messagePrefix: ", { error: ",
234
- messageCloser: " })",
235
- };
236
- case "regex":
237
- formatWasHandled = true;
238
- return {
239
- opener: ".refine((val) => { try { new RegExp(val); return true; } catch { return false; } }",
240
- closer: ")",
241
- messagePrefix: ", { error: ",
242
- messageCloser: " })",
243
- };
244
- }
245
- });
30
+ let r = "z.string()";
31
+ let zodType = "z.ZodString";
32
+ if (formatFn) {
33
+ const params = formatError !== undefined ? `{ message: ${JSON.stringify(formatError)} }` : "";
34
+ if (schema.format === "date-time") {
35
+ r = `z.iso.datetime({ offset: true${formatError ? `, message: ${JSON.stringify(formatError)}` : ""} })`;
36
+ }
37
+ else if (schema.format === "ipv4") {
38
+ r = `z.ipv4(${formatError ? `{ message: ${JSON.stringify(formatError)} }` : ""})`;
39
+ }
40
+ else if (schema.format === "ipv6") {
41
+ r = `z.ipv6(${formatError ? `{ message: ${JSON.stringify(formatError)} }` : ""})`;
42
+ }
43
+ else {
44
+ r = `${formatFn}(${params})`;
45
+ }
46
+ }
47
+ let formatWasHandled = Boolean(formatFn);
48
+ if (!formatWasHandled && schema.format) {
49
+ switch (schema.format) {
50
+ case "ip":
51
+ r += `.refine((val) => {
52
+ const v4 = z.ipv4().safeParse(val).success;
53
+ const v6 = z.ipv6().safeParse(val).success;
54
+ return v4 || v6;
55
+ }${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
56
+ formatWasHandled = true;
57
+ break;
58
+ case "binary":
59
+ r = `z.base64(${formatError ? `{ message: ${JSON.stringify(formatError)} }` : ""})`;
60
+ formatWasHandled = true;
61
+ break;
62
+ case "hostname":
63
+ case "idn-hostname":
64
+ r += `.refine((val) => {
65
+ if (typeof val !== "string" || val.length === 0 || val.length > 253) return false;
66
+ return val.split(".").every((label) => {
67
+ return label.length > 0 && label.length <= 63 && /^[A-Za-z0-9-]+$/.test(label) && label[0] !== "-" && label[label.length - 1] !== "-";
68
+ });
69
+ }${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
70
+ formatWasHandled = true;
71
+ break;
72
+ case "uri-reference":
73
+ case "iri":
74
+ case "iri-reference":
75
+ r += `.refine((val) => {
76
+ try {
77
+ new URL(val, "http://example.com");
78
+ return true;
79
+ } catch {
80
+ return false;
81
+ }
82
+ }${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
83
+ formatWasHandled = true;
84
+ break;
85
+ case "json-pointer":
86
+ r += `.refine((val) => typeof val === "string" && /^(?:\\/(?:[^/~]|~[01])*)*$/.test(val)${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
87
+ formatWasHandled = true;
88
+ break;
89
+ case "relative-json-pointer":
90
+ r += `.refine((val) => typeof val === "string" && /^(?:0|[1-9][0-9]*)(?:#|(?:\\/(?:[^/~]|~[01])*))*$/.test(val)${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
91
+ formatWasHandled = true;
92
+ break;
93
+ case "uri-template":
94
+ r += `.refine((val) => {
95
+ if (typeof val !== "string") return false;
96
+ const opens = (val.match(/\\{/g) || []).length;
97
+ const closes = (val.match(/\\}/g) || []).length;
98
+ return opens === closes;
99
+ }${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
100
+ formatWasHandled = true;
101
+ break;
102
+ case "regex":
103
+ r += `.refine((val) => {
104
+ try {
105
+ new RegExp(val);
106
+ return true;
107
+ } catch {
108
+ return false;
109
+ }
110
+ }${formatError ? `, { message: ${JSON.stringify(formatError)} }` : ""})`;
111
+ formatWasHandled = true;
112
+ break;
113
+ }
246
114
  }
247
115
  if (schema.format && !formatWasHandled) {
248
116
  refContext.onUnknownFormat?.(schema.format, refContext.path);
@@ -250,37 +118,31 @@ export const parseString = (schema, refs) => {
250
118
  r += withMessage(schema, "pattern", ({ json }) => ({
251
119
  opener: `.regex(new RegExp(${json})`,
252
120
  closer: ")",
253
- messagePrefix: ", { error: ",
121
+ messagePrefix: ", { message: ",
254
122
  messageCloser: " })",
255
123
  }));
256
124
  r += withMessage(schema, "minLength", ({ json }) => ({
257
125
  opener: `.min(${json}`,
258
126
  closer: ")",
259
- messagePrefix: ", { error: ",
127
+ messagePrefix: ", { message: ",
260
128
  messageCloser: " })",
261
129
  }));
262
130
  r += withMessage(schema, "maxLength", ({ json }) => ({
263
131
  opener: `.max(${json}`,
264
132
  closer: ")",
265
- messagePrefix: ", { error: ",
133
+ messagePrefix: ", { message: ",
266
134
  messageCloser: " })",
267
135
  }));
268
- r += withMessage(schema, "contentEncoding", ({ value }) => {
269
- if (value === "base64") {
270
- return {
271
- opener: ".base64(",
272
- closer: ")",
273
- messagePrefix: "{ error: ",
274
- messageCloser: " })",
275
- };
276
- }
277
- });
136
+ if (schema.contentEncoding === "base64" && schema.format !== "base64") {
137
+ const encodingError = schema.errorMessage?.contentEncoding;
138
+ r = `z.base64(${encodingError ? `{ message: ${JSON.stringify(encodingError)} }` : ""})`;
139
+ }
278
140
  const contentMediaType = withMessage(schema, "contentMediaType", ({ value }) => {
279
141
  if (value === "application/json") {
280
142
  return {
281
143
  opener: '.transform((str, ctx) => { try { return JSON.parse(str); } catch (err) { ctx.addIssue({ code: "custom", message: "Invalid JSON" }); }}',
282
144
  closer: ")",
283
- messagePrefix: ", { error: ",
145
+ messagePrefix: ", { message: ",
284
146
  messageCloser: " })",
285
147
  };
286
148
  }
@@ -296,14 +158,12 @@ export const parseString = (schema, refs) => {
296
158
  return {
297
159
  opener: `.pipe(${contentExpr}`,
298
160
  closer: ")",
299
- messagePrefix: ", { error: ",
161
+ messagePrefix: ", { message: ",
300
162
  messageCloser: " })",
301
163
  };
302
164
  }
303
165
  });
304
166
  }
305
- // Use the correct Zod type based on whether a format function was used
306
- const zodType = formatInfo?.zodType ?? "z.ZodString";
307
167
  return {
308
168
  expression: r,
309
169
  type: zodType,
@@ -25,9 +25,10 @@ export type JsonSchemaObject = {
25
25
  definitions?: Record<string, JsonSchema>;
26
26
  title?: string;
27
27
  description?: string;
28
- examples?: Serializable[];
28
+ examples?: Serializable | Serializable[];
29
29
  deprecated?: boolean;
30
30
  dependentSchemas?: Record<string, JsonSchema>;
31
+ dependentRequired?: Record<string, string[]>;
31
32
  contains?: JsonSchema;
32
33
  minContains?: number;
33
34
  maxContains?: number;
@@ -44,6 +45,7 @@ export type JsonSchemaObject = {
44
45
  required?: string[] | boolean;
45
46
  propertyNames?: JsonSchema;
46
47
  items?: JsonSchema | JsonSchema[];
48
+ prefixItems?: JsonSchema[];
47
49
  additionalItems?: JsonSchema;
48
50
  minItems?: number;
49
51
  maxItems?: number;
@@ -118,6 +120,14 @@ export type Options = {
118
120
  * @default false
119
121
  */
120
122
  strictOneOf?: boolean;
123
+ /**
124
+ * Root schema instance for JSON Pointer resolution (#/...).
125
+ */
126
+ root?: JsonSchema;
127
+ /**
128
+ * Full document root schema instance for cross-reference resolution.
129
+ */
130
+ documentRoot?: JsonSchema;
121
131
  /**
122
132
  * Called when a string format is encountered that has no built-in mapping.
123
133
  * Can be used to log or throw on unknown formats.
@@ -133,6 +143,16 @@ export type Options = {
133
143
  * Return a JsonSchema to register, or undefined if not found.
134
144
  */
135
145
  resolveExternalRef?: (uri: string) => JsonSchema | Promise<JsonSchema> | undefined;
146
+ /** Root/base URI for the document */
147
+ rootBaseUri?: string;
148
+ /** Prebuilt registry of resolved URIs/anchors */
149
+ refRegistry?: Map<string, {
150
+ schema: JsonSchema;
151
+ path: (string | number)[];
152
+ baseUri: string;
153
+ dynamic?: boolean;
154
+ anchor?: string;
155
+ }>;
136
156
  /**
137
157
  * Lift inline object schemas into top-level defs to improve reusability.
138
158
  * Default is ON; set enable: false to opt out.
@@ -180,6 +200,7 @@ export type Refs = Options & {
180
200
  dynamic?: boolean;
181
201
  anchor?: string;
182
202
  }>;
203
+ definitions?: Record<string, JsonSchema>;
183
204
  /** Stack of active dynamic anchors (nearest last) */
184
205
  dynamicAnchors?: {
185
206
  name: string;
@@ -21,5 +21,6 @@ export type AnalysisResult = {
21
21
  anchor?: string;
22
22
  }>;
23
23
  rootBaseUri: string;
24
+ definitions?: Record<string, JsonSchema>;
24
25
  };
25
26
  export declare const analyzeSchema: (schema: JsonSchema, options?: Options) => AnalysisResult;
@@ -2,7 +2,7 @@ import { JsonSchema, Options } from "../Types.js";
2
2
  export type SplitDefsOptions = {
3
3
  /** Include a root schema file in addition to $defs */
4
4
  includeRoot?: boolean;
5
- /** Override file name for each schema (default: `${def}.schema.ts`) */
5
+ /** Override file name for each schema (default: `${ def }.schema.ts`) */
6
6
  fileName?: (defName: string, ctx: {
7
7
  isRoot: boolean;
8
8
  }) => string;
@@ -58,8 +58,7 @@ export function parseArgs(params, args, help) {
58
58
  }
59
59
  export function parseOrReadJSON(jsonOrPath) {
60
60
  jsonOrPath = jsonOrPath.trim();
61
- if (jsonOrPath.length < 255 &&
62
- statSync(jsonOrPath, { throwIfNoEntry: false })?.isFile()) {
61
+ if (jsonOrPath.length < 255 && statSync(jsonOrPath, { throwIfNoEntry: false })?.isFile()) {
63
62
  jsonOrPath = readFileSync(jsonOrPath, "utf-8");
64
63
  }
65
64
  return JSON.parse(jsonOrPath);
@@ -52,7 +52,9 @@ export class EsmEmitter {
52
52
  const initializer = parseExpression(statement.expression);
53
53
  const typeNode = statement.typeAnnotation ? parseType(statement.typeAnnotation) : undefined;
54
54
  const decl = ts.factory.createVariableDeclaration(statement.name, undefined, typeNode, initializer);
55
- const modifiers = statement.exported ? [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)] : undefined;
55
+ const modifiers = statement.exported
56
+ ? [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)]
57
+ : undefined;
56
58
  const varStmt = ts.factory.createVariableStatement(modifiers, ts.factory.createVariableDeclarationList([decl], ts.NodeFlags.Const));
57
59
  __classPrivateFieldGet(this, _EsmEmitter_statements, "f").push({ node: attachJsdoc(varStmt, statement.jsdoc) });
58
60
  }
@@ -72,7 +74,9 @@ export class EsmEmitter {
72
74
  const sf = ts.createSourceFile("out.ts", "", ts.ScriptTarget.ES2020, false, ts.ScriptKind.TS);
73
75
  const importStmts = [...__classPrivateFieldGet(this, _EsmEmitter_imports, "f").entries()]
74
76
  .sort(([a], [b]) => a.localeCompare(b))
75
- .map(([source, names]) => ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports([...names].sort().map((name) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(name))))), ts.factory.createStringLiteral(source)));
77
+ .map(([source, names]) => ts.factory.createImportDeclaration(undefined, ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports([...names]
78
+ .sort()
79
+ .map((name) => ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(name))))), ts.factory.createStringLiteral(source)));
76
80
  const allStatements = [
77
81
  ...importStmts.map((node) => ({ node })),
78
82
  ...__classPrivateFieldGet(this, _EsmEmitter_statements, "f"),
@@ -99,9 +99,7 @@ const sanitizeIdentifier = (value) => {
99
99
  .replace(/[^a-zA-Z0-9_$\s]/g, " ")
100
100
  .split(/\s+/)
101
101
  .filter(Boolean);
102
- const pascalCase = words
103
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
104
- .join("");
102
+ const pascalCase = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
105
103
  const cleaned = pascalCase.replace(/^[^a-zA-Z_$]+/, "").replace(/[^a-zA-Z0-9_$]/g, "");
106
104
  return cleaned || "InlineSchema";
107
105
  };
@@ -1,9 +1,6 @@
1
1
  export const expandJsdocs = (jsdocs) => {
2
2
  const lines = jsdocs.split("\n");
3
- const result = lines.length === 1
4
- ? lines[0]
5
- : `\n${lines.map(x => `* ${x}`)
6
- .join("\n")}\n`;
3
+ const result = lines.length === 1 ? lines[0] : `\n${lines.map((x) => `* ${x}`).join("\n")}\n`;
7
4
  return `/**${result}*/\n`;
8
5
  };
9
6
  export const addJsdocs = (schema, parsed) => {