-
-
Notifications
You must be signed in to change notification settings - Fork 46
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
Changes from 7 commits
1c45189
64cfaec
5625b8a
4bf0d04
2691b1f
ed277b9
3128b82
f476080
02bbd5d
13b9322
5bd695a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,110 +18,148 @@ function parseDotenvFile(path, verbose = false) { | |
return dotenv.parse(content) | ||
} | ||
|
||
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, | ||
} | ||
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' | ||
|
||
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) | ||
} | ||
if (options.verbose) { | ||
console.log('dotenvMode', babelMode) | ||
} | ||
|
||
api.cache.using(() => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The function passed to I suggest doing something like this: function mtime(filePath) {
try {
return fs.statSync(filePath).mtimeMs
} catch {
return null
}
}
api.cache.using(() => mtime(options.path))
api.cache.using(() => mtime(localFilePath))
api.cache.using(() => mtime(modeFilePath))
api.cache.using(() => mtime(modeLocalFilePath))
let 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)
env = Object.assign(Object.assign(Object.assign(parsed, modeParsed), localParsed), modeLocalParsed)
} else {
dotenv.config({
path: modeLocalFilePath,
silent: true,
})
dotenv.config({
path: modeFilePath,
silent: true,
})
dotenv.config({
path: localFilePath,
silent: true,
})
dotenv.config({
path: options.path,
})
env = process.env
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so when it is re-initialized, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so I found that this did not work in tests unfortunately. do I also need to use |
||
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) | ||
|
||
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', | ||
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 | ||
} | ||
}, | ||
|
||
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 (specifier.type === 'ImportNamespaceSpecifier') { | ||
throw path.get('specifiers')[idx].buildCodeFrameError('Wildcard import is not supported') | ||
} | ||
|
||
if (specifier.imported && specifier.local) { | ||
const importedId = specifier.imported.name | ||
const localId = specifier.local.name | ||
}) | ||
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, | ||
} | ||
|
||
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 (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 = Object.assign(Object.assign(Object.assign(parsed, modeParsed), localParsed), modeLocalParsed) | ||
this.env.NODE_ENV = process.env.NODE_ENV || babelMode | ||
} else { | ||
this.env = process.env | ||
} | ||
}, | ||
|
||
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`) | ||
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.replaceWith(t.valueToNode(value)) | ||
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)) | ||
} | ||
} | ||
}, | ||
}, | ||
}, | ||
}) | ||
}) | ||
} |
There was a problem hiding this comment.
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.There was a problem hiding this comment.
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
!There was a problem hiding this comment.
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