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.
Files changed (102) hide show
  1. package/README.md +88 -342
  2. package/dist/config/index.d.ts +18 -0
  3. package/dist/config/index.js +13 -0
  4. package/dist/generator/mermaid-er/config/index.d.ts +4 -4
  5. package/dist/generator/mermaid-er/config/index.js +4 -10
  6. package/dist/generator/mermaid-er/core/extract-relations.d.ts +1 -1
  7. package/dist/generator/mermaid-er/core/extract-relations.js +3 -6
  8. package/dist/generator/mermaid-er/generator/{generate-er-content.d.ts → er-content.d.ts} +2 -3
  9. package/dist/generator/mermaid-er/generator/{generate-er-content.js → er-content.js} +3 -7
  10. package/dist/generator/mermaid-er/generator/index.d.ts +2 -0
  11. package/dist/generator/mermaid-er/generator/index.js +2 -0
  12. package/dist/generator/mermaid-er/generator/{generate-relation-line.d.ts → relation-line.d.ts} +2 -2
  13. package/dist/generator/mermaid-er/generator/{generate-relation-line.js → relation-line.js} +3 -6
  14. package/dist/generator/mermaid-er/index.d.ts +6 -5
  15. package/dist/generator/mermaid-er/index.js +15 -76
  16. package/dist/generator/mermaid-er/relationship/build-relation-line.js +3 -6
  17. package/dist/generator/mermaid-er/types.js +1 -0
  18. package/dist/generator/mermaid-er/validator/index.d.ts +4 -0
  19. package/dist/generator/mermaid-er/validator/index.js +4 -0
  20. package/dist/generator/mermaid-er/validator/is-relationship.d.ts +1 -1
  21. package/dist/generator/mermaid-er/validator/is-relationship.js +1 -4
  22. package/dist/generator/mermaid-er/validator/parse-relation-line.js +1 -4
  23. package/dist/generator/mermaid-er/validator/parse-table-info.d.ts +1 -7
  24. package/dist/generator/mermaid-er/validator/parse-table-info.js +69 -89
  25. package/dist/generator/mermaid-er/validator/remove-duplicate-relations.js +1 -4
  26. package/dist/generator/valibot/config/index.d.ts +2 -2
  27. package/dist/generator/valibot/config/index.js +6 -12
  28. package/dist/generator/valibot/core/extract-schema.d.ts +2 -5
  29. package/dist/generator/valibot/core/extract-schema.js +162 -81
  30. package/dist/generator/valibot/generator/infer-input.d.ts +5 -0
  31. package/dist/generator/valibot/generator/infer-input.js +8 -0
  32. package/dist/generator/valibot/generator/valibot-code.d.ts +8 -0
  33. package/dist/generator/valibot/generator/valibot-code.js +16 -0
  34. package/dist/generator/valibot/generator/valibot.d.ts +7 -0
  35. package/dist/generator/valibot/generator/valibot.js +11 -0
  36. package/dist/generator/valibot/index.d.ts +9 -3
  37. package/dist/generator/valibot/index.js +21 -76
  38. package/dist/generator/zod/config/index.d.ts +2 -2
  39. package/dist/generator/zod/config/index.js +6 -12
  40. package/dist/generator/zod/core/extract-schema.d.ts +1 -2
  41. package/dist/generator/zod/core/extract-schema.js +228 -81
  42. package/dist/generator/zod/generator/infer.d.ts +5 -0
  43. package/dist/generator/zod/generator/infer.js +8 -0
  44. package/dist/generator/zod/generator/{generate-zod-code.d.ts → zod-code.d.ts} +2 -3
  45. package/dist/generator/zod/generator/zod-code.js +18 -0
  46. package/dist/generator/zod/generator/{generate-zod-schema.d.ts → zod.d.ts} +2 -4
  47. package/dist/generator/zod/generator/zod.js +12 -0
  48. package/dist/generator/zod/index.d.ts +10 -3
  49. package/dist/generator/zod/index.js +29 -76
  50. package/dist/index.d.ts +3 -0
  51. package/dist/index.js +54 -0
  52. package/dist/shared/config/index.d.ts +13 -0
  53. package/dist/{common → shared}/config/index.js +1 -4
  54. package/dist/shared/format/index.d.ts +2 -0
  55. package/dist/shared/format/index.js +10 -0
  56. package/dist/shared/fs/index.d.ts +2 -0
  57. package/dist/shared/fs/index.js +10 -0
  58. package/dist/shared/fsp/index.d.ts +3 -0
  59. package/dist/shared/fsp/index.js +8 -0
  60. package/dist/shared/generator/field-definitions.d.ts +6 -0
  61. package/dist/shared/generator/field-definitions.js +12 -0
  62. package/dist/shared/types.js +1 -0
  63. package/dist/{common/text → shared/utils}/capitalize.js +1 -4
  64. package/dist/shared/utils/compose.d.ts +101 -0
  65. package/dist/shared/utils/compose.js +124 -0
  66. package/dist/shared/utils/file.d.ts +92 -0
  67. package/dist/shared/utils/file.js +177 -0
  68. package/dist/shared/utils/functional.d.ts +118 -0
  69. package/dist/shared/utils/functional.js +96 -0
  70. package/package.json +11 -10
  71. package/dist/common/config/index.d.ts +0 -13
  72. package/dist/common/format/index.d.ts +0 -1
  73. package/dist/common/format/index.js +0 -12
  74. package/dist/common/generator/generate-field-definitions.d.ts +0 -8
  75. package/dist/common/generator/generate-field-definitions.js +0 -16
  76. package/dist/common/helper/get-camel-case-schema-name-helper.d.ts +0 -7
  77. package/dist/common/helper/get-camel-case-schema-name-helper.js +0 -14
  78. package/dist/common/helper/get-pascal-case-schema-name-helper.d.ts +0 -8
  79. package/dist/common/helper/get-pascal-case-schema-name-helper.js +0 -15
  80. package/dist/common/helper/get-variable-name-helper.d.ts +0 -9
  81. package/dist/common/helper/get-variable-name-helper.js +0 -15
  82. package/dist/common/helper/get-variable-schema-name-helper.d.ts +0 -9
  83. package/dist/common/helper/get-variable-schema-name-helper.js +0 -17
  84. package/dist/common/text/decapitalize.d.ts +0 -17
  85. package/dist/common/text/decapitalize.js +0 -22
  86. package/dist/common/type/index.js +0 -2
  87. package/dist/generator/mermaid-er/type/index.js +0 -2
  88. package/dist/generator/mermaid-er/validator/is-relation.d.ts +0 -7
  89. package/dist/generator/mermaid-er/validator/is-relation.js +0 -40
  90. package/dist/generator/valibot/generator/generate-valibot-code.d.ts +0 -11
  91. package/dist/generator/valibot/generator/generate-valibot-code.js +0 -21
  92. package/dist/generator/valibot/generator/generate-valibot-infer-input.d.ts +0 -9
  93. package/dist/generator/valibot/generator/generate-valibot-infer-input.js +0 -16
  94. package/dist/generator/valibot/generator/generate-valibot-schema.d.ts +0 -9
  95. package/dist/generator/valibot/generator/generate-valibot-schema.js +0 -16
  96. package/dist/generator/zod/generator/generate-z-infer.d.ts +0 -11
  97. package/dist/generator/zod/generator/generate-z-infer.js +0 -18
  98. package/dist/generator/zod/generator/generate-zod-code.js +0 -21
  99. package/dist/generator/zod/generator/generate-zod-schema.js +0 -17
  100. /package/dist/generator/mermaid-er/{type/index.d.ts → types.d.ts} +0 -0
  101. /package/dist/{common/type/index.d.ts → shared/types.d.ts} +0 -0
  102. /package/dist/{common/text → shared/utils}/capitalize.d.ts +0 -0
