Make dependencies peer. LAzy load YN so that if the feature is not used the dependency is ignored'
This commit is contained in:
parent
eb190da262
commit
64920a7a24
8 changed files with 111 additions and 77 deletions
|
@ -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
11
package-lock.json
generated
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
28
src/booleanConverter.ts
Normal 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
46
src/dotenvMulti.ts
Normal 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);
|
||||
}
|
||||
}
|
71
src/index.ts
71
src/index.ts
|
@ -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
12
src/lazy.ts
Normal 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
9
src/types.ts
Normal 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue