fix: [#2374] classic <script> top-level var/function declarations must reach the global object - #2246
Open
marciomazza wants to merge 1 commit into
Conversation
marciomazza
force-pushed
the
fix/classic-script-global-declarations
branch
from
July 18, 2026 19:51
b02b316 to
50c4e33
Compare
Author
|
This PR's CI failure isn't caused by this diff — it's a known flaky test on Note there's a second unrelated flake still on |
marciomazza
force-pushed
the
fix/classic-script-global-declarations
branch
3 times, most recently
from
September 4, 2026 19:36
458e397 to
afaebf0
Compare
<script> top-level var/function declarations must reach the global object
Removes the anonymous function wrapper around evaluated script code, so top-level var/function declarations attach to window (follows the spec). Adds a test covering the behavior and fixes existing tests that hardcoded the old wrapped source.
marciomazza
force-pushed
the
fix/classic-script-global-declarations
branch
from
September 8, 2026 08:50
afaebf0 to
c6e6d4f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Resolves #2374
happy-dom wrapped every classic script body in
(function anonymous($happy_dom) { ... })before running it, trapping top-level "var" and function declarations inside that function's own scope instead of letting them become properties of the global object, as real browsers do (and as the ECMAScript spec requires for classic, non-module scripts). Only module scripts are supposed to get their own scope.Example of the bug:
I ran into this while loading a real-world library (htmx) as a classic
<script src>: the library's own top-levelvar htmx = ...never reachedwindow, even though the exact same file works fine in an actual browser.In this fix, JavaScriptCompiler no longer wraps the code in a function.
$happy_dom(needed fordynamicImport()/dispatchError()) is now bound as a non-enumerable property on the window instead of a function parameter — it stays installed for the window's lifetime because async continuations ofdynamicImport()reference it after the synchronous top level finishes.execute()runs the code directly throughevaluateScript()so top-level declarations land on the global object. Adjacent behavior change: a syntax error now throws fromexecute()instead ofcompile()(e.g. a top-levelreturn→SyntaxError), which is more spec-correct; both callers handle it.AI
I used Claude Code to write this, over multiple iterations. I guided and reviewed
it myself at every step.
Before submitting the PR, please make sure you do the following:
Tests
npm testlocally to make sure that all tests pass before submitting the PR. (Full suite passes locally. Also rantsc --noEmitandeslint --max-warnings 0clean.)Title
feat,fix,choreorBREAKING CHANGE. The issue is optional and can be omitted if the pull request does not relate to an issue.