@@ -1,96 +1,243 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extractSchemas = extractSchemas;
1
+ import { Node, Project } from 'ts-morph';
4
2
  /**
5
- * Check if line contains metadata
3
+ * Check if comment contains metadata
6
4
  */
7
- const isMetadataComment = (line) => {
8
- return line.includes('@z.') || line.includes('@v.') || line.includes('@relation.');
5
+ const isMetadataComment = (text) => {
6
+ return text.includes('@z.') || text.includes('@v.') || text.includes('@relation.');
9
7
  };
10
8
  /**
11
- * Check if line is a non-comment line
9
+ * Extract field comments that appear before a specific line position
12
10
  */
13
- const isNonCommentLine = (line) => {
14
- const trimmed = line.trim();
15
- return trimmed !== '' && !trimmed.startsWith('///');
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
- * Extract schemas from lines of code
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
- function extractSchemas(lines) {
24
- const process = (i, acc) => {
25
- if (i >= lines.length) {
26
- return acc;
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
- const line = lines[i];
29
- // extract schema
30
- const schemaMatch = line.match(/export const (\w+)\s*=/);
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
- // process comment
40
- if (line.trim().startsWith('///')) {
41
- // zod comment
42
- const zodComment = line.match(/\/\/\/\s*(@z\.(?:[^()]+|\([^)]*\))+)/);
43
- if (zodComment && acc.currentSchema) {
44
- // find next field definition line
45
- const remainingCandidates = lines.slice(i + 1);
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
- // if there is a field definition other than comment, use the pending comment as field information
76
- if (acc.currentSchema && acc.pendingDescription) {
77
- const fieldMatch = line.match(/^(\w+)\s*:/);
78
- if (fieldMatch) {
79
- const newField = {
80
- name: fieldMatch[1],
81
- definition: '',
82
- description: acc.pendingDescription,
83
- };
84
- acc.currentSchema.fields.push(newField);
85
- acc.pendingDescription = undefined;
86
- return process(i + 1, acc);
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 finalAcc.schemas;
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
  }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @param name
3
+ * @returns
4
+ */
5
+ export declare function infer(name: string): string;
@@ -0,0 +1,8 @@
1
+ import { capitalize } from '../../../shared/utils/capitalize.js';
2
+ /**
3
+ * @param name
4
+ * @returns
5
+ */
6
+ export function infer(name) {
7
+ return `export type ${capitalize(name)} = z.infer<typeof ${capitalize(name)}Schema>`;
8
+ }
@@ -1,5 +1,4 @@
1
- import type { Schema } from '../../../common/type';
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 generateZodCode(schema: Schema, config: Config): string;
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 '../../../common/type';
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 generateZodSchema(schema: Schema, config: Config): string;
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
- #!/usr/bin/env node
2
- import type { Config } from '../../common/config';
3
- export declare function main(dev?: boolean, config?: Config): Promise<boolean>;
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
- #!/usr/bin/env node
2
- "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.main = main;
8
- const node_fs_1 = require("node:fs");
9
- const node_path_1 = __importDefault(require("node:path"));
10
- const extract_schema_1 = require("./core/extract-schema");
11
- const generate_zod_code_1 = require("./generator/generate-zod-code");
12
- const format_1 = require("../../common/format");
13
- const config_1 = require("./config");
14
- const node_process_1 = require("node:process");
15
- const IMPORT_ZOD = 'import { z } from "zod"';
16
- async function main(dev = false, config = (0, config_1.getConfig)()) {
17
- // 1. argv ['**/bin/node', ''/workspaces/sizuku-test/packages/sizuku/dist/generator/zod/index.js',', 'db/schema.ts', '-o', 'zod/index.ts']
18
- if (config.output === undefined && !node_process_1.argv.includes('-o')) {
19
- console.error('Error: -o is not found');
20
- return false;
21
- }
22
- // 2. slice ['db/schema.ts', '-o', 'zod/index.ts']
23
- const args = process.argv.slice(2);
24
- // 3. input = args[0] = 'db/schema.ts'
25
- const input = config.input ?? args[0];
26
- config.input = input;
27
- // 4. output = 'zod/index.ts'
28
- const output = config.output ?? args[args.indexOf('-o') + 1];
29
- config.output = output;
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
  }
@@ -0,0 +1,3 @@
1
+ import type { Config } from './config/index.js';
2
+ import type { Result } from 'neverthrow';
3
+ export declare function main(config?: Config): Promise<Result<void, Error>>;
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,13 @@
1
+ export type Config = {
2
+ schema: {
3
+ name: 'PascalCase' | 'camelCase'
4
+ }
5
+ type: {
6
+ name: 'PascalCase' | 'camelCase'
7
+ export: boolean
8
+ }
9
+ input?: string
10
+ output?: string
11
+ comment?: boolean
12
+ }
13
+ export declare const DEFAULT_CONFIG: Config
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_CONFIG = void 0;
4
- exports.DEFAULT_CONFIG = {
1
+ export const DEFAULT_CONFIG = {
5
2
  schema: {
6
3
  name: 'PascalCase',
7
4
  },
@@ -0,0 +1,2 @@
1
+ import { ResultAsync } from 'neverthrow';
2
+ export declare function fmt(code: string): ResultAsync<string, Error>;
@@ -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,2 @@
1
+ import type { Result } from 'neverthrow';
2
+ export declare function readFileSync(path: string): Result<string, Error>;
@@ -0,0 +1,10 @@
1
+ import fs from 'node:fs';
2
+ import { ok, err } from 'neverthrow';
3
+ export function readFileSync(path) {
4
+ try {
5
+ return ok(fs.readFileSync(path, 'utf-8'));
6
+ }
7
+ catch (e) {
8
+ return err(new Error(String(e)));
9
+ }
10
+ }
@@ -0,0 +1,3 @@
1
+ import { ResultAsync } from 'neverthrow';
2
+ export declare function mkdir(path: string): ResultAsync<string | undefined, Error>;
3
+ export declare function writeFile(path: string, data: string): ResultAsync<void, Error>;
@@ -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
+ }