Skip to content

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
Pinta365 committed Mar 4, 2024
0 parents commit 777c881
Show file tree
Hide file tree
Showing 8 changed files with 596 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.env
.vscode
test.ts
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 cross-org <https://github.com/cross-org>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
123 changes: 123 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
## **Flexible Environment Variable Management for Deno, Bun, and Node.js**

This library provides a consistent and simple interface for managing environment variables across multiple runtimes,
making it ideal for cross-platform development.

## **Features**

- **Cross-runtime support:** Works seamlessly within Deno, Bun, and Node.js environments.
- **Get and Set environment variables:** Retrieve and Modify environment variables in a consistent interface across
multiple runtimes.
- **Validation:** Ensures environment variables are valid before usage.
- **Error handling:** Provides clear error messages for unsupported runtimes or validation failures.
- **Optional environmental file loading:** Supports loading variables from custom .env files _(experimental)_

## **Installation**

```bash
#For Deno
deno add @cross/env

#For Bun
bunx jsr add @cross/env

#For Node.js
npx jsr add @cross/env
```

## Getting Started

**Usage Examples**

import relevant functions.

```javascript
import { getEnv, setEnv, validateEnv } from "@cross/env";
```

Simple get example.

```javascript
const apiKey = getEnv("API_KEY");

// or
console.log(`Home directory: ${getEnv("HOME")}`);
```

Simple set example.

```javascript
setEnv("ENVIRONMENT", "development");
setEnv("THE_COLOUR", "red");
```

Checking if a variable exists.

```javascript
if (hasEnv("DB_USER")) {
// Handle database connection logic
}
```

Getting all environment variables.

```javascript
// getting all variables
const allVariables = getAllEnv();
// getting all variables prefixed with API_
const apiVariables = getAllEnv("API_");
// Output:
// { API_KEY: 'abc123', API_VERSION: 'v2' }
```

Validation through custom functions.

```javascript
// Validate a colour and execute conditional code.
const colourTest: ValidatorFunction = (value) => value === "red" || value === "green";
if (validateEnv("THE_COLOUR", colourTest)) {
console.log("Yep, its red or green.");
}
```

Validation through custom functions and getting the variable content.

```javascript
// or validating and getting a port number.
const isValidPort = (value: string): boolean => /^\d+$/.test(value);
const port = validateAndGetEnv("PORT", isValidPort);

// or checking it we are reading a positive number.
function isPositiveNumber(value: string): boolean {
return !isNaN(Number(value)) && Number(value) > 0;
}
const timeout = validateAndGetEnv("TIMEOUT", isPositiveNumber);
```

## **Configuration (optional):**

For more advanced use cases you can configure the behaviour of the library. The library defaults to showing console
warnings but not throwing errors.

```javascript
await setupEnv({
throwErrors: true, // Throw errors in unsupported runtimes
logWarnings: false, // Disable warnings
loadDotEnv: true, // Load from a .env file (experimental)
dotEnvFile: ".env.local", // Specify an alternate .env file (experimental)
});
```

**Experimental .env File Support**

Use the `loadDotEnv` parameter and optionally `dotEnvFile` in `setupEnv()` to automatically load environment variables
from a .env file. Currently, this feature might have runtime-specific limitations.

## Issues

Issues or questions concerning the library can be raised at the
[github repository](https://github.com/cross-org/env/issues) page.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
9 changes: 9 additions & 0 deletions deno.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tasks": {
"publish": "deno publish --config jsr.json"
},
"fmt": {
"lineWidth": 120,
"indentWidth": 4
}
}
5 changes: 5 additions & 0 deletions jsr.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "@cross/env",
"version": "0.1.0",
"exports": "./mod.ts"
}
115 changes: 115 additions & 0 deletions lib/filehandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { FileReadError, Runtimes, UnsupportedEnvironmentError } from "./helpers.ts";

//Simulates/shims the Deno runtime for development purposes.
declare const Deno: {
readTextFileSync(filePath: string): string;
env: {
get(key: string): string | undefined;
set(key: string, value: string): void;
toObject(): Record<string, string | undefined>;
};
};

//Simulates/shims the Bun runtime for development purposes.
declare const Bun: {
file(filePath: string): { text(): string };
env: Record<string, string>;
};

