@romaintaillandier1978/dotenv-never-lies 1.3.0 → 1.5.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 +7 -5
- package/dist/cli/commands/assert.d.ts +5 -1
- package/dist/cli/commands/assert.d.ts.map +1 -1
- package/dist/cli/commands/assert.js +5 -2
- package/dist/cli/commands/export.d.ts +1 -0
- package/dist/cli/commands/export.d.ts.map +1 -1
- package/dist/cli/commands/export.js +32 -10
- package/dist/cli/commands/infer.d.ts +4 -1
- package/dist/cli/commands/infer.d.ts.map +1 -1
- package/dist/cli/commands/infer.js +57 -19
- package/dist/cli/index.d.ts +2 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +36 -11
- package/dist/cli/utils/infer-rule-engine.d.ts +4 -0
- package/dist/cli/utils/infer-rule-engine.d.ts.map +1 -0
- package/dist/cli/utils/infer-rule-engine.js +30 -0
- package/dist/cli/utils/resolve-schema.d.ts.map +1 -1
- package/dist/cli/utils/resolve-schema.js +17 -16
- package/dist/core.d.ts +8 -3
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +29 -1
- package/dist/dnl-config.d.ts +10 -0
- package/dist/dnl-config.d.ts.map +1 -0
- package/dist/dnl-config.js +4 -0
- package/dist/infer/__tests__/rules/key-value.test.d.ts +2 -0
- package/dist/infer/__tests__/rules/key-value.test.d.ts.map +1 -0
- package/dist/infer/__tests__/rules/key-value.test.js +56 -0
- package/dist/infer/__tests__/rules/list.test.js +12 -45
- package/dist/infer/cross-rules/secret-non-string.d.ts +3 -0
- package/dist/infer/cross-rules/secret-non-string.d.ts.map +1 -0
- package/dist/infer/cross-rules/secret-non-string.js +5 -0
- package/dist/infer/generated/basic.d.ts +7 -7
- package/dist/infer/generated/basic.d.ts.map +1 -1
- package/dist/infer/generated/basic.js +12 -6
- package/dist/infer/generated/boolean.d.ts +2 -2
- package/dist/infer/generated/boolean.d.ts.map +1 -1
- package/dist/infer/generated/boolean.js +3 -2
- package/dist/infer/generated/duration.d.ts +2 -2
- package/dist/infer/generated/duration.d.ts.map +1 -1
- package/dist/infer/generated/duration.js +1 -0
- package/dist/infer/generated/ip.d.ts +2 -2
- package/dist/infer/generated/ip.d.ts.map +1 -1
- package/dist/infer/generated/ip.js +1 -0
- package/dist/infer/generated/json.d.ts +3 -3
- package/dist/infer/generated/json.d.ts.map +1 -1
- package/dist/infer/generated/json.js +1 -0
- package/dist/infer/generated/key-value.d.ts +4 -0
- package/dist/infer/generated/key-value.d.ts.map +1 -0
- package/dist/infer/generated/key-value.js +11 -0
- package/dist/infer/generated/list.d.ts +5 -7
- package/dist/infer/generated/list.d.ts.map +1 -1
- package/dist/infer/generated/list.js +13 -21
- package/dist/infer/generated/port.d.ts +3 -3
- package/dist/infer/generated/port.d.ts.map +1 -1
- package/dist/infer/generated/port.js +1 -0
- package/dist/infer/generated/url.d.ts +13 -13
- package/dist/infer/generated/url.d.ts.map +1 -1
- package/dist/infer/generated/url.js +6 -0
- package/dist/infer/generated/version.d.ts +3 -3
- package/dist/infer/generated/version.d.ts.map +1 -1
- package/dist/infer/generated/version.js +1 -0
- package/dist/infer/helpers.d.ts +1 -2
- package/dist/infer/helpers.d.ts.map +1 -1
- package/dist/infer/helpers.js +5 -16
- package/dist/infer/index.d.ts +2 -2
- package/dist/infer/index.d.ts.map +1 -1
- package/dist/infer/index.js +7 -11
- package/dist/infer/official-preset-registry.d.ts +3 -0
- package/dist/infer/official-preset-registry.d.ts.map +1 -0
- package/dist/infer/official-preset-registry.js +67 -0
- package/dist/infer/presets/agenda.d.ts +3 -0
- package/dist/infer/presets/agenda.d.ts.map +1 -0
- package/dist/infer/presets/agenda.js +15 -0
- package/dist/infer/presets/amqplib.d.ts +3 -0
- package/dist/infer/presets/amqplib.d.ts.map +1 -0
- package/dist/infer/presets/amqplib.js +15 -0
- package/dist/infer/presets/aws-sdk.d.ts +3 -0
- package/dist/infer/presets/aws-sdk.d.ts.map +1 -0
- package/dist/infer/presets/aws-sdk.js +33 -0
- package/dist/infer/presets/aws-sdk_client-s3.d.ts +3 -0
- package/dist/infer/presets/aws-sdk_client-s3.d.ts.map +1 -0
- package/dist/infer/presets/aws-sdk_client-s3.js +15 -0
- package/dist/infer/presets/bcrypt.d.ts +3 -0
- package/dist/infer/presets/bcrypt.d.ts.map +1 -0
- package/dist/infer/presets/bcrypt.js +15 -0
- package/dist/infer/presets/bull.d.ts +3 -0
- package/dist/infer/presets/bull.d.ts.map +1 -0
- package/dist/infer/presets/bull.js +116 -0
- package/dist/infer/presets/cookie-parser.d.ts +3 -0
- package/dist/infer/presets/cookie-parser.d.ts.map +1 -0
- package/dist/infer/presets/cookie-parser.js +15 -0
- package/dist/infer/presets/cron.d.ts +3 -0
- package/dist/infer/presets/cron.d.ts.map +1 -0
- package/dist/infer/presets/cron.js +22 -0
- package/dist/infer/presets/dotenv.d.ts +3 -0
- package/dist/infer/presets/dotenv.d.ts.map +1 -0
- package/dist/infer/presets/dotenv.js +15 -0
- package/dist/infer/presets/express-session.d.ts +3 -0
- package/dist/infer/presets/express-session.d.ts.map +1 -0
- package/dist/infer/presets/express-session.js +40 -0
- package/dist/infer/presets/google-cloud_storage.d.ts +3 -0
- package/dist/infer/presets/google-cloud_storage.d.ts.map +1 -0
- package/dist/infer/presets/google-cloud_storage.js +15 -0
- package/dist/infer/presets/google-maps.d.ts +3 -0
- package/dist/infer/presets/google-maps.d.ts.map +1 -0
- package/dist/infer/presets/google-maps.js +15 -0
- package/dist/infer/presets/ioredis.d.ts +3 -0
- package/dist/infer/presets/ioredis.d.ts.map +1 -0
- package/dist/infer/presets/ioredis.js +15 -0
- package/dist/infer/presets/jsonwebtoken.d.ts +3 -0
- package/dist/infer/presets/jsonwebtoken.d.ts.map +1 -0
- package/dist/infer/presets/jsonwebtoken.js +40 -0
- package/dist/infer/presets/mongoose.d.ts +3 -0
- package/dist/infer/presets/mongoose.d.ts.map +1 -0
- package/dist/infer/presets/mongoose.js +15 -0
- package/dist/infer/presets/multer.d.ts +3 -0
- package/dist/infer/presets/multer.d.ts.map +1 -0
- package/dist/infer/presets/multer.js +15 -0
- package/dist/infer/presets/mysql2.d.ts +3 -0
- package/dist/infer/presets/mysql2.d.ts.map +1 -0
- package/dist/infer/presets/mysql2.js +15 -0
- package/dist/infer/presets/newrelic.d.ts +3 -0
- package/dist/infer/presets/newrelic.d.ts.map +1 -0
- package/dist/infer/presets/newrelic.js +15 -0
- package/dist/infer/presets/node.d.ts +3 -0
- package/dist/infer/presets/node.d.ts.map +1 -0
- package/dist/infer/presets/node.js +47 -0
- package/dist/infer/presets/nodemailer.d.ts +3 -0
- package/dist/infer/presets/nodemailer.d.ts.map +1 -0
- package/dist/infer/presets/nodemailer.js +50 -0
- package/dist/infer/presets/passport-github2.d.ts +3 -0
- package/dist/infer/presets/passport-github2.d.ts.map +1 -0
- package/dist/infer/presets/passport-github2.js +24 -0
- package/dist/infer/presets/passport-google-oauth20.d.ts +3 -0
- package/dist/infer/presets/passport-google-oauth20.d.ts.map +1 -0
- package/dist/infer/presets/passport-google-oauth20.js +24 -0
- package/dist/infer/presets/passport-jwt.d.ts +3 -0
- package/dist/infer/presets/passport-jwt.d.ts.map +1 -0
- package/dist/infer/presets/passport-jwt.js +15 -0
- package/dist/infer/presets/passport.d.ts +3 -0
- package/dist/infer/presets/passport.d.ts.map +1 -0
- package/dist/infer/presets/passport.js +15 -0
- package/dist/infer/presets/pg.d.ts +3 -0
- package/dist/infer/presets/pg.d.ts.map +1 -0
- package/dist/infer/presets/pg.js +15 -0
- package/dist/infer/presets/pino.d.ts +3 -0
- package/dist/infer/presets/pino.d.ts.map +1 -0
- package/dist/infer/presets/pino.js +15 -0
- package/dist/infer/presets/prisma.d.ts +3 -0
- package/dist/infer/presets/prisma.d.ts.map +1 -0
- package/dist/infer/presets/prisma.js +24 -0
- package/dist/infer/presets/sentry.d.ts +3 -0
- package/dist/infer/presets/sentry.d.ts.map +1 -0
- package/dist/infer/presets/sentry.js +15 -0
- package/dist/infer/presets/something.d.ts +4 -0
- package/dist/infer/presets/something.d.ts.map +1 -0
- package/dist/infer/presets/something.js +29 -0
- package/dist/infer/presets/stripe.d.ts +3 -0
- package/dist/infer/presets/stripe.d.ts.map +1 -0
- package/dist/infer/presets/stripe.js +33 -0
- package/dist/infer/presets/typeorm.d.ts +3 -0
- package/dist/infer/presets/typeorm.d.ts.map +1 -0
- package/dist/infer/presets/typeorm.js +57 -0
- package/dist/infer/presets/vitest.d.ts +3 -0
- package/dist/infer/presets/vitest.d.ts.map +1 -0
- package/dist/infer/presets/vitest.js +13 -0
- package/dist/infer/presets/winston.d.ts +3 -0
- package/dist/infer/presets/winston.d.ts.map +1 -0
- package/dist/infer/presets/winston.js +15 -0
- package/dist/infer/presets.d.ts +6 -0
- package/dist/infer/presets.d.ts.map +1 -0
- package/dist/infer/presets.js +92 -0
- package/dist/infer/presets.types.d.ts +10 -0
- package/dist/infer/presets.types.d.ts.map +1 -0
- package/dist/infer/presets.types.js +1 -0
- package/dist/infer/rules/basic.d.ts +2 -2
- package/dist/infer/rules/basic.d.ts.map +1 -1
- package/dist/infer/rules/basic.js +7 -7
- package/dist/infer/rules/boolean.d.ts +2 -2
- package/dist/infer/rules/boolean.d.ts.map +1 -1
- package/dist/infer/rules/boolean.js +1 -1
- package/dist/infer/rules/duration.d.ts +2 -2
- package/dist/infer/rules/duration.d.ts.map +1 -1
- package/dist/infer/rules/duration.js +1 -1
- package/dist/infer/rules/ip.d.ts +2 -2
- package/dist/infer/rules/ip.d.ts.map +1 -1
- package/dist/infer/rules/ip.js +1 -1
- package/dist/infer/rules/json.d.ts +2 -2
- package/dist/infer/rules/json.d.ts.map +1 -1
- package/dist/infer/rules/json.js +2 -2
- package/dist/infer/rules/key-value.d.ts +3 -0
- package/dist/infer/rules/key-value.d.ts.map +1 -0
- package/dist/infer/rules/key-value.js +54 -0
- package/dist/infer/rules/list.d.ts +2 -4
- package/dist/infer/rules/list.d.ts.map +1 -1
- package/dist/infer/rules/list.js +52 -163
- package/dist/infer/rules/port.d.ts +2 -2
- package/dist/infer/rules/port.d.ts.map +1 -1
- package/dist/infer/rules/port.js +2 -2
- package/dist/infer/rules/url.d.ts +2 -2
- package/dist/infer/rules/url.d.ts.map +1 -1
- package/dist/infer/rules/url.js +15 -56
- package/dist/infer/rules/version.d.ts +2 -2
- package/dist/infer/rules/version.d.ts.map +1 -1
- package/dist/infer/rules/version.js +1 -1
- package/dist/infer/rules.d.ts +5 -0
- package/dist/infer/rules.d.ts.map +1 -0
- package/dist/infer/rules.js +31 -0
- package/dist/infer/rules.types.d.ts +144 -0
- package/dist/infer/rules.types.d.ts.map +1 -0
- package/dist/infer/rules.types.js +17 -0
- package/dist/infer/scripts/official-preset-registry.gen.d.ts +13 -0
- package/dist/infer/scripts/official-preset-registry.gen.d.ts.map +1 -0
- package/dist/infer/scripts/official-preset-registry.gen.js +154 -0
- package/dist/infer/types.d.ts +60 -7
- package/dist/infer/types.d.ts.map +1 -1
- package/dist/infer/types.js +16 -0
- package/dist/sample/env.dnl.d.ts +13 -385
- package/dist/sample/env.dnl.d.ts.map +1 -1
- package/dist/sample/env.dnl.js +15 -392
- package/dist/sample/main.d.ts +2 -0
- package/dist/sample/main.d.ts.map +1 -0
- package/dist/sample/main.js +17 -0
- package/dist/schemas/boolean.d.ts +1 -1
- package/dist/schemas/boolean.d.ts.map +1 -1
- package/dist/schemas/boolean.js +1 -1
- package/dist/schemas/common.d.ts +39 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +1 -0
- package/dist/schemas/key-value.d.ts +10 -0
- package/dist/schemas/key-value.d.ts.map +1 -0
- package/dist/schemas/key-value.js +33 -0
- package/dist/schemas/list.d.ts +4 -46
- package/dist/schemas/list.d.ts.map +1 -1
- package/dist/schemas/list.js +1 -36
- package/dist/schemas/path.d.ts +2 -2
- package/dist/schemas/path.d.ts.map +1 -1
- package/dist/schemas/path.js +2 -2
- package/dist/schemas/urls.d.ts.map +1 -1
- package/dist/schemas/urls.js +10 -1
- package/dist/scripts/script-desc.d.ts +6 -0
- package/dist/scripts/script-desc.d.ts.map +1 -0
- package/dist/scripts/script-desc.js +26 -0
- package/dist/utils/read-user-package-json.d.ts +5 -0
- package/dist/utils/read-user-package-json.d.ts.map +1 -0
- package/dist/utils/read-user-package-json.js +26 -0
- package/package.json +26 -3
package/README.md
CHANGED
|
@@ -10,12 +10,13 @@ It fails fast, loud, and before production.
|
|
|
10
10
|
|
|
11
11
|
## Why?
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
ecause all of this happens all the time:
|
|
14
14
|
|
|
15
15
|
- ❌ a missing env variable → runtime crash
|
|
16
16
|
- ❌ a malformed URL → subtle production bug
|
|
17
17
|
- ❌ CI was not updated after a new variable → confusing red deployment
|
|
18
18
|
- ❌ an optimistic `process.env.FOO!` → lying to yourself
|
|
19
|
+
- ❌ dotenv silently overrides duplicate keys
|
|
19
20
|
|
|
20
21
|
And because `.env` files are:
|
|
21
22
|
|
|
@@ -30,12 +31,13 @@ And because `.env` files are:
|
|
|
30
31
|
|
|
31
32
|
## What the library does
|
|
32
33
|
|
|
33
|
-
- ✅ validates environment variables at startup
|
|
34
|
-
powered by
|
|
35
|
-
- ✅
|
|
34
|
+
- ✅ validates environment variables at startup
|
|
35
|
+
powered by Zod, enabling complex transformations (arrays, parsing, coercion…)
|
|
36
|
+
- ✅ prevents duplicate environment variables
|
|
37
|
+
- ✅ infers a first real-world DNL schema from an existing `.env`
|
|
36
38
|
- ✅ provides reliable TypeScript typings
|
|
37
39
|
- ✅ documents each variable
|
|
38
|
-
- ✅ exposes a CLI for CI and humans (dnl export)
|
|
40
|
+
- ✅ exposes a CLI for CI and humans (`dnl export`)
|
|
39
41
|
|
|
40
42
|
---
|
|
41
43
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { ProgramCliOptions } from "./program.js";
|
|
2
2
|
export type AssertCliOptions = ProgramCliOptions & {
|
|
3
3
|
source?: string;
|
|
4
|
+
warnOnDuplicates?: boolean;
|
|
4
5
|
};
|
|
5
|
-
export
|
|
6
|
+
export type AssertResult = {
|
|
7
|
+
warnings: string[];
|
|
8
|
+
};
|
|
9
|
+
export declare const assertCommand: (opts?: AssertCliOptions | undefined) => Promise<AssertResult>;
|
|
6
10
|
//# sourceMappingURL=assert.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/assert.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,GAAG;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/assert.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,GAAG;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AACF,MAAM,MAAM,YAAY,GAAG;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,aAAa,GAAU,OAAO,gBAAgB,GAAG,SAAS,KAAG,OAAO,CAAC,YAAY,CAyB7F,CAAC"}
|
|
@@ -7,11 +7,14 @@ import { ValidationError } from "../../errors.js";
|
|
|
7
7
|
export const assertCommand = async (opts) => {
|
|
8
8
|
const schemaPath = resolveSchemaPath(opts?.schema);
|
|
9
9
|
const envDef = (await loadDef(schemaPath));
|
|
10
|
+
const warnings = [];
|
|
10
11
|
try {
|
|
11
12
|
envDef.assert({
|
|
12
|
-
source: opts?.source
|
|
13
|
+
source: opts?.source
|
|
14
|
+
? dnl.readEnvFile(path.resolve(process.cwd(), opts.source), { onDuplicate: opts?.warnOnDuplicates ? "warn" : "error" }, warnings)
|
|
15
|
+
: process.env,
|
|
13
16
|
});
|
|
14
|
-
|
|
17
|
+
return { warnings };
|
|
15
18
|
}
|
|
16
19
|
catch (error) {
|
|
17
20
|
if (error instanceof z.ZodError) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/export.ts"],"names":[],"mappings":"AAAA,OAAO,GAA4B,MAAM,gBAAgB,CAAC;AAM1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/export.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ import { resolveSchemaPath } from "../utils/resolve-schema.js";
|
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { UsageError } from "../../errors.js";
|
|
6
6
|
import { isRequired, isTransform, printZodTypeDts } from "../utils/printer.js";
|
|
7
|
+
// import { JsonObject } from "type-fest";
|
|
7
8
|
export const exportFormatsNames = [
|
|
8
9
|
"docker-env",
|
|
9
10
|
"docker-args",
|
|
@@ -87,7 +88,9 @@ const getRawValue = (key, source, envDef, options) => {
|
|
|
87
88
|
};
|
|
88
89
|
// this clones the .env
|
|
89
90
|
export const exportEnv = (envDef, options, warnings) => {
|
|
90
|
-
const source = options?.source
|
|
91
|
+
const source = options?.source
|
|
92
|
+
? dnl.readEnvFile(path.resolve(process.cwd(), options.source), { onDuplicate: options?.warnOnDuplicates ? "warn" : "error" }, warnings)
|
|
93
|
+
: process.env;
|
|
91
94
|
const values = envDef.assert({ source });
|
|
92
95
|
const args = [];
|
|
93
96
|
for (const key of Object.keys(values)) {
|
|
@@ -106,7 +109,9 @@ export const exportDockerArgs = (envDef, options, warnings) => {
|
|
|
106
109
|
if (options?.includeComments) {
|
|
107
110
|
warnings.push("The --include-comments option is invalid with the docker-args format");
|
|
108
111
|
}
|
|
109
|
-
const source = options?.source
|
|
112
|
+
const source = options?.source
|
|
113
|
+
? dnl.readEnvFile(path.resolve(process.cwd(), options.source), { onDuplicate: options?.warnOnDuplicates ? "warn" : "error" }, warnings)
|
|
114
|
+
: process.env;
|
|
110
115
|
const values = envDef.assert({ source });
|
|
111
116
|
const args = [];
|
|
112
117
|
for (const key of Object.keys(values)) {
|
|
@@ -119,7 +124,9 @@ export const exportDockerArgs = (envDef, options, warnings) => {
|
|
|
119
124
|
return args.join(" ");
|
|
120
125
|
};
|
|
121
126
|
export const exportDockerEnv = (envDef, options, warnings) => {
|
|
122
|
-
const source = options?.source
|
|
127
|
+
const source = options?.source
|
|
128
|
+
? dnl.readEnvFile(path.resolve(process.cwd(), options.source), { onDuplicate: options?.warnOnDuplicates ? "warn" : "error" }, warnings)
|
|
129
|
+
: process.env;
|
|
123
130
|
const values = envDef.assert({ source });
|
|
124
131
|
const args = [];
|
|
125
132
|
for (const key of Object.keys(values)) {
|
|
@@ -135,7 +142,9 @@ export const exportDockerEnv = (envDef, options, warnings) => {
|
|
|
135
142
|
return args.join("\n");
|
|
136
143
|
};
|
|
137
144
|
export const exportK8sConfigmap = (envDef, options, warnings) => {
|
|
138
|
-
const source = options?.source
|
|
145
|
+
const source = options?.source
|
|
146
|
+
? dnl.readEnvFile(path.resolve(process.cwd(), options.source), { onDuplicate: options?.warnOnDuplicates ? "warn" : "error" }, warnings)
|
|
147
|
+
: process.env;
|
|
139
148
|
const values = envDef.assert({ source });
|
|
140
149
|
const args = [];
|
|
141
150
|
args.push(`apiVersion: v1`);
|
|
@@ -158,7 +167,9 @@ export const exportK8sConfigmap = (envDef, options, warnings) => {
|
|
|
158
167
|
return args.join("\n");
|
|
159
168
|
};
|
|
160
169
|
export const exportK8sSecret = (envDef, options, warnings) => {
|
|
161
|
-
const source = options?.source
|
|
170
|
+
const source = options?.source
|
|
171
|
+
? dnl.readEnvFile(path.resolve(process.cwd(), options.source), { onDuplicate: options?.warnOnDuplicates ? "warn" : "error" }, warnings)
|
|
172
|
+
: process.env;
|
|
162
173
|
const values = envDef.assert({ source });
|
|
163
174
|
const args = [];
|
|
164
175
|
args.push(`apiVersion: v1`);
|
|
@@ -179,7 +190,9 @@ export const exportK8sSecret = (envDef, options, warnings) => {
|
|
|
179
190
|
return args.join("\n");
|
|
180
191
|
};
|
|
181
192
|
export const exportGithubEnv = (envDef, options, warnings) => {
|
|
182
|
-
const source = options?.source
|
|
193
|
+
const source = options?.source
|
|
194
|
+
? dnl.readEnvFile(path.resolve(process.cwd(), options.source), { onDuplicate: options?.warnOnDuplicates ? "warn" : "error" }, warnings)
|
|
195
|
+
: process.env;
|
|
183
196
|
const values = envDef.assert({ source });
|
|
184
197
|
const args = [];
|
|
185
198
|
for (const key of Object.keys(values)) {
|
|
@@ -197,7 +210,9 @@ export const exportGithubSecret = (envDef, options, warnings) => {
|
|
|
197
210
|
if (options?.githubOrg && options.githubOrg.includes(" ")) {
|
|
198
211
|
warnings.push("github-org contains a space; gh command likely invalid");
|
|
199
212
|
}
|
|
200
|
-
const source = options?.source
|
|
213
|
+
const source = options?.source
|
|
214
|
+
? dnl.readEnvFile(path.resolve(process.cwd(), options.source), { onDuplicate: options?.warnOnDuplicates ? "warn" : "error" }, warnings)
|
|
215
|
+
: process.env;
|
|
201
216
|
const values = envDef.assert({ source });
|
|
202
217
|
const scopeFlag = options?.githubOrg ? `--org ${shellEscape(options.githubOrg)}` : "";
|
|
203
218
|
const args = [];
|
|
@@ -231,8 +246,11 @@ export const exportJson = (envDef, options, warnings) => {
|
|
|
231
246
|
if (options?.includeComments) {
|
|
232
247
|
warnings.push("The --include-comments option is ignored for the json format");
|
|
233
248
|
}
|
|
234
|
-
const source = options?.source
|
|
249
|
+
const source = options?.source
|
|
250
|
+
? dnl.readEnvFile(path.resolve(process.cwd(), options.source), { onDuplicate: options?.warnOnDuplicates ? "warn" : "error" }, warnings)
|
|
251
|
+
: process.env;
|
|
235
252
|
const values = envDef.assert({ source });
|
|
253
|
+
//const args: JsonObject = {};
|
|
236
254
|
const args = {};
|
|
237
255
|
for (const key of Object.keys(values)) {
|
|
238
256
|
if (options?.excludeSecret && envDef.def[key].secret) {
|
|
@@ -243,7 +261,9 @@ export const exportJson = (envDef, options, warnings) => {
|
|
|
243
261
|
return JSON.stringify(args, null, 2);
|
|
244
262
|
};
|
|
245
263
|
export const exportTs = (envDef, options, warnings) => {
|
|
246
|
-
const source = options?.source
|
|
264
|
+
const source = options?.source
|
|
265
|
+
? dnl.readEnvFile(path.resolve(process.cwd(), options.source), { onDuplicate: options?.warnOnDuplicates ? "warn" : "error" }, warnings)
|
|
266
|
+
: process.env;
|
|
247
267
|
const values = envDef.assert({ source });
|
|
248
268
|
const middle = [];
|
|
249
269
|
for (const key of Object.keys(values)) {
|
|
@@ -258,7 +278,9 @@ export const exportTs = (envDef, options, warnings) => {
|
|
|
258
278
|
return `export const env = {\n${middle.join("\n")}\n} as const;`;
|
|
259
279
|
};
|
|
260
280
|
export const exportJs = (envDef, options, warnings) => {
|
|
261
|
-
const source = options?.source
|
|
281
|
+
const source = options?.source
|
|
282
|
+
? dnl.readEnvFile(path.resolve(process.cwd(), options.source), { onDuplicate: options?.warnOnDuplicates ? "warn" : "error" }, warnings)
|
|
283
|
+
: process.env;
|
|
262
284
|
const values = envDef.assert({ source });
|
|
263
285
|
const middle = [];
|
|
264
286
|
for (const key of Object.keys(values)) {
|
|
@@ -2,8 +2,11 @@ export type InferCliOptions = {
|
|
|
2
2
|
source?: string;
|
|
3
3
|
out?: string;
|
|
4
4
|
force?: boolean;
|
|
5
|
-
|
|
5
|
+
guessSecret?: boolean;
|
|
6
6
|
verbose?: boolean;
|
|
7
|
+
warnOnDuplicates?: boolean;
|
|
8
|
+
presets?: Array<string>;
|
|
9
|
+
discoverPresets?: boolean;
|
|
7
10
|
};
|
|
8
11
|
export type InferResult = {
|
|
9
12
|
content: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"infer.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/infer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"infer.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/infer.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,eAAe,GAAG;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,OAAO,eAAe,GAAG,SAAS,KAAG,OAAO,CAAC,WAAW,CA+G1F,CAAC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import dnl from "../../index.js";
|
|
3
3
|
import { guessSecret } from "../../infer/helpers.js";
|
|
4
|
-
import { infer } from "../utils/infer-
|
|
4
|
+
import { crossInfer, infer } from "../utils/infer-rule-engine.js";
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import { ExportError } from "../../errors.js";
|
|
7
|
+
import { discoverPresets, findPresetEntry, getPresetsFromNames } from "../../infer/presets.js";
|
|
7
8
|
export const inferCommand = async (opts) => {
|
|
8
9
|
const source = path.resolve(process.cwd(), opts?.source ?? ".env");
|
|
9
10
|
if (!fs.existsSync(source)) {
|
|
@@ -14,41 +15,78 @@ export const inferCommand = async (opts) => {
|
|
|
14
15
|
if (fs.existsSync(target) && !opts?.force) {
|
|
15
16
|
throw new ExportError(`${out} already exists. Use --force to overwrite.`);
|
|
16
17
|
}
|
|
17
|
-
const env = dnl.readEnvFile(source);
|
|
18
|
-
const lines = [];
|
|
19
18
|
const warnings = [];
|
|
20
|
-
|
|
19
|
+
let presets = [];
|
|
20
|
+
// Preset selection strategy:
|
|
21
|
+
// - `--presets ...` -> use ONLY the provided presets (disables discovery)
|
|
22
|
+
// - `--no-discover-presets` -> use no presets at all
|
|
23
|
+
// - default -> discover presets from package.json
|
|
24
|
+
const presetNames = (opts?.presets ?? []).filter((x) => typeof x === "string" && x.length > 0);
|
|
25
|
+
if (presetNames.length > 0) {
|
|
26
|
+
presets = getPresetsFromNames(presetNames);
|
|
27
|
+
}
|
|
28
|
+
else if (opts?.discoverPresets === true) {
|
|
29
|
+
presets = discoverPresets(warnings);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
presets = [];
|
|
33
|
+
}
|
|
34
|
+
const env = dnl.readEnvFile(source, { onDuplicate: opts?.warnOnDuplicates ? "warn" : "error" }, warnings);
|
|
35
|
+
const lines = [];
|
|
36
|
+
const importedSchemas = [{ name: "define", from: "@romaintaillandier1978/dotenv-never-lies" }, { name: "z", from: "zod" }];
|
|
21
37
|
const verbose = [];
|
|
22
38
|
lines.push(`// ⚠️ This file was generated by dotenv-never-lies`);
|
|
23
39
|
lines.push(`// Review and adjust schemas, descriptions and secrets before using`);
|
|
24
40
|
lines.push("");
|
|
25
|
-
lines.push(`import { z } from "zod";`);
|
|
26
|
-
lines.push(`import { define } from "@romaintaillandier1978/dotenv-never-lies";`);
|
|
27
41
|
lines.push("");
|
|
28
42
|
lines.push(`export default define({`);
|
|
29
|
-
for (const [
|
|
30
|
-
const isValidIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(
|
|
31
|
-
const safeKey = isValidIdentifier ?
|
|
43
|
+
for (const [name, rawValue] of Object.entries(env)) {
|
|
44
|
+
const isValidIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
45
|
+
const safeKey = isValidIdentifier ? name : JSON.stringify(name);
|
|
32
46
|
if (!isValidIdentifier) {
|
|
33
|
-
warnings.push(`Key ${
|
|
47
|
+
warnings.push(`Key ${name} is not a valid identifier. It has been escaped to ${safeKey}.`);
|
|
34
48
|
}
|
|
35
49
|
lines.push(` ${safeKey}: {`);
|
|
50
|
+
verbose.push(` Infer ${name} : `);
|
|
51
|
+
const result = findPresetEntry(presets, name, warnings);
|
|
52
|
+
if (result) {
|
|
53
|
+
const [origin, presetEntry] = result;
|
|
54
|
+
if (presetEntry.schema.safeParse(rawValue).success) {
|
|
55
|
+
verbose.push(` -> inferred from preset ${origin}`);
|
|
56
|
+
lines.push(` // from @preset ${origin}`);
|
|
57
|
+
lines.push(` description: "${presetEntry.description}",`);
|
|
58
|
+
lines.push(` schema: ${presetEntry.code},`);
|
|
59
|
+
lines.push(` secret: ${presetEntry.secret ? "true" : "false"},`);
|
|
60
|
+
lines.push(` examples: ${presetEntry.examples ? JSON.stringify(presetEntry.examples) : "[]"},`);
|
|
61
|
+
lines.push(` },`);
|
|
62
|
+
importedSchemas.push(...presetEntry.imports);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
warnings.push(`Preset "${origin}" ignored for ${name}: value "${rawValue}" does not match preset schema`);
|
|
66
|
+
}
|
|
36
67
|
lines.push(` description: "TODO",`);
|
|
37
|
-
|
|
68
|
+
const isSecret = (opts?.guessSecret ?? true) && guessSecret(name);
|
|
69
|
+
// NO : if (rawValue === undefined || rawValue === '""' || rawValue === "''") { // WRONG !
|
|
70
|
+
// Note: dotenv normalizes `FOO=`, `FOO=""` and `FOO=''` into an empty string ("").
|
|
71
|
+
// At this stage, we only treat `undefined` as "variable not set".
|
|
72
|
+
// Empty strings are handled by inference rules when relevant (e.g. number coercion).
|
|
73
|
+
if (rawValue === undefined) {
|
|
74
|
+
// undefined means the variable is not set in the .env file
|
|
38
75
|
lines.push(` schema: z.string().optional(),`);
|
|
39
76
|
}
|
|
40
77
|
else {
|
|
41
|
-
const
|
|
42
|
-
const schema = infer(
|
|
43
|
-
|
|
44
|
-
|
|
78
|
+
const context = { name, rawValue, imports: importedSchemas, reasons: verbose, codeWarnings: [] };
|
|
79
|
+
const schema = infer(context);
|
|
80
|
+
crossInfer({ inferredSchema: schema, isSecret, ...context });
|
|
81
|
+
for (const w of context.codeWarnings) {
|
|
82
|
+
lines.push(" // " + w);
|
|
45
83
|
}
|
|
46
|
-
lines.push(` schema: ${schema},`);
|
|
84
|
+
lines.push(` schema: ${schema.code},`);
|
|
47
85
|
}
|
|
48
|
-
if (
|
|
86
|
+
if (isSecret) {
|
|
49
87
|
lines.push(` secret: true, // ⚠️ inferred as secret`);
|
|
50
88
|
verbose.push(` -> inferred as secret`);
|
|
51
|
-
warnings.push(`${
|
|
89
|
+
warnings.push(`${name} inferred as secret`);
|
|
52
90
|
}
|
|
53
91
|
lines.push(` },`);
|
|
54
92
|
}
|
|
@@ -82,5 +120,5 @@ const insertImports = (lines, imports) => {
|
|
|
82
120
|
}
|
|
83
121
|
if (importLines.length === 0)
|
|
84
122
|
return;
|
|
85
|
-
lines.splice(
|
|
123
|
+
lines.splice(3, 0, ...importLines);
|
|
86
124
|
};
|
package/dist/cli/index.d.ts
CHANGED
package/dist/cli/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAYA,OAAO,EAAU,WAAW,EAAE,MAAM,WAAW,CAAC;AAGhD,eAAO,MAAM,cAAc,EAAE,WAAgC,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -7,9 +7,8 @@ import { explainCommand, printHuman } from "./commands/explain.js";
|
|
|
7
7
|
import { exportCommand } from "./commands/export.js";
|
|
8
8
|
import { toFile } from "./utils/toFile.js";
|
|
9
9
|
import { DnlError, ExitCodes, ValidationError } from "../errors.js";
|
|
10
|
-
import
|
|
11
|
-
const
|
|
12
|
-
const packageJson = require("../../package.json");
|
|
10
|
+
import pkg from "../../package.json" with { type: "json" };
|
|
11
|
+
export const dnlPackageJson = pkg;
|
|
13
12
|
const exitCodeHelp = {
|
|
14
13
|
[ExitCodes.success]: "Success (everything is valid, exit OK)",
|
|
15
14
|
[ExitCodes.usageError]: "Usage error or internal error",
|
|
@@ -21,7 +20,7 @@ const exitCodeHelp = {
|
|
|
21
20
|
program
|
|
22
21
|
.name("dnl")
|
|
23
22
|
//.version("0.3.0")
|
|
24
|
-
.version(
|
|
23
|
+
.version(dnlPackageJson.version ?? "0.0.0")
|
|
25
24
|
// allows passing positional arguments, before/after options
|
|
26
25
|
.enablePositionalOptions()
|
|
27
26
|
.exitOverride()
|
|
@@ -71,9 +70,14 @@ program
|
|
|
71
70
|
.command("assert")
|
|
72
71
|
.description("Verifies the runtime environment and exits the process if the schema is not satisfied.")
|
|
73
72
|
.option("-s, --source <source>", "Variables source (default: process.env)")
|
|
73
|
+
.option("--warn-on-duplicates", "Warn on duplicate environment variables instead of failing")
|
|
74
74
|
.action(async (opts) => {
|
|
75
75
|
const globalOpts = program.opts();
|
|
76
|
-
await assertCommand({ ...opts, schema: globalOpts.schema });
|
|
76
|
+
const { warnings } = await assertCommand({ ...opts, schema: globalOpts.schema });
|
|
77
|
+
for (const warning of warnings) {
|
|
78
|
+
console.error(`${warning}`);
|
|
79
|
+
}
|
|
80
|
+
console.log("✅ Environment is valid");
|
|
77
81
|
})
|
|
78
82
|
.addHelpText("after", `\nExamples:
|
|
79
83
|
|
|
@@ -112,6 +116,7 @@ program
|
|
|
112
116
|
.description("Exports environment variables to a specified format. Variables are exported after being validated against the schema.")
|
|
113
117
|
.argument("<format>", "Export format. See list and examples at the end")
|
|
114
118
|
.option("-s, --source <source>", "Variables source (default: process.env if none provided)")
|
|
119
|
+
.option("--warn-on-duplicates", "Warn on duplicate environment variables instead of failing")
|
|
115
120
|
.option("--hide-secret", 'Mask sensitive variables (replace with "********")')
|
|
116
121
|
.option("--exclude-secret", "Exclude sensitive variables (do not show them at all)")
|
|
117
122
|
.option("--include-comments", "Include comments in the export (does not work with the json format)")
|
|
@@ -287,14 +292,19 @@ program
|
|
|
287
292
|
"By default, the command will try to guess sensitive variables (e.g. SECRET, KEY, TOKEN, PASSWORD) as secrets.\n" +
|
|
288
293
|
"This detection is intentionally aggressive and may flag variables that are not secrets.\n" +
|
|
289
294
|
"This is a deliberate design choice to avoid missing sensitive values.\n" +
|
|
290
|
-
"Use the --
|
|
295
|
+
"Use the --no-guess-secret option to disable this behavior.\n" +
|
|
296
|
+
"By default, the command will discover presets in package.json.\n" +
|
|
297
|
+
"Use the --no-discover-presets option to disable this behavior, or --presets option to specify presets to use for inference.\n" +
|
|
291
298
|
"\n" +
|
|
292
299
|
"Documentation: https://github.com/rtaillandier/dotenv-never-lies/blob/main/docs/commands/infer.md")
|
|
293
300
|
.option("-s, --source <source>", "Source .env file", ".env")
|
|
294
301
|
.option("-o, --out <file>", "Output DNL file", "env.dnl.ts")
|
|
295
302
|
.option("-f, --force", "Overwrite existing file")
|
|
296
303
|
.option("--verbose", "Verbose mode")
|
|
297
|
-
.option("--
|
|
304
|
+
.option("--no-guess-secret", "Do not try to guess sensitive variables (heuristic)")
|
|
305
|
+
.option("--warn-on-duplicates", "Warn on duplicate environment variables instead of failing")
|
|
306
|
+
.option("--presets <presets...>", "Presets to use for inference (no discovery of presets in package.json)")
|
|
307
|
+
.option("--no-discover-presets", "Do not discover presets in package.json")
|
|
298
308
|
.action(async (opts) => {
|
|
299
309
|
const { content, out, warnings, verbose } = await inferCommand(opts);
|
|
300
310
|
if (opts.verbose) {
|
|
@@ -314,17 +324,32 @@ program
|
|
|
314
324
|
})
|
|
315
325
|
.addHelpText("after", `\nExamples:
|
|
316
326
|
|
|
317
|
-
# Generate an env.dnl.ts schema from a .env file,
|
|
318
|
-
dnl infer --
|
|
327
|
+
# Generate an env.dnl.ts schema from a .env file, and will show every inference rules applied, with confidence scoring and reasons
|
|
328
|
+
dnl infer --verbose
|
|
329
|
+
|
|
330
|
+
# Generate an env.dnl.ts schema from a .env file, do not try to guess sensitive variables
|
|
331
|
+
dnl infer --no-guess-secret
|
|
319
332
|
|
|
320
333
|
# Generate an env.dnl.ts schema from a .env.local file
|
|
321
334
|
dnl infer --source .env.local
|
|
322
335
|
|
|
323
|
-
# Generate a my-dnl.ts schema from a .env file
|
|
336
|
+
# Generate a my-dnl.ts schema from a .env file,
|
|
324
337
|
dnl infer --out my-dnl.ts
|
|
325
338
|
|
|
326
|
-
# Generate an env.dnl.ts schema from a .env file and overwrite the existing file
|
|
339
|
+
# Generate an env.dnl.ts schema from a .env file, and overwrite the existing file
|
|
327
340
|
dnl infer --force
|
|
341
|
+
|
|
342
|
+
# Generate an env.dnl.ts schema from a .env file, and use only specified presets (no package.json scanning)
|
|
343
|
+
dnl infer --presets prisma node
|
|
344
|
+
|
|
345
|
+
# Generate an env.dnl.ts schema from a .env file, do not automatically discover presets in package.json
|
|
346
|
+
dnl infer --no-discover-presets
|
|
347
|
+
|
|
348
|
+
# Generate an env.dnl.ts schema from a .env file, prevent to fail on duplicate keys, warn instead (dnl will never be silent on duplicate keys)
|
|
349
|
+
dnl infer --warn-on-duplicates
|
|
350
|
+
|
|
351
|
+
# Generate an env.dnl.ts schema from a .env file, Minimal inference (no preset discovery, no secret guessing)
|
|
352
|
+
dnl infer --no-discover-presets --no-guess-secret
|
|
328
353
|
|
|
329
354
|
# Full documentation:
|
|
330
355
|
# https://github.com/romaintaillandier1978/dotenv-never-lies/blob/main/docs/commands/infer.md
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { CrossInferContext, GeneratedSchema, InferContext } from "../../infer/rules.types.js";
|
|
2
|
+
export declare const infer: (context: InferContext) => GeneratedSchema;
|
|
3
|
+
export declare const crossInfer: (context: CrossInferContext) => void;
|
|
4
|
+
//# sourceMappingURL=infer-rule-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infer-rule-engine.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/infer-rule-engine.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE9F,eAAO,MAAM,KAAK,GAAI,SAAS,YAAY,KAAG,eA0B7C,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,SAAS,iBAAiB,KAAG,IAIvD,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { zStringGenSchema } from "../../infer/generated/basic.js";
|
|
2
|
+
import { CROSS_RULES, RULES } from "../../infer/rules.js";
|
|
3
|
+
export const infer = (context) => {
|
|
4
|
+
// for each rules,
|
|
5
|
+
for (const rule of RULES) {
|
|
6
|
+
// try to infer a schema for the current context
|
|
7
|
+
const result = rule.tryInfer({ name: context.name, rawValue: context.rawValue });
|
|
8
|
+
if (!result)
|
|
9
|
+
continue;
|
|
10
|
+
if (result.confidence >= rule.threshold) {
|
|
11
|
+
const importedNames = result.generated.imports.map((entry) => entry.name);
|
|
12
|
+
context.reasons.push(` [${importedNames.join(", ")}] confidence: ${result.confidence} / threshold: ${rule.threshold}`);
|
|
13
|
+
context.imports.push(...result.generated.imports);
|
|
14
|
+
if (result.reasons) {
|
|
15
|
+
context.reasons.push(...result.reasons.map((reason) => ` ${reason}`));
|
|
16
|
+
}
|
|
17
|
+
context.reasons.push(` -> selected schema: ${result.generated.code}`);
|
|
18
|
+
if (result.codeWarnings) {
|
|
19
|
+
context.codeWarnings.push(...result.codeWarnings);
|
|
20
|
+
}
|
|
21
|
+
return result.generated;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return zStringGenSchema;
|
|
25
|
+
};
|
|
26
|
+
export const crossInfer = (context) => {
|
|
27
|
+
for (const rule of CROSS_RULES) {
|
|
28
|
+
rule(context);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
@@ -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":"AAYA,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,KAAG,MAuBpD,CAAC"}
|
|
@@ -1,31 +1,32 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { SchemaNotFoundError } from "../../errors.js";
|
|
4
|
+
import { readDnlConfigInUserPackageJson } from "../../utils/read-user-package-json.js";
|
|
4
5
|
const CANDIDATES = ["env.dnl.ts", "env.dnl.js", "dnl.config.ts", "dnl.config.js"];
|
|
6
|
+
const resolveIfExists = (relativePath) => {
|
|
7
|
+
const full = path.resolve(process.cwd(), relativePath);
|
|
8
|
+
return fs.existsSync(full) ? full : null;
|
|
9
|
+
};
|
|
5
10
|
export const resolveSchemaPath = (cliPath) => {
|
|
6
11
|
// 1. --schema
|
|
7
12
|
if (cliPath) {
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
return full;
|
|
13
|
+
const resolved = resolveIfExists(cliPath);
|
|
14
|
+
if (resolved)
|
|
15
|
+
return resolved;
|
|
16
|
+
throw new SchemaNotFoundError(`Schema file not found: ${cliPath}`);
|
|
13
17
|
}
|
|
14
18
|
// 2. package.json
|
|
15
|
-
const
|
|
16
|
-
if (
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return path.resolve(process.cwd(), schema);
|
|
21
|
-
}
|
|
19
|
+
const dnlConfig = readDnlConfigInUserPackageJson();
|
|
20
|
+
if (dnlConfig !== null) {
|
|
21
|
+
const resolved = resolveIfExists(dnlConfig.schema);
|
|
22
|
+
if (resolved)
|
|
23
|
+
return resolved;
|
|
22
24
|
}
|
|
23
25
|
// 3. convention
|
|
24
26
|
for (const file of CANDIDATES) {
|
|
25
|
-
const
|
|
26
|
-
if (
|
|
27
|
-
return
|
|
28
|
-
}
|
|
27
|
+
const resolved = resolveIfExists(file);
|
|
28
|
+
if (resolved)
|
|
29
|
+
return resolved;
|
|
29
30
|
}
|
|
30
31
|
throw new SchemaNotFoundError("No env schema found. Use --schema, define dotenv-never-lies.schema in package.json, or add env.dnl.ts");
|
|
31
32
|
};
|
package/dist/core.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export interface EnvVarDefinition<T extends z.ZodType = z.ZodType> {
|
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* An object containing the defined environment variables.
|
|
29
|
-
|
|
30
|
-
export type EnvDefinition = Record<string, EnvVarDefinition<
|
|
29
|
+
*/
|
|
30
|
+
export type EnvDefinition = Record<string, EnvVarDefinition<z.ZodType>>;
|
|
31
31
|
/**
|
|
32
32
|
* The Zod shape of the environment schema.
|
|
33
33
|
*/
|
|
@@ -91,6 +91,9 @@ export type EnvDefinitionHelper<T extends EnvDefinition> = {
|
|
|
91
91
|
* @returns An object exposing functions to check and assert environment variables.
|
|
92
92
|
*/
|
|
93
93
|
export declare const define: <T extends EnvDefinition>(def: T) => EnvDefinitionHelper<T>;
|
|
94
|
+
export type ReadEnvFileOptions = {
|
|
95
|
+
onDuplicate: "warn" | "error";
|
|
96
|
+
};
|
|
94
97
|
/**
|
|
95
98
|
* Reads a .env file and returns environment variables as an object.
|
|
96
99
|
* Uses dotenv and dotenv-expand.
|
|
@@ -99,9 +102,11 @@ export declare const define: <T extends EnvDefinition>(def: T) => EnvDefinitionH
|
|
|
99
102
|
* const ENV = envDefinition.load({ source: readEnvFile(".env") });
|
|
100
103
|
* ```
|
|
101
104
|
* @param path - The path to the .env file.
|
|
105
|
+
* @param options - The options for reading the .env file.
|
|
106
|
+
* @param warnings - The warnings to add to.
|
|
102
107
|
* @returns The environment variables as an object.
|
|
103
108
|
* @throws If the .env file does not exist, or is not valid.
|
|
104
109
|
*/
|
|
105
|
-
export declare const readEnvFile: (path: string) => EnvSource;
|
|
110
|
+
export declare const readEnvFile: (path: string, options?: ReadEnvFileOptions, warnings?: string[]) => EnvSource;
|
|
106
111
|
export {};
|
|
107
112
|
//# sourceMappingURL=core.d.ts.map
|
package/dist/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO;IAE7D;;OAEG;IACH,MAAM,EAAE,CAAC,CAAC;IACV;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CAKvB;
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO;IAE7D;;OAEG;IACH,MAAM,EAAE,CAAC,CAAC;IACV;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CAKvB;AAKD;;EAEE;AACF,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AAExE;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,aAAa,IAAI;KAClD,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;CAC1C,CAAC;AACF;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,aAAa,IAAI;KAC3C,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;CACnD,CAAC;AAEF,KAAK,OAAO,GAAG,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;CAAE,KAAK,OAAO,CAAC;AACzE,KAAK,QAAQ,CAAC,CAAC,SAAS,aAAa,IAAI,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;CAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC;AAEvG;;;GAGG;AACH,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,aAAa,IAAI;IACvD;;OAEG;IACH,GAAG,EAAE,CAAC,CAAC;IACP;;OAEG;IACH,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;IAC7B;;OAEG;IACH,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C;;;;;;;;OAQG;IACH,KAAK,EAAE,OAAO,CAAC;IACf;;;;;;;;;OASG;IACH,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,aAAa,EAAE,KAAK,CAAC,KAAG,mBAAmB,CAAC,CAAC,CAuB7E,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAEnE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,UAAS,kBAA6C,EAAE,WAAW,MAAM,EAAE,KAAG,SAqBvH,CAAC"}
|