-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
315 additions
and
241 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# 🔐 [cool/dotenv](./) | ||
|
||
## Component Information | ||
|
||
cool/dotenv helps you load configurations from `.env.*` files and environment | ||
variables, a practice based on the 12-Factor App methodology which recommends | ||
separating configuration from code. | ||
|
||
For further details such as requirements, license information and support guide, | ||
please see [main cool repository](https://github.com/eser/cool). | ||
|
||
## Environment Variables | ||
|
||
Environment variables are variables that are available in all command line | ||
sessions and affect the behavior of the applications on your system. They are | ||
essential for managing the configuration of your applications separate from your | ||
code. | ||
|
||
The environment variables are loaded from the following files: | ||
|
||
- Environment variables | ||
- `.env.$(ENV).local` - Local overrides of environment-specific settings. | ||
- `.env.local` - Local overrides. This file is loaded for all environments | ||
**except "test"**. | ||
- `.env.$(ENV)` - Environment-specific settings. | ||
- `.env` - The Original® | ||
|
||
These files are loaded in the order listed above. The first value set (either | ||
from file or environment variable) takes precedence. That means you can use the | ||
`.env` file to store default values for all environments and | ||
`.env.development.local` to override them for development. | ||
|
||
## Usage | ||
|
||
With cool/dotenv, you may load environment configurations. | ||
|
||
### Loading environment variables | ||
|
||
**Basic usage:** | ||
|
||
```ts | ||
import { load } from "$cool/dotenv/mod.ts"; | ||
|
||
const vars = await load(); | ||
console.log(vars); | ||
``` | ||
|
||
**Load from different directory:** | ||
|
||
```ts | ||
import { load } from "$cool/dotenv/mod.ts"; | ||
|
||
const vars = await load({ baseDir: "./config" }); | ||
console.log(vars); | ||
``` | ||
|
||
### Configure an options object with environment reader | ||
|
||
**Basic usage:** | ||
|
||
```ts | ||
import { configure, env } from "$cool/dotenv/mod.ts"; | ||
|
||
const options = await configure( | ||
(reader, acc) => { | ||
acc["env"] = reader[env]; | ||
acc["port"] = reader.readInt("PORT", 8080); | ||
|
||
return acc; | ||
}, | ||
{}, | ||
); | ||
``` | ||
|
||
**With custom interfaces:** | ||
|
||
```ts | ||
import { configure, env } from "$cool/dotenv/mod.ts"; | ||
|
||
interface Options { | ||
env: string; | ||
port: number; | ||
} | ||
|
||
const options = await configure<Options>( | ||
(reader, acc) => { | ||
acc.env = reader[env]; | ||
acc.port = reader.readInt("PORT", 8080); | ||
|
||
return acc; | ||
}, | ||
{}, | ||
); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// public symbols | ||
export const env = Symbol("env"); | ||
|
||
// public constants | ||
export const defaultEnvVar = "ENV"; | ||
export const defaultEnvValue = "development"; | ||
|
||
// public types | ||
export type EnvMap = Map<typeof env | string, string>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import * as stdDotenv from "https://deno.land/[email protected]/dotenv/mod.ts"; | ||
import { defaultEnvValue, defaultEnvVar, env, type EnvMap } from "./base.ts"; | ||
|
||
// interface definitions | ||
export interface LoaderOptions { | ||
baseDir?: string; | ||
defaultEnvVar?: string; | ||
defaultEnvValue?: string; | ||
} | ||
|
||
// public functions | ||
export const parseEnvString = ( | ||
rawDotenv: string, | ||
): ReturnType<typeof stdDotenv.parse> => { | ||
return stdDotenv.parse(rawDotenv); | ||
}; | ||
|
||
export const parseEnvFromFile = async ( | ||
filepath: string, | ||
): Promise<ReturnType<typeof parseEnvString>> => { | ||
try { | ||
const data = await Deno.readFile(filepath); | ||
const decoded = new TextDecoder("utf-8").decode(data); | ||
const escaped = decodeURIComponent(decoded); | ||
|
||
const result = parseEnvString(escaped); | ||
|
||
return result; | ||
} catch (e) { | ||
if (e instanceof Deno.errors.NotFound) { | ||
return {}; | ||
} | ||
|
||
throw e; | ||
} | ||
}; | ||
|
||
export const load = async ( | ||
options?: LoaderOptions, | ||
): Promise<EnvMap> => { | ||
const options_ = { | ||
baseDir: ".", | ||
defaultEnvVar: defaultEnvVar, | ||
defaultEnvValue: defaultEnvValue, | ||
...(options ?? {}), | ||
}; | ||
|
||
const sysVars = (typeof Deno !== "undefined") ? Deno.env.toObject() : {}; | ||
const envName = sysVars[options_.defaultEnvVar] ?? options_.defaultEnvValue; | ||
|
||
const vars = new Map<typeof env | string, string>(); | ||
vars.set(env, envName); | ||
|
||
const envImport = (entries: Record<string, string>) => { | ||
for (const [key, value] of Object.entries(entries)) { | ||
vars.set(key, value); | ||
} | ||
}; | ||
|
||
console.log(`${options_.baseDir}/.env`); | ||
envImport(await parseEnvFromFile(`${options_.baseDir}/.env`)); | ||
envImport(await parseEnvFromFile(`${options_.baseDir}/.env.${envName}`)); | ||
if (envName !== "test") { | ||
envImport(await parseEnvFromFile(`${options_.baseDir}/.env.local`)); | ||
} | ||
envImport( | ||
await parseEnvFromFile(`${options_.baseDir}/.env.${envName}.local`), | ||
); | ||
|
||
envImport(sysVars); | ||
|
||
return vars; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from "./base.ts"; | ||
export * from "./loader.ts"; | ||
export * from "./options.ts"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { defaultEnvValue, env, type EnvMap } from "./base.ts"; | ||
import { load, type LoaderOptions } from "./loader.ts"; | ||
|
||
// interface definitions | ||
export interface BaseEnvVariables { | ||
[env]: string; | ||
} | ||
|
||
export type EnvVariables = BaseEnvVariables & Record<string, unknown>; | ||
|
||
export interface EnvReader { | ||
[env]: string; | ||
readString<T extends string>(key: string, defaultValue: T): T; | ||
readString<T extends string>(key: string): T | undefined; | ||
readEnum<T extends string>(key: string, values: T[], defaultValue: T): T; | ||
readEnum<T extends string>(key: string, values: T[]): T | undefined; | ||
readInt<T extends number>(key: string, defaultValue: T): T; | ||
readInt<T extends number>(key: string): T | undefined; | ||
readBool<T extends boolean>(key: string, defaultValue: T): T; | ||
readBool<T extends boolean>(key: string): T | undefined; | ||
} | ||
|
||
export type Promisable<T> = PromiseLike<T> | T; | ||
export type ConfigureFn<T = EnvVariables> = ( | ||
reader: EnvReader, | ||
target: T, | ||
) => Promisable<T | void>; | ||
|
||
// public functions | ||
export const createEnvReader = (state: EnvMap): EnvReader => { | ||
return { | ||
[env]: state.get(env) ?? defaultEnvValue, | ||
readString: <T extends string>( | ||
key: string, | ||
defaultValue?: T, | ||
): T | undefined => { | ||
return state.get(key) as T ?? defaultValue; | ||
}, | ||
readEnum: <T extends string>( | ||
key: string, | ||
values: T[], | ||
defaultValue?: T, | ||
): T | undefined => { | ||
const value = state.get(key); | ||
|
||
if (value === undefined) { | ||
return defaultValue; | ||
} | ||
|
||
if (values.includes(value as T)) { | ||
return value as T; | ||
} | ||
|
||
return defaultValue; | ||
}, | ||
readInt: <T extends number>( | ||
key: string, | ||
defaultValue?: T, | ||
): T | undefined => { | ||
const value = state.get(key); | ||
|
||
if (value === undefined) { | ||
return defaultValue; | ||
} | ||
|
||
return parseInt(value, 10) as T; | ||
}, | ||
readBool: <T extends boolean>( | ||
key: string, | ||
defaultValue?: T, | ||
): T | undefined => { | ||
const value = state.get(key); | ||
|
||
if (value === undefined) { | ||
return defaultValue; | ||
} | ||
|
||
const sanitizedValue = value.trim().toLowerCase(); | ||
|
||
if (["1", "true", true].includes(sanitizedValue)) { | ||
return true as T; | ||
} | ||
|
||
return false as T; | ||
}, | ||
}; | ||
}; | ||
|
||
export const configure = async <T>( | ||
configureFn: ConfigureFn<T>, | ||
target?: Partial<T>, | ||
options?: LoaderOptions, | ||
): Promise<T | undefined> => { | ||
const envMap = await load(options); | ||
const reader = createEnvReader(envMap); | ||
|
||
const result = await configureFn(reader, target as T); | ||
|
||
return result ?? Promise.resolve(target as T); | ||
}; | ||
|
||
export { type LoaderOptions }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.