@famgia/omnify-typescript 0.0.25 → 0.0.27

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/plugin.cjs CHANGED
@@ -144,6 +144,18 @@ function propertyToTSProperties(propertyName, property, allSchemas, options = {}
144
144
  comment: `${displayName ?? propertyName} (${field.suffix})`
145
145
  });
146
146
  }
147
+ if (customType.accessors) {
148
+ for (const accessor of customType.accessors) {
149
+ const accessorName = `${propertyName}_${toSnakeCase(accessor.name)}`;
150
+ expandedProps.push({
151
+ name: accessorName,
152
+ type: "string | null",
153
+ optional: true,
154
+ readonly: isReadonly,
155
+ comment: `${displayName ?? propertyName} (computed)`
156
+ });
157
+ }
158
+ }
147
159
  return expandedProps;
148
160
  }
149
161
  if (customType && !customType.compound) {
@@ -198,8 +210,22 @@ function propertyToTSProperties(propertyName, property, allSchemas, options = {}
198
210
  comment: displayName
199
211
  }];
200
212
  }
213
+ function extractTypeReferences(type, allSchemaNames) {
214
+ const primitives = /* @__PURE__ */ new Set(["string", "number", "boolean", "unknown", "null", "undefined", "void", "never", "any"]);
215
+ const refs = [];
216
+ const cleanType = type.replace(/\[\]/g, "").replace(/\s*\|\s*null/g, "");
217
+ const parts = cleanType.split(/\s*\|\s*/);
218
+ for (const part of parts) {
219
+ const trimmed = part.trim().replace(/^['"]|['"]$/g, "");
220
+ if (!primitives.has(trimmed) && allSchemaNames.has(trimmed)) {
221
+ refs.push(trimmed);
222
+ }
223
+ }
224
+ return refs;
225
+ }
201
226
  function schemaToInterface(schema, allSchemas, options = {}) {
202
227
  const properties = [];
228
+ const allSchemaNames = new Set(Object.keys(allSchemas).filter((name) => allSchemas[name].kind !== "enum"));
203
229
  if (schema.options?.id !== false) {
204
230
  const pkType = schema.options?.idType ?? "BigInt";
205
231
  properties.push({
@@ -242,11 +268,20 @@ function schemaToInterface(schema, allSchemas, options = {}) {
242
268
  comment: "Soft delete timestamp"
243
269
  });
244
270
  }
271
+ const dependencySet = /* @__PURE__ */ new Set();
272
+ for (const prop of properties) {
273
+ for (const ref of extractTypeReferences(prop.type, allSchemaNames)) {
274
+ if (ref !== schema.name) {
275
+ dependencySet.add(ref);
276
+ }
277
+ }
278
+ }
245
279
  const schemaDisplayName = resolveDisplayName(schema.displayName, options);
246
280
  return {
247
281
  name: toInterfaceName(schema.name),
248
282
  properties,
249
- comment: schemaDisplayName ?? schema.name
283
+ comment: schemaDisplayName ?? schema.name,
284
+ dependencies: dependencySet.size > 0 ? Array.from(dependencySet).sort() : void 0
250
285
  };
251
286
  }
252
287
  function formatProperty(property) {
@@ -303,11 +338,18 @@ function parseEnumValue(value, options = {}) {
303
338
  // No label or extra - will fallback to value
304
339
  };
305
340
  }
306
- const resolvedLabel = resolveDisplayName2(value.label, options);
341
+ let label;
342
+ if (value.label !== void 0) {
343
+ if (options.multiLocale && typeof value.label === "object") {
344
+ label = value.label;
345
+ } else {
346
+ label = resolveDisplayName2(value.label, options);
347
+ }
348
+ }
307
349
  return {
308
350
  name: toEnumMemberName(value.value),
309
351
  value: value.value,
310
- label: resolvedLabel,
352
+ label,
311
353
  extra: value.extra
312
354
  };
313
355
  }
@@ -337,6 +379,9 @@ function generateEnums(schemas, options = {}) {
337
379
  }
338
380
  return enums;
339
381
  }
382
+ function isMultiLocaleLabel(label) {
383
+ return label !== void 0 && typeof label === "object";
384
+ }
340
385
  function formatEnum(enumDef) {
341
386
  const { name, values, comment } = enumDef;
342
387
  const parts = [];
@@ -367,28 +412,64 @@ ${enumValues}
367
412
 
368
413
  `);
369
414
  const hasLabels = values.some((v) => v.label !== void 0);
415
+ const hasMultiLocale = values.some((v) => isMultiLocaleLabel(v.label));
370
416
  if (hasLabels) {
371
- const labelEntries = values.filter((v) => v.label !== void 0).map((v) => ` [${name}.${v.name}]: '${v.label}',`).join("\n");
372
- parts.push(`const ${lowerFirst(name)}Labels: Partial<Record<${name}, string>> = {
417
+ if (hasMultiLocale) {
418
+ const labelEntries = values.filter((v) => v.label !== void 0).map((v) => {
419
+ if (isMultiLocaleLabel(v.label)) {
420
+ const locales = Object.entries(v.label).map(([locale, text]) => `${locale}: '${text}'`).join(", ");
421
+ return ` [${name}.${v.name}]: { ${locales} },`;
422
+ }
423
+ return ` [${name}.${v.name}]: { default: '${v.label}' },`;
424
+ }).join("\n");
425
+ parts.push(`const ${lowerFirst(name)}Labels: Partial<Record<${name}, Record<string, string>>> = {
373
426
  ${labelEntries}
374
427
  };
375
428
 
376
429
  `);
377
- }
378
- parts.push(`/** Get label for ${name} value (fallback to value if no label) */
430
+ parts.push(`/** Get label for ${name} value with locale support */
379
431
  `);
380
- parts.push(`export function get${name}Label(value: ${name}): string {
432
+ parts.push(`export function get${name}Label(value: ${name}, locale?: string): string {
381
433
  `);
382
- if (hasLabels) {
383
- parts.push(` return ${lowerFirst(name)}Labels[value] ?? value;
434
+ parts.push(` const labels = ${lowerFirst(name)}Labels[value];
435
+ `);
436
+ parts.push(` if (!labels) return value;
384
437
  `);
438
+ parts.push(` if (locale && labels[locale]) return labels[locale];
439
+ `);
440
+ parts.push(` return Object.values(labels)[0] ?? value;
441
+ `);
442
+ parts.push(`}
443
+
444
+ `);
445
+ } else {
446
+ const labelEntries = values.filter((v) => v.label !== void 0).map((v) => ` [${name}.${v.name}]: '${v.label}',`).join("\n");
447
+ parts.push(`const ${lowerFirst(name)}Labels: Partial<Record<${name}, string>> = {
448
+ ${labelEntries}
449
+ };
450
+
451
+ `);
452
+ parts.push(`/** Get label for ${name} value (fallback to value if no label) */
453
+ `);
454
+ parts.push(`export function get${name}Label(value: ${name}): string {
455
+ `);
456
+ parts.push(` return ${lowerFirst(name)}Labels[value] ?? value;
457
+ `);
458
+ parts.push(`}
459
+
460
+ `);
461
+ }
385
462
  } else {
463
+ parts.push(`/** Get label for ${name} value (returns value as-is) */
464
+ `);
465
+ parts.push(`export function get${name}Label(value: ${name}): string {
466
+ `);
386
467
  parts.push(` return value;
387
468
  `);
388
- }
389
- parts.push(`}
469
+ parts.push(`}
390
470
 
391
471
  `);
472
+ }
392
473
  const hasExtra = values.some((v) => v.extra !== void 0);
393
474
  if (hasExtra) {
394
475
  const extraEntries = values.filter((v) => v.extra !== void 0).map((v) => ` [${name}.${v.name}]: ${JSON.stringify(v.extra)},`).join("\n");
@@ -502,6 +583,347 @@ function extractInlineEnums(schemas, options = {}) {
502
583
  return typeAliases;
503
584
  }
504
585
 
586
+ // src/validation-templates.ts
587
+ var DEFAULT_VALIDATION_TEMPLATES = {
588
+ required: {
589
+ ja: "${displayName}\u306F\u5FC5\u9808\u3067\u3059",
590
+ en: "${displayName} is required",
591
+ vi: "${displayName} l\xE0 b\u1EAFt bu\u1ED9c",
592
+ ko: "${displayName}\uC740(\uB294) \uD544\uC218\uC785\uB2C8\uB2E4",
593
+ zh: "${displayName}\u4E3A\u5FC5\u586B\u9879"
594
+ },
595
+ minLength: {
596
+ ja: "${displayName}\u306F${min}\u6587\u5B57\u4EE5\u4E0A\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
597
+ en: "${displayName} must be at least ${min} characters",
598
+ vi: "${displayName} ph\u1EA3i c\xF3 \xEDt nh\u1EA5t ${min} k\xFD t\u1EF1",
599
+ ko: "${displayName}\uC740(\uB294) ${min}\uC790 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4",
600
+ zh: "${displayName}\u81F3\u5C11\u9700\u8981${min}\u4E2A\u5B57\u7B26"
601
+ },
602
+ maxLength: {
603
+ ja: "${displayName}\u306F${max}\u6587\u5B57\u4EE5\u5185\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
604
+ en: "${displayName} must be at most ${max} characters",
605
+ vi: "${displayName} t\u1ED1i \u0111a ${max} k\xFD t\u1EF1",
606
+ ko: "${displayName}\uC740(\uB294) ${max}\uC790 \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4",
607
+ zh: "${displayName}\u4E0D\u80FD\u8D85\u8FC7${max}\u4E2A\u5B57\u7B26"
608
+ },
609
+ min: {
610
+ ja: "${displayName}\u306F${min}\u4EE5\u4E0A\u306E\u5024\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
611
+ en: "${displayName} must be at least ${min}",
612
+ vi: "${displayName} ph\u1EA3i l\u1EDBn h\u01A1n ho\u1EB7c b\u1EB1ng ${min}",
613
+ ko: "${displayName}\uC740(\uB294) ${min} \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4",
614
+ zh: "${displayName}\u4E0D\u80FD\u5C0F\u4E8E${min}"
615
+ },
616
+ max: {
617
+ ja: "${displayName}\u306F${max}\u4EE5\u4E0B\u306E\u5024\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
618
+ en: "${displayName} must be at most ${max}",
619
+ vi: "${displayName} ph\u1EA3i nh\u1ECF h\u01A1n ho\u1EB7c b\u1EB1ng ${max}",
620
+ ko: "${displayName}\uC740(\uB294) ${max} \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4",
621
+ zh: "${displayName}\u4E0D\u80FD\u5927\u4E8E${max}"
622
+ },
623
+ email: {
624
+ ja: "${displayName}\u306E\u5F62\u5F0F\u304C\u6B63\u3057\u304F\u3042\u308A\u307E\u305B\u3093",
625
+ en: "${displayName} is not a valid email address",
626
+ vi: "${displayName} kh\xF4ng ph\u1EA3i l\xE0 \u0111\u1ECBa ch\u1EC9 email h\u1EE3p l\u1EC7",
627
+ ko: "${displayName} \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
628
+ zh: "${displayName}\u4E0D\u662F\u6709\u6548\u7684\u90AE\u7BB1\u5730\u5740"
629
+ },
630
+ url: {
631
+ ja: "${displayName}\u306F\u6709\u52B9\u306AURL\u3067\u306F\u3042\u308A\u307E\u305B\u3093",
632
+ en: "${displayName} is not a valid URL",
633
+ vi: "${displayName} kh\xF4ng ph\u1EA3i l\xE0 URL h\u1EE3p l\u1EC7",
634
+ ko: "${displayName}\uC740(\uB294) \uC720\uD6A8\uD55C URL\uC774 \uC544\uB2D9\uB2C8\uB2E4",
635
+ zh: "${displayName}\u4E0D\u662F\u6709\u6548\u7684URL"
636
+ },
637
+ pattern: {
638
+ ja: "${displayName}\u306E\u5F62\u5F0F\u304C\u6B63\u3057\u304F\u3042\u308A\u307E\u305B\u3093",
639
+ en: "${displayName} format is invalid",
640
+ vi: "${displayName} kh\xF4ng \u0111\xFAng \u0111\u1ECBnh d\u1EA1ng",
641
+ ko: "${displayName} \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
642
+ zh: "${displayName}\u683C\u5F0F\u4E0D\u6B63\u786E"
643
+ },
644
+ enum: {
645
+ ja: "${displayName}\u306E\u5024\u304C\u7121\u52B9\u3067\u3059",
646
+ en: "${displayName} has an invalid value",
647
+ vi: "${displayName} c\xF3 gi\xE1 tr\u1ECB kh\xF4ng h\u1EE3p l\u1EC7",
648
+ ko: "${displayName} \uAC12\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
649
+ zh: "${displayName}\u7684\u503C\u65E0\u6548"
650
+ }
651
+ };
652
+ function mergeValidationTemplates(userTemplates) {
653
+ if (!userTemplates) {
654
+ return DEFAULT_VALIDATION_TEMPLATES;
655
+ }
656
+ const merged = {
657
+ required: { ...DEFAULT_VALIDATION_TEMPLATES.required },
658
+ minLength: { ...DEFAULT_VALIDATION_TEMPLATES.minLength },
659
+ maxLength: { ...DEFAULT_VALIDATION_TEMPLATES.maxLength },
660
+ min: { ...DEFAULT_VALIDATION_TEMPLATES.min },
661
+ max: { ...DEFAULT_VALIDATION_TEMPLATES.max },
662
+ email: { ...DEFAULT_VALIDATION_TEMPLATES.email },
663
+ url: { ...DEFAULT_VALIDATION_TEMPLATES.url },
664
+ pattern: { ...DEFAULT_VALIDATION_TEMPLATES.pattern },
665
+ enum: { ...DEFAULT_VALIDATION_TEMPLATES.enum }
666
+ };
667
+ for (const [key, value] of Object.entries(userTemplates)) {
668
+ if (value && key in merged) {
669
+ merged[key] = {
670
+ ...merged[key],
671
+ ...value
672
+ };
673
+ }
674
+ }
675
+ return merged;
676
+ }
677
+ function formatValidationMessage(template, vars) {
678
+ let result = template;
679
+ for (const [key, value] of Object.entries(vars)) {
680
+ result = result.replace(new RegExp(`\\$\\{${key}\\}`, "g"), String(value));
681
+ }
682
+ return result;
683
+ }
684
+ function getValidationMessages(templates, ruleType, locales, vars, fallbackLocale) {
685
+ const ruleTemplates = templates[ruleType];
686
+ const messages = {};
687
+ for (const locale of locales) {
688
+ const template = ruleTemplates[locale] ?? (fallbackLocale ? ruleTemplates[fallbackLocale] : void 0) ?? ruleTemplates["en"] ?? "";
689
+ messages[locale] = formatValidationMessage(template, vars);
690
+ }
691
+ return messages;
692
+ }
693
+
694
+ // src/rules-generator.ts
695
+ function getMultiLocaleDisplayName(value, locales, fallbackLocale, defaultValue) {
696
+ if (!value) {
697
+ const result2 = {};
698
+ for (const locale of locales) {
699
+ result2[locale] = defaultValue;
700
+ }
701
+ return result2;
702
+ }
703
+ if (typeof value === "string") {
704
+ const result2 = {};
705
+ for (const locale of locales) {
706
+ result2[locale] = value;
707
+ }
708
+ return result2;
709
+ }
710
+ const result = {};
711
+ for (const locale of locales) {
712
+ result[locale] = value[locale] ?? value[fallbackLocale] ?? value["en"] ?? defaultValue;
713
+ }
714
+ return result;
715
+ }
716
+ function generatePropertyRules(propName, property, displayName, locales, fallbackLocale, templates) {
717
+ const rules = [];
718
+ const propDef = property;
719
+ if (!propDef.nullable) {
720
+ rules.push({
721
+ required: true,
722
+ message: getValidationMessages(templates, "required", locales, { displayName: "${displayName}" }, fallbackLocale)
723
+ });
724
+ }
725
+ if (property.type === "Email") {
726
+ rules.push({
727
+ type: "email",
728
+ message: getValidationMessages(templates, "email", locales, { displayName: "${displayName}" }, fallbackLocale)
729
+ });
730
+ }
731
+ if (property.type === "String" || property.type === "Text" || property.type === "LongText") {
732
+ if (propDef.minLength) {
733
+ rules.push({
734
+ min: propDef.minLength,
735
+ message: getValidationMessages(templates, "minLength", locales, { displayName: "${displayName}", min: propDef.minLength }, fallbackLocale)
736
+ });
737
+ }
738
+ if (propDef.maxLength || propDef.length) {
739
+ const max = propDef.maxLength ?? propDef.length;
740
+ rules.push({
741
+ max,
742
+ message: getValidationMessages(templates, "maxLength", locales, { displayName: "${displayName}", max }, fallbackLocale)
743
+ });
744
+ }
745
+ }
746
+ if (property.type === "Int" || property.type === "BigInt" || property.type === "Float") {
747
+ if (propDef.min !== void 0) {
748
+ rules.push({
749
+ type: property.type === "Float" ? "number" : "integer",
750
+ min: propDef.min,
751
+ message: getValidationMessages(templates, "min", locales, { displayName: "${displayName}", min: propDef.min }, fallbackLocale)
752
+ });
753
+ }
754
+ if (propDef.max !== void 0) {
755
+ rules.push({
756
+ type: property.type === "Float" ? "number" : "integer",
757
+ max: propDef.max,
758
+ message: getValidationMessages(templates, "max", locales, { displayName: "${displayName}", max: propDef.max }, fallbackLocale)
759
+ });
760
+ }
761
+ }
762
+ if (propDef.pattern) {
763
+ rules.push({
764
+ pattern: propDef.pattern,
765
+ message: getValidationMessages(templates, "pattern", locales, { displayName: "${displayName}" }, fallbackLocale)
766
+ });
767
+ }
768
+ for (const rule of rules) {
769
+ const newMessage = {};
770
+ for (const locale of locales) {
771
+ const msg = rule.message[locale];
772
+ if (msg) {
773
+ newMessage[locale] = msg.replace(/\$\{displayName\}/g, displayName[locale] ?? propName);
774
+ }
775
+ }
776
+ rule.message = newMessage;
777
+ }
778
+ return rules;
779
+ }
780
+ function generateModelRules(schema, locales, fallbackLocale, templates) {
781
+ const modelDisplayName = getMultiLocaleDisplayName(
782
+ schema.displayName,
783
+ locales,
784
+ fallbackLocale,
785
+ schema.name
786
+ );
787
+ const properties = {};
788
+ if (schema.properties) {
789
+ for (const [propName, property] of Object.entries(schema.properties)) {
790
+ const propDef = property;
791
+ const displayName = getMultiLocaleDisplayName(
792
+ propDef.displayName,
793
+ locales,
794
+ fallbackLocale,
795
+ propName
796
+ );
797
+ properties[propName] = {
798
+ displayName,
799
+ rules: generatePropertyRules(propName, property, displayName, locales, fallbackLocale, templates)
800
+ };
801
+ }
802
+ }
803
+ return {
804
+ displayName: modelDisplayName,
805
+ properties
806
+ };
807
+ }
808
+ function formatRulesFile(schemaName, rules) {
809
+ const parts = [];
810
+ parts.push(`/**
811
+ * Auto-generated validation rules and metadata for ${schemaName}.
812
+ * DO NOT EDIT - This file is automatically generated and will be overwritten.
813
+ */
814
+
815
+ `);
816
+ parts.push(`export interface LocaleMap { [locale: string]: string; }
817
+
818
+ `);
819
+ parts.push(`export interface ValidationRule {
820
+ required?: boolean;
821
+ type?: 'string' | 'number' | 'email' | 'url' | 'integer';
822
+ min?: number;
823
+ max?: number;
824
+ len?: number;
825
+ pattern?: RegExp;
826
+ message: LocaleMap;
827
+ }
828
+
829
+ `);
830
+ parts.push(`/** Display name for ${schemaName} */
831
+ `);
832
+ parts.push(`export const ${schemaName}DisplayName: LocaleMap = ${JSON.stringify(rules.displayName, null, 2)};
833
+
834
+ `);
835
+ parts.push(`/** Property display names for ${schemaName} */
836
+ `);
837
+ parts.push(`export const ${schemaName}PropertyDisplayNames: Record<string, LocaleMap> = {
838
+ `);
839
+ for (const [propName, propRules] of Object.entries(rules.properties)) {
840
+ parts.push(` ${propName}: ${JSON.stringify(propRules.displayName)},
841
+ `);
842
+ }
843
+ parts.push(`};
844
+
845
+ `);
846
+ parts.push(`/** Validation rules for ${schemaName} (Ant Design compatible) */
847
+ `);
848
+ parts.push(`export const ${schemaName}Rules: Record<string, ValidationRule[]> = {
849
+ `);
850
+ for (const [propName, propRules] of Object.entries(rules.properties)) {
851
+ if (propRules.rules.length > 0) {
852
+ parts.push(` ${propName}: [
853
+ `);
854
+ for (const rule of propRules.rules) {
855
+ const ruleObj = {};
856
+ if (rule.required) ruleObj.required = true;
857
+ if (rule.type) ruleObj.type = `'${rule.type}'`;
858
+ if (rule.min !== void 0) ruleObj.min = rule.min;
859
+ if (rule.max !== void 0) ruleObj.max = rule.max;
860
+ if (rule.pattern) ruleObj.pattern = `/${rule.pattern}/`;
861
+ ruleObj.message = rule.message;
862
+ const ruleStr = Object.entries(ruleObj).map(([k, v]) => {
863
+ if (k === "type") return `${k}: ${v}`;
864
+ if (k === "pattern") return `${k}: ${v}`;
865
+ return `${k}: ${JSON.stringify(v)}`;
866
+ }).join(", ");
867
+ parts.push(` { ${ruleStr} },
868
+ `);
869
+ }
870
+ parts.push(` ],
871
+ `);
872
+ }
873
+ }
874
+ parts.push(`};
875
+
876
+ `);
877
+ parts.push(`/** Get validation rules with messages for a specific locale */
878
+ `);
879
+ parts.push(`export function get${schemaName}Rules(locale: string): Record<string, Array<{ required?: boolean; type?: string; min?: number; max?: number; pattern?: RegExp; message: string }>> {
880
+ const result: Record<string, Array<{ required?: boolean; type?: string; min?: number; max?: number; pattern?: RegExp; message: string }>> = {};
881
+ for (const [prop, rules] of Object.entries(${schemaName}Rules)) {
882
+ result[prop] = rules.map(rule => ({
883
+ ...rule,
884
+ message: rule.message[locale] ?? rule.message['en'] ?? '',
885
+ }));
886
+ }
887
+ return result;
888
+ }
889
+
890
+ `);
891
+ parts.push(`/** Get display name for a specific locale */
892
+ `);
893
+ parts.push(`export function get${schemaName}DisplayName(locale: string): string {
894
+ return ${schemaName}DisplayName[locale] ?? ${schemaName}DisplayName['en'] ?? '${schemaName}';
895
+ }
896
+
897
+ `);
898
+ parts.push(`/** Get property display name for a specific locale */
899
+ `);
900
+ parts.push(`export function get${schemaName}PropertyDisplayName(property: string, locale: string): string {
901
+ const names = ${schemaName}PropertyDisplayNames[property];
902
+ return names?.[locale] ?? names?.['en'] ?? property;
903
+ }
904
+ `);
905
+ return parts.join("");
906
+ }
907
+ function generateRulesFiles(schemas, options = {}) {
908
+ const files = [];
909
+ const localeConfig = options.localeConfig;
910
+ const locales = [...localeConfig?.locales ?? ["en"]];
911
+ const fallbackLocale = localeConfig?.fallbackLocale ?? "en";
912
+ const templates = mergeValidationTemplates(options.validationTemplates);
913
+ for (const schema of Object.values(schemas)) {
914
+ if (schema.kind === "enum") continue;
915
+ const rules = generateModelRules(schema, locales, fallbackLocale, templates);
916
+ const content = formatRulesFile(schema.name, rules);
917
+ files.push({
918
+ filePath: `rules/${schema.name}.rules.ts`,
919
+ content,
920
+ types: [`${schema.name}Rules`, `${schema.name}DisplayName`],
921
+ overwrite: true
922
+ });
923
+ }
924
+ return files;
925
+ }
926
+
505
927
  // src/generator.ts
506
928
  var DEFAULT_OPTIONS = {
507
929
  readonly: true,
@@ -533,6 +955,13 @@ function generateBaseInterfaceFile(schemaName, schemas, options) {
533
955
  throw new Error(`Interface not found for schema: ${schemaName}`);
534
956
  }
535
957
  const parts = [generateBaseHeader()];
958
+ if (iface.dependencies && iface.dependencies.length > 0) {
959
+ for (const dep of iface.dependencies) {
960
+ parts.push(`import type { ${dep} } from './${dep}.js';
961
+ `);
962
+ }
963
+ parts.push("\n");
964
+ }
536
965
  parts.push(formatInterface(iface));
537
966
  parts.push("\n");
538
967
  return {
@@ -649,11 +1078,11 @@ function generateIndexFile(schemas, enums, typeAliases) {
649
1078
  function generateTypeScript(schemas, options = {}) {
650
1079
  const opts = { ...DEFAULT_OPTIONS, ...options };
651
1080
  const files = [];
652
- const enums = generateEnums(schemas);
1081
+ const enums = generateEnums(schemas, opts);
653
1082
  for (const enumDef of enums) {
654
1083
  files.push(generateEnumFile(enumDef));
655
1084
  }
656
- const typeAliases = extractInlineEnums(schemas);
1085
+ const typeAliases = extractInlineEnums(schemas, opts);
657
1086
  for (const alias of typeAliases) {
658
1087
  files.push(generateTypeAliasFile(alias));
659
1088
  }
@@ -665,6 +1094,10 @@ function generateTypeScript(schemas, options = {}) {
665
1094
  if (schema.kind === "enum") continue;
666
1095
  files.push(generateModelFile(schema.name));
667
1096
  }
1097
+ if (opts.generateRules) {
1098
+ const rulesFiles = generateRulesFiles(schemas, opts);
1099
+ files.push(...rulesFiles);
1100
+ }
668
1101
  files.push(generateIndexFile(schemas, enums, typeAliases));
669
1102
  return files;
670
1103
  }