Skip to content

Downlevel the Babylon.js core bundle to ES5 in the nightly script fetch - #1789

Merged
bghgary merged 4 commits into
BabylonJS:masterfrom
bghgary:nightly-downlevel-core-script
Jul 27, 2026
Merged

Downlevel the Babylon.js core bundle to ES5 in the nightly script fetch#1789
bghgary merged 4 commits into
BabylonJS:masterfrom
bghgary:nightly-downlevel-core-script

Conversation

@bghgary

@bghgary bghgary commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

[Created by Copilot on behalf of @bghgary]

Context

The nightly has failed every run since 2026-07-07. Glow layer and LODs never satisfies Scene.executeWhenReady, burns the 600s budget and aborts the run at test 24; a green run executes 294.

Babylon.js emits its UMD bundles at an ES2015 target since #18647 — the TC39 decorator migration needs the accessor keyword. Babylon Native runs them on Chakra, which consumes ES5. Babylon.js added a downlevel step for this in #18630 and applies it to babylon.max.js before launching our Playground.exe / UnitTests.exe in its monorepo pipeline, which is why the Babylon.js side of this same test stays green. getNightly never got the equivalent.

Nothing errors: the bundle loads, and meshes, textures, the glow render target and all four blur post-processes all report ready. GlowLayer.isLayerReady() just stays false. Any scene with a GlowLayer is affected — the LOD in the failing test is incidental, and it surfaces through this test alone because every other glow scene is already excludeFromAutomaticTesting.

Change

getNightly downlevels node_modules/babylonjs/babylon.max.js after fetching. Playground and UnitTests both copy that file, so one transform covers both. Only the core bundle needs it: Chakra parses ES2015 classes and arrows fine, and it is the Stage 3 decorator emit that breaks it — the accessor keyword appears twice in preview core and not at all in preview addons. Babylon.js downlevels the same single file.

getNightly also now fetches babylonjs-addons, which it had been skipping. The Playground loads addons immediately after the core bundle, so a preview core was running against an npm-pinned addons build.

downlevelNativeScripts.mjs is a verbatim copy of the Babylon.js script apart from a provenance header, so the two stay diffable.

Downloads are now awaited and status-checked — they were fire-and-forget, so the downlevel could race a partial write.

Verification

Win32 x64 D3D11 against the current preview: the full suite goes from aborting at test 24 to ran=296 passed=296 failed=0, both with addons pinned and with addons fetched from preview.

Swapping only babylon.max.js in one unchanged build:

babylonjs Result
9.15.0 passes
9.16.0 – 9.18.0 never ready
9.16.0, downleveled passes

Unaffected in Chromium on all of those versions, so this is the script target rather than a Babylon.js regression.

The nightly pipeline has failed every scheduled run since 2026-07-07. `Run
VisualizationTests` stalls on `Glow layer and LODs`, which never satisfies
`Scene.executeWhenReady`, burns the 600s readiness budget and aborts the run at
the 24th test. A green run executes 294.

Babylon.js emits its UMD bundles at an ES2015 target starting with 9.16.0: the
TC39 decorator migration relies on the `accessor` keyword, which cannot be
expressed in ES5, and esbuild cannot emit ES5 classes. Babylon Native runs those
bundles on Chakra, which consumes ES5-level script.

Babylon.js already accounts for this. Its monorepo pipeline downloads this
repository's nightly artifacts and runs `downlevel:native-scripts` over
`babylon.max.js` before it launches either UnitTests.exe or Playground.exe,
which is why the Babylon.js side of the same test stays green. `npm run
getNightly` had no equivalent step, so the nightly ran the ES2015 bundle
directly.

The failure mode is silent rather than a parse error. The bundle loads, the
engine reports itself launched, meshes and textures become ready, the glow
layer's render target and all four blur post-processes report ready, and no
exception or shader compilation error is logged - `GlowLayer.isLayerReady()`
simply stays false forever. Any scene containing a `GlowLayer` is affected; the
LOD and `customEmissiveColorSelector` in the failing test are incidental. It
surfaces through this one test only because every other glow-layer scene in
config.json is already `excludeFromAutomaticTesting`.

`getNightly` now downlevels `node_modules/babylonjs/babylon.max.js` after
fetching. Both consumers read that same file - Apps/UnitTests/CMakeLists.txt and
Apps/Playground/CMakeLists.txt each copy it out of node_modules - so one
transform at the source covers both.

TypeScript performs the transform rather than Babel, for the reason Babylon.js
documents in its own script: Babel's ES5 class lowering emits
`Reflect.construct` / `_wrapNativeSuper` machinery for classes extending native
built-ins such as `Error`, and that machinery runs at class-definition time and
hard-crashes Chakra as the bundle loads.

The downloads in `getNightly` are now awaited and check their status code. They
were fire-and-forget, so a failed fetch silently left the previous file in
place, and the down-level step that now follows must not race an incomplete
write.

