#2845

Conditional module.exports assignment refuses even under if (true)

Openjlucaso1opened 11d ago

A module.exports assignment inside a branch is refused at module initialization even when the condition is the literal true. The build succeeds; the executable exits with SC1090 before the importer can use the exported function.

Reproduced on Linux x86_64 with scriptc 0.4.7, compiler source 71450b617d848ee79417762c826f545002468f16 from #2842 on base 5989ca7edec19af60379f41e97904b849955c0fb, and Node v24.22.0. The #2842 change concerns a util function value and does not change this export guard.

From a built scriptc checkout, with Node v24.22.0 selected:

task_cjs_repro=$(mktemp -d)
cat > "$task_cjs_repro/dep.cjs" <<'JS'
if (true) { module.exports = function sleep() { return 'shared' } }
JS
cat > "$task_cjs_repro/main.cjs" <<'JS'
const sleep = require('./dep.cjs')
console.log(typeof sleep, sleep())
JS
node "$task_cjs_repro/main.cjs"
node packages/cli/dist/main.js build "$task_cjs_repro/main.cjs" --node-islands off -o "$task_cjs_repro/native"
"$task_cjs_repro/native"
printf 'native_exit=%s\n' "$?"

Node prints:

function shared

The native build succeeds and prints its artifact path. Running that artifact prints no stdout, reports the following error, and exits 1. The temporary-directory prefix is shortened here:

Uncaught Error: replacing module.exports outside a top-level statement (a conditional or environment-dependent export object) is not supported yet [SC1090 at <repro>/dep.cjs:1]

The expected behavior for this constant-condition case is the same exported function and output as Node. This reproduction uses no npm dependency, SharedArrayBuffer, Atomics, timers, or network activity.

The broader real-world shape comes from Pino's atomic-sleep dependency:

if (typeof SharedArrayBuffer !== 'undefined' && typeof Atomics !== 'undefined') {
  module.exports = function sleep () { return 'shared' }
} else {
  module.exports = function sleep () { return 'fallback' }
}

Both the runtime-capability version and the constant-true version reach the named export-placement refusal. The constant version isolates the problem from support for those builtins. A control with a single top-level conditional-expression export compiles and selects the expected function; no compiler fix is proposed here.

Related work:

  • #1434 intentionally introduced the named conditional-export refusal and its test fixture.
  • #1558 was closed unmerged under the upstream CommonJS scope decision. This report records the smaller constant-branch case rather than proposing broader export-object emulation.
  • #2295 is an open proposal for process.env defines; it does not directly address this literal-true reproduction or builtin capability checks.
  • Open #1777 tracks other CommonJS entry/resolution failures, including SC2013. It does not contain this SC1090 reproduction.

I checked the open issue list and searched for module.exports, CommonJS, conditional exports, and atomic-sleep before filing; no equivalent open issue was found. This is an issue and reproduction only, with no implementation PR.

Activity

0

No activity yet.