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"
@@ -10,6 +12,15 @@ using v8::NewStringType;
1012using v8::Object;
1113using v8::String;
1214
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+
1324std::vector<std::string> Dotenv::GetPathFromArgs (
1425 const std::vector<std::string>& args) {
1526 const auto find_match = [](const std::string& arg) {
@@ -91,11 +102,34 @@ Local<Object> Dotenv::ToObject(Environment* env) {
91102}
92103
93104void 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);
96130
97- for ( const auto & line : lines) {
98- ParseLine (line );
131+ store_. insert_or_assign ( std::string (key), value);
132+ lines = match. suffix ( );
99133 }
100134}
101135
@@ -145,56 +179,13 @@ void Dotenv::AssignNodeOptionsIfAvailable(std::string* node_options) {
145179 }
146180}
147181
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 );
195187 }
196-
197- store_.insert_or_assign (std::string (key), value);
188+ return str;
198189}
199190
200191} // namespace node
0 commit comments