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,15 @@ 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
+ const std::regex LINE (
18
+ " \\ s*(?:export\\ s+)?([\\ w.-]+)(?:\\ s*=\\ s*?|:\\ s+?)(\\ s*'(?:\\\\ '|[^']"
19
+ " )*'|\\ s*\" (?:\\\\\" |[^\" ])*\" |\\ s*`(?:\\\\ `|[^`])*`|[^#\r\n ]+)?\\ s*(?"
20
+ " :#.*)?" ); // NOLINT(whitespace/line_length)
21
+
11
22
std::vector<std::string> Dotenv::GetPathFromArgs (
12
23
const std::vector<std::string>& args) {
13
24
const auto find_match = [](const std::string& arg) {
@@ -81,7 +92,7 @@ bool Dotenv::ParsePath(const std::string_view path) {
81
92
uv_fs_req_cleanup (&close_req);
82
93
});
83
94
84
- std::string result {};
95
+ std::string lines {};
85
96
char buffer[8192 ];
86
97
uv_buf_t buf = uv_buf_init (buffer, sizeof (buffer));
87
98
@@ -95,15 +106,29 @@ bool Dotenv::ParsePath(const std::string_view path) {
95
106
if (r <= 0 ) {
96
107
break ;
97
108
}
98
- result .append (buf.base , r);
109
+ lines .append (buf.base , r);
99
110
}
100
111
101
- using std::string_view_literals::operator " " sv;
102
- auto lines = SplitString (result, " \n " sv);
112
+ std::smatch match;
113
+ while (std::regex_search (lines, match, LINE)) {
114
+ const std::string key = match[1 ].str ();
115
+
116
+ // Default undefined or null to an empty string
117
+ std::string value = match[2 ].str ();
118
+
119
+ // Remove leading whitespaces
120
+ value.erase (0 , value.find_first_not_of (" \t " ));
121
+
122
+ // Remove trailing whitespaces
123
+ value.erase (value.find_last_not_of (" \t " ) + 1 );
103
124
104
- for (const auto & line : lines) {
105
- ParseLine (line);
125
+ // Remove surrounding quotes
126
+ value = trim_quotes (value);
127
+
128
+ store_.insert_or_assign (std::string (key), value);
129
+ lines = match.suffix ();
106
130
}
131
+
107
132
return true ;
108
133
}
109
134
@@ -115,56 +140,12 @@ void Dotenv::AssignNodeOptionsIfAvailable(std::string* node_options) {
115
140
}
116
141
}
117
142
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);
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 ;
143
+ std::string Dotenv::trim_quotes (std::string str) {
144
+ static const std::unordered_set<char > quotes = {' "' , ' \' ' , ' `' };
145
+ if (str.size () >= 2 && quotes.count (str[0 ]) && quotes.count (str.back ())) {
146
+ str = str.substr (1 , str.size () - 2 );
134
147
}
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);
148
+ return str;
168
149
}
169
150
170
151
} // namespace node
0 commit comments