sizuku 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -16
- package/dist/config/index.d.ts +22 -13
- package/dist/config/index.js +83 -11
- package/dist/generator/mermaid-er/generator/er-content.js +1 -1
- package/dist/generator/mermaid-er/validator/index.js +5 -2
- package/dist/generator/valibot/index.d.ts +1 -1
- package/dist/generator/valibot/index.js +2 -2
- package/dist/generator/zod/index.d.ts +5 -5
- package/dist/generator/zod/index.js +2 -2
- package/dist/index.d.ts +5 -6
- package/dist/index.js +16 -14
- package/dist/shared/format/index.d.ts +4 -6
- package/dist/shared/format/index.js +2 -2
- package/dist/shared/fs/index.d.ts +4 -4
- package/dist/shared/fsp/index.d.ts +4 -4
- package/dist/utils/index.d.ts +9 -9
- package/dist/utils/index.js +33 -84
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -70,24 +70,30 @@ export const postRelations = relations(post, ({ one }) => ({
|
|
|
70
70
|
}))
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
Prepare sizuku.
|
|
74
|
-
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
Prepare sizuku.config.ts:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import defineConfig from 'sizuku/config'
|
|
77
|
+
|
|
78
|
+
export default defineConfig({
|
|
79
|
+
input: 'db/schema.ts',
|
|
80
|
+
zod: {
|
|
81
|
+
output: 'zod/index.ts',
|
|
82
|
+
comment: true,
|
|
83
|
+
type: true,
|
|
84
|
+
zod: 'v4',
|
|
85
|
+
relation: true,
|
|
82
86
|
},
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
valibot: {
|
|
88
|
+
output: 'valibot/index.ts',
|
|
89
|
+
comment: true,
|
|
90
|
+
type: true,
|
|
91
|
+
relation: true,
|
|
86
92
|
},
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
}
|
|
93
|
+
mermaid: {
|
|
94
|
+
output: 'mermaid-er/ER.md',
|
|
95
|
+
},
|
|
96
|
+
})
|
|
91
97
|
```
|
|
92
98
|
|
|
93
99
|
Run Sizuku:
|
|
@@ -160,6 +166,8 @@ export const UserSchema = v.object({
|
|
|
160
166
|
name: v.pipe(v.string(), v.minLength(1), v.maxLength(50)),
|
|
161
167
|
})
|
|
162
168
|
|
|
169
|
+
export type User = v.InferInput<typeof UserSchema>
|
|
170
|
+
|
|
163
171
|
export const PostSchema = v.object({
|
|
164
172
|
/**
|
|
165
173
|
* Primary key
|
|
@@ -179,9 +187,15 @@ export const PostSchema = v.object({
|
|
|
179
187
|
userId: v.pipe(v.string(), v.uuid()),
|
|
180
188
|
})
|
|
181
189
|
|
|
190
|
+
export type Post = v.InferInput<typeof PostSchema>
|
|
191
|
+
|
|
182
192
|
export const UserRelationsSchema = v.object({ ...UserSchema.entries, posts: v.array(PostSchema) })
|
|
183
193
|
|
|
194
|
+
export type UserRelations = v.InferInput<typeof UserRelationsSchema>
|
|
195
|
+
|
|
184
196
|
export const PostRelationsSchema = v.object({ ...PostSchema.entries, user: UserSchema })
|
|
197
|
+
|
|
198
|
+
export type PostRelations = v.InferInput<typeof PostRelationsSchema>
|
|
185
199
|
```
|
|
186
200
|
|
|
187
201
|
### Mermaid ER
|
package/dist/config/index.d.ts
CHANGED
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
export type Config = {
|
|
2
|
-
input
|
|
3
|
-
zod?: {
|
|
4
|
-
output
|
|
5
|
-
comment?: boolean;
|
|
6
|
-
type?: boolean;
|
|
7
|
-
zod?: 'v4' | 'mini' | '@hono/zod-openapi';
|
|
2
|
+
readonly input: `${string}.ts`;
|
|
3
|
+
readonly zod?: {
|
|
4
|
+
readonly output: `${string}.ts`;
|
|
5
|
+
readonly comment?: boolean;
|
|
6
|
+
readonly type?: boolean;
|
|
7
|
+
readonly zod?: 'v4' | 'mini' | '@hono/zod-openapi';
|
|
8
|
+
readonly relation?: boolean;
|
|
8
9
|
};
|
|
9
|
-
valibot?: {
|
|
10
|
-
output
|
|
11
|
-
comment?: boolean;
|
|
12
|
-
type?: boolean;
|
|
10
|
+
readonly valibot?: {
|
|
11
|
+
readonly output: `${string}.ts`;
|
|
12
|
+
readonly comment?: boolean;
|
|
13
|
+
readonly type?: boolean;
|
|
14
|
+
readonly relation?: boolean;
|
|
13
15
|
};
|
|
14
|
-
mermaid?: {
|
|
15
|
-
output
|
|
16
|
+
readonly mermaid?: {
|
|
17
|
+
readonly output: string;
|
|
16
18
|
};
|
|
17
19
|
};
|
|
18
|
-
export declare function
|
|
20
|
+
export declare function config(): Promise<{
|
|
21
|
+
readonly ok: true;
|
|
22
|
+
readonly value: Config;
|
|
23
|
+
} | {
|
|
24
|
+
readonly ok: false;
|
|
25
|
+
readonly error: string;
|
|
26
|
+
}>;
|
|
27
|
+
export default function defineConfig(config: Config): Config;
|
package/dist/config/index.js
CHANGED
|
@@ -1,13 +1,85 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { register } from 'tsx/esm/api';
|
|
5
|
+
export async function config() {
|
|
6
|
+
const isTs = (o) => o.endsWith('.ts');
|
|
7
|
+
const abs = resolve(process.cwd(), 'sizuku.config.ts');
|
|
8
|
+
if (!existsSync(abs)) {
|
|
9
|
+
return { ok: false, error: `Config not found: ${abs}` };
|
|
5
10
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
try {
|
|
12
|
+
register();
|
|
13
|
+
const mod = await import(pathToFileURL(abs).href);
|
|
14
|
+
if (!('default' in mod)) {
|
|
15
|
+
return { ok: false, error: 'Config must export default object' };
|
|
16
|
+
}
|
|
17
|
+
if (mod.default !== undefined) {
|
|
18
|
+
if (!isTs(mod.default.input)) {
|
|
19
|
+
return { ok: false, error: 'Input must be a .ts file' };
|
|
20
|
+
}
|
|
21
|
+
// zod
|
|
22
|
+
if (mod.default.zod !== undefined) {
|
|
23
|
+
if (!isTs(mod.default.zod.output)) {
|
|
24
|
+
return { ok: false, error: 'Zod output must be a .ts file' };
|
|
25
|
+
}
|
|
26
|
+
if (mod.default.zod.comment !== undefined) {
|
|
27
|
+
if (typeof mod.default.zod.comment !== 'boolean') {
|
|
28
|
+
return { ok: false, error: 'Zod comment must be a boolean' };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (mod.default.zod.type !== undefined) {
|
|
32
|
+
if (typeof mod.default.zod.type !== 'boolean') {
|
|
33
|
+
return { ok: false, error: 'Zod type must be a boolean' };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (mod.default.zod.zod !== undefined) {
|
|
37
|
+
if (mod.default.zod.zod !== 'v4' &&
|
|
38
|
+
mod.default.zod.zod !== 'mini' &&
|
|
39
|
+
mod.default.zod.zod !== '@hono/zod-openapi') {
|
|
40
|
+
return { ok: false, error: 'zod must be v4, mini, or @hono/zod-openapi' };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (mod.default.zod.relation !== undefined) {
|
|
44
|
+
if (typeof mod.default.zod.relation !== 'boolean') {
|
|
45
|
+
return { ok: false, error: 'Zod relation must be a boolean' };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// valibot
|
|
51
|
+
if (mod.default.valibot !== undefined) {
|
|
52
|
+
if (!isTs(mod.default.valibot.output)) {
|
|
53
|
+
return { ok: false, error: 'Valibot output must be a .ts file' };
|
|
54
|
+
}
|
|
55
|
+
if (mod.default.valibot.comment !== undefined) {
|
|
56
|
+
if (typeof mod.default.valibot.comment !== 'boolean') {
|
|
57
|
+
return { ok: false, error: 'Valibot comment must be a boolean' };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (mod.default.valibot.type !== undefined) {
|
|
61
|
+
if (typeof mod.default.valibot.type !== 'boolean') {
|
|
62
|
+
return { ok: false, error: 'Valibot type must be a boolean' };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (mod.default.valibot.relation !== undefined) {
|
|
66
|
+
if (typeof mod.default.valibot.relation !== 'boolean') {
|
|
67
|
+
return { ok: false, error: 'Valibot relation must be a boolean' };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// mermaid
|
|
72
|
+
if (mod.default.mermaid !== undefined) {
|
|
73
|
+
if (typeof mod.default.mermaid.output !== 'string') {
|
|
74
|
+
return { ok: false, error: 'Mermaid output must be a string' };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { ok: true, value: mod.default };
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export default function defineConfig(config) {
|
|
84
|
+
return config;
|
|
13
85
|
}
|
|
@@ -14,7 +14,7 @@ export function erContent(relations, tables) {
|
|
|
14
14
|
// Generate per-table definitions
|
|
15
15
|
...tables.flatMap((table) => [
|
|
16
16
|
` ${table.name} {`,
|
|
17
|
-
...table.fields.map((field) => ` ${field.type} ${field.name}
|
|
17
|
+
...table.fields.map((field) => ` ${field.type} ${field.name}${field.description ? ` "${field.description}"` : ''}`),
|
|
18
18
|
' }',
|
|
19
19
|
]),
|
|
20
20
|
...ER_FOOTER,
|
|
@@ -43,7 +43,8 @@ export function parseTableInfo(code) {
|
|
|
43
43
|
const fieldType = baseBuilderName(initExpr);
|
|
44
44
|
const initText = initExpr.getText();
|
|
45
45
|
const lineIdx = prop.getStartLineNumber() - 1;
|
|
46
|
-
|
|
46
|
+
// Find the immediate comment above this field
|
|
47
|
+
const immediateComment = code
|
|
47
48
|
.slice(0, lineIdx)
|
|
48
49
|
.reverse()
|
|
49
50
|
.find((line) => {
|
|
@@ -59,10 +60,12 @@ export function parseTableInfo(code) {
|
|
|
59
60
|
: initText.includes('.references(')
|
|
60
61
|
? '(FK) '
|
|
61
62
|
: '';
|
|
63
|
+
// Only include description if there's a comment
|
|
64
|
+
const description = immediateComment ? `${prefix}${immediateComment}`.trim() : null;
|
|
62
65
|
return {
|
|
63
66
|
name: fieldName,
|
|
64
67
|
type: fieldType,
|
|
65
|
-
description
|
|
68
|
+
description,
|
|
66
69
|
};
|
|
67
70
|
})
|
|
68
71
|
.filter(isFieldInfo);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @param comment - Whether to include comments in the generated code
|
|
6
6
|
* @param type - Whether to include type information in the generated code
|
|
7
7
|
*/
|
|
8
|
-
export declare function sizukuValibot(code: string[], output: `${string}.ts`, comment?: boolean, type?: boolean,
|
|
8
|
+
export declare function sizukuValibot(code: string[], output: `${string}.ts`, comment?: boolean, type?: boolean, relation?: boolean): Promise<{
|
|
9
9
|
ok: true;
|
|
10
10
|
value: undefined;
|
|
11
11
|
} | {
|
|
@@ -11,14 +11,14 @@ import { valibotCode } from './generator/valibot-code.js';
|
|
|
11
11
|
* @param comment - Whether to include comments in the generated code
|
|
12
12
|
* @param type - Whether to include type information in the generated code
|
|
13
13
|
*/
|
|
14
|
-
export async function sizukuValibot(code, output, comment, type,
|
|
14
|
+
export async function sizukuValibot(code, output, comment, type, relation) {
|
|
15
15
|
const baseSchemas = extractSchemas(code, 'valibot');
|
|
16
16
|
const relationSchemas = extractRelationSchemas(code, 'valibot');
|
|
17
17
|
const valibotGeneratedCode = [
|
|
18
18
|
"import * as v from 'valibot'",
|
|
19
19
|
'',
|
|
20
20
|
...baseSchemas.map((schema) => valibotCode(schema, comment ?? false, type ?? false)),
|
|
21
|
-
...(
|
|
21
|
+
...(relation
|
|
22
22
|
? relationSchemas.map((schema) => relationValibotCode(schema, type ?? false))
|
|
23
23
|
: []),
|
|
24
24
|
].join('\n');
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* @param type - Whether to include type information in the generated code
|
|
7
7
|
* @param zod - The Zod version to use
|
|
8
8
|
*/
|
|
9
|
-
export declare function sizukuZod(code: string[], output: `${string}.ts`, comment?: boolean, type?: boolean, zod?: 'v4' | 'mini' | '@hono/zod-openapi',
|
|
10
|
-
ok: true;
|
|
11
|
-
value: undefined;
|
|
9
|
+
export declare function sizukuZod(code: string[], output: `${string}.ts`, comment?: boolean, type?: boolean, zod?: 'v4' | 'mini' | '@hono/zod-openapi', relation?: boolean): Promise<{
|
|
10
|
+
readonly ok: true;
|
|
11
|
+
readonly value: undefined;
|
|
12
12
|
} | {
|
|
13
|
-
ok: false;
|
|
14
|
-
error: string;
|
|
13
|
+
readonly ok: false;
|
|
14
|
+
readonly error: string;
|
|
15
15
|
}>;
|
|
@@ -12,7 +12,7 @@ import { zodCode } from './generator/zod-code.js';
|
|
|
12
12
|
* @param type - Whether to include type information in the generated code
|
|
13
13
|
* @param zod - The Zod version to use
|
|
14
14
|
*/
|
|
15
|
-
export async function sizukuZod(code, output, comment, type, zod,
|
|
15
|
+
export async function sizukuZod(code, output, comment, type, zod, relation) {
|
|
16
16
|
const importLine = zod === 'mini'
|
|
17
17
|
? `import * as z from 'zod/mini'`
|
|
18
18
|
: zod === '@hono/zod-openapi'
|
|
@@ -24,7 +24,7 @@ export async function sizukuZod(code, output, comment, type, zod, relations) {
|
|
|
24
24
|
importLine,
|
|
25
25
|
'',
|
|
26
26
|
...baseSchemas.map((schema) => zodCode(schema, comment ?? false, type ?? false)),
|
|
27
|
-
...(
|
|
27
|
+
...(relation ? relationSchemas.map((schema) => relationZodCode(schema, type ?? false)) : []),
|
|
28
28
|
].join('\n');
|
|
29
29
|
const mkdirResult = await mkdir(path.dirname(output));
|
|
30
30
|
if (!mkdirResult.ok) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
value: string;
|
|
1
|
+
export declare function main(): Promise<{
|
|
2
|
+
readonly ok: true;
|
|
3
|
+
readonly value: string;
|
|
5
4
|
} | {
|
|
6
|
-
ok: false;
|
|
7
|
-
error: string;
|
|
5
|
+
readonly ok: false;
|
|
6
|
+
readonly error: string;
|
|
8
7
|
}>;
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
// #!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { config } from './config/index.js';
|
|
3
3
|
import { sizukuMermaidER } from './generator/mermaid-er/index.js';
|
|
4
4
|
import { sizukuValibot } from './generator/valibot/index.js';
|
|
5
5
|
import { sizukuZod } from './generator/zod/index.js';
|
|
6
6
|
import { readFileSync } from './shared/fs/index.js';
|
|
7
|
-
export async function main(
|
|
8
|
-
|
|
7
|
+
export async function main() {
|
|
8
|
+
const configResult = await config();
|
|
9
|
+
if (!configResult.ok) {
|
|
9
10
|
return {
|
|
10
11
|
ok: false,
|
|
11
|
-
error:
|
|
12
|
+
error: configResult.error,
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
|
-
const
|
|
15
|
+
const c = configResult.value;
|
|
16
|
+
const contentResult = readFileSync(c.input);
|
|
15
17
|
if (!contentResult.ok) {
|
|
16
18
|
return {
|
|
17
19
|
ok: false,
|
|
@@ -24,37 +26,37 @@ export async function main(config = getConfig()) {
|
|
|
24
26
|
const code = lines.slice(codeStart);
|
|
25
27
|
const results = [];
|
|
26
28
|
/* zod */
|
|
27
|
-
if (
|
|
28
|
-
const zodResult = await sizukuZod(code,
|
|
29
|
+
if (c.zod?.output) {
|
|
30
|
+
const zodResult = await sizukuZod(code, c.zod.output, c.zod.comment, c.zod.type, c.zod.zod, c.zod.relation);
|
|
29
31
|
if (!zodResult.ok) {
|
|
30
32
|
return {
|
|
31
33
|
ok: false,
|
|
32
34
|
error: zodResult.error,
|
|
33
35
|
};
|
|
34
36
|
}
|
|
35
|
-
results.push(`Generated Zod schema at: ${
|
|
37
|
+
results.push(`Generated Zod schema at: ${c.zod?.output}`);
|
|
36
38
|
}
|
|
37
39
|
/* valibot */
|
|
38
|
-
if (
|
|
39
|
-
const valibotResult = await sizukuValibot(code,
|
|
40
|
+
if (c.valibot?.output) {
|
|
41
|
+
const valibotResult = await sizukuValibot(code, c.valibot.output, c.valibot.comment, c.valibot.type, c.valibot.relation);
|
|
40
42
|
if (!valibotResult.ok) {
|
|
41
43
|
return {
|
|
42
44
|
ok: false,
|
|
43
45
|
error: valibotResult.error,
|
|
44
46
|
};
|
|
45
47
|
}
|
|
46
|
-
results.push(`Generated Valibot schema at: ${
|
|
48
|
+
results.push(`Generated Valibot schema at: ${c.valibot?.output}`);
|
|
47
49
|
}
|
|
48
50
|
/* mermaid */
|
|
49
|
-
if (
|
|
50
|
-
const mermaidResult = await sizukuMermaidER(code,
|
|
51
|
+
if (c.mermaid?.output) {
|
|
52
|
+
const mermaidResult = await sizukuMermaidER(code, c.mermaid.output);
|
|
51
53
|
if (!mermaidResult.ok) {
|
|
52
54
|
return {
|
|
53
55
|
ok: false,
|
|
54
56
|
error: mermaidResult.error,
|
|
55
57
|
};
|
|
56
58
|
}
|
|
57
|
-
results.push(`Generated Mermaid ER at: ${
|
|
59
|
+
results.push(`Generated Mermaid ER at: ${c.mermaid?.output}`);
|
|
58
60
|
}
|
|
59
61
|
return {
|
|
60
62
|
ok: true,
|
|
@@ -5,11 +5,9 @@
|
|
|
5
5
|
* @returns A `Result` containing the formatted code or an error message.
|
|
6
6
|
*/
|
|
7
7
|
export declare function fmt(code: string): Promise<{
|
|
8
|
-
ok: true;
|
|
9
|
-
value: string;
|
|
10
|
-
error?: undefined;
|
|
8
|
+
readonly ok: true;
|
|
9
|
+
readonly value: string;
|
|
11
10
|
} | {
|
|
12
|
-
ok: false;
|
|
13
|
-
error: string;
|
|
14
|
-
value?: undefined;
|
|
11
|
+
readonly ok: false;
|
|
12
|
+
readonly error: string;
|
|
15
13
|
}>;
|
|
@@ -7,13 +7,13 @@ import { format } from 'prettier';
|
|
|
7
7
|
*/
|
|
8
8
|
export async function fmt(code) {
|
|
9
9
|
try {
|
|
10
|
-
const
|
|
10
|
+
const result = await format(code, {
|
|
11
11
|
parser: 'typescript',
|
|
12
12
|
printWidth: 100,
|
|
13
13
|
singleQuote: true,
|
|
14
14
|
semi: false,
|
|
15
15
|
});
|
|
16
|
-
return { ok: true, value:
|
|
16
|
+
return { ok: true, value: result };
|
|
17
17
|
}
|
|
18
18
|
catch (e) {
|
|
19
19
|
return {
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* @returns A `Result` that is `ok` on success, otherwise an error message.
|
|
6
6
|
*/
|
|
7
7
|
export declare function mkdir(dir: string): Promise<{
|
|
8
|
-
ok: false;
|
|
9
|
-
error: string;
|
|
8
|
+
readonly ok: false;
|
|
9
|
+
readonly error: string;
|
|
10
10
|
} | {
|
|
11
|
-
ok: true;
|
|
12
|
-
value: undefined;
|
|
11
|
+
readonly ok: true;
|
|
12
|
+
readonly value: undefined;
|
|
13
13
|
}>;
|
|
14
14
|
/**
|
|
15
15
|
* Writes UTF-8 text to a file, creating it if necessary.
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -48,14 +48,14 @@ export declare function removeAtSign(str: string): string;
|
|
|
48
48
|
* @param arr - Array of strings to join.
|
|
49
49
|
* @returns Joined string with spaces.
|
|
50
50
|
*/
|
|
51
|
-
export declare function joinWithSpace(arr: string[]): string;
|
|
51
|
+
export declare function joinWithSpace(arr: readonly string[]): string;
|
|
52
52
|
/**
|
|
53
53
|
* Split string by newline character.
|
|
54
54
|
*
|
|
55
55
|
* @param str - The input string.
|
|
56
56
|
* @returns Array of strings split by newline.
|
|
57
57
|
*/
|
|
58
|
-
export declare function splitByNewline(str: string): string[];
|
|
58
|
+
export declare function splitByNewline(str: string): readonly string[];
|
|
59
59
|
/**
|
|
60
60
|
* Trim whitespace from string.
|
|
61
61
|
*
|
|
@@ -96,14 +96,14 @@ export declare function removeOptionalSuffix(str: string): string;
|
|
|
96
96
|
* @param str - The input string.
|
|
97
97
|
* @returns Array of strings split by whitespace.
|
|
98
98
|
*/
|
|
99
|
-
export declare function splitByWhitespace(str: string): string[];
|
|
99
|
+
export declare function splitByWhitespace(str: string): readonly string[];
|
|
100
100
|
/**
|
|
101
101
|
* Split string by dot character.
|
|
102
102
|
*
|
|
103
103
|
* @param str - The input string.
|
|
104
104
|
* @returns Array of strings split by dot.
|
|
105
105
|
*/
|
|
106
|
-
export declare function splitByDot(str: string): string[];
|
|
106
|
+
export declare function splitByDot(str: string): readonly string[];
|
|
107
107
|
/**
|
|
108
108
|
* Parse field comments and extract definition line and description.
|
|
109
109
|
*
|
|
@@ -135,10 +135,10 @@ export declare function inferInput(name: string): string;
|
|
|
135
135
|
* @returns
|
|
136
136
|
*/
|
|
137
137
|
export declare function fieldDefinitions(schema: {
|
|
138
|
-
name: string;
|
|
139
|
-
fields: {
|
|
140
|
-
name: string;
|
|
141
|
-
definition: string;
|
|
142
|
-
description?: string;
|
|
138
|
+
readonly name: string;
|
|
139
|
+
readonly fields: {
|
|
140
|
+
readonly name: string;
|
|
141
|
+
readonly definition: string;
|
|
142
|
+
readonly description?: string;
|
|
143
143
|
}[];
|
|
144
144
|
}, comment: boolean): string;
|
package/dist/utils/index.js
CHANGED
|
@@ -73,19 +73,7 @@ export function joinWithSpace(arr) {
|
|
|
73
73
|
* @returns Array of strings split by newline.
|
|
74
74
|
*/
|
|
75
75
|
export function splitByNewline(str) {
|
|
76
|
-
|
|
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;
|
|
76
|
+
return str.split('\n');
|
|
89
77
|
}
|
|
90
78
|
/**
|
|
91
79
|
* Trim whitespace from string.
|
|
@@ -94,17 +82,7 @@ export function splitByNewline(str) {
|
|
|
94
82
|
* @returns Trimmed string.
|
|
95
83
|
*/
|
|
96
84
|
export function trimString(str) {
|
|
97
|
-
|
|
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);
|
|
85
|
+
return str.trim();
|
|
108
86
|
}
|
|
109
87
|
/**
|
|
110
88
|
* Parse relation line and extract components.
|
|
@@ -113,13 +91,13 @@ export function trimString(str) {
|
|
|
113
91
|
* @returns Parsed relation or null if not a relation line.
|
|
114
92
|
*/
|
|
115
93
|
export function parseRelationLine(line) {
|
|
116
|
-
if (!startsWith(
|
|
94
|
+
if (!line.startsWith('@relation'))
|
|
117
95
|
return null;
|
|
118
|
-
const parts =
|
|
96
|
+
const parts = line.trim().split(/\s+/);
|
|
119
97
|
if (parts.length < 5)
|
|
120
98
|
return null;
|
|
121
|
-
const fromParts =
|
|
122
|
-
const toParts =
|
|
99
|
+
const fromParts = parts[1].split('.');
|
|
100
|
+
const toParts = parts[2].split('.');
|
|
123
101
|
if (fromParts.length !== 2 || toParts.length !== 2)
|
|
124
102
|
return null;
|
|
125
103
|
return {
|
|
@@ -159,28 +137,10 @@ export function removeOptionalSuffix(str) {
|
|
|
159
137
|
* @returns Array of strings split by whitespace.
|
|
160
138
|
*/
|
|
161
139
|
export function splitByWhitespace(str) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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;
|
|
140
|
+
return str
|
|
141
|
+
.trim()
|
|
142
|
+
.split(/\s+/)
|
|
143
|
+
.filter((s) => s.length > 0);
|
|
184
144
|
}
|
|
185
145
|
/**
|
|
186
146
|
* Split string by dot character.
|
|
@@ -189,19 +149,7 @@ export function splitByWhitespace(str) {
|
|
|
189
149
|
* @returns Array of strings split by dot.
|
|
190
150
|
*/
|
|
191
151
|
export function splitByDot(str) {
|
|
192
|
-
|
|
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;
|
|
152
|
+
return str.split('.');
|
|
205
153
|
}
|
|
206
154
|
/* ========================================================================== *
|
|
207
155
|
* parse
|
|
@@ -214,24 +162,25 @@ export function splitByDot(str) {
|
|
|
214
162
|
* @returns Parsed definition and description
|
|
215
163
|
*/
|
|
216
164
|
export function parseFieldComments(commentLines, tag) {
|
|
217
|
-
const cleaned = commentLines
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const objectType = objectTypeLine
|
|
222
|
-
? '
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const
|
|
165
|
+
const cleaned = commentLines
|
|
166
|
+
.map((line) => (line.startsWith('///') ? line.substring(3) : line).trim())
|
|
167
|
+
.filter((line) => line.length > 0);
|
|
168
|
+
const objectTypeLine = cleaned.find((line) => line.includes(`${tag.slice(1)}strictObject`) || line.includes(`${tag.slice(1)}looseObject`));
|
|
169
|
+
const objectType = objectTypeLine
|
|
170
|
+
? objectTypeLine.includes('strictObject')
|
|
171
|
+
? 'strict'
|
|
172
|
+
: objectTypeLine.includes('looseObject')
|
|
173
|
+
? 'loose'
|
|
174
|
+
: undefined
|
|
175
|
+
: undefined;
|
|
176
|
+
const definitionLine = cleaned.find((line) => line.startsWith(tag) && !line.includes('strictObject') && !line.includes('looseObject'));
|
|
177
|
+
const definition = definitionLine
|
|
178
|
+
? definitionLine.startsWith('@')
|
|
179
|
+
? definitionLine.substring(1)
|
|
180
|
+
: definitionLine
|
|
181
|
+
: '';
|
|
182
|
+
const descriptionLines = cleaned.filter((line) => !(line.includes('@z.') || line.includes('@v.') || line.includes('@relation.')));
|
|
183
|
+
const description = descriptionLines.length > 0 ? descriptionLines.join(' ') : undefined;
|
|
235
184
|
return { definition, description, objectType };
|
|
236
185
|
}
|
|
237
186
|
/* ========================================================================== *
|
|
@@ -246,14 +195,14 @@ export function parseFieldComments(commentLines, tag) {
|
|
|
246
195
|
*/
|
|
247
196
|
export function extractFieldComments(sourceText, fieldStartPos) {
|
|
248
197
|
const beforeField = sourceText.substring(0, fieldStartPos);
|
|
249
|
-
const lines =
|
|
198
|
+
const lines = beforeField.split('\n');
|
|
250
199
|
const reverseIndex = lines
|
|
251
|
-
.map((line, index) => ({ line:
|
|
200
|
+
.map((line, index) => ({ line: line.trim(), index }))
|
|
252
201
|
.reverse()
|
|
253
202
|
.reduce((acc, { line }) => {
|
|
254
203
|
if (acc.shouldStop)
|
|
255
204
|
return acc;
|
|
256
|
-
if (startsWith(
|
|
205
|
+
if (line.startsWith('///')) {
|
|
257
206
|
return {
|
|
258
207
|
commentLines: [line, ...acc.commentLines],
|
|
259
208
|
shouldStop: false,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sizuku",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.1",
|
|
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": [
|
|
@@ -29,6 +29,12 @@
|
|
|
29
29
|
"bin": {
|
|
30
30
|
"sizuku": "dist/index.js"
|
|
31
31
|
},
|
|
32
|
+
"exports": {
|
|
33
|
+
"./config": {
|
|
34
|
+
"types": "./dist/config/index.d.ts",
|
|
35
|
+
"import": "./dist/config/index.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
32
38
|
"scripts": {
|
|
33
39
|
"deps": "rm -rf node_modules && pnpm install",
|
|
34
40
|
"build": "tsc",
|
|
@@ -48,6 +54,7 @@
|
|
|
48
54
|
},
|
|
49
55
|
"dependencies": {
|
|
50
56
|
"prettier": "^3.6.2",
|
|
51
|
-
"ts-morph": "^26.0.0"
|
|
57
|
+
"ts-morph": "^26.0.0",
|
|
58
|
+
"tsx": "^4.20.5"
|
|
52
59
|
}
|
|
53
60
|
}
|