main.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /* @flow */
  2. /*::
  3. type DotenvParseOptions = {
  4. debug?: boolean
  5. }
  6. // keys and values from src
  7. type DotenvParseOutput = { [string]: string }
  8. type DotenvConfigOptions = {
  9. path?: string, // path to .env file
  10. encoding?: string, // encoding of .env file
  11. debug?: string // turn on logging for debugging purposes
  12. }
  13. type DotenvConfigOutput = {
  14. parsed?: DotenvParseOutput,
  15. error?: Error
  16. }
  17. */
  18. const fs = require('fs')
  19. const path = require('path')
  20. const os = require('os')
  21. function log (message /*: string */) {
  22. console.log(`[dotenv][DEBUG] ${message}`)
  23. }
  24. const NEWLINE = '\n'
  25. const RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/
  26. const RE_NEWLINES = /\\n/g
  27. const NEWLINES_MATCH = /\r\n|\n|\r/
  28. // Parses src into an Object
  29. function parse (src /*: string | Buffer */, options /*: ?DotenvParseOptions */) /*: DotenvParseOutput */ {
  30. const debug = Boolean(options && options.debug)
  31. const obj = {}
  32. // convert Buffers before splitting into lines and processing
  33. src.toString().split(NEWLINES_MATCH).forEach(function (line, idx) {
  34. // matching "KEY' and 'VAL' in 'KEY=VAL'
  35. const keyValueArr = line.match(RE_INI_KEY_VAL)
  36. // matched?
  37. if (keyValueArr != null) {
  38. const key = keyValueArr[1]
  39. // default undefined or missing values to empty string
  40. let val = (keyValueArr[2] || '')
  41. const end = val.length - 1
  42. const isDoubleQuoted = val[0] === '"' && val[end] === '"'
  43. const isSingleQuoted = val[0] === "'" && val[end] === "'"
  44. // if single or double quoted, remove quotes
  45. if (isSingleQuoted || isDoubleQuoted) {
  46. val = val.substring(1, end)
  47. // if double quoted, expand newlines
  48. if (isDoubleQuoted) {
  49. val = val.replace(RE_NEWLINES, NEWLINE)
  50. }
  51. } else {
  52. // remove surrounding whitespace
  53. val = val.trim()
  54. }
  55. obj[key] = val
  56. } else if (debug) {
  57. log(`did not match key and value when parsing line ${idx + 1}: ${line}`)
  58. }
  59. })
  60. return obj
  61. }
  62. function resolveHome (envPath) {
  63. return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath
  64. }
  65. // Populates process.env from .env file
  66. function config (options /*: ?DotenvConfigOptions */) /*: DotenvConfigOutput */ {
  67. let dotenvPath = path.resolve(process.cwd(), '.env')
  68. let encoding /*: string */ = 'utf8'
  69. let debug = false
  70. if (options) {
  71. if (options.path != null) {
  72. dotenvPath = resolveHome(options.path)
  73. }
  74. if (options.encoding != null) {
  75. encoding = options.encoding
  76. }
  77. if (options.debug != null) {
  78. debug = true
  79. }
  80. }
  81. try {
  82. // specifying an encoding returns a string instead of a buffer
  83. const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug })
  84. Object.keys(parsed).forEach(function (key) {
  85. if (!Object.prototype.hasOwnProperty.call(process.env, key)) {
  86. process.env[key] = parsed[key]
  87. } else if (debug) {
  88. log(`"${key}" is already defined in \`process.env\` and will not be overwritten`)
  89. }
  90. })
  91. return { parsed }
  92. } catch (e) {
  93. return { error: e }
  94. }
  95. }
  96. module.exports.config = config
  97. module.exports.parse = parse