项目中常用的.env文件原理源码解析

2022-12-27 16:10:12
目录
前言如何使用源码解析读取文件解析文件赋值操作总结

前言

dotenv>

学习目标:

    学会 dotenv 原理和实现学会使用 fs模块 获取文件并解析

    资源:

    源码地址:dotenv

    如何使用

    使用>dotenv 库,可以在应用程序中创建一个名为 .env 的文件,并在该文件中存储环境变量。然后,可以使用 dotenv 库将这些变量加载到 Node.js 应用程序中。

    例如,您可以在 .env 文件中存储以下内容:

    DB_HOST=localhost
    DB_USERNAME=user
    DB_PASSWORD=password
    

    使用以下代码将这些变量加载到应用程序中:

    require('dotenv').config();
    const dbHost = process.env.DB_HOST;
    const dbUsername = process.env.DB_USERNAME;
    const dbPassword = process.env.DB_PASSWORD;
    

    源码解析

    阅读源码之前,可以猜测>

      读取 .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)
        if (options) {
          if (options.path != null) {
            dotenvPath = _resolveHome(options.path)
          }
          if (options.encoding != null) {
            encoding = options.encoding
          }
        }
      }
      

      代码中定义了一个变量 dotenvPath,并将其赋值为使用 path.resolve 函数处理后的路径。

      path.resolve 函数会从右到左依次遍历参数,并返回一个绝对路径。函数的第一个参数是 process.cwd,它返回 Node.js 进程的当前工作目录。第二个参数是字符串 '.env',它表示要在当前工作目录中查找的文件名。

      之后会进行一些参数的判断,如果参数中有path这个变量,则使用_resolveHome函数处理:

      function _resolveHome (envPath) {
        return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath
      }
      

      os.homedir 函数返回当前用户的主目录路径。

      _resolveHome 函数可用于将以波浪号开头的路径解析为主目录的实际路径。例如,如果 envPath 等于 '~/documents/file.txt',则函数将返回 '/home/user/documents/file.txt'(在基于 Unix 的系统上)或 'C:\Users\user\documents\file.txt'(在 Windows 上)。

      解析文件

      // 使用 `fs.readFileSync` 函数以指定的编码方式从文件系统中读取文件内容
      const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding }))
      // 解析文件
      function parse (src) {
        const obj = {}
        // 转为string类型
        let lines = src.toString()
        // 将换行符转换为相同的格式
        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 来匹配字符串 lines 中的内容。

      const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg
      

      这个正则表达式的目的是匹配类似于环境变量的行。它可以匹配以下格式的行:

      VARNAME=value
      VARNAME: value
      export VARNAME=value
      export VARNAME: value
      

      最后会返回一个包含所有变量的对象。

      赋值操作

        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 }
        } 
      

      拿到解析后的对象,使用 Object.keys(parsed) 获取所有的键,然后使用forEach循环将所有的键添加到process.env 中。

      总结

      dotenv>

      以上就是项目中常用的 .env 文件原理源码解析的详细内容,更多关于.env 文件原理的资料请关注易采站长站其它相关文章!