Make dependencies peer. LAzy load YN so that if the feature is not used the dependency is ignored'

This commit is contained in:
RingOfStorms (Joshua Bell) 2020-12-21 20:03:52 -06:00
parent eb190da262
commit 64920a7a24
8 changed files with 111 additions and 77 deletions

View file

@ -15,7 +15,7 @@ Example root files:
```
# with npm
npm install @ringofstorms/dotenv-multi
npm install @ringofstorms/dotenv-multi dotenv dotenv-expand yn
```
# Usage

11
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "@ringofstorms/dotenv-multi",
"version": "0.0.1",
"version": "0.1.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -35,12 +35,14 @@
"dotenv": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
"dev": true
},
"dotenv-expand": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA=="
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
"dev": true
},
"fs.realpath": {
"version": "1.0.0",
@ -126,7 +128,8 @@
"yn": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yn/-/yn-4.0.0.tgz",
"integrity": "sha512-huWiiCS4TxKc4SfgmTwW1K7JmXPPAmuXWYy4j9qjQo4+27Kni8mGhAAi1cloRWmBe2EqcLgt3IGqQoRL/MtPgg=="
"integrity": "sha512-huWiiCS4TxKc4SfgmTwW1K7JmXPPAmuXWYy4j9qjQo4+27Kni8mGhAAi1cloRWmBe2EqcLgt3IGqQoRL/MtPgg==",
"dev": true
}
}
}

View file

