From 16e46dca91bbdf859fc0cbbbf24c111d97f80b0d Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 28 Jan 2026 16:02:55 +0100 Subject: [PATCH 01/64] build,test: add tests for binary linked with shared libnode This adds tests to ensure the V8 parts (v8, libplatform, cppgc) in shared libnode works correctly. PR-URL: https://github.com/nodejs/node/pull/61463 Refs: https://github.com/nodejs/node/pull/61144 Reviewed-By: Antoine du Hamel Reviewed-By: Richard Lau --- node.gyp | 55 ++++++++++++++++++++ test/common/index.js | 11 ++++ test/embedding/shared_embedtest.cc | 60 ++++++++++++++++++++++ test/embedding/test-embedding.js | 10 +--- test/embedding/test-shared-embedding-v8.js | 25 +++++++++ 5 files changed, 152 insertions(+), 9 deletions(-) create mode 100644 test/embedding/shared_embedtest.cc create mode 100644 test/embedding/test-shared-embedding-v8.js diff --git a/node.gyp b/node.gyp index 8895bcd2917bdd..632c1f109e3cc6 100644 --- a/node.gyp +++ b/node.gyp @@ -1341,6 +1341,61 @@ ], }, # embedtest + { + 'target_name': 'shared_embedtest', + 'type': 'executable', + + 'dependencies': [ + '<(node_lib_target_name)', + ], + + # Don't depend on node.gypi - it otherwise links to + # the static libraries and resolve symbols at build time. + 'include_dirs': [ + 'deps/v8/include', + ], + + 'sources': [ + 'test/embedding/shared_embedtest.cc', + ], + 'conditions': [ + [ 'node_shared=="true"', { + 'defines': [ + 'USING_V8_SHARED', + 'USING_V8_PLATFORM_SHARED', + ], + 'defines!': [ + 'BUILDING_V8_PLATFORM_SHARED=1', + 'BUILDING_V8_SHARED=1', + ], + }, { + # Only test shared embedding when Node is built as shared library. + 'type': 'none', + }], + # Only test platforms known to work. + ['OS not in "mac win linux"', { + 'type': 'none', + }], + ['OS=="win"', { + 'libraries': [ + 'Dbghelp.lib', + 'winmm.lib', + 'Ws2_32.lib', + ], + }], + ['OS=="mac"', { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ '-Wl,-rpath,@loader_path', ], + } + }], + ['OS=="linux"', { + 'ldflags': [ + '-Wl,-rpath,\\$$ORIGIN' + ], + }], + ], + }, # shared_embedtest + { 'target_name': 'overlapped-checker', 'type': 'executable', diff --git a/test/common/index.js b/test/common/index.js index d4f4fab83767a6..dfc110e50f6d90 100755 --- a/test/common/index.js +++ b/test/common/index.js @@ -51,6 +51,8 @@ if (isMainThread) const noop = () => {}; +// Whether the executable is linked against the shared library i.e. libnode. +const usesSharedLibrary = process.config.variables.node_shared; const hasCrypto = Boolean(process.versions.openssl) && !process.env.NODE_SKIP_CRYPTO; @@ -923,6 +925,13 @@ function sleepSync(ms) { Atomics.wait(i32, 0, 0, ms); } +function resolveBuiltBinary(binary) { + if (isWindows) { + binary += '.exe'; + } + return path.join(path.dirname(process.execPath), binary); +} + const common = { allowGlobals, buildType, @@ -966,6 +975,7 @@ const common = { printSkipMessage, pwdCommand, requireNoPackageJSONAbove, + resolveBuiltBinary, runWithInvalidFD, skip, skipIf32Bits, @@ -974,6 +984,7 @@ const common = { skipIfSQLiteMissing, spawnPromisified, sleepSync, + usesSharedLibrary, get enoughTestMem() { return require('os').totalmem() > 0x70000000; /* 1.75 Gb */ diff --git a/test/embedding/shared_embedtest.cc b/test/embedding/shared_embedtest.cc new file mode 100644 index 00000000000000..f071f43c5b76a2 --- /dev/null +++ b/test/embedding/shared_embedtest.cc @@ -0,0 +1,60 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +class Wrappable : public v8::Object::Wrappable { + public: + void Trace(cppgc::Visitor* visitor) const override { + v8::Object::Wrappable::Trace(visitor); + } +}; + +int main(int argc, char* argv[]) { + std::unique_ptr platform = v8::platform::NewDefaultPlatform(); + v8::V8::InitializePlatform(platform.get()); + cppgc::InitializeProcess(platform->GetPageAllocator()); + v8::V8::Initialize(); + + auto heap = v8::CppHeap::Create(platform.get(), v8::CppHeapCreateParams{{}}); + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = + v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + create_params.cpp_heap = heap.release(); + + v8::Isolate* isolate = v8::Isolate::New(create_params); + { + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + v8::Local context = v8::Context::New(isolate); + v8::Context::Scope context_scope(context); + + v8::Local obj = v8::Object::New(isolate); + Wrappable* wrappable = cppgc::MakeGarbageCollected( + isolate->GetCppHeap()->GetAllocationHandle()); + v8::Object::Wrap( + isolate, obj, wrappable); + v8::Local source = + v8::String::NewFromUtf8Literal(isolate, "'Hello' + ', World!'"); + v8::Local script = + v8::Script::Compile(context, source).ToLocalChecked(); + v8::Local result = script->Run(context).ToLocalChecked(); + v8::String::Utf8Value utf8(isolate, result); + printf("%s\n", *utf8); + } + + isolate->Dispose(); + cppgc::ShutdownProcess(); + v8::V8::Dispose(); + v8::V8::DisposePlatform(); + delete create_params.array_buffer_allocator; + + return 0; +} diff --git a/test/embedding/test-embedding.js b/test/embedding/test-embedding.js index 49706079fd8b6a..79d79079b4b8c6 100644 --- a/test/embedding/test-embedding.js +++ b/test/embedding/test-embedding.js @@ -8,7 +8,6 @@ const { spawnSyncAndExit, spawnSyncAndExitWithoutError, } = require('../common/child_process'); -const path = require('path'); const fs = require('fs'); const os = require('os'); @@ -16,14 +15,7 @@ tmpdir.refresh(); common.allowGlobals(global.require); common.allowGlobals(global.embedVars); -function resolveBuiltBinary(binary) { - if (common.isWindows) { - binary += '.exe'; - } - return path.join(path.dirname(process.execPath), binary); -} - -const binary = resolveBuiltBinary('embedtest'); +const binary = common.resolveBuiltBinary('embedtest'); spawnSyncAndAssert( binary, diff --git a/test/embedding/test-shared-embedding-v8.js b/test/embedding/test-shared-embedding-v8.js new file mode 100644 index 00000000000000..4d03a9d4b480d6 --- /dev/null +++ b/test/embedding/test-shared-embedding-v8.js @@ -0,0 +1,25 @@ +'use strict'; + +// This tests the V8 parts in the shared library work correctly. +// TODO(joyeecheung): also test that the Node.js parts work correctly, +// which can be done in embedtest just built in shared library mode. + +const common = require('../common'); + +if (!common.usesSharedLibrary) { + common.skip('Only tests builds linking against Node.js shared library'); +} + +const { spawnSyncAndAssert } = require('../common/child_process'); +const fs = require('fs'); + +const binary = common.resolveBuiltBinary('shared_embedtest'); + +if (!fs.existsSync(binary)) { + common.skip('shared_embedtest binary not built'); +} + +spawnSyncAndAssert(binary, { + trim: true, + stdout: 'Hello, World!', +}); From d5abe80800bb15919428f581892b7664b06bb35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guilherme=20Ara=C3=BAjo?= Date: Wed, 28 Jan 2026 23:22:05 -0300 Subject: [PATCH 02/64] sqlite: reserve vectors space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/61540 Reviewed-By: René Reviewed-By: Edy Silva Reviewed-By: Colin Ihrig Reviewed-By: Rafael Gonzaga --- src/node_sqlite.cc | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index 1cbb0aefad9d13..c144527df13c37 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -322,6 +322,8 @@ class CustomAggregate { auto recv = Undefined(isolate); LocalVector js_argv(isolate); + js_argv.reserve(argc + 1); + js_argv.emplace_back(Local::New(isolate, agg->value)); for (int i = 0; i < argc; ++i) { @@ -625,6 +627,7 @@ void UserDefinedFunction::xFunc(sqlite3_context* ctx, auto recv = Undefined(isolate); auto fn = self->fn_.Get(isolate); LocalVector js_argv(isolate); + js_argv.reserve(argc); for (int i = 0; i < argc; ++i) { sqlite3_value* value = argv[i]; @@ -2063,18 +2066,16 @@ int DatabaseSync::AuthorizerCallback(void* user_data, CHECK(cb->IsFunction()); Local callback = cb.As(); - LocalVector js_argv(isolate); - // Convert SQLite authorizer parameters to JavaScript values - js_argv.emplace_back(Integer::New(isolate, action_code)); - js_argv.emplace_back( - NullableSQLiteStringToValue(isolate, param1).ToLocalChecked()); - js_argv.emplace_back( - NullableSQLiteStringToValue(isolate, param2).ToLocalChecked()); - js_argv.emplace_back( - NullableSQLiteStringToValue(isolate, param3).ToLocalChecked()); - js_argv.emplace_back( - NullableSQLiteStringToValue(isolate, param4).ToLocalChecked()); + LocalVector js_argv( + isolate, + { + Integer::New(isolate, action_code), + NullableSQLiteStringToValue(isolate, param1).ToLocalChecked(), + NullableSQLiteStringToValue(isolate, param2).ToLocalChecked(), + NullableSQLiteStringToValue(isolate, param3).ToLocalChecked(), + NullableSQLiteStringToValue(isolate, param4).ToLocalChecked(), + }); MaybeLocal retval = callback->Call( context, Undefined(isolate), js_argv.size(), js_argv.data()); From a6012fcddfc52945276123f7058ab4b2fa460e8e Mon Sep 17 00:00:00 2001 From: Ethan Arrowood Date: Thu, 29 Jan 2026 01:18:11 -0700 Subject: [PATCH 03/64] test_runner: add env option to run function Support an `env` option that is passed to the underlying child_process. Fixes: https://github.com/nodejs/node/issues/60709 PR-URL: https://github.com/nodejs/node/pull/61367 Reviewed-By: Aviv Keller Reviewed-By: Moshe Atlow Reviewed-By: Chemi Atlow Reviewed-By: Pietro Marchini Reviewed-By: Matteo Collina --- doc/api/test.md | 6 ++++++ lib/internal/test_runner/runner.js | 12 +++++++++++- test/fixtures/test-runner/process-env.js | 7 +++++++ test/parallel/test-runner-run.mjs | 23 +++++++++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/test-runner/process-env.js diff --git a/doc/api/test.md b/doc/api/test.md index 10ffa84a0d4a7b..0709231f40d2f0 100644 --- a/doc/api/test.md +++ b/doc/api/test.md @@ -1435,6 +1435,9 @@ added: - v18.9.0 - v16.19.0 changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/61367 + description: Add the `env` option. - version: v24.7.0 pr-url: https://github.com/nodejs/node/pull/59443 description: Added a rerunFailuresFilePath option. @@ -1555,6 +1558,9 @@ changes: * `functionCoverage` {number} Require a minimum percent of covered functions. If code coverage does not reach the threshold specified, the process will exit with code `1`. **Default:** `0`. + * `env` {Object} Specify environment variables to be passed along to the test process. + This options is not compatible with `isolation='none'`. These variables will override + those from the main process, and are not merged with `process.env`. * Returns: {TestsStream} **Note:** `shard` is used to horizontally parallelize test running across diff --git a/lib/internal/test_runner/runner.js b/lib/internal/test_runner/runner.js index a9b3cfb93c8abb..f5b6907280852b 100644 --- a/lib/internal/test_runner/runner.js +++ b/lib/internal/test_runner/runner.js @@ -403,7 +403,7 @@ function runTestFile(path, filesWatcher, opts) { const subtest = opts.root.createSubtest(FileTest, testPath, testOpts, async (t) => { const args = getRunArgs(path, opts); const stdio = ['pipe', 'pipe', 'pipe']; - const env = { __proto__: null, ...process.env, NODE_TEST_CONTEXT: 'child-v8' }; + const env = { __proto__: null, NODE_TEST_CONTEXT: 'child-v8', ...(opts.env || process.env) }; if (watchMode) { stdio.push('ipc'); env.WATCH_REPORT_DEPENDENCIES = '1'; @@ -623,6 +623,7 @@ function run(options = kEmptyObject) { argv = [], cwd = process.cwd(), rerunFailuresFilePath, + env, } = options; if (files != null) { @@ -731,6 +732,14 @@ function run(options = kEmptyObject) { validatePath(globalSetupPath, 'options.globalSetupPath'); } + if (env != null) { + validateObject(env); + + if (isolation === 'none') { + throw new ERR_INVALID_ARG_VALUE('options.env', env, 'is not supported with isolation=\'none\''); + } + } + const rootTestOptions = { __proto__: null, concurrency, timeout, signal }; const globalOptions = { __proto__: null, @@ -776,6 +785,7 @@ function run(options = kEmptyObject) { argv, execArgv, rerunFailuresFilePath, + env, }; if (isolation === 'process') { diff --git a/test/fixtures/test-runner/process-env.js b/test/fixtures/test-runner/process-env.js new file mode 100644 index 00000000000000..6f936de450d87e --- /dev/null +++ b/test/fixtures/test-runner/process-env.js @@ -0,0 +1,7 @@ +const { test } = require('node:test'); + +test('process.env is correct', (t) => { + t.assert.strictEqual(process.env.ABC, undefined, 'main process env var should be undefined'); + t.assert.strictEqual(process.env.NODE_TEST_CONTEXT, 'child-v8', 'NODE_TEST_CONTEXT should be set by run()'); + t.assert.strictEqual(process.env.FOOBAR, 'FUZZBUZZ', 'specified env var should be defined'); +}); diff --git a/test/parallel/test-runner-run.mjs b/test/parallel/test-runner-run.mjs index 7ddd8c1dcd83e5..904bcd7a307ea0 100644 --- a/test/parallel/test-runner-run.mjs +++ b/test/parallel/test-runner-run.mjs @@ -650,6 +650,29 @@ describe('require(\'node:test\').run', { concurrency: true }, () => { }); }); +describe('env', () => { + it('should allow env variables to be configured', async () => { + // Need to inherit some process.env variables so it runs reliably across different environments. + const env = { ...process.env, FOOBAR: 'FUZZBUZZ' }; + // Set a variable on main process env and test it does not exist within test env. + process.env.ABC = 'XYZ'; + + const stream = run({ files: [join(testFixtures, 'process-env.js')], env }); + stream.on('test:fail', common.mustNotCall()); + stream.on('test:pass', common.mustCall(1)); + // eslint-disable-next-line no-unused-vars + for await (const _ of stream); + delete process.env.ABC; + }); + + it('should throw error when env is specified with isolation=none', async () => { + assert.throws(() => run({ env: { foo: 'bar' }, isolation: 'none' }), { + code: 'ERR_INVALID_ARG_VALUE', + message: /The property 'options\.env' is not supported with isolation='none'\. Received { foo: 'bar' }/ + }); + }); +}); + describe('forceExit', () => { it('throws for non-boolean values', () => { [Symbol(), {}, 0, 1, '1', Promise.resolve([])].forEach((forceExit) => { From fb7acf0e88ee69230f621dac64d3d5c8ed923228 Mon Sep 17 00:00:00 2001 From: wantaek Date: Thu, 29 Jan 2026 22:24:39 +0900 Subject: [PATCH 04/64] stream: add bytes() method to stream/consumers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add bytes() method to get Uint8Array from streams - Add tests for bytes() method in PassThrough and ObjectMode scenarios - Update documentation Fixes: https://github.com/nodejs/node/issues/59542 PR-URL: https://github.com/nodejs/node/pull/60426 Reviewed-By: Ethan Arrowood Reviewed-By: René Reviewed-By: Jake Yuesong Li Reviewed-By: Mattias Buelens Reviewed-By: Benjamin Gruenbaum Reviewed-By: Gürgün Dayıoğlu --- doc/api/webstreams.md | 37 ++++++++++++++++++++++++++ lib/stream/consumers.js | 10 +++++++ test/parallel/test-stream-consumers.js | 31 +++++++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/doc/api/webstreams.md b/doc/api/webstreams.md index 905c0da6e9a8cf..75e0c3a436b12e 100644 --- a/doc/api/webstreams.md +++ b/doc/api/webstreams.md @@ -1690,6 +1690,43 @@ buffer(readable).then((data) => { }); ``` +#### `streamConsumers.bytes(stream)` + + + +* `stream` {ReadableStream|stream.Readable|AsyncIterator} +* Returns: {Promise} Fulfills with a {Uint8Array} containing the full + contents of the stream. + +```mjs +import { bytes } from 'node:stream/consumers'; +import { Readable } from 'node:stream'; +import { Buffer } from 'node:buffer'; + +const dataBuffer = Buffer.from('hello world from consumers!'); + +const readable = Readable.from(dataBuffer); +const data = await bytes(readable); +console.log(`from readable: ${data.length}`); +// Prints: from readable: 27 +``` + +```cjs +const { bytes } = require('node:stream/consumers'); +const { Readable } = require('node:stream'); +const { Buffer } = require('node:buffer'); + +const dataBuffer = Buffer.from('hello world from consumers!'); + +const readable = Readable.from(dataBuffer); +bytes(readable).then((data) => { + console.log(`from readable: ${data.length}`); + // Prints: from readable: 27 +}); +``` + #### `streamConsumers.json(stream)` -* `urlObject` {Object|string} A URL object (as returned by `url.parse()` or - constructed otherwise). If a string, it is converted to an object by passing - it to `url.parse()`. +* `urlObject` {Object} A URL object (as returned by `url.parse()` or + constructed otherwise). The `url.format()` method returns a formatted URL string derived from `urlObject`. @@ -1816,6 +1815,48 @@ An automated migration is available ([source](https://github.com/nodejs/userland npx codemod@latest @nodejs/node-url-to-whatwg-url ``` +### `url.format(urlString)` + + + +> Stability: 0 - Deprecated: Use the WHATWG URL API instead. + +* `urlString` {string} A string that will be passed to `url.parse()` and then + formatted. + +`url.format(urlString)` is shorthand for `url.format(url.parse(urlString))`. + +Because it invokes the deprecated [`url.parse()`][], passing a string argument +to `url.format()` is itself deprecated. + +Canonicalizing a URL string can be performed using the WHATWG URL API, by +constructing a new URL object and calling [`url.toString()`][]. + +```mjs +import { URL } from 'node:url'; + +const unformatted = 'http://[fe80:0:0:0:0:0:0:1]:/a/b?a=b#abc'; +const formatted = new URL(unformatted).toString(); + +console.log(formatted); // Prints: http://[fe80::1]/a/b?a=b#abc +``` + +```cjs +const { URL } = require('node:url'); + +const unformatted = 'http://[fe80:0:0:0:0:0:0:1]:/a/b?a=b#abc'; +const formatted = new URL(unformatted).toString(); + +console.log(formatted); // Prints: http://[fe80::1]/a/b?a=b#abc +``` + ### `url.parse(urlString[, parseQueryString[, slashesDenoteHost]])` - - - -#### Warning: binding inspector to a public IP:port combination is insecure - -Binding the inspector to a public IP (including `0.0.0.0`) with an open port is -insecure, as it allows external hosts to connect to the inspector and perform -a [remote code execution][] attack. - -If specifying a host, make sure that either: - -* The host is not accessible from public networks. -* A firewall disallows unwanted connections on the port. - -**More specifically, `--inspect=0.0.0.0` is insecure if the port (`9229` by -default) is not firewall-protected.** - -See the [debugging security implications][] section for more information. - ### `--inspect-brk[=[host:]port]` + + + +#### Warning: binding inspector to a public IP:port combination is insecure + +Binding the inspector to a public IP (including `0.0.0.0`) with an open port is +insecure, as it allows external hosts to connect to the inspector and perform +a [remote code execution][] attack. + +If specifying a host, make sure that either: + +* The host is not accessible from public networks. +* A firewall disallows unwanted connections on the port. + +**More specifically, `--inspect=0.0.0.0` is insecure if the port (`9229` by +default) is not firewall-protected.** + +See the [debugging security implications][] section for more information. + ### `-i`, `--interactive` + [6.1.7 Array Index]: https://tc39.es/ecma262/#integer-index [Addons]: addons.md @@ -1309,10 +1309,10 @@ resolution for ESM specifiers is [commonjs-extension-resolution-loader][]. [`process.dlopen`]: process.md#processdlopenmodule-filename-flags [`require(esm)`]: modules.md#loading-ecmascript-modules-using-require [`url.fileURLToPath()`]: url.md#urlfileurltopathurl-options -[cjs-module-lexer]: https://github.com/nodejs/cjs-module-lexer/tree/2.0.0 [commonjs-extension-resolution-loader]: https://github.com/nodejs/loaders-test/tree/main/commonjs-extension-resolution-loader [custom https loader]: module.md#import-from-https [import.meta.resolve]: #importmetaresolvespecifier +[merve]: https://github.com/anonrig/merve/tree/v1.0.0 [percent-encoded]: url.md#percent-encoding-in-urls [special scheme]: https://url.spec.whatwg.org/#special-scheme [status code]: process.md#exit-codes diff --git a/doc/api/packages.md b/doc/api/packages.md index b14c107e14fcca..4aa1dfbd9a6b57 100644 --- a/doc/api/packages.md +++ b/doc/api/packages.md @@ -187,7 +187,7 @@ or `import()` expressions (available in both CommonJS and ES Modules): * Any other file extensions will result in a [`ERR_UNKNOWN_FILE_EXTENSION`][] error. Additional file extensions can be facilitated via [customization hooks][]. * `import`/`import()` can be used to load JavaScript [CommonJS modules][commonjs]. - Such modules are passed through the `cjs-module-lexer` to try to identify named + Such modules are passed through [merve][] to try to identify named exports, which are available if they can be determined through static analysis. Regardless of how a module is requested, the resolution and loading process can be customized @@ -1184,6 +1184,7 @@ This field defines [subpath imports][] for the current package. [folders as modules]: modules.md#folders-as-modules [import maps]: https://github.com/WICG/import-maps [load ECMAScript modules from CommonJS modules]: modules.md#loading-ecmascript-modules-using-require +[merve]: https://github.com/anonrig/merve [packages folder mapping]: https://github.com/WICG/import-maps#packages-via-trailing-slashes [self-reference]: #self-referencing-a-package-using-its-name [subpath exports]: #subpath-exports diff --git a/doc/api/process.md b/doc/api/process.md index 5ab94c2cf0e8d2..5d671421ef3241 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -4526,32 +4526,35 @@ console.log(versions); Will generate an object similar to: ```console -{ node: '23.0.0', - acorn: '8.11.3', - ada: '2.7.8', - ares: '1.28.1', - base64: '0.5.2', - brotli: '1.1.0', - cjs_module_lexer: '1.2.2', - cldr: '45.0', - icu: '75.1', - llhttp: '9.2.1', - modules: '127', - napi: '9', - nghttp2: '1.61.0', - nghttp3: '0.7.0', - ngtcp2: '1.3.0', - openssl: '3.0.13+quic', - simdjson: '3.8.0', - simdutf: '5.2.4', - sqlite: '3.46.0', - tz: '2024a', - undici: '6.13.0', - unicode: '15.1', - uv: '1.48.0', - uvwasi: '0.0.20', - v8: '12.4.254.14-node.11', - zlib: '1.3.0.1-motley-7d77fb7' } +{ node: '26.0.0-pre', + acorn: '8.15.0', + ada: '3.4.1', + amaro: '1.1.5', + ares: '1.34.6', + brotli: '1.2.0', + merve: '1.0.0', + cldr: '48.0', + icu: '78.2', + llhttp: '9.3.0', + modules: '144', + napi: '10', + nbytes: '0.1.1', + ncrypto: '0.0.1', + nghttp2: '1.68.0', + nghttp3: '', + ngtcp2: '', + openssl: '3.5.4', + simdjson: '4.2.4', + simdutf: '7.3.3', + sqlite: '3.51.2', + tz: '2025c', + undici: '7.18.2', + unicode: '17.0', + uv: '1.51.0', + uvwasi: '0.0.23', + v8: '14.3.127.18-node.10', + zlib: '1.3.1-e00f703', + zstd: '1.5.7' } ``` ## Exit codes diff --git a/doc/contributing/maintaining/maintaining-cjs-module-lexer.md b/doc/contributing/maintaining/maintaining-cjs-module-lexer.md deleted file mode 100644 index a657f6b190d7ea..00000000000000 --- a/doc/contributing/maintaining/maintaining-cjs-module-lexer.md +++ /dev/null @@ -1,59 +0,0 @@ -# Maintaining cjs-module-lexer - -The [cjs-module-lexer](https://github.com/nodejs/node/tree/HEAD/deps/cjs-module-lexer) -dependency is used within the Node.js ESM implementation to detect the -named exports of a CommonJS module. - -It is used within -[`node:internal/modules/esm/translators`](https://github.com/nodejs/node/blob/HEAD/lib/internal/modules/esm/translators.js) -in which both `internal/deps/cjs-module-lexer/lexer` and -`internal/deps/cjs-module-lexer/dist/lexer` are required and used. - -`internal/deps/cjs-module-lexer/lexer` -is a regular JavaScript implementation that is -used when WebAssembly is not available on a platform. -`internal/deps/cjs-module-lexer/dist/lexer` is a faster -implementation using WebAssembly which is generated from a -C based implementation. These two paths -resolve to the files in `deps/cjs-module-lexer` due to their -inclusion in the `deps_files` entry in -[node.gyp](https://github.com/nodejs/node/blob/main/node.gyp). - -The two different versions of lexer.js are maintained in the -[nodejs/cjs-module-lexer][] project. - -In order to update the Node.js dependencies to use to a newer version -of cjs-module-lexer, complete the following steps: - -* Clone [nodejs/cjs-module-lexer][] - and check out the version that you want Node.js to use. -* Follow the WASM build steps outlined in - [wasm-build-steps](https://github.com/nodejs/cjs-module-lexer#wasm-build-steps). - This will generate the WASM based dist/lexer.js file. -* Preserving the same directory structure, copy the following files over - to `deps/cjs-module-lexer` directory where you have checked out Node.js. - -```text -├── CHANGELOG.md -├── dist -│ ├── lexer.js -│ └── lexer.mjs -├── lexer.js -├── LICENSE -├── package.json -└── README.md -``` - -* Update the link to the cjs-module-lexer in the list at the end of - [doc/api/esm.md](../../api/esm.md) - to point to the updated version. - -* Create a PR, adding the files in the deps/cjs-module-lexer that - were modified. - -If updates are needed to cjs-module-lexer for Node.js, first PR -those updates into -[nodejs/cjs-module-lexer][], -request a release and then pull in the updated version once available. - -[nodejs/cjs-module-lexer]: https://github.com/nodejs/cjs-module-lexer diff --git a/doc/contributing/maintaining/maintaining-dependencies.md b/doc/contributing/maintaining/maintaining-dependencies.md index 7cc3e483d268a8..83fa827e1443b5 100644 --- a/doc/contributing/maintaining/maintaining-dependencies.md +++ b/doc/contributing/maintaining/maintaining-dependencies.md @@ -14,7 +14,7 @@ This a list of all the dependencies: * [base64][] * [brotli][] * [c-ares][] -* [cjs-module-lexer][] +* [merve][] * [corepack][] * [googletest][] * [histogram][] @@ -238,12 +238,12 @@ used for the homonym generic-purpose lossless compression algorithm. The [c-ares](https://github.com/c-ares/c-ares) is a C library for asynchronous DNS requests. -### cjs-module-lexer +### merve -The [cjs-module-lexer](https://github.com/nodejs/node/tree/HEAD/deps/cjs-module-lexer) +The [merve](https://github.com/nodejs/node/tree/HEAD/deps/merve) dependency is used within the Node.js ESM implementation to detect the named exports of a CommonJS module. -See [maintaining-cjs-module-lexer][] for more information. +See [maintaining-merve][] for more information. ### corepack @@ -406,7 +406,6 @@ according to [RFC 8878](https://datatracker.ietf.org/doc/html/rfc8878). [base64]: #base64 [brotli]: #brotli [c-ares]: #c-ares -[cjs-module-lexer]: #cjs-module-lexer [corepack]: #corepack [dependency-update-action]: ../../../.github/workflows/tools.yml [googletest]: #googletest @@ -416,11 +415,12 @@ according to [RFC 8878](https://datatracker.ietf.org/doc/html/rfc8878). [libuv]: #libuv [llhttp]: #llhttp [maintaining-V8]: ./maintaining-V8.md -[maintaining-cjs-module-lexer]: ./maintaining-cjs-module-lexer.md [maintaining-http]: ./maintaining-http.md [maintaining-icu]: ./maintaining-icu.md +[maintaining-merve]: ./maintaining-merve.md [maintaining-openssl]: ./maintaining-openssl.md [maintaining-web-assembly]: ./maintaining-web-assembly.md +[merve]: #merve [minimatch]: #minimatch [nghttp2]: #nghttp2 [nghttp3]: #nghttp3 diff --git a/doc/contributing/maintaining/maintaining-merve.md b/doc/contributing/maintaining/maintaining-merve.md new file mode 100644 index 00000000000000..66b9d61e5b1311 --- /dev/null +++ b/doc/contributing/maintaining/maintaining-merve.md @@ -0,0 +1,32 @@ +# Maintaining merve + +The [merve](https://github.com/nodejs/node/tree/HEAD/deps/merve) +dependency is used within the Node.js ESM implementation to detect the +named exports of a CommonJS module. + +It is used within +[`node:internal/modules/esm/translators`](https://github.com/nodejs/node/blob/HEAD/lib/internal/modules/esm/translators.js) +where it is exposed via an internal binding. + +## Updating merve + +The `tools/dep_updaters/update-merve.sh` script automates the update of the +merve dependency. It fetches the latest release from GitHub and updates the +files in the `deps/merve` directory. + +To update merve manually: + +* Check the [merve releases][] for a new version. +* Download the latest single-header release. +* Replace the files in `deps/merve` (preserving `merve.gyp`). +* Update the link to merve in the list at the end of + [doc/api/esm.md](../../api/esm.md) + to point to the updated version. +* Create a PR adding the files in the deps/merve that were modified. + +If updates are needed to merve for Node.js, first PR those updates into +[anonrig/merve][], +request a release and then pull in the updated version once available. + +[anonrig/merve]: https://github.com/anonrig/merve +[merve releases]: https://github.com/anonrig/merve/releases diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index 965adcca20bf72..d6c96996a900da 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -7,7 +7,6 @@ const { ObjectAssign, ObjectPrototypeHasOwnProperty, ReflectApply, - SafeArrayIterator, SafeMap, SafeSet, SafeWeakMap, @@ -70,18 +69,7 @@ function getSource(url) { return getSourceSync(url); } -/** @type {import('deps/cjs-module-lexer/lexer.js').parse} */ -let cjsParse; -/** - * Initializes the CommonJS module lexer parser using the JavaScript version. - * TODO(joyeecheung): Use `require('internal/deps/cjs-module-lexer/dist/lexer').initSync()` - * when cjs-module-lexer 1.4.0 is rolled in. - */ -function initCJSParseSync() { - if (cjsParse === undefined) { - cjsParse = require('internal/deps/cjs-module-lexer/lexer').parse; - } -} +const { parse: cjsParse } = internalBinding('cjs_lexer'); const translators = new SafeMap(); exports.translators = translators; @@ -316,24 +304,18 @@ function createCJSNoSourceModuleWrap(url, parentURL) { } translators.set('commonjs-sync', function requireCommonJS(url, translateContext, parentURL) { - initCJSParseSync(); - return createCJSModuleWrap(url, translateContext, parentURL, loadCJSModuleWithModuleLoad); }); // Handle CommonJS modules referenced by `require` calls. // This translator function must be sync, as `require` is sync. translators.set('require-commonjs', (url, translateContext, parentURL) => { - initCJSParseSync(); - assert(cjsParse); - return createCJSModuleWrap(url, translateContext, parentURL); }); // Handle CommonJS modules referenced by `require` calls. // This translator function must be sync, as `require` is sync. translators.set('require-commonjs-typescript', (url, translateContext, parentURL) => { - assert(cjsParse); translateContext.source = stripTypeScriptModuleTypes(stringify(translateContext.source), url); return createCJSModuleWrap(url, translateContext, parentURL); }); @@ -349,10 +331,6 @@ function loadCJSModuleWithModuleLoad(module, source, url, filename, isMain) { // Handle CommonJS modules referenced by `import` statements or expressions, // or as the initial entry point when the ESM loader handles a CommonJS entry. translators.set('commonjs', function commonjsStrategy(url, translateContext, parentURL) { - if (!cjsParse) { - initCJSParseSync(); - } - // For backward-compatibility, it's possible to return a nullish value for // CJS source associated with a `file:` URL - that usually means the source is not // customized (is loaded by default load) or the hook author wants it to be reloaded @@ -414,15 +392,7 @@ function cjsPreparseModuleExports(filename, source, format) { module[kModuleSource] = source; debug(`Preparsing exports of ${filename}`); - let exports, reexports; - try { - ({ exports, reexports } = cjsParse(source || '')); - } catch { - exports = []; - reexports = []; - } - - const exportNames = new SafeSet(new SafeArrayIterator(exports)); + const { 0: exportNames, 1: reexports } = cjsParse(source); // Set first for cycles. module[kModuleExportNames] = exportNames; diff --git a/node.gyp b/node.gyp index 30e4a66e347bcc..0ff1cadea94442 100644 --- a/node.gyp +++ b/node.gyp @@ -18,6 +18,7 @@ 'node_shared_hdr_histogram%': 'false', 'node_shared_http_parser%': 'false', 'node_shared_libuv%': 'false', + 'node_shared_merve%': 'false', 'node_shared_nbytes%': 'false', 'node_shared_nghttp2%': 'false', 'node_shared_openssl%': 'false', @@ -109,6 +110,7 @@ 'src/node_config.cc', 'src/node_config_file.cc', 'src/node_constants.cc', + 'src/node_cjs_lexer.cc', 'src/node_contextify.cc', 'src/node_credentials.cc', 'src/node_debug.cc', diff --git a/node.gypi b/node.gypi index 79bb8a1e38287c..33c444c460a6ec 100644 --- a/node.gypi +++ b/node.gypi @@ -226,6 +226,10 @@ 'dependencies': [ 'deps/ada/ada.gyp:ada' ], }], + [ 'node_shared_merve=="false"', { + 'dependencies': [ 'deps/merve/merve.gyp:merve' ], + }], + [ 'node_shared_simdjson=="false"', { 'dependencies': [ 'deps/simdjson/simdjson.gyp:simdjson' ], }], diff --git a/src/cjs_module_lexer_version.h b/src/cjs_module_lexer_version.h deleted file mode 100644 index c734bdcc9ba184..00000000000000 --- a/src/cjs_module_lexer_version.h +++ /dev/null @@ -1,6 +0,0 @@ -// This is an auto generated file, please do not edit. -// Refer to tools/dep_updaters/update-cjs-module-lexer.sh -#ifndef SRC_CJS_MODULE_LEXER_VERSION_H_ -#define SRC_CJS_MODULE_LEXER_VERSION_H_ -#define CJS_MODULE_LEXER_VERSION "2.2.0" -#endif // SRC_CJS_MODULE_LEXER_VERSION_H_ diff --git a/src/node_binding.cc b/src/node_binding.cc index 3b284583d6ccc9..740706e917b7d2 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -43,6 +43,7 @@ V(buffer) \ V(builtins) \ V(cares_wrap) \ + V(cjs_lexer) \ V(config) \ V(constants) \ V(contextify) \ diff --git a/src/node_builtins.cc b/src/node_builtins.cc index 7b34b14856a519..f25ca01d6ef016 100644 --- a/src/node_builtins.cc +++ b/src/node_builtins.cc @@ -39,18 +39,6 @@ using v8::Value; BuiltinLoader::BuiltinLoader() : config_(GetConfig()), code_cache_(std::make_shared()) { LoadJavaScriptSource(); -#ifdef NODE_SHARED_BUILTIN_CJS_MODULE_LEXER_LEXER_PATH - AddExternalizedBuiltin( - "internal/deps/cjs-module-lexer/lexer", - STRINGIFY(NODE_SHARED_BUILTIN_CJS_MODULE_LEXER_LEXER_PATH)); -#endif // NODE_SHARED_BUILTIN_CJS_MODULE_LEXER_LEXER_PATH - -#ifdef NODE_SHARED_BUILTIN_CJS_MODULE_LEXER_DIST_LEXER_PATH - AddExternalizedBuiltin( - "internal/deps/cjs-module-lexer/dist/lexer", - STRINGIFY(NODE_SHARED_BUILTIN_CJS_MODULE_LEXER_DIST_LEXER_PATH)); -#endif // NODE_SHARED_BUILTIN_CJS_MODULE_LEXER_DIST_LEXER_PATH - #ifdef NODE_SHARED_BUILTIN_UNDICI_UNDICI_PATH AddExternalizedBuiltin("internal/deps/undici/undici", STRINGIFY(NODE_SHARED_BUILTIN_UNDICI_UNDICI_PATH)); @@ -115,9 +103,6 @@ BuiltinLoader::BuiltinCategories BuiltinLoader::GetBuiltinCategories() const { "internal/main/" }; - builtin_categories.can_be_required.emplace( - "internal/deps/cjs-module-lexer/lexer"); - builtin_categories.cannot_be_required = std::set { #if !HAVE_INSPECTOR "inspector", "inspector/promises", "internal/util/inspector", diff --git a/src/node_cjs_lexer.cc b/src/node_cjs_lexer.cc new file mode 100644 index 00000000000000..1b44dec9174841 --- /dev/null +++ b/src/node_cjs_lexer.cc @@ -0,0 +1,107 @@ +#include "env-inl.h" +#include "node_external_reference.h" +#include "simdutf.h" +#include "util-inl.h" +#include "v8.h" + +#include "merve.h" + +namespace node { +namespace cjs_lexer { + +using v8::Array; +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::Isolate; +using v8::Local; +using v8::LocalVector; +using v8::NewStringType; +using v8::Object; +using v8::Set; +using v8::String; +using v8::Value; + +// Create a V8 string from an export_string variant, using fast path for ASCII +inline Local CreateString(Isolate* isolate, + const lexer::export_string& str) { + std::string_view sv = lexer::get_string_view(str); + + if (simdutf::validate_ascii(sv.data(), sv.size())) { + return String::NewFromOneByte(isolate, + reinterpret_cast(sv.data()), + NewStringType::kInternalized, + static_cast(sv.size())) + .ToLocalChecked(); + } + + return String::NewFromUtf8(isolate, + sv.data(), + NewStringType::kInternalized, + static_cast(sv.size())) + .ToLocalChecked(); +} + +void Parse(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + + // Handle null, undefined, or missing argument by defaulting to empty string + if (args.Length() < 1 || args[0]->IsNullOrUndefined()) { + Local result_elements[] = {Set::New(isolate), + Array::New(isolate, 0)}; + args.GetReturnValue().Set(Array::New(isolate, result_elements, 2)); + return; + } + + CHECK(args[0]->IsString()); + + Utf8Value source(isolate, args[0]); + + auto result = lexer::parse_commonjs(source.ToStringView()); + + if (!result.has_value()) { + Local result_elements[] = {Set::New(isolate), + Array::New(isolate, 0)}; + args.GetReturnValue().Set(Array::New(isolate, result_elements, 2)); + return; + } + + const auto& analysis = result.value(); + + // Convert exports to JS Set + Local exports_set = Set::New(isolate); + for (const auto& exp : analysis.exports) { + exports_set->Add(context, CreateString(isolate, exp)).ToLocalChecked(); + } + + // Convert reexports to JS array using batch creation + LocalVector reexports_vec(isolate); + reexports_vec.reserve(analysis.re_exports.size()); + for (const auto& reexp : analysis.re_exports) { + reexports_vec.push_back(CreateString(isolate, reexp)); + } + + // Create result array [exports (Set), reexports (Array)] + Local result_elements[] = { + exports_set, + Array::New(isolate, reexports_vec.data(), reexports_vec.size())}; + args.GetReturnValue().Set(Array::New(isolate, result_elements, 2)); +} + +void Initialize(Local target, + Local unused, + Local context, + void* priv) { + SetMethodNoSideEffect(context, target, "parse", Parse); +} + +void RegisterExternalReferences(ExternalReferenceRegistry* registry) { + registry->Register(Parse); +} + +} // namespace cjs_lexer +} // namespace node + +NODE_BINDING_CONTEXT_AWARE_INTERNAL(cjs_lexer, node::cjs_lexer::Initialize) +NODE_BINDING_EXTERNAL_REFERENCE(cjs_lexer, + node::cjs_lexer::RegisterExternalReferences) diff --git a/src/node_metadata.cc b/src/node_metadata.cc index 6544999b84dc0f..a6063ce4d25409 100644 --- a/src/node_metadata.cc +++ b/src/node_metadata.cc @@ -4,8 +4,8 @@ #include "amaro_version.h" #include "ares.h" #include "brotli/encode.h" -#include "cjs_module_lexer_version.h" #include "llhttp.h" +#include "merve.h" #include "nbytes.h" #include "nghttp2/nghttp2ver.h" #include "node.h" @@ -127,7 +127,7 @@ Metadata::Versions::Versions() { #endif acorn = ACORN_VERSION; - cjs_module_lexer = CJS_MODULE_LEXER_VERSION; + merve = MERVE_VERSION; uvwasi = UVWASI_VERSION_STRING; zstd = ZSTD_VERSION_STRING; diff --git a/src/node_metadata.h b/src/node_metadata.h index d9c533f100d25a..7f6268a1d83d31 100644 --- a/src/node_metadata.h +++ b/src/node_metadata.h @@ -61,7 +61,7 @@ namespace node { V(nbytes) \ NODE_VERSIONS_KEY_AMARO(V) \ NODE_VERSIONS_KEY_UNDICI(V) \ - V(cjs_module_lexer) + V(merve) #if HAVE_OPENSSL #define NODE_VERSIONS_KEY_CRYPTO(V) V(openssl) V(ncrypto) diff --git a/test/parallel/test-process-versions.js b/test/parallel/test-process-versions.js index 82b08567993a4a..a8f78a77660839 100644 --- a/test/parallel/test-process-versions.js +++ b/test/parallel/test-process-versions.js @@ -4,7 +4,6 @@ const assert = require('assert'); // Import of pure js (non-shared) deps for comparison const acorn = require('../../deps/acorn/acorn/package.json'); -const cjs_module_lexer = require('../../deps/cjs-module-lexer/src/package.json'); const expected_keys = [ 'ares', @@ -22,8 +21,8 @@ const expected_keys = [ 'simdjson', 'simdutf', 'ada', - 'cjs_module_lexer', 'nbytes', + 'merve', ]; @@ -73,6 +72,7 @@ assert.match(process.versions.acorn, commonTemplate); assert.match(process.versions.ares, commonTemplate); assert.match(process.versions.brotli, commonTemplate); assert.match(process.versions.llhttp, commonTemplate); +assert.match(process.versions.merve, commonTemplate); assert.match(process.versions.node, commonTemplate); assert.match(process.versions.uv, commonTemplate); assert.match(process.versions.nbytes, commonTemplate); @@ -88,7 +88,6 @@ assert.match( /^\d+\.\d+\.\d+(?:\.\d+)?-node\.\d+(?: \(candidate\))?$/ ); assert.match(process.versions.modules, /^\d+$/); -assert.match(process.versions.cjs_module_lexer, commonTemplate); if (common.hasCrypto) { const { hasOpenSSL3 } = require('../common/crypto'); @@ -123,5 +122,3 @@ if (hasUndici) { const expectedAcornVersion = acorn.version; assert.strictEqual(process.versions.acorn, expectedAcornVersion); -const expectedCjsModuleLexerVersion = cjs_module_lexer.version; -assert.strictEqual(process.versions.cjs_module_lexer, expectedCjsModuleLexerVersion); diff --git a/tools/dep_updaters/update-cjs-module-lexer.sh b/tools/dep_updaters/update-cjs-module-lexer.sh deleted file mode 100755 index 1d12546ab45deb..00000000000000 --- a/tools/dep_updaters/update-cjs-module-lexer.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/bin/sh -set -e -# Shell script to update cjs-module-lexer in the source tree to a specific version - -BASE_DIR=$(cd "$(dirname "$0")/../.." && pwd) - -DEPS_DIR="$BASE_DIR/deps" -[ -z "$NODE" ] && NODE="$BASE_DIR/out/Release/node" -[ -x "$NODE" ] || NODE=$(command -v node) - -NPM="$DEPS_DIR/npm/bin/npm-cli.js" - -# shellcheck disable=SC1091 -. "$BASE_DIR/tools/dep_updaters/utils.sh" - -NEW_VERSION="$("$NODE" --input-type=module <<'EOF' -const res = await fetch('https://api.github.com/repos/nodejs/cjs-module-lexer/tags', - process.env.GITHUB_TOKEN && { - headers: { - "Authorization": `Bearer ${process.env.GITHUB_TOKEN}` - }, - }); -if (!res.ok) throw new Error(`FetchError: ${res.status} ${res.statusText}`, { cause: res }); -const tags = await res.json(); -const { name } = tags.at(0) -console.log(name); -EOF -)" - - -CURRENT_VERSION=$("$NODE" -p "require('./deps/cjs-module-lexer/src/package.json').version") - -# This function exit with 0 if new version and current version are the same -compare_dependency_version "cjs-module-lexer" "$NEW_VERSION" "$CURRENT_VERSION" - -echo "Making temporary workspace" - -WORKSPACE=$(mktemp -d 2> /dev/null || mktemp -d -t 'tmp') - -cleanup () { - EXIT_CODE=$? - [ -d "$WORKSPACE" ] && rm -rf "$WORKSPACE" - exit $EXIT_CODE -} - -trap cleanup INT TERM EXIT - -CJS_MODULE_LEXER_ZIP="cjs-module-lexer-$NEW_VERSION" - -# remove existing source and built files -rm -rf "$DEPS_DIR/cjs-module-lexer/src" -rm -rf "$DEPS_DIR/cjs-module-lexer/dist" -rm "$DEPS_DIR/cjs-module-lexer/lexer.js" - -cd "$WORKSPACE" - -curl -sL -o "$CJS_MODULE_LEXER_ZIP.zip" "https://github.com/nodejs/cjs-module-lexer/archive/refs/tags/$NEW_VERSION.zip" - -log_and_verify_sha256sum "cjs-module-lexer" "$CJS_MODULE_LEXER_ZIP.zip" - -echo "Unzipping..." -unzip "$CJS_MODULE_LEXER_ZIP.zip" -d "src" -mv "src/$CJS_MODULE_LEXER_ZIP" "$DEPS_DIR/cjs-module-lexer/src" -rm "$CJS_MODULE_LEXER_ZIP.zip" -cd "$ROOT" - -( - # remove components we don't need to keep in nodejs/deps - # these are files that we don't need to build from source - cd "$DEPS_DIR/cjs-module-lexer/src" - rm -rf bench - rm -rf .github || true - rm -rf test - rm .gitignore - rm .travis.yml - rm CODE_OF_CONDUCT.md - rm CONTRIBUTING.md - - # Rebuild components from source - rm lib/*.* - "$NODE" "$NPM" install --ignore-scripts - "$NODE" "$NPM" run build-wasm - "$NODE" "$NPM" run build - "$NODE" "$NPM" prune --production - - # remove files we don't need in Node.js - rm -rf node_modules - - # copy over the built files to the expected locations - mv dist .. - cp lexer.js .. - cp LICENSE .. - cp README.md .. -) - -# update cjs_module_lexer_version.h -cat > "$BASE_DIR/src/cjs_module_lexer_version.h" << EOL -// This is an auto generated file, please do not edit. -// Refer to tools/dep_updaters/update-cjs-module-lexer.sh -#ifndef SRC_CJS_MODULE_LEXER_VERSION_H_ -#define SRC_CJS_MODULE_LEXER_VERSION_H_ -#define CJS_MODULE_LEXER_VERSION "$NEW_VERSION" -#endif // SRC_CJS_MODULE_LEXER_VERSION_H_ -EOL - -# Update the version number on maintaining-dependencies.md -# and print the new version as the last line of the script as we need -# to add it to $GITHUB_ENV variable -finalize_version_update "cjs-module-lexer" "$NEW_VERSION" "src/cjs_module_lexer_version.h" diff --git a/tools/dep_updaters/update-merve.sh b/tools/dep_updaters/update-merve.sh new file mode 100644 index 00000000000000..ec0525aadb48b6 --- /dev/null +++ b/tools/dep_updaters/update-merve.sh @@ -0,0 +1,65 @@ +#!/bin/sh +set -e +# Shell script to update merve in the source tree to a specific version + +BASE_DIR=$(cd "$(dirname "$0")/../.." && pwd) +DEPS_DIR="$BASE_DIR/deps" +[ -z "$NODE" ] && NODE="$BASE_DIR/out/Release/node" +[ -x "$NODE" ] || NODE=$(command -v node) + +# shellcheck disable=SC1091 +. "$BASE_DIR/tools/dep_updaters/utils.sh" + +NEW_VERSION="$("$NODE" --input-type=module <<'EOF' +const res = await fetch('https://api.github.com/repos/anonrig/merve/releases/latest', + process.env.GITHUB_TOKEN && { + headers: { + "Authorization": `Bearer ${process.env.GITHUB_TOKEN}` + }, + }); +if (!res.ok) throw new Error(`FetchError: ${res.status} ${res.statusText}`, { cause: res }); +const { tag_name } = await res.json(); +console.log(tag_name.replace('v', '')); +EOF +)" + +CURRENT_VERSION=$(grep "#define MERVE_VERSION" "$DEPS_DIR/merve/merve.h" | sed -n "s/^.*VERSION \"\(.*\)\"/\1/p") + +# This function exit with 0 if new version and current version are the same +compare_dependency_version "merve" "$NEW_VERSION" "$CURRENT_VERSION" + +echo "Making temporary workspace..." + +WORKSPACE=$(mktemp -d 2> /dev/null || mktemp -d -t 'tmp') + +cleanup () { + EXIT_CODE=$? + [ -d "$WORKSPACE" ] && rm -rf "$WORKSPACE" + exit $EXIT_CODE +} + +trap cleanup INT TERM EXIT + +MERVE_REF="v$NEW_VERSION" +MERVE_ZIP="singleheader.zip" +MERVE_LICENSE="LICENSE-MIT" + +cd "$WORKSPACE" + +echo "Fetching merve source archive..." +curl -sL -o "$MERVE_ZIP" "https://github.com/anonrig/merve/releases/download/$MERVE_REF/singleheader.zip" +log_and_verify_sha256sum "merve" "$MERVE_ZIP" +unzip "$MERVE_ZIP" +rm "$MERVE_ZIP" + +curl -sL -o "$MERVE_LICENSE" "https://raw.githubusercontent.com/anonrig/merve/HEAD/LICENSE-MIT" + +echo "Replacing existing merve (except GYP build files)" +mv "$DEPS_DIR/merve/merve.gyp" "$WORKSPACE/" +rm -rf "$DEPS_DIR/merve" +mv "$WORKSPACE" "$DEPS_DIR/merve" + +# Update the version number on maintaining-dependencies.md +# and print the new version as the last line of the script as we need +# to add it to $GITHUB_ENV variable +finalize_version_update "merve" "$NEW_VERSION" diff --git a/tools/license-builder.sh b/tools/license-builder.sh index c057b1f0d4c2db..d4f4382ca65597 100755 --- a/tools/license-builder.sh +++ b/tools/license-builder.sh @@ -34,8 +34,8 @@ licenseText="$(cat "${rootdir}/deps/acorn/acorn/LICENSE")" addlicense "Acorn" "deps/acorn" "$licenseText" licenseText="$(cat "${rootdir}/deps/cares/LICENSE.md")" addlicense "c-ares" "deps/cares" "$licenseText" -licenseText="$(cat "${rootdir}/deps/cjs-module-lexer/LICENSE")" -addlicense "cjs-module-lexer" "deps/cjs-module-lexer" "$licenseText" +licenseText="$(cat "${rootdir}/deps/merve/LICENSE-MIT")" +addlicense "merve" "deps/merve" "$licenseText" licenseText="$(cat "${rootdir}/deps/v8/third_party/ittapi/LICENSES/BSD-3-Clause.txt")" addlicense "ittapi" "deps/v8/third_party/ittapi" "$licenseText" licenseText="$(cat "${rootdir}/deps/amaro/LICENSE.md")" diff --git a/typings/globals.d.ts b/typings/globals.d.ts index 060494300c1caa..739f3d9a534026 100644 --- a/typings/globals.d.ts +++ b/typings/globals.d.ts @@ -2,6 +2,7 @@ import { AsyncContextFrameBinding } from './internalBinding/async_context_frame' import { AsyncWrapBinding } from './internalBinding/async_wrap'; import { BlobBinding } from './internalBinding/blob'; import { BufferBinding } from './internalBinding/buffer'; +import { CJSLexerBinding } from './internalBinding/cjs_lexer'; import { ConfigBinding } from './internalBinding/config'; import { ConstantsBinding } from './internalBinding/constants'; import { DebugBinding } from './internalBinding/debug'; @@ -36,6 +37,7 @@ interface InternalBindingMap { async_wrap: AsyncWrapBinding; blob: BlobBinding; buffer: BufferBinding; + cjs_lexer: CJSLexerBinding; config: ConfigBinding; constants: ConstantsBinding; debug: DebugBinding; diff --git a/typings/internalBinding/cjs_lexer.d.ts b/typings/internalBinding/cjs_lexer.d.ts new file mode 100644 index 00000000000000..70f44a4c5c64f4 --- /dev/null +++ b/typings/internalBinding/cjs_lexer.d.ts @@ -0,0 +1,5 @@ +export type CJSLexerParseResult = [exports: Set, reexports: string[]]; + +export interface CJSLexerBinding { + parse(source?: string | null): CJSLexerParseResult; +} From e22b03af993f0bc1de6e5d62d8c4d84d49b43ba9 Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Feb 2026 16:46:02 +0800 Subject: [PATCH 64/64] deps: V8: backport 6a0a25abaed3 Original commit message: [riscv] Fix sp handling in MacroAssembler::LeaveFrame Keep sp <= fp to ensure that data right above fp doesn't get clobbered by an inopportune signal and its handler. Such clobbering can happen in e.g. Node.js when JIT-compiled code is interrupted by a SIGCHLD handler. Bug: None Change-Id: Ief0836032ada7942e89f081f7605f61632c4d414 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7540554 Reviewed-by: Ji Qiu Commit-Queue: Yahan Lu (LuYahan) Reviewed-by: Rezvan Mahdavi Hezaveh Cr-Commit-Position: refs/heads/main@{#105069} Refs: https://github.com/v8/v8/commit/6a0a25abaed397f83eb0d92e4b33a5e18204f8bc Co-authored-by: kxxt --- common.gypi | 2 +- deps/v8/AUTHORS | 1 + deps/v8/src/codegen/riscv/macro-assembler-riscv.cc | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/common.gypi b/common.gypi index c5a7dc9cacf8b9..31ce3f73f51e6f 100644 --- a/common.gypi +++ b/common.gypi @@ -38,7 +38,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.41', + 'v8_embedder_string': '-node.42', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/AUTHORS b/deps/v8/AUTHORS index 280e33e6841cc1..b7e6fac8f83658 100644 --- a/deps/v8/AUTHORS +++ b/deps/v8/AUTHORS @@ -294,6 +294,7 @@ Vadim Gorbachev Varun Varada Victor Costan Victor Polevoy +Vivian Wang Vlad Burlik Vladimir Kempik Vladimir Krivosheev diff --git a/deps/v8/src/codegen/riscv/macro-assembler-riscv.cc b/deps/v8/src/codegen/riscv/macro-assembler-riscv.cc index 12379ec0ff6fd2..203b2bcd61c7dc 100644 --- a/deps/v8/src/codegen/riscv/macro-assembler-riscv.cc +++ b/deps/v8/src/codegen/riscv/macro-assembler-riscv.cc @@ -6709,9 +6709,10 @@ void MacroAssembler::EnterFrame(StackFrame::Type type) { void MacroAssembler::LeaveFrame(StackFrame::Type type) { ASM_CODE_COMMENT(this); - addi(sp, fp, 2 * kSystemPointerSize); + Move(sp, fp); LoadWord(ra, MemOperand(fp, 1 * kSystemPointerSize)); LoadWord(fp, MemOperand(fp, 0 * kSystemPointerSize)); + AddWord(sp, sp, 2 * kSystemPointerSize); } void MacroAssembler::EnterExitFrame(Register scratch, int stack_space,