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"
@@ -8,6 +10,17 @@ namespace node {
8
10
using v8::NewStringType;
9
11
using v8::String;
10
12
13
+ /* *
14
+ * The inspiration for this implementation comes from the original dotenv code,
15
+ * available at https://github.com/motdotla/dotenv
16
+ */
17
+
18
+ std::regex LINE (
19
+ " (?:^|^)\\ s*(?:export\\ s+)?([\\ w.-]+)(?:\\ s*=\\ s*?|:\\ s+?)(\\ s*'(?:\\\\ '|"
20
+ " [^'])*'|\\ s*\" (?:\\\\\" |[^\" ])*\" |\\ s*`(?:\\\\ `|[^`])*`|[^#\\ r\\ n]+)?"
21
+ " \\ s*(?:#.*)?(?:$|$)" ,
22
+ std::regex_constants::multiline);
23
+
11
24
std::vector<std::string> Dotenv::GetPathFromArgs (
12
25
const std::vector<std::string>& args) {
13
26
const auto find_match = [](const std::string& arg) {
@@ -81,7 +94,7 @@ bool Dotenv::ParsePath(const std::string_view path) {
81
94
uv_fs_req_cleanup (&close_req);
82
95
});
83
96
84
- std::string result {};
97
+ std::string lines {};
85
98
char buffer[8192 ];
86
99
uv_buf_t buf = uv_buf_init (buffer, sizeof (buffer));
87
100
@@ -95,15 +108,32 @@ bool Dotenv::ParsePath(const std::string_view path) {
95
108
if (r <= 0 ) {
96
109
break ;
97
110
}
98
- result .append (buf.base , r);
111
+ lines .append (buf.base , r);
99
112
}
100
113
101
- using std::string_view_literals::operator " " sv;
102
- auto lines = SplitString (result, " \n " sv);
114
+ // Convert line breaks to the same format
115
+ std::regex_replace (lines, std::regex (" \r\n ?" ), " \n " );
116
+
117
+ std::smatch match;
118
+ while (std::regex_search (lines, match, LINE)) {
119
+ const std::string key = match[1 ].str ();
120
+
121
+ // Default undefined or null to an empty string
122
+ std::string value = match[2 ].str ();
123
+
124
+ // Remove leading whitespaces
125
+ value.erase (0 , value.find_first_not_of (" \t " ));
126
+
127
+ // Remove trailing whitespaces
128
+ value.erase (value.find_last_not_of (" \t " ) + 1 );
129
+
130
+ // Remove surrounding quotes
131
+ value = trim_quotes (value);
103
132
104
- for ( const auto & line : lines) {
105
- ParseLine (line );
133
+ store_. insert_or_assign ( std::string (key), value);
134
+ lines = match. suffix ( );
106
135
}
136
+
107
137
return true ;
108
138
}
109
139
@@ -115,56 +145,12 @@ void Dotenv::AssignNodeOptionsIfAvailable(std::string* node_options) {
115
145
}
116
146
}
117
147
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 ;
148
+ std::string Dotenv::trim_quotes (std::string str) {
149
+ static const std::unordered_set<char > quotes = {' "' , ' \' ' , ' `' };
150
+ if (str.size () >= 2 && quotes.count (str[0 ]) && quotes.count (str.back ())) {
151
+ str = str.substr (1 , str.size () - 2 );
123
152
}
124
-
125
- auto key = line.substr (0 , equal_index);
126
-
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 );
130
-
131
- // Omit lines with comments
132
- if (key.front () == ' #' || key.empty ()) {
133
- return ;
134
- }
135
-
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 (' #' );
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 );
165
- }
166
-
167
- store_.insert_or_assign (std::string (key), value);
153
+ return str;
168
154
}
169
155
170
156
} // namespace node
0 commit comments