Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,8 @@ jobs:
- upload-test-results
test-bun:
executor: linux-python
environment:
OVERRIDE_NODE_JS_TEST_VERSION: "v24.0.0"
steps:
- checkout
- pip-install
Expand All @@ -880,10 +882,14 @@ jobs:
name: install bun
command: |
curl -fsSL https://bun.com/install | bash
echo "BUN_ENGINE = os.path.expanduser('~/.bun/bin/bun')" >> ~/emsdk/.emscripten
echo "JS_ENGINES = [BUN_ENGINE]" >> ~/emsdk/.emscripten
echo "NODE_JS_TEST = os.path.expanduser('~/.bun/bin/bun')" >> ~/emsdk/.emscripten
echo "JS_ENGINES = [NODE_JS_TEST]" >> ~/emsdk/.emscripten
- run-tests:
test_targets: "core0.test_hello_world"
test_targets: "
core0.test_hello_world
core0.test_hello_argc_pthreads
core2.test_pthread_create
"
test-jsc:
executor: linux-python
steps:
Expand Down
15 changes: 9 additions & 6 deletions src/pthread_esm_startup.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@ console.log("Running pthread_esm_startup");
#if ENVIRONMENT_MAY_BE_NODE
if ({{{ nodeDetectionCode() }}}) {
// Create as web-worker-like an environment as we can.
globalThis.self = globalThis;
var worker_threads = await import('worker_threads');
global.Worker = worker_threads.Worker;
globalThis.Worker = worker_threads.Worker;
var parentPort = worker_threads['parentPort'];
parentPort.on('message', (msg) => global.onmessage?.({ data: msg }));
Object.assign(globalThis, {
self: global,
postMessage: (msg) => parentPort['postMessage'](msg),
});
// Deno and Bun already have `postMessage` defined on the global scope and
// deliver messages to `globalThis.onmessage`, so we must not duplicate that
// behavior here if `postMessage` is already present.
if (!globalThis.postMessage) {
parentPort.on('message', (msg) => globalThis.onmessage?.({ data: msg }));
globalThis.postMessage = (msg) => parentPort['postMessage'](msg);
}
}
#endif

Expand Down
13 changes: 8 additions & 5 deletions src/runtime_common.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@ var readyPromiseResolve, readyPromiseReject;
#if (PTHREADS || WASM_WORKERS) && (ENVIRONMENT_MAY_BE_NODE && !WASM_ESM_INTEGRATION)
if (ENVIRONMENT_IS_NODE && {{{ ENVIRONMENT_IS_WORKER_THREAD() }}}) {
// Create as web-worker-like an environment as we can.
globalThis.self = globalThis;
var parentPort = worker_threads['parentPort'];
parentPort.on('message', (msg) => global.onmessage?.({ data: msg }));
Object.assign(globalThis, {
self: global,
postMessage: (msg) => parentPort['postMessage'](msg),
});
// Deno and Bun already have `postMessage` defined on the global scope and
// deliver messages to `globalThis.onmessage`, so we must not duplicate that
// behavior here if `postMessage` is already present.
if (!globalThis.postMessage) {
parentPort.on('message', (msg) => globalThis.onmessage?.({ data: msg }));
globalThis.postMessage = (msg) => parentPort['postMessage'](msg);
}
// Node.js Workers do not pass postMessage()s and uncaught exception events to the parent
// thread necessarily in the same order where they were generated in sequential program order.
// See https://github.com/nodejs/node/issues/59617
Expand Down
13 changes: 11 additions & 2 deletions test/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,15 @@ def require_node(self):
self.require_engine(nodejs)
return nodejs

def get_node_test_version(self, nodejs):
override = os.environ.get('OVERRIDE_NODE_JS_TEST_VERSION')
if override:
override = override.removeprefix('v')
override = override.split('-')[0].split('.')
override = tuple(int(v) for v in override)
return override
return shared.get_node_version(nodejs)
Comment on lines +396 to +403
Copy link

Copilot AI Dec 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a docstring to document this helper function, especially the OVERRIDE_NODE_JS_TEST_VERSION environment variable behavior. This would help other developers understand when and why this override mechanism should be used. For example:
"""Get the Node.js version for testing purposes.

Returns the version tuple from OVERRIDE_NODE_JS_TEST_VERSION environment
variable if set, otherwise returns the actual version of the nodejs binary.
This allows testing with runtimes like Bun/Deno that masquerade as Node.js."""

Copilot uses AI. Check for mistakes.

def node_is_canary(self, nodejs):
return nodejs and nodejs[0] and ('canary' in nodejs[0] or 'nightly' in nodejs[0])

Expand Down Expand Up @@ -435,7 +444,7 @@ def try_require_node_version(self, major, minor = 0, revision = 0):
nodejs = self.get_nodejs()
if not nodejs:
self.skipTest('Test requires nodejs to run')
version = shared.get_node_version(nodejs)
version = self.get_node_test_version(nodejs)
if version < (major, minor, revision):
return False

Expand Down Expand Up @@ -622,7 +631,7 @@ def setUp(self):

nodejs = self.get_nodejs()
if nodejs:
node_version = shared.get_node_version(nodejs)
node_version = self.get_node_test_version(nodejs)
if node_version < (13, 0, 0):
self.node_args.append('--unhandled-rejections=strict')
elif node_version < (15, 0, 0):
Expand Down
8 changes: 4 additions & 4 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -3682,7 +3682,7 @@ def test_embind_tsgen_jspi(self):
'legacy': [1],
})
def test_embind_tsgen_exceptions(self, legacy):
if not legacy and shared.get_node_version(config.NODE_JS)[0] < 22:
if not legacy and self.get_node_test_version(config.NODE_JS)[0] < 22:
self.skipTest('Node version needs to be 22 or greater to run tsgen with Wasm EH')
self.set_setting('WASM_LEGACY_EXCEPTIONS', legacy)

Expand Down Expand Up @@ -12368,7 +12368,7 @@ def test_gen_struct_info(self):
self.assertFileContents(path_from_root('src/struct_info_generated.json'), read_file('out.json'))

# Same again for wasm64
node_version = shared.get_node_version(self.get_nodejs())
node_version = self.get_node_test_version(self.get_nodejs())
if node_version and node_version >= (14, 0, 0):
self.run_process([PYTHON, path_from_root('tools/gen_struct_info.py'), '--wasm64', '-o', 'out.json'])
self.assertFileContents(path_from_root('src/struct_info_generated_wasm64.json'), read_file('out.json'))
Expand Down Expand Up @@ -12881,7 +12881,7 @@ def test_node_unhandled_rejection(self):
self.build('main.c', cflags=['--pre-js=pre.js', '-sNODEJS_CATCH_REJECTION=0'])
self.assertNotContained('unhandledRejection', read_file('main.js'))

if shared.get_node_version(self.get_nodejs())[0] >= 15:
if self.get_node_test_version(self.get_nodejs())[0] >= 15:
self.skipTest('old behaviour of node JS cannot be tested on node v15 or above')

output = self.run_js('main.js')
Expand Down Expand Up @@ -13960,7 +13960,7 @@ def test_parseTools_legacy(self):

@requires_node
def test_min_node_version(self):
node_version = shared.get_node_version(self.get_nodejs())
node_version = self.get_node_test_version(self.get_nodejs())
node_version = '.'.join(str(x) for x in node_version)
self.set_setting('MIN_NODE_VERSION', 300000)
expected = 'This emscripten-generated code requires node v30.0.0 (detected v%s' % node_version
Expand Down
Loading