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
2 changes: 1 addition & 1 deletion src/lib/libatomic.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ addToLibrary({

emscripten_num_logical_cores: () =>
#if ENVIRONMENT_MAY_BE_NODE
ENVIRONMENT_IS_NODE ? require('node:os').cpus().length :
ENVIRONMENT_IS_NODE ? getBuiltinModule('os').cpus().length :
#endif
navigator['hardwareConcurrency'],

Expand Down
2 changes: 1 addition & 1 deletion src/lib/libcore.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ addToLibrary({
var cmdstr = UTF8ToString(command);
if (!cmdstr.length) return 0; // this is what glibc seems to do (shell works test?)

var cp = require('node:child_process');
var cp = getBuiltinModule('child_process');
var ret = cp.spawnSync(cmdstr, [], {shell:true, stdio:'inherit'});

var _W_EXITCODE = (ret, sig) => ((ret) << 8 | (sig));
Expand Down
2 changes: 1 addition & 1 deletion src/lib/libembind_gen.js
Original file line number Diff line number Diff line change
Expand Up @@ -924,7 +924,7 @@ var LibraryEmbind = {
const printer = new TsPrinter(moduleDefinitions);
#endif
const output = printer.print();
var fs = require('node:fs');
var fs = getBuiltinModule('fs');
fs.writeFileSync(process.argv[2], output + '\n');
},

Expand Down
2 changes: 1 addition & 1 deletion src/lib/libnodepath.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// operations. Hence, using `nodePath` should be safe here.

addToLibrary({
$nodePath: "require('node:path')",
$nodePath: "getBuiltinModule('path')",
$PATH__deps: ['$nodePath'],
$PATH: `{
isAbs: nodePath.isAbsolute,
Expand Down
2 changes: 1 addition & 1 deletion src/lib/libnoderawfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ addToLibrary({
if (!ENVIRONMENT_IS_NODE) {
throw new Error("NODERAWFS is currently only supported on Node.js environment.")
}
var nodeTTY = require('node:tty');
var nodeTTY = getBuiltinModule('tty');
function _wrapNodeError(func) {
return (...args) => {
try {
Expand Down
21 changes: 21 additions & 0 deletions src/lib/libsockfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,25 @@
*/

addToLibrary({
#if ENVIRONMENT_MAY_BE_NODE && EXPORT_ES6
// ESM has no native `require`. Lazily construct one (node-gated, so it never
// runs during web startup) for loading the non-builtin `ws` module. The call
// site stays a literal `require('ws')` so third-party bundlers can still
// statically discover the dependency.
$require: undefined,
$ensureCreateRequire__deps: ['$require'],
$ensureCreateRequire: () => {
require ??= getBuiltinModule('module').createRequire(import.meta.url);
},
#endif
$SOCKFS__postset: () => {
addAtInit('SOCKFS.root = FS.mount(SOCKFS, {}, null);');
},
#if ENVIRONMENT_MAY_BE_NODE && EXPORT_ES6
$SOCKFS__deps: ['$FS', '$ensureCreateRequire'],
#else
$SOCKFS__deps: ['$FS'],
#endif
$SOCKFS: {
#if expectToReceiveOnModule('websocket')
websocketArgs: {},
Expand Down Expand Up @@ -216,6 +231,9 @@ addToLibrary({
var WebSocketConstructor;
#if ENVIRONMENT_MAY_BE_NODE
if (ENVIRONMENT_IS_NODE) {
#if EXPORT_ES6
ensureCreateRequire();
#endif
WebSocketConstructor = /** @type{(typeof WebSocket)} */(require('ws'));
} else
#endif // ENVIRONMENT_MAY_BE_NODE
Expand Down Expand Up @@ -518,6 +536,9 @@ addToLibrary({
if (sock.server) {
throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); // already listening
}
#if EXPORT_ES6
ensureCreateRequire();
#endif
var WebSocketServer = require('ws').Server;
var host = sock.saddr;
#if SOCKET_DEBUG
Expand Down
2 changes: 1 addition & 1 deletion src/lib/libwasi.js
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ var WasiLibrary = {
#if ENVIRONMENT_MAY_BE_NODE && MIN_NODE_VERSION < 190000
// This block is not needed on v19+ since crypto.getRandomValues is builtin
if (ENVIRONMENT_IS_NODE) {
var nodeCrypto = require('node:crypto');
var nodeCrypto = getBuiltinModule('crypto');
return (view) => (nodeCrypto.randomFillSync(view), 0);
}
#endif // ENVIRONMENT_MAY_BE_NODE
Expand Down
2 changes: 1 addition & 1 deletion src/lib/libwasm_worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ if (ENVIRONMENT_IS_WASM_WORKER

emscripten_navigator_hardware_concurrency: () => {
#if ENVIRONMENT_MAY_BE_NODE
if (ENVIRONMENT_IS_NODE) return require('node:os').cpus().length;
if (ENVIRONMENT_IS_NODE) return getBuiltinModule('os').cpus().length;
#endif
return navigator['hardwareConcurrency'];
},
Expand Down
2 changes: 1 addition & 1 deletion src/preamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ function instantiateSync(file, info) {
var binary = getBinarySync(file);
#if NODE_CODE_CACHING
if (ENVIRONMENT_IS_NODE) {
var v8 = require('node:v8');
var v8 = getBuiltinModule('v8');
// Include the V8 version in the cache name, so that we don't try to
// load cached code from another version, which fails silently (it seems
// to load ok, but we do actually recompile the binary every time).
Expand Down
4 changes: 2 additions & 2 deletions src/runtime_debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ function dbg(...args) {
// See https://github.com/emscripten-core/emscripten/issues/14804
if (ENVIRONMENT_IS_NODE) {
// TODO(sbc): Unify with err/out implementation in shell.sh.
var fs = require('node:fs');
var utils = require('node:util');
var fs = getBuiltinModule('fs');
var utils = getBuiltinModule('util');
function stringify(a) {
switch (typeof a) {
case 'object': return utils.inspect(a);
Expand Down
33 changes: 20 additions & 13 deletions src/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,18 +104,26 @@ if (ENVIRONMENT_IS_PTHREAD) {
#endif
#endif

#if ENVIRONMENT_MAY_BE_NODE && (EXPORT_ES6 || PTHREADS || WASM_WORKERS)
#if ENVIRONMENT_MAY_BE_NODE
// `process.getBuiltinModule()` loads builtins under both CJS and ESM. On older
// node (pre v18.20.4/v20.16/v22.3) fall back to `require()`.
var getBuiltinModule;
if (ENVIRONMENT_IS_NODE) {
if (process['getBuiltinModule']) {
getBuiltinModule = process['getBuiltinModule'];
} else {
#if EXPORT_ES6
// When building an ES module `require` is not normally available.
// We need to use `createRequire()` to construct the require()` function.
const { createRequire } = await import('node:module');
/** @suppress{duplicate} */
var require = createRequire(import.meta.url);
getBuiltinModule = (await import('node:module')).createRequire(import.meta.url);
#else
getBuiltinModule = require;
#endif
}
}
#endif // ENVIRONMENT_MAY_BE_NODE

#if PTHREADS || WASM_WORKERS
var worker_threads = require('node:worker_threads');
#if ENVIRONMENT_MAY_BE_NODE && (PTHREADS || WASM_WORKERS)
if (ENVIRONMENT_IS_NODE) {
var worker_threads = getBuiltinModule('worker_threads');
globalThis.Worker = worker_threads.Worker;
ENVIRONMENT_IS_WORKER = !worker_threads.isMainThread;
#if PTHREADS
Expand All @@ -126,9 +134,8 @@ if (ENVIRONMENT_IS_NODE) {
#if WASM_WORKERS
ENVIRONMENT_IS_WASM_WORKER = ENVIRONMENT_IS_WORKER && worker_threads.workerData == 'em-ww'
#endif
#endif // PTHREADS || WASM_WORKERS
}
#endif // ENVIRONMENT_MAY_BE_NODE && (EXPORT_ES6 || PTHREADS || WASM_WORKERS)
#endif // ENVIRONMENT_MAY_BE_NODE && (PTHREADS || WASM_WORKERS)

// --pre-jses are emitted after the Module integration code, so that they can
// refer to Module (if they choose; they can also define Module)
Expand Down Expand Up @@ -197,11 +204,11 @@ if (ENVIRONMENT_IS_NODE) {

// These modules will usually be used on Node.js. Load them eagerly to avoid
// the complexity of lazy-loading.
var fs = require('node:fs');
var fs = getBuiltinModule('fs');

#if EXPORT_ES6
if (_scriptName.startsWith('file:')) {
scriptDirectory = require('node:path').dirname(require('node:url').fileURLToPath(_scriptName)) + '/';
scriptDirectory = getBuiltinModule('path').dirname(getBuiltinModule('url').fileURLToPath(_scriptName)) + '/';
}
#else
scriptDirectory = __dirname + '/';
Expand Down Expand Up @@ -346,7 +353,7 @@ if (!ENVIRONMENT_IS_AUDIO_WORKLET)
var defaultPrint = console.log.bind(console);
var defaultPrintErr = console.error.bind(console);
if (ENVIRONMENT_IS_NODE) {
var utils = require('node:util');
var utils = getBuiltinModule('util');
var stringify = (a) => typeof a == 'object' ? utils.inspect(a) : a;
defaultPrint = (...args) => fs.writeSync(1, args.map(stringify).join(' ') + '\n');
defaultPrintErr = (...args) => fs.writeSync(2, args.map(stringify).join(' ') + '\n');
Expand Down
21 changes: 18 additions & 3 deletions src/shell_minimal.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,21 @@ var Module = {{{ EXPORT_NAME }}};

#if ENVIRONMENT_MAY_BE_NODE
var ENVIRONMENT_IS_NODE = {{{ nodeDetectionCode() }}};

// `process.getBuiltinModule()` loads builtins under both CJS and ESM. On older
// node (pre v18.20.4/v20.16/v22.3) fall back to `require()`.
var getBuiltinModule;
if (ENVIRONMENT_IS_NODE) {
if (process['getBuiltinModule']) {
getBuiltinModule = process['getBuiltinModule'];
} else {
#if EXPORT_ES6
getBuiltinModule = (await import('node:module')).createRequire(import.meta.url);
#else
getBuiltinModule = require;
#endif
}
}
#endif

#if ENVIRONMENT_MAY_BE_SHELL
Expand All @@ -59,7 +74,7 @@ var ENVIRONMENT_IS_WORKER = !!globalThis.WorkerGlobalScope;

#if ENVIRONMENT_MAY_BE_NODE && (PTHREADS || WASM_WORKERS)
if (ENVIRONMENT_IS_NODE) {
var worker_threads = require('node:worker_threads');
var worker_threads = getBuiltinModule('worker_threads');
globalThis.Worker = worker_threads.Worker;
ENVIRONMENT_IS_WORKER = !worker_threads.isMainThread;
}
Expand Down Expand Up @@ -104,7 +119,7 @@ if (ENVIRONMENT_IS_NODE && ENVIRONMENT_IS_SHELL) {
var defaultPrint = console.log.bind(console);
var defaultPrintErr = console.error.bind(console);
if (ENVIRONMENT_IS_NODE) {
var fs = require('node:fs');
var fs = getBuiltinModule('fs');
defaultPrint = (...args) => fs.writeSync(1, args.join(' ') + '\n');
defaultPrintErr = (...args) => fs.writeSync(2, args.join(' ') + '\n');
}
Expand Down Expand Up @@ -179,7 +194,7 @@ if (!ENVIRONMENT_IS_PTHREAD) {
// Wasm or Wasm2JS loading:

if (ENVIRONMENT_IS_NODE) {
var fs = require('node:fs');
var fs = getBuiltinModule('fs');
#if WASM == 2
if (globalThis.WebAssembly) Module['wasm'] = fs.readFileSync(__dirname + '/{{{ TARGET_BASENAME }}}.wasm');
else eval(fs.readFileSync(__dirname + '/{{{ TARGET_BASENAME }}}.wasm.js')+'');
Expand Down
4 changes: 2 additions & 2 deletions test/codesize/test_codesize_hello_dylink_all.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"a.out.js": 268278,
"a.out.js": 268326,
"a.out.nodebug.wasm": 587640,
"total": 855918,
"total": 855966,
"sent": [
"IMG_Init",
"IMG_Load",
Expand Down
16 changes: 8 additions & 8 deletions test/codesize/test_unoptimized_code_size.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"hello_world.js": 55076,
"hello_world.js.gz": 17352,
"hello_world.js": 55410,
"hello_world.js.gz": 17457,
"hello_world.wasm": 15115,
"hello_world.wasm.gz": 7464,
"no_asserts.js": 25903,
"no_asserts.js.gz": 8768,
"no_asserts.js": 26237,
"no_asserts.js.gz": 8876,
"no_asserts.wasm": 12229,
"no_asserts.wasm.gz": 6004,
"strict.js": 52833,
"strict.js.gz": 16583,
"strict.js": 53167,
"strict.js.gz": 16688,
"strict.wasm": 15115,
"strict.wasm.gz": 7461,
"total": 176271,
"total_gz": 63632
"total": 177273,
"total_gz": 63950
}
2 changes: 1 addition & 1 deletion test/fs/test_nodefs_home.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
int main(void)
{
EM_ASM(
var path = require("path");
var path = process.getBuiltinModule("path");
var home = process.env.HOME;
// On Windows HOME environment variable doesn't exist, but concatenating HOMEDRIVE and HOMEPATH
// does the same thing.
Expand Down
4 changes: 2 additions & 2 deletions test/fs/test_nodefs_rw.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ int main() {

// write something locally with node
EM_ASM(
var fs = require('fs');
var fs = process.getBuiltinModule('fs');
fs.writeFileSync('foobar.txt', 'yeehaw');
);

Expand All @@ -42,7 +42,7 @@ int main() {

// validate the changes were persisted to the underlying fs
EM_ASM(
var fs = require('fs');
var fs = process.getBuiltinModule('fs');
var contents = fs.readFileSync('foobar.txt', { encoding: 'utf8' });
assert(contents === 'cheez');
);
Expand Down
6 changes: 2 additions & 4 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -1665,16 +1665,14 @@ def test_minimal_runtime_export_all_modularize(self):

self.emcc('main.c', ['-sMODULARIZE=1', '-sMINIMAL_RUNTIME=2', '-sEXPORT_ALL', '-sEXPORT_ES6', '-o', 'test.mjs'])

# We must expose __dirname and require globally because emscripten
# uses those under the hood.
# We must expose __dirname globally because emscripten uses it under the
# hood (builtin modules are loaded via process.getBuiltinModule).
create_file('main.mjs', '''
import { dirname } from 'node:path';
import { createRequire } from 'node:module';
import { fileURLToPath } from 'node:url';

// `fileURLToPath` is used to get a valid path on Windows.
globalThis.__dirname = dirname(fileURLToPath(import.meta.url));
globalThis.require = createRequire(import.meta.url);

import Test from './test.mjs';
async function main() {
Expand Down
17 changes: 11 additions & 6 deletions tools/file_packager.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,11 +627,16 @@ def generate_preload_js(data_target, data_files, metadata):
if options.support_node:
ret += " var isNode = globalThis.process && globalThis.process.versions && globalThis.process.versions.node && globalThis.process.type != 'renderer';\n"

if options.support_node and options.export_es6:
if options.support_node:
ret += '''if (isNode) {
const { createRequire } = await import('node:module');
/** @suppress{duplicate} */
var require = createRequire(import.meta.url);
if (process['getBuiltinModule']) {
var getBuiltinModule = process['getBuiltinModule'];
} else {\n'''
if options.export_es6:
ret += ''' var getBuiltinModule = (await import('node:module')).createRequire(import.meta.url);\n'''
else:
ret += ''' var getBuiltinModule = require;\n'''
ret += ''' }
}\n'''

if options.export_es6:
Expand Down Expand Up @@ -910,7 +915,7 @@ def generate_preload_js(data_target, data_files, metadata):
if options.support_node:
node_support_code = '''
if (isNode) {
var contents = require('fs').readFileSync(packageName);
var contents = getBuiltinModule('fs').readFileSync(packageName);
return new Uint8Array(contents).buffer;
}'''.strip()

Expand Down Expand Up @@ -1045,7 +1050,7 @@ def generate_preload_js(data_target, data_files, metadata):
if options.support_node:
node_support_code = '''
if (isNode) {
var contents = require('fs').readFileSync(metadataUrl, 'utf8');
var contents = getBuiltinModule('fs').readFileSync(metadataUrl, 'utf8');
// The await here is needed, even though JSON.parse is a sync API. It works
// around a issue with `removeRunDependency` otherwise being called to early
// on the metadata object.
Expand Down
Loading