| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent d0a6f35 commit dc8fe9e
16 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2260,6 +2260,29 @@ process.kill(process.pid, 'SIGHUP'); | |||
| 2260 | 2260 | When `SIGUSR1` is received by a Node.js process, Node.js will start the | |
| 2261 | 2261 | debugger. See [Signal Events][]. | |
| 2262 | 2262 | ||
| 2263 | + ## `process.loadEnvFile(path)` | ||
| 2264 | + | ||
| 2265 | + <!-- YAML | ||
| 2266 | + added: REPLACEME | ||
| 2267 | + --> | ||
| 2268 | + | ||
| 2269 | + > Stability: 1.1 - Active development | ||
| 2270 | + | ||
| 2271 | + * `path` {string | URL | Buffer | undefined}. **Default:** `'./.env'` | ||
| 2272 | + | ||
| 2273 | + Loads the `.env` file into `process.env`. Usage of `NODE_OPTIONS` | ||
| 2274 | + in the `.env` file will not have any effect on Node.js. | ||
| 2275 | + | ||
| 2276 | + ```cjs | ||
| 2277 | + const { loadEnvFile } = require('node:process'); | ||
| 2278 | + loadEnvFile(); | ||
| 2279 | + ``` | ||
| 2280 | + | ||
| 2281 | + ```mjs | ||
| 2282 | + import { loadEnvFile } from 'node:process'; | ||
| 2283 | + loadEnvFile(); | ||
| 2284 | + ``` | ||
| 2285 | + | ||
| 2263 | 2286 | ## `process.mainModule` | |
| 2264 | 2287 | ||
| 2265 | 2288 | <!-- YAML | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1593,6 +1593,36 @@ $ node negate.js --no-logfile --logfile=test.log --color --no-color | |||
| 1593 | 1593 | { logfile: 'test.log', color: false } | |
| 1594 | 1594 | ``` | |
| 1595 | 1595 | ||
| 1596 | + ## `util.parseEnv(content)` | ||
| 1597 | + | ||
| 1598 | + > Stability: 1.1 - Active development | ||
| 1599 | + | ||
| 1600 | + <!-- YAML | ||
| 1601 | + added: REPLACEME | ||
| 1602 | + --> | ||
| 1603 | + | ||
| 1604 | + * `content` {string} | ||
| 1605 | + | ||
| 1606 | + The raw contents of a `.env` file. | ||
| 1607 | + | ||
| 1608 | + * Returns: {Object} | ||
| 1609 | + | ||
| 1610 | + Given an example `.env` file: | ||
| 1611 | + | ||
| 1612 | + ```cjs | ||
| 1613 | + const { parseEnv } = require('node:util'); | ||
| 1614 | + | ||
| 1615 | + parseEnv('HELLO=world\nHELLO=oh my\n'); | ||
| 1616 | + // Returns: { HELLO: 'oh my' } | ||
| 1617 | + ``` | ||
| 1618 | + | ||
| 1619 | + ```mjs | ||
| 1620 | + import { parseEnv } from 'node:util'; | ||
| 1621 | + | ||
| 1622 | + parseEnv('HELLO=world\nHELLO=oh my\n'); | ||
| 1623 | + // Returns: { HELLO: 'oh my' } | ||
| 1624 | + ``` | ||
| 1625 | + | ||
| 1596 | 1626 | ## `util.promisify(original)` | |
| 1597 | 1627 | ||
| 1598 | 1628 | <!-- YAML | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -172,6 +172,7 @@ const rawMethods = internalBinding('process_methods'); | |||
| 172 | 172 | process._kill = rawMethods._kill; | |
| 173 | 173 | ||
| 174 | 174 | const wrapped = perThreadSetup.wrapProcessMethods(rawMethods); | |
| 175 | + process.loadEnvFile = wrapped.loadEnvFile; | ||
| 175 | 176 | process._rawDebug = wrapped._rawDebug; | |
| 176 | 177 | process.cpuUsage = wrapped.cpuUsage; | |
| 177 | 178 | process.resourceUsage = wrapped.resourceUsage; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -46,6 +46,8 @@ const { | |||
| 46 | 46 | validateNumber, | |
| 47 | 47 | validateObject, | |
| 48 | 48 | } = require('internal/validators'); | |
| 49 | + const { getValidatedPath } = require('internal/fs/utils'); | ||
| 50 | + const { toNamespacedPath } = require('path'); | ||
| 49 | 51 | const constants = internalBinding('constants').os.signals; | |
| 50 | 52 | ||
| 51 | 53 | const kInternal = Symbol('internal properties'); | |
@@ -100,6 +102,7 @@ function wrapProcessMethods(binding) { | |||
| 100 | 102 | memoryUsage: _memoryUsage, | |
| 101 | 103 | rss, | |
| 102 | 104 | resourceUsage: _resourceUsage, | |
| 105 | + loadEnvFile: _loadEnvFile, | ||
| 103 | 106 | } = binding; | |
| 104 | 107 | ||
| 105 | 108 | function _rawDebug(...args) { | |
@@ -250,6 +253,19 @@ function wrapProcessMethods(binding) { | |||
| 250 | 253 | }; | |
| 251 | 254 | } | |
| 252 | 255 | ||
| 256 | + /** | ||
| 257 | + * Loads the `.env` file to process.env. | ||
| 258 | + * @param {string | URL | Buffer | undefined} path | ||
| 259 | + */ | ||
| 260 | + function loadEnvFile(path = undefined) { // Provide optional value so that `loadEnvFile.length` returns 0 | ||
| 261 | + if (path != null) { | ||
| 262 | + path = getValidatedPath(path); | ||
| 263 | + _loadEnvFile(toNamespacedPath(path)); | ||
| 264 | + } else { | ||
| 265 | + _loadEnvFile(); | ||
| 266 | + } | ||
| 267 | + } | ||
| 268 | + | ||
| 253 | 269 | ||
| 254 | 270 | return { | |
| 255 | 271 | _rawDebug, | |
@@ -258,6 +274,7 @@ function wrapProcessMethods(binding) { | |||
| 258 | 274 | memoryUsage, | |
| 259 | 275 | kill, | |
| 260 | 276 | exit, | |
| 277 | + loadEnvFile, | ||
| 261 | 278 | }; | |
| 262 | 279 | } | |
| 263 | 280 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -67,9 +67,11 @@ const { debuglog } = require('internal/util/debuglog'); | |||
| 67 | 67 | const { | |
| 68 | 68 | validateFunction, | |
| 69 | 69 | validateNumber, | |
| 70 | + validateString, | ||
| 70 | 71 | } = require('internal/validators'); | |
| 71 | 72 | const { isBuffer } = require('buffer').Buffer; | |
| 72 | 73 | const types = require('internal/util/types'); | |
| 74 | + const binding = internalBinding('util'); | ||
| 73 | 75 | ||
| 74 | 76 | const { | |
| 75 | 77 | deprecate, | |
@@ -371,6 +373,16 @@ function _exceptionWithHostPort(...args) { | |||
| 371 | 373 | return new ExceptionWithHostPort(...args); | |
| 372 | 374 | } | |
| 373 | 375 | ||
| 376 | + /** | ||
| 377 | + * Parses the content of a `.env` file. | ||
| 378 | + * @param {string} content | ||
| 379 | + * @returns {Record<string, string>} | ||
| 380 | + */ | ||
| 381 | + function parseEnv(content) { | ||
| 382 | + validateString(content, 'content'); | ||
| 383 | + return binding.parseEnv(content); | ||
| 384 | + } | ||
| 385 | + | ||
| 374 | 386 | // Keep the `exports =` so that various functions can still be monkeypatched | |
| 375 | 387 | module.exports = { | |
| 376 | 388 | _errnoException, | |
@@ -424,6 +436,7 @@ module.exports = { | |||
| 424 | 436 | return lazyAbortController().aborted; | |
| 425 | 437 | }, | |
| 426 | 438 | types, | |
| 439 | + parseEnv, | ||
| 427 | 440 | }; | |
| 428 | 441 | ||
| 429 | 442 | defineLazyProperties( | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -867,9 +867,18 @@ static ExitCode InitializeNodeWithArgsInternal( | |||
| 867 | 867 | CHECK(!per_process::v8_initialized); | |
| 868 | 868 | ||
| 869 | 869 | for (const auto& file_path : file_paths) { | |
| 870 | - bool path_exists = per_process::dotenv_file.ParsePath(file_path); | ||
| 871 | - | ||
| 872 | - if (!path_exists) errors->push_back(file_path + ": not found"); | ||
| 870 | + switch (per_process::dotenv_file.ParsePath(file_path)) { | ||
| 871 | + case Dotenv::ParseResult::Valid: | ||
| 872 | + break; | ||
| 873 | + case Dotenv::ParseResult::InvalidContent: | ||
| 874 | + errors->push_back(file_path + ": invalid format"); | ||
| 875 | + break; | ||
| 876 | + case Dotenv::ParseResult::FileError: | ||
| 877 | + errors->push_back(file_path + ": not found"); | ||
| 878 | + break; | ||
| 879 | + default: | ||
| 880 | + UNREACHABLE(); | ||
| 881 | + } | ||
| 873 | 882 | } | |
| 874 | 883 | ||
| 875 | 884 | per_process::dotenv_file.AssignNodeOptionsIfAvailable(&node_options); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,7 +5,9 @@ | |||
| 5 | 5 | ||
| 6 | 6 | namespace node { | |
| 7 | 7 | ||
| 8 | + using v8::Local; | ||
| 8 | 9 | using v8::NewStringType; | |
| 10 | + using v8::Object; | ||
| 9 | 11 | using v8::String; | |
| 10 | 12 | ||
| 11 | 13 | std::vector<std::string> Dotenv::GetPathFromArgs( | |
@@ -64,14 +66,47 @@ void Dotenv::SetEnvironment(node::Environment* env) { | |||
| 64 | 66 | } | |
| 65 | 67 | } | |
| 66 | 68 | ||
| 67 | - bool Dotenv::ParsePath(const std::string_view path) { | ||
| 69 | + Local<Object> Dotenv::ToObject(Environment* env) { | ||
| 70 | + Local<Object> result = Object::New(env->isolate()); | ||
| 71 | + | ||
| 72 | + for (const auto& entry : store_) { | ||
| 73 | + auto key = entry.first; | ||
| 74 | + auto value = entry.second; | ||
| 75 | + | ||
| 76 | + result | ||
| 77 | + ->Set( | ||
| 78 | + env->context(), | ||
| 79 | + v8::String::NewFromUtf8( | ||
| 80 | + env->isolate(), key.data(), NewStringType::kNormal, key.size()) | ||
| 81 | + .ToLocalChecked(), | ||
| 82 | + v8::String::NewFromUtf8(env->isolate(), | ||
| 83 | + value.data(), | ||
| 84 | + NewStringType::kNormal, | ||
| 85 | + value.size()) | ||
| 86 | + .ToLocalChecked()) | ||
| 87 | + .Check(); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + return result; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + void Dotenv::ParseContent(const std::string_view content) { | ||
| 94 | + using std::string_view_literals::operator""sv; | ||
| 95 | + auto lines = SplitString(content, "\n"sv); | ||
| 96 | + | ||
| 97 | + for (const auto& line : lines) { | ||
| 98 | + ParseLine(line); | ||
| 99 | + } | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + Dotenv::ParseResult Dotenv::ParsePath(const std::string_view path) { | ||
| 68 | 103 | uv_fs_t req; | |
| 69 | 104 | auto defer_req_cleanup = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); }); | |
| 70 | 105 | ||
| 71 | 106 | uv_file file = uv_fs_open(nullptr, &req, path.data(), 0, 438, nullptr); | |
| 72 | 107 | if (req.result < 0) { | |
| 73 | 108 | // req will be cleaned up by scope leave. | |
| 74 | - return false; | ||
| 109 | + return ParseResult::FileError; | ||
| 75 | 110 | } | |
| 76 | 111 | uv_fs_req_cleanup(&req); | |
| 77 | 112 | ||
@@ -89,7 +124,7 @@ bool Dotenv::ParsePath(const std::string_view path) { | |||
| 89 | 124 | auto r = uv_fs_read(nullptr, &req, file, &buf, 1, -1, nullptr); | |
| 90 | 125 | if (req.result < 0) { | |
| 91 | 126 | // req will be cleaned up by scope leave. | |
| 92 | - return false; | ||
| 127 | + return ParseResult::InvalidContent; | ||
| 93 | 128 | } | |
| 94 | 129 | uv_fs_req_cleanup(&req); | |
| 95 | 130 | if (r <= 0) { | |
@@ -98,13 +133,8 @@ bool Dotenv::ParsePath(const std::string_view path) { | |||
| 98 | 133 | result.append(buf.base, r); | |
| 99 | 134 | } | |
| 100 | 135 | ||
| 101 | - using std::string_view_literals::operator""sv; | ||
| 102 | - auto lines = SplitString(result, "\n"sv); | ||
| 103 | - | ||
| 104 | - for (const auto& line : lines) { | ||
| 105 | - ParseLine(line); | ||
| 106 | - } | ||
| 107 | - return true; | ||
| 136 | + ParseContent(result); | ||
| 137 | + return ParseResult::Valid; | ||
| 108 | 138 | } | |
| 109 | 139 | ||
| 110 | 140 | void Dotenv::AssignNodeOptionsIfAvailable(std::string* node_options) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,23 +4,28 @@ | |||
| 4 | 4 | #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | |
| 5 | 5 | ||
| 6 | 6 | #include "util-inl.h" | |
| 7 | + #include "v8.h" | ||
| 7 | 8 | ||
| 8 | 9 | #include <map> | |
| 9 | 10 | ||
| 10 | 11 | namespace node { | |
| 11 | 12 | ||
| 12 | 13 | class Dotenv { | |
| 13 | 14 | public: | |
| 15 | + enum ParseResult { Valid, FileError, InvalidContent }; | ||
| 16 | + | ||
| 14 | 17 | Dotenv() = default; | |
| 15 | 18 | Dotenv(const Dotenv& d) = default; | |
| 16 | 19 | Dotenv(Dotenv&& d) noexcept = default; | |
| 17 | 20 | Dotenv& operator=(Dotenv&& d) noexcept = default; | |
| 18 | 21 | Dotenv& operator=(const Dotenv& d) = default; | |
| 19 | 22 | ~Dotenv() = default; | |
| 20 | 23 | ||
| 21 | - bool ParsePath(const std::string_view path); | ||
| 24 | + void ParseContent(const std::string_view content); | ||
| 25 | + ParseResult ParsePath(const std::string_view path); | ||
| 22 | 26 | void AssignNodeOptionsIfAvailable(std::string* node_options); | |
| 23 | 27 | void SetEnvironment(Environment* env); | |
| 28 | + v8::Local<v8::Object> ToObject(Environment* env); | ||
| 24 | 29 | ||
| 25 | 30 | static std::vector<std::string> GetPathFromArgs( | |
| 26 | 31 | const std::vector<std::string>& args); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -84,6 +84,8 @@ class BindingData : public SnapshotableObject { | |||
| 84 | 84 | ||
| 85 | 85 | static void SlowBigInt(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 86 | 86 | ||
| 87 | + static void LoadEnvFile(const v8::FunctionCallbackInfo<v8::Value>& args); | ||
| 88 | + | ||
| 87 | 89 | private: | |
| 88 | 90 | // Buffer length in uint32. | |
| 89 | 91 | static constexpr size_t kHrTimeBufferLength = 3; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,6 +4,7 @@ | |||
| 4 | 4 | #include "env-inl.h" | |
| 5 | 5 | #include "memory_tracker-inl.h" | |
| 6 | 6 | #include "node.h" | |
| 7 | + #include "node_dotenv.h" | ||
| 7 | 8 | #include "node_errors.h" | |
| 8 | 9 | #include "node_external_reference.h" | |
| 9 | 10 | #include "node_internals.h" | |
@@ -463,6 +464,38 @@ static void ReallyExit(const FunctionCallbackInfo<Value>& args) { | |||
| 463 | 464 | env->Exit(code); | |
| 464 | 465 | } | |
| 465 | 466 | ||
| 467 | + static void LoadEnvFile(const v8::FunctionCallbackInfo<v8::Value>& args) { | ||
| 468 | + Environment* env = Environment::GetCurrent(args); | ||
| 469 | + std::string path = ".env"; | ||
| 470 | + if (args.Length() == 1) { | ||
| 471 | + Utf8Value path_value(args.GetIsolate(), args[0]); | ||
| 472 | + path = path_value.ToString(); | ||
| 473 | + } | ||
| 474 | + | ||
| 475 | + THROW_IF_INSUFFICIENT_PERMISSIONS( | ||
| 476 | + env, permission::PermissionScope::kFileSystemRead, path); | ||
| 477 | + | ||
| 478 | + Dotenv dotenv{}; | ||
| 479 | + | ||
| 480 | + switch (dotenv.ParsePath(path)) { | ||
| 481 | + case dotenv.ParseResult::Valid: { | ||
| 482 | + dotenv.SetEnvironment(env); | ||
| 483 | + break; | ||
| 484 | + } | ||
| 485 | + case dotenv.ParseResult::InvalidContent: { | ||
| 486 | + THROW_ERR_INVALID_ARG_TYPE( | ||
| 487 | + env, "Contents of '%s' should be a valid string.", path.c_str()); | ||
| 488 | + break; | ||
| 489 | + } | ||
| 490 | + case dotenv.ParseResult::FileError: { | ||
| 491 | + env->ThrowUVException(UV_ENOENT, "Failed to load '%s'.", path.c_str()); | ||
| 492 | + break; | ||
| 493 | + } | ||
| 494 | + default: | ||
| 495 | + UNREACHABLE(); | ||
| 496 | + } | ||
| 497 | + } | ||
| 498 | + | ||
| 466 | 499 | namespace process { | |
| 467 | 500 | ||
| 468 | 501 | BindingData::BindingData(Realm* realm, | |
@@ -616,6 +649,8 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data, | |||
| 616 | 649 | SetMethod(isolate, target, "reallyExit", ReallyExit); | |
| 617 | 650 | SetMethodNoSideEffect(isolate, target, "uptime", Uptime); | |
| 618 | 651 | SetMethod(isolate, target, "patchProcessObject", PatchProcessObject); | |
| 652 | + | ||
| 653 | + SetMethod(isolate, target, "loadEnvFile", LoadEnvFile); | ||
| 619 | 654 | } | |
| 620 | 655 | ||
| 621 | 656 | static void CreatePerContextProperties(Local<Object> target, | |
@@ -653,6 +688,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { | |||
| 653 | 688 | registry->Register(ReallyExit); | |
| 654 | 689 | registry->Register(Uptime); | |
| 655 | 690 | registry->Register(PatchProcessObject); | |
| 691 | + | ||
| 692 | + registry->Register(LoadEnvFile); | ||
| 656 | 693 | } | |
| 657 | 694 | ||
| 658 | 695 | } // namespace process | |
| Back | FazBrowse Home | New Git URL |
0 commit comments