@romaintaillandier1978/dotenv-never-lies 0.3.0 → 1.1.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 +437 -160
- package/dist/cli/commands/assert.d.ts +4 -2
- package/dist/cli/commands/assert.d.ts.map +1 -1
- package/dist/cli/commands/assert.js +7 -9
- package/dist/cli/commands/explain.d.ts +6 -1
- package/dist/cli/commands/explain.d.ts.map +1 -1
- package/dist/cli/commands/explain.js +9 -16
- package/dist/cli/commands/export.d.ts +35 -0
- package/dist/cli/commands/export.d.ts.map +1 -0
- package/dist/cli/commands/export.js +268 -0
- package/dist/cli/commands/generate.d.ts +10 -2
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +9 -7
- package/dist/cli/commands/reverseEnv.d.ts +8 -2
- package/dist/cli/commands/reverseEnv.d.ts.map +1 -1
- package/dist/cli/commands/reverseEnv.js +21 -8
- package/dist/cli/index.js +323 -99
- package/dist/cli/utils/exitCodes.d.ts +8 -0
- package/dist/cli/utils/exitCodes.d.ts.map +1 -0
- package/dist/cli/utils/exitCodes.js +8 -0
- package/dist/cli/utils/infer-schema.d.ts.map +1 -1
- package/dist/cli/utils/infer-schema.js +4 -3
- package/dist/cli/utils/load-schema.d.ts.map +1 -1
- package/dist/cli/utils/load-schema.js +20 -12
- package/dist/cli/utils/printer.d.ts.map +1 -1
- package/dist/cli/utils/printer.js +5 -4
- package/dist/cli/utils/resolve-schema.d.ts.map +1 -1
- package/dist/cli/utils/resolve-schema.js +7 -3
- package/dist/cli/utils/toFile.d.ts +2 -0
- package/dist/cli/utils/toFile.d.ts.map +1 -0
- package/dist/cli/utils/toFile.js +8 -0
- package/dist/core.d.ts +33 -33
- package/dist/core.js +10 -10
- package/dist/errors.d.ts +29 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +33 -4
- package/dist/romaintaillandier1978-dotenv-never-lies-0.3.0.tgz +0 -0
- package/package.json +9 -9
package/dist/cli/index.js
CHANGED
|
@@ -1,127 +1,351 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { program } from "commander";
|
|
2
|
+
import { program, CommanderError } from "commander";
|
|
3
3
|
import { assertCommand } from "./commands/assert.js";
|
|
4
4
|
import { generateCommand } from "./commands/generate.js";
|
|
5
5
|
import { reverseEnvCommand } from "./commands/reverseEnv.js";
|
|
6
|
-
import { explainCommand } from "./commands/explain.js";
|
|
6
|
+
import { explainCommand, printHuman } from "./commands/explain.js";
|
|
7
|
+
import { exportCommand } from "./commands/export.js";
|
|
8
|
+
import { toFile } from "./utils/toFile.js";
|
|
9
|
+
import { DnlError, ExitCodes, ValidationError } from "../errors.js";
|
|
10
|
+
import { createRequire } from "node:module";
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const packageJson = require("../../package.json");
|
|
13
|
+
const exitCodeHelp = {
|
|
14
|
+
[ExitCodes.success]: "Success (everything is valid, exit OK)",
|
|
15
|
+
[ExitCodes.usageError]: "Usage error or internal error",
|
|
16
|
+
[ExitCodes.schemaNotFound]: "DNL schema not found or not resolved",
|
|
17
|
+
[ExitCodes.validationError]: "Validation failed (invalid environment)",
|
|
18
|
+
[ExitCodes.exportError]: "Export error (format, file writing, secret, etc.)",
|
|
19
|
+
};
|
|
20
|
+
// #region Program
|
|
7
21
|
program
|
|
8
22
|
.name("dnl")
|
|
9
|
-
|
|
10
|
-
.
|
|
11
|
-
|
|
12
|
-
|
|
23
|
+
//.version("0.3.0")
|
|
24
|
+
.version(packageJson.version)
|
|
25
|
+
// allows passing positional arguments, before/after options
|
|
26
|
+
.enablePositionalOptions()
|
|
27
|
+
.exitOverride()
|
|
28
|
+
.addHelpText("before", `Summary:
|
|
29
|
+
CLI for dotenv-never-lies.
|
|
30
|
+
Validates typed environment variables from a TypeScript/Zod schema.
|
|
13
31
|
`)
|
|
14
|
-
.
|
|
32
|
+
.option("--schema <file>", "DNL schema file (e.g., path/to/my-dnl.ts). See the Environment schema section for details.")
|
|
33
|
+
.addHelpText("after", `\nExit codes:\n${Object.entries(exitCodeHelp)
|
|
34
|
+
.map(([key, value]) => ` - ${key}: ${value}`)
|
|
35
|
+
.join("\n")}
|
|
36
|
+
`)
|
|
37
|
+
.addHelpText("after", `\nEnvironment schema:
|
|
38
|
+
The dotenv-never-lies schema is resolved in the following order:
|
|
39
|
+
1. --schema option if provided
|
|
40
|
+
2. "dotenv-never-lies.schema" key in package.json
|
|
41
|
+
3. Convention files:
|
|
42
|
+
- env.dnl.ts
|
|
43
|
+
- env.dnl.js
|
|
44
|
+
- dnl.config.ts
|
|
45
|
+
- dnl.config.js
|
|
46
|
+
If no schema is found, the command fails.
|
|
47
|
+
`)
|
|
48
|
+
.addHelpText("after", `\nExamples:
|
|
15
49
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
50
|
+
# Check the environment at runtime and exit the process if the schema is not satisfied
|
|
51
|
+
dnl assert
|
|
52
|
+
dnl assert --schema my-dnl.ts
|
|
53
|
+
|
|
54
|
+
# Generate a documented .env file from the schema
|
|
55
|
+
dnl generate
|
|
56
|
+
dnl generate --schema my-dnl.ts --out .env
|
|
57
|
+
|
|
58
|
+
# Create an env.dnl.ts schema from an existing .env
|
|
59
|
+
dnl reverse-env --source .env
|
|
60
|
+
|
|
61
|
+
# Display known variables and their description
|
|
62
|
+
dnl explain
|
|
63
|
+
|
|
64
|
+
# Export variables in docker-args format
|
|
65
|
+
dnl export docker-args --source .env
|
|
66
|
+
|
|
67
|
+
`);
|
|
68
|
+
// #endregion Program
|
|
69
|
+
// #region assert
|
|
30
70
|
program
|
|
31
71
|
.command("assert")
|
|
32
|
-
.description("
|
|
33
|
-
.option("--
|
|
34
|
-
.option("-s, --source <source>", "Source des variables (défaut : process.env)")
|
|
72
|
+
.description("Verifies the runtime environment and exits the process if the schema is not satisfied.")
|
|
73
|
+
.option("-s, --source <source>", "Variables source (default: process.env)")
|
|
35
74
|
.action(assertCommand)
|
|
36
|
-
.addHelpText("after", `\
|
|
75
|
+
.addHelpText("after", `\nExamples:
|
|
37
76
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
77
|
+
# Validate environment variables from process.env
|
|
78
|
+
# Recommended in CI to prevent starting with an invalid configuration
|
|
79
|
+
dnl assert
|
|
80
|
+
dnl assert --schema my-dnl.ts
|
|
81
|
+
|
|
82
|
+
# Validate environment variables from a .env file
|
|
83
|
+
# Recommended locally (schema preparation, onboarding)
|
|
84
|
+
dnl assert --source .env
|
|
85
|
+
dnl assert --schema my-dnl.ts --source .env
|
|
86
|
+
|
|
87
|
+
# validate environment variables from the file provided by the CI
|
|
88
|
+
dnl assert --source $ENV_FILE
|
|
89
|
+
dnl assert --schema my-dnl.ts --source $ENV_FILE
|
|
51
90
|
`);
|
|
91
|
+
// #endregion assert
|
|
92
|
+
// #region export
|
|
93
|
+
const exportHelp = {
|
|
94
|
+
"docker-args": "Arguments `--env KEY=VALUE` for `docker run`",
|
|
95
|
+
"docker-env": "File compatible with Docker `--env-file`",
|
|
96
|
+
"github-env": "Inject into a GitHub Actions job environment",
|
|
97
|
+
"github-secret": "GitHub Secrets via gh CLI (repo or organization)",
|
|
98
|
+
"gitlab-env": "GitLab CI environment variables",
|
|
99
|
+
"k8s-configmap": "Kubernetes ConfigMap (NON-sensitive variables)",
|
|
100
|
+
"k8s-secret": "Kubernetes Secret (sensitive variables only)",
|
|
101
|
+
env: ".env file cleaned (without unnecessary comments)",
|
|
102
|
+
json: "Key/value JSON object",
|
|
103
|
+
ts: "Typed TypeScript object",
|
|
104
|
+
js: "JavaScript object",
|
|
105
|
+
};
|
|
52
106
|
program
|
|
53
|
-
.command("
|
|
54
|
-
.description("
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
.option("--
|
|
58
|
-
.option("-
|
|
59
|
-
.option("-
|
|
60
|
-
.
|
|
61
|
-
.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
107
|
+
.command("export")
|
|
108
|
+
.description("Exports environment variables to a specified format. Variables are exported after being validated against the schema.")
|
|
109
|
+
.argument("<format>", "Export format. See list and examples at the end")
|
|
110
|
+
.option("-s, --source <source>", "Variables source (default: process.env if none provided)")
|
|
111
|
+
.option("--hide-secret", 'Mask sensitive variables (replace with "********")')
|
|
112
|
+
.option("--exclude-secret", "Exclude sensitive variables (do not show them at all)")
|
|
113
|
+
.option("--include-comments", "Include comments in the export (does not work with the json format)")
|
|
114
|
+
.option("--serialize-typed", "Serialize validated runtime values (js/ts/json only). See below for more details.")
|
|
115
|
+
.option("-o, --out <file>", "Output file")
|
|
116
|
+
.option("-f, --force", "Overwrite the existing file, in conjunction with -o or --out")
|
|
117
|
+
.option("--k8s-name <name>", "Name for the k8s resource. Default: env-secret for k8s-secret, env-config for k8s-configmap")
|
|
118
|
+
.option("--github-org <org>", "GitHub organization name")
|
|
119
|
+
.action(async (format, opts) => {
|
|
120
|
+
const { content, warnings, out } = await exportCommand({ ...opts, format });
|
|
121
|
+
if (out) {
|
|
122
|
+
await toFile(content, out, opts.force ?? false);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.log(content);
|
|
126
|
+
}
|
|
127
|
+
for (const warning of warnings) {
|
|
128
|
+
console.error(`${warning}`);
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
.addHelpText("after", `\nExport formats:\n${Object.entries(exportHelp)
|
|
132
|
+
.map(([key, value]) => ` - ${key}: ${value}`)
|
|
133
|
+
.join("\n")}
|
|
134
|
+
`)
|
|
135
|
+
.addHelpText("after", `\nSerialize validated runtime values (js/ts/json only):
|
|
136
|
+
When the --serialize-typed option is used, runtime values (after Zod transformations and validation)
|
|
137
|
+
are serialized instead of the raw (but still validated) values from the source (.env or process.env).
|
|
65
138
|
|
|
66
|
-
|
|
67
|
-
dnl generate --schema my-dnl.ts
|
|
139
|
+
Example:
|
|
68
140
|
|
|
69
|
-
|
|
70
|
-
|
|
141
|
+
.env file:
|
|
142
|
+
NODE_CORS_ORIGIN=https://a.site.com;https://b.site.com;https://c.site.com
|
|
71
143
|
|
|
72
|
-
|
|
73
|
-
|
|
144
|
+
env.dnl.ts file:
|
|
145
|
+
NODE_CORS_ORIGIN: {
|
|
146
|
+
description: "Allowed frontend URLs separated by semicolons",
|
|
147
|
+
schema: z.string().transform((v) =>
|
|
148
|
+
v
|
|
149
|
+
.split(";")
|
|
150
|
+
.map((s) => s.trim())
|
|
151
|
+
.filter(Boolean)
|
|
152
|
+
),
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
dnl export json --source .env
|
|
156
|
+
{
|
|
157
|
+
"NODE_CORS_ORIGIN": "https://a.site.com;https://b.site.com;https://c.site.com"
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
dnl export json --source .env --serialize-typed
|
|
161
|
+
{
|
|
162
|
+
"NODE_CORS_ORIGIN": [
|
|
163
|
+
"https://a.site.com",
|
|
164
|
+
"https://b.site.com",
|
|
165
|
+
"https://c.site.com"
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
`)
|
|
170
|
+
.addHelpText("after", `\nExamples:
|
|
171
|
+
|
|
172
|
+
# --- Simple cases ----------------------------------------------------
|
|
173
|
+
|
|
174
|
+
# Export environment variables as JSON from a .env file
|
|
175
|
+
dnl export json --source .env
|
|
176
|
+
|
|
177
|
+
# Clean a .env file (remove comments and extraneous lines)
|
|
178
|
+
dnl export env --source .env --out .env.clean
|
|
179
|
+
dnl export env --source .env -fo .env
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# --- Docker / CI ----------------------------------------------------
|
|
183
|
+
|
|
184
|
+
# Export variables as docker-args
|
|
185
|
+
dnl export docker-args --source .env
|
|
186
|
+
|
|
187
|
+
# Concrete CI example to run a Docker container
|
|
188
|
+
# (variables are injected dynamically)
|
|
189
|
+
docker run \\
|
|
190
|
+
$(dnl export docker-args --source $DOTENV_FILE) \\
|
|
191
|
+
--restart always \\
|
|
192
|
+
-d my-image:latest
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# --- GitHub Actions -------------------------------------------------
|
|
196
|
+
|
|
197
|
+
# Export variables as GitHub secrets (current repo)
|
|
198
|
+
# Requires gh CLI configured (gh auth login)
|
|
199
|
+
dnl export github-secret
|
|
200
|
+
|
|
201
|
+
# Export variables as GitHub organization secrets
|
|
202
|
+
dnl export github-secret --github-org my-org
|
|
203
|
+
|
|
204
|
+
# Example usage in a GitHub Actions job:
|
|
205
|
+
# (variables are injected into the job environment)
|
|
206
|
+
dnl export github-env >> $GITHUB_ENV
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# --- Kubernetes -----------------------------------------------------
|
|
210
|
+
|
|
211
|
+
# Generate a Kubernetes ConfigMap (NON-sensitive variables), from process.env
|
|
212
|
+
dnl export k8s-configmap --out k8s-configmap.yaml
|
|
213
|
+
|
|
214
|
+
# Generate a Kubernetes Secret from a .env file
|
|
215
|
+
dnl export k8s-secret --source .env --k8s-name my-secret --out k8s-secret.yaml
|
|
216
|
+
|
|
217
|
+
# Apply the generated files
|
|
218
|
+
kubectl apply -f k8s-configmap.yaml
|
|
219
|
+
kubectl apply -f k8s-secret.yaml
|
|
220
|
+
|
|
221
|
+
# note: if no secret is present in the dnl config, for k8s-secret the output will be empty
|
|
222
|
+
|
|
223
|
+
# --- TypeScript / JavaScript ---------------------------------------
|
|
224
|
+
|
|
225
|
+
# Export variables as a typed TypeScript object, or js
|
|
226
|
+
dnl export ts --out env.generated.ts --serialize-typed
|
|
227
|
+
dnl export js --out env.generated.js --serialize-typed
|
|
228
|
+
`);
|
|
229
|
+
// #endregion export
|
|
230
|
+
// #region generate
|
|
231
|
+
program
|
|
232
|
+
.command("generate")
|
|
233
|
+
.description("Generates a .env file from a DNL schema.\n" +
|
|
234
|
+
"Useful to bootstrap a project or facilitate onboarding of a new developer.\n" +
|
|
235
|
+
"Only default values defined in the schema are written.")
|
|
236
|
+
.option("-o, --out <file>", "Output file (default: .env)")
|
|
237
|
+
.option("-f, --force", "Overwrite existing file")
|
|
238
|
+
.action(async (opts) => {
|
|
239
|
+
const { content, out } = await generateCommand(opts);
|
|
240
|
+
await toFile(content, out, opts.force ?? false);
|
|
241
|
+
})
|
|
242
|
+
.addHelpText("after", `\nExamples:
|
|
243
|
+
|
|
244
|
+
# Generate a .env file from the default schema (env.dnl.ts)
|
|
245
|
+
dnl generate
|
|
246
|
+
|
|
247
|
+
# Generate a .env file from a specified schema
|
|
248
|
+
dnl generate --schema my-dnl.ts
|
|
249
|
+
|
|
250
|
+
# Generate a .env.local file from the schema
|
|
251
|
+
dnl generate --out .env.local
|
|
252
|
+
|
|
253
|
+
# Generate a .env file from a schema and overwrite the existing file
|
|
254
|
+
dnl generate --out .env --force
|
|
74
255
|
`);
|
|
256
|
+
// #endregion generate
|
|
257
|
+
// #region reverse-env
|
|
75
258
|
program
|
|
76
259
|
.command("reverse-env")
|
|
77
|
-
.description("
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
.option("-
|
|
82
|
-
.option("-
|
|
83
|
-
.option("--
|
|
84
|
-
.
|
|
85
|
-
.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
260
|
+
.description("Generates a dotenv-never-lies schema from a .env file.\n" +
|
|
261
|
+
"Useful to migrate an existing project to dotenv-never-lies.\n" +
|
|
262
|
+
"The generated schema is a starting point and must be refined manually.\n" +
|
|
263
|
+
"Keys in the .env file that are not valid identifiers are escaped to JSON strings. (e.g. MY-KEY -> 'MY-KEY')")
|
|
264
|
+
.option("-s, --source <source>", "Source .env file", ".env")
|
|
265
|
+
.option("-o, --out <file>", "Output DNL file", "env.dnl.ts")
|
|
266
|
+
.option("-f, --force", "Overwrite existing file")
|
|
267
|
+
.option("--guess-secret", "Try to guess sensitive variables (heuristic)")
|
|
268
|
+
.action(async (opts) => {
|
|
269
|
+
const { content, out, warnings } = await reverseEnvCommand(opts);
|
|
270
|
+
await toFile(content, out, opts.force ?? false);
|
|
271
|
+
for (const warning of warnings) {
|
|
272
|
+
console.error(`${warning}`);
|
|
273
|
+
}
|
|
274
|
+
})
|
|
275
|
+
.addHelpText("after", `\nExamples:
|
|
92
276
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
277
|
+
# Generate an env.dnl.ts schema from a .env file, try to guess sensitive variables
|
|
278
|
+
dnl reverse-env --guess-secret
|
|
279
|
+
|
|
280
|
+
# Generate an env.dnl.ts schema from a .env.local file
|
|
281
|
+
dnl reverse-env --source .env.local
|
|
282
|
+
|
|
283
|
+
# Generate a my-dnl.ts schema from a .env file
|
|
284
|
+
dnl reverse-env --out my-dnl.ts
|
|
285
|
+
|
|
286
|
+
# Generate an env.dnl.ts schema from a .env file and overwrite the existing file
|
|
287
|
+
dnl reverse-env --force
|
|
288
|
+
`);
|
|
289
|
+
// #endregion reverse-env
|
|
290
|
+
// #region explain
|
|
99
291
|
program
|
|
100
292
|
.command("explain")
|
|
101
|
-
.description("
|
|
102
|
-
.argument("[keys...]", "
|
|
103
|
-
.option("--
|
|
104
|
-
.option("-f, --format <format>", 'Format d\'affichage ("human" | "json")', "human")
|
|
293
|
+
.description("Displays the list of known environment variables and their description.")
|
|
294
|
+
.argument("[keys...]", "Keys to explain (0..N). Without argument, all keys.")
|
|
295
|
+
.option("-f, --format <format>", 'Output format ("human" | "json")', "human")
|
|
105
296
|
.action(async (keys, opts) => {
|
|
106
|
-
const result = await explainCommand({ keys: keys ?? [], schema: opts.schema, format: opts.format });
|
|
107
|
-
|
|
297
|
+
const { format, result } = await explainCommand({ keys: keys ?? [], schema: opts.schema, format: opts.format });
|
|
298
|
+
if (format === "human") {
|
|
299
|
+
printHuman(result);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
console.log(JSON.stringify(result, null, 2));
|
|
303
|
+
}
|
|
108
304
|
})
|
|
109
|
-
.addHelpText("after", `\
|
|
305
|
+
.addHelpText("after", `\nExamples:
|
|
110
306
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
307
|
+
# explain all known variables and their description
|
|
308
|
+
dnl explain
|
|
309
|
+
|
|
310
|
+
# explain a variable in detail
|
|
311
|
+
dnl explain NODE_ENV
|
|
312
|
+
|
|
313
|
+
# machine-readable output
|
|
314
|
+
dnl explain --format json
|
|
315
|
+
|
|
316
|
+
# explain all known variables and their description from a schema
|
|
317
|
+
dnl explain --schema my-dnl.ts
|
|
318
|
+
|
|
319
|
+
# explain a subset of known variables and their description
|
|
320
|
+
dnl explain NODE_ENV NODE_PORT
|
|
125
321
|
|
|
126
322
|
`);
|
|
127
|
-
|
|
323
|
+
// #endregion explain
|
|
324
|
+
try {
|
|
325
|
+
await program.parseAsync(process.argv);
|
|
326
|
+
process.exit(ExitCodes.success);
|
|
327
|
+
}
|
|
328
|
+
catch (err) {
|
|
329
|
+
// Commander throws a controlled error when help or version is displayed
|
|
330
|
+
if (err instanceof CommanderError) {
|
|
331
|
+
if (err.code === "commander.helpDisplayed" || err.code === "commander.version") {
|
|
332
|
+
process.exit(ExitCodes.success);
|
|
333
|
+
}
|
|
334
|
+
process.exit(typeof err.exitCode === "number" ? err.exitCode : ExitCodes.usageError);
|
|
335
|
+
}
|
|
336
|
+
if (err instanceof ValidationError) {
|
|
337
|
+
console.error("❌ Invalid environment variables:\n");
|
|
338
|
+
for (const issue of err.issues ?? []) {
|
|
339
|
+
console.error(`- ${issue.key}`);
|
|
340
|
+
console.error(` → ${issue.message}`);
|
|
341
|
+
}
|
|
342
|
+
process.exit(err.exitCode);
|
|
343
|
+
}
|
|
344
|
+
if (err instanceof DnlError) {
|
|
345
|
+
console.error(err.message);
|
|
346
|
+
process.exit(err.exitCode);
|
|
347
|
+
}
|
|
348
|
+
console.error("Unexpected error");
|
|
349
|
+
console.error(err);
|
|
350
|
+
process.exit(ExitCodes.usageError);
|
|
351
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exitCodes.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/exitCodes.ts"],"names":[],"mappings":"AAAA,oBAAY,SAAS;IACjB,OAAO,IAAI;IACX,UAAU,IAAI;IACd,cAAc,IAAI;IAClB,eAAe,IAAI;IACnB,WAAW,IAAI;CAClB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export var ExitCodes;
|
|
2
|
+
(function (ExitCodes) {
|
|
3
|
+
ExitCodes[ExitCodes["success"] = 0] = "success";
|
|
4
|
+
ExitCodes[ExitCodes["usageError"] = 1] = "usageError";
|
|
5
|
+
ExitCodes[ExitCodes["schemaNotFound"] = 2] = "schemaNotFound";
|
|
6
|
+
ExitCodes[ExitCodes["validationError"] = 3] = "validationError";
|
|
7
|
+
ExitCodes[ExitCodes["exportError"] = 4] = "exportError";
|
|
8
|
+
})(ExitCodes || (ExitCodes = {}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"infer-schema.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/infer-schema.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,GAAG,SAAS,oIAuBpD,CAAC;AAIF,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"infer-schema.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/infer-schema.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,GAAG,SAAS,oIAuBpD,CAAC;AAIF,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,YAGxC,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const inferSchema = (value) => {
|
|
2
|
-
if (
|
|
2
|
+
if (value === undefined) {
|
|
3
3
|
return "z.string().optional()";
|
|
4
4
|
}
|
|
5
5
|
if (/^(true|false)$/i.test(value)) {
|
|
@@ -18,7 +18,8 @@ export const inferSchema = (value) => {
|
|
|
18
18
|
}
|
|
19
19
|
return "z.string()";
|
|
20
20
|
};
|
|
21
|
-
const
|
|
21
|
+
const secretMarkers = ["SECRET", "KEY", "TOKEN", "PASSWORD", "PASS", "AUTH"];
|
|
22
22
|
export const guessSecret = (value) => {
|
|
23
|
-
|
|
23
|
+
const parts = value.toUpperCase().split(/[_\-]/);
|
|
24
|
+
return secretMarkers.some((marker) => parts.includes(marker));
|
|
24
25
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-schema.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/load-schema.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"load-schema.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/load-schema.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAGpD,eAAO,MAAM,OAAO,GAAU,YAAY,MAAM,KAAG,OAAO,CAAC,mBAAmB,CAAC,GAAG,CAAC,CA2BlF,CAAC"}
|
|
@@ -2,21 +2,29 @@ import { build } from "esbuild";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { SchemaNotFoundError } from "../../errors.js";
|
|
5
6
|
export const loadDef = async (schemaPath) => {
|
|
6
7
|
const outDir = path.join(process.cwd(), ".dnl");
|
|
7
8
|
await fs.mkdir(outDir, { recursive: true });
|
|
9
|
+
// We always overwrite the file here; this could be an issue.
|
|
8
10
|
const outFile = path.join(outDir, "env.dnl.mjs");
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
try {
|
|
12
|
+
await build({
|
|
13
|
+
entryPoints: [schemaPath],
|
|
14
|
+
outfile: outFile,
|
|
15
|
+
format: "esm",
|
|
16
|
+
platform: "node",
|
|
17
|
+
target: "node18",
|
|
18
|
+
bundle: true,
|
|
19
|
+
packages: "external",
|
|
20
|
+
});
|
|
21
|
+
const mod = await import(pathToFileURL(outFile).href);
|
|
22
|
+
if (!mod.default) {
|
|
23
|
+
throw new SchemaNotFoundError(`The file ${schemaPath} must export a default schema (export default).`);
|
|
24
|
+
}
|
|
25
|
+
return mod.default;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
throw new SchemaNotFoundError(`Unable to load DNL schema (${schemaPath}): ${error}`);
|
|
20
29
|
}
|
|
21
|
-
return mod.default;
|
|
22
30
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"printer.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/printer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,MAAM,WAAW,GAAG;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AACF,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,EAAE,OAAO,gBAAgB,CAAC,GAAG,CAAC,KAAG,WAWzE,CAAC;AAEF,wBAAgB,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"printer.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/printer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,MAAM,WAAW,GAAG;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AACF,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,EAAE,OAAO,gBAAgB,CAAC,GAAG,CAAC,KAAG,WAWzE,CAAC;AAEF,wBAAgB,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,MAAM,CAqE5D;AAuBD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,MAAM,GAAG,SAAS,CAwB9E;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,OAAO,CAyB3D"}
|
|
@@ -45,11 +45,12 @@ export function printZodType(def) {
|
|
|
45
45
|
}
|
|
46
46
|
return "unknown | null";
|
|
47
47
|
case "default":
|
|
48
|
+
//TODO : ignore les valeurs par défaut falsy (0, false, "") dans printZodType, donc dnl explain omet des defaults valides.
|
|
48
49
|
if ("innerType" in def) {
|
|
49
50
|
const result = printZodType(def.innerType);
|
|
50
51
|
const defaultValue = typeof def.defaultValue === "function" ? def.defaultValue() : (def.defaultValue ?? undefined);
|
|
51
52
|
// const defaultValue = (def as any).defaultValue;
|
|
52
|
-
if (defaultValue) {
|
|
53
|
+
if (defaultValue !== undefined) {
|
|
53
54
|
return result + " (default: " + defaultValue.toString() + ")";
|
|
54
55
|
}
|
|
55
56
|
return result;
|
|
@@ -97,7 +98,7 @@ export function getDefaultEnvValue(def) {
|
|
|
97
98
|
const raw = typeof def.defaultValue === "function" ? def.defaultValue() : (def.defaultValue ?? undefined);
|
|
98
99
|
return stringifyEnvValue(raw);
|
|
99
100
|
}
|
|
100
|
-
// wrappers
|
|
101
|
+
// transparent wrappers
|
|
101
102
|
case "optional":
|
|
102
103
|
case "nullable":
|
|
103
104
|
if ("innerType" in def) {
|
|
@@ -119,13 +120,13 @@ export function isRequired(def) {
|
|
|
119
120
|
return false;
|
|
120
121
|
case "default":
|
|
121
122
|
return false;
|
|
122
|
-
// nullable
|
|
123
|
+
// nullable DOES NOT remove the required
|
|
123
124
|
case "nullable":
|
|
124
125
|
if ("innerType" in def) {
|
|
125
126
|
return isRequired(def.innerType);
|
|
126
127
|
}
|
|
127
128
|
return false;
|
|
128
|
-
// pipe / transform → transparent
|
|
129
|
+
// pipe / transform → transparent regarding required
|
|
129
130
|
case "pipe":
|
|
130
131
|
if ("in" in def) {
|
|
131
132
|
return isRequired(def.in);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-schema.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/resolve-schema.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolve-schema.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/resolve-schema.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,KAAG,MA6BpD,CAAC"}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { SchemaNotFoundError } from "../../errors.js";
|
|
3
4
|
const CANDIDATES = ["env.dnl.ts", "env.dnl.js", "dnl.config.ts", "dnl.config.js"];
|
|
4
5
|
export const resolveSchemaPath = (cliPath) => {
|
|
5
6
|
// 1. --schema
|
|
6
7
|
if (cliPath) {
|
|
7
|
-
|
|
8
|
+
const full = path.resolve(process.cwd(), cliPath);
|
|
9
|
+
if (!fs.existsSync(full)) {
|
|
10
|
+
throw new SchemaNotFoundError(`Schema file not found: ${cliPath}`);
|
|
11
|
+
}
|
|
12
|
+
return full;
|
|
8
13
|
}
|
|
9
14
|
// 2. package.json
|
|
10
15
|
const pkgPath = path.resolve(process.cwd(), "package.json");
|
|
@@ -18,10 +23,9 @@ export const resolveSchemaPath = (cliPath) => {
|
|
|
18
23
|
// 3. convention
|
|
19
24
|
for (const file of CANDIDATES) {
|
|
20
25
|
const full = path.resolve(process.cwd(), file);
|
|
21
|
-
console.log("full", full);
|
|
22
26
|
if (fs.existsSync(full)) {
|
|
23
27
|
return full;
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
|
-
throw new
|
|
30
|
+
throw new SchemaNotFoundError("No env schema found. Use --schema, define dotenv-never-lies.schema in package.json, or add env.dnl.ts");
|
|
27
31
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toFile.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/toFile.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,MAAM,GAAU,SAAS,MAAM,EAAE,MAAM,MAAM,EAAE,QAAO,OAAe,KAAG,OAAO,CAAC,IAAI,CAKhG,CAAC"}
|