react-native-dotenv
Advanced tools
Comparing version 3.3.1 to 3.4.0
@@ -56,2 +56,9 @@ const {transformFileSync} = require('@babel/core') | ||
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', () => { | ||
@@ -58,0 +65,0 @@ const {code} = transformFileSync(FIXTURES + 'filename/source.js') |
@@ -8,1 +8,2 @@ ## Link to issue ## | ||
* What was the solution to the problem? | ||
* Did you set option `verbose: true` in your babel config? If so, what was the output? |
265
index.js
@@ -1,2 +0,2 @@ | ||
const {readFileSync} = require('fs') | ||
const {readFileSync, statSync} = require('fs') | ||
const dotenv = require('dotenv') | ||
@@ -21,108 +21,195 @@ | ||
module.exports = ({types: t}) => ({ | ||
name: 'dotenv-import', | ||
pre() { | ||
this.opts = { | ||
envName: 'APP_ENV', | ||
moduleName: '@env', | ||
path: '.env', | ||
whitelist: null, | ||
blacklist: null, | ||
allowlist: null, | ||
blocklist: null, | ||
safe: false, | ||
allowUndefined: true, | ||
verbose: false, | ||
...this.opts, | ||
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]] | ||
} | ||
} | ||
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) | ||
for (let j = 0, length = exceptions.length; j < length; j++) { | ||
if (sourceObject[exceptions[j]]) { | ||
targetObject[exceptions[j]] = sourceObject[exceptions[j]] | ||
} | ||
} | ||
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) | ||
this.env.NODE_ENV = process.env.NODE_ENV || babelMode | ||
} else { | ||
dotenv.config({ | ||
path: this.opts.path + '.' + babelMode + '.local', | ||
silent: true, | ||
}) | ||
dotenv.config({ | ||
path: this.opts.path + '.' + babelMode, | ||
silent: true, | ||
}) | ||
dotenv.config({ | ||
path: this.opts.path + '.local', | ||
silent: true, | ||
}) | ||
dotenv.config({ | ||
path: this.opts.path, | ||
}) | ||
this.env = process.env | ||
} | ||
}, | ||
return targetObject | ||
} | ||
visitor: { | ||
ImportDeclaration(path, {opts}) { | ||
if (path.node.source.value === opts.moduleName) { | ||
for (const [idx, specifier] of path.node.specifiers.entries()) { | ||
if (specifier.type === 'ImportDefaultSpecifier') { | ||
throw path.get('specifiers')[idx].buildCodeFrameError('Default import is not supported') | ||
} | ||
function mtime(filePath) { | ||
try { | ||
return statSync(filePath).mtimeMs | ||
} catch { | ||
return null | ||
} | ||
} | ||
if (specifier.type === 'ImportNamespaceSpecifier') { | ||
throw path.get('specifiers')[idx].buildCodeFrameError('Wildcard import is not supported') | ||
} | ||
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' | ||
const localFilePath = options.path + '.local' | ||
const modeFilePath = options.path + '.' + babelMode | ||
const modeLocalFilePath = options.path + '.' + babelMode + '.local' | ||
if (specifier.imported && specifier.local) { | ||
const importedId = specifier.imported.name | ||
const localId = specifier.local.name | ||
if (options.verbose) { | ||
console.log('dotenvMode', babelMode) | ||
} | ||
if (Array.isArray(opts.allowlist) && !opts.allowlist.includes(importedId)) { | ||
throw path.get('specifiers')[idx].buildCodeFrameError(`"${importedId}" was not present in allowlist`) | ||
} else if (Array.isArray(opts.whitelist) && !opts.whitelist.includes(importedId)) { | ||
console.warn('[DEPRECATION WARNING] This option is will be deprecated soon. Use allowlist instead') | ||
throw path.get('specifiers')[idx].buildCodeFrameError(`"${importedId}" was not whitelisted`) | ||
} | ||
api.cache.using(() => mtime(options.path)) | ||
api.cache.using(() => mtime(localFilePath)) | ||
api.cache.using(() => mtime(modeFilePath)) | ||
api.cache.using(() => mtime(modeLocalFilePath)) | ||
if (Array.isArray(opts.blocklist) && opts.blocklist.includes(importedId)) { | ||
throw path.get('specifiers')[idx].buildCodeFrameError(`"${importedId}" was not present in blocklist`) | ||
} else if (Array.isArray(opts.blacklist) && opts.blacklist.includes(importedId)) { | ||
console.warn('[DEPRECATION WARNING] This option is will be deprecated soon. Use blocklist instead') | ||
throw path.get('specifiers')[idx].buildCodeFrameError(`"${importedId}" was blacklisted`) | ||
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() { | ||
this.opts = { | ||
envName: 'APP_ENV', | ||
moduleName: '@env', | ||
path: '.env', | ||
whitelist: null, | ||
blacklist: null, | ||
allowlist: null, | ||
blocklist: null, | ||
safe: false, | ||
allowUndefined: true, | ||
verbose: false, | ||
...this.opts, | ||
} | ||
const dotenvTemporary = Object.assign({}, process.env) | ||
if (this.opts.safe) { | ||
const parsed = parseDotenvFile(this.opts.path, this.opts.verbose) | ||
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: 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) | ||
} | ||
}, | ||
visitor: { | ||
ImportDeclaration(path, {opts}) { | ||
if (path.node.source.value === opts.moduleName) { | ||
for (const [idx, specifier] of path.node.specifiers.entries()) { | ||
if (specifier.type === 'ImportDefaultSpecifier') { | ||
throw path.get('specifiers')[idx].buildCodeFrameError('Default import is not supported') | ||
} | ||
if (!opts.allowUndefined && !Object.prototype.hasOwnProperty.call(this.env, importedId)) { | ||
throw path.get('specifiers')[idx].buildCodeFrameError(`"${importedId}" is not defined in ${opts.path}`) | ||
if (specifier.type === 'ImportNamespaceSpecifier') { | ||
throw path.get('specifiers')[idx].buildCodeFrameError('Wildcard import is not supported') | ||
} | ||
const binding = path.scope.getBinding(localId) | ||
for (const refPath of binding.referencePaths) { | ||
refPath.replaceWith(t.valueToNode(this.env[importedId])) | ||
if (specifier.imported && specifier.local) { | ||
const importedId = specifier.imported.name | ||
const localId = specifier.local.name | ||
if (Array.isArray(opts.allowlist) && !opts.allowlist.includes(importedId)) { | ||
throw path.get('specifiers')[idx].buildCodeFrameError(`"${importedId}" was not present in allowlist`) | ||
} else if (Array.isArray(opts.whitelist) && !opts.whitelist.includes(importedId)) { | ||
console.warn('[DEPRECATION WARNING] This option is will be deprecated soon. Use allowlist instead') | ||
throw path.get('specifiers')[idx].buildCodeFrameError(`"${importedId}" was not whitelisted`) | ||
} | ||
if (Array.isArray(opts.blocklist) && opts.blocklist.includes(importedId)) { | ||
throw path.get('specifiers')[idx].buildCodeFrameError(`"${importedId}" was not present in blocklist`) | ||
} else if (Array.isArray(opts.blacklist) && opts.blacklist.includes(importedId)) { | ||
console.warn('[DEPRECATION WARNING] This option is will be deprecated soon. Use blocklist instead') | ||
throw path.get('specifiers')[idx].buildCodeFrameError(`"${importedId}" was blacklisted`) | ||
} | ||
if (!opts.allowUndefined && !Object.prototype.hasOwnProperty.call(this.env, importedId)) { | ||
throw path.get('specifiers')[idx].buildCodeFrameError(`"${importedId}" is not defined in ${opts.path}`) | ||
} | ||
const binding = path.scope.getBinding(localId) | ||
for (const refPath of binding.referencePaths) { | ||
refPath.replaceWith(t.valueToNode(this.env[importedId])) | ||
} | ||
} | ||
} | ||
path.remove() | ||
} | ||
}, | ||
MemberExpression(path, {opts}) { | ||
if (path.get('object').matchesPattern('process.env')) { | ||
const key = path.toComputedKey() | ||
if (t.isStringLiteral(key)) { | ||
const importedId = key.value | ||
const value = (opts.env && importedId in opts.env) ? opts.env[importedId] : process.env[importedId] | ||
path.remove() | ||
} | ||
}, | ||
MemberExpression(path, {opts}) { | ||
if (path.get('object').matchesPattern('process.env')) { | ||
const key = path.toComputedKey() | ||
if (t.isStringLiteral(key)) { | ||
const importedId = key.value | ||
const value = (opts.env && importedId in opts.env) ? opts.env[importedId] : process.env[importedId] | ||
path.replaceWith(t.valueToNode(value)) | ||
path.replaceWith(t.valueToNode(value)) | ||
} | ||
} | ||
} | ||
}, | ||
}, | ||
}, | ||
}) | ||
}) | ||
} |
{ | ||
"name": "react-native-dotenv", | ||
"version": "3.3.1", | ||
"version": "3.4.0", | ||
"description": "Load environment variables using import statements.", | ||
@@ -26,10 +26,10 @@ "repository": "github:goatandsheep/react-native-dotenv", | ||
"dependencies": { | ||
"dotenv": "^10.0.0" | ||
"dotenv": "^16.0.0" | ||
}, | ||
"devDependencies": { | ||
"@babel/core": "7.16.0", | ||
"@babel/core": "7.17.7", | ||
"codecov": "^3.8.3", | ||
"jest": "27.4.4", | ||
"jest": "27.5.1", | ||
"jest-junit": "^13.0.0", | ||
"xo": "^0.47.0" | ||
"xo": "^0.48.0" | ||
}, | ||
@@ -36,0 +36,0 @@ "author": "Kemal Ahmed", |
@@ -6,3 +6,3 @@ # react-native-dotenv [![CircleCI](https://circleci.com/gh/goatandsheep/react-native-dotenv.svg?style=svg)](https://circleci.com/gh/goatandsheep/react-native-dotenv) | ||
[![npm version](https://badgen.net/npm/v/react-native-dotenv)](https://www.npmjs.com/package/react-native-dotenv) | ||
[![dependencies Status](https://badgen.net/david/dep/goatandsheep/react-native-dotenv)](https://david-dm.org/goatandsheep/react-native-dotenv) | ||
[![dependencies Status](https://img.shields.io/librariesio/release/npm/react-native-dotenv)](https://img.shields.io/librariesio/release/npm/react-native-dotenv) | ||
[![codecov](https://badgen.net/codecov/c/github/goatandsheep/react-native-dotenv)](https://codecov.io/gh/goatandsheep/react-native-dotenv) | ||
@@ -295,2 +295,6 @@ [![XO code style](https://badgen.net/badge/code%20style/XO/cyan)](https://github.com/xojs/xo) [![Join the chat at https://gitter.im/pass-it-on/react-native-dotenv](https://badges.gitter.im/pass-it-on/react-native-dotenv.svg)](https://gitter.im/pass-it-on/react-native-dotenv?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
and | ||
`expo start --clear` | ||
or | ||
@@ -300,2 +304,6 @@ | ||
You can also set `api.cache(false)` in your babel config | ||
metro.config.js`resetCache: true` | ||
Maybe a solution for updating package.json scripts: | ||
@@ -302,0 +310,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
49979
96
385
336
23
+ Addeddotenv@16.4.5(transitive)
- Removeddotenv@10.0.0(transitive)
Updateddotenv@^16.0.0