Skip to content

Node.js errors when running emconfigure ./configure when there exists a package.json with type: "module" #24244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jeremy-code opened this issue May 2, 2025 · 13 comments

Comments

@jeremy-code
Copy link

jeremy-code commented May 2, 2025

Version

emscripten/[email protected]

$ emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 4.0.8 (70404efec4458b60b953bc8f1529f2fa112cdfd1)
clang version 21.0.0git (https:/github.com/llvm/llvm-project 23e3cbb2e82b62586266116c8ab77ce68e412cf8)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /emsdk/upstream/bin
$ node -v
v20.18.0
$ which node
/emsdk/node/20.18.0_64bit/bin/node

Description

Suppose you had the following files: configure.ac, dummy.c, and a package.json with type: "module". This is true even if that package.json is not in the same directory as ./configure but only the closest package.json.

# configure.ac
AC_INIT([dummy], [0.1])
AC_CONFIG_SRCDIR([dummy.c])
AC_PROG_CC
AC_OUTPUT
// dummy.c
int x = 20;
// package.json
{ "type": "module" }

Failing command line in full:

autoreconf && emconfigure ./configure (or any variation that uses autoconf)

Terminal output:

$ emconfigure ./configure
configure: ./configure
checking for gcc... /emsdk/upstream/emscripten/emcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... configure: error: in `/src':
configure: error: cannot run C compiled programs.
If you meant to cross compile, use `--host'.
See `config.log' for more details
emconfigure: error: './configure' failed (returned 77)

config.log output snippet: (full output, full output with EMCC_DEBUG=1)

# config.log
# ...
configure:2721: checking whether we are cross compiling
configure:2729: /emsdk/upstream/emscripten/emcc -o conftest    conftest.c  >&5
configure:2733: $? = 0
configure:2740: ./conftest
file:///src/conftest:79
  var fs = require('fs');
           ^

ReferenceError: require is not defined in ES module scope, you can use import instead
    at file:///src/conftest:79:12
    at ModuleJob.run (node:internal/modules/esm/module_job:234:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:473:24)
    at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:123:5)

Node.js v20.18.0
configure:2744: $? = 1
configure:2751: error: in `/src':
configure:2753: error: cannot run C compiled programs.
If you meant to cross compile, use `--host'.

Indeed, if you remove type: "module" from package.json, config.log will be able to proceed past that test and a.wasm, config.status will be generated.

configure:2721: checking whether we are cross compiling
configure:2729: /emsdk/upstream/emscripten/emcc -o conftest    conftest.c  >&5
configure:2733: $? = 0
configure:2740: ./conftest
configure:2744: $? = 0
configure:2759: result: no
configure:2764: checking for suffix of object files

Hence, the failing command seems to be emcc -o conftest conftest.c >&5. I have linked the pastebins for the output of the generated files conftest and conftest.c.

I believe the reason this occurs is that a feature was added in Node.js v21.0.0 to automatically run extensionless JavaScript as modules based on the nearest package.json (see nodejs/node#49974). This was backported to Node.js v20.10.0 (nodejs/node#50682).

I haven't found any solutions for this other than generating a package.json in the configure directory with npm init --yes or removing type: "module" altogether. Setting --experimental-default-type=commonjs doesn't seem to fix it.


EDIT:

Alternatively, emconfigure ./configure CFLAGS='-sEXPORT_ES6=1' (or LDFLAGS, LIBS, CPPFLAGS) will work as intended.

@sbc100
Copy link
Collaborator

sbc100 commented May 2, 2025

Yeah it kind of sucks that node doesn't support forcing commonjs from the command line.

I guess one possible solution would be to convert all configure tests to use .mjs and ES modules? Would using the .mjs extensions for ES module mode even if type: module were not in the package.json? i.e. does mjs force module mode?

@jeremy-code
Copy link
Author

Yeah it kind of sucks that node doesn't support forcing commonjs from the command line.

I guess one possible solution would be to convert all configure tests to use .mjs and ES modules? Would using the .mjs extensions for ES module mode even if type: module were not in the package.json? i.e. does mjs force module mode?

Yeah, I'm empathetic that this issue isn't on Emscripten's end and is more of a quirk of Node.js. Personally, I'm fine with my solution of just running npm init --yes in any directories with a configure script since from what I can tell, while autodetecting module syntax is now unflagged (as of v22.7.0), it's still not recommended due to a bit of overhead and adding type: "module" to ES packages is still recommended.

@sbc100
Copy link
Collaborator

sbc100 commented May 5, 2025

When you say "autodetecting module syntax" do you mean looking at the syntax of the file? I was thinking node would just look at the file extension ".mjs"?

@jeremy-code
Copy link
Author

Oh, sorry to clarify, I believe the issue in this case is the lack of file extension — the error that occurs happens when emcc -o conftest conftest.c is ran, and conftest is an extensionless file with a shebang (see here) and conftest.c was in the original post

@sbc100
Copy link
Collaborator

sbc100 commented May 5, 2025

Are there flags we can pass to node to force module behaviour do you know?

@jeremy-code
Copy link
Author

Not to my knowledge — I tried it with --experimental-default-type=commonjs and it didn't work. Doing --input-type=commonjs just prompts that it's intended for use with --eval or stdin. --require I think would work (the fs error doesn't occur) but I didn't get a chance to do it with the wasm output.

I figure the best bet for compatibility would be to see if createRequire can be used to polyfill require in module syntax but I'm not too familiar with the codebase.

@sbc100
Copy link
Collaborator

sbc100 commented May 5, 2025

We do use createRequire already:

emscripten/src/shell.js

Lines 113 to 121 in b277d57

#if ENVIRONMENT_MAY_BE_NODE
if (ENVIRONMENT_IS_NODE) {
#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('module');
/** @suppress{duplicate} */
var require = createRequire(import.meta.url);
#endif

@sbc100
Copy link
Collaborator

sbc100 commented May 5, 2025

I wonder if we can get this bug re-opened: nodejs/node#44457

@sbc100
Copy link
Collaborator

sbc100 commented May 5, 2025

Another possible solution: Put the JS in a .mjs or .cjs file and then have the main executable be one line shell script that calls exec node ..?

@jeremy-code
Copy link
Author

jeremy-code commented May 5, 2025

We do use createRequire already:

emscripten/src/shell.js

Lines 113 to 121 in b277d57
#if ENVIRONMENT_MAY_BE_NODE
if (ENVIRONMENT_IS_NODE) {
#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('module');
/** @Suppress{duplicate} */
var require = createRequire(import.meta.url);
#endif

Actually — I realize you can pass options to emconfigure and for me, emconfigure ./configure LDFLAGS='-sEXPORT_ES6=1' passes while emconfigure ./configure fails. (Shell output: https://pastebin.com/gtLUhS80)

@sbc100
Copy link
Collaborator

sbc100 commented May 5, 2025

Right, is a workaround, but it depends on you knowing to match your configure args with the nearest package.json file in your directory hierarchy.. this is very non-obvious.

@kleisauke
Copy link
Collaborator

FWIW, this was previously reported in #13551 and #17431. Setting the ac_cv_exeext=.cjs environment variable can also be used as a workaround, see: #17451 (comment).

@sbc100
Copy link
Collaborator

sbc100 commented May 5, 2025

OK lets close this in favor of #13551

@sbc100 sbc100 closed this as completed May 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants