11#include " node_dotenv.h"
2+ #include < regex> // NOLINT(build/c++11)
3+ #include < unordered_set>
24#include " env-inl.h"
35#include " node_file.h"
46#include " uv.h"
@@ -8,6 +10,17 @@ namespace node {
810using v8::NewStringType;
911using v8::String;
1012
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+
1124std::vector<std::string> Dotenv::GetPathFromArgs (
1225 const std::vector<std::string>& args) {
1326 const auto find_match = [](const std::string& arg) {
@@ -81,7 +94,7 @@ bool Dotenv::ParsePath(const std::string_view path) {
8194 uv_fs_req_cleanup (&close_req);
8295 });
8396
84- std::string result {};
97+ std::string lines {};
8598 char buffer[8192 ];
8699 uv_buf_t buf = uv_buf_init (buffer, sizeof (buffer));
87100
@@ -95,15 +108,32 @@ bool Dotenv::ParsePath(const std::string_view path) {
95108 if (r <= 0 ) {
96109 break ;
97110 }
98- result .append (buf.base , r);
111+ lines .append (buf.base , r);
99112 }
100113
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);
103132
104- for ( const auto & line : lines) {
105- ParseLine (line );
133+ store_. insert_or_assign ( std::string (key), value);
134+ lines = match. suffix ( );
106135 }
136+
107137 return true ;
108138}
109139
@@ -115,56 +145,12 @@ void Dotenv::AssignNodeOptionsIfAvailable(std::string* node_options) {
115145 }
116146}
117147
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 );
123152 }
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;
168154}
169155
170156} // namespace node
0 commit comments