diff --git a/benchmark/napi/function_args/index.js b/benchmark/napi/function_args/index.js index 8ce9fa9d528f5c..2cf91650f4e77a 100644 --- a/benchmark/napi/function_args/index.js +++ b/benchmark/napi/function_args/index.js @@ -1,5 +1,5 @@ // Show the difference between calling a V8 binding C++ function -// relative to a comparable N-API C++ function, +// relative to a comparable Node-API C++ function, // in various types/numbers of arguments. // Reports n of calls per second. 'use strict'; @@ -19,7 +19,7 @@ try { try { napi = require(`./build/${common.buildType}/napi_binding`); } catch { - console.error(`${__filename}: NAPI-Binding failed to load`); + console.error(`${__filename}: Node-API binding failed to load`); process.exit(0); } diff --git a/benchmark/napi/function_call/index.js b/benchmark/napi/function_call/index.js index cde4c7ae223945..b22b5ac8b2151f 100644 --- a/benchmark/napi/function_call/index.js +++ b/benchmark/napi/function_call/index.js @@ -24,7 +24,7 @@ let napi_binding; try { napi_binding = require(`./build/${common.buildType}/napi_binding`); } catch { - console.error('misc/function_call/index.js NAPI-Binding failed to load'); + console.error('misc/function_call/index.js Node-API binding failed to load'); process.exit(0); } const napi = napi_binding.hello; diff --git a/doc/contributing/security-release-process.md b/doc/contributing/security-release-process.md index b58251de636daa..1107edc5a68976 100644 --- a/doc/contributing/security-release-process.md +++ b/doc/contributing/security-release-process.md @@ -19,29 +19,30 @@ steps listed in the process as outlined in The current security stewards are documented in the main Node.js [README.md](https://github.com/nodejs/node#security-release-stewards). -| Company | Person | Release Date | -| ------------ | --------------- | ------------ | -| NearForm | Matteo | 2021-Oct-12 | -| Datadog | Bryan | 2022-Jan-10 | -| RH and IBM | Joe | 2022-Mar-18 | -| NearForm | Matteo / Rafael | 2022-Jul-07 | -| Datadog | Vladimir | 2022-Sep-23 | -| NodeSource | Juan | 2022-Nov-04 | -| RH and IBM | Michael | 2023-Feb-16 | -| NearForm | Rafael | 2023-Jun-20 | -| NearForm | Rafael | 2023-Aug-09 | -| NearForm | Rafael | 2023-Oct-13 | -| NodeSource | Rafael | 2024-Feb-14 | -| NodeSource | Rafael | 2024-Apr-03 | -| NodeSource | Rafael | 2024-Apr-10 | -| NodeSource | Rafael | 2024-Jul-08 | -| NodeSource | Rafael | 2025-Jan-21 | -| NodeSource | Rafael | 2025-May-14 | -| NodeSource | Rafael | 2025-July-15 | -| Datadog | Bryan | | -| IBM | Joe | | -| Platformatic | Matteo | | -| NodeSource | Juan | | +| Company | Person | Release Date | +| ----------------------- | --------------- | ------------ | +| NearForm | Matteo | 2021-Oct-12 | +| Datadog | Bryan | 2022-Jan-10 | +| RH and IBM | Joe | 2022-Mar-18 | +| NearForm | Matteo / Rafael | 2022-Jul-07 | +| Datadog | Vladimir | 2022-Sep-23 | +| NodeSource | Juan | 2022-Nov-04 | +| RH and IBM | Michael | 2023-Feb-16 | +| NearForm | Rafael | 2023-Jun-20 | +| NearForm | Rafael | 2023-Aug-09 | +| NearForm | Rafael | 2023-Oct-13 | +| NodeSource | Rafael | 2024-Feb-14 | +| NodeSource | Rafael | 2024-Apr-03 | +| NodeSource | Rafael | 2024-Apr-10 | +| NodeSource | Rafael | 2024-Jul-08 | +| NodeSource | Rafael | 2025-Jan-21 | +| NodeSource | Rafael | 2025-May-14 | +| NodeSource | Rafael | 2025-Jul-15 | +| HeroDevs and NodeSource | Marco / Rafael | 2026-Jan-13 | +| Datadog | Bryan | | +| IBM | Joe | | +| Platformatic | Matteo | | +| NodeSource | Juan | | ## Planning diff --git a/lib/fs.js b/lib/fs.js index ace5c464b73b14..1c65c01c939f39 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -597,7 +597,6 @@ function openAsBlob(path, options = kEmptyObject) { */ function read(fd, buffer, offsetOrOptions, length, position, callback) { fd = getValidatedFd(fd); - let offset = offsetOrOptions; let params = null; if (arguments.length <= 4) { @@ -689,8 +688,6 @@ ObjectDefineProperty(read, kCustomPromisifyArgsSymbol, * @returns {number} */ function readSync(fd, buffer, offsetOrOptions, length, position) { - fd = getValidatedFd(fd); - validateBuffer(buffer); let offset = offsetOrOptions; @@ -779,7 +776,6 @@ ObjectDefineProperty(readv, kCustomPromisifyArgsSymbol, * @returns {number} */ function readvSync(fd, buffers, position) { - fd = getValidatedFd(fd); validateBufferArray(buffers); if (typeof position !== 'number') @@ -809,7 +805,6 @@ function write(fd, buffer, offsetOrOptions, length, position, callback) { } fd = getValidatedFd(fd); - let offset = offsetOrOptions; if (isArrayBufferView(buffer)) { callback ||= position || length || offset; @@ -880,7 +875,6 @@ ObjectDefineProperty(write, kCustomPromisifyArgsSymbol, * @returns {number} */ function writeSync(fd, buffer, offsetOrOptions, length, position) { - fd = getValidatedFd(fd); const ctx = {}; let result; @@ -970,7 +964,6 @@ ObjectDefineProperty(writev, kCustomPromisifyArgsSymbol, { * @returns {number} */ function writevSync(fd, buffers, position) { - fd = getValidatedFd(fd); validateBufferArray(buffers); if (buffers.length === 0) { diff --git a/lib/internal/test_runner/reporter/spec.js b/lib/internal/test_runner/reporter/spec.js index 9031025e57d930..515d17fd42b455 100644 --- a/lib/internal/test_runner/reporter/spec.js +++ b/lib/internal/test_runner/reporter/spec.js @@ -53,7 +53,7 @@ class SpecReporter extends Transform { } this.#failedTests = []; // Clean up the failed tests - return ArrayPrototypeJoin(results, '\n'); ; + return ArrayPrototypeJoin(results, '\n'); } #handleTestReportEvent(type, data) { const subtest = ArrayPrototypeShift(this.#stack); // This is the matching `test:start` event @@ -71,13 +71,8 @@ class SpecReporter extends Transform { ArrayPrototypeUnshift(this.#reported, msg); prefix += `${indent(msg.nesting)}${reporterUnicodeSymbolMap['arrow:right']}${msg.name}\n`; } - let hasChildren = false; - if (this.#reported[0] && this.#reported[0].nesting === data.nesting && this.#reported[0].name === data.name) { - ArrayPrototypeShift(this.#reported); - hasChildren = true; - } const indentation = indent(data.nesting); - return `${formatTestReport(type, data, prefix, indentation, hasChildren, false)}\n`; + return `${formatTestReport(type, data, false, prefix, indentation)}\n`; } #handleEvent({ type, data }) { switch (type) { diff --git a/lib/internal/test_runner/reporter/utils.js b/lib/internal/test_runner/reporter/utils.js index 63656813c17376..26e4a2d1a5c36c 100644 --- a/lib/internal/test_runner/reporter/utils.js +++ b/lib/internal/test_runner/reporter/utils.js @@ -58,7 +58,6 @@ function indent(nesting) { } function formatError(error, indent) { - if (!error) return ''; const err = error.code === 'ERR_TEST_FAILURE' ? error.cause : error; const message = ArrayPrototypeJoin( RegExpPrototypeSymbolSplit( @@ -68,7 +67,7 @@ function formatError(error, indent) { return `\n${indent} ${message}\n`; } -function formatTestReport(type, data, prefix = '', indent = '', hasChildren = false, showErrorDetails = true) { +function formatTestReport(type, data, showErrorDetails = true, prefix = '', indent = '') { let color = reporterColorMap[type] ?? colors.white; let symbol = reporterUnicodeSymbolMap[type] ?? ' '; const { skip, todo, expectFailure } = data; @@ -83,10 +82,7 @@ function formatTestReport(type, data, prefix = '', indent = '', hasChildren = fa title += ` # EXPECTED FAILURE`; } - const error = showErrorDetails ? formatError(data.details?.error, indent) : ''; - const err = hasChildren ? - (!error || data.details?.error?.failureType === 'subtestsFailed' ? '' : `\n${error}`) : - error; + const err = showErrorDetails && data.details?.error ? formatError(data.details.error, indent) : ''; if (skip !== undefined) { color = colors.gray; diff --git a/src/js_native_api_types.h b/src/js_native_api_types.h index 9642db6fba0281..add2088304a4bd 100644 --- a/src/js_native_api_types.h +++ b/src/js_native_api_types.h @@ -7,12 +7,11 @@ #ifdef NAPI_EXPERIMENTAL #define NAPI_VERSION NAPI_VERSION_EXPERIMENTAL #else -// The baseline version for N-API. -// The NAPI_VERSION controls which version will be used by default when -// compilling a native addon. If the addon developer specifically wants to use -// functions available in a new version of N-API that is not yet ported in all -// LTS versions, they can set NAPI_VERSION knowing that they have specifically -// depended on that version. +// The baseline version for Node-API. +// NAPI_VERSION controls which version is used by default when compiling +// a native addon. If the addon developer wants to use functions from a +// newer Node-API version not yet available in all LTS versions, they can +// set NAPI_VERSION to explicitly depend on that version. #define NAPI_VERSION 8 #endif #endif @@ -31,7 +30,7 @@ // This file needs to be compatible with C compilers. // This is a public include file, and these includes have essentially -// became part of it's API. +// become part of its API. #include // NOLINT(modernize-deprecated-headers) #include // NOLINT(modernize-deprecated-headers) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index 42e15e959bb1c9..1a16232a72cba0 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -18,7 +18,7 @@ #define CHECK_TO_NUMBER(env, context, result, src) \ CHECK_TO_TYPE((env), Number, (context), (result), (src), napi_number_expected) -// n-api defines NAPI_AUTO_LENGTH as the indicator that a string +// Node-API defines NAPI_AUTO_LENGTH as the indicator that a string // is null terminated. For V8 the equivalent is -1. The assert // validates that our cast of NAPI_AUTO_LENGTH results in -1 as // needed by V8. @@ -225,7 +225,7 @@ inline napi_status V8NameFromPropertyDescriptor( return napi_ok; } -// convert from n-api property attributes to v8::PropertyAttribute +// convert from Node-API property attributes to v8::PropertyAttribute inline v8::PropertyAttribute V8PropertyAttributesFromDescriptor( const napi_property_descriptor* descriptor) { unsigned int attribute_flags = v8::PropertyAttribute::None; @@ -378,11 +378,10 @@ inline napi_status Unwrap(napi_env env, //=== Function napi_callback wrapper ================================= -// Use this data structure to associate callback data with each N-API function -// exposed to JavaScript. The structure is stored in a v8::External which gets -// passed into our callback wrapper. This reduces the performance impact of -// calling through N-API. -// Ref: benchmark/misc/function_call +// Use this data structure to associate callback data with each Node-API +// function exposed to JavaScript. The structure is stored in a v8::External +// which gets passed into our callback wrapper. This reduces the performance +// impact of calling through Node-API. Ref: benchmark/misc/function_call // Discussion (incl. perf. data): https://github.com/nodejs/node/pull/21072 class CallbackBundle { public: @@ -407,7 +406,7 @@ class CallbackBundle { } public: - napi_env env; // Necessary to invoke C++ NAPI callback + napi_env env; // Necessary to invoke C++ Node-API callback void* cb_data; // The user provided callback data napi_callback cb; @@ -2126,7 +2125,7 @@ napi_status NAPI_CDECL napi_get_null(napi_env env, napi_value* result) { // Gets all callback info in a single call. (Ugly, but faster.) napi_status NAPI_CDECL napi_get_cb_info( - napi_env env, // [in] NAPI environment handle + napi_env env, // [in] Node-API environment handle napi_callback_info cbinfo, // [in] Opaque callback-info handle size_t* argc, // [in-out] Specifies the size of the provided argv array // and receives the actual count of args. diff --git a/src/js_native_api_v8_internals.h b/src/js_native_api_v8_internals.h index 7bf3bf0fa7e1a2..0914cf8504adc5 100644 --- a/src/js_native_api_v8_internals.h +++ b/src/js_native_api_v8_internals.h @@ -1,14 +1,14 @@ #ifndef SRC_JS_NATIVE_API_V8_INTERNALS_H_ #define SRC_JS_NATIVE_API_V8_INTERNALS_H_ -// The V8 implementation of N-API, including `js_native_api_v8.h` uses certain -// idioms which require definition here. For example, it uses a variant of -// persistent references which need not be reset in the constructor. It is the -// responsibility of this file to define these idioms. Optionally, this file -// may also define `NAPI_VERSION` and set it to the version of N-API to be +// The V8 implementation of Node-API, including `js_native_api_v8.h` uses +// certain idioms which require definition here. For example, it uses a variant +// of persistent references which need not be reset in the constructor. It is +// the responsibility of this file to define these idioms. Optionally, this file +// may also define `NAPI_VERSION` and set it to the version of Node-API to be // exposed. -// In the case of the Node.js implementation of N-API some of the idioms are +// In the case of the Node.js implementation of Node-API some of the idioms are // imported directly from Node.js by including `node_internals.h` below. Others // are bridged to remove references to the `node` namespace. `node_version.h`, // included below, defines `NAPI_VERSION`. diff --git a/src/node_api.cc b/src/node_api.cc index cd17f7c199dae5..191b3a28f568b2 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -56,10 +56,10 @@ static void ThrowNodeApiVersionError(node::Environment* node_env, result = new node_napi_env__(context, module_filename, module_api_version); // TODO(addaleax): There was previously code that tried to delete the // napi_env when its v8::Context was garbage collected; - // However, as long as N-API addons using this napi_env are in place, + // However, as long as Node-API addons using this napi_env are in place, // the Context needs to be accessible and alive. // Ideally, we'd want an on-addon-unload hook that takes care of this - // once all N-API addons using this napi_env are unloaded. + // once all Node-API addons using this napi_env are unloaded. // For now, a per-Environment cleanup hook is the best we can do. result->node_env()->AddCleanupHook( [](void* arg) { static_cast(arg)->Unref(); }, @@ -150,7 +150,7 @@ void node_napi_env__::CallbackIntoModule(T&& call) { !enforceUncaughtExceptionPolicy) { ProcessEmitDeprecationWarning( node_env, - "Uncaught N-API callback exception detected, please run node " + "Uncaught Node-API callback exception detected, please run node " "with option --force-node-api-uncaught-exceptions-policy=true " "to handle those exceptions properly.", "DEP0168"); @@ -675,8 +675,8 @@ class AsyncContext { } // end of namespace v8impl // Intercepts the Node-V8 module registration callback. Converts parameters -// to NAPI equivalents and then calls the registration callback specified -// by the NAPI module. +// to Node-API equivalents and then calls the registration callback specified +// by the Node-API module. static void napi_module_register_cb(v8::Local exports, v8::Local module, v8::Local context, @@ -796,7 +796,7 @@ node_module napi_module_to_node_module(const napi_module* mod) { } } // namespace node -// Registers a NAPI module. +// Registers a Node-API module. void NAPI_CDECL napi_module_register(napi_module* mod) { node::node_module* nm = new node::node_module(node::napi_module_to_node_module(mod)); @@ -839,7 +839,7 @@ struct napi_async_cleanup_hook_handle__ { if (done_cb_ != nullptr) done_cb_(done_data_); // Release the `env` handle asynchronously since it would be surprising if - // a call to a N-API function would destroy `env` synchronously. + // a call to a Node-API function would destroy `env` synchronously. static_cast(env_)->node_env()->SetImmediate( [env = env_](node::Environment*) { env->Unref(); }); } diff --git a/src/node_binding.cc b/src/node_binding.cc index 5bd07e5253ae64..3b284583d6ccc9 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -526,7 +526,7 @@ void DLOpen(const FunctionCallbackInfo& args) { } } - // -1 is used for N-API modules + // -1 is used for Node-API modules if ((mp->nm_version != -1) && (mp->nm_version != NODE_MODULE_VERSION)) { // Even if the module did self-register, it may have done so with the // wrong version. We must only give up after having checked to see if it diff --git a/src/node_file.cc b/src/node_file.cc index 15e0ff923bc112..9015dbc4a9c6ee 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -2314,8 +2314,10 @@ static void WriteBuffer(const FunctionCallbackInfo& args) { const int argc = args.Length(); CHECK_GE(argc, 4); - CHECK(args[0]->IsInt32()); - const int fd = args[0].As()->Value(); + int fd; + if (!GetValidatedFd(env, args[0]).To(&fd)) { + return; + } CHECK(Buffer::HasInstance(args[1])); Local buffer_obj = args[1].As(); @@ -2381,8 +2383,10 @@ static void WriteBuffers(const FunctionCallbackInfo& args) { const int argc = args.Length(); CHECK_GE(argc, 3); - CHECK(args[0]->IsInt32()); - const int fd = args[0].As()->Value(); + int fd; + if (!GetValidatedFd(env, args[0]).To(&fd)) { + return; + } CHECK(args[1]->IsArray()); Local chunks = args[1].As(); @@ -2441,8 +2445,10 @@ static void WriteString(const FunctionCallbackInfo& args) { const int argc = args.Length(); CHECK_GE(argc, 4); - CHECK(args[0]->IsInt32()); - const int fd = args[0].As()->Value(); + int fd; + if (!GetValidatedFd(env, args[0]).To(&fd)) { + return; + } const int64_t pos = GetOffset(args[2]); @@ -2634,8 +2640,10 @@ static void Read(const FunctionCallbackInfo& args) { const int argc = args.Length(); CHECK_GE(argc, 5); - CHECK(args[0]->IsInt32()); - const int fd = args[0].As()->Value(); + int fd; + if (!GetValidatedFd(env, args[0]).To(&fd)) { + return; + } CHECK(Buffer::HasInstance(args[1])); Local buffer_obj = args[1].As(); @@ -2765,8 +2773,10 @@ static void ReadBuffers(const FunctionCallbackInfo& args) { const int argc = args.Length(); CHECK_GE(argc, 3); - CHECK(args[0]->IsInt32()); - const int fd = args[0].As()->Value(); + int fd; + if (!GetValidatedFd(env, args[0]).To(&fd)) { + return; + } CHECK(args[1]->IsArray()); Local buffers = args[1].As(); diff --git a/src/node_mem-inl.h b/src/node_mem-inl.h index 70d28dd524be84..64f4704e62bb0c 100644 --- a/src/node_mem-inl.h +++ b/src/node_mem-inl.h @@ -8,6 +8,8 @@ namespace node { namespace mem { +static constexpr size_t kReserveSizeAndAlign = + std::max(sizeof(size_t), alignof(max_align_t)); template AllocatorStruct NgLibMemoryManager::MakeAllocator() { @@ -30,19 +32,18 @@ void* NgLibMemoryManager::ReallocImpl(void* ptr, char* original_ptr = nullptr; // We prepend each allocated buffer with a size_t containing the full - // size of the allocation. - if (size > 0) size += sizeof(size_t); + // size of the allocation, while keeping the returned pointer aligned. + if (size > 0) size += kReserveSizeAndAlign; if (ptr != nullptr) { // We are free()ing or re-allocating. - original_ptr = static_cast(ptr) - sizeof(size_t); + original_ptr = static_cast(ptr) - kReserveSizeAndAlign; previous_size = *reinterpret_cast(original_ptr); // This means we called StopTracking() on this pointer before. if (previous_size == 0) { // Fall back to the standard Realloc() function. char* ret = UncheckedRealloc(original_ptr, size); - if (ret != nullptr) - ret += sizeof(size_t); + if (ret != nullptr) ret += kReserveSizeAndAlign; return ret; } } @@ -62,7 +63,7 @@ void* NgLibMemoryManager::ReallocImpl(void* ptr, manager->env()->external_memory_accounter()->Update( manager->env()->isolate(), new_size); *reinterpret_cast(mem) = size; - mem += sizeof(size_t); + mem += kReserveSizeAndAlign; } else if (size == 0) { manager->DecreaseAllocatedSize(previous_size); manager->env()->external_memory_accounter()->Decrease( @@ -95,8 +96,8 @@ void* NgLibMemoryManager::CallocImpl(size_t nmemb, template void NgLibMemoryManager::StopTrackingMemory(void* ptr) { - size_t* original_ptr = reinterpret_cast( - static_cast(ptr) - sizeof(size_t)); + size_t* original_ptr = + reinterpret_cast(static_cast(ptr) - kReserveSizeAndAlign); Class* manager = static_cast(this); manager->DecreaseAllocatedSize(*original_ptr); manager->env()->external_memory_accounter()->Decrease( diff --git a/test/async-hooks/async-hooks.status b/test/async-hooks/async-hooks.status index 673883e4fe3d4d..dbcb29baed84a0 100644 --- a/test/async-hooks/async-hooks.status +++ b/test/async-hooks/async-hooks.status @@ -21,3 +21,5 @@ test-callback-error: PASS, FLAKY [$system==freebsd] [$system==aix] +# https://github.com/nodejs/node/issues/50245 +test-emit-after-on-destroyed: PASS, FLAKY diff --git a/test/benchmark/test-benchmark-napi.js b/test/benchmark/test-benchmark-napi.js index 518e10a5111a5b..51137440774b43 100644 --- a/test/benchmark/test-benchmark-napi.js +++ b/test/benchmark/test-benchmark-napi.js @@ -3,7 +3,7 @@ const common = require('../common'); if (common.isWindows) { - common.skip('vcbuild.bat doesn\'t build the n-api benchmarks yet'); + common.skip('vcbuild.bat doesn\'t build the Node-API benchmarks yet'); } const { isMainThread } = require('worker_threads'); diff --git a/test/cctest/test_linked_binding.cc b/test/cctest/test_linked_binding.cc index bcfc5050c9a3ec..10507950becfc8 100644 --- a/test/cctest/test_linked_binding.cc +++ b/test/cctest/test_linked_binding.cc @@ -348,7 +348,8 @@ TEST_F(LinkedBindingTest, ManyBindingsTest) { AddLinkedBinding(*test_env, "local_linked1", InitializeLocalBinding, &calls); AddLinkedBinding(*test_env, "local_linked2", InitializeLocalBinding, &calls); AddLinkedBinding(*test_env, "local_linked3", InitializeLocalBinding, &calls); - AddLinkedBinding(*test_env, local_linked_napi); // Add a N-API addon as well. + AddLinkedBinding(*test_env, + local_linked_napi); // Add a Node-API addon as well. AddLinkedBinding(*test_env, "local_linked4", InitializeLocalBinding, &calls); AddLinkedBinding(*test_env, "local_linked5", InitializeLocalBinding, &calls); diff --git a/test/es-module/test-esm-loader-hooks.mjs b/test/es-module/test-esm-loader-hooks.mjs deleted file mode 100644 index 3ff5954c06ef54..00000000000000 --- a/test/es-module/test-esm-loader-hooks.mjs +++ /dev/null @@ -1,860 +0,0 @@ -import { spawnPromisified } from '../common/index.mjs'; -import * as fixtures from '../common/fixtures.mjs'; -import assert from 'node:assert'; -import { execPath } from 'node:process'; -import { describe, it } from 'node:test'; - -describe('Loader hooks', { concurrency: !process.env.TEST_PARALLEL }, () => { - it('are called with all expected arguments', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - fixtures.fileURL('es-module-loaders/hooks-input.mjs'), - fixtures.path('es-modules/json-modules.mjs'), - ]); - - assert.strictEqual(stderr, ''); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - - const lines = stdout.split('\n'); - assert.match(lines[0], /{"url":"file:\/\/\/.*\/json-modules\.mjs","format":"test","shortCircuit":true}/); - assert.match(lines[1], /{"source":{"type":"Buffer","data":\[.*\]},"format":"module","shortCircuit":true}/); - assert.match(lines[2], /{"url":"file:\/\/\/.*\/experimental\.json","format":"test","shortCircuit":true}/); - assert.match(lines[3], /{"source":{"type":"Buffer","data":\[.*\]},"format":"json","shortCircuit":true}/); - assert.strictEqual(lines[4], ''); - assert.strictEqual(lines.length, 5); - }); - - it('are called with all expected arguments using register function', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader=data:text/javascript,', - '--input-type=module', - '--eval', - "import { register } from 'node:module';" + - `register(${JSON.stringify(fixtures.fileURL('es-module-loaders/hooks-input.mjs'))});` + - `await import(${JSON.stringify(fixtures.fileURL('es-modules/json-modules.mjs'))});`, - ]); - - assert.strictEqual(stderr, ''); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - - const lines = stdout.split('\n'); - assert.match(lines[0], /{"url":"file:\/\/\/.*\/json-modules\.mjs","format":"test","shortCircuit":true}/); - assert.match(lines[1], /{"source":{"type":"Buffer","data":\[.*\]},"format":"module","shortCircuit":true}/); - assert.match(lines[2], /{"url":"file:\/\/\/.*\/experimental\.json","format":"test","shortCircuit":true}/); - assert.match(lines[3], /{"source":{"type":"Buffer","data":\[.*\]},"format":"json","shortCircuit":true}/); - assert.strictEqual(lines[4], ''); - assert.strictEqual(lines.length, 5); - }); - - describe('should handle never-settling hooks in ESM files', { concurrency: !process.env.TEST_PARALLEL }, () => { - it('top-level await of a never-settling resolve without warning', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), - fixtures.path('es-module-loaders/never-settling-resolve-step/never-resolve.mjs'), - ]); - - assert.strictEqual(stderr, ''); - assert.match(stdout, /^should be output\r?\n$/); - assert.strictEqual(code, 13); - assert.strictEqual(signal, null); - }); - - it('top-level await of a never-settling resolve with warning', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--experimental-loader', - fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), - fixtures.path('es-module-loaders/never-settling-resolve-step/never-resolve.mjs'), - ]); - - assert.match(stderr, /Warning: Detected unsettled top-level await at.+never-resolve\.mjs:5/); - assert.match(stdout, /^should be output\r?\n$/); - assert.strictEqual(code, 13); - assert.strictEqual(signal, null); - }); - - it('top-level await of a never-settling load without warning', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), - fixtures.path('es-module-loaders/never-settling-resolve-step/never-load.mjs'), - ]); - - assert.strictEqual(stderr, ''); - assert.match(stdout, /^should be output\r?\n$/); - assert.strictEqual(code, 13); - assert.strictEqual(signal, null); - }); - - it('top-level await of a never-settling load with warning', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--experimental-loader', - fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), - fixtures.path('es-module-loaders/never-settling-resolve-step/never-load.mjs'), - ]); - - assert.match(stderr, /Warning: Detected unsettled top-level await at.+never-load\.mjs:5/); - assert.match(stdout, /^should be output\r?\n$/); - assert.strictEqual(code, 13); - assert.strictEqual(signal, null); - }); - - it('top-level await of a race of never-settling hooks', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), - fixtures.path('es-module-loaders/never-settling-resolve-step/race.mjs'), - ]); - - assert.strictEqual(stderr, ''); - assert.match(stdout, /^true\r?\n$/); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('import.meta.resolve of a never-settling resolve should throw', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), - fixtures.path('es-module-loaders/never-settling-resolve-step/import.meta.never-resolve.mjs'), - ]); - - assert.strictEqual(stderr, ''); - assert.match(stdout, /^should be output\r?\n$/); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - }); - - describe('should handle never-settling hooks in CJS files', { concurrency: !process.env.TEST_PARALLEL }, () => { - it('never-settling resolve', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), - fixtures.path('es-module-loaders/never-settling-resolve-step/never-resolve.cjs'), - ]); - - assert.strictEqual(stderr, ''); - assert.match(stdout, /^should be output\r?\n$/); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - - it('never-settling load', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), - fixtures.path('es-module-loaders/never-settling-resolve-step/never-load.cjs'), - ]); - - assert.strictEqual(stderr, ''); - assert.match(stdout, /^should be output\r?\n$/); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('race of never-settling hooks', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), - fixtures.path('es-module-loaders/never-settling-resolve-step/race.cjs'), - ]); - - assert.strictEqual(stderr, ''); - assert.match(stdout, /^true\r?\n$/); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - }); - - it('should not work without worker permission', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--permission', - '--allow-fs-read', - '*', - '--experimental-loader', - fixtures.fileURL('empty.js'), - fixtures.path('es-modules/esm-top-level-await.mjs'), - ]); - - assert.match(stderr, /Error: Access to this API has been restricted/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should allow loader hooks to spawn workers when allowed by the CLI flags', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--permission', - '--allow-worker', - '--allow-fs-read', - '*', - '--experimental-loader', - `data:text/javascript,import{Worker}from"worker_threads";new Worker(${encodeURIComponent(JSON.stringify(fixtures.path('empty.js')))}).unref()`, - fixtures.path('es-modules/esm-top-level-await.mjs'), - ]); - - assert.strictEqual(stderr, ''); - assert.match(stdout, /^1\r?\n2\r?\n$/); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('should not allow loader hooks to spawn workers if restricted by the CLI flags', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--permission', - '--allow-fs-read', - '*', - '--experimental-loader', - `data:text/javascript,import{Worker}from"worker_threads";new Worker(${encodeURIComponent(JSON.stringify(fixtures.path('empty.js')))}).unref()`, - fixtures.path('es-modules/esm-top-level-await.mjs'), - ]); - - assert.match(stderr, /code: 'ERR_ACCESS_DENIED'/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should not leak internals or expose import.meta.resolve', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - fixtures.fileURL('es-module-loaders/loader-edge-cases.mjs'), - fixtures.path('empty.js'), - ]); - - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('should be fine to call `process.exit` from a custom async hook', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,export function load(a,b,next){if(a==="data:exit")process.exit(42);return next(a,b)}', - '--input-type=module', - '--eval', - 'import "data:exit"', - ]); - - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 42); - assert.strictEqual(signal, null); - }); - - it('should be fine to call `process.exit` from a custom sync hook', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,export function resolve(a,b,next){if(a==="exit:")process.exit(42);return next(a,b)}', - '--input-type=module', - '--eval', - 'import "data:text/javascript,import.meta.resolve(%22exit:%22)"', - ]); - - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 42); - assert.strictEqual(signal, null); - }); - - it('should be fine to call `process.exit` from the loader thread top-level', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,process.exit(42)', - fixtures.path('empty.js'), - ]); - - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 42); - assert.strictEqual(signal, null); - }); - - describe('should handle a throwing top-level body', () => { - it('should handle regular Error object', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,throw new Error("error message")', - fixtures.path('empty.js'), - ]); - - assert.match(stderr, /Error: error message\r?\n/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should handle null', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,throw null', - fixtures.path('empty.js'), - ]); - - assert.match(stderr, /\nnull\r?\n/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should handle undefined', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,throw undefined', - fixtures.path('empty.js'), - ]); - - assert.match(stderr, /\nundefined\r?\n/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should handle boolean', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,throw true', - fixtures.path('empty.js'), - ]); - - assert.match(stderr, /\ntrue\r?\n/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should handle empty plain object', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,throw {}', - fixtures.path('empty.js'), - ]); - - assert.match(stderr, /\n\{\}\r?\n/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should handle plain object', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,throw {fn(){},symbol:Symbol("symbol"),u:undefined}', - fixtures.path('empty.js'), - ]); - - assert.match(stderr, /\n\{ fn: \[Function: fn\], symbol: Symbol\(symbol\), u: undefined \}\r?\n/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should handle number', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,throw 1', - fixtures.path('empty.js'), - ]); - - assert.match(stderr, /\n1\r?\n/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should handle bigint', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,throw 1n', - fixtures.path('empty.js'), - ]); - - assert.match(stderr, /\n1\r?\n/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should handle string', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,throw "literal string"', - fixtures.path('empty.js'), - ]); - - assert.match(stderr, /\nliteral string\r?\n/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should handle symbol', async () => { - const { code, signal, stdout } = await spawnPromisified(execPath, [ - '--experimental-loader', - 'data:text/javascript,throw Symbol("symbol descriptor")', - fixtures.path('empty.js'), - ]); - - // Throwing a symbol doesn't produce any output - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should handle function', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,throw function fnName(){}', - fixtures.path('empty.js'), - ]); - - assert.match(stderr, /\n\[Function: fnName\]\r?\n/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - }); - - describe('globalPreload', () => { - it('should emit warning', async () => { - const { stderr } = await spawnPromisified(execPath, [ - '--experimental-loader', - 'data:text/javascript,export function globalPreload(){}', - '--experimental-loader', - 'data:text/javascript,export function globalPreload(){return""}', - fixtures.path('empty.js'), - ]); - - assert.strictEqual(stderr.match(/`globalPreload` has been removed; use `initialize` instead/g).length, 1); - }); - - it('should not emit warning when initialize is supplied', async () => { - const { stderr } = await spawnPromisified(execPath, [ - '--experimental-loader', - 'data:text/javascript,export function globalPreload(){}export function initialize(){}', - fixtures.path('empty.js'), - ]); - - assert.doesNotMatch(stderr, /`globalPreload` has been removed; use `initialize` instead/); - }); - }); - - it('should be fine to call `process.removeAllListeners("beforeExit")` from the main thread', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - 'data:text/javascript,export function load(a,b,c){return new Promise(d=>setTimeout(()=>d(c(a,b)),99))}', - '--input-type=module', - '--eval', - 'setInterval(() => process.removeAllListeners("beforeExit"),1).unref();await import("data:text/javascript,")', - ]); - - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - describe('`initialize`/`register`', () => { - it('should invoke `initialize` correctly', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - fixtures.fileURL('es-module-loaders/hooks-initialize.mjs'), - '--input-type=module', - '--eval', - 'import os from "node:os";', - ]); - - assert.strictEqual(stderr, ''); - assert.deepStrictEqual(stdout.split('\n'), ['hooks initialize 1', '']); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('should allow communicating with loader via `register` ports', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--input-type=module', - '--eval', - ` - import {MessageChannel} from 'node:worker_threads'; - import {register} from 'node:module'; - import {once} from 'node:events'; - const {port1, port2} = new MessageChannel(); - port1.on('message', (msg) => { - console.log('message', msg); - }); - const result = register( - ${JSON.stringify(fixtures.fileURL('es-module-loaders/hooks-initialize-port.mjs'))}, - {data: port2, transferList: [port2]}, - ); - console.log('register', result); - - const timeout = setTimeout(() => {}, 2**31 - 1); // to keep the process alive. - await Promise.all([ - once(port1, 'message').then(() => once(port1, 'message')), - import('node:os'), - ]); - clearTimeout(timeout); - port1.close(); - `, - ]); - - assert.strictEqual(stderr, ''); - assert.deepStrictEqual(stdout.split('\n'), [ 'register undefined', - 'message initialize', - 'message resolve node:os', - '' ]); - - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('should have `register` accept URL objects as `parentURL`', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--import', - `data:text/javascript,${encodeURIComponent( - 'import{ register } from "node:module";' + - 'import { pathToFileURL } from "node:url";' + - 'register("./hooks-initialize.mjs", pathToFileURL("./"));' - )}`, - '--input-type=module', - '--eval', - ` - import {register} from 'node:module'; - register( - ${JSON.stringify(fixtures.fileURL('es-module-loaders/loader-load-foo-or-42.mjs'))}, - new URL('data:'), - ); - - import('node:os').then((result) => { - console.log(JSON.stringify(result)); - }); - `, - ], { cwd: fixtures.fileURL('es-module-loaders/') }); - - assert.strictEqual(stderr, ''); - assert.deepStrictEqual(stdout.split('\n').sort(), ['hooks initialize 1', '{"default":"foo"}', ''].sort()); - - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('should have `register` work with cjs', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--input-type=commonjs', - '--eval', - ` - 'use strict'; - const {register} = require('node:module'); - register( - ${JSON.stringify(fixtures.fileURL('es-module-loaders/hooks-initialize.mjs'))}, - ); - register( - ${JSON.stringify(fixtures.fileURL('es-module-loaders/loader-load-foo-or-42.mjs'))}, - ); - - import('node:os').then((result) => { - console.log(JSON.stringify(result)); - }); - `, - ]); - - assert.strictEqual(stderr, ''); - assert.deepStrictEqual(stdout.split('\n').sort(), ['hooks initialize 1', '{"default":"foo"}', ''].sort()); - - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('`register` should work with `require`', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--require', - fixtures.path('es-module-loaders/register-loader.cjs'), - '--input-type=module', - '--eval', - 'import "node:os";', - ]); - - assert.strictEqual(stderr, ''); - assert.deepStrictEqual(stdout.split('\n'), ['resolve passthru', 'resolve passthru', '']); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('`register` should work with `import`', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--import', - fixtures.fileURL('es-module-loaders/register-loader.mjs'), - '--input-type=module', - '--eval', - 'import "node:os"', - ]); - - assert.strictEqual(stderr, ''); - assert.deepStrictEqual(stdout.split('\n'), ['resolve passthru', '']); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('should execute `initialize` in sequence', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--input-type=module', - '--eval', - ` - import {register} from 'node:module'; - console.log('result 1', register( - ${JSON.stringify(fixtures.fileURL('es-module-loaders/hooks-initialize.mjs'))} - )); - console.log('result 2', register( - ${JSON.stringify(fixtures.fileURL('es-module-loaders/hooks-initialize.mjs'))} - )); - - await import('node:os'); - `, - ]); - - assert.strictEqual(stderr, ''); - assert.deepStrictEqual(stdout.split('\n'), [ 'hooks initialize 1', - 'result 1 undefined', - 'hooks initialize 2', - 'result 2 undefined', - '' ]); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('should handle `initialize` returning never-settling promise', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--input-type=module', - '--eval', - ` - import {register} from 'node:module'; - try { - register('data:text/javascript,export function initialize(){return new Promise(()=>{})}'); - } catch (e) { - console.log('caught', e.code); - } - `, - ]); - - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout.trim(), 'caught ERR_ASYNC_LOADER_REQUEST_NEVER_SETTLED'); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('should handle `initialize` returning rejecting promise', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--input-type=module', - '--eval', - ` - import {register} from 'node:module'; - register('data:text/javascript,export function initialize(){return Promise.reject()}'); - `, - ]); - - assert.match(stderr, /undefined\r?\n/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should handle `initialize` throwing null', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--input-type=module', - '--eval', - ` - import {register} from 'node:module'; - register('data:text/javascript,export function initialize(){throw null}'); - `, - ]); - - assert.match(stderr, /null\r?\n/); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should be fine to call `process.exit` from a initialize hook', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--input-type=module', - '--eval', - ` - import {register} from 'node:module'; - register('data:text/javascript,export function initialize(){process.exit(42);}'); - `, - ]); - - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 42); - assert.strictEqual(signal, null); - }); - }); - - it('should use CJS loader to respond to require.resolve calls by default', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - fixtures.fileURL('es-module-loaders/loader-resolve-passthru.mjs'), - fixtures.path('require-resolve.js'), - ]); - - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout, 'resolve passthru\n'); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('should use ESM loader to respond to require.resolve calls when opting in', async () => { - const readFile = async () => {}; - const fileURLToPath = () => {}; - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - `data:text/javascript,import{readFile}from"node:fs/promises";import{fileURLToPath}from"node:url";export ${ - async function load(url, context, nextLoad) { - const result = await nextLoad(url, context); - if (url.endsWith('/common/index.js')) { - result.source = '"use strict";module.exports=require("node:module").createRequire(' + - `${JSON.stringify(url)})(${JSON.stringify(fileURLToPath(url))});\n`; - } else if (url.startsWith('file:') && (context.format == null || context.format === 'commonjs')) { - result.source = await readFile(new URL(url)); - } - return result; - }}`, - '--experimental-loader', - fixtures.fileURL('es-module-loaders/loader-resolve-passthru.mjs'), - fixtures.path('require-resolve.js'), - ]); - - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout, 'resolve passthru\n'.repeat(10)); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('should use hooks', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(process.execPath, [ - '--no-experimental-require-module', - '--import', - fixtures.fileURL('es-module-loaders/builtin-named-exports.mjs'), - fixtures.path('es-modules/require-esm-throws-with-loaders.js'), - ]); - - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout, ''); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); - - it('should support source maps in commonjs translator', async () => { - const readFile = async () => {}; - const hook = ` - import { readFile } from 'node:fs/promises'; - export ${ - async function load(url, context, nextLoad) { - const resolved = await nextLoad(url, context); - if (context.format === 'commonjs') { - resolved.source = await readFile(new URL(url)); - } - return resolved; - } - }`; - - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--enable-source-maps', - '--import', - `data:text/javascript,${encodeURIComponent(` - import{ register } from "node:module"; - register(${ - JSON.stringify('data:text/javascript,' + encodeURIComponent(hook)) - }); - `)}`, - fixtures.path('source-map/throw-on-require.js'), - ]); - - assert.strictEqual(stdout, ''); - assert.match(stderr, /throw-on-require\.ts:9:9/); - assert.strictEqual(code, 1); - assert.strictEqual(signal, null); - }); - - it('should handle mixed of opt-in modules and non-opt-in ones', async () => { - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ - '--no-warnings', - '--experimental-loader', - `data:text/javascript,const fixtures=${encodeURI(JSON.stringify(fixtures.path('empty.js')))};export ${ - encodeURIComponent(function resolve(s, c, n) { - if (s.endsWith('entry-point')) { - return { - shortCircuit: true, - url: 'file:///c:/virtual-entry-point', - format: 'commonjs', - }; - } - return n(s, c); - }) - }export ${ - encodeURIComponent(async function load(u, c, n) { - if (u === 'file:///c:/virtual-entry-point') { - return { - shortCircuit: true, - source: `"use strict";require(${JSON.stringify(fixtures)});console.log("Hello");`, - format: 'commonjs', - }; - } - return n(u, c); - })}`, - 'entry-point', - ]); - - assert.strictEqual(stderr, ''); - assert.strictEqual(stdout, 'Hello\n'); - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }); -}); diff --git a/test/fixtures/es-module-loaders/loader-delayed-async-load.mjs b/test/fixtures/es-module-loaders/loader-delayed-async-load.mjs new file mode 100644 index 00000000000000..33bae9bdafd9c9 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-delayed-async-load.mjs @@ -0,0 +1,3 @@ +export function load(a, b, c) { + return new Promise(d => setTimeout(() => d(c(a, b)), 99)); +} diff --git a/test/fixtures/es-module-loaders/loader-exit-on-load.mjs b/test/fixtures/es-module-loaders/loader-exit-on-load.mjs new file mode 100644 index 00000000000000..4ee413fa46ce41 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-exit-on-load.mjs @@ -0,0 +1,4 @@ +export function load(a, b, next) { + if (a === 'data:exit') process.exit(42); + return next(a, b); +} diff --git a/test/fixtures/es-module-loaders/loader-exit-on-resolve.mjs b/test/fixtures/es-module-loaders/loader-exit-on-resolve.mjs new file mode 100644 index 00000000000000..cffa4bcc486a95 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-exit-on-resolve.mjs @@ -0,0 +1,4 @@ +export function resolve(a, b, next) { + if (a === 'exit:') process.exit(42); + return next(a, b); +} diff --git a/test/fixtures/es-module-loaders/loader-exit-top-level.mjs b/test/fixtures/es-module-loaders/loader-exit-top-level.mjs new file mode 100644 index 00000000000000..6427ca06037fcd --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-exit-top-level.mjs @@ -0,0 +1 @@ +process.exit(42); diff --git a/test/fixtures/es-module-loaders/loader-globalpreload-and-initialize.mjs b/test/fixtures/es-module-loaders/loader-globalpreload-and-initialize.mjs new file mode 100644 index 00000000000000..7055b90f4e7568 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-globalpreload-and-initialize.mjs @@ -0,0 +1,2 @@ +export function globalPreload() {} +export function initialize() {} diff --git a/test/fixtures/es-module-loaders/loader-globalpreload-only.mjs b/test/fixtures/es-module-loaders/loader-globalpreload-only.mjs new file mode 100644 index 00000000000000..6b7f59d6319804 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-globalpreload-only.mjs @@ -0,0 +1 @@ +export function globalPreload() {} diff --git a/test/fixtures/es-module-loaders/loader-globalpreload-with-return.mjs b/test/fixtures/es-module-loaders/loader-globalpreload-with-return.mjs new file mode 100644 index 00000000000000..f54cf72ce5b9f9 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-globalpreload-with-return.mjs @@ -0,0 +1,3 @@ +export function globalPreload() { + return ''; +} diff --git a/test/fixtures/es-module-loaders/loader-initialize-exit.mjs b/test/fixtures/es-module-loaders/loader-initialize-exit.mjs new file mode 100644 index 00000000000000..53cb4fa26f0164 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-initialize-exit.mjs @@ -0,0 +1,3 @@ +export function initialize() { + process.exit(42); +} diff --git a/test/fixtures/es-module-loaders/loader-initialize-never-settling.mjs b/test/fixtures/es-module-loaders/loader-initialize-never-settling.mjs new file mode 100644 index 00000000000000..80033b663a5a0d --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-initialize-never-settling.mjs @@ -0,0 +1,3 @@ +export function initialize() { + return new Promise(() => {}); +} diff --git a/test/fixtures/es-module-loaders/loader-initialize-rejecting.mjs b/test/fixtures/es-module-loaders/loader-initialize-rejecting.mjs new file mode 100644 index 00000000000000..899accc15114e9 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-initialize-rejecting.mjs @@ -0,0 +1,3 @@ +export function initialize() { + return Promise.reject(); +} diff --git a/test/fixtures/es-module-loaders/loader-initialize-throw-null.mjs b/test/fixtures/es-module-loaders/loader-initialize-throw-null.mjs new file mode 100644 index 00000000000000..3d834b5c23cfb3 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-initialize-throw-null.mjs @@ -0,0 +1,3 @@ +export function initialize() { + throw null; +} diff --git a/test/fixtures/es-module-loaders/loader-load-commonjs-with-source.mjs b/test/fixtures/es-module-loaders/loader-load-commonjs-with-source.mjs new file mode 100644 index 00000000000000..3f431e630cddce --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-load-commonjs-with-source.mjs @@ -0,0 +1,13 @@ +import { readFile } from 'node:fs/promises'; +import { fileURLToPath } from 'node:url'; + +export async function load(url, context, nextLoad) { + const result = await nextLoad(url, context); + if (url.endsWith('/common/index.js')) { + result.source = '"use strict";module.exports=require("node:module").createRequire(' + + JSON.stringify(url) + ')(' + JSON.stringify(fileURLToPath(url)) + ');\n'; + } else if (url.startsWith('file:') && (context.format == null || context.format === 'commonjs')) { + result.source = await readFile(new URL(url)); + } + return result; +} diff --git a/test/fixtures/es-module-loaders/loader-load-source-maps.mjs b/test/fixtures/es-module-loaders/loader-load-source-maps.mjs new file mode 100644 index 00000000000000..bf5ef6b91028a4 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-load-source-maps.mjs @@ -0,0 +1,9 @@ +import { readFile } from 'node:fs/promises'; + +export async function load(url, context, nextLoad) { + const resolved = await nextLoad(url, context); + if (context.format === 'commonjs') { + resolved.source = await readFile(new URL(url)); + } + return resolved; +} diff --git a/test/fixtures/es-module-loaders/loader-mixed-opt-in.mjs b/test/fixtures/es-module-loaders/loader-mixed-opt-in.mjs new file mode 100644 index 00000000000000..69b17a88249bef --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-mixed-opt-in.mjs @@ -0,0 +1,28 @@ +import { createRequire } from 'module'; + +const require = createRequire(import.meta.url); +const fixtures = require('../../common/fixtures.js'); + +const fixturesPath = fixtures.path('empty.js'); + +export function resolve(s, c, n) { + if (s.endsWith('entry-point')) { + return { + shortCircuit: true, + url: 'file:///c:/virtual-entry-point', + format: 'commonjs', + }; + } + return n(s, c); +} + +export async function load(u, c, n) { + if (u === 'file:///c:/virtual-entry-point') { + return { + shortCircuit: true, + source: `"use strict";require(${JSON.stringify(fixturesPath)});console.log("Hello");`, + format: 'commonjs', + }; + } + return n(u, c); +} diff --git a/test/fixtures/es-module-loaders/loader-throw-bigint.mjs b/test/fixtures/es-module-loaders/loader-throw-bigint.mjs new file mode 100644 index 00000000000000..700b0387fa7ffc --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-throw-bigint.mjs @@ -0,0 +1 @@ +throw 1n; diff --git a/test/fixtures/es-module-loaders/loader-throw-boolean.mjs b/test/fixtures/es-module-loaders/loader-throw-boolean.mjs new file mode 100644 index 00000000000000..874fbd92e5fa22 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-throw-boolean.mjs @@ -0,0 +1 @@ +throw true; diff --git a/test/fixtures/es-module-loaders/loader-throw-empty-object.mjs b/test/fixtures/es-module-loaders/loader-throw-empty-object.mjs new file mode 100644 index 00000000000000..778886fcb90a4d --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-throw-empty-object.mjs @@ -0,0 +1 @@ +throw {}; diff --git a/test/fixtures/es-module-loaders/loader-throw-error.mjs b/test/fixtures/es-module-loaders/loader-throw-error.mjs new file mode 100644 index 00000000000000..816051b6c7831f --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-throw-error.mjs @@ -0,0 +1 @@ +throw new Error('error message'); diff --git a/test/fixtures/es-module-loaders/loader-throw-function.mjs b/test/fixtures/es-module-loaders/loader-throw-function.mjs new file mode 100644 index 00000000000000..35503d670976cd --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-throw-function.mjs @@ -0,0 +1 @@ +throw function fnName() {}; diff --git a/test/fixtures/es-module-loaders/loader-throw-null.mjs b/test/fixtures/es-module-loaders/loader-throw-null.mjs new file mode 100644 index 00000000000000..37d3d14b8bfe1b --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-throw-null.mjs @@ -0,0 +1 @@ +throw null; diff --git a/test/fixtures/es-module-loaders/loader-throw-number.mjs b/test/fixtures/es-module-loaders/loader-throw-number.mjs new file mode 100644 index 00000000000000..6dbc3994c7ac5d --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-throw-number.mjs @@ -0,0 +1 @@ +throw 1; diff --git a/test/fixtures/es-module-loaders/loader-throw-object.mjs b/test/fixtures/es-module-loaders/loader-throw-object.mjs new file mode 100644 index 00000000000000..fa7c9b7d43af4d --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-throw-object.mjs @@ -0,0 +1 @@ +throw { fn() {}, symbol: Symbol('symbol'), u: undefined }; diff --git a/test/fixtures/es-module-loaders/loader-throw-string.mjs b/test/fixtures/es-module-loaders/loader-throw-string.mjs new file mode 100644 index 00000000000000..613a4321f50de6 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-throw-string.mjs @@ -0,0 +1 @@ +throw 'literal string'; diff --git a/test/fixtures/es-module-loaders/loader-throw-symbol.mjs b/test/fixtures/es-module-loaders/loader-throw-symbol.mjs new file mode 100644 index 00000000000000..05876e0b4ada86 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-throw-symbol.mjs @@ -0,0 +1 @@ +throw Symbol('symbol descriptor'); diff --git a/test/fixtures/es-module-loaders/loader-throw-undefined.mjs b/test/fixtures/es-module-loaders/loader-throw-undefined.mjs new file mode 100644 index 00000000000000..38ecbdff9801f6 --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-throw-undefined.mjs @@ -0,0 +1 @@ +throw undefined; diff --git a/test/fixtures/es-module-loaders/loader-worker-spawn.mjs b/test/fixtures/es-module-loaders/loader-worker-spawn.mjs new file mode 100644 index 00000000000000..2860d116e6478c --- /dev/null +++ b/test/fixtures/es-module-loaders/loader-worker-spawn.mjs @@ -0,0 +1,7 @@ +import { Worker } from 'worker_threads'; +import { createRequire } from 'module'; + +const require = createRequire(import.meta.url); +const fixtures = require('../../common/fixtures.js'); + +new Worker(fixtures.path('empty.js')).unref(); diff --git a/test/fixtures/es-module-loaders/register-hooks-with-current-cwd-parent-url.mjs b/test/fixtures/es-module-loaders/register-hooks-with-current-cwd-parent-url.mjs new file mode 100644 index 00000000000000..580d092ab5655a --- /dev/null +++ b/test/fixtures/es-module-loaders/register-hooks-with-current-cwd-parent-url.mjs @@ -0,0 +1,3 @@ +import { register } from 'node:module'; +import { pathToFileURL } from 'node:url'; +register('./hooks-initialize.mjs', pathToFileURL('./')); diff --git a/test/fixtures/es-module-loaders/register-loader-with-url-parenturl.mjs b/test/fixtures/es-module-loaders/register-loader-with-url-parenturl.mjs new file mode 100644 index 00000000000000..1d82d2abc5576d --- /dev/null +++ b/test/fixtures/es-module-loaders/register-loader-with-url-parenturl.mjs @@ -0,0 +1,11 @@ +import {register} from 'node:module'; +import fixtures from '../../common/fixtures.js'; + +register( + fixtures.fileURL('es-module-loaders/loader-load-foo-or-42.mjs'), + new URL('data:'), +); + +import('node:os').then((result) => { + console.log(JSON.stringify(result)); +}); diff --git a/test/fixtures/module-hooks/register-loader-in-sequence.mjs b/test/fixtures/module-hooks/register-loader-in-sequence.mjs new file mode 100644 index 00000000000000..c129fcfc18092d --- /dev/null +++ b/test/fixtures/module-hooks/register-loader-in-sequence.mjs @@ -0,0 +1,11 @@ +import {register} from 'node:module'; +import fixtures from '../../common/fixtures.js'; + +console.log('result 1', register( + fixtures.fileURL('es-module-loaders/hooks-initialize.mjs') +)); +console.log('result 2', register( + fixtures.fileURL('es-module-loaders/hooks-initialize.mjs') +)); + +await import('node:os'); diff --git a/test/fixtures/module-hooks/register-loader-initialize-never-settling.mjs b/test/fixtures/module-hooks/register-loader-initialize-never-settling.mjs new file mode 100644 index 00000000000000..13091effa970bf --- /dev/null +++ b/test/fixtures/module-hooks/register-loader-initialize-never-settling.mjs @@ -0,0 +1,8 @@ +import {register} from 'node:module'; +import fixtures from '../../common/fixtures.js'; + +try { + register(fixtures.fileURL('es-module-loaders/loader-initialize-never-settling.mjs')); +} catch (e) { + console.log('caught', e.code); +} diff --git a/test/fixtures/module-hooks/register-loader-with-cjs.cjs b/test/fixtures/module-hooks/register-loader-with-cjs.cjs new file mode 100644 index 00000000000000..2cd6c666bd147d --- /dev/null +++ b/test/fixtures/module-hooks/register-loader-with-cjs.cjs @@ -0,0 +1,14 @@ +'use strict'; +const {register} = require('node:module'); +const fixtures = require('../../common/fixtures.js'); + +register( + fixtures.fileURL('es-module-loaders/hooks-initialize.mjs'), +); +register( + fixtures.fileURL('es-module-loaders/loader-load-foo-or-42.mjs'), +); + +import('node:os').then((result) => { + console.log(JSON.stringify(result)); +}); diff --git a/test/fixtures/module-hooks/register-loader-with-ports.mjs b/test/fixtures/module-hooks/register-loader-with-ports.mjs new file mode 100644 index 00000000000000..45e125f96a208f --- /dev/null +++ b/test/fixtures/module-hooks/register-loader-with-ports.mjs @@ -0,0 +1,22 @@ +import {MessageChannel} from 'node:worker_threads'; +import {register} from 'node:module'; +import {once} from 'node:events'; +import fixtures from '../../common/fixtures.js'; + +const {port1, port2} = new MessageChannel(); +port1.on('message', (msg) => { + console.log('message', msg); +}); +const result = register( + fixtures.fileURL('es-module-loaders/hooks-initialize-port.mjs'), + {data: port2, transferList: [port2]}, +); +console.log('register', result); + +const timeout = setTimeout(() => {}, 2**31 - 1); // to keep the process alive. +await Promise.all([ + once(port1, 'message').then(() => once(port1, 'message')), + import('node:os'), +]); +clearTimeout(timeout); +port1.close(); diff --git a/test/js-native-api/test_constructor/test_null.js b/test/js-native-api/test_constructor/test_null.js index 832d2e6ef4811a..701b952ff0a953 100644 --- a/test/js-native-api/test_constructor/test_null.js +++ b/test/js-native-api/test_constructor/test_null.js @@ -2,7 +2,7 @@ const common = require('../../common'); const assert = require('assert'); -// Test passing NULL to object-related N-APIs. +// Test passing NULL to object-related Node-APIs. const { testNull } = require(`./build/${common.buildType}/test_constructor`); const expectedResult = { envIsNull: 'Invalid argument', diff --git a/test/js-native-api/test_date/test.js b/test/js-native-api/test_date/test.js index e504208520e4d3..bfae9e55b2b8ae 100644 --- a/test/js-native-api/test_date/test.js +++ b/test/js-native-api/test_date/test.js @@ -2,7 +2,7 @@ const common = require('../../common'); -// This tests the date-related n-api calls +// This tests the date-related Node-API calls const assert = require('assert'); const test_date = require(`./build/${common.buildType}/test_date`); diff --git a/test/js-native-api/test_object/test_null.js b/test/js-native-api/test_object/test_null.js index 399952aaeb0724..cb68df16ce095f 100644 --- a/test/js-native-api/test_object/test_null.js +++ b/test/js-native-api/test_object/test_null.js @@ -2,7 +2,7 @@ const common = require('../../common'); const assert = require('assert'); -// Test passing NULL to object-related N-APIs. +// Test passing NULL to object-related Node-APIs. const { testNull } = require(`./build/${common.buildType}/test_object`); const expectedForProperty = { diff --git a/test/js-native-api/test_promise/test.js b/test/js-native-api/test_promise/test.js index 3b43e6132ed4d9..5980ba9def6839 100644 --- a/test/js-native-api/test_promise/test.js +++ b/test/js-native-api/test_promise/test.js @@ -2,7 +2,7 @@ const common = require('../../common'); -// This tests the promise-related n-api calls +// This tests the promise-related Node-API calls const assert = require('assert'); const test_promise = require(`./build/${common.buildType}/test_promise`); diff --git a/test/js-native-api/test_string/test_null.js b/test/js-native-api/test_string/test_null.js index ad19b4a82b588b..bd927fa3e48a3f 100644 --- a/test/js-native-api/test_string/test_null.js +++ b/test/js-native-api/test_string/test_null.js @@ -2,7 +2,7 @@ const common = require('../../common'); const assert = require('assert'); -// Test passing NULL to object-related N-APIs. +// Test passing NULL to object-related Node-APIs. const { testNull } = require(`./build/${common.buildType}/test_string`); const expectedResult = { diff --git a/test/module-hooks/test-async-loader-hooks-called-with-expected-args.mjs b/test/module-hooks/test-async-loader-hooks-called-with-expected-args.mjs new file mode 100644 index 00000000000000..195bf02bb6fdb3 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-called-with-expected-args.mjs @@ -0,0 +1,28 @@ +// Test that loader hooks are called with all expected arguments +import '../common/index.mjs'; +import assert from 'node:assert'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/hooks-input.mjs'), + fixtures.path('es-modules/json-modules.mjs'), + ], + { + stdout(output) { + const lines = output.split('\n'); + assert.match(lines[0], /{"url":"file:\/\/\/.*\/json-modules\.mjs","format":"test","shortCircuit":true}/); + assert.match(lines[1], /{"source":{"type":"Buffer","data":\[.*\]},"format":"module","shortCircuit":true}/); + assert.match(lines[2], /{"url":"file:\/\/\/.*\/experimental\.json","format":"test","shortCircuit":true}/); + assert.match(lines[3], /{"source":{"type":"Buffer","data":\[.*\]},"format":"json","shortCircuit":true}/); + assert.strictEqual(lines.length, 4); + }, + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-called-with-register.mjs b/test/module-hooks/test-async-loader-hooks-called-with-register.mjs new file mode 100644 index 00000000000000..d1f9181240d758 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-called-with-register.mjs @@ -0,0 +1,31 @@ +// Test that loader hooks are called with all expected arguments using register function +import '../common/index.mjs'; +import assert from 'node:assert'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--experimental-loader=data:text/javascript,', + '--input-type=module', + '--eval', + "import { register } from 'node:module';" + + `register(${JSON.stringify(fixtures.fileURL('es-module-loaders/hooks-input.mjs'))});` + + `await import(${JSON.stringify(fixtures.fileURL('es-modules/json-modules.mjs'))});`, + ], + { + stdout(output) { + const lines = output.split('\n'); + assert.match(lines[0], /{"url":"file:\/\/\/.*\/json-modules\.mjs","format":"test","shortCircuit":true}/); + assert.match(lines[1], /{"source":{"type":"Buffer","data":\[.*\]},"format":"module","shortCircuit":true}/); + assert.match(lines[2], /{"url":"file:\/\/\/.*\/experimental\.json","format":"test","shortCircuit":true}/); + assert.match(lines[3], /{"source":{"type":"Buffer","data":\[.*\]},"format":"json","shortCircuit":true}/); + assert.strictEqual(lines.length, 4); + }, + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-globalpreload-no-warning-with-initialize.mjs b/test/module-hooks/test-async-loader-hooks-globalpreload-no-warning-with-initialize.mjs new file mode 100644 index 00000000000000..54501748bceac1 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-globalpreload-no-warning-with-initialize.mjs @@ -0,0 +1,21 @@ +// Test that `globalPreload` should not emit warning when `initialize` is supplied + +import '../common/index.mjs'; +import assert from 'node:assert'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-globalpreload-and-initialize.mjs'), + fixtures.path('empty.js'), + ], + { + stderr(output) { + assert.doesNotMatch(output, /`globalPreload` has been removed; use `initialize` instead/); + }, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-globalpreload-warning.mjs b/test/module-hooks/test-async-loader-hooks-globalpreload-warning.mjs new file mode 100644 index 00000000000000..36a2c0089a91d0 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-globalpreload-warning.mjs @@ -0,0 +1,23 @@ +// Test that `globalPreload` should emit warning +import '../common/index.mjs'; +import assert from 'node:assert'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-globalpreload-only.mjs'), + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-globalpreload-with-return.mjs'), + fixtures.path('empty.js'), + ], + { + stderr(output) { + const matches = output.match(/`globalPreload` has been removed; use `initialize` instead/g); + assert.strictEqual(matches.length, 1); + }, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-initialize-in-sequence.mjs b/test/module-hooks/test-async-loader-hooks-initialize-in-sequence.mjs new file mode 100644 index 00000000000000..bc973f58784527 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-initialize-in-sequence.mjs @@ -0,0 +1,21 @@ +// Test that `initialize` should execute in sequence +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + fixtures.path('module-hooks/register-loader-in-sequence.mjs'), + ], + { + stdout: 'hooks initialize 1\n' + + 'result 1 undefined\n' + + 'hooks initialize 2\n' + + 'result 2 undefined', + trim: true, + stderr: '', + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-initialize-invoke.mjs b/test/module-hooks/test-async-loader-hooks-initialize-invoke.mjs new file mode 100644 index 00000000000000..72dc87c12e359a --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-initialize-invoke.mjs @@ -0,0 +1,22 @@ +// Test that `initialize` should be invoked correctly +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/hooks-initialize.mjs'), + '--input-type=module', + '--eval', + 'import os from "node:os";', + ], + { + stdout: 'hooks initialize 1', + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-initialize-never-settling.mjs b/test/module-hooks/test-async-loader-hooks-initialize-never-settling.mjs new file mode 100644 index 00000000000000..d1c12238ef0ca6 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-initialize-never-settling.mjs @@ -0,0 +1,18 @@ +// Test that `initialize` returning never-settling promise should be handled +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + fixtures.path('module-hooks/register-loader-initialize-never-settling.mjs'), + ], + { + stdout: 'caught ERR_ASYNC_LOADER_REQUEST_NEVER_SETTLED', + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-initialize-process-exit.mjs b/test/module-hooks/test-async-loader-hooks-initialize-process-exit.mjs new file mode 100644 index 00000000000000..d9476af27e8f7d --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-initialize-process-exit.mjs @@ -0,0 +1,23 @@ +// Test that it should be fine to call `process.exit` from a `initialize` hook +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-initialize-exit.mjs'), + '--input-type=module', + '--eval', + 'import "node:os"', + ], + { + status: 42, + signal: null, + stdout: '', + stderr: '', + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-initialize-rejecting.mjs b/test/module-hooks/test-async-loader-hooks-initialize-rejecting.mjs new file mode 100644 index 00000000000000..e5d2a62fbc86da --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-initialize-rejecting.mjs @@ -0,0 +1,24 @@ +// Test that `initialize` returning rejecting promise should be handled +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-initialize-rejecting.mjs'), + '--input-type=module', + '--eval', + 'import "node:os"', + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /undefined$/m, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-initialize-throw-null.mjs b/test/module-hooks/test-async-loader-hooks-initialize-throw-null.mjs new file mode 100644 index 00000000000000..69844e31ae91fc --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-initialize-throw-null.mjs @@ -0,0 +1,24 @@ +// Test that `initialize` throwing null should be handled +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-initialize-throw-null.mjs'), + '--input-type=module', + '--eval', + 'import "node:os"', + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /null$/m, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-mixed-opt-in.mjs b/test/module-hooks/test-async-loader-hooks-mixed-opt-in.mjs new file mode 100644 index 00000000000000..398df25d7bbe66 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-mixed-opt-in.mjs @@ -0,0 +1,20 @@ +// Test that loader should handle mixed of opt-in modules and non-opt-in ones +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-mixed-opt-in.mjs'), + 'entry-point', + ], + { + stdout: 'Hello', + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-never-settling-import-meta-resolve.mjs b/test/module-hooks/test-async-loader-hooks-never-settling-import-meta-resolve.mjs new file mode 100644 index 00000000000000..35a4f7a566e519 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-never-settling-import-meta-resolve.mjs @@ -0,0 +1,20 @@ +// Test import.meta.resolve of a never-settling resolve should throw in ESM files +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), + fixtures.path('es-module-loaders/never-settling-resolve-step/import.meta.never-resolve.mjs'), + ], + { + stdout: /^should be output$/, + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-never-settling-load-cjs.mjs b/test/module-hooks/test-async-loader-hooks-never-settling-load-cjs.mjs new file mode 100644 index 00000000000000..05401b9080b339 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-never-settling-load-cjs.mjs @@ -0,0 +1,20 @@ +// Test never-settling load in CJS files +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), + fixtures.path('es-module-loaders/never-settling-resolve-step/never-load.cjs'), + ], + { + stdout: /^should be output$/, + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-never-settling-load-esm-no-warning.mjs b/test/module-hooks/test-async-loader-hooks-never-settling-load-esm-no-warning.mjs new file mode 100644 index 00000000000000..dbadecc3287672 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-never-settling-load-esm-no-warning.mjs @@ -0,0 +1,22 @@ +// Test top-level await of a never-settling load without warning in ESM files +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), + fixtures.path('es-module-loaders/never-settling-resolve-step/never-load.mjs'), + ], + { + status: 13, + signal: null, + stdout: /^should be output$/, + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-never-settling-load-esm-with-warning.mjs b/test/module-hooks/test-async-loader-hooks-never-settling-load-esm-with-warning.mjs new file mode 100644 index 00000000000000..d4b30ae23f86f5 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-never-settling-load-esm-with-warning.mjs @@ -0,0 +1,21 @@ +// Test top-level await of a never-settling load with warning in ESM files +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--experimental-loader', + fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), + fixtures.path('es-module-loaders/never-settling-resolve-step/never-load.mjs'), + ], + { + status: 13, + signal: null, + stdout: /^should be output$/, + stderr: /Warning: Detected unsettled top-level await at.+never-load\.mjs:5/, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-never-settling-race-cjs.mjs b/test/module-hooks/test-async-loader-hooks-never-settling-race-cjs.mjs new file mode 100644 index 00000000000000..1de677f888383b --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-never-settling-race-cjs.mjs @@ -0,0 +1,20 @@ +// Test race of never-settling hooks in CJS files +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), + fixtures.path('es-module-loaders/never-settling-resolve-step/race.cjs'), + ], + { + stdout: /^true$/, + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-never-settling-race-esm.mjs b/test/module-hooks/test-async-loader-hooks-never-settling-race-esm.mjs new file mode 100644 index 00000000000000..e662dc43210d72 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-never-settling-race-esm.mjs @@ -0,0 +1,20 @@ +// Test top-level await of a race of never-settling hooks in ESM files +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), + fixtures.path('es-module-loaders/never-settling-resolve-step/race.mjs'), + ], + { + stdout: /^true$/, + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-never-settling-resolve-cjs.mjs b/test/module-hooks/test-async-loader-hooks-never-settling-resolve-cjs.mjs new file mode 100644 index 00000000000000..1469a7b313cce2 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-never-settling-resolve-cjs.mjs @@ -0,0 +1,20 @@ +// Test never-settling resolve in CJS files +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), + fixtures.path('es-module-loaders/never-settling-resolve-step/never-resolve.cjs'), + ], + { + stdout: /^should be output$/, + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-never-settling-resolve-esm-no-warning.mjs b/test/module-hooks/test-async-loader-hooks-never-settling-resolve-esm-no-warning.mjs new file mode 100644 index 00000000000000..43a7a98127c851 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-never-settling-resolve-esm-no-warning.mjs @@ -0,0 +1,22 @@ +// Test top-level await of a never-settling resolve without warning in ESM files +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), + fixtures.path('es-module-loaders/never-settling-resolve-step/never-resolve.mjs'), + ], + { + status: 13, + signal: null, + stdout: /^should be output$/, + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-never-settling-resolve-esm-with-warning.mjs b/test/module-hooks/test-async-loader-hooks-never-settling-resolve-esm-with-warning.mjs new file mode 100644 index 00000000000000..1a4c40c8bb96c0 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-never-settling-resolve-esm-with-warning.mjs @@ -0,0 +1,21 @@ +// Test top-level await of a never-settling resolve with warning in ESM files +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--experimental-loader', + fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'), + fixtures.path('es-module-loaders/never-settling-resolve-step/never-resolve.mjs'), + ], + { + status: 13, + signal: null, + stdout: /^should be output$/, + stderr: /Warning: Detected unsettled top-level await at.+never-resolve\.mjs:5/, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-no-leak-internals.mjs b/test/module-hooks/test-async-loader-hooks-no-leak-internals.mjs new file mode 100644 index 00000000000000..781aed8601f54a --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-no-leak-internals.mjs @@ -0,0 +1,20 @@ +// Test that loader hooks should not leak internals or expose import.meta.resolve +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-edge-cases.mjs'), + fixtures.path('empty.js'), + ], + { + stdout: '', + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-process-exit-async.mjs b/test/module-hooks/test-async-loader-hooks-process-exit-async.mjs new file mode 100644 index 00000000000000..af3b939bcf8c0e --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-process-exit-async.mjs @@ -0,0 +1,24 @@ +// Test that it should be fine to call `process.exit` from a custom async hook +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-exit-on-load.mjs'), + '--input-type=module', + '--eval', + 'import "data:exit"', + ], + { + status: 42, + signal: null, + stdout: '', + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-process-exit-sync.mjs b/test/module-hooks/test-async-loader-hooks-process-exit-sync.mjs new file mode 100644 index 00000000000000..64c079b90b7507 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-process-exit-sync.mjs @@ -0,0 +1,24 @@ +// Test that it should be fine to call `process.exit` from a custom sync hook +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-exit-on-resolve.mjs'), + '--input-type=module', + '--eval', + 'import "data:text/javascript,import.meta.resolve(%22exit:%22)"', + ], + { + status: 42, + signal: null, + stdout: '', + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-process-exit-top-level.mjs b/test/module-hooks/test-async-loader-hooks-process-exit-top-level.mjs new file mode 100644 index 00000000000000..a25c0013c5076d --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-process-exit-top-level.mjs @@ -0,0 +1,22 @@ +// Test that it should be fine to call process.exit from the loader thread top-level +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-exit-top-level.mjs'), + fixtures.path('empty.js'), + ], + { + status: 42, + signal: null, + stdout: '', + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-register-with-cjs.mjs b/test/module-hooks/test-async-loader-hooks-register-with-cjs.mjs new file mode 100644 index 00000000000000..8d83ee6be87816 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-register-with-cjs.mjs @@ -0,0 +1,20 @@ +// Test that `register` should work with cjs +import '../common/index.mjs'; +import assert from 'node:assert'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + fixtures.path('module-hooks/register-loader-with-cjs.cjs'), + ], + { + stdout(output) { + assert.deepStrictEqual(output.split('\n').sort(), ['hooks initialize 1', '{"default":"foo"}', ''].sort()); + }, + stderr: '', + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-register-with-import.mjs b/test/module-hooks/test-async-loader-hooks-register-with-import.mjs new file mode 100644 index 00000000000000..454eccb5c62478 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-register-with-import.mjs @@ -0,0 +1,22 @@ +// Test that `register` should work with `import` +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--import', + fixtures.fileURL('es-module-loaders/register-loader.mjs'), + '--input-type=module', + '--eval', + 'import "node:os"', + ], + { + stdout: 'resolve passthru', + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-register-with-ports.mjs b/test/module-hooks/test-async-loader-hooks-register-with-ports.mjs new file mode 100644 index 00000000000000..022760596fd929 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-register-with-ports.mjs @@ -0,0 +1,18 @@ +// Test that loader should allow communicating with loader via `register` ports +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + fixtures.path('module-hooks/register-loader-with-ports.mjs'), + ], + { + stdout: 'register undefined\nmessage initialize\nmessage resolve node:os', + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-register-with-require.mjs b/test/module-hooks/test-async-loader-hooks-register-with-require.mjs new file mode 100644 index 00000000000000..87ea43f8a66c9d --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-register-with-require.mjs @@ -0,0 +1,22 @@ +// Test that `register` should work with `require` +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--require', + fixtures.path('es-module-loaders/register-loader.cjs'), + '--input-type=module', + '--eval', + 'import "node:os";', + ], + { + stdout: 'resolve passthru\nresolve passthru', + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-register-with-url-parenturl.mjs b/test/module-hooks/test-async-loader-hooks-register-with-url-parenturl.mjs new file mode 100644 index 00000000000000..a033ab3c4d2fdc --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-register-with-url-parenturl.mjs @@ -0,0 +1,26 @@ +// Test that `register` should accept URL objects as `parentURL` +import '../common/index.mjs'; +import assert from 'node:assert'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--import', + fixtures.fileURL('es-module-loaders/register-hooks-with-current-cwd-parent-url.mjs'), + fixtures.path('es-module-loaders/register-loader-with-url-parenturl.mjs'), + ], + { + cwd: fixtures.path('es-module-loaders/'), + }, + { + stdout(output) { + assert.deepStrictEqual(output.split('\n').sort(), ['hooks initialize 1', '{"default":"foo"}'].sort()); + }, + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-remove-beforeexit-listener.mjs b/test/module-hooks/test-async-loader-hooks-remove-beforeexit-listener.mjs new file mode 100644 index 00000000000000..9a28bb59dd3164 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-remove-beforeexit-listener.mjs @@ -0,0 +1,22 @@ +// Test that it should be fine to call `process.removeAllListeners("beforeExit")`("beforeExit") from the main thread +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-delayed-async-load.mjs'), + '--input-type=module', + '--eval', + 'setInterval(() => process.removeAllListeners("beforeExit"),1).unref();await import("data:text/javascript,")', + ], + { + stdout: '', + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-require-resolve-default.mjs b/test/module-hooks/test-async-loader-hooks-require-resolve-default.mjs new file mode 100644 index 00000000000000..71e539fe118b8b --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-require-resolve-default.mjs @@ -0,0 +1,20 @@ +// Test that loader should use CJS loader to respond to `require.resolve` calls by default +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-resolve-passthru.mjs'), + fixtures.path('require-resolve.js'), + ], + { + stdout: 'resolve passthru', + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-require-resolve-opt-in.mjs b/test/module-hooks/test-async-loader-hooks-require-resolve-opt-in.mjs new file mode 100644 index 00000000000000..0cbf2fd2d942a4 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-require-resolve-opt-in.mjs @@ -0,0 +1,22 @@ +// Test that loader should use ESM loader to respond to `require.resolve` calls when opting in +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-load-commonjs-with-source.mjs'), + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-resolve-passthru.mjs'), + fixtures.path('require-resolve.js'), + ], + { + stdout: 'resolve passthru\n'.repeat(10).trim(), + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-source-maps-cjs.mjs b/test/module-hooks/test-async-loader-hooks-source-maps-cjs.mjs new file mode 100644 index 00000000000000..83bd53405d1bd9 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-source-maps-cjs.mjs @@ -0,0 +1,23 @@ +// Test that loader should support source maps in commonjs translator +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--enable-source-maps', + '--import', + fixtures.fileURL('es-module-loaders/loader-load-source-maps.mjs'), + fixtures.path('source-map/throw-on-require.js'), + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /throw-on-require\.ts:9:9/, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-throw-bigint.mjs b/test/module-hooks/test-async-loader-hooks-throw-bigint.mjs new file mode 100644 index 00000000000000..34136f81bf2193 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-throw-bigint.mjs @@ -0,0 +1,22 @@ +// Test that loader hooks should handle bigint thrown from top-level +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-throw-bigint.mjs'), + fixtures.path('empty.js'), + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /^1$/m, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-throw-boolean.mjs b/test/module-hooks/test-async-loader-hooks-throw-boolean.mjs new file mode 100644 index 00000000000000..476fad3c141535 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-throw-boolean.mjs @@ -0,0 +1,22 @@ +// Test that loader hooks should handle boolean thrown from top-level +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-throw-boolean.mjs'), + fixtures.path('empty.js'), + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /^true$/m, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-throw-empty-object.mjs b/test/module-hooks/test-async-loader-hooks-throw-empty-object.mjs new file mode 100644 index 00000000000000..ac09704c28ca6d --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-throw-empty-object.mjs @@ -0,0 +1,22 @@ +// Test that loader hooks should handle empty plain object thrown from top-level +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-throw-empty-object.mjs'), + fixtures.path('empty.js'), + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /^\{\}$/m, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-throw-error.mjs b/test/module-hooks/test-async-loader-hooks-throw-error.mjs new file mode 100644 index 00000000000000..b37ce50db1e3f4 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-throw-error.mjs @@ -0,0 +1,22 @@ +// Test that loader hooks should handle regular Error object thrown from top-level +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-throw-error.mjs'), + fixtures.path('empty.js'), + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /^Error: error message$/m, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-throw-function.mjs b/test/module-hooks/test-async-loader-hooks-throw-function.mjs new file mode 100644 index 00000000000000..bb371f727c84c7 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-throw-function.mjs @@ -0,0 +1,22 @@ +// Test that loader hooks should handle function thrown from top-level +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-throw-function.mjs'), + fixtures.path('empty.js'), + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /^\[Function: fnName\]$/m, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-throw-null.mjs b/test/module-hooks/test-async-loader-hooks-throw-null.mjs new file mode 100644 index 00000000000000..33528ff2b97560 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-throw-null.mjs @@ -0,0 +1,22 @@ +// Test that loader hooks should handle null thrown from top-level +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-throw-null.mjs'), + fixtures.path('empty.js'), + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /^null$/m, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-throw-number.mjs b/test/module-hooks/test-async-loader-hooks-throw-number.mjs new file mode 100644 index 00000000000000..da1f301089ca6c --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-throw-number.mjs @@ -0,0 +1,22 @@ +// Test that loader hooks should handle number thrown from top-level +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-throw-number.mjs'), + fixtures.path('empty.js'), + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /^1$/m, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-throw-object.mjs b/test/module-hooks/test-async-loader-hooks-throw-object.mjs new file mode 100644 index 00000000000000..8280bd0d105bb9 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-throw-object.mjs @@ -0,0 +1,22 @@ +// Test that loader hooks should handle plain object thrown from top-level +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-throw-object.mjs'), + fixtures.path('empty.js'), + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /^\{ fn: \[Function: fn\], symbol: Symbol\(symbol\), u: undefined \}$/m, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-throw-string.mjs b/test/module-hooks/test-async-loader-hooks-throw-string.mjs new file mode 100644 index 00000000000000..4ea6372e865c61 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-throw-string.mjs @@ -0,0 +1,22 @@ +// Test that loader hooks should handle string thrown from top-level +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-throw-string.mjs'), + fixtures.path('empty.js'), + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /^literal string$/m, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-throw-symbol.mjs b/test/module-hooks/test-async-loader-hooks-throw-symbol.mjs new file mode 100644 index 00000000000000..44339537ba9ce8 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-throw-symbol.mjs @@ -0,0 +1,20 @@ +// Test that loader hooks should handle symbol thrown from top-level +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-throw-symbol.mjs'), + fixtures.path('empty.js'), + ], + { + status: 1, + signal: null, + stdout: '', // Throwing a symbol doesn't produce any output + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-throw-undefined.mjs b/test/module-hooks/test-async-loader-hooks-throw-undefined.mjs new file mode 100644 index 00000000000000..aff5292a857f59 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-throw-undefined.mjs @@ -0,0 +1,22 @@ +// Test that loader hooks should handle undefined thrown from top-level +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-throw-undefined.mjs'), + fixtures.path('empty.js'), + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /^undefined$/m, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-use-hooks-require-esm.mjs b/test/module-hooks/test-async-loader-hooks-use-hooks-require-esm.mjs new file mode 100644 index 00000000000000..a86b0607246fa9 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-use-hooks-require-esm.mjs @@ -0,0 +1,20 @@ +// Test that loader should use hooks for require of ESM +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-experimental-require-module', + '--import', + fixtures.fileURL('es-module-loaders/builtin-named-exports.mjs'), + fixtures.path('es-modules/require-esm-throws-with-loaders.js'), + ], + { + stdout: '', + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-with-worker-permission-allowed.mjs b/test/module-hooks/test-async-loader-hooks-with-worker-permission-allowed.mjs new file mode 100644 index 00000000000000..9be7e8c39600a5 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-with-worker-permission-allowed.mjs @@ -0,0 +1,24 @@ +// Test that loader hooks should allow spawning workers when allowed by CLI flags +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndAssert } from '../common/child_process.js'; + +spawnSyncAndAssert( + execPath, + [ + '--no-warnings', + '--permission', + '--allow-worker', + '--allow-fs-read', + '*', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-worker-spawn.mjs'), + fixtures.path('es-modules/esm-top-level-await.mjs'), + ], + { + stdout: /^1\n2$/, + stderr: '', + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-with-worker-permission-restricted.mjs b/test/module-hooks/test-async-loader-hooks-with-worker-permission-restricted.mjs new file mode 100644 index 00000000000000..fe76f2f5842c00 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-with-worker-permission-restricted.mjs @@ -0,0 +1,25 @@ +// Test that loader hooks should not allow spawning workers if restricted by CLI flags +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--permission', + '--allow-fs-read', + '*', + '--experimental-loader', + fixtures.fileURL('es-module-loaders/loader-worker-spawn.mjs'), + fixtures.path('es-modules/esm-top-level-await.mjs'), + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /code: 'ERR_ACCESS_DENIED'/, + trim: true, + }, +); diff --git a/test/module-hooks/test-async-loader-hooks-without-worker-permission.mjs b/test/module-hooks/test-async-loader-hooks-without-worker-permission.mjs new file mode 100644 index 00000000000000..21589234144db1 --- /dev/null +++ b/test/module-hooks/test-async-loader-hooks-without-worker-permission.mjs @@ -0,0 +1,25 @@ +// Test that loader hooks should not work without worker permission +import '../common/index.mjs'; +import { execPath } from 'node:process'; +import fixtures from '../common/fixtures.js'; +import { spawnSyncAndExit } from '../common/child_process.js'; + +spawnSyncAndExit( + execPath, + [ + '--no-warnings', + '--permission', + '--allow-fs-read', + '*', + '--experimental-loader', + fixtures.fileURL('empty.js'), + fixtures.path('es-modules/esm-top-level-await.mjs'), + ], + { + status: 1, + signal: null, + stdout: '', + stderr: /Error: Access to this API has been restricted/, + trim: true, + }, +); diff --git a/test/node-api/test_callback_scope/test-async-hooks.js b/test/node-api/test_callback_scope/test-async-hooks.js index bb5927236f6765..7c12e25848d0f9 100644 --- a/test/node-api/test_callback_scope/test-async-hooks.js +++ b/test/node-api/test_callback_scope/test-async-hooks.js @@ -5,7 +5,7 @@ const assert = require('assert'); const async_hooks = require('async_hooks'); // The async_hook that we enable would register the process.emitWarning() -// call from loading the N-API addon as asynchronous activity because +// call from loading the Node-API addon as asynchronous activity because // it contains a process.nextTick() call. Monkey patch it to be a no-op // before we load the addon in order to avoid this. process.emitWarning = () => {}; diff --git a/test/node-api/test_fatal/test_fatal.c b/test/node-api/test_fatal/test_fatal.c index aebbda76888f4f..6d3f345c315829 100644 --- a/test/node-api/test_fatal/test_fatal.c +++ b/test/node-api/test_fatal/test_fatal.c @@ -2,7 +2,7 @@ // on a threading library for a new project it bears remembering that in the // future libuv may introduce API changes which may render it non-ABI-stable, // which, in turn, may affect the ABI stability of the project despite its use -// of N-API. +// of Node-API. #include #include #include "../../js-native-api/common.h" diff --git a/test/node-api/test_threadsafe_function/binding.c b/test/node-api/test_threadsafe_function/binding.c index 53b1cb3056370f..5a2e9355719685 100644 --- a/test/node-api/test_threadsafe_function/binding.c +++ b/test/node-api/test_threadsafe_function/binding.c @@ -2,7 +2,7 @@ // on a threading library for a new project it bears remembering that in the // future libuv may introduce API changes which may render it non-ABI-stable, // which, in turn, may affect the ABI stability of the project despite its use -// of N-API. +// of Node-API. #include #include #include "../../js-native-api/common.h" @@ -207,8 +207,11 @@ static napi_value StartThreadInternal(napi_env env, NODE_API_ASSERT(env, (ts_fn == NULL), "Existing thread-safe function"); napi_value async_name; - NODE_API_CALL(env, napi_create_string_utf8(env, - "N-API Thread-safe Function Test", NAPI_AUTO_LENGTH, &async_name)); + NODE_API_CALL(env, + napi_create_string_utf8(env, + "Node-API Thread-safe Function Test", + NAPI_AUTO_LENGTH, + &async_name)); NODE_API_CALL(env, napi_get_value_uint32(env, argv[3], &ts_info.max_queue_size)); NODE_API_CALL(env, napi_create_threadsafe_function(env, diff --git a/test/node-api/test_worker_buffer_callback/test_worker_buffer_callback.c b/test/node-api/test_worker_buffer_callback/test_worker_buffer_callback.c index bf4a101763d091..34307b44e55483 100644 --- a/test/node-api/test_worker_buffer_callback/test_worker_buffer_callback.c +++ b/test/node-api/test_worker_buffer_callback/test_worker_buffer_callback.c @@ -25,9 +25,9 @@ NAPI_MODULE_INIT() { NODE_API_CALL(env, napi_define_properties( env, exports, sizeof(properties) / sizeof(*properties), properties)); - // This is a slight variation on the non-N-API test: We create an ArrayBuffer - // rather than a Node.js Buffer, since testing the latter would only test - // the same code paths and not the ones specific to N-API. + // This is a slight variation on the non-Node-API test: We create an + // ArrayBuffer rather than a Node.js Buffer, since testing the latter would + // only test the same code paths and not the ones specific to Node-API. napi_value buffer; char* data = malloc(sizeof(char)); diff --git a/test/node-api/test_worker_terminate/test.js b/test/node-api/test_worker_terminate/test.js index eefb974af5a669..138acdbb59b9dc 100644 --- a/test/node-api/test_worker_terminate/test.js +++ b/test/node-api/test_worker_terminate/test.js @@ -5,7 +5,7 @@ const { Worker, isMainThread, workerData } = require('worker_threads'); if (isMainThread) { // Load the addon in the main thread first. - // This checks that N-API addons can be loaded from multiple contexts + // This checks that Node-API addons can be loaded from multiple contexts // when they are not loaded through NAPI_MODULE(). require(`./build/${common.buildType}/test_worker_terminate`);