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,12 @@ namespace node {
8
9
using v8::NewStringType;
9
10
using v8::String;
10
11
12
+ std::regex LINE (
13
+ " (?:^|^)\\ s*(?:export\\ s+)?([\\ w.-]+)(?:\\ s*=\\ s*?|:\\ s+?)(\\ s*'(?:\\\\ '|"
14
+ " [^'])*'|\\ s*\" (?:\\\\\" |[^\" ])*\" |\\ s*`(?:\\\\ `|[^`])*`|[^#\\ r\\ n]+)?"
15
+ " \\ s*(?:#.*)?(?:$|$)" ,
16
+ std::regex::multiline);
17
+
11
18
std::vector<std::string> Dotenv::GetPathFromArgs (
12
19
const std::vector<std::string>& args) {
13
20
const auto find_match = [](const std::string& arg) {
@@ -98,12 +105,7 @@ bool Dotenv::ParsePath(const std::string_view path) {
98
105
result.append (buf.base , r);
99
106
}
100
107
101
- using std::string_view_literals::operator " " sv;
102
- auto lines = SplitString (result, " \n " sv);
103
-
104
- for (const auto & line : lines) {
105
- ParseLine (line);
106
- }
108
+ Parse (result);
107
109
return true ;
108
110
}
109
111
@@ -115,56 +117,37 @@ void Dotenv::AssignNodeOptionsIfAvailable(std::string* node_options) {
115
117
}
116
118
}
117
119
118
- void Dotenv::ParseLine (const std::string_view line) {
119
- auto equal_index = line.find (' =' );
120
-
121
- if (equal_index == std::string_view::npos) {
122
- return ;
123
- }
124
-
125
- auto key = line.substr (0 , equal_index);
120
+ void Dotenv::Parse (const std::string& src) {
121
+ // Convert line breaks to the same format
122
+ std::string lines = src;
123
+ std::regex_replace (lines, std::regex (" \r\n ?" ), " \n " );
126
124
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 );
125
+ std::smatch match;
126
+ while (std::regex_search (lines, match, LINE)) {
127
+ const std::string key = match[ 1 ]. str ( );
130
128
131
- // Omit lines with comments
132
- if (key.front () == ' #' || key.empty ()) {
133
- return ;
134
- }
129
+ // Default undefined or null to an empty string
130
+ std::string value = match[2 ].str ();
135
131
136
- auto value = std::string (line.substr (equal_index + 1 ));
132
+ // Remove whitespace
133
+ value = std::regex_replace (value, std::regex (" ^\\ s+|\\ s+$" ), " " );
137
134
138
- // Might start and end with `"' characters.
139
- auto quotation_index = value. find_first_of ( " ` \" ' " ) ;
135
+ // Check if double-quoted
136
+ const char maybeQuote = value[ 0 ] ;
140
137
141
- if (quotation_index == 0 ) {
142
- auto quote_character = value[quotation_index];
143
- value. erase ( 0 , 1 );
138
+ // Remove surrounding quotes
139
+ value =
140
+ std::regex_replace (value, std::regex ( " ^([' \" `])([ \\ s \\ S]*) \\ 1$ " ), " $2 " );
144
141
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 ;
142
+ // Expand newlines if double quoted
143
+ if (maybeQuote == ' "' ) {
144
+ value = std::regex_replace (value, std::regex (" \\\\ n" ), " \n " );
145
+ value = std::regex_replace (value, std::regex (" \\\\ r" ), " \r " );
150
146
}
151
147
152
- value.erase (end_quotation_index);
153
- } else {
154
- auto hash_index = value.find (' #' );
155
-
156
- // Remove any inline comments
157
- if (hash_index != std::string::npos) {
158
- value.erase (hash_index);
159
- }
160
-
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 );
148
+ store_.insert_or_assign (std::string (key), value);
149
+ lines = match.suffix ();
165
150
}
166
-
167
- store_.insert_or_assign (std::string (key), value);
168
151
}
169
152
170
153
} // namespace node
0 commit comments