Skip to content

Commit

Permalink
Initial release of lazy-universal-dotenv
Browse files Browse the repository at this point in the history
  • Loading branch information
pksunkara committed Oct 14, 2018
1 parent 684196f commit f5668ea
Show file tree
Hide file tree
Showing 15 changed files with 118 additions and 174 deletions.
1 change: 1 addition & 0 deletions .env.expansion1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
EXPAND_PATH = /usr/bin:$BINARY_PATH
1 change: 1 addition & 0 deletions .env.expansion1.local
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BINARY_PATH = /usr/local/bin
1 change: 1 addition & 0 deletions .env.expansion2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
EXPAND_DATA=red,$APP_DATA
2 changes: 0 additions & 2 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
extends:
- readable
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "universal-dotenv",
"name": "lazy-universal-dotenv",
"version": "1.9.1",
"description": "Robust Environment Configuration for Universal Applications.",
"main": "lib/index.cjs.js",
Expand Down
10 changes: 2 additions & 8 deletions src/__snapshots__/build.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,18 @@

exports[`Serializes BUILD_TARGET 1`] = `
Object {
"APP_ROOT": Any<String>,
"APP_SOURCE": Any<String>,
"APP_TEST_IS_CLIENT": "true",
"APP_TEST_OVERRIDE": "overridden",
"APP_TEST_PATH": "one/two/three.js",
"BUILD_TARGET": "client",
"NODE_ENV": "test",
"TEST_IGNORE": "123",
}
`;

exports[`Serializes BUILD_TARGET 2`] = `
Object {
"APP_ROOT": Any<String>,
"APP_SOURCE": Any<String>,
"APP_TEST_IS_CLIENT": "\\"true\\"",
"APP_TEST_OVERRIDE": "\\"overridden\\"",
"APP_TEST_PATH": "\\"one/two/three.js\\"",
"BUILD_TARGET": "\\"client\\"",
"NODE_ENV": "\\"test\\"",
"TEST_IGNORE": "\\"123\\"",
}
`;
39 changes: 39 additions & 0 deletions src/__snapshots__/expand.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Supports expand of variable from .env 1`] = `
Object {
"APP_TEST_OVERRIDE": "original",
"APP_TEST_PATH": "one/two/three.js",
"BINARY_PATH": "/usr/local/bin",
"EXPAND_PATH": "/usr/bin:/usr/local/bin",
"TEST_IGNORE": "123",
}
`;

exports[`Supports expand of variable from .env 2`] = `
Object {
"APP_TEST_OVERRIDE": "\\"original\\"",
"APP_TEST_PATH": "\\"one/two/three.js\\"",
"BINARY_PATH": "\\"/usr/local/bin\\"",
"EXPAND_PATH": "\\"/usr/bin:/usr/local/bin\\"",
"TEST_IGNORE": "\\"123\\"",
}
`;

exports[`Supports expand of variable from environment 1`] = `
Object {
"APP_TEST_OVERRIDE": "original",
"APP_TEST_PATH": "one/two/three.js",
"EXPAND_DATA": "red,yellow",
"TEST_IGNORE": "123",
}
`;

exports[`Supports expand of variable from environment 2`] = `
Object {
"APP_TEST_OVERRIDE": "\\"original\\"",
"APP_TEST_PATH": "\\"one/two/three.js\\"",
"EXPAND_DATA": "\\"red,yellow\\"",
"TEST_IGNORE": "\\"123\\"",
}
`;
34 changes: 4 additions & 30 deletions src/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,43 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Ignores non app-specific settings 1`] = `
exports[`Doesn't support manually defined envs 1`] = `
Object {
"APP_ROOT": Any<String>,
"APP_SOURCE": Any<String>,
"APP_TEST_OVERRIDE": "overridden",
"APP_TEST_PATH": "one/two/three.js",
"NODE_ENV": "test",
"TEST_IGNORE": "123",
}
`;

exports[`Ignores non app-specific settings 2`] = `
exports[`Doesn't support manually defined envs 2`] = `
Object {
"APP_ROOT": Any<String>,
"APP_SOURCE": Any<String>,
"APP_TEST_OVERRIDE": "\\"overridden\\"",
"APP_TEST_PATH": "\\"one/two/three.js\\"",
"NODE_ENV": "\\"test\\"",
}
`;
exports[`Supports manually defined envs 1`] = `
Object {
"APP_DATA": "yellow",
"APP_ROOT": Any<String>,
"APP_SOURCE": Any<String>,
"APP_TEST_OVERRIDE": "overridden",
"APP_TEST_PATH": "one/two/three.js",
"NODE_ENV": "test",
}
`;
exports[`Supports manually defined envs 2`] = `
Object {
"APP_DATA": "\\"yellow\\"",
"APP_ROOT": Any<String>,
"APP_SOURCE": Any<String>,
"APP_TEST_OVERRIDE": "\\"overridden\\"",
"APP_TEST_PATH": "\\"one/two/three.js\\"",
"NODE_ENV": "\\"test\\"",
"TEST_IGNORE": "\\"123\\"",
}
`;
8 changes: 2 additions & 6 deletions src/__snapshots__/noenv.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,16 @@

