sizuku 0.1.0 → 0.2.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 (160) hide show
  1. package/README.md +17 -11
  2. package/dist/cli/main.d.ts +10 -0
  3. package/dist/cli/main.js +61 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +51 -0
  6. package/dist/config/index.d.ts +1 -1
  7. package/dist/config/loader.d.ts +19 -0
  8. package/dist/config/loader.js +30 -0
  9. package/dist/generator/engine.d.ts +3 -0
  10. package/dist/generator/engine.js +63 -0
  11. package/dist/generator/mermaid-er/core.d.ts +6 -0
  12. package/dist/generator/mermaid-er/core.js +54 -0
  13. package/dist/generator/mermaid-er/generator/er-content.d.ts +14 -2
  14. package/dist/generator/mermaid-er/generator/index.d.ts +1 -1
  15. package/dist/generator/mermaid-er/generator/index.js +1 -1
  16. package/dist/generator/mermaid-er/generator/relation-line.d.ts +7 -2
  17. package/dist/generator/mermaid-er/generator/relation-line.js +1 -1
  18. package/dist/generator/mermaid-er/generator.d.ts +3 -0
  19. package/dist/generator/mermaid-er/generator.js +14 -0
  20. package/dist/generator/mermaid-er/index.d.ts +7 -1
  21. package/dist/generator/mermaid-er/index.js +21 -4
  22. package/dist/generator/mermaid-er/validator/index.d.ts +8 -4
  23. package/dist/generator/mermaid-er/validator/index.js +71 -4
  24. package/dist/generator/mermaid-er/validator/parse-table-info.js +1 -1
  25. package/dist/generator/valibot/core.d.ts +5 -0
  26. package/dist/generator/valibot/core.js +39 -0
  27. package/dist/generator/valibot/generator/infer-input.js +1 -1
  28. package/dist/generator/valibot/generator/relation-valibot-code.d.ts +13 -0
  29. package/dist/generator/valibot/generator/relation-valibot-code.js +19 -0
  30. package/dist/generator/valibot/generator/valibot-code.d.ts +9 -2
  31. package/dist/generator/valibot/generator/valibot-code.js +1 -1
  32. package/dist/generator/valibot/generator/valibot.d.ts +9 -2
  33. package/dist/generator/valibot/generator/valibot.js +7 -3
  34. package/dist/generator/valibot/generator.d.ts +3 -0
  35. package/dist/generator/valibot/generator.js +14 -0
  36. package/dist/generator/valibot/index.d.ts +7 -2
  37. package/dist/generator/valibot/index.js +36 -8
  38. package/dist/generator/zod/core.d.ts +5 -0
  39. package/dist/generator/zod/core.js +39 -0
  40. package/dist/generator/zod/generator/infer.js +2 -2
  41. package/dist/generator/zod/generator/relation-zod-code.d.ts +13 -0
  42. package/dist/generator/zod/generator/relation-zod-code.js +19 -0
  43. package/dist/generator/zod/generator/zod-code.d.ts +9 -2
  44. package/dist/generator/zod/generator/zod-code.js +1 -1
  45. package/dist/generator/zod/generator/zod.d.ts +9 -2
  46. package/dist/generator/zod/generator/zod.js +7 -3
  47. package/dist/generator/zod/generator.d.ts +3 -0
  48. package/dist/generator/zod/generator.js +14 -0
  49. package/dist/generator/zod/index.d.ts +7 -2
  50. package/dist/generator/zod/index.js +40 -16
  51. package/dist/index.d.ts +7 -2
  52. package/dist/index.js +60 -43
  53. package/dist/shared/format/index.d.ts +15 -2
  54. package/dist/shared/format/index.js +22 -8
  55. package/dist/shared/fs/index.d.ts +7 -2
  56. package/dist/shared/fs/index.js +9 -3
  57. package/dist/shared/fsp/index.d.ts +27 -3
  58. package/dist/shared/fsp/index.js +35 -5
  59. package/dist/shared/generator/field-definitions.d.ts +8 -2
  60. package/dist/shared/helper/ast-parser.d.ts +3 -0
  61. package/dist/shared/helper/ast-parser.js +202 -0
  62. package/dist/shared/helper/build-schema-extractor.d.ts +25 -0
  63. package/dist/shared/helper/build-schema-extractor.js +33 -0
  64. package/dist/shared/helper/create-extract-field-from-property.d.ts +15 -0
  65. package/dist/shared/helper/create-extract-field-from-property.js +20 -0
  66. package/dist/shared/helper/create-extract-fields-from-call-expression.d.ts +14 -0
  67. package/dist/shared/helper/create-extract-fields-from-call-expression.js +14 -0
  68. package/dist/shared/helper/create-extract-relation-field-from-property.d.ts +12 -0
  69. package/dist/shared/helper/create-extract-relation-field-from-property.js +27 -0
  70. package/dist/shared/helper/extract-schemas.d.ts +133 -0
  71. package/dist/shared/helper/extract-schemas.js +445 -0
  72. package/dist/shared/helper/file-writer.d.ts +3 -0
  73. package/dist/shared/helper/file-writer.js +25 -0
  74. package/dist/shared/helper/find-object-literal-expression.d.ts +12 -0
  75. package/dist/shared/helper/find-object-literal-expression.js +31 -0
  76. package/dist/shared/helper/find-object-literalIn-args.d.ts +2 -0
  77. package/dist/shared/helper/find-object-literalIn-args.js +8 -0
  78. package/dist/shared/helper/is-relation-function.d.ts +10 -0
  79. package/dist/shared/helper/is-relation-function.js +16 -0
  80. package/dist/shared/utils/index.d.ts +20 -0
  81. package/dist/shared/utils/index.js +48 -0
  82. package/dist/shared/utils/string-utils.d.ts +8 -0
  83. package/dist/shared/utils/string-utils.js +28 -0
  84. package/dist/shared/utils/types.d.ts +32 -0
  85. package/dist/shared/utils/types.js +2 -0
  86. package/dist/shared/utils/validation-utils.d.ts +8 -0
  87. package/dist/shared/utils/validation-utils.js +25 -0
  88. package/dist/src/config/index.d.ts +18 -0
  89. package/dist/src/config/index.js +13 -0
  90. package/dist/src/generator/mermaid-er/core/extract-relations.d.ts +8 -0
  91. package/dist/src/generator/mermaid-er/core/extract-relations.js +12 -0
  92. package/dist/src/generator/mermaid-er/generator/er-content.d.ts +9 -0
  93. package/dist/src/generator/mermaid-er/generator/er-content.js +25 -0
  94. package/dist/src/generator/mermaid-er/generator/index.d.ts +2 -0
  95. package/dist/src/generator/mermaid-er/generator/index.js +2 -0
  96. package/dist/src/generator/mermaid-er/generator/relation-line.d.ts +8 -0
  97. package/dist/src/generator/mermaid-er/generator/relation-line.js +14 -0
  98. package/dist/src/generator/mermaid-er/index.d.ts +6 -0
  99. package/dist/src/generator/mermaid-er/index.js +16 -0
  100. package/dist/src/generator/mermaid-er/relationship/build-relation-line.d.ts +15 -0
  101. package/dist/src/generator/mermaid-er/relationship/build-relation-line.js +35 -0
  102. package/dist/src/generator/mermaid-er/types.d.ts +21 -0
  103. package/dist/src/generator/mermaid-er/types.js +1 -0
  104. package/dist/src/generator/mermaid-er/validator/index.d.ts +4 -0
  105. package/dist/src/generator/mermaid-er/validator/index.js +4 -0
  106. package/dist/src/generator/mermaid-er/validator/is-relationship.d.ts +8 -0
  107. package/dist/src/generator/mermaid-er/validator/is-relationship.js +9 -0
  108. package/dist/src/generator/mermaid-er/validator/parse-relation-line.d.ts +13 -0
  109. package/dist/src/generator/mermaid-er/validator/parse-relation-line.js +21 -0
  110. package/dist/src/generator/mermaid-er/validator/parse-table-info.d.ts +8 -0
  111. package/dist/src/generator/mermaid-er/validator/parse-table-info.js +91 -0
  112. package/dist/src/generator/mermaid-er/validator/remove-duplicate-relations.d.ts +7 -0
  113. package/dist/src/generator/mermaid-er/validator/remove-duplicate-relations.js +9 -0
  114. package/dist/src/generator/valibot/generator/infer-input.d.ts +5 -0
  115. package/dist/src/generator/valibot/generator/infer-input.js +8 -0
  116. package/dist/src/generator/valibot/generator/valibot-code.d.ts +14 -0
  117. package/dist/src/generator/valibot/generator/valibot-code.js +16 -0
  118. package/dist/src/generator/valibot/generator/valibot.d.ts +13 -0
  119. package/dist/src/generator/valibot/generator/valibot.js +11 -0
  120. package/dist/src/generator/valibot/index.d.ts +9 -0
  121. package/dist/src/generator/valibot/index.js +34 -0
  122. package/dist/src/generator/zod/generator/infer.d.ts +5 -0
  123. package/dist/src/generator/zod/generator/infer.js +8 -0
  124. package/dist/src/generator/zod/generator/zod-code.d.ts +16 -0
  125. package/dist/src/generator/zod/generator/zod-code.js +18 -0
  126. package/dist/src/generator/zod/generator/zod.d.ts +15 -0
  127. package/dist/src/generator/zod/generator/zod.js +12 -0
  128. package/dist/src/generator/zod/index.d.ts +10 -0
  129. package/dist/src/generator/zod/index.js +40 -0
  130. package/dist/src/index.d.ts +10 -0
  131. package/dist/src/index.js +35 -0
  132. package/dist/src/shared/format/index.d.ts +2 -0
  133. package/dist/src/shared/format/index.js +10 -0
  134. package/dist/src/shared/fs/index.d.ts +2 -0
  135. package/dist/src/shared/fs/index.js +10 -0
  136. package/dist/src/shared/fsp/index.d.ts +3 -0
  137. package/dist/src/shared/fsp/index.js +8 -0
  138. package/dist/src/shared/generator/field-definitions.d.ts +12 -0
  139. package/dist/src/shared/generator/field-definitions.js +12 -0
  140. package/dist/src/shared/helper/build-schema-extractor.d.ts +25 -0
  141. package/dist/src/shared/helper/build-schema-extractor.js +33 -0
  142. package/dist/src/shared/helper/create-extract-field-from-property.d.ts +15 -0
  143. package/dist/src/shared/helper/create-extract-field-from-property.js +20 -0
  144. package/dist/src/shared/helper/create-extract-fields-from-call-expression.d.ts +14 -0
  145. package/dist/src/shared/helper/create-extract-fields-from-call-expression.js +14 -0
  146. package/dist/src/shared/helper/create-extract-relation-field-from-property.d.ts +12 -0
  147. package/dist/src/shared/helper/create-extract-relation-field-from-property.js +27 -0
  148. package/dist/src/shared/helper/extract-schemas.d.ts +16 -0
  149. package/dist/src/shared/helper/extract-schemas.js +19 -0
  150. package/dist/src/shared/helper/find-object-literal-expression.d.ts +12 -0
  151. package/dist/src/shared/helper/find-object-literal-expression.js +31 -0
  152. package/dist/src/shared/helper/find-object-literalIn-args.d.ts +2 -0
  153. package/dist/src/shared/helper/find-object-literalIn-args.js +8 -0
  154. package/dist/src/shared/helper/is-relation-function.d.ts +10 -0
  155. package/dist/src/shared/helper/is-relation-function.js +16 -0
  156. package/dist/src/shared/utils/index.d.ts +33 -0
  157. package/dist/src/shared/utils/index.js +61 -0
  158. package/dist/utils/index.d.ts +144 -0
  159. package/dist/utils/index.js +301 -0
  160. package/package.json +2 -6
