Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: playcanvas/engine
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.19.2
Choose a base ref
...
head repository: playcanvas/engine
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v2.19.3
Choose a head ref
  • 4 commits
  • 11 files changed
  • 3 contributors

Commits on Jun 4, 2026

  1. Fix ESM script registration by name and subclass name collision (#8831)

    * Fix ESM script registration by name and subclass name collision
    
    Script name resolution previously read `__name`/`scriptName` through the
    prototype chain, which caused two bugs:
    
    - ESM scripts declare their name via a static `scriptName` field. Being a
      class field, it shadows the inherited `scriptName` accessor with [[Define]]
      semantics, so the setter that assigns `__name` never runs. `ScriptRegistry.add`
      read the unset `__name` and registered the script under a null key, so it
      could not be created by name (e.g. `entity.script.create('test')`), breaking
      templates/procedural/async workflows.
    
    - A subclass inherited its base class's already-assigned `__name`/`scriptName`,
      so registering the subclass overwrote the base in the registry (and could
      trigger an unintended hot-swap).
    
    Name resolution now considers only own properties of the class. Logic is
    centralized in `getScriptRegistryName` (own `__name` -> own `scriptName` ->
    lowerCamelCase class name) and used by `registerScript`, `ScriptRegistry.add`,
    `ScriptComponent.create`, and the ESM script handler, so all paths resolve the
    same name. Nameless scripts now consistently use the lowerCamelCase class name
    (previously `registerScript`/`add` used PascalCase while the asset loader and
    `create` already used camelCase).
    
    Adds dedicated ScriptRegistry tests covering both fixes.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    * Harden script name resolution against unresolvable names
    
    Address review feedback on edge cases where a script name cannot be resolved
    (e.g. an anonymous class with an empty `.name` and no static `scriptName`):
    
    - `toLowerCamelCase` returns the input unchanged for empty/falsy values instead
      of throwing on `str[0]`.
    - `registerScript` logs an error and returns early instead of registering under
      an undefined name and pushing to `ScriptTypes`.
    - `ScriptComponent.create` returns null (and logs) rather than indexing the
      instance under an `undefined`/empty key.
    - the ESM script handler skips exports whose name cannot be resolved.
    
    Adds tests for the registerScript and ScriptComponent.create fail-fast paths.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
    Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
    3 people committed Jun 4, 2026
    Configuration menu
    Copy the full SHA
    374e9df View commit details
    Browse the repository at this point in the history
  2. Use a Map for the script registry and guard reserved names in add() (#…

    …8835)
    
    Store registered scripts in a Map instead of a plain object so that script
    names colliding with `Object.prototype` members (e.g. `hasOwnProperty`,
    `toString`, `__proto__`) are stored and looked up safely, rather than shadowing
    methods on the backing object (which caused a TypeError) or, in the case of
    `__proto__`, silently failing to store.
    
    Also enforce the reserved-name check in `ScriptRegistry.add()` so the public
    `app.scripts.add()` path is validated (returns false), not only the
    `registerScript`/`createScript` paths (which still throw, unchanged). The
    reserved-name set is moved to `constants.js` to avoid an import cycle.
    
    Adds tests for collision-prone names and reserved-name rejection via add().
    
    Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
    Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
    3 people committed Jun 4, 2026
    Configuration menu
    Copy the full SHA
    16271aa View commit details
    Browse the repository at this point in the history
  3. fix(build): preserve live bindings of mutable UMD exports (pc.app) (#…

    …8837)
    
    The UMD footer bridged the esbuild IIFE namespace to the global `exports`
    object with `Object.assign(exports, pc)`. `Object.assign` invokes each
    getter once and copies its *value*, so the mutable `app` export (null
    until an application is created) was snapshotted to null and `pc.app`
    never updated.
    
    Copy the property descriptors instead, so the getter — and therefore the
    live binding — is preserved on the UMD namespace. The ESM build was
    already unaffected as it uses native live bindings.
    
    Fixes #8836
    
    Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
    Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
    3 people committed Jun 4, 2026
    Configuration menu
    Copy the full SHA
    668b11a View commit details
    Browse the repository at this point in the history
  4. 2.19.3

    Martin Valigursky committed Jun 4, 2026
    Configuration menu
    Copy the full SHA
    4ca251a View commit details
    Browse the repository at this point in the history
Loading