Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BREAKING(dotenv): remove defaultsPath option from load[Sync]() #5451

Merged
merged 4 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 2 additions & 60 deletions dotenv/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,6 @@ export interface LoadOptions {
* @default {false}
*/
export?: boolean;

/**
* Optional path to `.env.defaults` file which is used to define default
* (fallback) values. To prevent the default value from being used,
* set to `null`.
*
* ```sh
* # .env.defaults
* # Will not be set if GREETING is set in base .env file
* GREETING="a secret to everybody"
* ```
*
* @default {"./.env.defaults"}
*/
defaultsPath?: string | null;
}

/**
Expand All @@ -77,20 +62,10 @@ export function loadSync(
): Record<string, string> {
const {
envPath = ".env",
defaultsPath = ".env.defaults",
export: _export = false,
} = options;
const conf = envPath ? parseFileSync(envPath) : {};

if (defaultsPath) {
const confDefaults = parseFileSync(defaultsPath);
for (const [key, value] of Object.entries(confDefaults)) {
if (!(key in conf)) {
conf[key] = value;
}
}
}

if (_export) {
for (const [key, value] of Object.entries(conf)) {
if (Deno.env.get(key) !== undefined) continue;
Expand Down Expand Up @@ -152,28 +127,6 @@ export function loadSync(
* |File|Purpose|
* |----|-------|
* |.env|primary file for storing key-value environment entries
* |.env.defaults|specify default values for env variables to be used when there is no entry in the `.env` file
*
* ### Defaults
*
* This file is used to provide a list of default environment variables
* which will be used if there is no overriding variable in the `.env`
* file.
*
* ```sh
* # .env.defaults
* KEY_1=DEFAULT_VALUE
* KEY_2=ANOTHER_DEFAULT_VALUE
* ```
* ```sh
* # .env
* KEY_1=ABCD
* ```
* The environment variables set after dotenv loads are:
* ```sh
* KEY_1=ABCD
* KEY_2=ANOTHER_DEFAULT_VALUE
* ```
*
* ## Configuration
*
Expand All @@ -183,8 +136,7 @@ export function loadSync(
* |Option|Default|Description
* |------|-------|-----------
* |envPath|./.env|Path and filename of the `.env` file. Use null to prevent the .env file from being loaded.
* |defaultsPath|./.env.defaults|Path and filename of the `.env.defaults` file. Use null to prevent the .env.defaults file from being loaded.
* |export|false|When true, this will export all environment variables in the `.env` and `.env.default` files to the process environment (e.g. for use by `Deno.env.get()`) but only if they are not already set. If a variable is already in the process, the `.env` value is ignored.
* |export|false|When true, this will export all environment variables in the `.env` file to the process environment (e.g. for use by `Deno.env.get()`) but only if they are not already set. If a variable is already in the process, the `.env` value is ignored.
*
* ### Example configuration
*
Expand All @@ -205,7 +157,7 @@ export function loadSync(
* in your `.env` file, you will need the `--allow-env` permission. E.g.
*
* ```sh
* deno run --allow-read=.env,.env.defaults --allow-env=ENV1,ENV2 app.ts
* deno run --allow-read=.env --allow-env=ENV1,ENV2 app.ts
* ```
*
* ## Parsing Rules
Expand Down Expand Up @@ -256,20 +208,10 @@ export async function load(
): Promise<Record<string, string>> {
const {
envPath = ".env",
defaultsPath = ".env.defaults",
export: _export = false,
} = options;
const conf = envPath ? await parseFile(envPath) : {};

if (defaultsPath) {
const confDefaults = await parseFile(defaultsPath);
for (const [key, value] of Object.entries(confDefaults)) {
if (!(key in conf)) {
conf[key] = value;
}
}
}

if (_export) {
for (const [key, value] of Object.entries(conf)) {
if (Deno.env.get(key) !== undefined) continue;
Expand Down
50 changes: 2 additions & 48 deletions dotenv/mod_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import {
assert,
assertEquals,
assertRejects,
assertStrictEquals,
assertThrows,
} from "@std/assert";
Expand All @@ -15,42 +14,26 @@ const testdataDir = path.resolve(moduleDir, "testdata");

const testOptions = Object.freeze({
envPath: path.join(testdataDir, ".env"),
defaultsPath: path.join(testdataDir, ".env.defaults"),
});

Deno.test("load() handles non-existent .env files", async () => {
//n.b. neither .env nor .env.default exist in the current directory
// .env doesn't exist in the current directory
assertEquals({}, await load());
assertEquals({}, loadSync());

const loadOptions = {
envPath: "some.nonexistent.env",
defaultsPath: "some.nonexistent.defaults",
};
assertEquals({}, await load(loadOptions));
assertEquals({}, loadSync(loadOptions));
});

Deno.test("load() handles build from .env.default only", async () => {
const conf = loadSync({
defaultsPath: path.join(testdataDir, ".env.defaults"),
});
assertEquals(conf.DEFAULT1, "Some Default", "loaded from .env.default");

const asyncConf = await load({
defaultsPath: path.join(testdataDir, ".env.defaults"),
});
assertEquals(asyncConf.DEFAULT1, "Some Default", "loaded from .env.default");
});

Deno.test("load() handles comprised .env and .env.defaults", async () => {
const conf = loadSync(testOptions);
assertEquals(conf.GREETING, "hello world", "loaded from .env");
assertEquals(conf.DEFAULT1, "Some Default", "loaded from .env.default");

const asyncConf = await load(testOptions);
assertEquals(asyncConf.GREETING, "hello world", "loaded from .env");
assertEquals(asyncConf.DEFAULT1, "Some Default", "loaded from .env.default");
});

Deno.test("load() handles exported entries accessibility in Deno.env", async () => {
Expand All @@ -71,14 +54,8 @@ function validateExport(): void {
"hello world",
"exported from .env -> Deno.env",
);
assertEquals(
Deno.env.get("DEFAULT1"),
"Some Default",
"exported from .env.default -> Deno.env",
);
} finally {
Deno.env.delete("GREETING");
Deno.env.delete("DEFAULT1");
}
}

Expand All @@ -98,17 +75,12 @@ function validateNotOverridden(conf: Record<string, string>): void {
"Do not override!",
"not exported from .env -> Deno.env",
);
assertEquals(
Deno.env.get("DEFAULT1"),
"Some Default",
"exported from .env.default -> Deno.env",
);
} finally {
Deno.env.delete("DEFAULT1");
}
}

Deno.test("load() loads .env and .env.defaults successfully from default file names/paths", async () => {
Deno.test("load() loads .env successfully from default file names/paths", async () => {
const command = new Deno.Command(Deno.execPath(), {
args: [
"run",
Expand All @@ -125,7 +97,6 @@ Deno.test("load() loads .env and .env.defaults successfully from default file na
const conf = JSON.parse(decoder.decode(stdout).trim());

assertEquals(conf.GREETING, "hello world", "fetches .env by default");
assertEquals(conf.DEFAULT1, "Some Default", "default value loaded");
});

Deno.test("load() expands empty values from process env expand as empty value", async () => {
Expand Down Expand Up @@ -166,7 +137,6 @@ Deno.test(
// note lack of --allow-env permission
const conf = loadSync(testOptions);
assertEquals(conf.GREETING, "hello world");
assertEquals(conf.DEFAULT1, "Some Default");
},
);

Expand All @@ -182,7 +152,6 @@ Deno.test(
// note lack of --allow-env permission
const loadOptions = {
envPath: path.join(testdataDir, "./.env.single.expand"),
defaultsPath: null,
};
assertThrows(
() => loadSync(loadOptions),
Expand All @@ -206,7 +175,6 @@ Deno.test(

const loadOptions = {
envPath: path.join(testdataDir, "./.env.single.expand"),
defaultsPath: null,
};
const conf = loadSync(loadOptions);
assertEquals(
Expand All @@ -232,7 +200,6 @@ Deno.test(
},
async (t) => {
const optsNoPaths = {
defaultsPath: null,
envPath: null,
} satisfies LoadOptions;

Expand All @@ -242,7 +209,6 @@ Deno.test(

const optsOnlyEnvPath = {
...optsEnvPath,
defaultsPath: null,
} satisfies LoadOptions;

const assertEnv = (env: Record<string, string>): void => {
Expand All @@ -254,23 +220,11 @@ Deno.test(
await t.step("load", async () => {
assertStrictEquals(Object.keys(await load(optsNoPaths)).length, 0);
assertEnv(await load(optsOnlyEnvPath));

await assertRejects(
() => load(optsEnvPath),
Deno.errors.PermissionDenied,
`Requires read access to ".env.defaults"`,
);
});

await t.step("loadSync", () => {
assertStrictEquals(Object.keys(loadSync(optsNoPaths)).length, 0);
assertEnv(loadSync(optsOnlyEnvPath));

assertThrows(
() => loadSync(optsEnvPath),
Deno.errors.PermissionDenied,
`Requires read access to ".env.defaults"`,
);
});
},
);
1 change: 0 additions & 1 deletion dotenv/testdata/.env.defaults

This file was deleted.

1 change: 0 additions & 1 deletion dotenv/testdata/.env.example

This file was deleted.

1 change: 0 additions & 1 deletion dotenv/testdata/.env.example.test

This file was deleted.

2 changes: 0 additions & 2 deletions dotenv/testdata/.env.example2.test

This file was deleted.

1 change: 0 additions & 1 deletion dotenv/testdata/.env.example3.test

This file was deleted.

2 changes: 0 additions & 2 deletions dotenv/testdata/.env.required.empty.test

This file was deleted.