package/README.md CHANGED
@@ -24,8 +24,8 @@ npm install -D sizuku
24
24
  Prepare schema.ts:
25
25
 
26
26
  ```ts
27
- import { mysqlTable, varchar } from 'drizzle-orm/mysql-core'
28
27
  import { relations } from 'drizzle-orm'
28
+ import { mysqlTable, varchar } from 'drizzle-orm/mysql-core'
29
29
 
30
30
  export const user = mysqlTable('user', {
31
31
  /// Primary key
@@ -49,8 +49,8 @@ export const post = mysqlTable('post', {
49
49
  /// @v.pipe(v.string(), v.minLength(1), v.maxLength(100))
50
50
  title: varchar('title', { length: 100 }).notNull(),
51
51
  /// Body content (no length limit)
52
- /// @z.string()
53
- /// @v.string()
52
+ /// @z.string().min(1).max(65535)
53
+ /// @v.pipe(v.string(), v.minLength(1), v.maxLength(65535))
54
54
  content: varchar('content', { length: 65535 }).notNull(),
55
55
  /// Foreign key referencing User.id
56
56
  /// @z.uuid()
@@ -99,7 +99,7 @@ npx sizuku
99
99
  ### Zod
100
100
 
101
101
  ```ts
102
- import { z } from 'zod/v4'
102
+ import * as z from 'zod'
103
103
 