Verified on Win32 x64 D3D11 against the current preview build: the full
visualization suite goes from aborting at test 24 to `ran=296 passed=296
failed=0`. Swapping only `babylon.max.js` in one unchanged Playground build
confirms the boundary - 9.15.0 passes, 9.16.0 through 9.18.0 hang, and 9.16.0
passes once downleveled. The same scene is unaffected in a browser on all of
those versions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 714b4495-258e-4645-abf7-26c17bc29d5b
Copilot AI review requested due to automatic review settings July 24, 2026 23:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Babylon Native nightly failures on Chakra by adding an explicit ES5 downleveling step for the fetched Babylon.js core UMD bundle (babylon.max.js) after downloading the nightly artifacts.

Changes:

  • Make getNightly downloads awaited and fail-fast on HTTP errors.
  • Add a new Node/TypeScript-based downlevel script that transpiles the Babylon.js core bundle to ES5.
  • Wire the downlevel step into Apps npm scripts and add TypeScript as a dev dependency.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 2 comments.

File Description
Apps/scripts/getNightly.js Converts downloads to awaited, status-checked promises before continuing.
Apps/scripts/downlevelNativeScripts.mjs New script that transpiles large UMD bundles to ES5 using the TypeScript compiler API.
Apps/package.json Runs downleveling after nightly fetch; adds a reusable downlevel script entry.
Apps/package-lock.json Adds TypeScript dev dependency to support the downlevel script.
Files not reviewed (1)
  • Apps/package-lock.json: Generated file

Comment thread Apps/scripts/getNightly.js Outdated
Comment thread Apps/scripts/downlevelNativeScripts.mjs Outdated
…verbatim

getNightly refreshed babylonjs, -gui, -loaders, -materials and -serializers but
not babylonjs-addons, so every nightly ran a preview core against an npm-pinned
addons build. The Playground loads addons immediately after the core bundle
(PlaygroundScripts.cpp), so the two were free to drift a full release apart.
Addons is subject to the same ES2015 target as the core - the preview build has
13 class declarations and 93 arrow functions where the npm 9.15.0 build has
none - so it is down-leveled alongside it. UnitTests does not load addons.

downlevelNativeScripts.mjs is now a verbatim copy of the Babylon.js script,
apart from a provenance header. The earlier adaptation had dropped the
diagnostic handling: Babylon.js's version throws on fatal syntactic diagnostics
and on empty output, whereas the trimmed version ignored both, so a corrupt
download would have silently produced a broken bundle - the same class of silent
failure this branch exists to fix. Keeping the two files diffable also stops
them drifting.

Verified on Win32 x64 D3D11 against the current preview with both bundles
down-leveled: ran=296 passed=296 failed=0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 714b4495-258e-4645-abf7-26c17bc29d5b
@bghgary bghgary changed the title Downlevel the Babylon.js core bundle to ES5 in the nightly script fetch Downlevel the Babylon.js bundles to ES5 in the nightly script fetch Jul 24, 2026
The previous commit down-leveled babylonjs.addons.js alongside babylon.max.js.
That was unnecessary. Chakra parses ES2015 classes and arrow functions without
trouble; what it cannot take is the Stage 3 decorator emit, and the `accessor`
keyword that produces appears twice in the preview core bundle and not at all in
preview addons. Babylon.js reaches the same conclusion in its own pipeline - it
replaces every babylon* script from its snapshot but down-levels only
babylon.max.js.

Verified on Win32 x64 D3D11 against the current preview with addons left as the
raw ES2015 build: ran=296 passed=296 failed=0.

Addons is still fetched from preview, which is the point of the previous commit;
only the down-level of it is dropped.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 714b4495-258e-4645-abf7-26c17bc29d5b
@bghgary bghgary changed the title Downlevel the Babylon.js bundles to ES5 in the nightly script fetch Downlevel the Babylon.js core bundle to ES5 in the nightly script fetch Jul 24, 2026
Addresses review feedback on the download helper.

Resolving on the write stream's `finish` event released the promise once the
data was flushed but before the descriptor was closed. The down-level step runs
against these files immediately afterwards, so on Windows that is a file-lock
race.

A server that aborts mid-transfer emitted neither `finish` nor `error` on the
file, so the promise never settled and the script hung. Confirmed against the
previous implementation with a local server that destroys the response
mid-body: it never settles, while the new one rejects in ~60ms. `response`
error and abort are now handled, the request has a two minute timeout, and the
stream is destroyed on failure.

Exercised all three failure paths against a local server - non-200, mid-transfer
abort, and connection refused - each rejects promptly rather than hanging.
`npm run getNightly` still produces the same bytes as before.

The other review comment, about ignoring `ts.transpileModule` diagnostics, was
raised against the trimmed adaptation of the down-level script and no longer
applies: that file is now a verbatim copy of the Babylon.js script, which passes
`reportDiagnostics: true`, throws on fatal diagnostics and throws on empty
output.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 714b4495-258e-4645-abf7-26c17bc29d5b
@bghgary
bghgary merged commit 866b6a8 into BabylonJS:master Jul 27, 2026
34 checks passed
@bghgary
bghgary deleted the nightly-downlevel-core-script branch July 27, 2026 15:41
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.

4 participants