sizuku 0.0.6 → 0.1.0
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/README.md +88 -342
- package/dist/config/index.d.ts +18 -0
- package/dist/config/index.js +13 -0
- package/dist/generator/mermaid-er/config/index.d.ts +4 -4
- package/dist/generator/mermaid-er/config/index.js +4 -10
- package/dist/generator/mermaid-er/core/extract-relations.d.ts +1 -1
- package/dist/generator/mermaid-er/core/extract-relations.js +3 -6
- package/dist/generator/mermaid-er/generator/{generate-er-content.d.ts → er-content.d.ts} +2 -3
- package/dist/generator/mermaid-er/generator/{generate-er-content.js → er-content.js} +3 -7
- package/dist/generator/mermaid-er/generator/index.d.ts +2 -0
- package/dist/generator/mermaid-er/generator/index.js +2 -0
- package/dist/generator/mermaid-er/generator/{generate-relation-line.d.ts → relation-line.d.ts} +2 -2
- package/dist/generator/mermaid-er/generator/{generate-relation-line.js → relation-line.js} +3 -6
- package/dist/generator/mermaid-er/index.d.ts +6 -5
- package/dist/generator/mermaid-er/index.js +15 -76
- package/dist/generator/mermaid-er/relationship/build-relation-line.js +3 -6
- package/dist/generator/mermaid-er/types.js +1 -0
- package/dist/generator/mermaid-er/validator/index.d.ts +4 -0
- package/dist/generator/mermaid-er/validator/index.js +4 -0
- package/dist/generator/mermaid-er/validator/is-relationship.d.ts +1 -1
- package/dist/generator/mermaid-er/validator/is-relationship.js +1 -4
- package/dist/generator/mermaid-er/validator/parse-relation-line.js +1 -4
- package/dist/generator/mermaid-er/validator/parse-table-info.d.ts +1 -7
- package/dist/generator/mermaid-er/validator/parse-table-info.js +69 -89
- package/dist/generator/mermaid-er/validator/remove-duplicate-relations.js +1 -4
- package/dist/generator/valibot/config/index.d.ts +2 -2
- package/dist/generator/valibot/config/index.js +6 -12
- package/dist/generator/valibot/core/extract-schema.d.ts +2 -5
- package/dist/generator/valibot/core/extract-schema.js +162 -81
- package/dist/generator/valibot/generator/infer-input.d.ts +5 -0
- package/dist/generator/valibot/generator/infer-input.js +8 -0
- package/dist/generator/valibot/generator/valibot-code.d.ts +8 -0
- package/dist/generator/valibot/generator/valibot-code.js +16 -0
- package/dist/generator/valibot/generator/valibot.d.ts +7 -0
- package/dist/generator/valibot/generator/valibot.js +11 -0
- package/dist/generator/valibot/index.d.ts +9 -3
- package/dist/generator/valibot/index.js +21 -76
- package/dist/generator/zod/config/index.d.ts +2 -2
- package/dist/generator/zod/config/index.js +6 -12
- package/dist/generator/zod/core/extract-schema.d.ts +1 -2
- package/dist/generator/zod/core/extract-schema.js +228 -81
- package/dist/generator/zod/generator/infer.d.ts +5 -0
- package/dist/generator/zod/generator/infer.js +8 -0
- package/dist/generator/zod/generator/{generate-zod-code.d.ts → zod-code.d.ts} +2 -3
- package/dist/generator/zod/generator/zod-code.js +18 -0
- package/dist/generator/zod/generator/{generate-zod-schema.d.ts → zod.d.ts} +2 -4
- package/dist/generator/zod/generator/zod.js +12 -0
- package/dist/generator/zod/index.d.ts +10 -3
- package/dist/generator/zod/index.js +29 -76
- package/dist/index.d.ts +3 -0
- package/dist/index.js +54 -0
- package/dist/shared/config/index.d.ts +13 -0
- package/dist/{common → shared}/config/index.js +1 -4
- package/dist/shared/format/index.d.ts +2 -0
- package/dist/shared/format/index.js +10 -0
- package/dist/shared/fs/index.d.ts +2 -0
- package/dist/shared/fs/index.js +10 -0
- package/dist/shared/fsp/index.d.ts +3 -0
- package/dist/shared/fsp/index.js +8 -0
- package/dist/shared/generator/field-definitions.d.ts +6 -0
- package/dist/shared/generator/field-definitions.js +12 -0
- package/dist/shared/types.js +1 -0
- package/dist/{common/text → shared/utils}/capitalize.js +1 -4
- package/dist/shared/utils/compose.d.ts +101 -0
- package/dist/shared/utils/compose.js +124 -0
- package/dist/shared/utils/file.d.ts +92 -0
- package/dist/shared/utils/file.js +177 -0
- package/dist/shared/utils/functional.d.ts +118 -0
- package/dist/shared/utils/functional.js +96 -0
- package/package.json +11 -10
- package/dist/common/config/index.d.ts +0 -13
- package/dist/common/format/index.d.ts +0 -1
- package/dist/common/format/index.js +0 -12
- package/dist/common/generator/generate-field-definitions.d.ts +0 -8
- package/dist/common/generator/generate-field-definitions.js +0 -16
- package/dist/common/helper/get-camel-case-schema-name-helper.d.ts +0 -7
- package/dist/common/helper/get-camel-case-schema-name-helper.js +0 -14
- package/dist/common/helper/get-pascal-case-schema-name-helper.d.ts +0 -8
- package/dist/common/helper/get-pascal-case-schema-name-helper.js +0 -15
- package/dist/common/helper/get-variable-name-helper.d.ts +0 -9
- package/dist/common/helper/get-variable-name-helper.js +0 -15
- package/dist/common/helper/get-variable-schema-name-helper.d.ts +0 -9
- package/dist/common/helper/get-variable-schema-name-helper.js +0 -17
- package/dist/common/text/decapitalize.d.ts +0 -17
- package/dist/common/text/decapitalize.js +0 -22
- package/dist/common/type/index.js +0 -2
- package/dist/generator/mermaid-er/type/index.js +0 -2
- package/dist/generator/mermaid-er/validator/is-relation.d.ts +0 -7
- package/dist/generator/mermaid-er/validator/is-relation.js +0 -40
- package/dist/generator/valibot/generator/generate-valibot-code.d.ts +0 -11
- package/dist/generator/valibot/generator/generate-valibot-code.js +0 -21
- package/dist/generator/valibot/generator/generate-valibot-infer-input.d.ts +0 -9
- package/dist/generator/valibot/generator/generate-valibot-infer-input.js +0 -16
- package/dist/generator/valibot/generator/generate-valibot-schema.d.ts +0 -9
- package/dist/generator/valibot/generator/generate-valibot-schema.js +0 -16
- package/dist/generator/zod/generator/generate-z-infer.d.ts +0 -11
- package/dist/generator/zod/generator/generate-z-infer.js +0 -18
- package/dist/generator/zod/generator/generate-zod-code.js +0 -21
- package/dist/generator/zod/generator/generate-zod-schema.js +0 -17
- /package/dist/generator/mermaid-er/{type/index.d.ts → types.d.ts} +0 -0
- /package/dist/{common/type/index.d.ts → shared/types.d.ts} +0 -0
- /package/dist/{common/text → shared/utils}/capitalize.d.ts +0 -0
|
@@ -1,96 +1,243 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.extractSchemas = extractSchemas;
|
|
1
|
+
import { Node, Project } from 'ts-morph';
|
|
4
2
|
/**
|
|
5
|
-
* Check if
|
|
3
|
+
* Check if comment contains metadata
|
|
6
4
|
*/
|
|
7
|
-
const isMetadataComment = (
|
|
8
|
-
return
|
|
5
|
+
const isMetadataComment = (text) => {
|
|
6
|
+
return text.includes('@z.') || text.includes('@v.') || text.includes('@relation.');
|
|
9
7
|
};
|
|
10
8
|
/**
|
|
11
|
-
*
|
|
9
|
+
* Extract field comments that appear before a specific line position
|
|
12
10
|
*/
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
11
|
+
const extractFieldComments = (sourceText, fieldStartPos) => {
|
|
12
|
+
const beforeField = sourceText.substring(0, fieldStartPos);
|
|
13
|
+
const lines = beforeField.split('\n');
|
|
14
|
+
const reverseIndex = lines
|
|
15
|
+
.map((line, index) => ({ line: line.trim(), index }))
|
|
16
|
+
.reverse()
|
|
17
|
+
.reduce((acc, { line }) => {
|
|
18
|
+
if (acc.shouldStop)
|
|
19
|
+
return acc;
|
|
20
|
+
if (line.startsWith('///')) {
|
|
21
|
+
return {
|
|
22
|
+
commentLines: [line, ...acc.commentLines],
|
|
23
|
+
shouldStop: false,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (line === '') {
|
|
27
|
+
return acc;
|
|
28
|
+
}
|
|
29
|
+
return { commentLines: acc.commentLines, shouldStop: true };
|
|
30
|
+
}, { commentLines: [], shouldStop: false });
|
|
31
|
+
return reverseIndex.commentLines;
|
|
16
32
|
};
|
|
17
33
|
/**
|
|
18
|
-
*
|
|
19
|
-
* @function extractSchemas
|
|
20
|
-
* @param lines - Lines of code
|
|
21
|
-
* @returns Schemas
|
|
34
|
+
* Parse comment lines and extract Zod definition and description
|
|
22
35
|
*/
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
36
|
+
const parseFieldComments = (commentLines) => {
|
|
37
|
+
const cleanLines = commentLines
|
|
38
|
+
.map((line) => line.replace(/^\/\/\/\s*/, '').trim())
|
|
39
|
+
.filter((line) => line.length > 0);
|
|
40
|
+
const zodDefinition = cleanLines.find((line) => line.startsWith('@z.'))?.replace(/^@/, '') ?? '';
|
|
41
|
+
const descriptionLines = cleanLines.filter((line) => !isMetadataComment(line));
|
|
42
|
+
const description = descriptionLines.length > 0 ? descriptionLines.join(' ') : undefined;
|
|
43
|
+
return { zodDefinition, description };
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Extract field information from object property
|
|
47
|
+
*/
|
|
48
|
+
const extractFieldFromProperty = (property, sourceText) => {
|
|
49
|
+
if (!Node.isPropertyAssignment(property))
|
|
50
|
+
return null;
|
|
51
|
+
const fieldName = property.getName();
|
|
52
|
+
if (!fieldName)
|
|
53
|
+
return null;
|
|
54
|
+
const fieldStartPos = property.getStart();
|
|
55
|
+
const commentLines = extractFieldComments(sourceText, fieldStartPos);
|
|
56
|
+
const { zodDefinition, description } = parseFieldComments(commentLines);
|
|
57
|
+
return {
|
|
58
|
+
name: fieldName,
|
|
59
|
+
definition: zodDefinition,
|
|
60
|
+
description,
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Convert table name to Schema name (e.g., 'user' -> 'UserSchema', 'post' -> 'PostSchema')
|
|
65
|
+
*/
|
|
66
|
+
const toSchemaName = (tableName) => `${tableName.charAt(0).toUpperCase() + tableName.slice(1)}Schema`;
|
|
67
|
+
/**
|
|
68
|
+
* Extract relation field with type inference
|
|
69
|
+
*/
|
|
70
|
+
const extractRelationFieldFromProperty = (property, sourceText) => {
|
|
71
|
+
if (!Node.isPropertyAssignment(property))
|
|
72
|
+
return null;
|
|
73
|
+
const fieldName = property.getName();
|
|
74
|
+
if (!fieldName)
|
|
75
|
+
return null;
|
|
76
|
+
const initializer = property.getInitializer();
|
|
77
|
+
if (!Node.isCallExpression(initializer)) {
|
|
78
|
+
return {
|
|
79
|
+
name: fieldName,
|
|
80
|
+
definition: '',
|
|
81
|
+
description: undefined,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const expression = initializer.getExpression();
|
|
85
|
+
if (!Node.isIdentifier(expression)) {
|
|
86
|
+
return {
|
|
87
|
+
name: fieldName,
|
|
88
|
+
definition: '',
|
|
89
|
+
description: undefined,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const functionName = expression.getText();
|
|
93
|
+
const args = initializer.getArguments();
|
|
94
|
+
if (args.length === 0) {
|
|
95
|
+
return {
|
|
96
|
+
name: fieldName,
|
|
97
|
+
definition: '',
|
|
98
|
+
description: undefined,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const firstArg = args[0];
|
|
102
|
+
if (!Node.isIdentifier(firstArg)) {
|
|
103
|
+
return {
|
|
104
|
+
name: fieldName,
|
|
105
|
+
definition: '',
|
|
106
|
+
description: undefined,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const referencedTable = firstArg.getText();
|
|
110
|
+
const schemaName = toSchemaName(referencedTable);
|
|
111
|
+
const zodDefinition = functionName === 'many'
|
|
112
|
+
? `z.array(${schemaName})` // many(post) -> z.array(PostSchema)
|
|
113
|
+
: functionName === 'one'
|
|
114
|
+
? schemaName // one(user, {...}) -> UserSchema
|
|
115
|
+
: '';
|
|
116
|
+
const fieldStartPos = property.getStart();
|
|
117
|
+
const commentLines = extractFieldComments(sourceText, fieldStartPos);
|
|
118
|
+
const { description } = parseFieldComments(commentLines);
|
|
119
|
+
return {
|
|
120
|
+
name: fieldName,
|
|
121
|
+
definition: zodDefinition,
|
|
122
|
+
description,
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Extract object literal from any expression
|
|
127
|
+
*/
|
|
128
|
+
const extractObjectLiteralFromExpression = (expression) => {
|
|
129
|
+
// Direct object literal
|
|
130
|
+
if (Node.isObjectLiteralExpression(expression)) {
|
|
131
|
+
return expression;
|
|
132
|
+
}
|
|
133
|
+
if (Node.isParenthesizedExpression(expression)) {
|
|
134
|
+
const inner = expression.getExpression();
|
|
135
|
+
return Node.isObjectLiteralExpression(inner) ? inner : null;
|
|
136
|
+
}
|
|
137
|
+
if (Node.isArrowFunction(expression)) {
|
|
138
|
+
const body = expression.getBody();
|
|
139
|
+
if (Node.isObjectLiteralExpression(body)) {
|
|
140
|
+
return body;
|
|
27
141
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (schemaMatch) {
|
|
32
|
-
if (acc.currentSchema) {
|
|
33
|
-
acc.schemas.push(acc.currentSchema);
|
|
34
|
-
}
|
|
35
|
-
acc.currentSchema = { name: schemaMatch[1], fields: [] };
|
|
36
|
-
acc.pendingDescription = undefined;
|
|
37
|
-
return process(i + 1, acc);
|
|
142
|
+
if (Node.isParenthesizedExpression(body)) {
|
|
143
|
+
const inner = body.getExpression();
|
|
144
|
+
return Node.isObjectLiteralExpression(inner) ? inner : null;
|
|
38
145
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const foundRelative = remainingCandidates.findIndex(isNonCommentLine);
|
|
47
|
-
if (foundRelative !== -1) {
|
|
48
|
-
const j = i + 1 + foundRelative;
|
|
49
|
-
const candidate = lines[j].trim();
|
|
50
|
-
const fieldMatch = candidate.match(/^(\w+)\s*:/);
|
|
51
|
-
if (fieldMatch) {
|
|
52
|
-
const newField = {
|
|
53
|
-
name: fieldMatch[1],
|
|
54
|
-
definition: zodComment[1].replace('@', ''),
|
|
55
|
-
description: acc.pendingDescription,
|
|
56
|
-
};
|
|
57
|
-
acc.currentSchema.fields.push(newField);
|
|
58
|
-
acc.pendingDescription = undefined;
|
|
59
|
-
return process(i + 1, acc);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
146
|
+
if (Node.isBlock(body)) {
|
|
147
|
+
const returnStatement = body.getStatements().find((stmt) => Node.isReturnStatement(stmt));
|
|
148
|
+
if (returnStatement && Node.isReturnStatement(returnStatement)) {
|
|
149
|
+
const returnExpression = returnStatement.getExpression();
|
|
150
|
+
return returnExpression && Node.isObjectLiteralExpression(returnExpression)
|
|
151
|
+
? returnExpression
|
|
152
|
+
: null;
|
|
62
153
|
}
|
|
63
|
-
else {
|
|
64
|
-
// comments other than metadata are pending
|
|
65
|
-
if (!isMetadataComment(line)) {
|
|
66
|
-
const commentText = line.replace('///', '').trim();
|
|
67
|
-
acc.pendingDescription = acc.pendingDescription
|
|
68
|
-
? `${acc.pendingDescription} ${commentText}`
|
|
69
|
-
: commentText;
|
|
70
|
-
return process(i + 1, acc);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return process(i + 1, acc);
|
|
74
154
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Find object literal in call expression arguments
|
|
160
|
+
*/
|
|
161
|
+
const findObjectLiteralInArgs = (callExpr) => {
|
|
162
|
+
const args = callExpr.getArguments();
|
|
163
|
+
for (const arg of args) {
|
|
164
|
+
const objectLiteral = extractObjectLiteralFromExpression(arg);
|
|
165
|
+
if (objectLiteral) {
|
|
166
|
+
return objectLiteral;
|
|
88
167
|
}
|
|
89
|
-
return process(i + 1, acc);
|
|
90
|
-
};
|
|
91
|
-
const finalAcc = process(0, { currentSchema: null, pendingDescription: undefined, schemas: [] });
|
|
92
|
-
if (finalAcc.currentSchema) {
|
|
93
|
-
finalAcc.schemas.push(finalAcc.currentSchema);
|
|
94
168
|
}
|
|
95
|
-
return
|
|
169
|
+
return null;
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Determine if this is a relation-like function call
|
|
173
|
+
*/
|
|
174
|
+
const isRelationFunction = (callExpr) => {
|
|
175
|
+
const expression = callExpr.getExpression();
|
|
176
|
+
if (!Node.isIdentifier(expression))
|
|
177
|
+
return false;
|
|
178
|
+
const functionName = expression.getText();
|
|
179
|
+
return functionName === 'relations' || functionName.includes('relation');
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Extract fields from any call expression
|
|
183
|
+
*/
|
|
184
|
+
const extractFieldsFromCallExpression = (callExpr, sourceText) => {
|
|
185
|
+
const objectLiteral = findObjectLiteralInArgs(callExpr);
|
|
186
|
+
if (!objectLiteral)
|
|
187
|
+
return [];
|
|
188
|
+
const isRelation = isRelationFunction(callExpr);
|
|
189
|
+
return objectLiteral
|
|
190
|
+
.getProperties()
|
|
191
|
+
.map((prop) => isRelation
|
|
192
|
+
? extractRelationFieldFromProperty(prop, sourceText)
|
|
193
|
+
: extractFieldFromProperty(prop, sourceText))
|
|
194
|
+
.filter((field) => field !== null);
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Extract a single schema (variable declaration)
|
|
198
|
+
*/
|
|
199
|
+
const extractSchemaFromDeclaration = (declaration, sourceText) => {
|
|
200
|
+
if (!Node.isVariableDeclaration(declaration))
|
|
201
|
+
return null;
|
|
202
|
+
const name = declaration.getName();
|
|
203
|
+
if (!name)
|
|
204
|
+
return null;
|
|
205
|
+
const initializer = declaration.getInitializer();
|
|
206
|
+
if (Node.isCallExpression(initializer)) {
|
|
207
|
+
if (isRelationFunction(initializer))
|
|
208
|
+
return null;
|
|
209
|
+
const fields = extractFieldsFromCallExpression(initializer, sourceText);
|
|
210
|
+
return { name, fields };
|
|
211
|
+
}
|
|
212
|
+
if (Node.isObjectLiteralExpression(initializer)) {
|
|
213
|
+
const fields = initializer
|
|
214
|
+
.getProperties()
|
|
215
|
+
.map((prop) => extractFieldFromProperty(prop, sourceText))
|
|
216
|
+
.filter((field) => field !== null);
|
|
217
|
+
return { name, fields };
|
|
218
|
+
}
|
|
219
|
+
return { name, fields: [] };
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* Extract schemas from lines of code
|
|
223
|
+
* @param lines - Lines of code
|
|
224
|
+
* @returns Schemas
|
|
225
|
+
*/
|
|
226
|
+
export function extractSchemas(lines) {
|
|
227
|
+
const sourceCode = lines.join('\n');
|
|
228
|
+
const project = new Project({
|
|
229
|
+
useInMemoryFileSystem: true,
|
|
230
|
+
compilerOptions: {
|
|
231
|
+
allowJs: true,
|
|
232
|
+
skipLibCheck: true,
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
const sourceFile = project.createSourceFile('temp.ts', sourceCode);
|
|
236
|
+
const sourceText = sourceFile.getFullText();
|
|
237
|
+
return sourceFile
|
|
238
|
+
.getVariableStatements()
|
|
239
|
+
.filter((stmt) => stmt.hasExportKeyword())
|
|
240
|
+
.flatMap((stmt) => stmt.getDeclarations())
|
|
241
|
+
.map((decl) => extractSchemaFromDeclaration(decl, sourceText))
|
|
242
|
+
.filter((schema) => schema !== null);
|
|
96
243
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { Schema } from '../../../
|
|
2
|
-
import type { Config } from '../../../common/config';
|
|
1
|
+
import type { Schema } from '../../../shared/types.js';
|
|
3
2
|
/**
|
|
4
3
|
* Generates Zod code for a given schema and config.
|
|
5
4
|
*
|
|
@@ -8,4 +7,4 @@ import type { Config } from '../../../common/config';
|
|
|
8
7
|
* @param config - The configuration for the code generation.
|
|
9
8
|
* @returns The generated Zod code.
|
|
10
9
|
*/
|
|
11
|
-
export declare function
|
|
10
|
+
export declare function zodCode(schema: Schema, comment: boolean, type: boolean): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { infer } from './infer.js';
|
|
2
|
+
import { zod } from './zod.js';
|
|
3
|
+
/**
|
|
4
|
+
* Generates Zod code for a given schema and config.
|
|
5
|
+
*
|
|
6
|
+
* @function generateZodCode
|
|
7
|
+
* @param schema - The schema to generate code for.
|
|
8
|
+
* @param config - The configuration for the code generation.
|
|
9
|
+
* @returns The generated Zod code.
|
|
10
|
+
*/
|
|
11
|
+
export function zodCode(schema, comment, type) {
|
|
12
|
+
const zodSchema = zod(schema, comment);
|
|
13
|
+
if (type) {
|
|
14
|
+
const zInfer = infer(schema.name);
|
|
15
|
+
return `${zodSchema}\n\n${zInfer}\n`;
|
|
16
|
+
}
|
|
17
|
+
return `${zodSchema}\n`;
|
|
18
|
+
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import type { Schema } from '../../../
|
|
2
|
-
import type { Config } from '../../../common/config';
|
|
1
|
+
import type { Schema } from '../../../shared/types.js';
|
|
3
2
|
/**
|
|
4
3
|
* Generates a Zod schema for a given schema and config.
|
|
5
4
|
*
|
|
6
|
-
* @function generateZodSchema
|
|
7
5
|
* @param schema - The schema to generate code for.
|
|
8
6
|
* @param config - The configuration for the code generation.
|
|
9
7
|
* @returns The generated Zod schema.
|
|
10
8
|
*/
|
|
11
|
-
export declare function
|
|
9
|
+
export declare function zod(schema: Schema, comment: boolean): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { fieldDefinitions } from '../../../shared/generator/field-definitions.js';
|
|
2
|
+
import { capitalize } from '../../../shared/utils/capitalize.js';
|
|
3
|
+
/**
|
|
4
|
+
* Generates a Zod schema for a given schema and config.
|
|
5
|
+
*
|
|
6
|
+
* @param schema - The schema to generate code for.
|
|
7
|
+
* @param config - The configuration for the code generation.
|
|
8
|
+
* @returns The generated Zod schema.
|
|
9
|
+
*/
|
|
10
|
+
export function zod(schema, comment) {
|
|
11
|
+
return `export const ${capitalize(schema.name)}Schema = z.object({${fieldDefinitions(schema, comment)}})`;
|
|
12
|
+
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { Result } from 'neverthrow';
|
|
2
|
+
/**
|
|
3
|
+
* Generate Zod schema
|
|
4
|
+
* @param code - The code to generate Zod schema from
|
|
5
|
+
* @param output - The output file path
|
|
6
|
+
* @param comment - Whether to include comments in the generated code
|
|
7
|
+
* @param type - Whether to include type information in the generated code
|
|
8
|
+
* @param zod - The Zod version to use
|
|
9
|
+
*/
|
|
10
|
+
export declare function sizukuZod(code: string[], output: `${string}.ts`, comment?: boolean, type?: boolean, zod?: 'v4' | 'v4-mini' | '@hono/zod-openapi'): Promise<Result<void, Error>>;
|
|
@@ -1,77 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
// 5. read db/schema.ts
|
|
32
|
-
const content = (0, node_fs_1.readFileSync)(input, 'utf-8');
|
|
33
|
-
// 6. split lines
|
|
34
|
-
const lines = content.split('\n');
|
|
35
|
-
// 7. create output directory
|
|
36
|
-
const outputDir = node_path_1.default.dirname(output);
|
|
37
|
-
if (!(0, node_fs_1.existsSync)(outputDir)) {
|
|
38
|
-
(0, node_fs_1.mkdirSync)(outputDir, { recursive: true });
|
|
39
|
-
}
|
|
40
|
-
// 8. skip import section
|
|
41
|
-
const codeStart = lines.findIndex((line) => !line.trim().startsWith('import') && line.trim() !== '');
|
|
42
|
-
// 9. extract schemas
|
|
43
|
-
const schemas = (0, extract_schema_1.extractSchemas)(lines.slice(codeStart));
|
|
44
|
-
// 10. generate zod code
|
|
45
|
-
const generatedCode = [
|
|
46
|
-
IMPORT_ZOD,
|
|
47
|
-
'',
|
|
48
|
-
...schemas.map((schema) => (0, generate_zod_code_1.generateZodCode)(schema, config)),
|
|
49
|
-
].join('\n');
|
|
50
|
-
// 11. format code
|
|
51
|
-
const code = await (0, format_1.formatCode)(generatedCode);
|
|
52
|
-
// 12. write to output file
|
|
53
|
-
(0, node_fs_1.writeFileSync)(output, code);
|
|
54
|
-
console.log(`Generated Zod schema at: ${output}`);
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
catch (e) {
|
|
58
|
-
if (e instanceof Error) {
|
|
59
|
-
console.error(e.message);
|
|
60
|
-
if (dev) {
|
|
61
|
-
throw e;
|
|
62
|
-
}
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
if (dev) {
|
|
66
|
-
throw new Error('Unknown error occurred');
|
|
67
|
-
}
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (require.main === module) {
|
|
72
|
-
main().then((success) => {
|
|
73
|
-
if (!success) {
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { extractSchemas } from './core/extract-schema.js';
|
|
3
|
+
import { zodCode } from './generator/zod-code.js';
|
|
4
|
+
import { mkdir, writeFile } from '../../shared/fsp/index.js';
|
|
5
|
+
import { fmt } from '../../shared/format/index.js';
|
|
6
|
+
const ZODV4_IMPORT = `import { z } from 'zod/v4'`;
|
|
7
|
+
const ZODV4_MINI_IMPORT = `import { z } from 'zod/v4-mini'`;
|
|
8
|
+
const ZOD_OPENAPI_HONO_IMPORT = `import { z } from '@hono/zod-openapi'`;
|
|
9
|
+
/**
|
|
10
|
+
* Generate Zod schema
|
|
11
|
+
* @param code - The code to generate Zod schema from
|
|
12
|
+
* @param output - The output file path
|
|
13
|
+
* @param comment - Whether to include comments in the generated code
|
|
14
|
+
* @param type - Whether to include type information in the generated code
|
|
15
|
+
* @param zod - The Zod version to use
|
|
16
|
+
*/
|
|
17
|
+
export async function sizukuZod(code, output, comment, type, zod) {
|
|
18
|
+
const zodGeneratedCode = [
|
|
19
|
+
zod === 'v4-mini'
|
|
20
|
+
? ZODV4_MINI_IMPORT
|
|
21
|
+
: zod === '@hono/zod-openapi'
|
|
22
|
+
? ZOD_OPENAPI_HONO_IMPORT
|
|
23
|
+
: ZODV4_IMPORT,
|
|
24
|
+
'',
|
|
25
|
+
...extractSchemas(code).map((schema) => zodCode(schema, comment ?? false, type ?? false)),
|
|
26
|
+
].join('\n');
|
|
27
|
+
return await mkdir(path.dirname(output))
|
|
28
|
+
.andThen(() => fmt(zodGeneratedCode))
|
|
29
|
+
.andThen((formatted) => writeFile(output, formatted));
|
|
77
30
|
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { getConfig } from './config/index.js';
|
|
2
|
+
import { sizukuZod } from './generator/zod/index.js';
|
|
3
|
+
import { sizukuValibot } from './generator/valibot/index.js';
|
|
4
|
+
import { sizukuMermaidER } from './generator/mermaid-er/index.js';
|
|
5
|
+
import { readFileSync } from './shared/fs/index.js';
|
|
6
|
+
import { ok, err, ResultAsync } from 'neverthrow';
|
|
7
|
+
export async function main(config = getConfig()) {
|
|
8
|
+
return ResultAsync.fromPromise(Promise.resolve(), () => new Error('init'))
|
|
9
|
+
.andThen(() => {
|
|
10
|
+
if (!config.input) {
|
|
11
|
+
return err(new Error('input is not found'));
|
|
12
|
+
}
|
|
13
|
+
const contentResult = readFileSync(config.input);
|
|
14
|
+
if (contentResult.isErr()) {
|
|
15
|
+
return err(contentResult.error);
|
|
16
|
+
}
|
|
17
|
+
const content = contentResult.value;
|
|
18
|
+
const lines = content.split('\n');
|
|
19
|
+
const codeStart = lines.findIndex((line) => !line.trim().startsWith('import') && line.trim() !== '');
|
|
20
|
+
return ok(lines.slice(codeStart));
|
|
21
|
+
})
|
|
22
|
+
.andThen((code) => {
|
|
23
|
+
if (config.zod?.output) {
|
|
24
|
+
return ResultAsync.fromPromise(sizukuZod(code, config.zod.output, config.zod.comment, config.zod.type, config.zod.zod), (e) => (e instanceof Error ? e : new Error(String(e)))).map(() => {
|
|
25
|
+
console.log(`Generated Zod schema at: ${config.zod?.output}`);
|
|
26
|
+
return code;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return ok(code);
|
|
30
|
+
})
|
|
31
|
+
.andThen((code) => {
|
|
32
|
+
if (config.valibot?.output) {
|
|
33
|
+
return ResultAsync.fromPromise(sizukuValibot(code, config.valibot.output, config.valibot.comment, config.valibot.type), (e) => (e instanceof Error ? e : new Error(String(e)))).map(() => {
|
|
34
|
+
console.log(`Generated Valibot schema at: ${config.valibot?.output}`);
|
|
35
|
+
return code;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return ok(code);
|
|
39
|
+
})
|
|
40
|
+
.andThen((code) => {
|
|
41
|
+
if (config.mermaid?.output) {
|
|
42
|
+
return ResultAsync.fromPromise(sizukuMermaidER(code, config.mermaid.output), (e) => e instanceof Error ? e : new Error(String(e))).map(() => {
|
|
43
|
+
console.log(`Generated Mermaid ER at: ${config.mermaid?.output}`);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return ok(undefined);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
main().then((result) => {
|
|
50
|
+
if (!result.isOk()) {
|
|
51
|
+
console.error(result.error);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { format } from 'prettier';
|
|
2
|
+
import { ResultAsync } from 'neverthrow';
|
|
3
|
+
export function fmt(code) {
|
|
4
|
+
return ResultAsync.fromPromise(format(code, {
|
|
5
|
+
parser: 'typescript',
|
|
6
|
+
printWidth: 100,
|
|
7
|
+
singleQuote: true,
|
|
8
|
+
semi: false,
|
|
9
|
+
}), (e) => new Error(String(e)));
|
|
10
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import fsp from 'node:fs/promises';
|
|
2
|
+
import { ResultAsync } from 'neverthrow';
|
|
3
|
+
export function mkdir(path) {
|
|
4
|
+
return ResultAsync.fromPromise(fsp.mkdir(path, { recursive: true }), (e) => new Error(String(e)));
|
|
5
|
+
}
|
|
6
|
+
export function writeFile(path, data) {
|
|
7
|
+
return ResultAsync.fromPromise(fsp.writeFile(path, data), (e) => new Error(String(e)));
|
|
8
|
+
}
|