analytics-next icon indicating copy to clipboard operation
analytics-next copied to clipboard

Issue: ajs-destination dynamic import breaks when analytics-next is re-bundled (Vite / ESBuild / library mode)

Open duytruong0916 opened this issue 1 month ago • 0 comments

Hi team, we’ve run into a reproducible issue with @segment/analytics-next when it’s consumed inside a multi-layer package setup.

Summary

When @segment/analytics-next is included inside a library that is then re-bundled downstream (e.g. Vite → Vite → Vite), the dynamic import of ../plugins/ajs-destination does not retain a stable module shape. The code assumes:

const mod = await import('../plugins/ajs-destination')
mod.ajsDestinations(/* … */)

But after bundling, this often becomes:

import("./index-xxxx.mjs").then(mod => mod.ajsDestinations)

…and mod ends up being:

Module { [Symbol.toStringTag]: "Module" }

with no ajsDestinations property, leading to:

TypeError: mod.ajsDestinations is not a function

This causes a hard crash or blank screen unless we patch the package to tolerate different module shapes.

This leads to a crash or a blank screen. Our temporary workaround does not fully resolve the issue — it only prevents the hard failure and allows the app to degrade gracefully without going blank.

What we’ve observed

The original ajs-destination module does export ajsDestinations.

However, when analytics-next is bundled more than once, Vite / ESBuild / Rollup reshape the module, and the dynamic import may return any of the following:

mod.ajsDestinations

mod.default.ajsDestinations

mod.default (function)

mod itself (function)

A Module {} namespace wrapper with no callable export

The SDK only checks for mod.ajsDestinations, so any other shape causes initialization to fa

const ajs =
  mod.ajsDestinations ??
  mod.default?.ajsDestinations ??
  (typeof mod.default === 'function' ? mod.default : null) ??
  (typeof mod === 'function' ? mod : null)

This restores functionality and avoids blank pages, but obviously isn’t ideal.

Request

Would it be possible for the SDK to:

  • Make the dynamic import resolution more robust (supporting default and function exports), or

  • Move the legacy-destination loader to a statically imported module (avoiding dynamic import shape drift), or

  • Provide a documented API/flag to disable legacy destination loading entirely when bundling?

This would make the SDK more resilient in real-world bundler pipelines (especially monorepos, Vite library mode, and multi-layer package architectures).

Happy to provide repro steps or a minimal Vite project that demonstrates the issue.

Thanks!

duytruong0916 avatar Nov 13 '25 19:11 duytruong0916