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
@@ -0,0 +1,16 @@
1
+ import { Node } from 'ts-morph';
2
+ /**
3
+ * Determines whether a given `CallExpression` is a relation-related function call.
4
+ *
5
+ * This checks if the function name is either `"relations"` or contains the substring `"relation"`.
6
+ *
7
+ * @param callExpr - The call expression node to check.
8
+ * @returns `true` if the function is a relation function; otherwise, `false`.
9
+ */
10
+ export function isRelationFunctionCall(callExpr) {
11
+ const expression = callExpr.getExpression();
12
+ if (!Node.isIdentifier(expression))
13
+ return false;
14
+ const functionName = expression.getText();
15
+ return functionName === 'relations' || functionName.includes('relation');
16
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Capitalize the first letter of a string.
3
+ *
4
+ * @param str - The input string.
5
+ * @returns A new string with the first letter capitalized.
6
+ */
7
+ export declare function capitalize(str: string): string;
8
+ /**
9
+ * Generate schema name from table name.
10
+ *
11
+ * @param str - The table name.
12
+ * @returns A schema name with PascalCase and Schema suffix.
13
+ */
14
+ export declare function schemaName(str: string): string;
15
+ /**
16
+ * Parse field comments and extract definition line and description.
17
+ *
18
+ * @param commentLines - Raw comment lines (e.g., from source text)
19
+ * @param tag - The tag to look for (e.g., '@v.' or '@z.')
20
+ * @returns Parsed definition and description
21
+ */
22
+ export declare function parseFieldComments(commentLines: string[], tag: '@v.' | '@z.'): {
23
+ definition: string;
24
+ description?: string;
25
+ };
26
+ /**
27
+ * Extract field comments from source text.
28
+ *
29
+ * @param sourceText - The source text to extract comments from.
30
+ * @param fieldStartPos - The start position of the field.
31
+ * @returns Array of comment lines.
32
+ */
33
+ export declare function extractFieldComments(sourceText: string, fieldStartPos: number): string[];
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Capitalize the first letter of a string.
3
+ *
4
+ * @param str - The input string.
5
+ * @returns A new string with the first letter capitalized.
6
+ */
7
+ export function capitalize(str) {
8
+ return `${str.charAt(0).toUpperCase()}${str.slice(1)}`;
9
+ }
10
+ /**
11
+ * Generate schema name from table name.
12
+ *
13
+ * @param str - The table name.
14
+ * @returns A schema name with PascalCase and Schema suffix.
15
+ */
16
+ export function schemaName(str) {
17
+ return `${capitalize(str)}Schema`;
18
+ }
19
+ /**
20
+ * Parse field comments and extract definition line and description.
21
+ *
22
+ * @param commentLines - Raw comment lines (e.g., from source text)
23
+ * @param tag - The tag to look for (e.g., '@v.' or '@z.')
24
+ * @returns Parsed definition and description
25
+ */
26
+ export function parseFieldComments(commentLines, tag) {
27
+ const cleaned = commentLines.map((line) => line.replace(/^\/\/\/\s*/, '').trim()).filter(Boolean);
28
+ const definition = cleaned.find((line) => line.startsWith(tag))?.replace(/^@/, '') ?? '';
29
+ const descriptionLines = cleaned.filter((line) => !(line.includes('@z.') || line.includes('@v.') || line.includes('@relation.')));
30
+ const description = descriptionLines.length ? descriptionLines.join(' ') : undefined;
31
+ return { definition, description };
32
+ }
33
+ /**
34
+ * Extract field comments from source text.
35
+ *
36
+ * @param sourceText - The source text to extract comments from.
37
+ * @param fieldStartPos - The start position of the field.
38
+ * @returns Array of comment lines.
39
+ */
40
+ export function extractFieldComments(sourceText, fieldStartPos) {
41
+ const beforeField = sourceText.substring(0, fieldStartPos);
42
+ const lines = beforeField.split('\n');
43
+ const reverseIndex = lines
44
+ .map((line, index) => ({ line: line.trim(), index }))
45
+ .reverse()
46
+ .reduce((acc, { line }) => {
47
+ if (acc.shouldStop)
48
+ return acc;
49
+ if (line.startsWith('///')) {
50
+ return {
51
+ commentLines: [line, ...acc.commentLines],
52
+ shouldStop: false,
53
+ };
54
+ }
55
+ if (line === '') {
56
+ return acc;
57
+ }
58
+ return { commentLines: acc.commentLines, shouldStop: true };
59
+ }, { commentLines: [], shouldStop: false });
60
+ return reverseIndex.commentLines;
61
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Capitalize the first letter of a string.
3
+ *
4
+ * @param str - The input string.
5
+ * @returns A new string with the first letter capitalized.
6
+ */
7
+ export declare function capitalize(str: string): string;
8
+ /**
9
+ * Remove triple slash prefix from a string.
10
+ *
11
+ * @param str - The input string.
12
+ * @returns String with triple slash prefix removed.
13
+ */
14
+ export declare function removeTripleSlash(str: string): string;
15
+ /**
16
+ * Check if a string is non-empty.
17
+ *
18
+ * @param str - The input string.
19
+ * @returns True if string is non-empty.
20
+ */
21
+ export declare function isNonEmpty(str: string): boolean;
22
+ /**
23
+ * Check if a string contains a substring.
24
+ *
25
+ * @param str - The input string.
26
+ * @param substr - The substring to search for.
27
+ * @returns True if string contains substring.
28
+ */
29
+ export declare function containsSubstring(str: string, substr: string): boolean;
30
+ /**
31
+ * Check if a string starts with a prefix.
32
+ *
33
+ * @param str - The input string.
34
+ * @param prefix - The prefix to check.
35
+ * @returns True if string starts with prefix.
36
+ */
37
+ export declare function startsWith(str: string, prefix: string): boolean;
38
+ /**
39
+ * Remove @ sign from the beginning of a string.
40
+ *
41
+ * @param str - The input string.
42
+ * @returns String with @ sign removed.
43
+ */
44
+ export declare function removeAtSign(str: string): string;
45
+ /**
46
+ * Join array of strings with space separator.
47
+ *
48
+ * @param arr - Array of strings to join.
49
+ * @returns Joined string with spaces.
50
+ */
51
+ export declare function joinWithSpace(arr: string[]): string;
52
+ /**
53
+ * Split string by newline character.
54
+ *
55
+ * @param str - The input string.
56
+ * @returns Array of strings split by newline.
57
+ */
58
+ export declare function splitByNewline(str: string): string[];
59
+ /**
60
+ * Trim whitespace from string.
61
+ *
62
+ * @param str - The input string.
63
+ * @returns Trimmed string.
64
+ */
65
+ export declare function trimString(str: string): string;
66
+ /**
67
+ * Parse relation line and extract components.
68
+ *
69
+ * @param line - The line to parse.
70
+ * @returns Parsed relation or null if not a relation line.
71
+ */
72
+ export declare function parseRelationLine(line: string): {
73
+ fromModel: string;
74
+ toModel: string;
75
+ fromField: string;
76
+ toField: string;
77
+ type: string;
78
+ } | null;
79
+ /**
80
+ * Split string by '-to-' delimiter.
81
+ *
82
+ * @param str - The input string.
83
+ * @returns Array with two parts or null if not found.
84
+ */
85
+ export declare function splitByTo(str: string): [string, string] | null;
86
+ /**
87
+ * Remove optional suffix from string.
88
+ *
89
+ * @param str - The input string.
90
+ * @returns String with optional suffix removed.
91
+ */
92
+ export declare function removeOptionalSuffix(str: string): string;
93
+ /**
94
+ * Split string by whitespace.
95
+ *
96
+ * @param str - The input string.
97
+ * @returns Array of strings split by whitespace.
98
+ */
99
+ export declare function splitByWhitespace(str: string): string[];
100
+ /**
101
+ * Split string by dot character.
102
+ *
103
+ * @param str - The input string.
104
+ * @returns Array of strings split by dot.
105
+ */
106
+ export declare function splitByDot(str: string): string[];
107
+ /**
108
+ * Parse field comments and extract definition line and description.
109
+ *
110
+ * @param commentLines - Raw comment lines (e.g., from source text)
111
+ * @param tag - The tag to look for (e.g., '@v.' or '@z.')
112
+ * @returns Parsed definition and description
113
+ */
114
+ export declare function parseFieldComments(commentLines: string[], tag: '@v.' | '@z.'): {
115
+ definition: string;
116
+ description?: string;
117
+ objectType?: 'strict' | 'loose';
118
+ };
119
+ /**
120
+ * Extract field comments from source text.
121
+ *
122
+ * @param sourceText - The source text to extract comments from.
123
+ * @param fieldStartPos - The position of the field in the source text.
124
+ * @returns An array of comment lines.
125
+ */
126
+ export declare function extractFieldComments(sourceText: string, fieldStartPos: number): string[];
127
+ /**
128
+ * @param name
129
+ * @returns
130
+ */
131
+ export declare function infer(name: string): string;
132
+ export declare function inferInput(name: string): string;
133
+ /**
134
+ * @param schema
135
+ * @returns
136
+ */
137
+ export declare function fieldDefinitions(schema: {
138
+ name: string;
139
+ fields: {
140
+ name: string;
141
+ definition: string;
142
+ description?: string;
143
+ }[];
144
+ }, comment: boolean): string;
@@ -0,0 +1,301 @@
1
+ /* ========================================================================== *
2
+ * text
3
+ * ========================================================================== */
4
+ /**
5
+ * Capitalize the first letter of a string.
6
+ *
7
+ * @param str - The input string.
8
+ * @returns A new string with the first letter capitalized.
9
+ */
10
+ export function capitalize(str) {
11
+ return `${str.charAt(0).toUpperCase()}${str.slice(1)}`;
12
+ }
13
+ /**
14
+ * Remove triple slash prefix from a string.
15
+ *
16
+ * @param str - The input string.
17
+ * @returns String with triple slash prefix removed.
18
+ */
19
+ export function removeTripleSlash(str) {
20
+ return str.startsWith('///') ? str.substring(3) : str;
21
+ }
22
+ /**
23
+ * Check if a string is non-empty.
24
+ *
25
+ * @param str - The input string.
26
+ * @returns True if string is non-empty.
27
+ */
28
+ export function isNonEmpty(str) {
29
+ return str.length > 0;
30
+ }
31
+ /**
32
+ * Check if a string contains a substring.
33
+ *
34
+ * @param str - The input string.
35
+ * @param substr - The substring to search for.
36
+ * @returns True if string contains substring.
37
+ */
38
+ export function containsSubstring(str, substr) {
39
+ return str.indexOf(substr) !== -1;
40
+ }
41
+ /**
42
+ * Check if a string starts with a prefix.
43
+ *
44
+ * @param str - The input string.
45
+ * @param prefix - The prefix to check.
46
+ * @returns True if string starts with prefix.
47
+ */
48
+ export function startsWith(str, prefix) {
49
+ return str.indexOf(prefix) === 0;
50
+ }
51
+ /**
52
+ * Remove @ sign from the beginning of a string.
53
+ *
54
+ * @param str - The input string.
55
+ * @returns String with @ sign removed.
56
+ */
57
+ export function removeAtSign(str) {
58
+ return str.startsWith('@') ? str.substring(1) : str;
59
+ }
60
+ /**
61
+ * Join array of strings with space separator.
62
+ *
63
+ * @param arr - Array of strings to join.
64
+ * @returns Joined string with spaces.
65
+ */
66
+ export function joinWithSpace(arr) {
67
+ return arr.join(' ');
68
+ }
69
+ /**
70
+ * Split string by newline character.
71
+ *
72
+ * @param str - The input string.
73
+ * @returns Array of strings split by newline.
74
+ */
75
+ export function splitByNewline(str) {
76
+ const result = [];
77
+ let current = '';
78
+ for (let i = 0; i < str.length; i++) {
79
+ if (str[i] === '\n') {
80
+ result.push(current);
81
+ current = '';
82
+ }
83
+ else {
84
+ current += str[i];
85
+ }
86
+ }
87
+ result.push(current);
88
+ return result;
89
+ }
90
+ /**
91
+ * Trim whitespace from string.
92
+ *
93
+ * @param str - The input string.
94
+ * @returns Trimmed string.
95
+ */
96
+ export function trimString(str) {
97
+ let start = 0;
98
+ let end = str.length - 1;
99
+ while (start <= end &&
100
+ (str[start] === ' ' || str[start] === '\t' || str[start] === '\r' || str[start] === '\n')) {
101
+ start++;
102
+ }
103
+ while (end >= start &&
104
+ (str[end] === ' ' || str[end] === '\t' || str[end] === '\r' || str[end] === '\n')) {
105
+ end--;
106
+ }
107
+ return str.substring(start, end + 1);
108
+ }
109
+ /**
110
+ * Parse relation line and extract components.
111
+ *
112
+ * @param line - The line to parse.
113
+ * @returns Parsed relation or null if not a relation line.
114
+ */
115
+ export function parseRelationLine(line) {
116
+ if (!startsWith(line, '@relation'))
117
+ return null;
118
+ const parts = splitByWhitespace(line);
119
+ if (parts.length < 5)
120
+ return null;
121
+ const fromParts = splitByDot(parts[1]);
122
+ const toParts = splitByDot(parts[2]);
123
+ if (fromParts.length !== 2 || toParts.length !== 2)
124
+ return null;
125
+ return {
126
+ fromModel: fromParts[0],
127
+ fromField: fromParts[1],
128
+ toModel: toParts[0],
129
+ toField: toParts[1],
130
+ type: parts[3],
131
+ };
132
+ }
133
+ /**
134
+ * Split string by '-to-' delimiter.
135
+ *
136
+ * @param str - The input string.
137
+ * @returns Array with two parts or null if not found.
138
+ */
139
+ export function splitByTo(str) {
140
+ const index = str.indexOf('-to-');
141
+ if (index === -1)
142
+ return null;
143
+ return [str.substring(0, index), str.substring(index + 4)];
144
+ }
145
+ /**
146
+ * Remove optional suffix from string.
147
+ *
148
+ * @param str - The input string.
149
+ * @returns String with optional suffix removed.
150
+ */
151
+ export function removeOptionalSuffix(str) {
152
+ const index = str.indexOf('-optional');
153
+ return index !== -1 ? str.substring(0, index) : str;
154
+ }
155
+ /**
156
+ * Split string by whitespace.
157
+ *
158
+ * @param str - The input string.
159
+ * @returns Array of strings split by whitespace.
160
+ */
161
+ export function splitByWhitespace(str) {
162
+ const result = [];
163
+ let current = '';
164
+ let inWord = false;
165
+ for (let i = 0; i < str.length; i++) {
166
+ const char = str[i];
167
+ const isWhitespace = char === ' ' || char === '\t' || char === '\r' || char === '\n';
168
+ if (isWhitespace) {
169
+ if (inWord) {
170
+ result.push(current);
171
+ current = '';
172
+ inWord = false;
173
+ }
174
+ }
175
+ else {
176
+ current += char;
177
+ inWord = true;
178
+ }
179
+ }
180
+ if (inWord) {
181
+ result.push(current);
182
+ }
183
+ return result;
184
+ }
185
+ /**
186
+ * Split string by dot character.
187
+ *
188
+ * @param str - The input string.
189
+ * @returns Array of strings split by dot.
190
+ */
191
+ export function splitByDot(str) {
192
+ const result = [];
193
+ let current = '';
194
+ for (let i = 0; i < str.length; i++) {
195
+ if (str[i] === '.') {
196
+ result.push(current);
197
+ current = '';
198
+ }
199
+ else {
200
+ current += str[i];
201
+ }
202
+ }
203
+ result.push(current);
204
+ return result;
205
+ }
206
+ /* ========================================================================== *
207
+ * parse
208
+ * ========================================================================== */
209
+ /**
210
+ * Parse field comments and extract definition line and description.
211
+ *
212
+ * @param commentLines - Raw comment lines (e.g., from source text)
213
+ * @param tag - The tag to look for (e.g., '@v.' or '@z.')
214
+ * @returns Parsed definition and description
215
+ */
216
+ export function parseFieldComments(commentLines, tag) {
217
+ const cleaned = commentLines.map((line) => removeTripleSlash(line).trim()).filter(isNonEmpty);
218
+ // Extract object type from strictObject/looseObject tags
219
+ const objectTypeLine = cleaned.find((line) => containsSubstring(line, `${tag.slice(1)}strictObject`) ||
220
+ containsSubstring(line, `${tag.slice(1)}looseObject`));
221
+ const objectType = objectTypeLine && containsSubstring(objectTypeLine, 'strictObject')
222
+ ? 'strict'
223
+ : objectTypeLine && containsSubstring(objectTypeLine, 'looseObject')
224
+ ? 'loose'
225
+ : undefined;
226
+ // Extract definition (excluding strictObject/looseObject tags)
227
+ const definitionLine = cleaned.find((line) => startsWith(line, tag) &&
228
+ !containsSubstring(line, 'strictObject') &&
229
+ !containsSubstring(line, 'looseObject'));
230
+ const definition = definitionLine ? removeAtSign(definitionLine) : '';
231
+ const descriptionLines = cleaned.filter((line) => !(containsSubstring(line, '@z.') ||
232
+ containsSubstring(line, '@v.') ||
233
+ containsSubstring(line, '@relation.')));
234
+ const description = descriptionLines.length > 0 ? joinWithSpace(descriptionLines) : undefined;
235
+ return { definition, description, objectType };
236
+ }
237
+ /* ========================================================================== *
238
+ * extractFieldComments
239
+ * ========================================================================== */
240
+ /**
241
+ * Extract field comments from source text.
242
+ *
243
+ * @param sourceText - The source text to extract comments from.
244
+ * @param fieldStartPos - The position of the field in the source text.
245
+ * @returns An array of comment lines.
246
+ */
247
+ export function extractFieldComments(sourceText, fieldStartPos) {
248
+ const beforeField = sourceText.substring(0, fieldStartPos);
249
+ const lines = splitByNewline(beforeField);
250
+ const reverseIndex = lines
251
+ .map((line, index) => ({ line: trimString(line), index }))
252
+ .reverse()
253
+ .reduce((acc, { line }) => {
254
+ if (acc.shouldStop)
255
+ return acc;
256
+ if (startsWith(line, '///')) {
257
+ return {
258
+ commentLines: [line, ...acc.commentLines],
259
+ shouldStop: false,
260
+ };
261
+ }
262
+ if (line === '') {
263
+ return acc;
264
+ }
265
+ return { commentLines: acc.commentLines, shouldStop: true };
266
+ }, { commentLines: [], shouldStop: false });
267
+ return reverseIndex.commentLines;
268
+ }
269
+ /* ========================================================================== *
270
+ * zod
271
+ * ========================================================================== */
272
+ /**
273
+ * @param name
274
+ * @returns
275
+ */
276
+ export function infer(name) {
277
+ const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
278
+ return `export type ${capitalizedName} = z.infer<typeof ${capitalizedName}Schema>`;
279
+ }
280
+ /* ========================================================================== *
281
+ * valibot
282
+ * ========================================================================== */
283
+ export function inferInput(name) {
284
+ const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
285
+ return `export type ${capitalizedName} = v.InferInput<typeof ${capitalizedName}Schema>`;
286
+ }
287
+ /* ========================================================================== *
288
+ * schema
289
+ * ========================================================================== */
290
+ /**
291
+ * @param schema
292
+ * @returns
293
+ */
294
+ export function fieldDefinitions(schema, comment) {
295
+ return schema.fields
296
+ .map(({ name, definition, description }) => {
297
+ const commentCode = description && comment ? `/**\n* ${description}\n*/\n` : '';
298
+ return `${commentCode}${name}:${definition}`;
299
+ })
300
+ .join(',\n');
301
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sizuku",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "description": "Sizuku is a tool that generates validation schemas for Zod and Valibot, as well as ER diagrams, from Drizzle schemas annotated with comments.",
6
6
  "license": "MIT",
7
7
  "keywords": [
@@ -31,9 +31,6 @@
31
31
  },
32
32
  "scripts": {
33
33
  "deps": "rm -rf node_modules && pnpm install",
34
- "demo-er": "sizuku-mermaid-er db/schema.ts -o mermaid-er/ER.md",
35
- "demo-z": "sizuku-zod db/schema.ts -o zod/index.ts",
36
- "demo-v": "sizuku-valibot db/schema.ts -o valibot/index.ts",
37
34
  "build": "tsc",
38
35
  "typecheck": "tsc --noEmit",
39
36
  "dev": "pnpm --filter sizuku-test dev",
@@ -47,10 +44,9 @@
47
44
  "drizzle-orm": "^0.40.1",
48
45
  "valibot": "1.0.0-rc.3",
49
46
  "vitest": "^3.2.4",
50
- "zod": "^3.25.67"
47
+ "zod": "^4.0.15"
51
48
  },
52
49
  "dependencies": {
53
- "neverthrow": "^8.2.0",
54
50
  "prettier": "^3.6.2",
55
51
  "ts-morph": "^26.0.0"
56
52
  }