-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
274 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
#include "env-inl.h" | ||
#include "node_dotenv.h" | ||
#include "node_file.h" | ||
#include "uv.h" | ||
|
||
#ifdef __POSIX__ | ||
constexpr std::string_view kNewlineCharacter = "\n"; | ||
#else | ||
constexpr std::string_view kNewlineCharacter = "\r\n"; | ||
#endif | ||
|
||
namespace node { | ||
|
||
using v8::Isolate; | ||
using v8::NewStringType; | ||
|
||
namespace dotenv { | ||
|
||
void ParseLine(const std::string_view line, Isolate* isolate, std::shared_ptr<KVStore> store) { | ||
auto equal_index = line.find('='); | ||
|
||
if (equal_index == std::string_view::npos) { | ||
return; | ||
} | ||
|
||
auto key = line.substr(0, equal_index); | ||
|
||
// Remove leading and trailing space characters from key. | ||
while (!key.empty() && key.front() == ' ') key.remove_prefix(1); | ||
while (!key.empty() && key.back() == ' ') key.remove_suffix(1); | ||
|
||
// Omit lines with comments | ||
if (key.front() == '#' || key.empty()) { | ||
return; | ||
} | ||
|
||
auto value = std::string(line.substr(equal_index + 1)); | ||
|
||
// Might start and end with `"' characters. | ||
auto quotation_index = value.find_first_of("`\"'"); | ||
|
||
if (quotation_index == 0) { | ||
auto quote_character = value[quotation_index]; | ||
value.erase(0, 1); | ||
|
||
auto end_quotation_index = value.find_last_of(quote_character); | ||
|
||
// We couldn't find the closing quotation character. Terminate. | ||
if (end_quotation_index == std::string::npos) { | ||
return; | ||
} | ||
|
||
value.erase(end_quotation_index); | ||
} else { | ||
auto hash_index = value.find('#'); | ||
|
||
// Remove any inline comments | ||
if (hash_index != std::string::npos) { | ||
value.erase(hash_index); | ||
} | ||
|
||
// Remove any leading/trailing spaces from unquoted values. | ||
while (!value.empty() && value.front() == ' ') value.erase(0, 1); | ||
while (!value.empty() && value.back() == ' ') value.erase(value.size() - 1); | ||
} | ||
|
||
store->Set(isolate, | ||
v8::String::NewFromUtf8( | ||
isolate, key.data(), NewStringType::kNormal, key.size()) | ||
.ToLocalChecked(), | ||
v8::String::NewFromUtf8( | ||
isolate, value.data(), NewStringType::kNormal, value.size()) | ||
.ToLocalChecked()); | ||
} | ||
|
||
void LoadFromFile(Environment* env, const std::string_view path) { | ||
Isolate* isolate = env->isolate(); | ||
auto store = env->env_vars(); | ||
uv_fs_t req; | ||
auto defer_req_cleanup = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); }); | ||
|
||
uv_file file = uv_fs_open(nullptr, &req, path.data(), 0, 438, nullptr); | ||
if (req.result < 0) { | ||
// req will be cleaned up by scope leave. | ||
return; | ||
} | ||
uv_fs_req_cleanup(&req); | ||
|
||
auto defer_close = OnScopeLeave([file]() { | ||
uv_fs_t close_req; | ||
CHECK_EQ(0, uv_fs_close(nullptr, &close_req, file, nullptr)); | ||
uv_fs_req_cleanup(&close_req); | ||
}); | ||
|
||
std::string result{}; | ||
char buffer[8192]; | ||
uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer)); | ||
|
||
while (true) { | ||
auto r = uv_fs_read(nullptr, &req, file, &buf, 1, -1, nullptr); | ||
if (req.result < 0) { | ||
// req will be cleaned up by scope leave. | ||
return; | ||
} | ||
uv_fs_req_cleanup(&req); | ||
if (r <= 0) { | ||
break; | ||
} | ||
result.append(buf.base, r); | ||
} | ||
|
||
using std::string_view_literals::operator""sv; | ||
|
||
for (const auto& line : SplitString(result, kNewlineCharacter)) { | ||
ParseLine(line, isolate, store); | ||
} | ||
} | ||
|
||
} // namespace dotenv | ||
|
||
} // namespace node |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#ifndef SRC_NODE_DOTENV_H_ | ||
#define SRC_NODE_DOTENV_H_ | ||
|
||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | ||
|
||
#include "util-inl.h" | ||
|
||
namespace node { | ||
|
||
namespace dotenv { | ||
|
||
void LoadFromFile(Environment* env, | ||
const std::string_view path); | ||
|
||
} // namespace dotenv | ||
|
||
} // namespace node | ||
|
||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | ||
|
||
#endif // SRC_NODE_DOTENV_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
BASIC=basic | ||
|
||
# previous line intentionally left blank | ||
AFTER_LINE=after_line | ||
EMPTY= | ||
EMPTY_SINGLE_QUOTES='' | ||
EMPTY_DOUBLE_QUOTES="" | ||
EMPTY_BACKTICKS=`` | ||
SINGLE_QUOTES='single_quotes' | ||
SINGLE_QUOTES_SPACED=' single quotes ' | ||
DOUBLE_QUOTES="double_quotes" | ||
DOUBLE_QUOTES_SPACED=" double quotes " | ||
DOUBLE_QUOTES_INSIDE_SINGLE='double "quotes" work inside single quotes' | ||
DOUBLE_QUOTES_WITH_NO_SPACE_BRACKET="{ port: $MONGOLAB_PORT}" | ||
SINGLE_QUOTES_INSIDE_DOUBLE="single 'quotes' work inside double quotes" | ||
BACKTICKS_INSIDE_SINGLE='`backticks` work inside single quotes' | ||
BACKTICKS_INSIDE_DOUBLE="`backticks` work inside double quotes" | ||
BACKTICKS=`backticks` | ||
BACKTICKS_SPACED=` backticks ` | ||
DOUBLE_QUOTES_INSIDE_BACKTICKS=`double "quotes" work inside backticks` | ||
SINGLE_QUOTES_INSIDE_BACKTICKS=`single 'quotes' work inside backticks` | ||
DOUBLE_AND_SINGLE_QUOTES_INSIDE_BACKTICKS=`double "quotes" and single 'quotes' work inside backticks` | ||
EXPAND_NEWLINES="expand\nnew\nlines" | ||
DONT_EXPAND_UNQUOTED=dontexpand\nnewlines | ||
DONT_EXPAND_SQUOTED='dontexpand\nnewlines' | ||
# COMMENTS=work | ||
INLINE_COMMENTS=inline comments # work #very #well | ||
INLINE_COMMENTS_SINGLE_QUOTES='inline comments outside of #singlequotes' # work | ||
INLINE_COMMENTS_DOUBLE_QUOTES="inline comments outside of #doublequotes" # work | ||
INLINE_COMMENTS_BACKTICKS=`inline comments outside of #backticks` # work | ||
INLINE_COMMENTS_SPACE=inline comments start with a#number sign. no space required. | ||
EQUAL_SIGNS=equals== | ||
RETAIN_INNER_QUOTES={"foo": "bar"} | ||
RETAIN_INNER_QUOTES_AS_STRING='{"foo": "bar"}' | ||
RETAIN_INNER_QUOTES_AS_BACKTICKS=`{"foo": "bar's"}` | ||
TRIM_SPACE_FROM_UNQUOTED= some spaced out string | ||
USERNAME=therealnerdybeast@example.tld | ||
SPACED_KEY = parsed |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Flags: --env-file test/fixtures/dotenv/valid.env | ||
'use strict'; | ||
|
||
require('../common'); | ||
const assert = require('node:assert'); | ||
|
||
assert.strictEqual(process.env.BASIC, 'basic', 'sets basic environment variable'); | ||
|
||
assert.strictEqual(process.env.AFTER_LINE, 'after_line', 'reads after a skipped line'); | ||
|
||
assert.strictEqual(process.env.EMPTY, '', 'defaults empty values to empty string'); | ||
|
||
assert.strictEqual(process.env.EMPTY_SINGLE_QUOTES, '', 'defaults empty values to empty string'); | ||
|
||
assert.strictEqual(process.env.EMPTY_DOUBLE_QUOTES, '', 'defaults empty values to empty string'); | ||
|
||
assert.strictEqual(process.env.EMPTY_BACKTICKS, '', 'defaults empty values to empty string'); | ||
|
||
assert.strictEqual(process.env.SINGLE_QUOTES, 'single_quotes', 'escapes single quoted values'); | ||
|
||
assert.strictEqual(process.env.SINGLE_QUOTES_SPACED, ' single quotes ', 'respects surrounding spaces in single quotes'); | ||
|
||
assert.strictEqual(process.env.DOUBLE_QUOTES, 'double_quotes', 'escapes double quoted values'); | ||
|
||
assert.strictEqual(process.env.DOUBLE_QUOTES_SPACED, ' double quotes ', 'respects surrounding spaces in double quotes'); | ||
|
||
assert.strictEqual(process.env.DOUBLE_QUOTES_INSIDE_SINGLE, 'double "quotes" work inside single quotes', 'respects double quotes inside single quotes'); | ||
|
||
assert.strictEqual(process.env.DOUBLE_QUOTES_WITH_NO_SPACE_BRACKET, '{ port: $MONGOLAB_PORT}', 'respects spacing for badly formed brackets'); | ||
|
||
assert.strictEqual(process.env.SINGLE_QUOTES_INSIDE_DOUBLE, "single 'quotes' work inside double quotes", 'respects single quotes inside double quotes'); | ||
|
||
assert.strictEqual(process.env.BACKTICKS_INSIDE_SINGLE, '`backticks` work inside single quotes', 'respects backticks inside single quotes'); | ||
|
||
assert.strictEqual(process.env.BACKTICKS_INSIDE_DOUBLE, '`backticks` work inside double quotes', 'respects backticks inside double quotes'); | ||
|
||
assert.strictEqual(process.env.BACKTICKS, 'backticks'); | ||
|
||
assert.strictEqual(process.env.BACKTICKS_SPACED, ' backticks '); | ||
|
||
assert.strictEqual(process.env.DOUBLE_QUOTES_INSIDE_BACKTICKS, 'double "quotes" work inside backticks', 'respects double quotes inside backticks'); | ||
|
||
assert.strictEqual(process.env.SINGLE_QUOTES_INSIDE_BACKTICKS, "single 'quotes' work inside backticks", 'respects single quotes inside backticks'); | ||
|
||
assert.strictEqual(process.env.DOUBLE_AND_SINGLE_QUOTES_INSIDE_BACKTICKS, "double \"quotes\" and single 'quotes' work inside backticks", 'respects single quotes inside backticks'); | ||
|
||
assert.strictEqual(process.env.INLINE_COMMENTS, 'inline comments', 'ignores inline comments'); | ||
|
||
assert.strictEqual(process.env.INLINE_COMMENTS_SINGLE_QUOTES, 'inline comments outside of #singlequotes', 'ignores inline comments and respects # character inside of single quotes'); | ||
|
||
assert.strictEqual(process.env.INLINE_COMMENTS_DOUBLE_QUOTES, 'inline comments outside of #doublequotes', 'ignores inline comments and respects # character inside of double quotes'); | ||
|
||
assert.strictEqual(process.env.INLINE_COMMENTS_BACKTICKS, 'inline comments outside of #backticks', 'ignores inline comments and respects # character inside of backticks'); | ||
|
||
assert.strictEqual(process.env.INLINE_COMMENTS_SPACE, 'inline comments start with a', 'treats # character as start of comment'); | ||
|
||
assert.strictEqual(process.env.EQUAL_SIGNS, 'equals==', 'respects equals signs in values'); | ||
|
||
assert.strictEqual(process.env.RETAIN_INNER_QUOTES, '{"foo": "bar"}', 'retains inner quotes'); | ||
|
||
assert.strictEqual(process.env.EQUAL_SIGNS, 'equals==', 'respects equals signs in values'); | ||
|
||
assert.strictEqual(process.env.RETAIN_INNER_QUOTES, '{"foo": "bar"}', 'retains inner quotes'); | ||
|
||
assert.strictEqual(process.env.RETAIN_INNER_QUOTES_AS_STRING, '{"foo": "bar"}', 'retains inner quotes'); | ||
|
||
assert.strictEqual(process.env.RETAIN_INNER_QUOTES_AS_BACKTICKS, '{"foo": "bar\'s"}', 'retains inner quotes'); | ||
|
||
assert.strictEqual(process.env.TRIM_SPACE_FROM_UNQUOTED, 'some spaced out string', 'retains spaces in string'); | ||
|
||
assert.strictEqual(process.env.USERNAME, 'therealnerdybeast@example.tld', 'parses email addresses completely'); | ||
|
||
assert.strictEqual(process.env.SPACED_KEY, 'parsed', 'parses keys and values surrounded by spaces'); |