Skip to content

fix: rename declarations shadowing globals used by the emit - #505

Closed
dsherret wants to merge 1 commit into
denoland:mainfrom
dsherret:fix_shadowed_global_declarations
Closed

fix: rename declarations shadowing globals used by the emit#505
dsherret wants to merge 1 commit into
denoland:mainfrom
dsherret:fix_shadowed_global_declarations

Conversation

@dsherret

@dsherret dsherret commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

Closes #444

A module declaring a module scoped binding with the same name as an identifier the emit relies on caused the output to resolve that identifier to the declaration instead of the global:

const Object = "hello";
export default Object;

...emitted CommonJS that throws with "Cannot access 'Object' before initialization":

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Object = "hello";
exports.default = Object;

The ES module output has the same problem when downleveling (ex. class fields emit Object.defineProperty(this, ...) when targeting below ES2022).

This adds a compiler transformer that renames these module scoped declarations (const Object_1 = "hello";) along with their references, keeping the original export names so the public API and declaration files are unchanged:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Object_1 = "hello";
exports.default = Object_1;

Notes:

  • The type checker is only asked for symbols when a module actually declares one of these names, so the common case is a scan of the top level statements.
  • Exported variable declarations don't create a module scoped binding in the CommonJS emit (they become properties on exports), so they're left alone there.
  • Renamed exports are re-added at the end of the file as export const <name> = <renamed>; for CommonJS/UMD (the CommonJS transform can't resolve a synthesized export specifier) and as export { <renamed> as <name> }; for ES modules.

A module declaring something like `const Object = "hello";` in the module
scope caused the emit to reference that declaration instead of the global
and throw at runtime (ex. `Object.defineProperty(exports, "__esModule",
{ value: true });` in the CommonJS output or `Object.defineProperty(this,
...)` in the ES module output when downleveling class fields).

Closes denoland#444

Claude-Session: https://claude.ai/code/session_01SqfDuWeEek5XnHSbXxCUE9
@dsherret

Copy link
Copy Markdown
Collaborator Author

Bad and way too complicated. I think this is a limitation in TypeScript and we might not want to fix it.

@dsherret dsherret closed this Jul 26, 2026
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

Successfully merging this pull request may close these issues.

Compiling a module that exports Object defined anew results in invalid CommonJS code

1 participant