@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.
Files changed (38) hide show
  1. package/README.md +437 -160
  2. package/dist/cli/commands/assert.d.ts +4 -2
  3. package/dist/cli/commands/assert.d.ts.map +1 -1
  4. package/dist/cli/commands/assert.js +7 -9
  5. package/dist/cli/commands/explain.d.ts +6 -1
  6. package/dist/cli/commands/explain.d.ts.map +1 -1
  7. package/dist/cli/commands/explain.js +9 -16
  8. package/dist/cli/commands/export.d.ts +35 -0
  9. package/dist/cli/commands/export.d.ts.map +1 -0
  10. package/dist/cli/commands/export.js +268 -0
  11. package/dist/cli/commands/generate.d.ts +10 -2
  12. package/dist/cli/commands/generate.d.ts.map +1 -1
  13. package/dist/cli/commands/generate.js +9 -7
  14. package/dist/cli/commands/reverseEnv.d.ts +8 -2
  15. package/dist/cli/commands/reverseEnv.d.ts.map +1 -1
  16. package/dist/cli/commands/reverseEnv.js +21 -8
  17. package/dist/cli/index.js +323 -99
  18. package/dist/cli/utils/exitCodes.d.ts +8 -0
  19. package/dist/cli/utils/exitCodes.d.ts.map +1 -0
  20. package/dist/cli/utils/exitCodes.js +8 -0
  21. package/dist/cli/utils/infer-schema.d.ts.map +1 -1
  22. package/dist/cli/utils/infer-schema.js +4 -3
  23. package/dist/cli/utils/load-schema.d.ts.map +1 -1
  24. package/dist/cli/utils/load-schema.js +20 -12
  25. package/dist/cli/utils/printer.d.ts.map +1 -1
  26. package/dist/cli/utils/printer.js +5 -4
  27. package/dist/cli/utils/resolve-schema.d.ts.map +1 -1
  28. package/dist/cli/utils/resolve-schema.js +7 -3
  29. package/dist/cli/utils/toFile.d.ts +2 -0
  30. package/dist/cli/utils/toFile.d.ts.map +1 -0
  31. package/dist/cli/utils/toFile.js +8 -0
  32. package/dist/core.d.ts +33 -33
  33. package/dist/core.js +10 -10
  34. package/dist/errors.d.ts +29 -2
  35. package/dist/errors.d.ts.map +1 -1
  36. package/dist/errors.js +33 -4
  37. package/dist/romaintaillandier1978-dotenv-never-lies-0.3.0.tgz +0 -0
  38. package/package.json +9 -9
@@ -1,5 +1,7 @@
1
- export declare const assertCommand: (opts: {
1
+ type AssertCliOptions = {
2
2
  schema: string;
3
3
  source: string;
4
- }) => Promise<void>;
4
+ };
5
+ export declare const assertCommand: (opts?: AssertCliOptions | undefined) => Promise<void>;
6
+ export {};
5
7
  //# sourceMappingURL=assert.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/assert.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,aAAa,GAAU,MAAM;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,kBAwB3E,CAAC"}
1
+ {"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/assert.ts"],"names":[],"mappings":"AAOA,KAAK,gBAAgB,GAAG;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,aAAa,GAAU,OAAO,gBAAgB,GAAG,SAAS,KAAG,OAAO,CAAC,IAAI,CAsBrF,CAAC"}
@@ -3,24 +3,22 @@ import dnl from "../../index.js";
3
3
  import { loadDef as loadDef } from "../utils/load-schema.js";
4
4
  import { resolveSchemaPath } from "../utils/resolve-schema.js";
5
5
  import { z } from "zod";