//Simulates/shims Node.js function to load modules for development purposes.
// deno-lint-ignore no-explicit-any
declare const require: (module: string) => any;

//Simulates/shims Node.js process object for development purposes.
declare const process: { env: Record<string, string> };

//Simulates/shims the Node.js fs namespace for development purposes.
// deno-lint-ignore no-explicit-any
declare const fs: any;

/**
* Loads environment variables from a .env file, handling file existence,
* runtime differences, and errors.
*
* @param {Runtimes} currentRuntime - The current runtime environment.
* @param {string} [filePath=".env"] - The path to the file to load, defaults to .env
* @param {boolean} throwErrors - Controls whether errors are thrown
* @param {boolean} logWarnings - Controls whether warnings are logged.
* @returns {Record<string, string>} A object of parsed environment variables.
* @throws {UnsupportedEnvironmentError} If the runtime is unsupported and the 'throwErrors' flag is set.
* @throws {FileReadError} If there's an error reading the .env file and the 'throwErrors' flag is set.
*/
export async function loadEnvFile(
currentRuntime: Runtimes,
filePath: string = ".env",
throwErrors: boolean,
logWarnings: boolean,
): Promise<Record<string, string>> {
let fileContent = "";

try {
switch (currentRuntime) {
case Runtimes.Deno:
fileContent = Deno.readTextFileSync(filePath);
break;
case Runtimes.Bun:
fileContent = await Bun.file(filePath).text();
break;
case Runtimes.Node: {
if (typeof fs === "undefined") {
const fs = require("fs");
fileContent = fs.readFileSync(filePath, "utf-8");
} else {
throw new Error("Node.js 'fs' module is not available in this environment.");
}
break;
}
default:
{
if (throwErrors) {
throw new UnsupportedEnvironmentError();
}
if (logWarnings) {
console.warn("Unsupported runtime");
}
}
break;
}
} catch (err) {
if (throwErrors) {
throw new FileReadError(err.message);
}
if (logWarnings) {
console.warn(err.message);
}
}

return parseEnvFile(fileContent);
}

/**
* Parses a string representing the content of a .env file and creates a
* dictionary of environment variables.
*
* @param {string} content - The string content of the .env file.
* @returns {Record<string, string>} A object of parsed environment variables.
*/
function parseEnvFile(content: string): Record<string, string> {
const envVars: Record<string, string> = {};

if (content.length > 0) {
content.split("\n").forEach((line) => {
const trimmedLine = line.trim();

// Ignore comments and empty lines
if (!trimmedLine || trimmedLine.startsWith("#")) {
return;
}

const [key, value] = trimmedLine.split("=");
envVars[key] = value;
});
}

return envVars;
}
57 changes: 57 additions & 0 deletions lib/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Enum of supported runtimes.
*/
export enum Runtimes {
Deno = "deno",
Bun = "bun",
Node = "node",
Unsupported = "unsupported",
}

/**
* Type alias for a validator function used in environment variable checks.
*/
export type ValidatorFunction = (value: string) => boolean;

/** Env setup options. */
export interface EnvOptions {
/** (default: false) - If true, throws an errors in unsupported runtimes. */
throwErrors?: boolean;
/** (default: true) - If true, logs a warning to the console when environment variables
* are accessed in unsupported runtimes. */
logWarnings?: boolean;
/** (default: false) - If true, read and load environment variables a file.
* (default file: ".env") **@experimental** Support for loading .env files may have
* limitations in certain runtimes. */
loadDotEnv?: boolean;
/** (default: ".env") - filename of the file containing environment variables to load. */
dotEnvFile?: string;
}

/**
* Error thrown when attempting to set or retrieve environment variables in
* unsupported runtimes.
*/
export class UnsupportedEnvironmentError extends Error {
constructor() {
super("Unsupported runtime environment.");
}
}

/**
* Error thrown when attempting to validate an environment variable.
*/
export class ValidationError extends Error {
constructor(message: string) {
super(message);
}
}

/**
* Error thrown when attempting to read file with environment variables.
*/
export class FileReadError extends Error {
constructor(message: string) {
super(message);
}
}
Loading

0 comments on commit 777c881

Please sign in to comment.