UNPKG

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