6
+ import { ValidationError } from "../../errors.js";
6
7
  export const assertCommand = async (opts) => {
7
- const schemaPath = resolveSchemaPath(opts.schema);
8
+ const schemaPath = resolveSchemaPath(opts?.schema);
8
9
  const envDef = (await loadDef(schemaPath));
9
10
  try {
10
11
  envDef.assert({
11
- source: opts.source ? dnl.readEnvFile(path.resolve(process.cwd(), opts.source)) : process.env,
12
+ source: opts?.source ? dnl.readEnvFile(path.resolve(process.cwd(), opts.source)) : process.env,
12
13
  });
13
14
  console.log("✅ Environment is valid");
14
15
  }
15
16
  catch (error) {
16
17
  if (error instanceof z.ZodError) {
17
- console.error("Invalid environment variables:\n");
18
- for (const issue of error.issues) {
19
- const key = issue.path.join(".");
20
- console.error(`- ${key}`);
21
- console.error(` → ${issue.message}`);
22
- }
23
- process.exit(1);
18
+ throw new ValidationError("Invalid environment variables", error.issues.map((issue) => ({
19
+ key: issue.path.join("."),
20
+ message: issue.message,
21
+ })));
24
22
  }
25
23
  throw error;
26
24
  }
@@ -1,8 +1,13 @@
1
+ import { Explanation } from "../utils/printer.js";
1
2
  type ExplainCliOptions = {
2
3
  schema?: string | undefined;
3
4
  keys?: string[] | undefined;
4
5
  format?: "human" | "json" | undefined;
5
6
  };
6
- export declare const explainCommand: (options?: ExplainCliOptions) => Promise<number>;
7
+ export declare const explainCommand: (options?: ExplainCliOptions) => Promise<{
8
+ format: "human" | "json";
9
+ result: Explanation[];
10
+ }>;
11
+ export declare const printHuman: (result: Explanation[]) => void;
7
12
  export {};
8
13
  //# sourceMappingURL=explain.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/explain.ts"],"names":[],"mappings":"AAKA,KAAK,iBAAiB,GAAG;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,UAAU,iBAAiB,KAAG,OAAO,CAAC,MAAM,CAmChF,CAAC"}
1
+ {"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/explain.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAiB,MAAM,qBAAqB,CAAC;AAGjE,KAAK,iBAAiB,GAAG;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,UAAU,iBAAiB,KAAG,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,EAAE,CAAA;CAAE,CA2B7H,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,QAAQ,WAAW,EAAE,SAqB/C,CAAC"}
@@ -1,10 +1,14 @@
1
1
  import { loadDef } from "../utils/load-schema.js";
2
2
  import { resolveSchemaPath } from "../utils/resolve-schema.js";
3
3
  import { toExplanation } from "../utils/printer.js";
4
+ import { UsageError } from "../../errors.js";
4
5
  export const explainCommand = async (options) => {
6
+ const format = options?.format ?? "human";
7
+ if (format !== "human" && format !== "json") {
8
+ throw new UsageError(`Invalid format: ${format}`);
9
+ }
5
10
  const schemaPath = resolveSchemaPath(options?.schema);
6
11
  const envDef = (await loadDef(schemaPath));
7
- const format = options?.format ?? "human";
8
12
  const keysToSerialize = new Array();
9
13
  if (options?.keys) {
10
14
  keysToSerialize.push(...options.keys);
@@ -17,25 +21,14 @@ export const explainCommand = async (options) => {
17
21
  result.push(toExplanation(key, value));
18
22
  }
19
23
  if (result.length === 0) {
20
- console.error("No variables found");
21
- return 1;
22
- }
23
- switch (format) {
24
- case "json":
25
- console.log(JSON.stringify(result, null, 2));
26
- return 0;
27
- case "human":
28
- printHuman(result);
29
- return 0;
30
- default:
31
- console.error(`Invalid format: ${format}`);
32
- return 1;
24
+ throw new UsageError("No matching environment variables found");
33
25
  }
26
+ return { format, result };
34
27
  };
35
- const printHuman = (result) => {
28
+ export const printHuman = (result) => {
36
29
  if (result.length > 1) {
37
30
  for (const item of result) {
38
- console.log(`${item.key}: ${item.description} --- dnl explain ${item.key} --format human for more details`);
31
+ console.log(`${item.key}: ${item.description} \t\tfor more details : dnl explain ${item.key}`);
39
32
  }
40
33
  return;
41
34
  }
@@ -0,0 +1,35 @@
1
+ import dnl from "../../index.js";
2
+ export declare const exportFormatsNames: readonly ["docker-env", "docker-args", "env", "k8s-configmap", "k8s-secret", "github-env", "github-secret", "gitlab-env", "json", "ts", "js"];
3
+ export type ExportFormat = (typeof exportFormatsNames)[number];
4
+ export type ExportResult = {
5
+ content: string;
6
+ warnings: string[];
7
+ out?: string;
8
+ };
9
+ export type ExportCliOptions = {
10
+ format: ExportFormat;
11
+ schema?: string | undefined;
12
+ source?: string | undefined;
13
+ hideSecret?: boolean;
14
+ excludeSecret?: boolean;
15
+ includeComments?: boolean;
16
+ serializeTyped?: boolean;
17
+ out?: string | undefined;
18
+ force?: boolean;
19
+ k8sName?: string | undefined;
20
+ githubOrg?: string | undefined;
21
+ };
22
+ export declare const exportCommand: (options: ExportCliOptions) => Promise<ExportResult>;
23
+ export declare const contentByFormat: (format: ExportFormat, envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
24
+ export declare const exportEnv: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
25
+ export declare const exportDockerArgs: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
26
+ export declare const exportDockerEnv: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
27
+ export declare const exportK8sConfigmap: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
28
+ export declare const exportK8sSecret: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
29
+ export declare const exportGithubEnv: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
30
+ export declare const exportGithubSecret: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
31
+ export declare const exportGitlabEnv: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
32
+ export declare const exportJson: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
33
+ export declare const exportTs: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
34
+ export declare const exportJs: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
35
+ //# sourceMappingURL=export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/export.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,268 @@
1
+ import dnl from "../../index.js";
2
+ import { loadDef } from "../utils/load-schema.js";
3
+ import { resolveSchemaPath } from "../utils/resolve-schema.js";
4
+ import path from "path";
5
+ import { UsageError } from "../../errors.js";
6
+ export const exportFormatsNames = [
7
+ "docker-env",
8
+ "docker-args",
9
+ "env",
10
+ "k8s-configmap",
11
+ "k8s-secret",
12
+ "github-env",
13
+ "github-secret",
14
+ "gitlab-env",
15
+ "json",
16
+ "ts",
17
+ "js",
18
+ ];
19
+ const shellEscape = (value) => {
20
+ if (value.length === 0) {
21
+ return "''";
22
+ }
23
+ return `'${value.replace(/'/g, "'\\''")}'`;
24
+ };
25
+ export const exportCommand = async (options) => {
26
+ if (options.githubOrg && options.format !== "github-secret") {
27
+ throw new UsageError("--github-org can only be used with the github-secret format");
28
+ }
29
+ if (options.k8sName && !options.format.startsWith("k8s-")) {
30
+ throw new UsageError("--k8s-name can only be used with a k8s-* format");
31
+ }
32
+ if (options.serializeTyped && !["js", "ts", "json"].includes(options.format)) {
33
+ throw new UsageError("--serialize-typed is only valid for js, ts and json exports");
34
+ }
35
+ const schemaPath = resolveSchemaPath(options?.schema);
36
+ const envDef = (await loadDef(schemaPath));
37
+ const warnings = [];
38
+ const content = contentByFormat(options.format, envDef, options, warnings);
39
+ return {
40
+ content,
41
+ warnings,
42
+ out: options.out,
43
+ };
44
+ };
45
+ export const contentByFormat = (format, envDef, options, warnings) => {
46
+ switch (format) {
47
+ case "k8s-configmap":
48
+ return exportK8sConfigmap(envDef, options, warnings);
49
+ case "k8s-secret":
50
+ return exportK8sSecret(envDef, options, warnings);
51
+ case "ts":
52
+ return exportTs(envDef, options, warnings);
53
+ case "js":
54
+ return exportJs(envDef, options, warnings);
55
+ case "github-env":
56
+ return exportGithubEnv(envDef, options, warnings);
57
+ case "github-secret":
58
+ return exportGithubSecret(envDef, options, warnings);
59
+ case "gitlab-env":
60
+ return exportGitlabEnv(envDef, options, warnings);
61
+ case "json":
62
+ return exportJson(envDef, options, warnings);
63
+ case "env":
64
+ return exportEnv(envDef, options, warnings);
65
+ case "docker-args":
66
+ return exportDockerArgs(envDef, options, warnings);
67
+ case "docker-env":
68
+ return exportDockerEnv(envDef, options, warnings);
69
+ default:
70
+ throw new UsageError(`Unsupported format: ${format}`);
71
+ }
72
+ };
73
+ // #region serialisation env-like
74
+ const getRawValue = (key, source, envDef, options) => {
75
+ if (options?.hideSecret && envDef.def[key].secret) {
76
+ return "********";
77
+ }
78
+ const raw = source[key]; // raw value from source (i.e. .env or process.env)
79
+ return raw == null ? "" : String(raw);
80
+ };
81
+ // this clones the .env
82
+ export const exportEnv = (envDef, options, warnings) => {
83
+ const source = options?.source ? dnl.readEnvFile(path.resolve(process.cwd(), options.source)) : process.env;
84
+ const values = envDef.assert({ source });
85
+ const args = [];
86
+ for (const key of Object.keys(values)) {
87
+ if (options?.excludeSecret && envDef.def[key].secret) {
88
+ continue;
89
+ }
90
+ if (options?.includeComments && envDef.def[key].description) {
91
+ args.push(`# ${envDef.def[key].description}`);
92
+ }
93
+ const rawValue = getRawValue(key, source, envDef, options);
94
+ args.push(`${key}=${rawValue}`);
95
+ }
96
+ return args.join("\n");
97
+ };
98
+ export const exportDockerArgs = (envDef, options, warnings) => {
99
+ if (options?.includeComments) {
100
+ warnings.push("The --include-comments option is invalid with the docker-args format");
101
+ }
102
+ const source = options?.source ? dnl.readEnvFile(path.resolve(process.cwd(), options.source)) : process.env;
103
+ const values = envDef.assert({ source });
104
+ const args = [];
105
+ for (const key of Object.keys(values)) {
106
+ if (options?.excludeSecret && envDef.def[key].secret) {
107
+ continue;
108
+ }
109
+ const rawValue = getRawValue(key, source, envDef, options);
110
+ args.push(`-e ${shellEscape(`${key}=${rawValue}`)}`);
111
+ }
112
+ return args.join(" ");
113
+ };
114
+ export const exportDockerEnv = (envDef, options, warnings) => {
115
+ const source = options?.source ? dnl.readEnvFile(path.resolve(process.cwd(), options.source)) : process.env;
116
+ const values = envDef.assert({ source });
117
+ const args = [];
118
+ for (const key of Object.keys(values)) {
119
+ if (options?.excludeSecret && envDef.def[key].secret) {
120
+ continue;
121
+ }
122
+ if (options?.includeComments && envDef.def[key].description) {
123
+ args.push(`# ${envDef.def[key].description}`);
124
+ }
125
+ const rawValue = getRawValue(key, source, envDef, options);
126
+ args.push(`${key}=${rawValue}`);
127
+ }
128
+ return args.join("\n");
129
+ };
130
+ export const exportK8sConfigmap = (envDef, options, warnings) => {
131
+ const source = options?.source ? dnl.readEnvFile(path.resolve(process.cwd(), options.source)) : process.env;
132
+ const values = envDef.assert({ source });
133
+ const args = [];
134
+ args.push(`apiVersion: v1`);
135
+ args.push(`kind: ConfigMap`);
136
+ args.push(`metadata:`);
137
+ const name = options?.k8sName ?? "env-config";
138
+ args.push(` name: ${name}`);
139
+ args.push(`data:`);
140
+ for (const key of Object.keys(values)) {
141
+ if (envDef.def[key].secret) {
142
+ if (options?.excludeSecret)
143
+ continue;
144
+ if (!options?.hideSecret) {
145
+ warnings.push(`Secret ${key} exported in a ConfigMap. Use the k8s-secret format.`);
146
+ }
147
+ }
148
+ const rawValue = getRawValue(key, source, envDef, options);
149
+ args.push(` ${key}: ${JSON.stringify(rawValue)}`);
150
+ }
151
+ return args.join("\n");
152
+ };
153
+ export const exportK8sSecret = (envDef, options, warnings) => {
154
+ const source = options?.source ? dnl.readEnvFile(path.resolve(process.cwd(), options.source)) : process.env;
155
+ const values = envDef.assert({ source });
156
+ const args = [];
157
+ args.push(`apiVersion: v1`);
158
+ args.push(`kind: Secret`);
159
+ args.push(`type: Opaque`);
160
+ args.push(`metadata:`);
161
+ const name = options?.k8sName ?? "env-secret";
162
+ args.push(` name: ${name}`);
163
+ args.push(`stringData:`);
164
+ for (const key of Object.keys(values)) {
165
+ if (options?.excludeSecret && envDef.def[key].secret)
166
+ continue;
167
+ if (!envDef.def[key].secret)
168
+ continue; // Secret = only variables marked as secret
169
+ const rawValue = getRawValue(key, source, envDef, options);
170
+ args.push(` ${key}: ${JSON.stringify(rawValue)}`);
171
+ }
172
+ return args.join("\n");
173
+ };
174
+ export const exportGithubEnv = (envDef, options, warnings) => {
175
+ const source = options?.source ? dnl.readEnvFile(path.resolve(process.cwd(), options.source)) : process.env;
176
+ const values = envDef.assert({ source });
177
+ const args = [];
178
+ for (const key of Object.keys(values)) {
179
+ if (options?.excludeSecret && envDef.def[key].secret)
180
+ continue;
181
+ const rawValue = getRawValue(key, source, envDef, options);
182
+ args.push(`printf '%s\\n' ${shellEscape(`${key}=${rawValue}`)} >> "$GITHUB_ENV"`);
183
+ }
184
+ return args.join("\n");
185
+ };
186
+ export const exportGithubSecret = (envDef, options, warnings) => {
187
+ if (options?.hideSecret) {
188
+ warnings.push("The --hide-secret option is incompatible with github-secret");
189
+ }
190
+ if (options?.githubOrg && options.githubOrg.includes(" ")) {
191
+ warnings.push("github-org contains a space; gh command likely invalid");
192
+ }
193
+ const source = options?.source ? dnl.readEnvFile(path.resolve(process.cwd(), options.source)) : process.env;
194
+ const values = envDef.assert({ source });
195
+ const scopeFlag = options?.githubOrg ? `--org ${shellEscape(options.githubOrg)}` : "";
196
+ const args = [];
197
+ for (const key of Object.keys(values)) {
198
+ if (!envDef.def[key].secret)
199
+ continue;
200
+ if (options?.excludeSecret)
201
+ continue;
202
+ const rawValue = getRawValue(key, source, envDef, options);
203
+ args.push(`printf '%s' ${shellEscape(rawValue)} | gh secret set ${key} ${scopeFlag} --body-file -`.trim());
204
+ }
205
+ return args.join("\n");
206
+ };
207
+ export const exportGitlabEnv = (envDef, options, warnings) => {
208
+ return exportEnv(envDef, options, warnings);
209
+ };
210
+ // #endregion serialisation env-like
211
+ // #region serialisation json/ts/js
212
+ // WARNING : For design reasons and separation of concerns, do not attempt to merge getRawValue and getTypedOrRawValue into a single function.
213
+ const getTypedOrRawValue = (key, source, values, envDef, options) => {
214
+ if (options?.hideSecret && envDef.def[key].secret) {
215
+ return "********";
216
+ }
217
+ if (options?.serializeTyped) {
218
+ return values[key]; // runtime validated value
219
+ }
220
+ const raw = source[key]; // raw value from source (i.e. .env or process.env)
221
+ return raw == null ? "" : String(raw);
222
+ };
223
+ export const exportJson = (envDef, options, warnings) => {
224
+ if (options?.includeComments) {
225
+ warnings.push("The --include-comments option is ignored for the json format");
226
+ }
227
+ const source = options?.source ? dnl.readEnvFile(path.resolve(process.cwd(), options.source)) : process.env;
228
+ const values = envDef.assert({ source });
229
+ const args = {};
230
+ for (const key of Object.keys(values)) {
231
+ if (options?.excludeSecret && envDef.def[key].secret) {
232
+ continue;
233
+ }
234
+ args[key] = getTypedOrRawValue(key, source, values, envDef, options);
235
+ }
236
+ return JSON.stringify(args, null, 2);
237
+ };
238
+ export const exportTs = (envDef, options, warnings) => {
239
+ const source = options?.source ? dnl.readEnvFile(path.resolve(process.cwd(), options.source)) : process.env;
240
+ const values = envDef.assert({ source });
241
+ const middle = [];
242
+ for (const key of Object.keys(values)) {
243
+ if (options?.excludeSecret && envDef.def[key].secret) {
244
+ continue;
245
+ }
246
+ if (options?.includeComments && envDef.def[key].description) {
247
+ middle.push(` // ${envDef.def[key].description}`);
248
+ }
249
+ middle.push(` ${key}: ${JSON.stringify(getTypedOrRawValue(key, source, values, envDef, options), null, 2)},`);
250
+ }
251
+ return `export const env = {\n${middle.join("\n")}\n} as const;`;
252
+ };
253
+ export const exportJs = (envDef, options, warnings) => {
254
+ const source = options?.source ? dnl.readEnvFile(path.resolve(process.cwd(), options.source)) : process.env;
255
+ const values = envDef.assert({ source });
256
+ const middle = [];
257
+ for (const key of Object.keys(values)) {
258
+ if (options?.excludeSecret && envDef.def[key].secret) {
259
+ continue;
260
+ }
261
+ if (options?.includeComments && envDef.def[key].description) {
262
+ middle.push(` // ${envDef.def[key].description}`);
263
+ }
264
+ middle.push(` ${key}: ${JSON.stringify(getTypedOrRawValue(key, source, values, envDef, options), null, 2)},`);
265
+ }
266
+ return `export const env = {\n${middle.join("\n")}\n};`;
267
+ };
268
+ // #endregion serialisation json/ts/js
@@ -1,7 +1,15 @@
1
- export declare const generateCommand: (opts: {
1
+ export type GenerateCliOptions = {
2
2
  schema?: string;
3
3
  out?: string;
4
4
  includeSecret?: boolean;
5
5
  force?: boolean;
6
- }) => Promise<void>;
6
+ };
7
+ export type GenerateResult = {
8
+ content: string;
9
+ out: string;
10
+ };
11
+ export declare const generateCommand: (opts?: GenerateCliOptions | undefined) => Promise<{
12
+ content: string;
13
+ out: string;
14
+ }>;
7
15
  //# sourceMappingURL=generate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,eAAe,GAAU,MAAM;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,kBA4BtH,CAAC"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,kBAAkB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AACF,MAAM,MAAM,cAAc,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,eAAe,GAAU,OAAO,kBAAkB,GAAG,SAAS,KAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CA6BrH,CAAC"}
@@ -3,14 +3,14 @@ import path from "node:path";
3
3
  import { loadDef } from "../utils/load-schema.js";
4
4
  import { resolveSchemaPath } from "../utils/resolve-schema.js";
5
5
  import { getDefaultEnvValue } from "../utils/printer.js";
6
+ import { ExportError } from "../../errors.js";
6
7
  export const generateCommand = async (opts) => {
7
- const outFile = opts.out ?? ".env";
8
+ const outFile = opts?.out ?? ".env";
8
9
  const target = path.resolve(process.cwd(), outFile);
9
- if (fs.existsSync(target) && !opts.force) {
10
- console.error(`❌ ${outFile} already exists. Use --force to overwrite.`);
11
- process.exit(1);
10
+ if (fs.existsSync(target) && !opts?.force) {
11
+ throw new ExportError(`${outFile} already exists. Use --force to overwrite.`);
12
12
  }
13
- const schemaPath = resolveSchemaPath(opts.schema);
13
+ const schemaPath = resolveSchemaPath(opts?.schema);
14
14
  const envDef = (await loadDef(schemaPath));
15
15
  const lines = [];
16
16
  for (const [key, def] of Object.entries(envDef.def)) {
@@ -23,6 +23,8 @@ export const generateCommand = async (opts) => {
23
23
  lines.push(""); // blank line
24
24
  }
25
25
  const output = lines.join("\n");
26
- fs.writeFileSync(target, output);
27
- console.log(`✅ ${outFile} generated`);
26
+ return {
27
+ content: output,
28
+ out: target,
29
+ };
28
30
  };
@@ -1,7 +1,13 @@
1
- export declare const reverseEnvCommand: (opts: {
1
+ export type ReverseEnvCliOptions = {
2
2
  source?: string;
3
3
  out?: string;
4
4
  force?: boolean;
5
5
  guessSecret?: boolean;
6
- }) => Promise<void>;
6
+ };
7
+ export type ReverseEnvResult = {
8
+ content: string;
9
+ out: string;
10
+ warnings: string[];
11
+ };
12
+ export declare const reverseEnvCommand: (opts?: ReverseEnvCliOptions | undefined) => Promise<ReverseEnvResult>;
7
13
  //# sourceMappingURL=reverseEnv.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reverseEnv.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/reverseEnv.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,iBAAiB,GAAU,MAAM;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,kBAiCtH,CAAC"}
1
+ {"version":3,"file":"reverseEnv.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/reverseEnv.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,oBAAoB,GAAG;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AACF,eAAO,MAAM,iBAAiB,GAAU,OAAO,oBAAoB,GAAG,SAAS,KAAG,OAAO,CAAC,gBAAgB,CA6CzG,CAAC"}
@@ -2,16 +2,20 @@ import path from "node:path";
2
2
  import dnl from "../../index.js";
3
3
  import { guessSecret, inferSchema } from "../utils/infer-schema.js";
4
4
  import fs from "node:fs";
5
+ import { ExportError } from "../../errors.js";
5
6
  export const reverseEnvCommand = async (opts) => {
6
- const source = path.resolve(process.cwd(), opts.source ?? ".env");
7
- const out = opts.out ?? "env.dnl.ts";
7
+ const source = path.resolve(process.cwd(), opts?.source ?? ".env");
8
+ if (!fs.existsSync(source)) {
9
+ throw new ExportError(`Source env file not found: ${source}`);
10
+ }
11
+ const out = opts?.out ?? "env.dnl.ts";
8
12
  const target = path.resolve(process.cwd(), out);
9
- if (fs.existsSync(target) && !opts.force) {
10
- console.error(`❌ ${out} already exists. Use --force to overwrite.`);
11
- process.exit(1);
13
+ if (fs.existsSync(target) && !opts?.force) {
14
+ throw new ExportError(`${out} already exists. Use --force to overwrite.`);
12
15
  }
13
16
  const env = dnl.readEnvFile(source);
14
17
  const lines = [];
18
+ const warnings = [];
15
19
  lines.push(`// ⚠️ This file was generated by dotenv-never-lies`);
16
20
  lines.push(`// Review and adjust schemas, descriptions and secrets before using`);
17
21
  lines.push("");
@@ -20,14 +24,23 @@ export const reverseEnvCommand = async (opts) => {
20
24
  lines.push("");
21
25
  lines.push(`export default define({`);
22
26
  for (const [key, value] of Object.entries(env)) {
23
- lines.push(` ${key}: {`);
27
+ const isValidIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key);
28
+ const safeKey = isValidIdentifier ? key : JSON.stringify(key);
29
+ if (!isValidIdentifier) {
30
+ warnings.push(`Key ${key} is not a valid identifier. It has been escaped to ${safeKey}.`);
31
+ }
32
+ lines.push(` ${safeKey}: {`);
24
33
  lines.push(` description: "TODO",`);
25
34
  lines.push(` schema: ${inferSchema(value)},`);
26
- if (opts.guessSecret && guessSecret(key)) {
35
+ if (opts?.guessSecret && guessSecret(key)) {
27
36
  lines.push(` secret: true,`);
28
37
  }
29
38
  lines.push(` },`);
30
39
  }
31
40
  lines.push(`});`);
32
- fs.writeFileSync(target, lines.join("\n"));
41
+ return {
42
+ content: lines.join("\n"),
43
+ out: target,
44
+ warnings: [...warnings, "Generated schema is a prototype. Review schemas, descriptions and secrets before using."],
45
+ };
33
46
  };