@omnifyjp/omnify 3.13.0 → 3.13.1
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnifyjp/omnify",
|
|
3
|
-
"version": "3.13.
|
|
3
|
+
"version": "3.13.1",
|
|
4
4
|
"description": "Schema-driven code generation for Laravel, TypeScript, and SQL",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"zod": "^3.24.0"
|
|
37
37
|
},
|
|
38
38
|
"optionalDependencies": {
|
|
39
|
-
"@omnifyjp/omnify-darwin-arm64": "3.13.
|
|
40
|
-
"@omnifyjp/omnify-darwin-x64": "3.13.
|
|
41
|
-
"@omnifyjp/omnify-linux-x64": "3.13.
|
|
42
|
-
"@omnifyjp/omnify-linux-arm64": "3.13.
|
|
43
|
-
"@omnifyjp/omnify-win32-x64": "3.13.
|
|
39
|
+
"@omnifyjp/omnify-darwin-arm64": "3.13.1",
|
|
40
|
+
"@omnifyjp/omnify-darwin-x64": "3.13.1",
|
|
41
|
+
"@omnifyjp/omnify-linux-x64": "3.13.1",
|
|
42
|
+
"@omnifyjp/omnify-linux-arm64": "3.13.1",
|
|
43
|
+
"@omnifyjp/omnify-win32-x64": "3.13.1"
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -38,6 +38,15 @@ export interface FormField {
|
|
|
38
38
|
nullable: boolean;
|
|
39
39
|
/** Default value for `empty<Model>CreateForm()`, as a JS literal string. */
|
|
40
40
|
defaultLiteral: string;
|
|
41
|
+
/**
|
|
42
|
+
* When true, the field is intentionally omitted from the
|
|
43
|
+
* `empty<Model>CreateForm()` factory body. Used for optional enum
|
|
44
|
+
* reference fields where no sensible default exists — the interface
|
|
45
|
+
* marks the field `?:` so omitting the key is valid, and this is
|
|
46
|
+
* safer than emitting `undefined` under `exactOptionalPropertyTypes`.
|
|
47
|
+
* Issue omnify-jp/omnify-go#56.
|
|
48
|
+
*/
|
|
49
|
+
omitFromEmpty?: boolean;
|
|
41
50
|
}
|
|
42
51
|
/** Resolved per-schema form metadata. */
|
|
43
52
|
export interface SchemaFormShape {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { toSnakeCase } from './interface-generator.js';
|
|
24
24
|
import { classifyFieldType } from './metadata-generator.js';
|
|
25
|
+
import { toEnumMemberName } from './enum-generator.js';
|
|
25
26
|
// ---------------------------------------------------------------------------
|
|
26
27
|
// Build the IR
|
|
27
28
|
// ---------------------------------------------------------------------------
|
|
@@ -71,26 +72,86 @@ function tsTypeForField(metaType, prop) {
|
|
|
71
72
|
return 'string';
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Resolve an enum's value list either from a plugin enum bank
|
|
77
|
+
* (customTypes.enums) or from a schema-defined enum (`kind: enum` schemas).
|
|
78
|
+
* Returns undefined if the enum reference can't be resolved at codegen time.
|
|
79
|
+
*/
|
|
80
|
+
function resolveEnumValueList(prop, allSchemas, pluginEnums) {
|
|
81
|
+
if (typeof prop.enum === 'string') {
|
|
82
|
+
// Plugin enum first (customTypes.enums), then schema enum.
|
|
83
|
+
const fromPlugin = pluginEnums[prop.enum];
|
|
84
|
+
if (fromPlugin)
|
|
85
|
+
return fromPlugin;
|
|
86
|
+
const schemaEnum = allSchemas[prop.enum];
|
|
87
|
+
if (schemaEnum?.values && schemaEnum.values.length > 0) {
|
|
88
|
+
return schemaEnum.values.map((v) => v.value);
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
if (Array.isArray(prop.enum)) {
|
|
93
|
+
return prop.enum.map((v) => typeof v === 'string' ? v : v.value);
|
|
94
|
+
}
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
function defaultLiteralForField(metaType, prop, required, allSchemas, pluginEnums) {
|
|
98
|
+
// -----------------------------------------------------------------
|
|
99
|
+
// Enum fields — reference the enum member, not a string literal.
|
|
100
|
+
//
|
|
101
|
+
// `status: 'free'` is not assignable to `TableStatus` under strict
|
|
102
|
+
// TypeScript because enum types are nominal. We must emit
|
|
103
|
+
// `TableStatus.Free` instead. For inline enums (array of strings) the
|
|
104
|
+
// TS type is a literal union, so string literals are fine — that case
|
|
105
|
+
// falls through to the JSON.stringify path at the bottom.
|
|
106
|
+
//
|
|
107
|
+
// Issue omnify-jp/omnify-go#56.
|
|
108
|
+
// -----------------------------------------------------------------
|
|
109
|
+
if (metaType === 'enum' && typeof prop.enum === 'string') {
|
|
110
|
+
const enumName = prop.enum;
|
|
111
|
+
// Optional / nullable enum refs: omit from the factory body entirely.
|
|
112
|
+
// The form-state interface already marks the field `?:`, so skipping
|
|
113
|
+
// the key is a valid object literal under strict TS regardless of
|
|
114
|
+
// `exactOptionalPropertyTypes`.
|
|
115
|
+
if (!required) {
|
|
116
|
+
return { literal: '', omit: true };
|
|
117
|
+
}
|
|
118
|
+
// Pick the default value: honor YAML `default` first, else first
|
|
119
|
+
// declared enum member.
|
|
120
|
+
let rawValue;
|
|
121
|
+
if (prop.default !== undefined && prop.default !== null) {
|
|
122
|
+
rawValue = String(prop.default);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const values = resolveEnumValueList(prop, allSchemas, pluginEnums);
|
|
126
|
+
if (values && values.length > 0) {
|
|
127
|
+
rawValue = values[0];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (rawValue === undefined) {
|
|
131
|
+
// Can't resolve the enum values at codegen time (unlikely, but
|
|
132
|
+
// keeps the generator total). Fall back to omitting — the dev
|
|
133
|
+
// will get a TS error at form init and can fix it manually.
|
|
134
|
+
return { literal: '', omit: true };
|
|
135
|
+
}
|
|
136
|
+
return { literal: `${enumName}.${toEnumMemberName(rawValue)}` };
|
|
137
|
+
}
|
|
138
|
+
// Honor an explicit YAML default when present (non-enum path).
|
|
76
139
|
if (prop.default !== undefined && prop.default !== null) {
|
|
77
|
-
return JSON.stringify(prop.default);
|
|
140
|
+
return { literal: JSON.stringify(prop.default) };
|
|
78
141
|
}
|
|
79
|
-
//
|
|
80
|
-
//
|
|
142
|
+
// Inline enum (array form) — the form-state tsType is a literal union,
|
|
143
|
+
// so a string literal is directly assignable. Use the first value.
|
|
81
144
|
if (metaType === 'enum') {
|
|
82
145
|
if (Array.isArray(prop.enum) && prop.enum.length > 0) {
|
|
83
146
|
const first = prop.enum[0];
|
|
84
147
|
const val = typeof first === 'string' ? first : first.value;
|
|
85
|
-
return JSON.stringify(val);
|
|
148
|
+
return { literal: JSON.stringify(val) };
|
|
86
149
|
}
|
|
87
|
-
|
|
88
|
-
// string and let the dev select on first interaction.
|
|
89
|
-
return "''";
|
|
150
|
+
return { literal: "''" };
|
|
90
151
|
}
|
|
91
152
|
if (metaType === 'boolean')
|
|
92
|
-
return 'false';
|
|
93
|
-
return PRIMITIVE_TS_DEFAULTS[metaType];
|
|
153
|
+
return { literal: 'false' };
|
|
154
|
+
return { literal: PRIMITIVE_TS_DEFAULTS[metaType] };
|
|
94
155
|
}
|
|
95
156
|
/** Build the form shape IR from a schema. */
|
|
96
157
|
export function buildFormShape(schema, allSchemas, options) {
|
|
@@ -129,6 +190,7 @@ export function buildFormShape(schema, allSchemas, options) {
|
|
|
129
190
|
: prop.rules?.required === true
|
|
130
191
|
? true
|
|
131
192
|
: !(prop.nullable ?? false);
|
|
193
|
+
const { literal, omit } = defaultLiteralForField(metaType, prop, required, allSchemas, options.customTypes.enums);
|
|
132
194
|
fields.push({
|
|
133
195
|
fieldName,
|
|
134
196
|
tsType: tsTypeForField(metaType, prop),
|
|
@@ -136,7 +198,8 @@ export function buildFormShape(schema, allSchemas, options) {
|
|
|
136
198
|
translatable: prop.translatable === true,
|
|
137
199
|
required,
|
|
138
200
|
nullable: prop.nullable ?? false,
|
|
139
|
-
defaultLiteral:
|
|
201
|
+
defaultLiteral: literal,
|
|
202
|
+
omitFromEmpty: omit,
|
|
140
203
|
});
|
|
141
204
|
}
|
|
142
205
|
return { modelName: schema.name, fields };
|
|
@@ -184,6 +247,11 @@ export function formatPayloadBuilderSection(shape, defaultLocale) {
|
|
|
184
247
|
parts.push(`export function empty${modelName}CreateForm(): ${modelName}CreateFormState {\n`);
|
|
185
248
|
parts.push(` return {\n`);
|
|
186
249
|
for (const f of fields) {
|
|
250
|
+
// Skip fields marked for omission (e.g. optional enum references
|
|
251
|
+
// with no sensible default — the interface marks them `?:`).
|
|
252
|
+
// Issue omnify-jp/omnify-go#56.
|
|
253
|
+
if (f.omitFromEmpty)
|
|
254
|
+
continue;
|
|
187
255
|
if (f.translatable) {
|
|
188
256
|
parts.push(` ${f.fieldName}: emptyLocaleMap(),\n`);
|
|
189
257
|
}
|