@rafaelsilvadeveloper/zod-meta-form 1.0.2

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 ADDED
@@ -0,0 +1,99 @@
1
+ # @rafaelsilvadeveloper/zod-meta-form
2
+
3
+ A framework-agnostic generator of Form UI metadata from Zod schemas, converting schemas into dynamic, client-ready forms.
4
+
5
+ [![NPM Version](https://img.shields.io/npm/v/@rafaelsilvadeveloper/zod-meta-form.svg?style=flat-square)](https://www.npmjs.com/package/@rafaelsilvadeveloper/zod-meta-form)
6
+ [![Discord Support](https://img.shields.io/discord/1111111111?color=7289da&label=Discord&logo=discord&style=flat-square)](https://discord.gg/7Fw7snafYS)
7
+ [![Zero Dependencies](https://img.shields.io/badge/dependencies-zero-blueviolet.svg?style=flat-square)](https://www.npmjs.com/package/@rafaelsilvadeveloper/zod-meta-form)
8
+
9
+ ## Features
10
+
11
+ * 🎯 **Framework Agnostic**: Outputs pure JSON-serializable UI form trees. Works with React, Vue, Svelte, Angular, or Vanilla JS.
12
+ * 🔄 **Recursive Parsing**: Deeply inspects nested `ZodObject` and `ZodArray` schemas.
13
+ * 🏷️ **Auto-Label Generation**: Automatically converts camelCase, snake_case, and kebab-case field names into clean, readable Title Case labels.
14
+ * 🛠️ **Zod Constraint Mapping**: Translates string and number constraints (email, url, uuid, min, max, regex) into form validation rules.
15
+ * 🎛️ **Supports Enums**: Converts Zod `enum` and `nativeEnum` options directly into dropdown options lists.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @rafaelsilvadeveloper/zod-meta-form zod
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ```typescript
26
+ import { z } from 'zod';
27
+ import { zodToFormMeta } from '@rafaelsilvadeveloper/zod-meta-form';
28
+
29
+ // 1. Define your Zod schema
30
+ const signupSchema = z.object({
31
+ firstName: z.string().min(2).describe('Your first name'),
32
+ email: z.string().email(),
33
+ role: z.enum(['developer', 'designer', 'manager']).default('developer'),
34
+ subscribeToNewsletter: z.boolean().optional(),
35
+ });
36
+
37
+ // 2. Generate form UI metadata
38
+ const formMeta = zodToFormMeta(signupSchema);
39
+
40
+ console.log(JSON.stringify(formMeta, null, 2));
41
+ ```
42
+
43
+ ### Output Form Metadata Structure
44
+
45
+ ```json
46
+ {
47
+ "type": "object",
48
+ "label": "",
49
+ "required": true,
50
+ "fields": {
51
+ "firstName": {
52
+ "type": "text",
53
+ "label": "First Name",
54
+ "required": true,
55
+ "description": "Your first name",
56
+ "validations": {
57
+ "min": { "value": 2 }
58
+ }
59
+ },
60
+ "email": {
61
+ "type": "email",
62
+ "label": "Email",
63
+ "required": true,
64
+ "validations": {
65
+ "email": true
66
+ }
67
+ },
68
+ "role": {
69
+ "type": "select",
70
+ "label": "Role",
71
+ "required": true,
72
+ "defaultValue": "developer",
73
+ "options": ["developer", "designer", "manager"]
74
+ },
75
+ "subscribeToNewsletter": {
76
+ "type": "checkbox",
77
+ "label": "Subscribe To Newsletter",
78
+ "required": false
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ ## API
85
+
86
+ ### `zodToFormMeta(schema: z.ZodTypeAny, fieldName?: string): FormMeta`
87
+ Recursively generates form controls, type mappings, validation rules, default values, options, and descriptions from a Zod schema.
88
+
89
+ ### `toLabel(fieldName: string): string`
90
+ Converts `camelCase`, `snake_case`, and `kebab-case` string patterns into standard capitalized labels.
91
+
92
+ ## Support
93
+
94
+ For support, questions, or discussions, join our Discord server:
95
+
96
+ [![Discord Server](https://img.shields.io/discord/1111111111?color=7289da&label=Discord&logo=discord&style=for-the-badge)](https://discord.gg/7Fw7snafYS)
97
+
98
+ ## License
99
+ MIT
@@ -0,0 +1,2 @@
1
+ export { zodToFormMeta, toLabel } from './zodToFormMeta';
2
+ export type * from './types';
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toLabel = exports.zodToFormMeta = void 0;
4
+ var zodToFormMeta_1 = require("./zodToFormMeta");
5
+ Object.defineProperty(exports, "zodToFormMeta", { enumerable: true, get: function () { return zodToFormMeta_1.zodToFormMeta; } });
6
+ Object.defineProperty(exports, "toLabel", { enumerable: true, get: function () { return zodToFormMeta_1.toLabel; } });
@@ -0,0 +1,24 @@
1
+ export type FieldType = 'text' | 'email' | 'url' | 'number' | 'checkbox' | 'select' | 'date' | 'object' | 'array';
2
+ export interface ValidationRule {
3
+ value: any;
4
+ message?: string;
5
+ }
6
+ export interface FieldMeta {
7
+ type: FieldType;
8
+ label: string;
9
+ required: boolean;
10
+ defaultValue?: any;
11
+ description?: string;
12
+ options?: string[];
13
+ validations?: {
14
+ min?: ValidationRule;
15
+ max?: ValidationRule;
16
+ email?: boolean;
17
+ url?: boolean;
18
+ uuid?: boolean;
19
+ regex?: ValidationRule;
20
+ };
21
+ fields?: Record<string, FieldMeta>;
22
+ element?: FieldMeta;
23
+ }
24
+ export type FormMeta = FieldMeta;
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod';
2
+ import type { FormMeta } from './types';
3
+ export declare function toLabel(str: string): string;
4
+ /**
5
+ * Recursively parses a Zod schema to produce a UI-friendly form metadata object.
6
+ *
7
+ * @param schema The Zod schema to inspect.
8
+ * @param fieldName The original name of the field (used to generate human-readable labels).
9
+ * @returns A structured metadata configuration for generating form fields.
10
+ */
11
+ export declare function zodToFormMeta(schema: z.ZodTypeAny, fieldName?: string): FormMeta;
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toLabel = toLabel;
4
+ exports.zodToFormMeta = zodToFormMeta;
5
+ function toLabel(str) {
6
+ if (!str)
7
+ return '';
8
+ const words = str
9
+ .replace(/([A-Z])/g, ' $1')
10
+ .replace(/[_-]+/g, ' ')
11
+ .trim()
12
+ .split(/\s+/);
13
+ return words.map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' ');
14
+ }
15
+ function unwrapZodType(schema) {
16
+ let current = schema;
17
+ let required = true;
18
+ let defaultValue = undefined;
19
+ const description = schema.description;
20
+ while (true) {
21
+ const typeName = current._def.typeName;
22
+ if (typeName === 'ZodOptional' || typeName === 'ZodNullable') {
23
+ required = false;
24
+ current = current.unwrap();
25
+ }
26
+ else if (typeName === 'ZodDefault') {
27
+ defaultValue = current._def.defaultValue();
28
+ current = current._def.innerType;
29
+ }
30
+ else if (typeName === 'ZodEffects') {
31
+ current = current.innerType();
32
+ }
33
+ else {
34
+ break;
35
+ }
36
+ }
37
+ return { unwrapped: current, required, defaultValue, description };
38
+ }
39
+ /**
40
+ * Recursively parses a Zod schema to produce a UI-friendly form metadata object.
41
+ *
42
+ * @param schema The Zod schema to inspect.
43
+ * @param fieldName The original name of the field (used to generate human-readable labels).
44
+ * @returns A structured metadata configuration for generating form fields.
45
+ */
46
+ function zodToFormMeta(schema, fieldName = '') {
47
+ const { unwrapped, required, defaultValue, description } = unwrapZodType(schema);
48
+ const typeName = unwrapped._def.typeName;
49
+ const label = fieldName ? toLabel(fieldName) : '';
50
+ const meta = {
51
+ type: 'text',
52
+ label,
53
+ required,
54
+ };
55
+ if (defaultValue !== undefined) {
56
+ meta.defaultValue = defaultValue;
57
+ }
58
+ if (description !== undefined) {
59
+ meta.description = description;
60
+ }
61
+ if (typeName === 'ZodString') {
62
+ meta.type = 'text';
63
+ const checks = unwrapped._def.checks;
64
+ const validations = {};
65
+ for (const check of checks) {
66
+ if (check.kind === 'email') {
67
+ meta.type = 'email';
68
+ validations.email = true;
69
+ }
70
+ else if (check.kind === 'url') {
71
+ meta.type = 'url';
72
+ validations.url = true;
73
+ }
74
+ else if (check.kind === 'uuid') {
75
+ validations.uuid = true;
76
+ }
77
+ else if (check.kind === 'min') {
78
+ validations.min = { value: check.value, message: check.message };
79
+ }
80
+ else if (check.kind === 'max') {
81
+ validations.max = { value: check.value, message: check.message };
82
+ }
83
+ else if (check.kind === 'regex') {
84
+ validations.regex = { value: check.regex.source, message: check.message };
85
+ }
86
+ }
87
+ if (Object.keys(validations).length > 0) {
88
+ meta.validations = validations;
89
+ }
90
+ }
91
+ else if (typeName === 'ZodNumber') {
92
+ meta.type = 'number';
93
+ const checks = unwrapped._def.checks;
94
+ const validations = {};
95
+ for (const check of checks) {
96
+ if (check.kind === 'min') {
97
+ validations.min = { value: check.value, message: check.message };
98
+ }
99
+ else if (check.kind === 'max') {
100
+ validations.max = { value: check.value, message: check.message };
101
+ }
102
+ }
103
+ if (Object.keys(validations).length > 0) {
104
+ meta.validations = validations;
105
+ }
106
+ }
107
+ else if (typeName === 'ZodBoolean') {
108
+ meta.type = 'checkbox';
109
+ }
110
+ else if (typeName === 'ZodDate') {
111
+ meta.type = 'date';
112
+ }
113
+ else if (typeName === 'ZodEnum') {
114
+ meta.type = 'select';
115
+ meta.options = unwrapped._def.values;
116
+ }
117
+ else if (typeName === 'ZodNativeEnum') {
118
+ meta.type = 'select';
119
+ const enumObj = unwrapped._def.values;
120
+ meta.options = Object.values(enumObj).filter((v) => typeof v === 'string');
121
+ }
122
+ else if (typeName === 'ZodObject') {
123
+ meta.type = 'object';
124
+ const shape = unwrapped.shape;
125
+ const fields = {};
126
+ for (const key of Object.keys(shape)) {
127
+ fields[key] = zodToFormMeta(shape[key], key);
128
+ }
129
+ meta.fields = fields;
130
+ }
131
+ else if (typeName === 'ZodArray') {
132
+ meta.type = 'array';
133
+ meta.element = zodToFormMeta(unwrapped.element, '');
134
+ }
135
+ return meta;
136
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@rafaelsilvadeveloper/zod-meta-form",
3
+ "version": "1.0.2",
4
+ "description": "A framework-agnostic generator of Form UI metadata from Zod schemas, converting schemas into client-ready forms.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/rafael-packages/zod-meta-form.git"
8
+ },
9
+ "bugs": {
10
+ "url": "https://github.com/rafael-packages/zod-meta-form/issues"
11
+ },
12
+ "homepage": "https://github.com/rafael-packages/zod-meta-form#readme",
13
+ "main": "dist/index.js",
14
+ "types": "dist/index.d.ts",
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "type": "module",
19
+ "scripts": {
20
+ "build": "tsc",
21
+ "test": "bun test",
22
+ "prepare": "bun run build",
23
+ "lint": "eslint src/**/*.ts",
24
+ "format": "prettier --write src/**/*.ts tests/**/*.ts"
25
+ },
26
+ "keywords": [
27
+ "zod",
28
+ "form",
29
+ "metadata",
30
+ "form-generator",
31
+ "typescript",
32
+ "schema",
33
+ "validation"
34
+ ],
35
+ "author": "realkalashnikov",
36
+ "license": "MIT",
37
+ "peerDependencies": {
38
+ "typescript": "^5.0.0",
39
+ "zod": "^3.0.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^25.9.1",
43
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
44
+ "@typescript-eslint/parser": "^7.18.0",
45
+ "eslint": "^8.57.0",
46
+ "prettier": "^3.3.3",
47
+ "zod": "^3.23.8"
48
+ }
49
+ }