-
Notifications
You must be signed in to change notification settings - Fork 29.5k
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
9 changed files
with
294 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
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,115 @@ | ||
#include "env-inl.h" | ||
#include "node_dotenv.h" | ||
#include "node_file.h" | ||
#include "uv.h" | ||
|
||
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() && std::isspace(key.front())) key.remove_prefix(1); | ||
while (!key.empty() && std::isspace(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() && std::isspace(value.front())) value.erase(0, 1); | ||
while (!value.empty() && std::isspace(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, "\n"sv)) { | ||
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,39 @@ | ||
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 | ||
NODE_OPTIONS="--experimental-permission" |
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,80 @@ | ||
// Flags: --env-file test/fixtures/dotenv/valid.env | ||
'use strict'; | ||
|
||
const common = require('../common'); | ||
const assert = require('node:assert'); | ||
const fs = require('node:fs'); | ||
const path = require('node:path'); | ||
|
||
// Sets basic environment variable | ||
assert.strictEqual(process.env.BASIC, 'basic'); | ||
// Reads after a skipped line | ||
assert.strictEqual(process.env.AFTER_LINE, 'after_line'); | ||
// Defaults empty values to empty string | ||
assert.strictEqual(process.env.EMPTY, ''); | ||
assert.strictEqual(process.env.EMPTY_SINGLE_QUOTES, ''); | ||
assert.strictEqual(process.env.EMPTY_DOUBLE_QUOTES, ''); | ||
assert.strictEqual(process.env.EMPTY_BACKTICKS, ''); | ||
// Escapes single quoted values | ||
assert.strictEqual(process.env.SINGLE_QUOTES, 'single_quotes'); | ||
// Respects surrounding spaces in single quotes | ||
assert.strictEqual(process.env.SINGLE_QUOTES_SPACED, ' single quotes '); | ||
// Escapes double quoted values | ||
assert.strictEqual(process.env.DOUBLE_QUOTES, 'double_quotes'); | ||
// Respects surrounding spaces in double quotes | ||
assert.strictEqual(process.env.DOUBLE_QUOTES_SPACED, ' double quotes '); | ||
// Respects double quotes inside single quotes | ||
assert.strictEqual(process.env.DOUBLE_QUOTES_INSIDE_SINGLE, 'double "quotes" work inside single quotes'); | ||
// Respects spacing for badly formed brackets | ||
assert.strictEqual(process.env.DOUBLE_QUOTES_WITH_NO_SPACE_BRACKET, '{ port: $MONGOLAB_PORT}'); | ||
// Respects single quotes inside double quotes | ||
assert.strictEqual(process.env.SINGLE_QUOTES_INSIDE_DOUBLE, "single 'quotes' work inside double quotes"); | ||
// Respects backticks inside single quotes | ||
assert.strictEqual(process.env.BACKTICKS_INSIDE_SINGLE, '`backticks` work inside single quotes'); | ||
// Respects backticks inside double quotes | ||
assert.strictEqual(process.env.BACKTICKS_INSIDE_DOUBLE, '`backticks` work inside double quotes'); | ||
assert.strictEqual(process.env.BACKTICKS, 'backticks'); | ||
assert.strictEqual(process.env.BACKTICKS_SPACED, ' backticks '); | ||
// Respects double quotes inside backticks | ||
assert.strictEqual(process.env.DOUBLE_QUOTES_INSIDE_BACKTICKS, 'double "quotes" work inside backticks'); | ||
// Respects single 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", | ||
); | ||
// Ignores inline comments | ||
assert.strictEqual(process.env.INLINE_COMMENTS, 'inline comments'); | ||
// Ignores inline comments and respects # character inside of single quotes | ||
assert.strictEqual(process.env.INLINE_COMMENTS_SINGLE_QUOTES, 'inline comments outside of #singlequotes'); | ||
// Ignores inline comments and respects # character inside of double quotes | ||
assert.strictEqual(process.env.INLINE_COMMENTS_DOUBLE_QUOTES, 'inline comments outside of #doublequotes'); | ||
// Ignores inline comments and respects # character inside of backticks | ||
assert.strictEqual(process.env.INLINE_COMMENTS_BACKTICKS, 'inline comments outside of #backticks'); | ||
// Treats # character as start of comment | ||
assert.strictEqual(process.env.INLINE_COMMENTS_SPACE, 'inline comments start with a'); | ||
// Respects equals signs in values | ||
assert.strictEqual(process.env.EQUAL_SIGNS, 'equals=='); | ||
// Retains inner quotes | ||
assert.strictEqual(process.env.RETAIN_INNER_QUOTES, '{"foo": "bar"}'); | ||
// Respects equals signs in values | ||
assert.strictEqual(process.env.EQUAL_SIGNS, 'equals=='); | ||
// Retains inner quotes | ||
assert.strictEqual(process.env.RETAIN_INNER_QUOTES, '{"foo": "bar"}'); | ||
assert.strictEqual(process.env.RETAIN_INNER_QUOTES_AS_STRING, '{"foo": "bar"}'); | ||
assert.strictEqual(process.env.RETAIN_INNER_QUOTES_AS_BACKTICKS, '{"foo": "bar\'s"}'); | ||
// Retains spaces in string | ||
assert.strictEqual(process.env.TRIM_SPACE_FROM_UNQUOTED, 'some spaced out string'); | ||
// Parses email addresses completely | ||
assert.strictEqual(process.env.USERNAME, 'therealnerdybeast@example.tld'); | ||
// Parses keys and values surrounded by spaces | ||
assert.strictEqual(process.env.SPACED_KEY, 'parsed'); | ||
|
||
// Check if NODE_OPTIONS is set properly | ||
assert.throws(() => { | ||
fs.readFileSync(path.join(__dirname, 'test-dotenv.js'), 'utf8'); | ||
}, common.expectsError({ | ||
code: 'ERR_ACCESS_DENIED', | ||
permission: 'FileSystemRead', | ||
})); |