104
104
  export const UserSchema = z.object({
105
105
  /**
@@ -126,7 +126,7 @@ export const PostSchema = z.object({
126
126
  /**
127
127
  * Body content (no length limit)
128
128
  */
129
- content: z.string(),
129
+ content: z.string().min(1).max(65535),
130
130
  /**
131
131
  * Foreign key referencing User.id
132
132
  */
@@ -134,6 +134,14 @@ export const PostSchema = z.object({
134
134
  })
135
135
 
136
136
  export type Post = z.infer<typeof PostSchema>
137
+
138
+ export const UserRelationsSchema = z.object({ ...UserSchema.shape, posts: z.array(PostSchema) })
139
+
140
+ export type UserRelations = z.infer<typeof UserRelationsSchema>
141
+
142
+ export const PostRelationsSchema = z.object({ ...PostSchema.shape, user: UserSchema })
143
+
144
+ export type PostRelations = z.infer<typeof PostRelationsSchema>
137
145
  ```
138
146
 
139
147
  ### Valibot
@@ -152,8 +160,6 @@ export const UserSchema = v.object({
152
160
  name: v.pipe(v.string(), v.minLength(1), v.maxLength(50)),
153
161
  })
154
162
 
155
- export type User = v.InferInput<typeof UserSchema>
156
-
157
163
  export const PostSchema = v.object({
158
164
  /**
159
165
  * Primary key
@@ -166,14 +172,16 @@ export const PostSchema = v.object({
166
172
  /**
167
173
  * Body content (no length limit)
168
174
  */
169
- content: v.string(),
175
+ content: v.pipe(v.string(), v.minLength(1), v.maxLength(65535)),
170
176
  /**
171
177
  * Foreign key referencing User.id
172
178
  */
173
179
  userId: v.pipe(v.string(), v.uuid()),
174
180
  })
175
181
 
176
- export type Post = v.InferInput<typeof PostSchema>
182
+ export const UserRelationsSchema = v.object({ ...UserSchema.entries, posts: v.array(PostSchema) })
183
+
184
+ export const PostRelationsSchema = v.object({ ...PostSchema.entries, user: UserSchema })
177
185
  ```
178
186
 
179
187
  ### Mermaid ER
@@ -193,8 +201,6 @@ erDiagram
193
201
  }
194
202
  ```
195
203
 
196
- This project is in **early development** and being maintained by a developer with about 2 years of experience. While I'm doing my best to create a useful tool:
197
-
198
204
  ### ⚠️ WARNING: Potential Breaking Changes Without Notice
199
205
 
200
206
  **This package is in active development and may introduce breaking changes without prior notice.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import type { Result } from 'neverthrow';
3
+ import type { Config } from '../src/config/index.js';
4
+ /**
5
+ * Main CLI function that orchestrates the schema generation process.
6
+ *
7
+ * @param config - The configuration object.
8
+ * @returns A Result indicating success or failure.
9
+ */
10
+ export declare function main(config?: Config): Promise<Result<void, Error>>;
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ import { err, ok, ResultAsync } from 'neverthrow';
3
+ import { getConfig } from '../src/config/index.js';
4
+ import { sizukuMermaidER } from '../src/generator/mermaid-er/index.js';
5
+ import { sizukuValibot } from '../src/generator/valibot/index.js';
6
+ import { sizukuZod } from '../src/generator/zod/index.js';
7
+ import { readFileSync } from '../src/shared/fs/index.js';
8
+ /**
9
+ * Main CLI function that orchestrates the schema generation process.
10
+ *
11
+ * @param config - The configuration object.
12
+ * @returns A Result indicating success or failure.
13
+ */
14
+ export async function main(config = getConfig()) {
15
+ return ResultAsync.fromPromise(Promise.resolve(), () => new Error('init'))
16
+ .andThen(() => {
17
+ if (!config.input) {
18
+ return err(new Error('input is not found'));
19
+ }
20
+ const contentResult = readFileSync(config.input);
21
+ if (contentResult.isErr()) {
22
+ return err(contentResult.error);
23
+ }
24
+ const content = contentResult.value;
25
+ const lines = content.split('\n');
26
+ const codeStart = lines.findIndex((line) => !line.trim().startsWith('import') && line.trim() !== '');
27
+ return ok(lines.slice(codeStart));
28
+ })
29
+ .andThen((code) => {
30
+ if (config.zod?.output) {
31
+ 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(() => {
32
+ console.log(`Generated Zod schema at: ${config.zod?.output}`);
33
+ return code;
34
+ });
35
+ }
36
+ return ok(code);
37
+ })
38
+ .andThen((code) => {
39
+ if (config.valibot?.output) {
40
+ return ResultAsync.fromPromise(sizukuValibot(code, config.valibot.output, config.valibot.comment, config.valibot.type), (e) => (e instanceof Error ? e : new Error(String(e)))).map(() => {
41
+ console.log(`Generated Valibot schema at: ${config.valibot?.output}`);
42
+ return code;
43
+ });
44
+ }
45
+ return ok(code);
46
+ })
47
+ .andThen((code) => {
48
+ if (config.mermaid?.output) {
49
+ return ResultAsync.fromPromise(sizukuMermaidER(code, config.mermaid.output), (e) => e instanceof Error ? e : new Error(String(e))).map(() => {
50
+ console.log(`Generated Mermaid ER at: ${config.mermaid?.output}`);
51
+ });
52
+ }
53
+ return ok(undefined);
54
+ });
55
+ }
56
+ main().then((result) => {
57
+ if (!result.isOk()) {
58
+ console.error(result.error);
59
+ process.exit(1);
60
+ }
61
+ });
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ import { resolve } from 'path';
3
+ import { Result, err, ok } from 'neverthrow';
4
+ import { loadConfig, validateConfig } from './config/loader.js';
5
+ import { generateSchemas } from './generator/engine.js';
6
+ const main = async () => {
7
+ try {
8
+ // コマンドライン引数を解析
9
+ const args = process.argv.slice(2);
10
+ if (args.length === 0) {
11
+ return err(new Error('Usage: sizuku <config-file>'));
12
+ }
13
+ const configPath = resolve(args[0]);
14
+ // 設定ファイルを読み込み
15
+ const configResult = await loadConfig(configPath);
16
+ if (configResult.isErr()) {
17
+ return err(configResult.error);
18
+ }
19
+ // 設定をバリデーション
20
+ const validationResult = validateConfig(configResult.value);
21
+ if (validationResult.isErr()) {
22
+ return err(validationResult.error);
23
+ }
24
+ const config = validationResult.value;
25
+ // スキーマ生成を実行
26
+ const generationResult = await generateSchemas(config);
27
+ if (generationResult.isErr()) {
28
+ return err(generationResult.error);
29
+ }
30
+ console.log('✅ Schema generation completed successfully!');
31
+ return ok(undefined);
32
+ }
33
+ catch (error) {
34
+ return err(new Error(`CLI execution failed: ${error instanceof Error ? error.message : String(error)}`));
35
+ }
36
+ };
37
+ // CLI実行
38
+ if (import.meta.url === `file://${process.argv[1]}`) {
39
+ main()
40
+ .then(result => {
41
+ if (result.isErr()) {
42
+ console.error('❌ Error:', result.error.message);
43
+ process.exit(1);
44
+ }
45
+ process.exit(0);
46
+ })
47
+ .catch(error => {
48
+ console.error('❌ Unexpected error:', error instanceof Error ? error.message : String(error));
49
+ process.exit(1);
50
+ });
51
+ }
@@ -4,7 +4,7 @@ export type Config = {
4
4
  output?: `${string}.ts`;
5
5
  comment?: boolean;
6
6
  type?: boolean;
7
- zod?: 'v4' | 'v4-mini' | '@hono/zod-openapi';
7
+ zod?: 'v4' | 'mini' | '@hono/zod-openapi';
8
8
  };
