Downlevel the Babylon.js core bundle to ES5 in the nightly script fetch - #1789
Merged
Merged
Conversation
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
Contributor
There was a problem hiding this comment.
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
getNightlydownloads 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
Appsnpm 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
…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
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
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
VicenteCartas
approved these changes
Jul 25, 2026
bkaradzic-microsoft
approved these changes
Jul 25, 2026
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.
[Created by Copilot on behalf of @bghgary]
Context
The nightly has failed every run since 2026-07-07.
Glow layer and LODsnever satisfiesScene.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
accessorkeyword. Babylon Native runs them on Chakra, which consumes ES5. Babylon.js added a downlevel step for this in #18630 and applies it tobabylon.max.jsbefore launching ourPlayground.exe/UnitTests.exein its monorepo pipeline, which is why the Babylon.js side of this same test stays green.getNightlynever 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 staysfalse. Any scene with aGlowLayeris affected — the LOD in the failing test is incidental, and it surfaces through this test alone because every other glow scene is alreadyexcludeFromAutomaticTesting.Change
getNightlydownlevelsnode_modules/babylonjs/babylon.max.jsafter 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 — theaccessorkeyword appears twice in preview core and not at all in preview addons. Babylon.js downlevels the same single file.getNightlyalso now fetchesbabylonjs-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.mjsis 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.jsin one unchanged build:Unaffected in Chromium on all of those versions, so this is the script target rather than a Babylon.js regression.