exports[`Defaults to development for NODE_ENV 1`] = `
Object {
"APP_ROOT": Any<String>,
"APP_SOURCE": Any<String>,
"APP_TEST_OVERRIDE": "original",
"APP_TEST_PATH": "one/two/three.js",
"NODE_ENV": "development",
"TEST_IGNORE": "123",
}
`;

exports[`Defaults to development for NODE_ENV 2`] = `
Object {
"APP_ROOT": Any<String>,
"APP_SOURCE": Any<String>,
"APP_TEST_OVERRIDE": "\\"original\\"",
"APP_TEST_PATH": "\\"one/two/three.js\\"",
"NODE_ENV": "\\"development\\"",
"TEST_IGNORE": "\\"123\\"",
}
`;
10 changes: 1 addition & 9 deletions src/build.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
process.env.BUILD_TARGET = "client"

const snapshotOpts = {
APP_ROOT: expect.any(String),
APP_SOURCE: expect.any(String)
}
const snapshotOpts = {}

/* eslint-disable import/no-commonjs */
// We can't use ESM when relying on the fact the the env from the top is correctly respected.
Expand All @@ -15,8 +12,3 @@ test("Serializes BUILD_TARGET", () => {
expect(stringified).toMatchSnapshot(snapshotOpts)
expect(webpack).toBeDefined()
})

test("Exports BUILD_TARGET", () => {
const { raw } = api.getEnvironment()
expect(raw.BUILD_TARGET).toBe("client")
})
19 changes: 19 additions & 0 deletions src/expand.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getEnvironment } from "."

const snapshotOpts = {}

test("Supports expand of variable from .env", () => {
const { raw, stringified, webpack } = getEnvironment({ nodeEnv: 'expansion1' })
expect(raw).toMatchSnapshot(snapshotOpts)
expect(stringified).toMatchSnapshot(snapshotOpts)
expect(webpack).toBeDefined()
})

test("Supports expand of variable from environment", () => {
process.env.APP_DATA = "yellow"
const { raw, stringified, webpack } = getEnvironment({ nodeEnv: 'expansion2' })
expect(raw).toMatchSnapshot(snapshotOpts)
expect(stringified).toMatchSnapshot(snapshotOpts)
expect(webpack).toBeDefined()
delete process.env.APP_DATA
})
108 changes: 40 additions & 68 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,82 +1,54 @@
import fs from "fs"
import path from "path"
import appRoot from "app-root-dir"
import dotenv from "dotenv"
import expand from "dotenv-expand"

const dotEnvBase = path.join(appRoot.get(), ".env")

// Cache Node environment at load time. We have to do it to make
// sure that the serialization, which might happen later, is in sync
// with the parsing of the conditional NODE_ENV files now.
const NODE_ENV = process.env.NODE_ENV

// Either "client" or "server"
const BUILD_TARGET = process.env.BUILD_TARGET

// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
const dotenvFiles = [
BUILD_TARGET && NODE_ENV && `${dotEnvBase}.${BUILD_TARGET}.${NODE_ENV}.local`,
BUILD_TARGET && NODE_ENV && `${dotEnvBase}.${BUILD_TARGET}.${NODE_ENV}`,
BUILD_TARGET && NODE_ENV !== "test" && `${dotEnvBase}.${BUILD_TARGET}.local`,
BUILD_TARGET && `${dotEnvBase}.${BUILD_TARGET}`,
NODE_ENV && `${dotEnvBase}.${NODE_ENV}.local`,
NODE_ENV && `${dotEnvBase}.${NODE_ENV}`,
NODE_ENV !== "test" && `${dotEnvBase}.local`,
dotEnvBase
].filter(Boolean)
export function getEnvironment({ nodeEnv, buildTarget } = {}) {
let raw = {}
const stringified = {}
const webpack = { "process.env": stringified }

// Load environment variables from .env* files. Suppress warnings using silent
// if this file is missing. dotenv will never modify any environment variables
// that have already been set. Variable expansion is supported in .env files.
// https://github.com/motdotla/dotenv
// https://github.com/motdotla/dotenv-expand
dotenvFiles.forEach((dotenvFile) => {
if (fs.existsSync(dotenvFile)) {
require("dotenv-expand")(
require("dotenv").config({
// Cache Node environment at load time. We have to do it to make
// sure that the serialization, which might happen later, is in sync
// with the parsing of the conditional NODE_ENV files now.
const NODE_ENV = typeof nodeEnv === "undefined" ? process.env.NODE_ENV : nodeEnv

// Either "client" or "server"
const BUILD_TARGET = typeof nodeEnv === "undefined" ? process.env.BUILD_TARGET : buildTarget

// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
const dotenvFiles = [
BUILD_TARGET && NODE_ENV && `${dotEnvBase}.${BUILD_TARGET}.${NODE_ENV}.local`,
BUILD_TARGET && NODE_ENV && `${dotEnvBase}.${BUILD_TARGET}.${NODE_ENV}`,
BUILD_TARGET && NODE_ENV !== "test" && `${dotEnvBase}.${BUILD_TARGET}.local`,
BUILD_TARGET && `${dotEnvBase}.${BUILD_TARGET}`,
NODE_ENV && `${dotEnvBase}.${NODE_ENV}.local`,
NODE_ENV && `${dotEnvBase}.${NODE_ENV}`,
NODE_ENV !== "test" && `${dotEnvBase}.local`,
dotEnvBase
].filter(Boolean)

// Load environment variables from .env* files. Suppress warnings using silent
// if this file is missing. dotenv will never modify any environment variables
// that have already been set. Variable expansion is supported in .env files.
// https://github.com/motdotla/dotenv
// https://github.com/motdotla/dotenv-expand
dotenvFiles.forEach((dotenvFile) => {
if (fs.existsSync(dotenvFile)) {
const config = dotenv.config({
path: dotenvFile
})
)
}
})

if (process.env.APP_ROOT == null) {
process.env.APP_ROOT = appRoot.get()
}

if (process.env.APP_SOURCE == null) {
const sourceFolder = path.join(process.env.APP_ROOT, "src")
process.env.APP_SOURCE = fs.existsSync(sourceFolder) ? sourceFolder : process.env.APP_ROOT
}

// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
// injected into the application via DefinePlugin in Webpack configuration.
const APP_SPECIFIC_ENV = /^APP_/i

export function getEnvironment() {
const raw = {}
Object.keys(process.env)
.filter((key) => APP_SPECIFIC_ENV.test(key))
.forEach((key) => {
raw[key] = process.env[key]
})

// Add core settings to raw data - which is not prefixed at all
raw.NODE_ENV = NODE_ENV || "development"

if (BUILD_TARGET) {
raw.BUILD_TARGET = BUILD_TARGET
}

// Add hint about root and source folders
raw.APP_ROOT = process.env.APP_ROOT
raw.APP_SOURCE = process.env.APP_SOURCE
raw = Object.assign({}, raw, expand(config).parsed)
}
})

// Stringify all values so we can feed into Webpack DefinePlugin
const stringified = {}
const webpack = { "process.env": stringified }
Object.keys(raw).forEach((key) => {
stringified[key] = JSON.stringify(raw[key])
})
Expand Down
42 changes: 2 additions & 40 deletions src/index.test.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,12 @@
import { getEnvironment } from "."

const snapshotOpts = {
APP_ROOT: expect.any(String),
APP_SOURCE: expect.any(String)
}
const snapshotOpts = {}

test("Sets up process.env", () => {
expect(process.env.APP_TEST_PATH).toBe("one/two/three.js")
expect(process.env.TEST_IGNORE).toBe("123")
})

test("Ignores non app-specific settings", () => {
const { raw, stringified, webpack } = getEnvironment()
expect(raw).toMatchSnapshot(snapshotOpts)
expect(stringified).toMatchSnapshot(snapshotOpts)
expect(webpack).toBeDefined()
})

test("Supports manually defined envs", () => {
test("Doesn't support manually defined envs", () => {
process.env.APP_DATA = "yellow"
const { raw, stringified, webpack } = getEnvironment()
expect(raw).toMatchSnapshot(snapshotOpts)
expect(stringified).toMatchSnapshot(snapshotOpts)
expect(webpack).toBeDefined()
delete process.env.APP_DATA
})

test("Exports NODE_ENV", () => {
const { raw } = getEnvironment()
expect(raw.NODE_ENV).toBe("test")
})

test("Exports APP_ROOT", () => {
const { raw } = getEnvironment()
expect(raw.APP_ROOT).toMatch(/universal-dotenv$/)
})

test("Exports APP_SOURCE", () => {
const { raw } = getEnvironment()
expect(raw.APP_SOURCE).toMatch(/universal-dotenv[\\/]src$/)
})

test("Adds APP_ROOT to process.env", () => {
expect(process.env.APP_ROOT).toBeDefined()
})

test("Adds APP_SOURCE to process.env", () => {
expect(process.env.APP_SOURCE).toBeDefined()
})
Loading

0 comments on commit f5668ea

Please sign in to comment.