You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
2.6 KiB

4 years ago
  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. function log (message /*: string */) {
  21. console.log(`[dotenv][DEBUG] ${message}`)
  22. }
  23. // Parses src into an Object
  24. function parse (src /*: string | Buffer */, options /*: ?DotenvParseOptions */) /*: DotenvParseOutput */ {
  25. const debug = Boolean(options && options.debug)
  26. const obj = {}
  27. // convert Buffers before splitting into lines and processing
  28. src.toString().split('\n').forEach(function (line, idx) {
  29. // matching "KEY' and 'VAL' in 'KEY=VAL'
  30. const keyValueArr = line.match(/^\s*([\w.-]+)\s*=\s*(.*)?\s*$/)
  31. // matched?
  32. if (keyValueArr != null) {
  33. const key = keyValueArr[1]
  34. // default undefined or missing values to empty string
  35. let value = keyValueArr[2] || ''
  36. // expand newlines in quoted values
  37. const len = value ? value.length : 0
  38. if (len > 0 && value.charAt(0) === '"' && value.charAt(len - 1) === '"') {
  39. value = value.replace(/\\n/gm, '\n')
  40. }
  41. // remove any surrounding quotes and extra spaces
  42. value = value.replace(/(^['"]|['"]$)/g, '').trim()
  43. obj[key] = value
  44. } else if (debug) {
  45. log(`did not match key and value when parsing line ${idx + 1}: ${line}`)
  46. }
  47. })
  48. return obj
  49. }
  50. // Populates process.env from .env file
  51. function config (options /*: ?DotenvConfigOptions */) /*: DotenvConfigOutput */ {
  52. let dotenvPath = path.resolve(process.cwd(), '.env')
  53. let encoding /*: string */ = 'utf8'
  54. let debug = false
  55. if (options) {
  56. if (options.path != null) {
  57. dotenvPath = options.path
  58. }
  59. if (options.encoding != null) {
  60. encoding = options.encoding
  61. }
  62. if (options.debug != null) {
  63. debug = true
  64. }
  65. }
  66. try {
  67. // specifying an encoding returns a string instead of a buffer
  68. const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug })
  69. Object.keys(parsed).forEach(function (key) {
  70. if (!process.env.hasOwnProperty(key)) {
  71. process.env[key] = parsed[key]
  72. } else if (debug) {
  73. log(`"${key}" is already defined in \`process.env\` and will not be overwritten`)
  74. }
  75. })
  76. return { parsed }
  77. } catch (e) {
  78. return { error: e }
  79. }
  80. }
  81. module.exports.config = config
  82. module.exports.load = config
  83. module.exports.parse = parse