Skip to content

Commit 93d0f35

Browse files
author
A.P.A. Slaa
committed
feat: add url parser from env
1 parent 33f060c commit 93d0f35

3 files changed

Lines changed: 23 additions & 8 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ const mode = env.utils.select("FEATURE_X", "enabled", "disabled");
124124
| `env.string(key, default?)` | Return the variable as a string (or default). |
125125
| `env.number(key, default?)` | Parse variable to number (or default). |
126126
| `env.boolean(key, default?)` | Parse variable to boolean (or default). |
127+
| `env.url(key, default?)` | Parse variable to URL object (or default(http://localhost)). |
127128
| `env.has(key)` | Returns true if key exists in `process.env`. |
128129
| `env.assert([keys], error_builder?)` | Throws an error if one or more keys doesn't exists in `process.env`. |
129130
| `env.defined(key)` | Returns true if key exists and value is not `undefined`. |

src/index.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {readFileSync, existsSync} from "fs";
2+
import {T} from "vitest/dist/chunks/global.d.MAmajcmJ";
23

34
function loadDotEnv(file = ".env") {
45
if (!existsSync(file)) return {};
@@ -41,7 +42,6 @@ export const env = Object.freeze({
4142
if (!(key in process.env)) process.env[key] = _default ?? "";
4243
return process.env[key] as T;
4344
},
44-
4545
number(key: Uppercase<string>, _default = 0): number {
4646
const raw = process.env[key];
4747
if (raw === undefined) {
@@ -51,7 +51,6 @@ export const env = Object.freeze({
5151
const val = Number(raw);
5252
return Number.isFinite(val) ? val : _default;
5353
},
54-
5554
boolean(key: Uppercase<string>, _default = false): boolean {
5655
const raw = process.env[key];
5756
if (raw === undefined) {
@@ -61,13 +60,20 @@ export const env = Object.freeze({
6160
const val = raw.toLowerCase();
6261
return val === "true" || val === "1";
6362
},
64-
63+
url(key: Uppercase<string>, _default = new URL("http://localhost")): URL {
64+
const raw = process.env[key];
65+
if (raw === undefined || !URL.canParse(raw)) {
66+
process.env[key] = _default.toString();
67+
return _default;
68+
}
69+
return URL.parse(raw)!;
70+
},
6571
has(key: Uppercase<string>): boolean {
6672
return Object.prototype.hasOwnProperty.call(process.env, key);
6773
},
6874
assert(keys: Uppercase<string>[], error_builder: (missing_keys: string[]) => string | Error = ((missing_keys) => new Error(`Missing required keys(${missing_keys.join()}) in environment`)),) {
6975
const missing_keys: string[] = [];
70-
keys.forEach((key) => this.has(key) ? void 0: missing_keys.push(key));
76+
keys.forEach((key) => this.has(key) ? void 0 : missing_keys.push(key));
7177
if (missing_keys.length > 0) {
7278
const result = error_builder(missing_keys);
7379
if (typeof result === "string") throw new Error(result);
@@ -113,5 +119,5 @@ export const env = Object.freeze({
113119
}),
114120
get raw() {
115121
return Object.freeze(dotEnvVars)
116-
}
122+
},
117123
});

tests/env.test.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { beforeEach, describe, expect, it, vi } from "vitest";
1+
import {beforeEach, describe, expect, it, vi} from "vitest";
22
import * as fs from "fs";
33

44
vi.mock("fs");
@@ -39,7 +39,8 @@ describe("env library", () => {
3939
mockReadFileSync.mockImplementation(() => {
4040
throw new Error("File read failed");
4141
});
42-
const spy = vi.spyOn(console, "warn").mockImplementation(() => {});
42+
const spy = vi.spyOn(console, "warn").mockImplementation(() => {
43+
});
4344
env = (await import("../src")).env;
4445
expect(spy).toHaveBeenCalledWith("File read failed");
4546
});
@@ -81,13 +82,20 @@ describe("env library", () => {
8182
expect(env.boolean("FLAG_FALSE")).toBe(false);
8283
});
8384

85+
it("handles URL parsing correctly", async () => {
86+
mockExistsSync.mockReturnValue(false);
87+
env = (await import("../src")).env;
88+
(process.env as any).API_ENDPOINT = "http://localhost:8080/";
89+
expect(env.url("API_ENDPOINT")).toBeInstanceOf(URL);
90+
});
91+
8492
it("creates collection with and without prefix removal", async () => {
8593
mockExistsSync.mockReturnValue(false);
8694
(process.env as any).APP_FOO = "bar";
8795
(process.env as any).APP_BAR = "baz";
8896
env = (await import("../src")).env;
8997
const all = env.collection("APP_");
90-
const stripped = env.collection("APP_", { removePrefix: true });
98+
const stripped = env.collection("APP_", {removePrefix: true});
9199
expect(all.APP_FOO).toBe("bar");
92100
expect(stripped.FOO).toBe("bar");
93101
});

0 commit comments

Comments
 (0)