Skip to content

Instantly share code, notes, and snippets.

@resynth1943
Last active September 22, 2019 23:56
Show Gist options
  • Save resynth1943/30ef2f41a0ffd727d58b2ff567a17ffa to your computer and use it in GitHub Desktop.
Save resynth1943/30ef2f41a0ffd727d58b2ff567a17ffa to your computer and use it in GitHub Desktop.
Custom dotenv parser
import { promises, readFileSync } from 'fs';
import { DotEnvOptions } from './Types';
import { resolve } from 'path';
import Logger from 'Base/Logging';
const { readFile } = promises;
const logger = Logger.get('dotenv');
/** Bisects the line, revealing the key & value pair in an Array. */
function bisectLine(line: string) {
return line.split('=').slice(0, 2);
}
/** Checks if the line is a comment.*/
function isComment(line: string) {
return /^(#|\/\/)/.test(line);
}
/** Dequotes a vavue, and removes any spaces if quotes aren't present. */
function normalizeValue(value: string) {
if (/^"(.+)"$/.test(value)) {
// Drop the quotes.
return value.slice(1, -1);
}
// There aren't any quotes there. Drop the spaces.
return value.replace(/^\s+/, '');
}
/** Parses the source of a `.env` file into an object. */
export function parse(text: string) {
let lineNumber = 0;
const store: Record<string, string> = {};
for (const line of text.split('\n')) {
const bisected = bisectLine(line);
if (!isComment(line) && bisected.length !== 2) {
logger.error(
`Syntax error on line number ${lineNumber}:\n ${line}\n Expected either a comment, or a KEY=VALUE pair.`
);
}
const [key, value] = bisected;
store[key] = normalizeValue(value);
++lineNumber;
}
return store;
}
/** Parses the content from a `.env` file. */
export async function parseFile(
path: string,
options?: { encoding?: string | null; flag?: string | number } | null
) {
const content = await readFile(path, options);
return parse(content.toString());
}
/** Like `parseFile`, but runs synchronously. */
export function parseFileSync(path: string, options?: { encoding?: string | null; flag?: string } | null) {
const content = readFileSync(path, options);
return parse(content.toString());
}
export function install(record: Record<string, string>) {
for (const varName of Object.keys(record)) {
if (process.env[varName]) {
logger.info(`Not writing environmental variable ${varName}: It is already present in process.env.`);
continue;
}
process.env[varName] = record[varName as keyof typeof record];
}
}
/** Takes an optional path (which defaults to the .env file in the cwd), and adds all the environmental variables from
* that file to `process.env`. */
export async function installFile({ path: basePath, encoding: baseEncoding }: DotEnvOptions = {}) {
const path = basePath || resolve(process.cwd(), '.env');
const encoding = baseEncoding || 'utf8';
const record = await parseFile(path, { encoding });
install(record);
return record;
}
export function installFileSync({ path: basePath, encoding: baseEncoding }: DotEnvOptions = {}) {
const path = basePath || resolve(process.cwd(), '.env');
const encoding = baseEncoding || 'utf8';
const record = parseFileSync(path, { encoding });
install(record);
return record;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment