绿色应用市场
当前位置:首页 > 学习教程 > 正文

dotenv源码解读从.env文件中读取环境变量

发布时间:2024-01-23 19:59:26来源:周小白软件园编辑:本站整理

引言

dotenv.env文件中读取环境变量,然后将其添加到process.env中。这是一个非常简单的库,但是它在开发中非常有用,因为它允许你在.env文件中存储敏感信息,而不是将其存储在代码中。

现在很多库都支持.env文件,例如create-react-appvue-clinext.js等。

源码地址:github.com/motdotla/do…

使用

根据READMEdotenv只有两个方法:

config:读取.env文件并将其添加到process.env中。parse:解析一段包含环境变量的字符串或Buffer,并返回一个对象。
const dotenv = require('dotenv') // 读取.env文件并将其添加到process.env中 dotenv.config() // 解析一段包含环境变量的字符串或Buffer,返回一个对象 const config1 = dotenv.parse('FOO=bar\nBAR=foo') console.log(config1) // { FOO: 'bar', BAR: 'foo' } const buffer = Buffer.from('FOO=bar\nBAR=foo') const config2 = dotenv.parse(buffer) console.log(config2) // { FOO: 'bar', BAR: 'foo' } 

可以看到,dotenv的使用非常简单,通常我们只需要调用config方法即可。

还有一种方法是预加载,直接通过node -r dotenv/config来运行脚本,这样就不需要在脚本中引入dotenv了。

源码

源码在lib/main.js中,先来看一下全部的代码:

const fs = require('fs') const path = require('path') const os = require('os') const packageJson = require('../package.json') const version = packageJson.version const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\'|[^'])*'|\s*"(?:\"|[^"])*"|\s*`(?:\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg // Parser src into an Object function parse (src) {   const obj = {}   // Convert buffer to string   let lines = src.toString()   // Convert line breaks to same format   lines = lines.replace(/\r\n?/mg, '\n')   let match   while ((match = LINE.exec(lines)) != null) { const key = match[1] // Default undefined or null to empty string let value = (match[2] || '') // Remove whitespace value = value.trim() // Check if double quoted const maybeQuote = value[0] // Remove surrounding quotes value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2') // Expand newlines if double quoted if (maybeQuote === '"') {   value = value.replace(/\n/g, '\n')   value = value.replace(/\r/g, '\r') } // Add to object obj[key] = value   }   return obj } function _log (message) {   console.log(`[dotenv@${version}][DEBUG] ${message}`) } function _resolveHome (envPath) {   return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath } // Populates process.env from .env file function config (options) {   let dotenvPath = path.resolve(process.cwd(), '.env')   let encoding = 'utf8'   const debug = Boolean(options && options.debug)   const override = Boolean(options && options.override)   if (options) { if (options.path != null) {   dotenvPath = _resolveHome(options.path) } if (options.encoding != null) {   encoding = options.encoding }   }   try { // Specifying an encoding returns a string instead of a buffer const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding })) Object.keys(parsed).forEach(function (key) {   if (!Object.prototype.hasOwnProperty.call(process.env, key)) { process.env[key] = parsed[key]   } else { if (override === true) {   process.env[key] = parsed[key] } if (debug) {   if (override === true) { _log(`"${key}" is already defined in `process.env` and WAS overwritten`)   } else { _log(`"${key}" is already defined in `process.env` and was NOT overwritten`)   } }   } }) return { parsed }   } catch (e) { if (debug) {   _log(`Failed to load ${dotenvPath} ${e.message}`) } return { error: e }   } } const DotenvModule = {   config,   parse } module.exports.config = DotenvModule.config module.exports.parse = DotenvModule.parse module.exports = DotenvModule 

可以看到最后导出的是一个对象,包含了configparse两个方法。

config

config方法的作用是读取.env文件,并将其添加到process.env中。

function config (options) {   let dotenvPath = path.resolve(process.cwd(), '.env')   let encoding = 'utf8'   const debug = Boolean(options && options.debug)   const override = Boolean(options && options.override) } 

首先定义了一些变量:

dotenvPath.env文件的路径encoding是文件的编码debugoverride分别表示是否开启调试模式和是否覆盖已有的环境变量。
if (options) {   if (options.path != null) { dotenvPath = _resolveHome(options.path)   }   if (options.encoding != null) { encoding = options.encoding   } } 

然后判断了一下options是否存在,如果存在的话,就会根据options的值来修改dotenvPathencoding的值。

const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding })) 

然后是调用parse方法来解析.env文件,parse方法的实现在下面会讲到。

这里是只用fs.readFileSync来读取.env文件,然后将其传入parse方法中,接着往下:

Object.keys(parsed).forEach(function (key) { if (!Object.prototype.hasOwnProperty.call(process.env, key)) { process.env[key] = parsed[key] } else { if (override === true) { process.env[key] = parsed[key] } if (debug) { if (override === true) { _log(`"${key}" is already defined in `process.env` and WAS overwritten`) } else { _log(`"${key}" is already defined in `process.env` and was NOT overwritten`) } } } }) 

这里是遍历parsed对象,然后将其添加到process.env中,如果process.env中已经存在了该环境变量,那么就会根据override的值来决定是否覆盖。

debug的值表示是否开启调试模式,如果开启了调试模式,那么就会打印一些日志。

最后就是直接返回parsed对象。

parse

parse方法的作用是解析.env文件,将其转换为一个对象。

const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\'|[^'])*'|\s*"(?:\"|[^"])*"|\s*`(?:\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg function parse (src) {   const obj = {}   // Convert buffer to string   let lines = src.toString()   // Convert line breaks to same format   lines = lines.replace(/\r\n?/mg, '\n')   let match   while ((match = LINE.exec(lines)) != null) { const key = match[1] // Default undefined or null to empty string let value = (match[2] || '') // Remove whitespace value = value.trim() // Check if double quoted const maybeQuote = value[0] // Remove surrounding quotes value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2') // Expand newlines if double quoted if (maybeQuote === '"') {   value = value.replace(/\n/g, '\n')   value = value.replace(/\r/g, '\r') } // Add to object obj[key] = value   }   return obj } 

首先定义了一个正则表达式LINE,用来匹配.env文件中的每一行。

然后是将src转换为字符串,然后将换行符统一为\n

接着就是核心,通过正则表达式的特性通过while循环来匹配每一行。

这个正则着实有点复杂,我是正则渣渣,可以在regex101查看一下。

这个正则上面标出了三种颜色,和下面的匹配的值的颜色相互对应,然后右边会展示匹配的值。

这里我不过多解读,可以自己去看一下,然后输入不同的值对比一下结果。

通过上面的截图可以看到匹配会捕获两个值,第一个是环境变量的名称,第二个是环境变量的值。

然后对值进行处理,首先去掉首尾的空格,然后通过正则去掉首尾的引号,最后再将转义的换行符转换还原。

经过上面的处理,就可以将每一行的环境变量添加到obj对象中了,最后返回obj对象。

总结

dotenv真的是非常惊艳的一个库,没有任何依赖,只有一个文件,而且功能也非常强大。

如果你将README中的内容全部看完,你还会发现dotenv还有很多其他的功能,都是一些很实用的功能,并且还有很多引导你如何使用的例子。

以上就是dotenv源码解读从.env文件中读取环境变量的详细内容,更多关于dotenv .env文件读取环境变量的资料请关注云初冀北其它相关文章!

相关推荐