Skip to content

fix: Hot reload edited ESM scripts - #9989

Merged
kpal81xd merged 1 commit into
mainfrom
fix/esm-script-hot-reload
Nov 10, 2026
Merged

kpal81xd merged 1 commit into
mainfrom
fix/esm-script-hot-reload

Conversation

@kpal81xd

Copy link
Copy Markdown
Contributor

Description

Hot reloading with swap never worked for ESM (.mjs) scripts. Saving an edited script left the old code running and swap was never called. Two things stopped it:

  • The loader cache. The script handler deletes its cache entry so an edited script loads again. For ESM scripts it deleted the entry under the URL with its query removed, which isn't the key the loader stored it under. The entry stayed, and the next load returned the old result without reaching the handler.
  • The module map. The browser keeps an imported module for the life of the page, so importing the same URL again returns the old module.

This PR:

  • deletes the loader cache entry under the key it was stored with;
  • imports a module script by its plain URL the first time, as now, so scripts that import each other still share one module (ESM Preload Fix #7161);
  • imports it with ?t=<file hash> only when the file has been edited since then, so the new code runs, re-registers its script types and fires swap.

Only the edited file is evaluated again. Modules that import it, or that it imports, keep their existing instances, and its top-level code runs again. Each edit keeps one more module instance in memory until the page reloads. Published builds don't change their files at runtime, so they are unaffected.

Fixes playcanvas/editor#1337
Part of playcanvas/editor#1115

Manual smoke test

  1. In an Editor project using this engine, add an ESM script with a swap(old) method and an update that rotates its entity, and attach it to an entity.
  2. Launch the scene.
  3. Change the rotation speed in update and save.

Expected: the launched app rotates at the new speed without a refresh, and swap is called.

Checklist

  • I have read the contributing guidelines
  • My code follows the project's coding standards
  • This PR focuses on a single change

@github-actions

Copy link
Copy Markdown

Public API report

This PR changes the public API surface (+1 / −1), per the docs' rules (@ignore / @Private / undocumented are excluded).

Show API diff
-ScriptHandler.load(url: any, callback: any): void
+ScriptHandler.load(url: any, callback: any, asset: any): void

Informational only — this never fails the build.

@github-actions

Copy link
Copy Markdown

Build size report

This PR changes the size of the minified bundles.

Bundle Minified Gzip Brotli
playcanvas.min.js 2829.4 KB (+142 B, +0.00%) 736.6 KB (+63 B, +0.01%) 570.3 KB (+255 B, +0.04%)
playcanvas.min.mjs 2826.1 KB (+142 B, +0.00%) 734.7 KB (+50 B, +0.01%) 569.3 KB (−281 B, −0.05%)

@kpal81xd

Copy link
Copy Markdown
Contributor Author

Tested in the Editor with a launched scene. The scene has one entity using an ESM script with a swap method, and each edit changes the script's file the way saving it does.

Engine After saving an edited ESM script Script type running swap called
2.23.3 The launch page received the new file The old version Never
This PR Two edits in a row Updated to each new version, on the registry and the live instance Once per edit

initialize was not called again, and there were no console errors.

Not tested: typing in the code editor itself (the edits were saved programmatically), and browsers other than Chrome.

@kpal81xd
kpal81xd marked this pull request as ready for review November 10, 2026 14:24
@kpal81xd
kpal81xd merged commit c4b180f into main Nov 10, 2026
9 checks passed
@kpal81xd
kpal81xd deleted the fix/esm-script-hot-reload branch November 10, 2026 14:30
kpal81xd added a commit that referenced this pull request Nov 10, 2026

This branch was successfully deployed

2 active deployments
Preview – engine — 92c9e369 Deployed Nov 10, 2026 by vercel[bot]
Preview – engine-api-docs — 92c9e369 Deployed Nov 10, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hot reloading not working with .mjs scripts

1 participant