9
9
  valibot?: {
10
10
  output?: `${string}.ts`;
@@ -0,0 +1,19 @@
1
+ import { Result } from 'neverthrow';
2
+ export type SizukuConfig = {
3
+ input: string;
4
+ zod?: {
5
+ output: string;
6
+ comment?: boolean;
7
+ type?: boolean;
8
+ };
9
+ valibot?: {
10
+ output: string;
11
+ comment?: boolean;
12
+ type?: boolean;
13
+ };
14
+ mermaid?: {
15
+ output: string;
16
+ };
17
+ };
18
+ export declare const loadConfig: (configPath: string) => Promise<Result<SizukuConfig, Error>>;
19
+ export declare const validateConfig: (config: SizukuConfig) => Result<SizukuConfig, Error>;
@@ -0,0 +1,30 @@
1
+ import { readFile } from 'fs/promises';
2
+ import { Result, err, ok } from 'neverthrow';
3
+ export const loadConfig = async (configPath) => {
4
+ try {
5
+ const configContent = await readFile(configPath, 'utf-8');
6
+ const config = JSON.parse(configContent);
7
+ // 基本的なバリデーション
8
+ if (!config.input) {
9
+ return err(new Error('Config must specify input file path'));
10
+ }
11
+ return ok(config);
12
+ }
13
+ catch (error) {
14
+ return err(new Error(`Failed to load config from ${configPath}: ${error instanceof Error ? error.message : String(error)}`));
15
+ }
16
+ };
17
+ export const validateConfig = (config) => {
18
+ try {
19
+ if (!config.input) {
20
+ return err(new Error('Input file path is required'));
21
+ }
22
+ if (!config.zod && !config.valibot && !config.mermaid) {
23
+ return err(new Error('At least one output format must be specified (zod, valibot, or mermaid)'));
24
+ }
25
+ return ok(config);
26
+ }
27
+ catch (error) {
28
+ return err(new Error(`Config validation failed: ${error instanceof Error ? error.message : String(error)}`));
29
+ }
30
+ };
@@ -0,0 +1,3 @@
1
+ import { Result } from 'neverthrow';
2
+ import type { SizukuConfig } from '../config/loader.js';
3
+ export declare const generateSchemas: (config: SizukuConfig) => Promise<Result<void, Error>>;
@@ -0,0 +1,63 @@
1
+ import { Result, err, ok } from 'neverthrow';
2
+ import { parseSchemaFile } from '../shared/helper/ast-parser.js';
3
+ import { generateZodSchemas } from './zod/generator.js';
4
+ import { generateValibotSchemas } from './valibot/generator.js';
5
+ import { generateMermaidERDiagram } from './mermaid-er/generator.js';
6
+ import { writeFileSafely } from '../shared/helper/file-writer.js';
7
+ export const generateSchemas = async (config) => {
8
+ try {
9
+ // スキーマファイルを解析
10
+ const parseResult = await parseSchemaFile(config.input);
11
+ if (parseResult.isErr()) {
12
+ return err(parseResult.error);
13
+ }
14
+ const schema = parseResult.value;
15
+ // 各フォーマットを生成
16
+ const results = [];
17
+ if (config.zod) {
18
+ results.push(generateZodOutput(schema, config.zod.output));
19
+ }
20
+ if (config.valibot) {
21
+ results.push(generateValibotOutput(schema, config.valibot.output));
22
+ }
23
+ if (config.mermaid) {
24
+ results.push(generateMermaidOutput(schema, config.mermaid.output));
25
+ }
26
+ // すべての生成を待機
27
+ const generationResults = await Promise.all(results);
28
+ // エラーがあれば最初のエラーを返す
29
+ for (const result of generationResults) {
30
+ if (result.isErr()) {
31
+ return err(result.error);
32
+ }
33
+ }
34
+ return ok(undefined);
35
+ }
36
+ catch (error) {
37
+ return err(new Error(`Schema generation failed: ${error instanceof Error ? error.message : String(error)}`));
38
+ }
39
+ };
40
+ const generateZodOutput = async (schema, outputPath) => {
41
+ const zodResult = generateZodSchemas(schema);
42
+ if (zodResult.isErr()) {
43
+ return err(zodResult.error);
44
+ }
45
+ const writeResult = await writeFileSafely(outputPath, zodResult.value);
46
+ return writeResult;
47
+ };
48
+ const generateValibotOutput = async (schema, outputPath) => {
49
+ const valibotResult = generateValibotSchemas(schema);
50
+ if (valibotResult.isErr()) {
51
+ return err(valibotResult.error);
52
+ }
53
+ const writeResult = await writeFileSafely(outputPath, valibotResult.value);
54
+ return writeResult;
55
+ };
56
+ const generateMermaidOutput = async (schema, outputPath) => {
57
+ const mermaidResult = generateMermaidERDiagram(schema);
58
+ if (mermaidResult.isErr()) {
59
+ return err(mermaidResult.error);
60
+ }
61
+ const writeResult = await writeFileSafely(outputPath, mermaidResult.value);
62
+ return writeResult;
63
+ };
@@ -0,0 +1,6 @@
1
+ import type { TableInfo, ColumnInfo, RelationInfo } from '../../shared/utils/types.js';
2
+ export declare const generateMermaidER: (tables: TableInfo[]) => string;
3
+ export declare const generateRelations: (tables: TableInfo[]) => string;
4
+ export declare const generateRelationLine: (relation: RelationInfo) => string;
5
+ export declare const generateEntity: (table: TableInfo) => string;
6
+ export declare const generateColumnDefinition: (column: ColumnInfo) => string;
@@ -0,0 +1,54 @@
1
+ // 完全純粋なMermaid ER図生成ユーティリティ - 外部import禁止
2
+ export const generateMermaidER = (tables) => {
3
+ const header = 'erDiagram';
4
+ const relations = generateRelations(tables);
5
+ const entities = tables.map(table => generateEntity(table)).join('\n ');
6
+ return `${header}
7
+ ${relations}
8
+ ${entities}`;
9
+ };
10
+ export const generateRelations = (tables) => {
11
+ const relations = [];
12
+ for (const table of tables) {
13
+ for (const relation of table.relations) {
14
+ const relationLine = generateRelationLine(relation);
15
+ if (relationLine) {
16
+ relations.push(relationLine);
17
+ }
18
+ }
19
+ }
20
+ return relations.join('\n ');
21
+ };
22
+ export const generateRelationLine = (relation) => {
23
+ const sourceTable = relation.sourceTable;
24
+ const targetTable = relation.targetTable;
25
+ const sourceColumn = relation.sourceColumn;
26
+ const targetColumn = relation.targetColumn;
27
+ if (relation.type === 'one-to-many') {
28
+ return `${sourceTable} ||--}| ${targetTable} : "(${sourceColumn}) - (${targetColumn})"`;
29
+ }
30
+ else if (relation.type === 'many-to-one') {
31
+ return `${targetTable} ||--}| ${sourceTable} : "(${targetColumn}) - (${sourceColumn})"`;
32
+ }
33
+ else if (relation.type === 'one-to-one') {
34
+ return `${sourceTable} ||--|| ${targetTable} : "(${sourceColumn}) - (${targetColumn})"`;
35
+ }
36
+ else if (relation.type === 'many-to-many') {
37
+ return `${sourceTable} }|--|{ ${targetTable} : "(${sourceColumn}) - (${targetColumn})"`;
38
+ }
39
+ return '';
40
+ };
41
+ export const generateEntity = (table) => {
42
+ const columns = table.columns.map(column => generateColumnDefinition(column)).join('\n ');
43
+ return `${table.name} {
44
+ ${columns}
45
+ }`;
46
+ };
47
+ export const generateColumnDefinition = (column) => {
48
+ const type = column.type;
49
+ const name = column.name;
50
+ const comment = column.comment || '';
51
+ const primaryKey = column.isPrimary ? ' (PK)' : '';
52
+ const nullable = column.isNullable ? '' : ' NOT NULL';
53
+ return `${type} ${name}${primaryKey}${nullable} "${comment}"`;
54
+ };
@@ -1,8 +1,20 @@
1
- import type { Relation, TableInfo } from '../types.js';
2
1
  /**
3
2
  * Generate ER content
4
3
  * @param relations - The relations to generate the ER content from
5
4
  * @param tables - The tables to generate the ER content from
6
5
  * @returns The generated ER content
7
6
  */
8
- export declare function erContent(relations: Relation[], tables: TableInfo[]): string;
7
+ export declare function erContent(relations: {
8
+ fromModel: string;
9
+ toModel: string;
10
+ fromField: string;
11
+ toField: string;
12
+ type: string;
13
+ }[], tables: {
14
+ name: string;
15
+ fields: {
16
+ type: string;
17
+ name: string;
18
+ description: string | null;
19
+ }[];
20
+ }[]): string;
@@ -1,2 +1,2 @@
1
- export { relationLine } from './relation-line.js';
2
1
  export { erContent } from './er-content.js';
2
+ export { relationLine } from './relation-line.js';
@@ -1,2 +1,2 @@
1
- export { relationLine } from './relation-line.js';
2
1
  export { erContent } from './er-content.js';
2
+ export { relationLine } from './relation-line.js';
@@ -1,7 +1,12 @@
1
- import type { Relation } from '../types.js';
2
1
  /**
3
2
  * Generate a relation line for a relation
4
3
  * @param relation - The relation to generate a line for
5
4
  * @returns The generated relation line
6
5
  */
7
- export declare function relationLine(relation: Relation): string;
6
+ export declare function relationLine(relation: {
7
+ fromModel: string;
8
+ toModel: string;
9
+ fromField: string;
10
+ toField: string;
11
+ type: string;
12
+ }): string;
@@ -1,4 +1,4 @@
1
- import { buildRelationLine } from '../relationship/build-relation-line.js';
1
+ import { buildRelationLine } from '../../../shared/helper/extract-schemas.js';
2
2
  /**
3
3
  * Generate a relation line for a relation
4
4
  * @param relation - The relation to generate a line for
@@ -0,0 +1,3 @@
1
+ import { Result } from 'neverthrow';
2
+ import type { ParsedSchema } from '../../shared/utils/types.js';
3
+ export declare const generateMermaidERDiagram: (schema: ParsedSchema) => Result<string, Error>;
@@ -0,0 +1,14 @@
1
+ import { Result, err, ok } from 'neverthrow';
2
+ import { generateMermaidER } from './core.js';
3
+ export const generateMermaidERDiagram = (schema) => {
4
+ try {
5
+ if (schema.tables.length === 0) {
6
+ return err(new Error('No tables found in schema'));
7
+ }
8
+ const mermaidContent = generateMermaidER(schema.tables);
9
+ return ok(mermaidContent);
10
+ }
11
+ catch (error) {
12
+ return err(new Error(`Failed to generate Mermaid ER diagram: ${error instanceof Error ? error.message : String(error)}`));
13
+ }
14
+ };
@@ -3,4 +3,10 @@
3
3
  * @param code - The code to generate Mermaid ER diagram from
4
4
  * @param output - The output file path
5
5
  */
6
- export declare function sizukuMermaidER(code: string[], output: string): Promise<import("neverthrow").Result<void, Error>>;
6
+ export declare function sizukuMermaidER(code: string[], output: string): Promise<{
7
+ ok: true;
8
+ value: undefined;
9
+ } | {
10
+ ok: false;
11
+ error: string;
12
+ }>;
@@ -1,8 +1,8 @@
1
+ import path from 'node:path';
1
2
  import { mkdir, writeFile } from '../../shared/fsp/index.js';
2
- import { extractRelations } from './core/extract-relations.js';
3
+ import { extractRelations } from '../../shared/helper/extract-schemas.js';
3
4
  import { erContent } from './generator/index.js';
4
- import { parseTableInfo } from './validator/parse-table-info.js';
5
- import path from 'node:path';
5
+ import { parseTableInfo } from './validator/index.js';
6
6
  /**
7
7
  * Generate Mermaid ER diagram
8
8
  * @param code - The code to generate Mermaid ER diagram from
@@ -12,5 +12,22 @@ export async function sizukuMermaidER(code, output) {
12
12
  const tables = parseTableInfo(code);
13
13
  const relations = extractRelations(code);
14
14
  const ERContent = erContent(relations, tables);
15
- return await mkdir(path.dirname(output)).andThen(() => writeFile(output, ERContent));
15
+ const mkdirResult = await mkdir(path.dirname(output));
16
+ if (!mkdirResult.ok) {
17
+ return {
18
+ ok: false,
19
+ error: mkdirResult.error,
20
+ };
21
+ }
22
+ const writeFileResult = await writeFile(output, ERContent);
23
+ if (!writeFileResult.ok) {
24
+ return {
25
+ ok: false,
26
+ error: writeFileResult.error,
27
+ };
28
+ }
29
+ return {
30
+ ok: true,
31
+ value: undefined,
32
+ };
16
33
  }
@@ -1,4 +1,8 @@
1
- export { isRelationship } from './is-relationship.js';
2
- export { parseRelationLine } from './parse-relation-line.js';
3
- export { parseTableInfo } from './parse-table-info.js';
4
- export { removeDuplicateRelations } from './remove-duplicate-relations.js';
1
+ export declare function parseTableInfo(code: string[]): {
2
+ name: string;
3
+ fields: {
4
+ type: string;
5
+ name: string;
6
+ description: string | null;
7
+ }[];
8
+ }[];
@@ -1,4 +1,71 @@
1
- export { isRelationship } from './is-relationship.js';
2
- export { parseRelationLine } from './parse-relation-line.js';
3
- export { parseTableInfo } from './parse-table-info.js';
4
- export { removeDuplicateRelations } from './remove-duplicate-relations.js';
1
+ import { Node, Project } from 'ts-morph';
2
+ const baseBuilderName = (expr) => {
3
+ if (Node.isIdentifier(expr))
4
+ return expr.getText();
5
+ if (Node.isCallExpression(expr) || Node.isPropertyAccessExpression(expr))
6
+ return baseBuilderName(expr.getExpression());
7
+ return '';
8
+ };
9
+ const isFieldInfo = (v) => v !== null;
10
+ export function parseTableInfo(code) {
11
+ const source = code.join('\n');
12
+ const file = new Project({ useInMemoryFileSystem: true }).createSourceFile('temp.ts', source);
13
+ return file
14
+ .getVariableStatements()
15
+ .filter((stmt) => stmt.isExported())
16
+ .flatMap((stmt) => {
17
+ const decl = stmt.getDeclarations()[0];
18
+ if (!Node.isVariableDeclaration(decl))
19
+ return [];
20
+ const varName = decl.getName();
21
+ if (varName.toLowerCase().includes('relation'))
22
+ return [];
23
+ const init = decl.getInitializer();
24
+ if (!(init && Node.isCallExpression(init)))
25
+ return [];
26
+ const callee = init.getExpression().getText();
27
+ if (!callee.endsWith('Table') || callee === 'relations')
28
+ return [];
29
+ const objLit = init.getArguments()[1];
30
+ if (!(objLit && Node.isObjectLiteralExpression(objLit)))
31
+ return [];
32
+ const fields = objLit
33
+ .getProperties()
34
+ .filter(Node.isPropertyAssignment)
35
+ .map((prop) => {
36
+ const keyNode = prop.getNameNode();
37
+ if (!Node.isIdentifier(keyNode))
38
+ return null;
39
+ const fieldName = keyNode.getText();
40
+ const initExpr = prop.getInitializer();
41
+ if (!(initExpr && Node.isCallExpression(initExpr)))
42
+ return null;
43
+ const fieldType = baseBuilderName(initExpr);
44
+ const initText = initExpr.getText();
45
+ const lineIdx = prop.getStartLineNumber() - 1;
46
+ const baseDesc = code
47
+ .slice(0, lineIdx)
48
+ .reverse()
49
+ .find((line) => {
50
+ const t = line.trim();
51
+ return (t.startsWith('///') &&
52
+ !t.includes('@z.') &&
53
+ !t.includes('@v.') &&
54
+ !t.includes('@relation'));
55
+ })
56
+ ?.replace(/^\s*\/\/\/\s*/, '') ?? '';
57
+ const prefix = initText.includes('.primaryKey()')
58
+ ? '(PK) '
59
+ : initText.includes('.references(')
60
+ ? '(FK) '
61
+ : '';
62
+ return {
63
+ name: fieldName,
64
+ type: fieldType,
65
+ description: `${prefix}${baseDesc}`.trim(),
66
+ };
67
+ })
68
+ .filter(isFieldInfo);
69
+ return [{ name: varName, fields }];
70
+ });
71
+ }
@@ -1,4 +1,4 @@
1
- import { Project, Node } from 'ts-morph';
1
+ import { Node, Project } from 'ts-morph';
2
2
  const baseBuilderName = (expr) => {
3
3
  if (Node.isIdentifier(expr))
4
4
  return expr.getText();
@@ -0,0 +1,5 @@
1
+ import type { TableInfo, ColumnInfo } from '../../shared/utils/types.js';
2
+ export declare const generateValibotSchema: (table: TableInfo) => string;
3
+ export declare const generateValibotProperty: (column: ColumnInfo) => string;
4
+ export declare const generateDefaultValibotSchema: (column: ColumnInfo) => string;
5
+ export declare const generateValibotFile: (tables: TableInfo[]) => string;