@famgia/omnify-typescript 0.0.66 → 0.0.68
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/ai-guides/react-form-guide.md +259 -0
- package/ai-guides/typescript-guide.md +53 -0
- package/dist/{chunk-4L77AHAC.js → chunk-6I4O23X6.js} +521 -66
- package/dist/chunk-6I4O23X6.js.map +1 -0
- package/dist/index.cjs +761 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +138 -2
- package/dist/index.d.ts +138 -2
- package/dist/index.js +227 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin.cjs +624 -75
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +6 -0
- package/dist/plugin.d.ts +6 -0
- package/dist/plugin.js +96 -11
- package/dist/plugin.js.map +1 -1
- package/package.json +4 -3
- package/scripts/postinstall.js +29 -40
- package/stubs/JapaneseAddressField.tsx.stub +289 -0
- package/stubs/JapaneseBankField.tsx.stub +212 -0
- package/stubs/JapaneseNameField.tsx.stub +194 -0
- package/stubs/ai-guides/checklists/react.md.stub +108 -0
- package/stubs/ai-guides/cursor/react-design.mdc.stub +289 -0
- package/stubs/ai-guides/cursor/react-form.mdc.stub +277 -0
- package/stubs/ai-guides/cursor/react-services.mdc.stub +304 -0
- package/stubs/ai-guides/cursor/react.mdc.stub +305 -0
- package/stubs/ai-guides/react/README.md.stub +221 -0
- package/stubs/ai-guides/react/antd-guide.md.stub +294 -0
- package/stubs/ai-guides/react/checklist.md.stub +108 -0
- package/stubs/ai-guides/react/datetime-guide.md.stub +137 -0
- package/stubs/ai-guides/react/design-philosophy.md.stub +363 -0
- package/stubs/ai-guides/react/i18n-guide.md.stub +211 -0
- package/stubs/ai-guides/react/laravel-integration.md.stub +181 -0
- package/stubs/ai-guides/react/service-pattern.md.stub +180 -0
- package/stubs/ai-guides/react/tanstack-query.md.stub +339 -0
- package/stubs/ai-guides/react/types-guide.md.stub +524 -0
- package/stubs/components-index.ts.stub +13 -0
- package/stubs/form-validation.ts.stub +106 -0
- package/stubs/rules/index.ts.stub +48 -0
- package/stubs/rules/kana.ts.stub +291 -0
- package/stubs/use-form-mutation.ts.stub +117 -0
- package/stubs/zod-i18n.ts.stub +32 -0
- package/dist/chunk-4L77AHAC.js.map +0 -1
package/dist/plugin.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/plugin.ts
|
|
@@ -108,6 +118,10 @@ function getPropertyType(property, _allSchemas) {
|
|
|
108
118
|
return "unknown";
|
|
109
119
|
}
|
|
110
120
|
}
|
|
121
|
+
if (property.type === "EnumRef") {
|
|
122
|
+
const enumRefProp = property;
|
|
123
|
+
return enumRefProp.enum;
|
|
124
|
+
}
|
|
111
125
|
if (property.type === "Enum") {
|
|
112
126
|
const enumProp = property;
|
|
113
127
|
if (typeof enumProp.enum === "string") {
|
|
@@ -136,7 +150,7 @@ function propertyToTSProperties(propertyName, property, allSchemas, options = {}
|
|
|
136
150
|
for (const field of customType.expand) {
|
|
137
151
|
const fieldName = `${propertyName}_${toSnakeCase(field.suffix)}`;
|
|
138
152
|
const fieldOverride = baseProp.fields?.[field.suffix];
|
|
139
|
-
const isNullable = fieldOverride?.nullable ?? baseProp.nullable ?? false;
|
|
153
|
+
const isNullable = fieldOverride?.nullable ?? field.sql?.nullable ?? baseProp.nullable ?? false;
|
|
140
154
|
const tsType = field.typescript?.type ?? "string";
|
|
141
155
|
expandedProps.push({
|
|
142
156
|
name: fieldName,
|
|
@@ -278,12 +292,24 @@ function schemaToInterface(schema, allSchemas, options = {}) {
|
|
|
278
292
|
}
|
|
279
293
|
}
|
|
280
294
|
}
|
|
295
|
+
const enumDependencySet = /* @__PURE__ */ new Set();
|
|
296
|
+
if (schema.properties) {
|
|
297
|
+
for (const property of Object.values(schema.properties)) {
|
|
298
|
+
if (property.type === "EnumRef") {
|
|
299
|
+
const enumRefProp = property;
|
|
300
|
+
if (enumRefProp.enum) {
|
|
301
|
+
enumDependencySet.add(enumRefProp.enum);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
281
306
|
const schemaDisplayName = resolveDisplayName(schema.displayName, options);
|
|
282
307
|
return {
|
|
283
308
|
name: toInterfaceName(schema.name),
|
|
284
309
|
properties,
|
|
285
310
|
comment: schemaDisplayName ?? schema.name,
|
|
286
|
-
dependencies: dependencySet.size > 0 ? Array.from(dependencySet).sort() : void 0
|
|
311
|
+
dependencies: dependencySet.size > 0 ? Array.from(dependencySet).sort() : void 0,
|
|
312
|
+
enumDependencies: enumDependencySet.size > 0 ? Array.from(enumDependencySet).sort() : void 0
|
|
287
313
|
};
|
|
288
314
|
}
|
|
289
315
|
function formatProperty(property) {
|
|
@@ -330,7 +356,11 @@ function resolveDisplayName2(value, options = {}) {
|
|
|
330
356
|
});
|
|
331
357
|
}
|
|
332
358
|
function toEnumMemberName(value) {
|
|
333
|
-
|
|
359
|
+
let result = value.split(/[-_\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
360
|
+
if (/^\d/.test(result)) {
|
|
361
|
+
result = "_" + result;
|
|
362
|
+
}
|
|
363
|
+
return result;
|
|
334
364
|
}
|
|
335
365
|
function toEnumName(schemaName) {
|
|
336
366
|
return schemaName;
|
|
@@ -384,6 +414,46 @@ function generateEnums(schemas, options = {}) {
|
|
|
384
414
|
}
|
|
385
415
|
return enums;
|
|
386
416
|
}
|
|
417
|
+
function pluginEnumToTSEnum(enumDef, options = {}) {
|
|
418
|
+
const values = enumDef.values.map((v) => {
|
|
419
|
+
let label;
|
|
420
|
+
if (v.label !== void 0) {
|
|
421
|
+
if (typeof v.label === "string") {
|
|
422
|
+
label = v.label;
|
|
423
|
+
} else if (options.multiLocale) {
|
|
424
|
+
label = v.label;
|
|
425
|
+
} else {
|
|
426
|
+
label = resolveDisplayName2(v.label, options);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
name: toEnumMemberName(v.value),
|
|
431
|
+
value: v.value,
|
|
432
|
+
label,
|
|
433
|
+
extra: v.extra
|
|
434
|
+
};
|
|
435
|
+
});
|
|
436
|
+
let comment;
|
|
437
|
+
if (enumDef.displayName !== void 0) {
|
|
438
|
+
if (typeof enumDef.displayName === "string") {
|
|
439
|
+
comment = enumDef.displayName;
|
|
440
|
+
} else {
|
|
441
|
+
comment = resolveDisplayName2(enumDef.displayName, options);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
name: enumDef.name,
|
|
446
|
+
values,
|
|
447
|
+
comment: comment ?? enumDef.name
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function generatePluginEnums(pluginEnums, options = {}) {
|
|
451
|
+
const enums = [];
|
|
452
|
+
for (const enumDef of pluginEnums.values()) {
|
|
453
|
+
enums.push(pluginEnumToTSEnum(enumDef, options));
|
|
454
|
+
}
|
|
455
|
+
return enums;
|
|
456
|
+
}
|
|
387
457
|
function isMultiLocaleLabel(label) {
|
|
388
458
|
return label !== void 0 && typeof label === "object";
|
|
389
459
|
}
|
|
@@ -404,7 +474,7 @@ ${enumValues}
|
|
|
404
474
|
`);
|
|
405
475
|
parts.push(`/** All ${name} values */
|
|
406
476
|
`);
|
|
407
|
-
parts.push(`export const ${name}Values = Object.values(${name});
|
|
477
|
+
parts.push(`export const ${name}Values = Object.values(${name}) as ${name}[];
|
|
408
478
|
|
|
409
479
|
`);
|
|
410
480
|
parts.push(`/** Type guard for ${name} */
|
|
@@ -422,10 +492,10 @@ ${enumValues}
|
|
|
422
492
|
if (hasMultiLocale) {
|
|
423
493
|
const labelEntries = values.filter((v) => v.label !== void 0).map((v) => {
|
|
424
494
|
if (isMultiLocaleLabel(v.label)) {
|
|
425
|
-
const locales = Object.entries(v.label).map(([locale, text]) =>
|
|
495
|
+
const locales = Object.entries(v.label).map(([locale, text]) => `'${locale}': '${escapeString(text)}'`).join(", ");
|
|
426
496
|
return ` [${name}.${v.name}]: { ${locales} },`;
|
|
427
497
|
}
|
|
428
|
-
return ` [${name}.${v.name}]: { default: '${v.label}' },`;
|
|
498
|
+
return ` [${name}.${v.name}]: { default: '${escapeString(String(v.label))}' },`;
|
|
429
499
|
}).join("\n");
|
|
430
500
|
parts.push(`const ${lowerFirst(name)}Labels: Partial<Record<${name}, Record<string, string>>> = {
|
|
431
501
|
${labelEntries}
|
|
@@ -442,13 +512,15 @@ ${labelEntries}
|
|
|
442
512
|
`);
|
|
443
513
|
parts.push(` if (locale && labels[locale]) return labels[locale];
|
|
444
514
|
`);
|
|
445
|
-
parts.push(`
|
|
515
|
+
parts.push(` // Fallback: ja \u2192 en \u2192 first available
|
|
516
|
+
`);
|
|
517
|
+
parts.push(` return labels['ja'] ?? labels['en'] ?? Object.values(labels)[0] ?? value;
|
|
446
518
|
`);
|
|
447
519
|
parts.push(`}
|
|
448
520
|
|
|
449
521
|
`);
|
|
450
522
|
} else {
|
|
451
|
-
const labelEntries = values.filter((v) => v.label !== void 0).map((v) => ` [${name}.${v.name}]: '${v.label}',`).join("\n");
|
|
523
|
+
const labelEntries = values.filter((v) => v.label !== void 0).map((v) => ` [${name}.${v.name}]: '${escapeString(String(v.label))}',`).join("\n");
|
|
452
524
|
parts.push(`const ${lowerFirst(name)}Labels: Partial<Record<${name}, string>> = {
|
|
453
525
|
${labelEntries}
|
|
454
526
|
};
|
|
@@ -504,6 +576,9 @@ ${extraEntries}
|
|
|
504
576
|
function lowerFirst(str) {
|
|
505
577
|
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
506
578
|
}
|
|
579
|
+
function escapeString(str) {
|
|
580
|
+
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
581
|
+
}
|
|
507
582
|
function formatTypeAlias(alias) {
|
|
508
583
|
const { name, type, comment } = alias;
|
|
509
584
|
const parts = [];
|
|
@@ -1017,6 +1092,13 @@ function getZodSchemaForType(propDef, fieldName, customTypes) {
|
|
|
1017
1092
|
case "Json":
|
|
1018
1093
|
schema = "z.unknown()";
|
|
1019
1094
|
break;
|
|
1095
|
+
case "EnumRef":
|
|
1096
|
+
if (typeof def.enum === "string") {
|
|
1097
|
+
schema = `z.nativeEnum(${def.enum})`;
|
|
1098
|
+
} else {
|
|
1099
|
+
schema = "z.string()";
|
|
1100
|
+
}
|
|
1101
|
+
break;
|
|
1020
1102
|
case "Enum":
|
|
1021
1103
|
if (typeof def.enum === "string") {
|
|
1022
1104
|
schema = `${def.enum}Schema`;
|
|
@@ -1062,15 +1144,35 @@ function generateCompoundTypeSchemas(propName, propDef, customType, options) {
|
|
|
1062
1144
|
for (const field of customType.expand) {
|
|
1063
1145
|
const fieldName = `${toSnakeCase(propName)}_${toSnakeCase(field.suffix)}`;
|
|
1064
1146
|
const fieldOverride = propFields?.[field.suffix];
|
|
1065
|
-
const isNullable = fieldOverride?.nullable ?? propDef.nullable ?? false;
|
|
1066
|
-
const
|
|
1147
|
+
const isNullable = fieldOverride?.nullable ?? field.sql?.nullable ?? propDef.nullable ?? false;
|
|
1148
|
+
const pluginRules = field.rules;
|
|
1149
|
+
const overrideRules = fieldOverride?.rules;
|
|
1150
|
+
const length = fieldOverride?.length ?? overrideRules?.maxLength ?? pluginRules?.maxLength ?? field.sql?.length;
|
|
1151
|
+
const minLength = overrideRules?.minLength ?? pluginRules?.minLength;
|
|
1152
|
+
const pattern = overrideRules?.pattern ?? pluginRules?.pattern;
|
|
1153
|
+
const format = overrideRules?.format ?? pluginRules?.format;
|
|
1067
1154
|
let schema = "z.string()";
|
|
1155
|
+
if (format === "email") {
|
|
1156
|
+
schema = "z.string().email()";
|
|
1157
|
+
} else if (format === "url") {
|
|
1158
|
+
schema = "z.string().url()";
|
|
1159
|
+
} else if (format === "phone") {
|
|
1160
|
+
schema = "z.string()";
|
|
1161
|
+
} else if (format === "postal_code") {
|
|
1162
|
+
schema = `z.string().regex(/^\\d{3}-?\\d{4}$/)`;
|
|
1163
|
+
}
|
|
1068
1164
|
if (!isNullable) {
|
|
1069
|
-
|
|
1165
|
+
const min = minLength ?? 1;
|
|
1166
|
+
schema += `.min(${min})`;
|
|
1167
|
+
} else if (minLength) {
|
|
1168
|
+
schema += `.min(${minLength})`;
|
|
1070
1169
|
}
|
|
1071
1170
|
if (length) {
|
|
1072
1171
|
schema += `.max(${length})`;
|
|
1073
1172
|
}
|
|
1173
|
+
if (pattern && !format) {
|
|
1174
|
+
schema += `.regex(/${pattern}/)`;
|
|
1175
|
+
}
|
|
1074
1176
|
if (isNullable) {
|
|
1075
1177
|
schema += ".optional().nullable()";
|
|
1076
1178
|
}
|
|
@@ -1126,6 +1228,7 @@ function generateDisplayNames(schema, options) {
|
|
|
1126
1228
|
schema.name
|
|
1127
1229
|
);
|
|
1128
1230
|
const propertyDisplayNames = {};
|
|
1231
|
+
const propertyPlaceholders = {};
|
|
1129
1232
|
if (schema.properties) {
|
|
1130
1233
|
for (const [propName, propDef] of Object.entries(schema.properties)) {
|
|
1131
1234
|
const prop = propDef;
|
|
@@ -1133,19 +1236,47 @@ function generateDisplayNames(schema, options) {
|
|
|
1133
1236
|
if (customTypes) {
|
|
1134
1237
|
const customType = customTypes.get(propDef.type);
|
|
1135
1238
|
if (customType?.compound && customType.expand) {
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
propertyDisplayNames[expandedFieldName] = getMultiLocaleDisplayName2(
|
|
1239
|
+
if (prop.displayName) {
|
|
1240
|
+
propertyDisplayNames[fieldName] = getMultiLocaleDisplayName2(
|
|
1139
1241
|
prop.displayName,
|
|
1140
1242
|
locales,
|
|
1141
1243
|
fallbackLocale,
|
|
1142
1244
|
propName
|
|
1143
1245
|
);
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1246
|
+
}
|
|
1247
|
+
for (const field of customType.expand) {
|
|
1248
|
+
const expandedFieldName = `${fieldName}_${toSnakeCase(field.suffix)}`;
|
|
1249
|
+
const fieldOverride = prop.fields?.[field.suffix];
|
|
1250
|
+
const labelSource = fieldOverride?.displayName ?? field.label;
|
|
1251
|
+
if (labelSource) {
|
|
1252
|
+
propertyDisplayNames[expandedFieldName] = getMultiLocaleDisplayName2(
|
|
1253
|
+
labelSource,
|
|
1254
|
+
locales,
|
|
1255
|
+
fallbackLocale,
|
|
1256
|
+
field.suffix
|
|
1257
|
+
);
|
|
1258
|
+
} else {
|
|
1259
|
+
propertyDisplayNames[expandedFieldName] = getMultiLocaleDisplayName2(
|
|
1260
|
+
prop.displayName,
|
|
1261
|
+
locales,
|
|
1262
|
+
fallbackLocale,
|
|
1263
|
+
propName
|
|
1264
|
+
);
|
|
1265
|
+
for (const locale of locales) {
|
|
1266
|
+
propertyDisplayNames[expandedFieldName] = {
|
|
1267
|
+
...propertyDisplayNames[expandedFieldName],
|
|
1268
|
+
[locale]: `${propertyDisplayNames[expandedFieldName][locale]} (${field.suffix})`
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
const placeholderSource = fieldOverride?.placeholder ?? field.placeholder;
|
|
1273
|
+
if (placeholderSource) {
|
|
1274
|
+
propertyPlaceholders[expandedFieldName] = getMultiLocaleDisplayName2(
|
|
1275
|
+
placeholderSource,
|
|
1276
|
+
locales,
|
|
1277
|
+
fallbackLocale,
|
|
1278
|
+
""
|
|
1279
|
+
);
|
|
1149
1280
|
}
|
|
1150
1281
|
}
|
|
1151
1282
|
continue;
|
|
@@ -1157,9 +1288,17 @@ function generateDisplayNames(schema, options) {
|
|
|
1157
1288
|
fallbackLocale,
|
|
1158
1289
|
propName
|
|
1159
1290
|
);
|
|
1291
|
+
if (prop.placeholder) {
|
|
1292
|
+
propertyPlaceholders[fieldName] = getMultiLocaleDisplayName2(
|
|
1293
|
+
prop.placeholder,
|
|
1294
|
+
locales,
|
|
1295
|
+
fallbackLocale,
|
|
1296
|
+
""
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1160
1299
|
}
|
|
1161
1300
|
}
|
|
1162
|
-
return { displayName, propertyDisplayNames };
|
|
1301
|
+
return { displayName, propertyDisplayNames, propertyPlaceholders };
|
|
1163
1302
|
}
|
|
1164
1303
|
function getExcludedFields(schema, customTypes) {
|
|
1165
1304
|
const createExclude = /* @__PURE__ */ new Set();
|
|
@@ -1203,24 +1342,44 @@ function formatZodSchemasSection(schemaName, zodSchemas, displayNames, excludedF
|
|
|
1203
1342
|
const lowerName = schemaName.charAt(0).toLowerCase() + schemaName.slice(1);
|
|
1204
1343
|
parts.push(`// ============================================================================
|
|
1205
1344
|
`);
|
|
1206
|
-
parts.push(`//
|
|
1345
|
+
parts.push(`// I18n (Internationalization)
|
|
1207
1346
|
`);
|
|
1208
1347
|
parts.push(`// ============================================================================
|
|
1209
1348
|
|
|
1210
1349
|
`);
|
|
1211
|
-
parts.push(`/**
|
|
1350
|
+
parts.push(`/**
|
|
1212
1351
|
`);
|
|
1213
|
-
parts.push(`
|
|
1214
|
-
|
|
1352
|
+
parts.push(` * Unified i18n object for ${schemaName}
|
|
1215
1353
|
`);
|
|
1216
|
-
parts.push(
|
|
1354
|
+
parts.push(` * Contains model label and all field labels/placeholders
|
|
1355
|
+
`);
|
|
1356
|
+
parts.push(` */
|
|
1357
|
+
`);
|
|
1358
|
+
parts.push(`export const ${lowerName}I18n = {
|
|
1359
|
+
`);
|
|
1360
|
+
parts.push(` /** Model display name */
|
|
1361
|
+
`);
|
|
1362
|
+
parts.push(` label: ${JSON.stringify(displayNames.displayName)},
|
|
1363
|
+
`);
|
|
1364
|
+
parts.push(` /** Field labels and placeholders */
|
|
1365
|
+
`);
|
|
1366
|
+
parts.push(` fields: {
|
|
1367
|
+
`);
|
|
1368
|
+
for (const [propName, labelMap] of Object.entries(displayNames.propertyDisplayNames)) {
|
|
1369
|
+
const placeholderMap = displayNames.propertyPlaceholders[propName];
|
|
1370
|
+
parts.push(` ${propName}: {
|
|
1371
|
+
`);
|
|
1372
|
+
parts.push(` label: ${JSON.stringify(labelMap)},
|
|
1217
1373
|
`);
|
|
1218
|
-
|
|
1374
|
+
if (placeholderMap) {
|
|
1375
|
+
parts.push(` placeholder: ${JSON.stringify(placeholderMap)},
|
|
1219
1376
|
`);
|
|
1220
|
-
|
|
1221
|
-
parts.push(`
|
|
1377
|
+
}
|
|
1378
|
+
parts.push(` },
|
|
1222
1379
|
`);
|
|
1223
1380
|
}
|
|
1381
|
+
parts.push(` },
|
|
1382
|
+
`);
|
|
1224
1383
|
parts.push(`} as const;
|
|
1225
1384
|
|
|
1226
1385
|
`);
|
|
@@ -1277,35 +1436,50 @@ function formatZodSchemasSection(schemaName, zodSchemas, displayNames, excludedF
|
|
|
1277
1436
|
`);
|
|
1278
1437
|
parts.push(`// ============================================================================
|
|
1279
1438
|
`);
|
|
1280
|
-
parts.push(`// Helper Functions
|
|
1439
|
+
parts.push(`// I18n Helper Functions
|
|
1281
1440
|
`);
|
|
1282
1441
|
parts.push(`// ============================================================================
|
|
1283
1442
|
|
|
1284
1443
|
`);
|
|
1285
|
-
parts.push(`/** Get
|
|
1444
|
+
parts.push(`/** Get model label for a specific locale */
|
|
1286
1445
|
`);
|
|
1287
|
-
parts.push(`export function get${schemaName}
|
|
1446
|
+
parts.push(`export function get${schemaName}Label(locale: string): string {
|
|
1288
1447
|
`);
|
|
1289
|
-
parts.push(` return ${
|
|
1448
|
+
parts.push(` return ${lowerName}I18n.label[locale as keyof typeof ${lowerName}I18n.label] ?? ${lowerName}I18n.label['en'] ?? '${schemaName}';
|
|
1290
1449
|
`);
|
|
1291
1450
|
parts.push(`}
|
|
1292
1451
|
|
|
1293
1452
|
`);
|
|
1294
|
-
parts.push(`/** Get
|
|
1453
|
+
parts.push(`/** Get field label for a specific locale */
|
|
1295
1454
|
`);
|
|
1296
|
-
parts.push(`export function get${schemaName}
|
|
1455
|
+
parts.push(`export function get${schemaName}FieldLabel(field: string, locale: string): string {
|
|
1456
|
+
`);
|
|
1457
|
+
parts.push(` const fieldI18n = ${lowerName}I18n.fields[field as keyof typeof ${lowerName}I18n.fields];
|
|
1458
|
+
`);
|
|
1459
|
+
parts.push(` if (!fieldI18n) return field;
|
|
1460
|
+
`);
|
|
1461
|
+
parts.push(` return fieldI18n.label[locale as keyof typeof fieldI18n.label] ?? fieldI18n.label['en'] ?? field;
|
|
1462
|
+
`);
|
|
1463
|
+
parts.push(`}
|
|
1464
|
+
|
|
1465
|
+
`);
|
|
1466
|
+
parts.push(`/** Get field placeholder for a specific locale */
|
|
1467
|
+
`);
|
|
1468
|
+
parts.push(`export function get${schemaName}FieldPlaceholder(field: string, locale: string): string {
|
|
1297
1469
|
`);
|
|
1298
|
-
parts.push(` const
|
|
1470
|
+
parts.push(` const fieldI18n = ${lowerName}I18n.fields[field as keyof typeof ${lowerName}I18n.fields];
|
|
1299
1471
|
`);
|
|
1300
|
-
parts.push(` if (!
|
|
1472
|
+
parts.push(` if (!fieldI18n || !('placeholder' in fieldI18n)) return '';
|
|
1301
1473
|
`);
|
|
1302
|
-
parts.push(`
|
|
1474
|
+
parts.push(` const placeholder = fieldI18n.placeholder as Record<string, string>;
|
|
1475
|
+
`);
|
|
1476
|
+
parts.push(` return placeholder[locale] ?? placeholder['en'] ?? '';
|
|
1303
1477
|
`);
|
|
1304
1478
|
parts.push(`}
|
|
1305
1479
|
`);
|
|
1306
1480
|
return parts.join("");
|
|
1307
1481
|
}
|
|
1308
|
-
function formatZodModelFile(schemaName) {
|
|
1482
|
+
function formatZodModelFile(schemaName, _hasPlaceholders = false) {
|
|
1309
1483
|
const lowerName = schemaName.charAt(0).toLowerCase() + schemaName.slice(1);
|
|
1310
1484
|
return `/**
|
|
1311
1485
|
* ${schemaName} Model
|
|
@@ -1321,10 +1495,10 @@ import {
|
|
|
1321
1495
|
base${schemaName}Schemas,
|
|
1322
1496
|
base${schemaName}CreateSchema,
|
|
1323
1497
|
base${schemaName}UpdateSchema,
|
|
1324
|
-
${
|
|
1325
|
-
${schemaName}
|
|
1326
|
-
get${schemaName}
|
|
1327
|
-
get${schemaName}
|
|
1498
|
+
${lowerName}I18n,
|
|
1499
|
+
get${schemaName}Label,
|
|
1500
|
+
get${schemaName}FieldLabel,
|
|
1501
|
+
get${schemaName}FieldPlaceholder,
|
|
1328
1502
|
} from './base/${schemaName}.js';
|
|
1329
1503
|
|
|
1330
1504
|
// ============================================================================
|
|
@@ -1350,12 +1524,12 @@ export const ${lowerName}UpdateSchema = base${schemaName}UpdateSchema;
|
|
|
1350
1524
|
export type ${schemaName}Create = z.infer<typeof ${lowerName}CreateSchema>;
|
|
1351
1525
|
export type ${schemaName}Update = z.infer<typeof ${lowerName}UpdateSchema>;
|
|
1352
1526
|
|
|
1353
|
-
// Re-export
|
|
1527
|
+
// Re-export i18n and helpers
|
|
1354
1528
|
export {
|
|
1355
|
-
${
|
|
1356
|
-
${schemaName}
|
|
1357
|
-
get${schemaName}
|
|
1358
|
-
get${schemaName}
|
|
1529
|
+
${lowerName}I18n,
|
|
1530
|
+
get${schemaName}Label,
|
|
1531
|
+
get${schemaName}FieldLabel,
|
|
1532
|
+
get${schemaName}FieldPlaceholder,
|
|
1359
1533
|
};
|
|
1360
1534
|
|
|
1361
1535
|
// Re-export base type for internal use
|
|
@@ -1471,13 +1645,24 @@ function generateBaseInterfaceFile(schemaName, schemas, options) {
|
|
|
1471
1645
|
parts.push(`import type { ${commonImports.join(", ")} } from '../common.js';
|
|
1472
1646
|
`);
|
|
1473
1647
|
}
|
|
1648
|
+
if (iface.enumDependencies && iface.enumDependencies.length > 0) {
|
|
1649
|
+
const enumPrefix = options.enumImportPrefix ? `../${options.enumImportPrefix}` : "../enum";
|
|
1650
|
+
const pluginEnumNames = new Set(
|
|
1651
|
+
options.pluginEnums ? Array.from(options.pluginEnums.keys()) : []
|
|
1652
|
+
);
|
|
1653
|
+
for (const enumName of iface.enumDependencies) {
|
|
1654
|
+
const enumPath = pluginEnumNames.has(enumName) ? `${enumPrefix}/plugin/${enumName}.js` : `${enumPrefix}/${enumName}.js`;
|
|
1655
|
+
parts.push(`import { ${enumName} } from '${enumPath}';
|
|
1656
|
+
`);
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1474
1659
|
if (iface.dependencies && iface.dependencies.length > 0) {
|
|
1475
1660
|
for (const dep of iface.dependencies) {
|
|
1476
1661
|
parts.push(`import type { ${dep} } from './${dep}.js';
|
|
1477
1662
|
`);
|
|
1478
1663
|
}
|
|
1479
1664
|
parts.push("\n");
|
|
1480
|
-
} else if (commonImports.length > 0 || options.generateZodSchemas) {
|
|
1665
|
+
} else if (commonImports.length > 0 || options.generateZodSchemas || iface.enumDependencies && iface.enumDependencies.length > 0) {
|
|
1481
1666
|
parts.push("\n");
|
|
1482
1667
|
}
|
|
1483
1668
|
parts.push(formatInterface(iface));
|
|
@@ -1498,15 +1683,17 @@ function generateBaseInterfaceFile(schemaName, schemas, options) {
|
|
|
1498
1683
|
overwrite: true
|
|
1499
1684
|
};
|
|
1500
1685
|
}
|
|
1501
|
-
function generateEnumFile(enumDef) {
|
|
1686
|
+
function generateEnumFile(enumDef, isPluginEnum = false) {
|
|
1502
1687
|
const parts = [generateBaseHeader()];
|
|
1503
1688
|
parts.push(formatEnum(enumDef));
|
|
1504
1689
|
parts.push("\n");
|
|
1690
|
+
const filePath = isPluginEnum ? `plugin/${enumDef.name}.ts` : `${enumDef.name}.ts`;
|
|
1505
1691
|
return {
|
|
1506
|
-
filePath
|
|
1692
|
+
filePath,
|
|
1507
1693
|
content: parts.join(""),
|
|
1508
1694
|
types: [enumDef.name],
|
|
1509
|
-
overwrite: true
|
|
1695
|
+
overwrite: true,
|
|
1696
|
+
category: "enum"
|
|
1510
1697
|
};
|
|
1511
1698
|
}
|
|
1512
1699
|
function generateTypeAliasFile(alias) {
|
|
@@ -1514,10 +1701,11 @@ function generateTypeAliasFile(alias) {
|
|
|
1514
1701
|
parts.push(formatTypeAlias(alias));
|
|
1515
1702
|
parts.push("\n");
|
|
1516
1703
|
return {
|
|
1517
|
-
filePath:
|
|
1704
|
+
filePath: `${alias.name}.ts`,
|
|
1518
1705
|
content: parts.join(""),
|
|
1519
1706
|
types: [alias.name],
|
|
1520
|
-
overwrite: true
|
|
1707
|
+
overwrite: true,
|
|
1708
|
+
category: "enum"
|
|
1521
1709
|
};
|
|
1522
1710
|
}
|
|
1523
1711
|
function generateModelFile(schemaName, options) {
|
|
@@ -1558,6 +1746,88 @@ function generateModelFile(schemaName, options) {
|
|
|
1558
1746
|
// Never overwrite user models
|
|
1559
1747
|
};
|
|
1560
1748
|
}
|
|
1749
|
+
var DEFAULT_VALIDATION_MESSAGES = {
|
|
1750
|
+
required: {
|
|
1751
|
+
en: "${displayName} is required",
|
|
1752
|
+
ja: "${displayName}\u306F\u5FC5\u9808\u3067\u3059",
|
|
1753
|
+
vi: "${displayName} l\xE0 b\u1EAFt bu\u1ED9c",
|
|
1754
|
+
ko: "${displayName}\uC740(\uB294) \uD544\uC218\uC785\uB2C8\uB2E4",
|
|
1755
|
+
"zh-CN": "${displayName}\u662F\u5FC5\u586B\u9879",
|
|
1756
|
+
"zh-TW": "${displayName}\u70BA\u5FC5\u586B\u6B04\u4F4D",
|
|
1757
|
+
th: "${displayName} \u0E08\u0E33\u0E40\u0E1B\u0E47\u0E19\u0E15\u0E49\u0E2D\u0E07\u0E01\u0E23\u0E2D\u0E01",
|
|
1758
|
+
es: "${displayName} es obligatorio"
|
|
1759
|
+
},
|
|
1760
|
+
minLength: {
|
|
1761
|
+
en: "${displayName} must be at least ${min} characters",
|
|
1762
|
+
ja: "${displayName}\u306F${min}\u6587\u5B57\u4EE5\u4E0A\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
1763
|
+
vi: "${displayName} ph\u1EA3i c\xF3 \xEDt nh\u1EA5t ${min} k\xFD t\u1EF1",
|
|
1764
|
+
ko: "${displayName}\uC740(\uB294) ${min}\uC790 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4",
|
|
1765
|
+
"zh-CN": "${displayName}\u81F3\u5C11\u9700\u8981${min}\u4E2A\u5B57\u7B26",
|
|
1766
|
+
"zh-TW": "${displayName}\u81F3\u5C11\u9700\u8981${min}\u500B\u5B57\u5143",
|
|
1767
|
+
th: "${displayName} \u0E15\u0E49\u0E2D\u0E07\u0E21\u0E35\u0E2D\u0E22\u0E48\u0E32\u0E07\u0E19\u0E49\u0E2D\u0E22 ${min} \u0E15\u0E31\u0E27\u0E2D\u0E31\u0E01\u0E29\u0E23",
|
|
1768
|
+
es: "${displayName} debe tener al menos ${min} caracteres"
|
|
1769
|
+
},
|
|
1770
|
+
maxLength: {
|
|
1771
|
+
en: "${displayName} must be at most ${max} characters",
|
|
1772
|
+
ja: "${displayName}\u306F${max}\u6587\u5B57\u4EE5\u5185\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
1773
|
+
vi: "${displayName} kh\xF4ng \u0111\u01B0\u1EE3c qu\xE1 ${max} k\xFD t\u1EF1",
|
|
1774
|
+
ko: "${displayName}\uC740(\uB294) ${max}\uC790 \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4",
|
|
1775
|
+
"zh-CN": "${displayName}\u6700\u591A${max}\u4E2A\u5B57\u7B26",
|
|
1776
|
+
"zh-TW": "${displayName}\u6700\u591A${max}\u500B\u5B57\u5143",
|
|
1777
|
+
th: "${displayName} \u0E15\u0E49\u0E2D\u0E07\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 ${max} \u0E15\u0E31\u0E27\u0E2D\u0E31\u0E01\u0E29\u0E23",
|
|
1778
|
+
es: "${displayName} debe tener como m\xE1ximo ${max} caracteres"
|
|
1779
|
+
},
|
|
1780
|
+
min: {
|
|
1781
|
+
en: "${displayName} must be at least ${min}",
|
|
1782
|
+
ja: "${displayName}\u306F${min}\u4EE5\u4E0A\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
1783
|
+
vi: "${displayName} ph\u1EA3i l\u1EDBn h\u01A1n ho\u1EB7c b\u1EB1ng ${min}",
|
|
1784
|
+
ko: "${displayName}\uC740(\uB294) ${min} \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4",
|
|
1785
|
+
"zh-CN": "${displayName}\u5FC5\u987B\u5927\u4E8E\u7B49\u4E8E${min}",
|
|
1786
|
+
"zh-TW": "${displayName}\u5FC5\u9808\u5927\u65BC\u7B49\u65BC${min}",
|
|
1787
|
+
th: "${displayName} \u0E15\u0E49\u0E2D\u0E07\u0E21\u0E32\u0E01\u0E01\u0E27\u0E48\u0E32\u0E2B\u0E23\u0E37\u0E2D\u0E40\u0E17\u0E48\u0E32\u0E01\u0E31\u0E1A ${min}",
|
|
1788
|
+
es: "${displayName} debe ser al menos ${min}"
|
|
1789
|
+
},
|
|
1790
|
+
max: {
|
|
1791
|
+
en: "${displayName} must be at most ${max}",
|
|
1792
|
+
ja: "${displayName}\u306F${max}\u4EE5\u4E0B\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
1793
|
+
vi: "${displayName} ph\u1EA3i nh\u1ECF h\u01A1n ho\u1EB7c b\u1EB1ng ${max}",
|
|
1794
|
+
ko: "${displayName}\uC740(\uB294) ${max} \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4",
|
|
1795
|
+
"zh-CN": "${displayName}\u5FC5\u987B\u5C0F\u4E8E\u7B49\u4E8E${max}",
|
|
1796
|
+
"zh-TW": "${displayName}\u5FC5\u9808\u5C0F\u65BC\u7B49\u65BC${max}",
|
|
1797
|
+
th: "${displayName} \u0E15\u0E49\u0E2D\u0E07\u0E19\u0E49\u0E2D\u0E22\u0E01\u0E27\u0E48\u0E32\u0E2B\u0E23\u0E37\u0E2D\u0E40\u0E17\u0E48\u0E32\u0E01\u0E31\u0E1A ${max}",
|
|
1798
|
+
es: "${displayName} debe ser como m\xE1ximo ${max}"
|
|
1799
|
+
},
|
|
1800
|
+
email: {
|
|
1801
|
+
en: "Please enter a valid email address",
|
|
1802
|
+
ja: "\u6709\u52B9\u306A\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
1803
|
+
vi: "Vui l\xF2ng nh\u1EADp \u0111\u1ECBa ch\u1EC9 email h\u1EE3p l\u1EC7",
|
|
1804
|
+
ko: "\uC720\uD6A8\uD55C \uC774\uBA54\uC77C \uC8FC\uC18C\uB97C \uC785\uB825\uD558\uC138\uC694",
|
|
1805
|
+
"zh-CN": "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u7535\u5B50\u90AE\u4EF6\u5730\u5740",
|
|
1806
|
+
"zh-TW": "\u8ACB\u8F38\u5165\u6709\u6548\u7684\u96FB\u5B50\u90F5\u4EF6\u5730\u5740",
|
|
1807
|
+
th: "\u0E01\u0E23\u0E38\u0E13\u0E32\u0E01\u0E23\u0E2D\u0E01\u0E2D\u0E35\u0E40\u0E21\u0E25\u0E17\u0E35\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07",
|
|
1808
|
+
es: "Por favor, introduce una direcci\xF3n de correo electr\xF3nico v\xE1lida"
|
|
1809
|
+
},
|
|
1810
|
+
url: {
|
|
1811
|
+
en: "Please enter a valid URL",
|
|
1812
|
+
ja: "\u6709\u52B9\u306AURL\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
1813
|
+
vi: "Vui l\xF2ng nh\u1EADp URL h\u1EE3p l\u1EC7",
|
|
1814
|
+
ko: "\uC720\uD6A8\uD55C URL\uC744 \uC785\uB825\uD558\uC138\uC694",
|
|
1815
|
+
"zh-CN": "\u8BF7\u8F93\u5165\u6709\u6548\u7684URL",
|
|
1816
|
+
"zh-TW": "\u8ACB\u8F38\u5165\u6709\u6548\u7684\u7DB2\u5740",
|
|
1817
|
+
th: "\u0E01\u0E23\u0E38\u0E13\u0E32\u0E01\u0E23\u0E2D\u0E01 URL \u0E17\u0E35\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07",
|
|
1818
|
+
es: "Por favor, introduce una URL v\xE1lida"
|
|
1819
|
+
},
|
|
1820
|
+
pattern: {
|
|
1821
|
+
en: "${displayName} format is invalid",
|
|
1822
|
+
ja: "${displayName}\u306E\u5F62\u5F0F\u304C\u6B63\u3057\u304F\u3042\u308A\u307E\u305B\u3093",
|
|
1823
|
+
vi: "${displayName} kh\xF4ng \u0111\xFAng \u0111\u1ECBnh d\u1EA1ng",
|
|
1824
|
+
ko: "${displayName} \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4",
|
|
1825
|
+
"zh-CN": "${displayName}\u683C\u5F0F\u4E0D\u6B63\u786E",
|
|
1826
|
+
"zh-TW": "${displayName}\u683C\u5F0F\u4E0D\u6B63\u78BA",
|
|
1827
|
+
th: "\u0E23\u0E39\u0E1B\u0E41\u0E1A\u0E1A${displayName}\u0E44\u0E21\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07",
|
|
1828
|
+
es: "El formato de ${displayName} no es v\xE1lido"
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
1561
1831
|
function generateCommonFile(options) {
|
|
1562
1832
|
const locales = options.localeConfig?.locales ?? ["ja", "en"];
|
|
1563
1833
|
const localeUnion = locales.map((l) => `'${l}'`).join(" | ");
|
|
@@ -1605,15 +1875,142 @@ export type DateString = string;
|
|
|
1605
1875
|
overwrite: true
|
|
1606
1876
|
};
|
|
1607
1877
|
}
|
|
1608
|
-
function
|
|
1878
|
+
function generateI18nFile(options) {
|
|
1879
|
+
const locales = options.localeConfig?.locales ?? ["ja", "en"];
|
|
1880
|
+
const defaultLocale = options.localeConfig?.defaultLocale ?? "ja";
|
|
1881
|
+
const fallbackLocale = options.localeConfig?.fallbackLocale ?? "en";
|
|
1882
|
+
const userMessages = options.localeConfig?.messages ?? {};
|
|
1883
|
+
const mergedMessages = {};
|
|
1884
|
+
for (const [key, defaultMsgs] of Object.entries(DEFAULT_VALIDATION_MESSAGES)) {
|
|
1885
|
+
mergedMessages[key] = {};
|
|
1886
|
+
for (const locale of locales) {
|
|
1887
|
+
if (defaultMsgs[locale]) {
|
|
1888
|
+
mergedMessages[key][locale] = defaultMsgs[locale];
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
for (const [key, userMsgs] of Object.entries(userMessages)) {
|
|
1893
|
+
if (userMsgs) {
|
|
1894
|
+
if (!mergedMessages[key]) {
|
|
1895
|
+
mergedMessages[key] = {};
|
|
1896
|
+
}
|
|
1897
|
+
for (const [locale, msg] of Object.entries(userMsgs)) {
|
|
1898
|
+
mergedMessages[key][locale] = msg;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
const messagesJson = JSON.stringify(mergedMessages, null, 2);
|
|
1903
|
+
const content = `${generateBaseHeader()}
|
|
1904
|
+
import type { LocaleMap } from './common.js';
|
|
1905
|
+
|
|
1906
|
+
/**
|
|
1907
|
+
* Default locale for this project.
|
|
1908
|
+
*/
|
|
1909
|
+
export const defaultLocale = '${defaultLocale}' as const;
|
|
1910
|
+
|
|
1911
|
+
/**
|
|
1912
|
+
* Fallback locale when requested locale is not found.
|
|
1913
|
+
*/
|
|
1914
|
+
export const fallbackLocale = '${fallbackLocale}' as const;
|
|
1915
|
+
|
|
1916
|
+
/**
|
|
1917
|
+
* Supported locales in this project.
|
|
1918
|
+
*/
|
|
1919
|
+
export const supportedLocales = ${JSON.stringify(locales)} as const;
|
|
1920
|
+
|
|
1921
|
+
/**
|
|
1922
|
+
* Validation messages for all supported locales.
|
|
1923
|
+
* Use getMessage(key, locale, params) to get formatted message.
|
|
1924
|
+
*/
|
|
1925
|
+
export const validationMessages = ${messagesJson} as const;
|
|
1926
|
+
|
|
1927
|
+
/**
|
|
1928
|
+
* Get validation message for a specific key and locale.
|
|
1929
|
+
* Supports template placeholders: \${displayName}, \${min}, \${max}, etc.
|
|
1930
|
+
*
|
|
1931
|
+
* @param key - Message key (e.g., 'required', 'minLength')
|
|
1932
|
+
* @param locale - Locale code (e.g., 'ja', 'en')
|
|
1933
|
+
* @param params - Template parameters to replace
|
|
1934
|
+
* @returns Formatted message string
|
|
1935
|
+
*
|
|
1936
|
+
* @example
|
|
1937
|
+
* getMessage('required', 'ja', { displayName: '\u6C0F\u540D' })
|
|
1938
|
+
* // => '\u6C0F\u540D\u306F\u5FC5\u9808\u3067\u3059'
|
|
1939
|
+
*/
|
|
1940
|
+
export function getMessage(
|
|
1941
|
+
key: string,
|
|
1942
|
+
locale: string,
|
|
1943
|
+
params: Record<string, string | number> = {}
|
|
1944
|
+
): string {
|
|
1945
|
+
const messages = validationMessages[key as keyof typeof validationMessages];
|
|
1946
|
+
if (!messages) return key;
|
|
1947
|
+
|
|
1948
|
+
let message = (messages as LocaleMap)[locale]
|
|
1949
|
+
?? (messages as LocaleMap)[fallbackLocale]
|
|
1950
|
+
?? (messages as LocaleMap)[defaultLocale]
|
|
1951
|
+
?? key;
|
|
1952
|
+
|
|
1953
|
+
// Replace template placeholders
|
|
1954
|
+
for (const [param, value] of Object.entries(params)) {
|
|
1955
|
+
message = message.replace(new RegExp(\`\\\\$\\{\${param}\\}\`, 'g'), String(value));
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
return message;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
/**
|
|
1962
|
+
* Get all validation messages for a specific locale.
|
|
1963
|
+
*
|
|
1964
|
+
* @param locale - Locale code
|
|
1965
|
+
* @returns Object with all messages for the locale
|
|
1966
|
+
*/
|
|
1967
|
+
export function getMessages(locale: string): Record<string, string> {
|
|
1968
|
+
const result: Record<string, string> = {};
|
|
1969
|
+
for (const [key, messages] of Object.entries(validationMessages)) {
|
|
1970
|
+
result[key] = (messages as LocaleMap)[locale]
|
|
1971
|
+
?? (messages as LocaleMap)[fallbackLocale]
|
|
1972
|
+
?? (messages as LocaleMap)[defaultLocale]
|
|
1973
|
+
?? key;
|
|
1974
|
+
}
|
|
1975
|
+
return result;
|
|
1976
|
+
}
|
|
1977
|
+
`;
|
|
1978
|
+
return {
|
|
1979
|
+
filePath: "i18n.ts",
|
|
1980
|
+
content,
|
|
1981
|
+
types: ["validationMessages", "getMessage", "getMessages"],
|
|
1982
|
+
overwrite: true
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
function generateIndexFile(schemas, enums, pluginEnums, typeAliases, options) {
|
|
1609
1986
|
const parts = [generateBaseHeader()];
|
|
1610
1987
|
parts.push(`// Common Types
|
|
1611
1988
|
`);
|
|
1612
1989
|
parts.push(`export type { LocaleMap, Locale, ValidationRule, DateTimeString, DateString } from './common.js';
|
|
1613
1990
|
|
|
1614
1991
|
`);
|
|
1615
|
-
|
|
1616
|
-
|
|
1992
|
+
parts.push(`// i18n (Internationalization)
|
|
1993
|
+
`);
|
|
1994
|
+
parts.push(`export {
|
|
1995
|
+
`);
|
|
1996
|
+
parts.push(` defaultLocale,
|
|
1997
|
+
`);
|
|
1998
|
+
parts.push(` fallbackLocale,
|
|
1999
|
+
`);
|
|
2000
|
+
parts.push(` supportedLocales,
|
|
2001
|
+
`);
|
|
2002
|
+
parts.push(` validationMessages,
|
|
2003
|
+
`);
|
|
2004
|
+
parts.push(` getMessage,
|
|
2005
|
+
`);
|
|
2006
|
+
parts.push(` getMessages,
|
|
2007
|
+
`);
|
|
2008
|
+
parts.push(`} from './i18n.js';
|
|
2009
|
+
|
|
2010
|
+
`);
|
|
2011
|
+
const enumPrefix = options.enumImportPrefix ?? "./enum";
|
|
2012
|
+
if (enums.length > 0) {
|
|
2013
|
+
parts.push(`// Schema Enums
|
|
1617
2014
|
`);
|
|
1618
2015
|
for (const enumDef of enums) {
|
|
1619
2016
|
parts.push(`export {
|
|
@@ -1628,9 +2025,35 @@ function generateIndexFile(schemas, enums, typeAliases, options) {
|
|
|
1628
2025
|
`);
|
|
1629
2026
|
parts.push(` get${enumDef.name}Extra,
|
|
1630
2027
|
`);
|
|
1631
|
-
parts.push(`} from '
|
|
2028
|
+
parts.push(`} from '${enumPrefix}/${enumDef.name}.js';
|
|
1632
2029
|
`);
|
|
1633
2030
|
}
|
|
2031
|
+
parts.push("\n");
|
|
2032
|
+
}
|
|
2033
|
+
if (pluginEnums.length > 0) {
|
|
2034
|
+
parts.push(`// Plugin Enums
|
|
2035
|
+
`);
|
|
2036
|
+
for (const enumDef of pluginEnums) {
|
|
2037
|
+
parts.push(`export {
|
|
2038
|
+
`);
|
|
2039
|
+
parts.push(` ${enumDef.name},
|
|
2040
|
+
`);
|
|
2041
|
+
parts.push(` ${enumDef.name}Values,
|
|
2042
|
+
`);
|
|
2043
|
+
parts.push(` is${enumDef.name},
|
|
2044
|
+
`);
|
|
2045
|
+
parts.push(` get${enumDef.name}Label,
|
|
2046
|
+
`);
|
|
2047
|
+
parts.push(` get${enumDef.name}Extra,
|
|
2048
|
+
`);
|
|
2049
|
+
parts.push(`} from '${enumPrefix}/plugin/${enumDef.name}.js';
|
|
2050
|
+
`);
|
|
2051
|
+
}
|
|
2052
|
+
parts.push("\n");
|
|
2053
|
+
}
|
|
2054
|
+
if (typeAliases.length > 0) {
|
|
2055
|
+
parts.push(`// Inline Enums (Type Aliases)
|
|
2056
|
+
`);
|
|
1634
2057
|
for (const alias of typeAliases) {
|
|
1635
2058
|
parts.push(`export {
|
|
1636
2059
|
`);
|
|
@@ -1644,13 +2067,13 @@ function generateIndexFile(schemas, enums, typeAliases, options) {
|
|
|
1644
2067
|
`);
|
|
1645
2068
|
parts.push(` get${alias.name}Extra,
|
|
1646
2069
|
`);
|
|
1647
|
-
parts.push(`} from '
|
|
2070
|
+
parts.push(`} from '${enumPrefix}/${alias.name}.js';
|
|
1648
2071
|
`);
|
|
1649
2072
|
}
|
|
1650
2073
|
parts.push("\n");
|
|
1651
2074
|
}
|
|
1652
2075
|
if (options.generateZodSchemas) {
|
|
1653
|
-
parts.push(`// Models (with Zod schemas and Create/Update types)
|
|
2076
|
+
parts.push(`// Models (with Zod schemas, i18n, and Create/Update types)
|
|
1654
2077
|
`);
|
|
1655
2078
|
for (const schema of Object.values(schemas)) {
|
|
1656
2079
|
if (schema.kind === "enum") continue;
|
|
@@ -1666,13 +2089,13 @@ function generateIndexFile(schemas, enums, typeAliases, options) {
|
|
|
1666
2089
|
`);
|
|
1667
2090
|
parts.push(` ${lowerName}UpdateSchema,
|
|
1668
2091
|
`);
|
|
1669
|
-
parts.push(` ${
|
|
2092
|
+
parts.push(` ${lowerName}I18n,
|
|
1670
2093
|
`);
|
|
1671
|
-
parts.push(` ${schema.name}
|
|
2094
|
+
parts.push(` get${schema.name}Label,
|
|
1672
2095
|
`);
|
|
1673
|
-
parts.push(` get${schema.name}
|
|
2096
|
+
parts.push(` get${schema.name}FieldLabel,
|
|
1674
2097
|
`);
|
|
1675
|
-
parts.push(` get${schema.name}
|
|
2098
|
+
parts.push(` get${schema.name}FieldPlaceholder,
|
|
1676
2099
|
`);
|
|
1677
2100
|
parts.push(`} from './${schema.name}.js';
|
|
1678
2101
|
`);
|
|
@@ -1718,9 +2141,47 @@ function generateIndexFile(schemas, enums, typeAliases, options) {
|
|
|
1718
2141
|
function generateTypeScript(schemas, options = {}) {
|
|
1719
2142
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
1720
2143
|
const files = [];
|
|
2144
|
+
const schemasByName = /* @__PURE__ */ new Map();
|
|
2145
|
+
for (const schema of Object.values(schemas)) {
|
|
2146
|
+
const paths = schemasByName.get(schema.name) ?? [];
|
|
2147
|
+
paths.push(schema.relativePath ?? schema.filePath);
|
|
2148
|
+
schemasByName.set(schema.name, paths);
|
|
2149
|
+
}
|
|
2150
|
+
const duplicateSchemas = Array.from(schemasByName.entries()).filter(([, paths]) => paths.length > 1);
|
|
2151
|
+
if (duplicateSchemas.length > 0) {
|
|
2152
|
+
const errors = duplicateSchemas.map(
|
|
2153
|
+
([name, paths]) => ` - "${name}" defined in: ${paths.join(", ")}`
|
|
2154
|
+
).join("\n");
|
|
2155
|
+
throw new Error(
|
|
2156
|
+
`Duplicate schema/enum names detected. Names must be globally unique:
|
|
2157
|
+
${errors}
|
|
2158
|
+
Hint: Rename to unique names like "Blog${duplicateSchemas[0][0]}" or "Shop${duplicateSchemas[0][0]}"`
|
|
2159
|
+
);
|
|
2160
|
+
}
|
|
2161
|
+
if (opts.pluginEnums && opts.pluginEnums.size > 0) {
|
|
2162
|
+
const conflicts = [];
|
|
2163
|
+
for (const schema of Object.values(schemas)) {
|
|
2164
|
+
if (schema.kind === "enum" && opts.pluginEnums.has(schema.name)) {
|
|
2165
|
+
conflicts.push(`"${schema.name}" (schema: ${schema.relativePath ?? schema.filePath}, plugin enum)`);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
if (conflicts.length > 0) {
|
|
2169
|
+
throw new Error(
|
|
2170
|
+
`Schema enum conflicts with plugin enum:
|
|
2171
|
+
- ${conflicts.join("\n - ")}
|
|
2172
|
+
Hint: Rename your schema enum to avoid conflict with plugin-provided enums`
|
|
2173
|
+
);
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
1721
2176
|
const enums = generateEnums(schemas, opts);
|
|
1722
2177
|
for (const enumDef of enums) {
|
|
1723
|
-
files.push(generateEnumFile(enumDef));
|
|
2178
|
+
files.push(generateEnumFile(enumDef, false));
|
|
2179
|
+
}
|
|
2180
|
+
if (opts.pluginEnums && opts.pluginEnums.size > 0) {
|
|
2181
|
+
const pluginEnums = generatePluginEnums(opts.pluginEnums, opts);
|
|
2182
|
+
for (const enumDef of pluginEnums) {
|
|
2183
|
+
files.push(generateEnumFile(enumDef, true));
|
|
2184
|
+
}
|
|
1724
2185
|
}
|
|
1725
2186
|
const typeAliases = extractInlineEnums(schemas, opts);
|
|
1726
2187
|
for (const alias of typeAliases) {
|
|
@@ -1741,11 +2202,19 @@ function generateTypeScript(schemas, options = {}) {
|
|
|
1741
2202
|
files.push(...rulesFiles);
|
|
1742
2203
|
}
|
|
1743
2204
|
files.push(generateCommonFile(opts));
|
|
1744
|
-
files.push(
|
|
2205
|
+
files.push(generateI18nFile(opts));
|
|
2206
|
+
const pluginEnumsList = opts.pluginEnums ? generatePluginEnums(opts.pluginEnums, opts) : [];
|
|
2207
|
+
files.push(generateIndexFile(schemas, enums, pluginEnumsList, typeAliases, opts));
|
|
1745
2208
|
return files;
|
|
1746
2209
|
}
|
|
1747
2210
|
|
|
1748
2211
|
// src/plugin.ts
|
|
2212
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
2213
|
+
var import_path = __toESM(require("path"), 1);
|
|
2214
|
+
var import_url = require("url");
|
|
2215
|
+
var import_meta = {};
|
|
2216
|
+
var __filename = (0, import_url.fileURLToPath)(import_meta.url);
|
|
2217
|
+
var __dirname = import_path.default.dirname(__filename);
|
|
1749
2218
|
var TYPESCRIPT_CONFIG_SCHEMA = {
|
|
1750
2219
|
fields: [
|
|
1751
2220
|
{
|
|
@@ -1763,15 +2232,38 @@ var TYPESCRIPT_CONFIG_SCHEMA = {
|
|
|
1763
2232
|
description: "Generate Zod schemas alongside TypeScript types for form validation",
|
|
1764
2233
|
default: true,
|
|
1765
2234
|
group: "output"
|
|
2235
|
+
},
|
|
2236
|
+
{
|
|
2237
|
+
key: "stubsPath",
|
|
2238
|
+
type: "path",
|
|
2239
|
+
label: "React Stubs Path",
|
|
2240
|
+
description: "Directory for React utility stubs (hooks, components). Leave empty to disable.",
|
|
2241
|
+
default: "omnify",
|
|
2242
|
+
group: "output"
|
|
1766
2243
|
}
|
|
1767
2244
|
]
|
|
1768
2245
|
};
|
|
1769
2246
|
function resolveOptions(options) {
|
|
1770
2247
|
return {
|
|
1771
2248
|
modelsPath: options?.modelsPath ?? "types/schemas",
|
|
1772
|
-
generateZodSchemas: options?.generateZodSchemas ?? true
|
|
2249
|
+
generateZodSchemas: options?.generateZodSchemas ?? true,
|
|
2250
|
+
stubsPath: options?.stubsPath ?? "omnify"
|
|
1773
2251
|
};
|
|
1774
2252
|
}
|
|
2253
|
+
var STUB_FILES = [
|
|
2254
|
+
{
|
|
2255
|
+
stub: "CompoundField.tsx.stub",
|
|
2256
|
+
output: "components/CompoundField.tsx"
|
|
2257
|
+
},
|
|
2258
|
+
{
|
|
2259
|
+
stub: "use-form-mutation.ts.stub",
|
|
2260
|
+
output: "hooks/use-form-mutation.ts"
|
|
2261
|
+
},
|
|
2262
|
+
{
|
|
2263
|
+
stub: "form-validation.ts.stub",
|
|
2264
|
+
output: "lib/form-validation.ts"
|
|
2265
|
+
}
|
|
2266
|
+
];
|
|
1775
2267
|
function typescriptPlugin(options) {
|
|
1776
2268
|
const resolved = resolveOptions(options);
|
|
1777
2269
|
return {
|
|
@@ -1786,17 +2278,74 @@ function typescriptPlugin(options) {
|
|
|
1786
2278
|
const files = generateTypeScript(ctx.schemas, {
|
|
1787
2279
|
generateZodSchemas: resolved.generateZodSchemas,
|
|
1788
2280
|
localeConfig: ctx.localeConfig,
|
|
1789
|
-
customTypes: ctx.customTypes
|
|
2281
|
+
customTypes: ctx.customTypes,
|
|
2282
|
+
pluginEnums: ctx.pluginEnums
|
|
1790
2283
|
});
|
|
1791
|
-
return files.map((file) =>
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
2284
|
+
return files.map((file) => {
|
|
2285
|
+
let outputPath;
|
|
2286
|
+
if (file.category === "enum") {
|
|
2287
|
+
const enumPath = resolved.modelsPath.replace(/\/schemas\/?$/, "/enum");
|
|
2288
|
+
outputPath = `${enumPath}/${file.filePath}`;
|
|
2289
|
+
} else {
|
|
2290
|
+
outputPath = `${resolved.modelsPath}/${file.filePath}`;
|
|
1798
2291
|
}
|
|
1799
|
-
|
|
2292
|
+
return {
|
|
2293
|
+
path: outputPath,
|
|
2294
|
+
content: file.content,
|
|
2295
|
+
type: "type",
|
|
2296
|
+
skipIfExists: !file.overwrite,
|
|
2297
|
+
// Invert: overwrite=true means skipIfExists=false
|
|
2298
|
+
metadata: {
|
|
2299
|
+
types: file.types
|
|
2300
|
+
}
|
|
2301
|
+
};
|
|
2302
|
+
});
|
|
2303
|
+
}
|
|
2304
|
+
},
|
|
2305
|
+
{
|
|
2306
|
+
name: "typescript-stubs",
|
|
2307
|
+
description: "Generate React utility stubs (hooks, components)",
|
|
2308
|
+
generate: async () => {
|
|
2309
|
+
if (resolved.stubsPath === false) {
|
|
2310
|
+
return [];
|
|
2311
|
+
}
|
|
2312
|
+
const outputs = [];
|
|
2313
|
+
const stubsDir = import_path.default.join(__dirname, "..", "stubs");
|
|
2314
|
+
for (const { stub, output } of STUB_FILES) {
|
|
2315
|
+
const stubPath = import_path.default.join(stubsDir, stub);
|
|
2316
|
+
if (import_fs.default.existsSync(stubPath)) {
|
|
2317
|
+
const content = import_fs.default.readFileSync(stubPath, "utf-8");
|
|
2318
|
+
outputs.push({
|
|
2319
|
+
path: `${resolved.stubsPath}/${output}`,
|
|
2320
|
+
content,
|
|
2321
|
+
type: "other",
|
|
2322
|
+
skipIfExists: true
|
|
2323
|
+
// Never overwrite - user can customize
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
outputs.push({
|
|
2328
|
+
path: `${resolved.stubsPath}/components/index.ts`,
|
|
2329
|
+
content: `export { CompoundField, type FieldConfig } from './CompoundField';
|
|
2330
|
+
`,
|
|
2331
|
+
type: "other",
|
|
2332
|
+
skipIfExists: true
|
|
2333
|
+
});
|
|
2334
|
+
outputs.push({
|
|
2335
|
+
path: `${resolved.stubsPath}/hooks/index.ts`,
|
|
2336
|
+
content: `export { useFormMutation } from './use-form-mutation';
|
|
2337
|
+
`,
|
|
2338
|
+
type: "other",
|
|
2339
|
+
skipIfExists: true
|
|
2340
|
+
});
|
|
2341
|
+
outputs.push({
|
|
2342
|
+
path: `${resolved.stubsPath}/lib/index.ts`,
|
|
2343
|
+
content: `export { zodRule, requiredRule } from './form-validation';
|
|
2344
|
+
`,
|
|
2345
|
+
type: "other",
|
|
2346
|
+
skipIfExists: true
|
|
2347
|
+
});
|
|
2348
|
+
return outputs;
|
|
1800
2349
|
}
|
|
1801
2350
|
}
|
|
1802
2351
|
]
|