zodify-env 1.0.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.
- package/README.md +151 -0
- package/dist/index.js +58 -0
- package/env.schema.ts +9 -0
- package/package.json +29 -0
- package/src/index.ts +71 -0
package/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# zodify-env 🚀
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/zodify-env)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
A blazing-fast, **zero-dependency** CLI tool that automatically generates strongly-typed [Zod](https://zod.dev/) schemas from your `.env.example` files.
|
|
7
|
+
|
|
8
|
+
Stop manually writing validation schemas for your environment variables. `zodify-env` reads your template, infers the types (strings, numbers, booleans), and writes a ready-to-use TypeScript schema file so you get instant auto-complete and runtime validation.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 🚀 Quick Start (No Install Required)
|
|
13
|
+
|
|
14
|
+
You don't even need to install it to use it! Just run it via `npx` in the root of your project:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx zodify-env
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
By default, this looks for a `.env.example` file in your current directory and generates an `env.schema.ts` file.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## ⚙️ Arguments & Options
|
|
25
|
+
|
|
26
|
+
You can customize where `zodify-env` looks for your environment variables and where it saves the generated schema using these arguments:
|
|
27
|
+
|
|
28
|
+
| Argument | Short Flag | Default Value | Description |
|
|
29
|
+
| :--- | :---: | :--- | :--- |
|
|
30
|
+
| `--input` | `-i` | `.env.example` | The path to your environment variable template or source file. |
|
|
31
|
+
| `--output` | `-o` | `env.schema.ts` | The destination path and filename for the generated TypeScript file. |
|
|
32
|
+
|
|
33
|
+
### 💡 Usage Examples
|
|
34
|
+
|
|
35
|
+
**1. Basic Run (Uses Defaults)**
|
|
36
|
+
Reads from `.env.example` and outputs to `env.schema.ts` in the current folder.
|
|
37
|
+
```bash
|
|
38
|
+
npx zodify-env
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**2. Custom Input File**
|
|
42
|
+
If your team uses a different naming convention, like `.env.local` or `.env.template`:
|
|
43
|
+
```bash
|
|
44
|
+
npx zodify-env --input .env.local
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**3. Custom Output Location**
|
|
48
|
+
If you want to save the generated schema directly into your source code folder:
|
|
49
|
+
```bash
|
|
50
|
+
npx zodify-env --output src/config/env.ts
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**4. Using Short Flags for Both**
|
|
54
|
+
You can combine the short flags (`-i` and `-o`) for a quicker command:
|
|
55
|
+
```bash
|
|
56
|
+
npx zodify-env -i .env.template -o src/schemas/env.ts
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 📦 Installation & Team Workflow
|
|
62
|
+
|
|
63
|
+
For teams, it is highly recommended to install `zodify-env` as a development dependency so everyone stays in sync.
|
|
64
|
+
|
|
65
|
+
### 1. Install
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install -D zodify-env
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 2. Add a Script
|
|
72
|
+
|
|
73
|
+
Add a sync script to your `package.json`. You can pass your custom arguments right here:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
"scripts": {
|
|
77
|
+
"env:sync": "zodify-env --input .env.example --output src/config/env.schema.ts"
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 3. Run it
|
|
82
|
+
|
|
83
|
+
Whenever you add a new variable to your `.env.example`, simply run:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm run env:sync
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 📖 How it Works
|
|
92
|
+
|
|
93
|
+
Given an input file like this (`.env.example`):
|
|
94
|
+
|
|
95
|
+
```env
|
|
96
|
+
# Server Configuration
|
|
97
|
+
PORT=3000
|
|
98
|
+
DEBUG=true
|
|
99
|
+
API_KEY=
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`zodify-env` will generate the following TypeScript file:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { z } from 'zod';
|
|
106
|
+
|
|
107
|
+
export const envSchema = z.object({
|
|
108
|
+
PORT: z.coerce.number(),
|
|
109
|
+
DEBUG: z.coerce.boolean(),
|
|
110
|
+
API_KEY: z.string(),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
export type Env = z.infer<typeof envSchema>;
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Implementing in your app
|
|
117
|
+
|
|
118
|
+
Import the generated schema into your application entry point (e.g., `server.ts` or `index.ts`) to validate your environment variables at startup:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { envSchema } from './env.schema';
|
|
122
|
+
|
|
123
|
+
const parsedEnv = envSchema.safeParse(process.env);
|
|
124
|
+
|
|
125
|
+
if (!parsedEnv.success) {
|
|
126
|
+
console.error("❌ Invalid environment variables:", parsedEnv.error.format());
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// config is now fully typed!
|
|
131
|
+
const config = parsedEnv.data;
|
|
132
|
+
console.log(`Server starting on port ${config.PORT}`);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 🤝 Contributing
|
|
138
|
+
|
|
139
|
+
Contributions, issues, and feature requests are welcome!
|
|
140
|
+
|
|
141
|
+
1. Fork the project.
|
|
142
|
+
2. Clone your fork: `git clone https://github.com/your-username/zodify-env.git`
|
|
143
|
+
3. Install dependencies: `npm install`
|
|
144
|
+
4. Make your changes in the `src` directory.
|
|
145
|
+
5. Build the project to test your changes: `npm run build`
|
|
146
|
+
6. Test your local build: `node dist/index.js`
|
|
147
|
+
7. Create a Pull Request!
|
|
148
|
+
|
|
149
|
+
## 📝 License
|
|
150
|
+
|
|
151
|
+
This project is [MIT](https://opensource.org/licenses/MIT) licensed.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import { parseArgs } from "util";
|
|
7
|
+
console.log("\u{1F680} Starting zodify-env...");
|
|
8
|
+
var { values } = parseArgs({
|
|
9
|
+
options: {
|
|
10
|
+
input: {
|
|
11
|
+
type: "string",
|
|
12
|
+
short: "i",
|
|
13
|
+
default: ".env.example"
|
|
14
|
+
},
|
|
15
|
+
output: {
|
|
16
|
+
type: "string",
|
|
17
|
+
short: "o",
|
|
18
|
+
default: "env.schema.ts"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
var currentDir = process.cwd();
|
|
23
|
+
var envFilePath = path.resolve(currentDir, values.input);
|
|
24
|
+
var outputPath = path.resolve(currentDir, values.output);
|
|
25
|
+
if (!fs.existsSync(envFilePath)) {
|
|
26
|
+
console.error(`\u274C Error: Could not find the input file at ${envFilePath}`);
|
|
27
|
+
console.error(`\u{1F4A1} Tip: Make sure the file exists, or specify a different path using --input`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
var envFileContent = fs.readFileSync(envFilePath, "utf-8");
|
|
31
|
+
var lines = envFileContent.split("\n");
|
|
32
|
+
var zodSchemaString = `import { z } from 'zod';
|
|
33
|
+
|
|
34
|
+
export const envSchema = z.object({
|
|
35
|
+
`;
|
|
36
|
+
for (const line of lines) {
|
|
37
|
+
const trimmedLine = line.trim();
|
|
38
|
+
if (!trimmedLine || trimmedLine.startsWith("#")) continue;
|
|
39
|
+
const [key, ...valueParts] = trimmedLine.split("=");
|
|
40
|
+
const value = valueParts.join("=").trim();
|
|
41
|
+
const cleanKey = key.trim();
|
|
42
|
+
if (!cleanKey) continue;
|
|
43
|
+
let zodType = "z.string()";
|
|
44
|
+
if (value === "true" || value === "false") {
|
|
45
|
+
zodType = "z.coerce.boolean()";
|
|
46
|
+
} else if (!isNaN(Number(value)) && value !== "") {
|
|
47
|
+
zodType = "z.coerce.number()";
|
|
48
|
+
}
|
|
49
|
+
zodSchemaString += ` ${cleanKey}: ${zodType},
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
zodSchemaString += `});
|
|
53
|
+
|
|
54
|
+
`;
|
|
55
|
+
zodSchemaString += `export type Env = z.infer<typeof envSchema>;
|
|
56
|
+
`;
|
|
57
|
+
fs.writeFileSync(outputPath, zodSchemaString);
|
|
58
|
+
console.log(`\u2705 Success! Zod schema generated at ${outputPath}`);
|
package/env.schema.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zodify-env",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Automatically generate Zod schemas from your .env files",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"zodify-env": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsup src/index.ts --format esm --clean",
|
|
12
|
+
"dev": "tsup src/index.ts --format esm --watch",
|
|
13
|
+
"start": "node dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"zod",
|
|
17
|
+
"env",
|
|
18
|
+
"cli",
|
|
19
|
+
"typescript",
|
|
20
|
+
"generator"
|
|
21
|
+
],
|
|
22
|
+
"author": "Your Name",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^20.19.33",
|
|
26
|
+
"tsup": "^8.5.1",
|
|
27
|
+
"typescript": "^5.9.3"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { parseArgs } from 'util';
|
|
6
|
+
|
|
7
|
+
console.log('🚀 Starting zodify-env...');
|
|
8
|
+
|
|
9
|
+
// 1. Parse command-line arguments natively
|
|
10
|
+
const { values } = parseArgs({
|
|
11
|
+
options: {
|
|
12
|
+
input: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
short: 'i',
|
|
15
|
+
default: '.env.example',
|
|
16
|
+
},
|
|
17
|
+
output: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
short: 'o',
|
|
20
|
+
default: 'env.schema.ts',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// 2. Resolve paths based on the directory where the user runs the command (process.cwd())
|
|
26
|
+
const currentDir = process.cwd();
|
|
27
|
+
const envFilePath = path.resolve(currentDir, values.input as string);
|
|
28
|
+
const outputPath = path.resolve(currentDir, values.output as string);
|
|
29
|
+
|
|
30
|
+
// 3. Check if the input file exists
|
|
31
|
+
if (!fs.existsSync(envFilePath)) {
|
|
32
|
+
console.error(`❌ Error: Could not find the input file at ${envFilePath}`);
|
|
33
|
+
console.error(`💡 Tip: Make sure the file exists, or specify a different path using --input`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 4. Read the file
|
|
38
|
+
const envFileContent = fs.readFileSync(envFilePath, 'utf-8');
|
|
39
|
+
const lines = envFileContent.split('\n');
|
|
40
|
+
|
|
41
|
+
let zodSchemaString = `import { z } from 'zod';\n\nexport const envSchema = z.object({\n`;
|
|
42
|
+
|
|
43
|
+
// 5. Parse the variables and infer types
|
|
44
|
+
for (const line of lines) {
|
|
45
|
+
const trimmedLine = line.trim();
|
|
46
|
+
|
|
47
|
+
if (!trimmedLine || trimmedLine.startsWith('#')) continue;
|
|
48
|
+
|
|
49
|
+
const [key, ...valueParts] = trimmedLine.split('=');
|
|
50
|
+
const value = valueParts.join('=').trim();
|
|
51
|
+
const cleanKey = key.trim();
|
|
52
|
+
|
|
53
|
+
if (!cleanKey) continue;
|
|
54
|
+
|
|
55
|
+
let zodType = 'z.string()';
|
|
56
|
+
|
|
57
|
+
if (value === 'true' || value === 'false') {
|
|
58
|
+
zodType = 'z.coerce.boolean()';
|
|
59
|
+
} else if (!isNaN(Number(value)) && value !== '') {
|
|
60
|
+
zodType = 'z.coerce.number()';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
zodSchemaString += ` ${cleanKey}: ${zodType},\n`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
zodSchemaString += `});\n\n`;
|
|
67
|
+
zodSchemaString += `export type Env = z.infer<typeof envSchema>;\n`;
|
|
68
|
+
|
|
69
|
+
// 6. Write the schema to the target output path
|
|
70
|
+
fs.writeFileSync(outputPath, zodSchemaString);
|
|
71
|
+
console.log(`✅ Success! Zod schema generated at ${outputPath}`);
|