@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/{chunk-4NNFIIZ4.js → chunk-HPORV62S.js} +454 -15
- package/dist/chunk-HPORV62S.js.map +1 -0
- package/dist/index.cjs +459 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -3
- package/dist/index.d.ts +97 -3
- package/dist/index.js +13 -1
- package/dist/plugin.cjs +447 -14
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-4NNFIIZ4.js.map +0 -1
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
|
-
|
|
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
|
|
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
|
-
|
|
372
|
-
|
|
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
|
-
|
|
432
|
+
parts.push(`export function get${name}Label(value: ${name}, locale?: string): string {
|
|
381
433
|
`);
|
|
382
|
-
|
|
383
|
-
|
|
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
|
}
|