logo

Babylon.js Market

Members Only

Round-trip with Eject

Download, inject, and sell assets & code on BabylonJS Market

Once your component is in the @babylonjsmarket/arcade library, a downstream consumer can pull it into their project the same way they pull anything else out of the arcade library — with arcade eject. The shape of that round-trip is the contract you sign by publishing.

What a downstream consumer does

They scaffold a project from create-arcade, install @babylonjsmarket/arcade, see your component listed in the registry, and decide they want to edit it locally:

Terminal
npx @babylonjsmarket/create-arcade my-game
cd my-game
npm install @babylonjsmarket/arcade
npx arcade eject Bouncer

After the eject:

text
my-game/src/components/Bouncer/
├── Bouncer.ts
├── Bouncer.core.ts
├── Bouncer.test.ts
└── meta.json

The folder shape is the one you injected — eject preserves it because eject and inject share the same per-component layout.

What gets rewritten

arcade eject reads the file you wrote and rewrites sibling imports based on which siblings were ejected alongside.

  • A sibling that was ejected too — say the consumer ran arcade eject Bouncer Surface — keeps its relative path: ../Surface/Surface. Both live under the consumer's src/components/ and the path still resolves.
  • A sibling that was not ejected — say the consumer kept Surface in the package — gets rewritten from ../Surface/Surface to @babylonjsmarket/arcade/Surface. The import now points at the package subpath.

The rewrite is the inverse of what bjs inject does. When you inject, package subpaths collapse to relative paths because the siblings now share a parent. When the consumer ejects, relative paths to non-ejected siblings expand back to package subpaths because the siblings are once again on different sides of the project/package boundary.

The pure-data files — your meta.json, your tests, your Bouncer.core.ts — are copied byte-identical unless they contained one of these sibling imports.

The contract you're signing

By publishing into the arcade library you commit to a few things, all of them eject-shaped:

  • Stable filenames. Your <Name>.ts, <Name>.core.ts, <Name>.test.ts are what shows up in the consumer's project. Renaming any of them is a breaking change for anyone who's ejected.
  • Stable export names. BouncerComponent, BouncerSystem, BouncerEvents are referenced by name from scene JSON and from the lazy registry. Renaming any of them is a breaking change.
  • Stable event names. The strings inside BouncerEvents are what other Systems subscribe to. Renaming a 'bouncer.bounced' to 'bouncer.collision' is a breaking change.
  • Self-contained logic. Anything your component imports via ../Sibling/ either lives in the same library (the import rewrites cleanly) or lives in an ecs-side dependency (ecs/babylon/three imports are left alone). Reaching into the consumer's project from inside a published component breaks the round-trip.
  • No magic strings tied to your game. Hard-coded asset paths, scene names, or layer IDs from your project will follow the component into the library and out into the consumer's project. Either generalize them away or document them as required configuration.

What stays in the library

Your component keeps living in the arcade library after the consumer ejects it. The eject is a copy, not a move. The consumer's local copy overrides the package copy via their project's src/registry.ts — that's the only thing that changes about which copy wins at runtime.

A consumer who ejects and then deletes their local folder reverts to using your published copy. The package install always works.

The viz round-trip

The viz side of the eject story is the same in spirit but different in layout. viz is flat — every file lives in one of three layers — so the eject moves files out of @babylonjsmarket/viz's <layer>/ tree into the consumer's src/components/<Name>/<File>.ts. The layer information is preserved in the file name (your MyDebugger.viz.tsx was already named that way).

The import rewrites use the same symmetry: @babylonjsmarket/viz/<Sibling> collapses to a relative path when the sibling is also being ejected; otherwise it stays a package subpath.

A practical test

The simplest way to know whether your component is eject-compatible is to publish a dry-run-equivalent of the round-trip before you ship: inject into a scratch checkout of the arcade library, then run arcade eject <YourThing> from a fresh create-arcade project that installs your scratch arcade by file: path. If the ejected folder runs your tests green, the round-trip works. If it doesn't — usually because something reached across the boundary — fix it before you publish for real.

What's next

The next section, troubleshooting, lists the real errors bjs inject emits and what to do about each one.

Was this page helpful?

We read every note — tell us what's working and what isn't.

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search