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 sharedThe 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.envdefines; 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
0No activity yet.