@zod-to-form/cli 0.2.1 → 0.2.3

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.
@@ -0,0 +1,11 @@
1
+ import type { FormField } from '@zod-to-form/core';
2
+ export type CodegenConfig = {
3
+ schemaPath: string;
4
+ exportName: string;
5
+ outputPath: string;
6
+ componentName: string;
7
+ ui: 'shadcn' | 'unstyled';
8
+ serverAction: boolean;
9
+ };
10
+ export declare function generateFormComponent(fields: FormField[], config: CodegenConfig): Promise<string>;
11
+ //# sourceMappingURL=codegen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGnD,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,EAAE,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAuGF,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EAAE,EACnB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,MAAM,CAAC,CAuCjB"}
@@ -0,0 +1,125 @@
1
+ import path from 'node:path';
2
+ import { getFileHeader, renderField } from './templates.js';
3
+ function getSchemaImportPath(config) {
4
+ const relative = path
5
+ .relative(path.dirname(config.outputPath), config.schemaPath)
6
+ .replace(/\\/g, '/');
7
+ if (relative.startsWith('.')) {
8
+ return relative;
9
+ }
10
+ return `./${relative}`;
11
+ }
12
+ /** Convert a field key to a safe camelCase variable prefix (e.g. 'address.street' → 'addressStreet') */
13
+ function toVarName(key) {
14
+ return key.replace(/[^a-zA-Z0-9]+([a-zA-Z0-9])/g, (_, c) => c.toUpperCase());
15
+ }
16
+ /** Collect all array fields (recursively through nested objects) */
17
+ function collectArrayFields(fields) {
18
+ const result = [];
19
+ for (const field of fields) {
20
+ if (field.component === 'ArrayField') {
21
+ result.push(field);
22
+ }
23
+ if (field.component === 'Fieldset' && field.children) {
24
+ result.push(...collectArrayFields(field.children));
25
+ }
26
+ }
27
+ return result;
28
+ }
29
+ function renderNestedBlock(field, indent) {
30
+ const children = (field.children ?? [])
31
+ .map((child) => renderFieldBlock(child, `${indent} `))
32
+ .join('\n');
33
+ return [
34
+ `${indent}<div>`,
35
+ `${indent} <label>${field.label}</label>`,
36
+ `${indent} <fieldset>`,
37
+ `${indent} <legend>${field.label}</legend>`,
38
+ children,
39
+ `${indent} </fieldset>`,
40
+ `${indent}</div>`
41
+ ].join('\n');
42
+ }
43
+ function renderArrayBlock(field, indent) {
44
+ const varName = toVarName(field.key);
45
+ const itemField = field.arrayItem;
46
+ const itemJsx = itemField
47
+ ? renderField({ ...itemField, key: `\${${varName}Fields[index].id}` })
48
+ : `<input {...register(\`${field.key}.\${index}\`)} />`;
49
+ return [
50
+ `${indent}<div>`,
51
+ `${indent} <label>${field.label}</label>`,
52
+ `${indent} {${varName}Fields.map((item, index) => (`,
53
+ `${indent} <div key={item.id}>`,
54
+ `${indent} ${itemJsx.replace(new RegExp(`register\\('${field.key}\\.0'\\)`), `register(\`${field.key}.\${index}\`)`)}`,
55
+ `${indent} <button type="button" onClick={() => remove${capitalize(varName)}(index)}>Remove</button>`,
56
+ `${indent} </div>`,
57
+ `${indent} ))}`,
58
+ `${indent} <button type="button" onClick={() => append${capitalize(varName)}('')}>Add</button>`,
59
+ `${indent}</div>`
60
+ ].join('\n');
61
+ }
62
+ function capitalize(s) {
63
+ return s.charAt(0).toUpperCase() + s.slice(1);
64
+ }
65
+ function renderFieldBlock(field, indent = ' ') {
66
+ if (field.hasCustomRender) {
67
+ const styleAttr = field.gridColumn ? ` style={{ gridColumn: '${field.gridColumn}' }}` : '';
68
+ return [
69
+ `${indent}<div${styleAttr}>`,
70
+ `${indent} <label htmlFor="${field.key}">${field.label}</label>`,
71
+ `${indent} {/* TODO: custom renderer for ${field.key} — replace with your component */}`,
72
+ `${indent}</div>`
73
+ ].join('\n');
74
+ }
75
+ if (field.component === 'Fieldset') {
76
+ return renderNestedBlock(field, indent);
77
+ }
78
+ if (field.component === 'ArrayField') {
79
+ return renderArrayBlock(field, indent);
80
+ }
81
+ const styleAttr = field.gridColumn ? ` style={{ gridColumn: '${field.gridColumn}' }}` : '';
82
+ return [
83
+ `${indent}<div${styleAttr}>`,
84
+ `${indent} <label htmlFor="${field.key}">${field.label}</label>`,
85
+ `${indent} ${renderField(field)}`,
86
+ `${indent}</div>`
87
+ ].join('\n');
88
+ }
89
+ export async function generateFormComponent(fields, config) {
90
+ const schemaImportPath = getSchemaImportPath(config);
91
+ const arrayFields = collectArrayFields(fields);
92
+ const hasArrays = arrayFields.length > 0;
93
+ const header = getFileHeader(schemaImportPath, config.exportName, hasArrays);
94
+ const body = fields.map((field) => renderFieldBlock(field)).join('\n');
95
+ // useFieldArray hook declarations
96
+ const arrayHooks = arrayFields
97
+ .map((f) => {
98
+ const varName = toVarName(f.key);
99
+ return ` const { fields: ${varName}Fields, append: append${capitalize(varName)}, remove: remove${capitalize(varName)} } = useFieldArray({ control, name: '${f.key}' });`;
100
+ })
101
+ .join('\n');
102
+ const useFormDestructure = hasArrays
103
+ ? `{ register, handleSubmit, control }`
104
+ : `{ register, handleSubmit }`;
105
+ return [
106
+ header,
107
+ '',
108
+ `export function ${config.componentName}(props: {`,
109
+ ` onSubmit: (data: FormData) => void;`,
110
+ `}) {`,
111
+ ` const ${useFormDestructure} = useForm<FormData>({`,
112
+ ` resolver: zodResolver(${config.exportName})`,
113
+ ` });`,
114
+ ...(hasArrays ? [arrayHooks] : []),
115
+ '',
116
+ ` return (`,
117
+ ` <form onSubmit={handleSubmit(props.onSubmit)}>`,
118
+ body,
119
+ ` <button type="submit">Submit</button>`,
120
+ ` </form>`,
121
+ ` );`,
122
+ `}`
123
+ ].join('\n');
124
+ }
125
+ //# sourceMappingURL=codegen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen.js","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @zod-to-form/cli — Build-time code generator for Zod v4 forms
4
+ */
5
+ import { Command } from 'commander';
6
+ type GenerateOptions = {
7
+ schema: string;
8
+ export: string;
9
+ out?: string;
10
+ name?: string;
11
+ ui?: 'shadcn' | 'unstyled';
12
+ force?: boolean;
13
+ dryRun?: boolean;
14
+ serverAction?: boolean;
15
+ watch?: boolean;
16
+ };
17
+ export declare function runGenerate(options: GenerateOptions): Promise<{
18
+ outputPath: string;
19
+ code: string;
20
+ wroteFile: boolean;
21
+ actionPath?: string;
22
+ actionCode?: string;
23
+ }>;
24
+ export declare function createProgram(): Command;
25
+ export {};
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;GAEG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAwCF,wBAAsB,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC,CA8CD;AAED,wBAAgB,aAAa,IAAI,OAAO,CAgCvC"}
package/dist/index.js ADDED
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @zod-to-form/cli — Build-time code generator for Zod v4 forms
4
+ */
5
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
6
+ import path from 'node:path';
7
+ import { Command } from 'commander';
8
+ import { walkSchema } from '@zod-to-form/core';
9
+ import { generateFormComponent } from './codegen.js';
10
+ import { loadSchema } from './loader.js';
11
+ import { generateServerAction } from './server-action.js';
12
+ import { startWatch } from './watcher.js';
13
+ function toPascalCase(value) {
14
+ return value
15
+ .replace(/[^a-zA-Z0-9]+/g, ' ')
16
+ .trim()
17
+ .split(/\s+/)
18
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
19
+ .join('');
20
+ }
21
+ function toKebabCase(value) {
22
+ return value.replace(/([A-Z])/g, (m) => `-${m.toLowerCase()}`).replace(/^-/, '');
23
+ }
24
+ function resolveComponentName(exportName, explicitName) {
25
+ if (explicitName?.trim()) {
26
+ return toPascalCase(explicitName.trim());
27
+ }
28
+ const normalized = exportName.endsWith('Schema')
29
+ ? exportName.slice(0, -'Schema'.length)
30
+ : exportName;
31
+ return `${toPascalCase(normalized)}Form`;
32
+ }
33
+ function resolveOutputPath(cwd, out, componentName) {
34
+ if (!out) {
35
+ return path.resolve(cwd, `${componentName}.tsx`);
36
+ }
37
+ const absoluteOut = path.resolve(cwd, out);
38
+ if (absoluteOut.endsWith('.tsx')) {
39
+ return absoluteOut;
40
+ }
41
+ return path.join(absoluteOut, `${componentName}.tsx`);
42
+ }
43
+ export async function runGenerate(options) {
44
+ const cwd = process.cwd();
45
+ const schemaPath = path.resolve(cwd, options.schema);
46
+ const exportName = options.export;
47
+ const componentName = resolveComponentName(exportName, options.name);
48
+ const outputPath = resolveOutputPath(cwd, options.out, componentName);
49
+ const schema = await loadSchema(schemaPath, exportName);
50
+ const fields = walkSchema(schema);
51
+ const config = {
52
+ schemaPath,
53
+ exportName,
54
+ outputPath,
55
+ componentName,
56
+ ui: options.ui ?? 'shadcn',
57
+ serverAction: options.serverAction ?? false
58
+ };
59
+ const generated = await generateFormComponent(fields, config);
60
+ const code = generated;
61
+ if (options.dryRun) {
62
+ process.stdout.write(code);
63
+ return { outputPath, code, wroteFile: false };
64
+ }
65
+ try {
66
+ await readFile(outputPath, 'utf8');
67
+ if (!options.force) {
68
+ return { outputPath, code, wroteFile: false };
69
+ }
70
+ }
71
+ catch { }
72
+ await mkdir(path.dirname(outputPath), { recursive: true });
73
+ await writeFile(outputPath, code, 'utf8');
74
+ // Generate server action alongside the form component when requested
75
+ if (options.serverAction) {
76
+ const actionFileName = `${toKebabCase(componentName)}-action.ts`;
77
+ const actionPath = path.join(path.dirname(outputPath), actionFileName);
78
+ const actionCode = await generateServerAction({ ...config, outputPath: actionPath });
79
+ await writeFile(actionPath, actionCode, 'utf8');
80
+ return { outputPath, code, wroteFile: true, actionPath, actionCode };
81
+ }
82
+ return { outputPath, code, wroteFile: true };
83
+ }
84
+ export function createProgram() {
85
+ const program = new Command();
86
+ program
87
+ .name('zodform')
88
+ .description('Generate form components from Zod v4 schemas')
89
+ .version('0.0.0');
90
+ program
91
+ .command('generate')
92
+ .requiredOption('--schema <path>', 'Path to schema file')
93
+ .requiredOption('--export <name>', 'Named export containing the schema')
94
+ .option('--out <path>', 'Output directory or file path')
95
+ .option('--name <componentName>', 'Generated component name')
96
+ .option('--ui <preset>', 'UI preset (shadcn|unstyled)', 'shadcn')
97
+ .option('--force', 'Overwrite existing output file', false)
98
+ .option('--dry-run', 'Print generated code without writing files', false)
99
+ .option('--server-action', 'Generate a Next.js server action alongside the form', false)
100
+ .option('--watch', 'Watch schema file for changes and regenerate on change', false)
101
+ .action(async (commandOptions) => {
102
+ await runGenerate(commandOptions);
103
+ if (commandOptions.watch) {
104
+ const schemaPath = path.resolve(process.cwd(), commandOptions.schema);
105
+ console.log('Watching for changes...');
106
+ await startWatch(schemaPath, () => runGenerate({ ...commandOptions, force: true }).then(() => { }));
107
+ }
108
+ });
109
+ return program;
110
+ }
111
+ if (import.meta.url === `file://${process.argv[1]}`) {
112
+ const program = createProgram();
113
+ await program.parseAsync();
114
+ }
115
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function loadSchema(schemaPath: string, exportName: string): Promise<unknown>;
2
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAWA,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAyBzF"}
package/dist/loader.js ADDED
@@ -0,0 +1,32 @@
1
+ import path from 'node:path';
2
+ import { createJiti } from 'jiti';
3
+ function isZodSchema(value) {
4
+ if (!value || typeof value !== 'object') {
5
+ return false;
6
+ }
7
+ return '_zod' in value;
8
+ }
9
+ export async function loadSchema(schemaPath, exportName) {
10
+ const absolutePath = path.resolve(schemaPath);
11
+ const jiti = createJiti(import.meta.url, {
12
+ moduleCache: false,
13
+ interopDefault: true
14
+ });
15
+ let moduleExports;
16
+ try {
17
+ moduleExports = await jiti.import(absolutePath);
18
+ }
19
+ catch (error) {
20
+ const message = error instanceof Error ? error.message : String(error);
21
+ throw new Error(`Unable to load schema file "${absolutePath}": ${message}`);
22
+ }
23
+ if (!(exportName in moduleExports)) {
24
+ throw new Error(`Export "${exportName}" was not found in schema file "${absolutePath}".`);
25
+ }
26
+ const candidate = moduleExports[exportName];
27
+ if (!isZodSchema(candidate)) {
28
+ throw new Error(`Export "${exportName}" from "${absolutePath}" is not a Zod schema.`);
29
+ }
30
+ return candidate;
31
+ }
32
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAElC,SAAS,WAAW,CAAC,KAAc,EAAW;IAC5C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,MAAM,IAAK,KAAiC,CAAC;AAAA,CACrD;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAAkB,EAAE,UAAkB,EAAoB;IACzF,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,GAAG,EAAE;QACvC,WAAW,EAAE,KAAK;QAClB,cAAc,EAAE,IAAI;KACrB,CAAC,CAAC;IAEH,IAAI,aAAsC,CAAC;IAC3C,IAAI,CAAC;QACH,aAAa,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,+BAA+B,YAAY,MAAM,OAAO,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,IAAI,CAAC,CAAC,UAAU,IAAI,aAAa,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,WAAW,UAAU,mCAAmC,YAAY,IAAI,CAAC,CAAC;IAC5F,CAAC;IAED,MAAM,SAAS,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,WAAW,UAAU,WAAW,YAAY,wBAAwB,CAAC,CAAC;IACxF,CAAC;IAED,OAAO,SAAS,CAAC;AAAA,CAClB"}
@@ -0,0 +1,13 @@
1
+ import type { CodegenConfig } from './codegen.js';
2
+ /**
3
+ * Generate a Next.js server action TypeScript file from the codegen config.
4
+ *
5
+ * The generated file:
6
+ * - Has a `"use server"` directive
7
+ * - Imports the Zod schema (no @zod-to-form/* imports)
8
+ * - Declares a typed FormState type
9
+ * - Exports an async action that validates formData via safeParse
10
+ * - Returns fieldErrors on validation failure or a success message on pass
11
+ */
12
+ export declare function generateServerAction(config: CodegenConfig): Promise<string>;
13
+ //# sourceMappingURL=server-action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-action.d.ts","sourceRoot":"","sources":["../src/server-action.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAkBlD;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA+BjF"}
@@ -0,0 +1,57 @@
1
+ import path from 'node:path';
2
+ /**
3
+ * Compute the relative import path from the action file to the schema file.
4
+ * The action file sits alongside the form component (same directory).
5
+ */
6
+ function schemaImportPath(config) {
7
+ const rel = path.relative(path.dirname(config.outputPath), config.schemaPath).replace(/\\/g, '/');
8
+ const withSlash = rel.startsWith('.') ? rel : `./${rel}`;
9
+ // Replace .ts extension with .js for ESM compatibility
10
+ return withSlash.replace(/\.ts$/, '.js');
11
+ }
12
+ /** Derive camelCase action function name from PascalCase component name */
13
+ function toActionName(componentName) {
14
+ return componentName.charAt(0).toLowerCase() + componentName.slice(1) + 'Action';
15
+ }
16
+ /**
17
+ * Generate a Next.js server action TypeScript file from the codegen config.
18
+ *
19
+ * The generated file:
20
+ * - Has a `"use server"` directive
21
+ * - Imports the Zod schema (no @zod-to-form/* imports)
22
+ * - Declares a typed FormState type
23
+ * - Exports an async action that validates formData via safeParse
24
+ * - Returns fieldErrors on validation failure or a success message on pass
25
+ */
26
+ export async function generateServerAction(config) {
27
+ const { exportName, componentName } = config;
28
+ const importPath = schemaImportPath(config);
29
+ const stateName = `${componentName}State`;
30
+ const actionName = toActionName(componentName);
31
+ return [
32
+ '"use server";',
33
+ '',
34
+ `import { ${exportName} } from '${importPath}';`,
35
+ '',
36
+ `export type ${stateName} = {`,
37
+ ` errors: Partial<Record<string, string[]>>;`,
38
+ ` message: string | null;`,
39
+ `};`,
40
+ '',
41
+ `export async function ${actionName}(`,
42
+ ` _prevState: ${stateName},`,
43
+ ` formData: FormData`,
44
+ `): Promise<${stateName}> {`,
45
+ ` const result = ${exportName}.safeParse(Object.fromEntries(formData));`,
46
+ '',
47
+ ` if (!result.success) {`,
48
+ ` const fieldErrors = result.error.flatten().fieldErrors;`,
49
+ ` return { errors: fieldErrors, message: null };`,
50
+ ` }`,
51
+ '',
52
+ ` return { errors: {}, message: 'Form submitted successfully.' };`,
53
+ `}`,
54
+ ''
55
+ ].join('\n');
56
+ }
57
+ //# sourceMappingURL=server-action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-action.js","sourceRoot":"","sources":["../src/server-action.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B;;;GAGG;AACH,SAAS,gBAAgB,CAAC,MAAqB,EAAU;IACvD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAClG,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;IACzD,uDAAuD;IACvD,OAAO,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAAA,CAC1C;AAED,2EAA2E;AAC3E,SAAS,YAAY,CAAC,aAAqB,EAAU;IACnD,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC;AAAA,CAClF;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAqB,EAAmB;IACjF,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC;IAC7C,MAAM,UAAU,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,GAAG,aAAa,OAAO,CAAC;IAC1C,MAAM,UAAU,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;IAE/C,OAAO;QACL,eAAe;QACf,EAAE;QACF,YAAY,UAAU,YAAY,UAAU,IAAI;QAChD,EAAE;QACF,eAAe,SAAS,MAAM;QAC9B,8CAA8C;QAC9C,2BAA2B;QAC3B,IAAI;QACJ,EAAE;QACF,yBAAyB,UAAU,GAAG;QACtC,iBAAiB,SAAS,GAAG;QAC7B,sBAAsB;QACtB,cAAc,SAAS,KAAK;QAC5B,oBAAoB,UAAU,2CAA2C;QACzE,EAAE;QACF,0BAA0B;QAC1B,6DAA6D;QAC7D,oDAAoD;QACpD,KAAK;QACL,EAAE;QACF,mEAAmE;QACnE,GAAG;QACH,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CACd"}
@@ -0,0 +1,4 @@
1
+ import type { FormField } from '@zod-to-form/core';
2
+ export declare function getFileHeader(schemaImportPath: string, exportName: string, hasArrays?: boolean): string;
3
+ export declare function renderField(field: FormField): string;
4
+ //# sourceMappingURL=templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,wBAAgB,aAAa,CAC3B,gBAAgB,EAAE,MAAM,EACxB,UAAU,EAAE,MAAM,EAClB,SAAS,UAAQ,GAChB,MAAM,CAYR;AA2BD,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAiBpD"}
@@ -0,0 +1,50 @@
1
+ export function getFileHeader(schemaImportPath, exportName, hasArrays = false) {
2
+ const rhfImports = hasArrays
3
+ ? `import { useForm, useFieldArray } from 'react-hook-form';`
4
+ : `import { useForm } from 'react-hook-form';`;
5
+ return [
6
+ rhfImports,
7
+ `import { zodResolver } from '@hookform/resolvers/zod';`,
8
+ `import { ${exportName} } from '${schemaImportPath}';`,
9
+ ``,
10
+ `type FormData = (typeof ${exportName})['_zod']['output'];`
11
+ ].join('\n');
12
+ }
13
+ function renderInput(field) {
14
+ const inputType = typeof field.props['type'] === 'string' ? field.props['type'] : 'text';
15
+ return `<input id="${field.key}" type="${inputType}" {...register('${field.key}')} />`;
16
+ }
17
+ function renderCheckbox(field) {
18
+ return `<input id="${field.key}" type="checkbox" {...register('${field.key}')} />`;
19
+ }
20
+ function renderDatePicker(field) {
21
+ return `<input id="${field.key}" type="date" {...register('${field.key}', { valueAsDate: true })} />`;
22
+ }
23
+ function renderFileInput(field) {
24
+ return `<input id="${field.key}" type="file" {...register('${field.key}')} />`;
25
+ }
26
+ function renderSelect(field) {
27
+ const options = (field.options ?? [])
28
+ .map((option) => `<option value="${String(option.value)}">${option.label}</option>`)
29
+ .join('');
30
+ return `<select id="${field.key}" {...register('${field.key}')}>${options}</select>`;
31
+ }
32
+ export function renderField(field) {
33
+ switch (field.component) {
34
+ case 'Checkbox':
35
+ case 'Switch':
36
+ return renderCheckbox(field);
37
+ case 'DatePicker':
38
+ return renderDatePicker(field);
39
+ case 'FileInput':
40
+ return renderFileInput(field);
41
+ case 'Select':
42
+ case 'RadioGroup':
43
+ return renderSelect(field);
44
+ case 'Textarea':
45
+ return `<textarea id="${field.key}" {...register('${field.key}')} />`;
46
+ default:
47
+ return renderInput(field);
48
+ }
49
+ }
50
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa,CAC3B,gBAAwB,EACxB,UAAkB,EAClB,SAAS,GAAG,KAAK,EACT;IACR,MAAM,UAAU,GAAG,SAAS;QAC1B,CAAC,CAAC,2DAA2D;QAC7D,CAAC,CAAC,4CAA4C,CAAC;IAEjD,OAAO;QACL,UAAU;QACV,wDAAwD;QACxD,YAAY,UAAU,YAAY,gBAAgB,IAAI;QACtD,EAAE;QACF,2BAA2B,UAAU,sBAAsB;KAC5D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CACd;AAED,SAAS,WAAW,CAAC,KAAgB,EAAU;IAC7C,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACzF,OAAO,cAAc,KAAK,CAAC,GAAG,WAAW,SAAS,mBAAmB,KAAK,CAAC,GAAG,QAAQ,CAAC;AAAA,CACxF;AAED,SAAS,cAAc,CAAC,KAAgB,EAAU;IAChD,OAAO,cAAc,KAAK,CAAC,GAAG,mCAAmC,KAAK,CAAC,GAAG,QAAQ,CAAC;AAAA,CACpF;AAED,SAAS,gBAAgB,CAAC,KAAgB,EAAU;IAClD,OAAO,cAAc,KAAK,CAAC,GAAG,+BAA+B,KAAK,CAAC,GAAG,+BAA+B,CAAC;AAAA,CACvG;AAED,SAAS,eAAe,CAAC,KAAgB,EAAU;IACjD,OAAO,cAAc,KAAK,CAAC,GAAG,+BAA+B,KAAK,CAAC,GAAG,QAAQ,CAAC;AAAA,CAChF;AAED,SAAS,YAAY,CAAC,KAAgB,EAAU;IAC9C,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;SAClC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,kBAAkB,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,KAAK,WAAW,CAAC;SACnF,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO,eAAe,KAAK,CAAC,GAAG,mBAAmB,KAAK,CAAC,GAAG,OAAO,OAAO,WAAW,CAAC;AAAA,CACtF;AAED,MAAM,UAAU,WAAW,CAAC,KAAgB,EAAU;IACpD,QAAQ,KAAK,CAAC,SAAS,EAAE,CAAC;QACxB,KAAK,UAAU,CAAC;QAChB,KAAK,QAAQ;YACX,OAAO,cAAc,CAAC,KAAK,CAAC,CAAC;QAC/B,KAAK,YAAY;YACf,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACjC,KAAK,WAAW;YACd,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAChC,KAAK,QAAQ,CAAC;QACd,KAAK,YAAY;YACf,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;QAC7B,KAAK,UAAU;YACb,OAAO,iBAAiB,KAAK,CAAC,GAAG,mBAAmB,KAAK,CAAC,GAAG,QAAQ,CAAC;QACxE;YACE,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;AAAA,CACF"}
@@ -0,0 +1,11 @@
1
+ import { type FSWatcher } from 'chokidar';
2
+ /**
3
+ * Start watching a schema file for changes.
4
+ * Debounces rapid changes by 200ms before calling regenerate().
5
+ *
6
+ * @param schemaPath - Absolute path to the schema file to watch
7
+ * @param regenerate - Async callback to invoke after a debounced change event
8
+ * @returns The chokidar FSWatcher instance (with patched close() for state tracking)
9
+ */
10
+ export declare function startWatch(schemaPath: string, regenerate: () => Promise<void>): Promise<FSWatcher>;
11
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAIlE;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAC9B,OAAO,CAAC,SAAS,CAAC,CA6CpB"}
@@ -0,0 +1,51 @@
1
+ import { watch as chokidarWatch } from 'chokidar';
2
+ const DEBOUNCE_MS = 200;
3
+ /**
4
+ * Start watching a schema file for changes.
5
+ * Debounces rapid changes by 200ms before calling regenerate().
6
+ *
7
+ * @param schemaPath - Absolute path to the schema file to watch
8
+ * @param regenerate - Async callback to invoke after a debounced change event
9
+ * @returns The chokidar FSWatcher instance (with patched close() for state tracking)
10
+ */
11
+ export async function startWatch(schemaPath, regenerate) {
12
+ let debounceTimer = null;
13
+ let closed = false;
14
+ const watcher = chokidarWatch(schemaPath, {
15
+ persistent: true,
16
+ ignoreInitial: true
17
+ });
18
+ watcher.on('change', (file) => {
19
+ if (closed)
20
+ return;
21
+ console.log(`Change detected: ${file}`);
22
+ if (debounceTimer !== null) {
23
+ clearTimeout(debounceTimer);
24
+ }
25
+ debounceTimer = setTimeout(() => {
26
+ debounceTimer = null;
27
+ if (closed)
28
+ return;
29
+ regenerate()
30
+ .then(() => {
31
+ console.log('Regeneration complete.');
32
+ })
33
+ .catch((err) => {
34
+ console.error('Regeneration failed:', err);
35
+ });
36
+ }, DEBOUNCE_MS);
37
+ });
38
+ // Patch close() to set the closed flag and clear any pending debounce
39
+ const nativeClose = watcher.close.bind(watcher);
40
+ // eslint-disable-next-line @typescript-eslint/require-await -- wrapping native close for flag tracking
41
+ watcher.close = async () => {
42
+ closed = true;
43
+ if (debounceTimer !== null) {
44
+ clearTimeout(debounceTimer);
45
+ debounceTimer = null;
46
+ }
47
+ return nativeClose();
48
+ };
49
+ return watcher;
50
+ }
51
+ //# sourceMappingURL=watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,aAAa,EAAkB,MAAM,UAAU,CAAC;AAElE,MAAM,WAAW,GAAG,GAAG,CAAC;AAExB;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,UAAkB,EAClB,UAA+B,EACX;IACpB,IAAI,aAAa,GAAyC,IAAI,CAAC;IAC/D,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,MAAM,OAAO,GAAG,aAAa,CAAC,UAAU,EAAE;QACxC,UAAU,EAAE,IAAI;QAChB,aAAa,EAAE,IAAI;KACpB,CAAC,CAAC;IAEH,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC;QACrC,IAAI,MAAM;YAAE,OAAO;QAEnB,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,EAAE,CAAC,CAAC;QAExC,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;YAC3B,YAAY,CAAC,aAAa,CAAC,CAAC;QAC9B,CAAC;QAED,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC;YAC/B,aAAa,GAAG,IAAI,CAAC;YACrB,IAAI,MAAM;gBAAE,OAAO;YAEnB,UAAU,EAAE;iBACT,IAAI,CAAC,GAAG,EAAE,CAAC;gBACV,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;YAAA,CACvC,CAAC;iBACD,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC;gBACvB,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC;YAAA,CAC5C,CAAC,CAAC;QAAA,CACN,EAAE,WAAW,CAAC,CAAC;IAAA,CACjB,CAAC,CAAC;IAEH,sEAAsE;IACtE,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChD,uGAAuG;IACvG,OAAO,CAAC,KAAK,GAAG,KAAK,IAAmB,EAAE,CAAC;QACzC,MAAM,GAAG,IAAI,CAAC;QACd,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;YAC3B,YAAY,CAAC,aAAa,CAAC,CAAC;YAC5B,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC;QACD,OAAO,WAAW,EAAE,CAAC;IAAA,CACtB,CAAC;IAEF,OAAO,OAAO,CAAC;AAAA,CAChB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zod-to-form/cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Build-time code generator for Zod v4 form components",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/pradeepmouli/zodforms#readme",
@@ -36,14 +36,14 @@
36
36
  "chokidar": "^5.0.0",
37
37
  "commander": "^14.0.3",
38
38
  "jiti": "^2.6.1",
39
- "@zod-to-form/core": "0.2.1"
39
+ "@zod-to-form/core": "0.2.3"
40
40
  },
41
41
  "devDependencies": {
42
42
  "zod": "^4.3.6"
43
43
  },
44
44
  "scripts": {
45
45
  "build": "tsgo -p tsconfig.build.json",
46
- "clean": "rm -rf dist",
46
+ "clean": "rm -rf dist tsconfig.build.tsbuildinfo tsconfig.tsbuildinfo",
47
47
  "dev": "tsgo -p tsconfig.build.json --watch",
48
48
  "test": "vitest run",
49
49
  "test:coverage": "vitest run --coverage",