@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/index.cjs
CHANGED
|
@@ -20,17 +20,23 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
DEFAULT_VALIDATION_TEMPLATES: () => DEFAULT_VALIDATION_TEMPLATES,
|
|
23
24
|
enumToUnionType: () => enumToUnionType,
|
|
24
25
|
extractInlineEnums: () => extractInlineEnums,
|
|
25
26
|
formatEnum: () => formatEnum,
|
|
26
27
|
formatInterface: () => formatInterface,
|
|
27
28
|
formatProperty: () => formatProperty,
|
|
28
29
|
formatTypeAlias: () => formatTypeAlias,
|
|
30
|
+
formatValidationMessage: () => formatValidationMessage,
|
|
29
31
|
generateEnums: () => generateEnums,
|
|
30
32
|
generateInterfaces: () => generateInterfaces,
|
|
33
|
+
generateModelRules: () => generateModelRules,
|
|
34
|
+
generateRulesFiles: () => generateRulesFiles,
|
|
31
35
|
generateTypeScript: () => generateTypeScript,
|
|
32
36
|
generateTypeScriptFiles: () => generateTypeScript,
|
|
33
37
|
getPropertyType: () => getPropertyType,
|
|
38
|
+
getValidationMessages: () => getValidationMessages,
|
|
39
|
+
mergeValidationTemplates: () => mergeValidationTemplates,
|
|
34
40
|
propertyToTSProperty: () => propertyToTSProperty,
|
|
35
41
|
schemaToEnum: () => schemaToEnum,
|
|
36
42
|
schemaToInterface: () => schemaToInterface,
|
|
@@ -160,6 +166,18 @@ function propertyToTSProperties(propertyName, property, allSchemas, options = {}
|
|
|
160
166
|
comment: `${displayName ?? propertyName} (${field.suffix})`
|
|
161
167
|
});
|
|
162
168
|
}
|
|
169
|
+
if (customType.accessors) {
|
|
170
|
+
for (const accessor of customType.accessors) {
|
|
171
|
+
const accessorName = `${propertyName}_${toSnakeCase(accessor.name)}`;
|
|
172
|
+
expandedProps.push({
|
|
173
|
+
name: accessorName,
|
|
174
|
+
type: "string | null",
|
|
175
|
+
optional: true,
|
|
176
|
+
readonly: isReadonly,
|
|
177
|
+
comment: `${displayName ?? propertyName} (computed)`
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
163
181
|
return expandedProps;
|
|
164
182
|
}
|
|
165
183
|
if (customType && !customType.compound) {
|
|
@@ -217,8 +235,22 @@ function propertyToTSProperties(propertyName, property, allSchemas, options = {}
|
|
|
217
235
|
function propertyToTSProperty(propertyName, property, allSchemas, options = {}) {
|
|
218
236
|
return propertyToTSProperties(propertyName, property, allSchemas, options)[0];
|
|
219
237
|
}
|
|
238
|
+
function extractTypeReferences(type, allSchemaNames) {
|
|
239
|
+
const primitives = /* @__PURE__ */ new Set(["string", "number", "boolean", "unknown", "null", "undefined", "void", "never", "any"]);
|
|
240
|
+
const refs = [];
|
|
241
|
+
const cleanType = type.replace(/\[\]/g, "").replace(/\s*\|\s*null/g, "");
|
|
242
|
+
const parts = cleanType.split(/\s*\|\s*/);
|
|
243
|
+
for (const part of parts) {
|
|
244
|
+
const trimmed = part.trim().replace(/^['"]|['"]$/g, "");
|
|
245
|
+
if (!primitives.has(trimmed) && allSchemaNames.has(trimmed)) {
|
|
246
|
+
refs.push(trimmed);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return refs;
|
|
250
|
+
}
|
|
220
251
|
function schemaToInterface(schema, allSchemas, options = {}) {
|
|
221
252
|
const properties = [];
|
|
253
|
+
const allSchemaNames = new Set(Object.keys(allSchemas).filter((name) => allSchemas[name].kind !== "enum"));
|
|
222
254
|
if (schema.options?.id !== false) {
|
|
223
255
|
const pkType = schema.options?.idType ?? "BigInt";
|
|
224
256
|
properties.push({
|
|
@@ -261,11 +293,20 @@ function schemaToInterface(schema, allSchemas, options = {}) {
|
|
|
261
293
|
comment: "Soft delete timestamp"
|
|
262
294
|
});
|
|
263
295
|
}
|
|
296
|
+
const dependencySet = /* @__PURE__ */ new Set();
|
|
297
|
+
for (const prop of properties) {
|
|
298
|
+
for (const ref of extractTypeReferences(prop.type, allSchemaNames)) {
|
|
299
|
+
if (ref !== schema.name) {
|
|
300
|
+
dependencySet.add(ref);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
264
304
|
const schemaDisplayName = resolveDisplayName(schema.displayName, options);
|
|
265
305
|
return {
|
|
266
306
|
name: toInterfaceName(schema.name),
|
|
267
307
|
properties,
|
|
268
|
-
comment: schemaDisplayName ?? schema.name
|
|
308
|
+
comment: schemaDisplayName ?? schema.name,
|
|
309
|
+
dependencies: dependencySet.size > 0 ? Array.from(dependencySet).sort() : void 0
|
|
269
310
|
};
|
|
270
311
|
}
|
|
271
312
|
function formatProperty(property) {
|
|
@@ -322,11 +363,18 @@ function parseEnumValue(value, options = {}) {
|
|
|
322
363
|
// No label or extra - will fallback to value
|
|
323
364
|
};
|
|
324
365
|
}
|
|
325
|
-
|
|
366
|
+
let label;
|
|
367
|
+
if (value.label !== void 0) {
|
|
368
|
+
if (options.multiLocale && typeof value.label === "object") {
|
|
369
|
+
label = value.label;
|
|
370
|
+
} else {
|
|
371
|
+
label = resolveDisplayName2(value.label, options);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
326
374
|
return {
|
|
327
375
|
name: toEnumMemberName(value.value),
|
|
328
376
|
value: value.value,
|
|
329
|
-
label
|
|
377
|
+
label,
|
|
330
378
|
extra: value.extra
|
|
331
379
|
};
|
|
332
380
|
}
|
|
@@ -356,6 +404,9 @@ function generateEnums(schemas, options = {}) {
|
|
|
356
404
|
}
|
|
357
405
|
return enums;
|
|
358
406
|
}
|
|
407
|
+
function isMultiLocaleLabel(label) {
|
|
408
|
+
return label !== void 0 && typeof label === "object";
|
|
409
|
+
}
|
|
359
410
|
function formatEnum(enumDef) {
|
|
360
411
|
const { name, values, comment } = enumDef;
|
|
361
412
|
const parts = [];
|
|
@@ -386,28 +437,64 @@ ${enumValues}
|
|
|
386
437
|
|
|
387
438
|
`);
|
|
388
439
|
const hasLabels = values.some((v) => v.label !== void 0);
|
|
440
|
+
const hasMultiLocale = values.some((v) => isMultiLocaleLabel(v.label));
|
|
389
441
|
if (hasLabels) {
|
|
390
|
-
|
|
391
|
-
|
|
442
|
+
if (hasMultiLocale) {
|
|
443
|
+
const labelEntries = values.filter((v) => v.label !== void 0).map((v) => {
|
|
444
|
+
if (isMultiLocaleLabel(v.label)) {
|
|
445
|
+
const locales = Object.entries(v.label).map(([locale, text]) => `${locale}: '${text}'`).join(", ");
|
|
446
|
+
return ` [${name}.${v.name}]: { ${locales} },`;
|
|
447
|
+
}
|
|
448
|
+
return ` [${name}.${v.name}]: { default: '${v.label}' },`;
|
|
449
|
+
}).join("\n");
|
|
450
|
+
parts.push(`const ${lowerFirst(name)}Labels: Partial<Record<${name}, Record<string, string>>> = {
|
|
392
451
|
${labelEntries}
|
|
393
452
|
};
|
|
394
453
|
|
|
395
454
|
`);
|
|
396
|
-
|
|
397
|
-
parts.push(`/** Get label for ${name} value (fallback to value if no label) */
|
|
455
|
+
parts.push(`/** Get label for ${name} value with locale support */
|
|
398
456
|
`);
|
|
399
|
-
|
|
457
|
+
parts.push(`export function get${name}Label(value: ${name}, locale?: string): string {
|
|
400
458
|
`);
|
|
401
|
-
|
|
402
|
-
|
|
459
|
+
parts.push(` const labels = ${lowerFirst(name)}Labels[value];
|
|
460
|
+
`);
|
|
461
|
+
parts.push(` if (!labels) return value;
|
|
403
462
|
`);
|
|
463
|
+
parts.push(` if (locale && labels[locale]) return labels[locale];
|
|
464
|
+
`);
|
|
465
|
+
parts.push(` return Object.values(labels)[0] ?? value;
|
|
466
|
+
`);
|
|
467
|
+
parts.push(`}
|
|
468
|
+
|
|
469
|
+
`);
|
|
470
|
+
} else {
|
|
471
|
+
const labelEntries = values.filter((v) => v.label !== void 0).map((v) => ` [${name}.${v.name}]: '${v.label}',`).join("\n");
|
|
472
|
+
parts.push(`const ${lowerFirst(name)}Labels: Partial<Record<${name}, string>> = {
|
|
473
|
+
${labelEntries}
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
`);
|
|
477
|
+
parts.push(`/** Get label for ${name} value (fallback to value if no label) */
|
|
478
|
+
`);
|
|
479
|
+
parts.push(`export function get${name}Label(value: ${name}): string {
|
|
480
|
+
`);
|
|
481
|
+
parts.push(` return ${lowerFirst(name)}Labels[value] ?? value;
|
|
482
|
+
`);
|
|
483
|
+
parts.push(`}
|
|
484
|
+
|
|
485
|
+
`);
|
|
486
|
+
}
|
|
404
487
|
} else {
|
|
488
|
+
parts.push(`/** Get label for ${name} value (returns value as-is) */
|
|
489
|
+
`);
|
|
490
|
+
parts.push(`export function get${name}Label(value: ${name}): string {
|
|
491
|
+
`);
|
|
405
492
|
parts.push(` return value;
|
|
406
493
|
`);
|
|
407
|
-
|
|
408
|
-
parts.push(`}
|
|
494
|
+
parts.push(`}
|
|
409
495
|
|
|
410
496
|
`);
|
|
497
|
+
}
|
|
411
498
|
const hasExtra = values.some((v) => v.extra !== void 0);
|
|
412
499
|
if (hasExtra) {
|
|
413
500
|
const extraEntries = values.filter((v) => v.extra !== void 0).map((v) => ` [${name}.${v.name}]: ${JSON.stringify(v.extra)},`).join("\n");
|
|
@@ -529,6 +616,347 @@ function extractInlineEnums(schemas, options = {}) {
|
|
|
529
616
|
return typeAliases;
|
|
530
617
|
}
|
|
531
618
|
|
|
619
|
+
// src/validation-templates.ts
|
|
620
|
+
var DEFAULT_VALIDATION_TEMPLATES = {
|
|
621
|
+
required: {
|
|
622
|
+
ja: "${displayName}\u306F\u5FC5\u9808\u3067\u3059",
|
|
623
|
+
en: "${displayName} is required",
|
|
624
|
+
vi: "${displayName} l\xE0 b\u1EAFt bu\u1ED9c",
|
|
625
|
+
ko: "${displayName}\uC740(\uB294) \uD544\uC218\uC785\uB2C8\uB2E4",
|
|
626
|
+
zh: "${displayName}\u4E3A\u5FC5\u586B\u9879"
|
|
627
|
+
},
|
|
628
|
+
minLength: {
|
|
629
|
+
ja: "${displayName}\u306F${min}\u6587\u5B57\u4EE5\u4E0A\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
630
|
+
en: "${displayName} must be at least ${min} characters",
|
|
631
|
+
vi: "${displayName} ph\u1EA3i c\xF3 \xEDt nh\u1EA5t ${min} k\xFD t\u1EF1",
|
|
632
|
+
ko: "${displayName}\uC740(\uB294) ${min}\uC790 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4",
|
|
633
|
+
zh: "${displayName}\u81F3\u5C11\u9700\u8981${min}\u4E2A\u5B57\u7B26"
|
|
634
|
+
},
|
|
635
|
+
maxLength: {
|
|
636
|
+
ja: "${displayName}\u306F${max}\u6587\u5B57\u4EE5\u5185\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
637
|
+
en: "${displayName} must be at most ${max} characters",
|
|
638
|
+
vi: "${displayName} t\u1ED1i \u0111a ${max} k\xFD t\u1EF1",
|
|
639
|
+
ko: "${displayName}\uC740(\uB294) ${max}\uC790 \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4",
|
|
640
|
+
zh: "${displayName}\u4E0D\u80FD\u8D85\u8FC7${max}\u4E2A\u5B57\u7B26"
|
|
641
|
+
},
|
|
642
|
+
min: {
|
|
643
|
+
ja: "${displayName}\u306F${min}\u4EE5\u4E0A\u306E\u5024\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
644
|
+
en: "${displayName} must be at least ${min}",
|
|
645
|
+
vi: "${displayName} ph\u1EA3i l\u1EDBn h\u01A1n ho\u1EB7c b\u1EB1ng ${min}",
|
|
646
|
+
ko: "${displayName}\uC740(\uB294) ${min} \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4",
|
|
647
|
+
zh: "${displayName}\u4E0D\u80FD\u5C0F\u4E8E${min}"
|
|
648
|
+
},
|
|
649
|
+
max: {
|
|
650
|
+
ja: "${displayName}\u306F${max}\u4EE5\u4E0B\u306E\u5024\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
651
|
+
en: "${displayName} must be at most ${max}",
|
|
652
|
+
vi: "${displayName} ph\u1EA3i nh\u1ECF h\u01A1n ho\u1EB7c b\u1EB1ng ${max}",
|
|
653
|
+
ko: "${displayName}\uC740(\uB294) ${max} \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4",
|
|
654
|
+
zh: "${displayName}\u4E0D\u80FD\u5927\u4E8E${max}"
|
|
655
|
+
},
|
|
656
|
+
email: {
|
|
657
|
+
ja: "${displayName}\u306E\u5F62\u5F0F\u304C\u6B63\u3057\u304F\u3042\u308A\u307E\u305B\u3093",
|
|
658
|
+
en: "${displayName} is not a valid email address",
|
|
659
|
+
vi: "${displayName} kh\xF4ng ph\u1EA3i l\xE0 \u0111\u1ECBa ch\u1EC9 email h\u1EE3p l\u1EC7",
|
|
660
|
+
ko: "${displayName} \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
|
|
661
|
+
zh: "${displayName}\u4E0D\u662F\u6709\u6548\u7684\u90AE\u7BB1\u5730\u5740"
|
|
662
|
+
},
|
|
663
|
+
url: {
|
|
664
|
+
ja: "${displayName}\u306F\u6709\u52B9\u306AURL\u3067\u306F\u3042\u308A\u307E\u305B\u3093",
|
|
665
|
+
en: "${displayName} is not a valid URL",
|
|
666
|
+
vi: "${displayName} kh\xF4ng ph\u1EA3i l\xE0 URL h\u1EE3p l\u1EC7",
|
|
667
|
+
ko: "${displayName}\uC740(\uB294) \uC720\uD6A8\uD55C URL\uC774 \uC544\uB2D9\uB2C8\uB2E4",
|
|
668
|
+
zh: "${displayName}\u4E0D\u662F\u6709\u6548\u7684URL"
|
|
669
|
+
},
|
|
670
|
+
pattern: {
|
|
671
|
+
ja: "${displayName}\u306E\u5F62\u5F0F\u304C\u6B63\u3057\u304F\u3042\u308A\u307E\u305B\u3093",
|
|
672
|
+
en: "${displayName} format is invalid",
|
|
673
|
+
vi: "${displayName} kh\xF4ng \u0111\xFAng \u0111\u1ECBnh d\u1EA1ng",
|
|
674
|
+
ko: "${displayName} \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
|
|
675
|
+
zh: "${displayName}\u683C\u5F0F\u4E0D\u6B63\u786E"
|
|
676
|
+
},
|
|
677
|
+
enum: {
|
|
678
|
+
ja: "${displayName}\u306E\u5024\u304C\u7121\u52B9\u3067\u3059",
|
|
679
|
+
en: "${displayName} has an invalid value",
|
|
680
|
+
vi: "${displayName} c\xF3 gi\xE1 tr\u1ECB kh\xF4ng h\u1EE3p l\u1EC7",
|
|
681
|
+
ko: "${displayName} \uAC12\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
|
|
682
|
+
zh: "${displayName}\u7684\u503C\u65E0\u6548"
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
function mergeValidationTemplates(userTemplates) {
|
|
686
|
+
if (!userTemplates) {
|
|
687
|
+
return DEFAULT_VALIDATION_TEMPLATES;
|
|
688
|
+
}
|
|
689
|
+
const merged = {
|
|
690
|
+
required: { ...DEFAULT_VALIDATION_TEMPLATES.required },
|
|
691
|
+
minLength: { ...DEFAULT_VALIDATION_TEMPLATES.minLength },
|
|
692
|
+
maxLength: { ...DEFAULT_VALIDATION_TEMPLATES.maxLength },
|
|
693
|
+
min: { ...DEFAULT_VALIDATION_TEMPLATES.min },
|
|
694
|
+
max: { ...DEFAULT_VALIDATION_TEMPLATES.max },
|
|
695
|
+
email: { ...DEFAULT_VALIDATION_TEMPLATES.email },
|
|
696
|
+
url: { ...DEFAULT_VALIDATION_TEMPLATES.url },
|
|
697
|
+
pattern: { ...DEFAULT_VALIDATION_TEMPLATES.pattern },
|
|
698
|
+
enum: { ...DEFAULT_VALIDATION_TEMPLATES.enum }
|
|
699
|
+
};
|
|
700
|
+
for (const [key, value] of Object.entries(userTemplates)) {
|
|
701
|
+
if (value && key in merged) {
|
|
702
|
+
merged[key] = {
|
|
703
|
+
...merged[key],
|
|
704
|
+
...value
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return merged;
|
|
709
|
+
}
|
|
710
|
+
function formatValidationMessage(template, vars) {
|
|
711
|
+
let result = template;
|
|
712
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
713
|
+
result = result.replace(new RegExp(`\\$\\{${key}\\}`, "g"), String(value));
|
|
714
|
+
}
|
|
715
|
+
return result;
|
|
716
|
+
}
|
|
717
|
+
function getValidationMessages(templates, ruleType, locales, vars, fallbackLocale) {
|
|
718
|
+
const ruleTemplates = templates[ruleType];
|
|
719
|
+
const messages = {};
|
|
720
|
+
for (const locale of locales) {
|
|
721
|
+
const template = ruleTemplates[locale] ?? (fallbackLocale ? ruleTemplates[fallbackLocale] : void 0) ?? ruleTemplates["en"] ?? "";
|
|
722
|
+
messages[locale] = formatValidationMessage(template, vars);
|
|
723
|
+
}
|
|
724
|
+
return messages;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// src/rules-generator.ts
|
|
728
|
+
function getMultiLocaleDisplayName(value, locales, fallbackLocale, defaultValue) {
|
|
729
|
+
if (!value) {
|
|
730
|
+
const result2 = {};
|
|
731
|
+
for (const locale of locales) {
|
|
732
|
+
result2[locale] = defaultValue;
|
|
733
|
+
}
|
|
734
|
+
return result2;
|
|
735
|
+
}
|
|
736
|
+
if (typeof value === "string") {
|
|
737
|
+
const result2 = {};
|
|
738
|
+
for (const locale of locales) {
|
|
739
|
+
result2[locale] = value;
|
|
740
|
+
}
|
|
741
|
+
return result2;
|
|
742
|
+
}
|
|
743
|
+
const result = {};
|
|
744
|
+
for (const locale of locales) {
|
|
745
|
+
result[locale] = value[locale] ?? value[fallbackLocale] ?? value["en"] ?? defaultValue;
|
|
746
|
+
}
|
|
747
|
+
return result;
|
|
748
|
+
}
|
|
749
|
+
function generatePropertyRules(propName, property, displayName, locales, fallbackLocale, templates) {
|
|
750
|
+
const rules = [];
|
|
751
|
+
const propDef = property;
|
|
752
|
+
if (!propDef.nullable) {
|
|
753
|
+
rules.push({
|
|
754
|
+
required: true,
|
|
755
|
+
message: getValidationMessages(templates, "required", locales, { displayName: "${displayName}" }, fallbackLocale)
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
if (property.type === "Email") {
|
|
759
|
+
rules.push({
|
|
760
|
+
type: "email",
|
|
761
|
+
message: getValidationMessages(templates, "email", locales, { displayName: "${displayName}" }, fallbackLocale)
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
if (property.type === "String" || property.type === "Text" || property.type === "LongText") {
|
|
765
|
+
if (propDef.minLength) {
|
|
766
|
+
rules.push({
|
|
767
|
+
min: propDef.minLength,
|
|
768
|
+
message: getValidationMessages(templates, "minLength", locales, { displayName: "${displayName}", min: propDef.minLength }, fallbackLocale)
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
if (propDef.maxLength || propDef.length) {
|
|
772
|
+
const max = propDef.maxLength ?? propDef.length;
|
|
773
|
+
rules.push({
|
|
774
|
+
max,
|
|
775
|
+
message: getValidationMessages(templates, "maxLength", locales, { displayName: "${displayName}", max }, fallbackLocale)
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
if (property.type === "Int" || property.type === "BigInt" || property.type === "Float") {
|
|
780
|
+
if (propDef.min !== void 0) {
|
|
781
|
+
rules.push({
|
|
782
|
+
type: property.type === "Float" ? "number" : "integer",
|
|
783
|
+
min: propDef.min,
|
|
784
|
+
message: getValidationMessages(templates, "min", locales, { displayName: "${displayName}", min: propDef.min }, fallbackLocale)
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
if (propDef.max !== void 0) {
|
|
788
|
+
rules.push({
|
|
789
|
+
type: property.type === "Float" ? "number" : "integer",
|
|
790
|
+
max: propDef.max,
|
|
791
|
+
message: getValidationMessages(templates, "max", locales, { displayName: "${displayName}", max: propDef.max }, fallbackLocale)
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
if (propDef.pattern) {
|
|
796
|
+
rules.push({
|
|
797
|
+
pattern: propDef.pattern,
|
|
798
|
+
message: getValidationMessages(templates, "pattern", locales, { displayName: "${displayName}" }, fallbackLocale)
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
for (const rule of rules) {
|
|
802
|
+
const newMessage = {};
|
|
803
|
+
for (const locale of locales) {
|
|
804
|
+
const msg = rule.message[locale];
|
|
805
|
+
if (msg) {
|
|
806
|
+
newMessage[locale] = msg.replace(/\$\{displayName\}/g, displayName[locale] ?? propName);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
rule.message = newMessage;
|
|
810
|
+
}
|
|
811
|
+
return rules;
|
|
812
|
+
}
|
|
813
|
+
function generateModelRules(schema, locales, fallbackLocale, templates) {
|
|
814
|
+
const modelDisplayName = getMultiLocaleDisplayName(
|
|
815
|
+
schema.displayName,
|
|
816
|
+
locales,
|
|
817
|
+
fallbackLocale,
|
|
818
|
+
schema.name
|
|
819
|
+
);
|
|
820
|
+
const properties = {};
|
|
821
|
+
if (schema.properties) {
|
|
822
|
+
for (const [propName, property] of Object.entries(schema.properties)) {
|
|
823
|
+
const propDef = property;
|
|
824
|
+
const displayName = getMultiLocaleDisplayName(
|
|
825
|
+
propDef.displayName,
|
|
826
|
+
locales,
|
|
827
|
+
fallbackLocale,
|
|
828
|
+
propName
|
|
829
|
+
);
|
|
830
|
+
properties[propName] = {
|
|
831
|
+
displayName,
|
|
832
|
+
rules: generatePropertyRules(propName, property, displayName, locales, fallbackLocale, templates)
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return {
|
|
837
|
+
displayName: modelDisplayName,
|
|
838
|
+
properties
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
function formatRulesFile(schemaName, rules) {
|
|
842
|
+
const parts = [];
|
|
843
|
+
parts.push(`/**
|
|
844
|
+
* Auto-generated validation rules and metadata for ${schemaName}.
|
|
845
|
+
* DO NOT EDIT - This file is automatically generated and will be overwritten.
|
|
846
|
+
*/
|
|
847
|
+
|
|
848
|
+
`);
|
|
849
|
+
parts.push(`export interface LocaleMap { [locale: string]: string; }
|
|
850
|
+
|
|
851
|
+
`);
|
|
852
|
+
parts.push(`export interface ValidationRule {
|
|
853
|
+
required?: boolean;
|
|
854
|
+
type?: 'string' | 'number' | 'email' | 'url' | 'integer';
|
|
855
|
+
min?: number;
|
|
856
|
+
max?: number;
|
|
857
|
+
len?: number;
|
|
858
|
+
pattern?: RegExp;
|
|
859
|
+
message: LocaleMap;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
`);
|
|
863
|
+
parts.push(`/** Display name for ${schemaName} */
|
|
864
|
+
`);
|
|
865
|
+
parts.push(`export const ${schemaName}DisplayName: LocaleMap = ${JSON.stringify(rules.displayName, null, 2)};
|
|
866
|
+
|
|
867
|
+
`);
|
|
868
|
+
parts.push(`/** Property display names for ${schemaName} */
|
|
869
|
+
`);
|
|
870
|
+
parts.push(`export const ${schemaName}PropertyDisplayNames: Record<string, LocaleMap> = {
|
|
871
|
+
`);
|
|
872
|
+
for (const [propName, propRules] of Object.entries(rules.properties)) {
|
|
873
|
+
parts.push(` ${propName}: ${JSON.stringify(propRules.displayName)},
|
|
874
|
+
`);
|
|
875
|
+
}
|
|
876
|
+
parts.push(`};
|
|
877
|
+
|
|
878
|
+
`);
|
|
879
|
+
parts.push(`/** Validation rules for ${schemaName} (Ant Design compatible) */
|
|
880
|
+
`);
|
|
881
|
+
parts.push(`export const ${schemaName}Rules: Record<string, ValidationRule[]> = {
|
|
882
|
+
`);
|
|
883
|
+
for (const [propName, propRules] of Object.entries(rules.properties)) {
|
|
884
|
+
if (propRules.rules.length > 0) {
|
|
885
|
+
parts.push(` ${propName}: [
|
|
886
|
+
`);
|
|
887
|
+
for (const rule of propRules.rules) {
|
|
888
|
+
const ruleObj = {};
|
|
889
|
+
if (rule.required) ruleObj.required = true;
|
|
890
|
+
if (rule.type) ruleObj.type = `'${rule.type}'`;
|
|
891
|
+
if (rule.min !== void 0) ruleObj.min = rule.min;
|
|
892
|
+
if (rule.max !== void 0) ruleObj.max = rule.max;
|
|
893
|
+
if (rule.pattern) ruleObj.pattern = `/${rule.pattern}/`;
|
|
894
|
+
ruleObj.message = rule.message;
|
|
895
|
+
const ruleStr = Object.entries(ruleObj).map(([k, v]) => {
|
|
896
|
+
if (k === "type") return `${k}: ${v}`;
|
|
897
|
+
if (k === "pattern") return `${k}: ${v}`;
|
|
898
|
+
return `${k}: ${JSON.stringify(v)}`;
|
|
899
|
+
}).join(", ");
|
|
900
|
+
parts.push(` { ${ruleStr} },
|
|
901
|
+
`);
|
|
902
|
+
}
|
|
903
|
+
parts.push(` ],
|
|
904
|
+
`);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
parts.push(`};
|
|
908
|
+
|
|
909
|
+
`);
|
|
910
|
+
parts.push(`/** Get validation rules with messages for a specific locale */
|
|
911
|
+
`);
|
|
912
|
+
parts.push(`export function get${schemaName}Rules(locale: string): Record<string, Array<{ required?: boolean; type?: string; min?: number; max?: number; pattern?: RegExp; message: string }>> {
|
|
913
|
+
const result: Record<string, Array<{ required?: boolean; type?: string; min?: number; max?: number; pattern?: RegExp; message: string }>> = {};
|
|
914
|
+
for (const [prop, rules] of Object.entries(${schemaName}Rules)) {
|
|
915
|
+
result[prop] = rules.map(rule => ({
|
|
916
|
+
...rule,
|
|
917
|
+
message: rule.message[locale] ?? rule.message['en'] ?? '',
|
|
918
|
+
}));
|
|
919
|
+
}
|
|
920
|
+
return result;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
`);
|
|
924
|
+
parts.push(`/** Get display name for a specific locale */
|
|
925
|
+
`);
|
|
926
|
+
parts.push(`export function get${schemaName}DisplayName(locale: string): string {
|
|
927
|
+
return ${schemaName}DisplayName[locale] ?? ${schemaName}DisplayName['en'] ?? '${schemaName}';
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
`);
|
|
931
|
+
parts.push(`/** Get property display name for a specific locale */
|
|
932
|
+
`);
|
|
933
|
+
parts.push(`export function get${schemaName}PropertyDisplayName(property: string, locale: string): string {
|
|
934
|
+
const names = ${schemaName}PropertyDisplayNames[property];
|
|
935
|
+
return names?.[locale] ?? names?.['en'] ?? property;
|
|
936
|
+
}
|
|
937
|
+
`);
|
|
938
|
+
return parts.join("");
|
|
939
|
+
}
|
|
940
|
+
function generateRulesFiles(schemas, options = {}) {
|
|
941
|
+
const files = [];
|
|
942
|
+
const localeConfig = options.localeConfig;
|
|
943
|
+
const locales = [...localeConfig?.locales ?? ["en"]];
|
|
944
|
+
const fallbackLocale = localeConfig?.fallbackLocale ?? "en";
|
|
945
|
+
const templates = mergeValidationTemplates(options.validationTemplates);
|
|
946
|
+
for (const schema of Object.values(schemas)) {
|
|
947
|
+
if (schema.kind === "enum") continue;
|
|
948
|
+
const rules = generateModelRules(schema, locales, fallbackLocale, templates);
|
|
949
|
+
const content = formatRulesFile(schema.name, rules);
|
|
950
|
+
files.push({
|
|
951
|
+
filePath: `rules/${schema.name}.rules.ts`,
|
|
952
|
+
content,
|
|
953
|
+
types: [`${schema.name}Rules`, `${schema.name}DisplayName`],
|
|
954
|
+
overwrite: true
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
return files;
|
|
958
|
+
}
|
|
959
|
+
|
|
532
960
|
// src/generator.ts
|
|
533
961
|
var DEFAULT_OPTIONS = {
|
|
534
962
|
readonly: true,
|
|
@@ -560,6 +988,13 @@ function generateBaseInterfaceFile(schemaName, schemas, options) {
|
|
|
560
988
|
throw new Error(`Interface not found for schema: ${schemaName}`);
|
|
561
989
|
}
|
|
562
990
|
const parts = [generateBaseHeader()];
|
|
991
|
+
if (iface.dependencies && iface.dependencies.length > 0) {
|
|
992
|
+
for (const dep of iface.dependencies) {
|
|
993
|
+
parts.push(`import type { ${dep} } from './${dep}.js';
|
|
994
|
+
`);
|
|
995
|
+
}
|
|
996
|
+
parts.push("\n");
|
|
997
|
+
}
|
|
563
998
|
parts.push(formatInterface(iface));
|
|
564
999
|
parts.push("\n");
|
|
565
1000
|
return {
|
|
@@ -676,11 +1111,11 @@ function generateIndexFile(schemas, enums, typeAliases) {
|
|
|
676
1111
|
function generateTypeScript(schemas, options = {}) {
|
|
677
1112
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
678
1113
|
const files = [];
|
|
679
|
-
const enums = generateEnums(schemas);
|
|
1114
|
+
const enums = generateEnums(schemas, opts);
|
|
680
1115
|
for (const enumDef of enums) {
|
|
681
1116
|
files.push(generateEnumFile(enumDef));
|
|
682
1117
|
}
|
|
683
|
-
const typeAliases = extractInlineEnums(schemas);
|
|
1118
|
+
const typeAliases = extractInlineEnums(schemas, opts);
|
|
684
1119
|
for (const alias of typeAliases) {
|
|
685
1120
|
files.push(generateTypeAliasFile(alias));
|
|
686
1121
|
}
|
|
@@ -692,22 +1127,32 @@ function generateTypeScript(schemas, options = {}) {
|
|
|
692
1127
|
if (schema.kind === "enum") continue;
|
|
693
1128
|
files.push(generateModelFile(schema.name));
|
|
694
1129
|
}
|
|
1130
|
+
if (opts.generateRules) {
|
|
1131
|
+
const rulesFiles = generateRulesFiles(schemas, opts);
|
|
1132
|
+
files.push(...rulesFiles);
|
|
1133
|
+
}
|
|
695
1134
|
files.push(generateIndexFile(schemas, enums, typeAliases));
|
|
696
1135
|
return files;
|
|
697
1136
|
}
|
|
698
1137
|
// Annotate the CommonJS export names for ESM import in node:
|
|
699
1138
|
0 && (module.exports = {
|
|
1139
|
+
DEFAULT_VALIDATION_TEMPLATES,
|
|
700
1140
|
enumToUnionType,
|
|
701
1141
|
extractInlineEnums,
|
|
702
1142
|
formatEnum,
|
|
703
1143
|
formatInterface,
|
|
704
1144
|
formatProperty,
|
|
705
1145
|
formatTypeAlias,
|
|
1146
|
+
formatValidationMessage,
|
|
706
1147
|
generateEnums,
|
|
707
1148
|
generateInterfaces,
|
|
1149
|
+
generateModelRules,
|
|
1150
|
+
generateRulesFiles,
|
|
708
1151
|
generateTypeScript,
|
|
709
1152
|
generateTypeScriptFiles,
|
|
710
1153
|
getPropertyType,
|
|
1154
|
+
getValidationMessages,
|
|
1155
|
+
mergeValidationTemplates,
|
|
711
1156
|
propertyToTSProperty,
|
|
712
1157
|
schemaToEnum,
|
|
713
1158
|
schemaToInterface,
|