@ -1,6 +1,6 @@
{
"name": "@ringofstorms/dotenv-multi",
"version": "0.1.1",
"version": "0.2.0",
"description": "Dot environment file loader that acts like Create React App's supporting many files with per NODE_ENV/local settings.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -14,7 +14,7 @@
},
"author": "Josh (RingOfStorms)",
"license": "ISC",
"dependencies": {
"peerDependencies": {
"dotenv": "^8.2.0",
"dotenv-expand": "^5.1.0",
"yn": "^4.0.0"
@ -22,7 +22,10 @@
"devDependencies": {
"@types/node": "^14.14.14",
"rimraf": "^3.0.2",
"typescript": "^4.1.3"
"typescript": "^4.1.3",
"dotenv": "^8.2.0",
"dotenv-expand": "^5.1.0",
"yn": "^4.0.0"
},
"repository": {
"type": "git",

28
src/booleanConverter.ts Normal file
View file

@ -0,0 +1,28 @@
import * as yn from 'yn';
import { BooleanConverter, ConfigOptions } from "./types"
export const booleanConverter: BooleanConverter = (options: ConfigOptions, log: (...args: any[]) => void) => {
const convertPredicate = (key: string): boolean => {
if (key) {
if (Array.isArray(options.convertToBoolean)) {
if (options.convertToBoolean.includes(key)) {
return true;
}
} else if (options.convertToBoolean === key) {
return true;
}
if (typeof options.convertToBooleanPredicate === 'function' && options.convertToBooleanPredicate(key)) {
return true;
}
}
return false;
}
Object.entries(process.env).forEach(([key, value]) => {
if (convertPredicate(key)) {
const boolValue = yn(value) ? 'true' : '';
process.env[key] = boolValue;
log('Environment variable converted into string boolean value:', key, "original:", value, 'newValue:', boolValue);
}
});
}

46
src/dotenvMulti.ts Normal file
View file

@ -0,0 +1,46 @@
import * as fs from 'fs';
import * as path from 'path';
import * as dotenv from 'dotenv';
import * as dotenvExpand from 'dotenv-expand';
import { BooleanConverter, ConfigOptions } from "./types"
import { Lazy } from "./lazy"
const lazyBoolConverter = new Lazy<BooleanConverter>(() => require('./booleanConverter').booleanConverter);
export function config(options: ConfigOptions = {}): void {
const log = options.debug ? console.log : (() => {});
// Same logic as create-react-app for resolving env files
// https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/config/env.js
const NODE_ENV = (process.env.NODE_ENV || 'unknown').toLowerCase();
const appDirectory = fs.realpathSync(process.cwd());
const dotenvFilePath: string = path.resolve(appDirectory, '.env');
const dotenvFiles = [
`${ dotenvFilePath }.${ NODE_ENV }.local`,
`${ dotenvFilePath }.${ NODE_ENV }`,
NODE_ENV !== 'test' && `${ dotenvFilePath }.local`,
dotenvFilePath,
].filter(i => i) as string[]; // this cast is safe due to the identity filter
log('Environment files to check:', dotenvFiles.join());
const propsConfigured = {};
dotenvFiles.forEach((dotenvFile) => {
if (fs.existsSync(dotenvFile)) {
const {parsed} = dotenvExpand(dotenv.config({path: dotenvFile, debug: options.debug || undefined }));
Object.assign(propsConfigured, parsed);
}
});
log('Environment variables loaded from dotenvs:', Object.keys(propsConfigured).length);
log('\t', propsConfigured);
const runBoolConverter: boolean = !!(options.convertToBooleanPredicate || options.convertToBoolean);
log('Environment variable boolean converter will run:', runBoolConverter);
if (runBoolConverter) {
lazyBoolConverter.get()(options, log);
}
}

View file

@ -1,69 +1,2 @@
import * as fs from 'fs';
import * as path from 'path';
import * as dotenv from 'dotenv';
import * as dotenvExpand from 'dotenv-expand';
import * as yn from 'yn';
export interface ConfigOptions {
convertToBooleanPredicate?: (variable: string) => boolean;
convertToBoolean?: string | string[];
debug?: boolean;
}
export function config(options: ConfigOptions = {}): void {
const log = options.debug ? console.log : (() => {});
// Same logic as create-react-app for resolving env files
// https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/config/env.js
const NODE_ENV = (process.env.NODE_ENV || 'unknown').toLowerCase();
const appDirectory = fs.realpathSync(process.cwd());
const dotenvFilePath: string = path.resolve(appDirectory, '.env');
const dotenvFiles = [
`${ dotenvFilePath }.${ NODE_ENV }.local`,
`${ dotenvFilePath }.${ NODE_ENV }`,
NODE_ENV !== 'test' && `${ dotenvFilePath }.local`,
dotenvFilePath,
].filter(i => i) as string[]; // this cast is safe due to the identity filter
log('Environment files to check:', dotenvFiles.join());
const propsConfigured = {};
dotenvFiles.forEach((dotenvFile) => {
if (fs.existsSync(dotenvFile)) {
const {parsed} = dotenvExpand(dotenv.config({path: dotenvFile, debug: options.debug || undefined }));
Object.assign(propsConfigured, parsed);
}
});
log('Environment variables loaded from dotenvs:', Object.keys(propsConfigured).length);
log('\t', propsConfigured);
const runBoolConverter: boolean = !!(options.convertToBooleanPredicate || options.convertToBoolean);
log('Environment variable boolean converter will run:', runBoolConverter);
if (runBoolConverter) {
const convertPredicate = (key: string): boolean => {
if (key) {
if (Array.isArray(options.convertToBoolean)) {
if (options.convertToBoolean.includes(key)) {
return true;
}
} else if (options.convertToBoolean === key) {
return true;
}
if (typeof options.convertToBooleanPredicate === 'function' && options.convertToBooleanPredicate(key)) {
return true;
}
}
return false;
}
Object.entries(process.env).forEach(([key, value]) => {
if (convertPredicate(key)) {
const boolValue = yn(value) ? 'true' : '';
process.env[key] = boolValue;
log('Environment variable converted into string boolean value:', key, "original:", value, 'newValue:', boolValue);
}
});
}
}
export * from './dotenvMulti'
export * from './types';

12
src/lazy.ts Normal file
View file

@ -0,0 +1,12 @@
export class Lazy<T> {
private instance: T | undefined = undefined;
constructor(private readonly factory: () => any) {}
get (): T {
if (!this.instance) {
this.instance = this.factory();
}
return this.instance as T;
}
}

9
src/types.ts Normal file
View file

@ -0,0 +1,9 @@
export interface ConfigOptions {
convertToBooleanPredicate?: (variable: string) => boolean;
convertToBoolean?: string | string[];
debug?: boolean;
}
export interface BooleanConverter {
(options: ConfigOptions, log: (...args: any[]) => void): void;
}