Skip to content

Commit

Permalink
BREAKING: Map-like interface for Deno.env (denoland#4942)
Browse files Browse the repository at this point in the history
  • Loading branch information
SyrupThinker authored and SASUKE40 committed May 7, 2020
1 parent db0d699 commit 53f2eb4
Show file tree
Hide file tree
Showing 9 changed files with 53 additions and 58 deletions.
50 changes: 28 additions & 22 deletions cli/js/lib.deno.ns.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,29 +116,35 @@ declare namespace Deno {
*/
export function exit(code?: number): never;

/** Returns a snapshot of the environment variables at invocation. Changing a
* property in the object will set that variable in the environment for the
* process. The environment object will only accept `string`s as values.
*
* const myEnv = Deno.env();
* console.log(myEnv.SHELL);
* myEnv.TEST_VAR = "HELLO";
* const newEnv = Deno.env();
* console.log(myEnv.TEST_VAR === newEnv.TEST_VAR); // outputs "true"
*
* Requires `allow-env` permission. */
export function env(): {
[index: string]: string;
};
export const env: {
/** Retrieve the value of an environment variable. Returns undefined if that
* key doesn't exist.
*
* console.log(Deno.env.get("HOME")); // e.g. outputs "/home/alice"
* console.log(Deno.env.get("MADE_UP_VAR")); // outputs "Undefined"
*
* Requires `allow-env` permission. */
get(key: string): string | undefined;

/** Retrieve the value of an environment variable. Returns undefined if that
* key doesn't exist.
*
* console.log(Deno.env("HOME")); // e.g. outputs "/home/alice"
* console.log(Deno.env("MADE_UP_VAR")); // outputs "Undefined"
*
* Requires `allow-env` permission. */
export function env(key: string): string | undefined;
/** Set the value of an environment variable.
*
* Deno.env.set("SOME_VAR", "Value"));
* Deno.env.get("SOME_VAR"); // outputs "Value"
*
* Requires `allow-env` permission. */
set(key: string, value: string): void;

/** Returns a snapshot of the environment variables at invocation.
*
* Deno.env.set("TEST_VAR", "A");
* const myEnv = Deno.env.toObject();
* console.log(myEnv.SHELL);
* Deno.env.set("TEST_VAR", "B");
* console.log(myEnv.TEST_VAR); // outputs "A"
*
* Requires `allow-env` permission. */
toObject(): { [index: string]: string };
};

/** **UNSTABLE** */
export type DirKind =
Expand Down
23 changes: 7 additions & 16 deletions cli/js/ops/os.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,13 @@ function getEnv(key: string): string | undefined {
return sendSync("op_get_env", { key })[0];
}

export function env(): { [index: string]: string };
export function env(key: string): string | undefined;
export function env(
key?: string
): { [index: string]: string } | string | undefined {
if (key) {
return getEnv(key);
}
const env = sendSync("op_env");
return new Proxy(env, {
set(obj, prop: string, value: string): boolean {
setEnv(prop, value);
return Reflect.set(obj, prop, value);
},
});
}
export const env = {
get: getEnv,
toObject(): { [key: string]: string } {
return sendSync("op_env");
},
set: setEnv,
};

type DirKind =
| "home"
Expand Down
24 changes: 11 additions & 13 deletions cli/js/tests/os_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,22 @@ import {
} from "./test_util.ts";

unitTest({ perms: { env: true } }, function envSuccess(): void {
const env = Deno.env();
assert(env !== null);
// eslint-disable-next-line @typescript-eslint/camelcase
env.test_var = "Hello World";
const newEnv = Deno.env();
assertEquals(env.test_var, newEnv.test_var);
assertEquals(Deno.env("test_var"), env.test_var);
Deno.env.set("TEST_VAR", "A");
const env = Deno.env.toObject();
Deno.env.set("TEST_VAR", "B");
assertEquals(env["TEST_VAR"], "A");
assertNotEquals(Deno.env.get("TEST_VAR"), env["TEST_VAR"]);
});

unitTest({ perms: { env: true } }, function envNotFound(): void {
const r = Deno.env("env_var_does_not_exist!");
const r = Deno.env.get("env_var_does_not_exist!");
assertEquals(r, undefined);
});

unitTest(function envPermissionDenied1(): void {
let err;
try {
Deno.env();
Deno.env.toObject();
} catch (e) {
err = e;
}
Expand All @@ -37,7 +35,7 @@ unitTest(function envPermissionDenied1(): void {
unitTest(function envPermissionDenied2(): void {
let err;
try {
Deno.env("PATH");
Deno.env.get("PATH");
} catch (e) {
err = e;
}
Expand All @@ -62,7 +60,7 @@ unitTest(
): Promise<void> => {
const src = `
console.log(
${JSON.stringify(Object.keys(expectedEnv))}.map(k => Deno.env(k))
${JSON.stringify(Object.keys(expectedEnv))}.map(k => Deno.env.get(k))
)`;
const proc = Deno.run({
cmd: [Deno.execPath(), "eval", src],
Expand All @@ -79,8 +77,8 @@ unitTest(
proc.close();
};

assertEquals(Deno.env("path"), Deno.env("PATH"));
assertEquals(Deno.env("Path"), Deno.env("PATH"));
assertEquals(Deno.env.get("path"), Deno.env.get("PATH"));
assertEquals(Deno.env.get("Path"), Deno.env.get("PATH"));

// Check 'foo', 'Foo' and 'Foo' are case folded.
await checkChildEnv({ foo: "X" }, { foo: "X", Foo: "X", FOO: "X" });
Expand Down
2 changes: 1 addition & 1 deletion cli/tests/permission_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const test: { [key: string]: Function } = {
makeTempDirSync();
},
envRequired(): void {
env().home;
env.get("home");
},
netRequired(): void {
listen({ transport: "tcp", port: 4541 });
Expand Down
2 changes: 1 addition & 1 deletion std/examples/gist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function pathBase(p: string): string {
return parts[parts.length - 1];
}

const token = Deno.env()["GIST_TOKEN"];
const token = Deno.env.get("GIST_TOKEN");
if (!token) {
console.error("GIST_TOKEN environmental variable not set.");
console.error("Get a token here: https://github.com/settings/tokens");
Expand Down
4 changes: 2 additions & 2 deletions std/node/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,8 @@ class Module {
}

static _initPaths(): void {
const homeDir = Deno.env("HOME");
const nodePath = Deno.env("NODE_PATH");
const homeDir = Deno.env.get("HOME");
const nodePath = Deno.env.get("NODE_PATH");

// Removed $PREFIX/bin/node case

Expand Down
2 changes: 1 addition & 1 deletion std/node/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const process = {
on,
get env(): { [index: string]: string } {
// using getter to avoid --allow-env unless it's used
return Deno.env();
return Deno.env.toObject();
},
get argv(): string[] {
// Deno.execPath() also requires --allow-env
Expand Down
2 changes: 1 addition & 1 deletion std/node/process_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { assert, assertThrows, assertEquals } from "../testing/asserts.ts";
import { process } from "./process.ts";

// NOTE: Deno.execPath() (and thus process.argv) currently requires --allow-env
// (Also Deno.env() (and process.env) requires --allow-env but it's more obvious)
// (Also Deno.env.toObject() (and process.env) requires --allow-env but it's more obvious)

test({
name: "process.cwd and process.chdir success",
Expand Down
2 changes: 1 addition & 1 deletion std/path/win32.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function resolve(...pathSegments: string[]): string {
// absolute path, get cwd for that drive, or the process cwd if
// the drive cwd is not available. We're sure the device is not
// a UNC path at this points, because UNC paths are always absolute.
path = env()[`=${resolvedDevice}`] || cwd();
path = env.get(`=${resolvedDevice}`) || cwd();

// Verify that a cwd was found and that it actually points
// to our drive. If not, default to the drive's root.
Expand Down

0 comments on commit 53f2eb4

Please sign in to comment.