Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-75 fixing babel cache issue #288

Merged
merged 11 commits into from
Mar 17, 2022
Merged
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,11 @@ reports

.vs
.idea

.yarn/*
!.yarn/patches
!.yarn/releases
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
.pnp.*
8 changes: 8 additions & 0 deletions __tests__/__fixtures__/default-safe/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"plugins": [
["../../../", {
"path": "__tests__/__fixtures__/default/.env",
"safe": true
}]
]
}
2 changes: 2 additions & 0 deletions __tests__/__fixtures__/default-safe/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
API_KEY=abc123
DEV_USERNAME=username
4 changes: 4 additions & 0 deletions __tests__/__fixtures__/default-safe/source.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import {API_KEY, DEV_USERNAME} from '@env'

console.log(API_KEY)
console.log(DEV_USERNAME)
7 changes: 7 additions & 0 deletions __tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ describe('react-native-dotenv', () => {
expect(code).toBe('console.log("i win");\nconsole.log("username");')
})

it('should prioritize environment variables over variables defined in .env even when safe', () => {
process.env.API_KEY = 'i win'

const {code} = transformFileSync(FIXTURES + 'default-safe/source.js')
expect(code).toBe('console.log("i win");\nconsole.log("username");')
})

it('should load custom env file', () => {
const {code} = transformFileSync(FIXTURES + 'filename/source.js')
expect(code).toBe('console.log("abc123456");\nconsole.log("username123456");')
Expand Down
117 changes: 102 additions & 15 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const {readFileSync} = require('fs')
const {readFileSync, statSync} = require('fs')
const dotenv = require('dotenv')

function parseDotenvFile(path, verbose = false) {
Expand All @@ -18,7 +18,96 @@ function parseDotenvFile(path, verbose = false) {
return dotenv.parse(content)
}

module.exports = ({types: t}) => ({
function safeObjectAssign(targetObject, sourceObject, exceptions = []) {
const keys = Object.keys(targetObject)
for (let i = 0, length = keys.length; i < length; i++) {
if (targetObject[keys[i]] && sourceObject[keys[i]]) {
targetObject[keys[i]] = sourceObject[keys[i]]
}
}

for (let j = 0, length = exceptions.length; j < length; j++) {
if (sourceObject[exceptions[j]]) {
targetObject[exceptions[j]] = sourceObject[exceptions[j]]
}
}

return targetObject
}

function mtime(filePath) {
try {
return statSync(filePath).mtimeMs
} catch {
return null
}
}

module.exports = (api, options) => {
const t = api.types
this.env = {}
options = {
envName: 'APP_ENV',
moduleName: '@env',
path: '.env',
whitelist: null,
blacklist: null,
allowlist: null,
blocklist: null,
safe: false,
allowUndefined: true,
verbose: false,
...options,
}
const babelMode = process.env[options.envName] || (process.env.BABEL_ENV && process.env.BABEL_ENV !== 'undefined' && process.env.BABEL_ENV !== 'development' && process.env.BABEL_ENV) || process.env.NODE_ENV || 'development'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to wrap this in cache.using, so that if the env changes without restarting Node.js the plugin is reinstantiated.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you! I moved things around. I still don't know if it will reload properly in hot reload, but I don't think the environment mode can change without a js reload so I left that out. Hopefully it works in other tests when we release it in @next!

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am the second maintainer to this project so I'm not even sure about how all the babel features work

const localFilePath = options.path + '.local'
const modeFilePath = options.path + '.' + babelMode
const modeLocalFilePath = options.path + '.' + babelMode + '.local'

if (options.verbose) {
console.log('dotenvMode', babelMode)
}

api.cache.using(() => mtime(options.path))
api.cache.using(() => mtime(localFilePath))
api.cache.using(() => mtime(modeFilePath))
api.cache.using(() => mtime(modeLocalFilePath))

const dotenvTemporary = Object.assign({}, process.env)
if (options.safe) {
const parsed = parseDotenvFile(options.path, options.verbose)
const localParsed = parseDotenvFile(localFilePath, options.verbose)
const modeParsed = parseDotenvFile(modeFilePath, options.verbose)
const modeLocalParsed = parseDotenvFile(modeLocalFilePath, options.verbose)

this.env = safeObjectAssign(Object.assign(Object.assign(Object.assign(parsed, modeParsed), localParsed), modeLocalParsed), dotenvTemporary, ['NODE_ENV', 'BABEL_ENV', options.envName])
this.env.NODE_ENV = process.env.NODE_ENV || babelMode
} else {
dotenv.config({
path: modeLocalFilePath,
silent: true,
})
dotenv.config({
path: modeFilePath,
silent: true,
})
dotenv.config({
path: localFilePath,
silent: true,
})
dotenv.config({
path: options.path,
})
this.env = process.env
this.env = Object.assign(this.env, dotenvTemporary)
}

api.addExternalDependency(options.path)
api.addExternalDependency(localFilePath)
api.addExternalDependency(modeFilePath)
api.addExternalDependency(modeLocalFilePath)

return ({
name: 'dotenv-import',

pre() {
Expand All @@ -36,35 +125,32 @@ module.exports = ({types: t}) => ({
...this.opts,
}

const babelMode = process.env[this.opts.envName] || (process.env.BABEL_ENV && process.env.BABEL_ENV !== 'undefined' && process.env.BABEL_ENV !== 'development' && process.env.BABEL_ENV) || process.env.NODE_ENV || 'development'
if (this.opts.verbose) {
console.log('dotenvMode', babelMode)
}

const dotenvTemporary = Object.assign({}, process.env)
if (this.opts.safe) {
const parsed = parseDotenvFile(this.opts.path, this.opts.verbose)
const localParsed = parseDotenvFile(this.opts.path + '.local')
const modeParsed = parseDotenvFile(this.opts.path + '.' + babelMode)
const modeLocalParsed = parseDotenvFile(this.opts.path + '.' + babelMode + '.local')
this.env = Object.assign(Object.assign(Object.assign(parsed, modeParsed), localParsed), modeLocalParsed)
const localParsed = parseDotenvFile(localFilePath)
const modeParsed = parseDotenvFile(modeFilePath)
const modeLocalParsed = parseDotenvFile(modeLocalFilePath)
this.env = safeObjectAssign(Object.assign(Object.assign(Object.assign(parsed, modeParsed), localParsed), modeLocalParsed), dotenvTemporary, ['NODE_ENV', 'BABEL_ENV', options.envName])
this.env.NODE_ENV = process.env.NODE_ENV || babelMode
} else {
dotenv.config({
path: this.opts.path + '.' + babelMode + '.local',
path: modeLocalFilePath,
silent: true,
})
dotenv.config({
path: this.opts.path + '.' + babelMode,
path: modeFilePath,
silent: true,
})
dotenv.config({
path: this.opts.path + '.local',
path: localFilePath,
silent: true,
})
dotenv.config({
path: this.opts.path,
path: options.path,
})
this.env = process.env
this.env = Object.assign(this.env, dotenvTemporary)
}
},

Expand Down Expand Up @@ -125,3 +211,4 @@ module.exports = ({types: t}) => ({
},
},
})
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"dotenv": "^10.0.0"
},
"devDependencies": {
"@babel/core": "7.17.0",
"@babel/core": "7.17.2",
"codecov": "^3.8.3",
"jest": "27.4.4",
"jest-junit": "^13.0.0",
Expand Down
38 changes: 34 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,28 @@
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.0.tgz#86850b8597ea6962089770952075dcaabb8dba34"
integrity sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==

"@babel/core@7.17.0", "@babel/core@^7.1.0", "@babel/core@^7.12.16", "@babel/core@^7.7.2", "@babel/core@^7.7.5":
"@babel/core@7.17.2":
version "7.17.2"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.2.tgz#2c77fc430e95139d816d39b113b31bf40fb22337"
integrity sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw==
dependencies:
"@ampproject/remapping" "^2.0.0"
"@babel/code-frame" "^7.16.7"
"@babel/generator" "^7.17.0"
"@babel/helper-compilation-targets" "^7.16.7"
"@babel/helper-module-transforms" "^7.16.7"
"@babel/helpers" "^7.17.2"
"@babel/parser" "^7.17.0"
"@babel/template" "^7.16.7"
"@babel/traverse" "^7.17.0"
"@babel/types" "^7.17.0"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
json5 "^2.1.2"
semver "^6.3.0"

"@babel/core@^7.1.0", "@babel/core@^7.12.16", "@babel/core@^7.7.2", "@babel/core@^7.7.5":
version "7.17.0"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.0.tgz#16b8772b0a567f215839f689c5ded6bb20e864d5"
integrity sha512-x/5Ea+RO5MvF9ize5DeVICJoVrNv0Mi2RnIABrZEKYvPEpldXwauPkgvYA17cKa6WpU3LoYvYbuEMFtSNFsarA==
Expand Down Expand Up @@ -170,6 +191,15 @@
"@babel/traverse" "^7.17.0"
"@babel/types" "^7.17.0"

"@babel/helpers@^7.17.2":
version "7.17.2"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.2.tgz#23f0a0746c8e287773ccd27c14be428891f63417"
integrity sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==
dependencies:
"@babel/template" "^7.16.7"
"@babel/traverse" "^7.17.0"
"@babel/types" "^7.17.0"

"@babel/highlight@^7.16.7":
version "7.16.10"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88"
Expand Down Expand Up @@ -4626,15 +4656,15 @@ xo@^0.47.0:
integrity sha512-QHRIpaPSG7tK7PX4K4fqe0V4EH1u2IkM7Pr356u1fKcVsZskw7i9gfEqyBLsnnc4e4Y8gnLtIqasLJsGPqM8sA==
dependencies:
"@eslint/eslintrc" "^1.0.4"
"@typescript-eslint/eslint-plugin" "*"
"@typescript-eslint/parser" "*"
"@typescript-eslint/eslint-plugin" "^5.4.0"
"@typescript-eslint/parser" "^5.4.0"
arrify "^3.0.0"
cosmiconfig "^7.0.1"
define-lazy-prop "^3.0.0"
eslint "^8.3.0"
eslint-config-prettier "^8.3.0"
eslint-config-xo "^0.39.0"
eslint-config-xo-typescript "*"
eslint-config-xo-typescript "^0.47.1"
eslint-formatter-pretty "^4.1.0"
eslint-import-resolver-webpack "^0.13.2"
eslint-plugin-ava "^13.1.0"
Expand Down