1
1
#include " node_dotenv.h"
2
+ #include < regex> // NOLINT(build/c++11)
3
+ #include < unordered_set>
2
4
#include " env-inl.h"
3
5
#include " node_file.h"
4
6
#include " uv.h"
@@ -10,6 +12,15 @@ using v8::NewStringType;
10
12
using v8::Object;
11
13
using v8::String;
12
14
15
+ /* *
16
+ * The inspiration for this implementation comes from the original dotenv code,
17
+ * available at https://github.com/motdotla/dotenv
18
+ */
19
+ const std::regex LINE (
20
+ " \\ s*(?:export\\ s+)?([\\ w.-]+)(?:\\ s*=\\ s*?|:\\ s+?)(\\ s*'(?:\\\\ '|[^']"
21
+ " )*'|\\ s*\" (?:\\\\\" |[^\" ])*\" |\\ s*`(?:\\\\ `|[^`])*`|[^#\r\n ]+)?\\ s*(?"
22
+ " :#.*)?" ); // NOLINT(whitespace/line_length)
23
+
13
24
std::vector<std::string> Dotenv::GetPathFromArgs (
14
25
const std::vector<std::string>& args) {
15
26
const auto find_match = [](const std::string& arg) {
@@ -91,11 +102,34 @@ Local<Object> Dotenv::ToObject(Environment* env) {
91
102
}
92
103
93
104
void Dotenv::ParseContent (const std::string_view content) {
94
- using std::string_view_literals::operator " " sv;
95
- auto lines = SplitString (content, " \n " sv);
105
+ std::string lines = std::string (content);
106
+ lines = std::regex_replace (lines, std::regex (" \r\n ?" ), " \n " );
107
+
108
+ std::smatch match;
109
+ while (std::regex_search (lines, match, LINE)) {
110
+ const std::string key = match[1 ].str ();
111
+
112
+ // Default undefined or null to an empty string
113
+ std::string value = match[2 ].str ();
114
+
115
+ // Remove leading whitespaces
116
+ value.erase (0 , value.find_first_not_of (" \t " ));
117
+
118
+ // Remove trailing whitespaces
119
+ value.erase (value.find_last_not_of (" \t " ) + 1 );
120
+
121
+ const char maybeQuote = value.front ();
122
+
123
+ if (maybeQuote == ' "' ) {
124
+ value = std::regex_replace (value, std::regex (" \\\\ n" ), " \n " );
125
+ value = std::regex_replace (value, std::regex (" \\\\ r" ), " \r " );
126
+ }
127
+
128
+ // Remove surrounding quotes
129
+ value = trim_quotes (value);
96
130
97
- for ( const auto & line : lines) {
98
- ParseLine (line );
131
+ store_. insert_or_assign ( std::string (key), value);
132
+ lines = match. suffix ( );
99
133
}
100
134
}
101
135
@@ -145,56 +179,13 @@ void Dotenv::AssignNodeOptionsIfAvailable(std::string* node_options) {
145
179
}
146
180
}
147
181
148
- void Dotenv::ParseLine (const std::string_view line) {
149
- auto equal_index = line.find (' =' );
150
-
151
- if (equal_index == std::string_view::npos) {
152
- return ;
153
- }
154
-
155
- auto key = line.substr (0 , equal_index);
156
-
157
- // Remove leading and trailing space characters from key.
158
- while (!key.empty () && std::isspace (key.front ())) key.remove_prefix (1 );
159
- while (!key.empty () && std::isspace (key.back ())) key.remove_suffix (1 );
160
-
161
- // Omit lines with comments
162
- if (key.front () == ' #' || key.empty ()) {
163
- return ;
164
- }
165
-
166
- auto value = std::string (line.substr (equal_index + 1 ));
167
-
168
- // Might start and end with `"' characters.
169
- auto quotation_index = value.find_first_of (" `\" '" );
170
-
171
- if (quotation_index == 0 ) {
172
- auto quote_character = value[quotation_index];
173
- value.erase (0 , 1 );
174
-
175
- auto end_quotation_index = value.find (quote_character);
176
-
177
- // We couldn't find the closing quotation character. Terminate.
178
- if (end_quotation_index == std::string::npos) {
179
- return ;
180
- }
181
-
182
- value.erase (end_quotation_index);
183
- } else {
184
- auto hash_index = value.find (' #' );
185
-
186
- // Remove any inline comments
187
- if (hash_index != std::string::npos) {
188
- value.erase (hash_index);
189
- }
190
-
191
- // Remove any leading/trailing spaces from unquoted values.
192
- while (!value.empty () && std::isspace (value.front ())) value.erase (0 , 1 );
193
- while (!value.empty () && std::isspace (value.back ()))
194
- value.erase (value.size () - 1 );
182
+ std::string_view Dotenv::trim_quotes (std::string_view str) {
183
+ static const std::unordered_set<char > quotes = {' "' , ' \' ' , ' `' };
184
+ if (str.size () >= 2 && quotes.count (str.front ()) &&
185
+ quotes.count (str.back ())) {
186
+ str = str.substr (1 , str.size () - 2 );
195
187
}
196
-
197
- store_.insert_or_assign (std::string (key), value);
188
+ return str;
198
189
}
199
190
200
191
} // namespace node
0 commit comments