var declarations inside if blocks not hoisted when using __esm wrappers (original) (raw)

Full disclosure: this issue was identified and minimum repro was isolated by Claude Code.

It's a real issue we ran into building Quarto, triggered by upstream dependencies jszip and @jspm/core.

When esbuild creates __esm lazy initialization wrappers (triggered by CommonJS requiring ESM), it fails to hoist var declarations from inline if statement blocks to module scope.

Source pattern

if (condition) var b = getValue();

Current output (buggy)

function useB() { return b(); // ReferenceError: b is not defined }

var init_module = __esm({ "module.js"() { if (condition) var b = getValue(); // b scoped to init function } });

Expected output

var b; // Hoisted to module scope

function useB() { return b(); // OK }

var init_module = __esm({ "module.js"() { if (condition) b = getValue(); // Assignment only } });

Reproduction

https://github.com/gordonwoodhull/esbuild-not-hoisting-conditional-vars

It is not possible to reproduce this bug in the playground since the bug requires:

The repository contains two reproductions:

1. flat/ - Plain esbuild (simpler)

Uses pre-downloaded JSPM CDN files with plain esbuild.

cd flat/ npm install npx esbuild entry-cjs-real-jspm.js --bundle --outfile=bundle-cjs-real-jspm.js npx eslint bundle-cjs-real-jspm.js # Shows 16 errors node bundle-cjs-real-jspm.js # ReferenceError: b is not defined

2. bundled/ - With esbuild-deno-loader (matches real-world usage)

Uses @luca/esbuild-deno-loader plugin with mixed CommonJS/ESM setup.

cd bundled/ npm install deno run -A bundle-with-deno-loader.ts npx eslint bundle-hybrid.js # Shows 18 errors

Impact

This affects:

The bug is often masked by defensive programming. In our environment it does not cause an actual problem at runtime, but it causes eslint to fail, requiring globals to be declared.

This appears related to the fix in esbuild 0.12.17 (PR #1455) for var hoisting in for loop initializers, but if blocks weren't covered.

Environment