1
1
#include " node_dotenv.h"
2
+ #include < regex> // NOLINT(build/c++11)
2
3
#include " env-inl.h"
3
4
#include " node_file.h"
4
5
#include " uv.h"
@@ -8,6 +9,17 @@ namespace node {
8
9
using v8::NewStringType;
9
10
using v8::String;
10
11
12
+ /* *
13
+ * The inspiration for this implementation comes from the original dotenv code,
14
+ * available at https://github.com/motdotla/dotenv/blob/master/lib/main.js#L12
15
+ */
16
+
17
+ std::regex LINE (
18
+ " (?:^|^)\\ s*(?:export\\ s+)?([\\ w.-]+)(?:\\ s*=\\ s*?|:\\ s+?)(\\ s*'(?:\\\\ '|"
19
+ " [^'])*'|\\ s*\" (?:\\\\\" |[^\" ])*\" |\\ s*`(?:\\\\ `|[^`])*`|[^#\\ r\\ n]+)?"
20
+ " \\ s*(?:#.*)?(?:$|$)" ,
21
+ std::regex::multiline);
22
+
11
23
std::vector<std::string> Dotenv::GetPathFromArgs (
12
24
const std::vector<std::string>& args) {
13
25
const auto find_match = [](const std::string& arg) {
@@ -98,73 +110,46 @@ bool Dotenv::ParsePath(const std::string_view path) {
98
110
result.append (buf.base , r);
99
111
}
100
112
101
- using std::string_view_literals::operator " " sv;
102
- auto lines = SplitString (result, " \n " sv);
113
+ // Convert line breaks to the same format
114
+ std::string lines = result;
115
+ std::regex_replace (lines, std::regex (" \r\n ?" ), " \n " );
103
116
104
- for (const auto & line : lines) {
105
- ParseLine (line);
106
- }
107
- return true ;
108
- }
117
+ std::smatch match;
118
+ while (std::regex_search (lines, match, LINE)) {
119
+ const std::string key = match[1 ].str ();
109
120
110
- void Dotenv::AssignNodeOptionsIfAvailable (std::string* node_options) {
111
- auto match = store_. find ( " NODE_OPTIONS " );
121
+ // Default undefined or null to an empty string
122
+ std::string value = match[ 2 ]. str ( );
112
123
113
- if (match != store_.end ()) {
114
- *node_options = match->second ;
115
- }
116
- }
124
+ // Remove whitespace
125
+ value = std::regex_replace (value, std::regex (" ^\\ s+|\\ s+$" ), " " );
117
126
118
- void Dotenv::ParseLine ( const std::string_view line) {
119
- auto equal_index = line. find ( ' = ' ) ;
127
+ // Check if double-quoted
128
+ const char maybeQuote = value[ 0 ] ;
120
129
121
- if (equal_index == std::string_view::npos) {
122
- return ;
123
- }
124
-
125
- auto key = line.substr (0 , equal_index);
130
+ // Remove surrounding quotes
131
+ value =
132
+ std::regex_replace (value, std::regex (" ^(['\" `])([\\ s\\ S]*)\\ 1$" ), " $2" );
126
133
127
- // Remove leading and trailing space characters from key.
128
- while (!key.empty () && std::isspace (key.front ())) key.remove_prefix (1 );
129
- while (!key.empty () && std::isspace (key.back ())) key.remove_suffix (1 );
134
+ // Expand newlines if double quoted
135
+ if (maybeQuote == ' "' ) {
136
+ value = std::regex_replace (value, std::regex (" \\\\ n" ), " \n " );
137
+ value = std::regex_replace (value, std::regex (" \\\\ r" ), " \r " );
138
+ }
130
139
131
- // Omit lines with comments
132
- if (key.front () == ' #' || key.empty ()) {
133
- return ;
140
+ store_.insert_or_assign (std::string (key), value);
141
+ lines = match.suffix ();
134
142
}
135
143
136
- auto value = std::string (line.substr (equal_index + 1 ));
137
-
138
- // Might start and end with `"' characters.
139
- auto quotation_index = value.find_first_of (" `\" '" );
140
-
141
- if (quotation_index == 0 ) {
142
- auto quote_character = value[quotation_index];
143
- value.erase (0 , 1 );
144
-
145
- auto end_quotation_index = value.find_last_of (quote_character);
146
-
147
- // We couldn't find the closing quotation character. Terminate.
148
- if (end_quotation_index == std::string::npos) {
149
- return ;
150
- }
151
-
152
- value.erase (end_quotation_index);
153
- } else {
154
- auto hash_index = value.find (' #' );
144
+ return true ;
145
+ }
155
146
156
- // Remove any inline comments
157
- if (hash_index != std::string::npos) {
158
- value.erase (hash_index);
159
- }
147
+ void Dotenv::AssignNodeOptionsIfAvailable (std::string* node_options) {
148
+ auto match = store_.find (" NODE_OPTIONS" );
160
149
161
- // Remove any leading/trailing spaces from unquoted values.
162
- while (!value.empty () && std::isspace (value.front ())) value.erase (0 , 1 );
163
- while (!value.empty () && std::isspace (value.back ()))
164
- value.erase (value.size () - 1 );
150
+ if (match != store_.end ()) {
151
+ *node_options = match->second ;
165
152
}
166
-
167
- store_.insert_or_assign (std::string (key), value);
168
153
}
169
154
170
155
} // namespace node
0 commit comments