Skip to content

Lazy-load player adapters on demand via ESM dynamic import #229

Description

@maboa

Problem

Once player adapters are split into separate files (#228), consumers using non-native players still have to include the right <script> tag(s) manually. The library already knows which adapter is needed — it reads data-player-type on the player element at instantiation. Letting the core fetch the adapter itself would remove the manual step entirely.

Proposed direction

Once the library is distributed as ES modules (#218), the hyperaudioPlayer factory can do something like:

async function hyperaudioPlayer(playerType, instance) {
  if (playerType === 'native') return new NativePlayer(instance);
  const mod = await import(`./players/${playerType}.js`);
  return new mod.default(instance);
}

That means:

  • Consumers using SoundCloud/YouTube/Vimeo/VideoJS/Spotify can drop the extra <script> tag from their HTML. Including the core is enough.
  • Native-only consumers (the common case) never pay for adapter bytes.
  • Paths are module-relative — no document.currentScript.src discovery, no global registration shims, no consumer-configured playerPath option.

Why this needs #218 first

Without ESM, doing the same thing with classic scripts requires:

  • A custom loader using dynamic <script> injection
  • Base-path discovery via document.currentScript.src (fragile)
  • A global-registration namespace for adapters to attach themselves to
  • Manual error handling for failed loads

All of that gets replaced by a one-line import() once ESM is in place. Better to wait than build a loader we throw away.

Why this needs #228 first

The adapters need to be in separate files before they can be loaded separately. #228 does that refactor without changing runtime behaviour.

Compat notes

Once shipped, this changes behaviour in a few subtle ways consumers should know about:

  • Constructor becomes async-ish. new HyperaudioLite(...) returns before the adapter has loaded. instance.myPlayer won't exist for a brief window. Transcript clicks during that window need to queue (or no-op gracefully). Direct access patterns like instance.myPlayer.play() immediately after construction break.
  • First-play latency. On slow connections, clicking play within the first ~200ms after page load might wait for the adapter fetch. Usually imperceptible.
  • Error UX shifts to runtime. A 404 on the adapter file shows up at HAL instantiation rather than at page load. Needs a clear console error.
  • YouTube API script load serialises. Currently the YouTube IFrame API script load happens during page load. With lazy loading, it's: youtube.js fetch → then YT API script fetch → then ready. Two round trips instead of one.

#217 (options-object constructor) would give a natural place to expose an onReady callback for consumers who need to know when the adapter has finished loading.

Related

  • Depends on #218 — ESM/CJS distribution
  • Depends on #228 — split adapters into separate files
  • Touches #217 — options-object constructor (natural home for an onReady callback)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions