Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
cleanup & add comments
  • Loading branch information
marekpiechut committed Aug 5, 2024
commit 1456714fc651b543d9b6e89fb63183fcf5345b7c
53 changes: 40 additions & 13 deletions src/node_dotenv.cc
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,25 @@ Local<Object> Dotenv::ToObject(Environment* env) const {
return result;
}

/**
* Remove trailing and leading quotes from a string
* if they match.
*/
std::string_view trim_quotes(std::string_view input) {
if (input.empty()) return "";
auto first = input.front();
if ((first == '\'' || first == '"' || first == '`') &&
input.back() == first) {
input = input.substr(1, input.size() - 2);
input.remove_prefix(1);
input.remove_suffix(1);
}

return input;
}

/**
* Remove leading and trailing spaces from a string.
*/
std::string_view trim_spaces(std::string_view input) {
if (input.empty()) return "";
if (input.front() == ' ') {
Expand All @@ -109,6 +117,10 @@ std::string_view trim_spaces(std::string_view input) {
return input;
}

/**
* Trim key from .env file, remove leading "export" if found,
* like it is ignored in dotenv.
*/
std::string_view parse_key(std::string_view key) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment to what this function does?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

key = trim_spaces(key);
if (key.empty()) return key;
Expand All @@ -119,23 +131,30 @@ std::string_view parse_key(std::string_view key) {
return key;
}

/**
* Parse single value from .env file.
* Remove leading and trailing spaces and quotes.
* Leave empty space inside the quotes as is.
* Expand \n to newline only in double-quote strings.
* (like dotenv)
*/
std::string parse_value(std::string_view value) {
value = trim_spaces(value);
if (value.empty()) return "";

auto trimmed = trim_quotes(value);
if (value.front() == '\"' && value.back() == '\"') {
// Expand \n to newline in double-quote strings
size_t pos = 0;
auto expanded = std::string(trimmed);
while ((pos = expanded.find("\\n", pos)) != std::string_view::npos) {
expanded.replace(pos, 2, "\n");
pos += 1;
}
return expanded;
} else {
if (value.front() != '\"' || value.back() != '\"') {
return std::string(trimmed);
}

// Expand \n to newline in double-quote strings
size_t pos = 0;
auto expanded = std::string(trimmed);
while ((pos = expanded.find("\\n", pos)) != std::string::npos) {
expanded.replace(pos, 2, "\n");
pos += 1;
}
return expanded;
}

/**
Expand Down Expand Up @@ -163,21 +182,26 @@ void Dotenv::ParseContent(const std::string_view input) {
// Skip whitespace after key
i++;
}
//Move start/end pointers to the beginning of the value
start = i + 1;
end = i + 1;
continue;
} else if (!inComment && (c == '"' || c == '\'' || c == '`')) {
if (start == i) {
// Whole value stars with a quote, parse it as a quoted string
quote = c;
} else if (quote == c) {
// Closing quote for quoted string found, no longer in quoted value
quote = 0;
}

end++;
} else if (!inComment && c == '#' && quote == 0) {
// Mark end as i, skips rest of the line
end = i;
inComment = true;
} else if ((c == '\n' || c == '\r') && quote == 0) {
// If found any key store it
if (!key.empty()) {
auto value_str = parse_value(input.substr(start, end - start));
store_.insert_or_assign(std::string(key), value_str);
Expand All @@ -187,13 +211,15 @@ void Dotenv::ParseContent(const std::string_view input) {
if (i + 1 < input.size() && input[i + 1] == '\n') {
i++;
}
// Reset counters and flags as we're about to parse a new key
start = i + 1;
end = start;
value = "";
key = "";
quote = 0;
inComment = false;
} else if (!inComment) {
//Char is not a comment, advance the key/value end pointer
end++;
}
}
Expand Down Expand Up @@ -250,11 +276,12 @@ void Dotenv::AssignNodeOptionsIfAvailable(std::string* node_options) const {
}
}

std::optional<std::string> Dotenv::GetValue(const std::string_view key) const {
std::optional<std::string_view> Dotenv::GetValue(
const std::string_view key) const {
auto match = store_.find(key.data());

if (match != store_.end()) {
return std::optional<std::string>{match->second};
return match->second;
}
return std::nullopt;
}
Expand Down
2 changes: 1 addition & 1 deletion src/node_dotenv.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Dotenv {
~Dotenv() = default;

void ParseContent(const std::string_view content);
std::optional<std::string> GetValue(const std::string_view key) const;
std::optional<std::string_view> GetValue(const std::string_view key) const;
ParseResult ParsePath(const std::string_view path);
void AssignNodeOptionsIfAvailable(std::string* node_options) const;
void SetEnvironment(Environment* env);
Expand Down
4 changes: 0 additions & 4 deletions test/parallel/test-dotenv.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,6 @@ assert.strictEqual(process.env.MULTI_DOUBLE_QUOTED, 'THIS\nIS\nA\nMULTILINE\nSTR
assert.strictEqual(process.env.MULTI_SINGLE_QUOTED, 'THIS\nIS\nA\nMULTILINE\nSTRING');
assert.strictEqual(process.env.MULTI_BACKTICKED, 'THIS\nIS\nA\n"MULTILINE\'S"\nSTRING');

// These are no longer true, parser is now more strict when it comes to balancing double quotes
// assert.strictEqual(process.env.MULTI_NOT_VALID_QUOTE, '"');
// assert.strictEqual(process.env.MULTI_NOT_VALID, 'THIS');

// Test that \n is expanded to a newline in double-quoted string
assert.strictEqual(process.env.EXPAND_NEWLINES, 'expand\nnew\nlines');
assert.strictEqual(process.env.DONT_EXPAND_UNQUOTED, 'dontexpand\\nnewlines');
Expand Down