A worked example, end to end. The component is Bouncer — a small Component+System pair that reflects an entity's velocity off a surface normal. It lives in your project. You want it in the arcade library.
The starting tree
Bouncer.ts defines BouncerComponent, BouncerSystem, BouncerEvents, BouncerInputEvents. Bouncer.core.ts holds reflectVelocity(...). Bouncer.test.ts exercises the core function and one System integration. meta.json declares the public surface and lists Physics as a dependency.
src/registry.ts has Bouncer wired into it from a previous eject-style scaffold:
Step 1: Dry-run
The first run is always a dry-run. Nothing writes. You read the plan.
The output:
Read top to bottom:
target: arcade— no viz imports, no Solid, no PanelDebuggerSystem subclasses. The auto-detect chosearcade.components: Bouncer— the closure is one entry. Bouncer imports nothing from../Sibling/, so nothing else came along.+ src/components/Bouncer/— the destination folder. The wholesrc/components/Bouncer/will becp -r'd.patched registry: Bouncer— one new line in the project'ssrc/registry.ts.
If the target is wrong, fix it with --target and run the dry-run again. If the closure is wrong, follow the ../Sibling/ imports that brought in the surprise and decide whether to inject the sibling too or refactor it out of Bouncer.
Step 2: Apply
Same plan, no dry-run header, a green confirmation at the end:
Step 3: Inspect the diff
The new folder in the arcade library:
Files are byte-identical to the source unless they had a sibling import to rewrite. For arcade injection, the rewrite replaces @babylonjsmarket/arcade/<Sibling> with the relative ../<Sibling>/<Sibling> only when <Sibling> was co-injected. Bouncer has no co-injected siblings, so no rewrites apply.
The registry diff:
The patch is idempotent. Re-running bjs inject Bouncer does not add a second line — the registry text is scanned for ./components/Bouncer/Bouncer first, and the patch skips if it's already there.
Step 4: Run the tests
Your test should pass in the library exactly as it passed in your project. If it doesn't, the test had a hidden dependency on your project — a fixture file outside the component folder, an alias the project's vitest.config.ts set up, a shared test helper. Fix the test (decouple, or co-inject the helper) and re-run.
A multi-name example
If your Bouncer imported a sibling Surface via ../Surface/Surface, the closure walk would have picked it up automatically and the dry-run would have shown two components:
Sibling-import rewrites only matter when at least two components in the same run reference each other via ../. Then the relative paths stay valid inside the library (both siblings live under src/components/), and the rewrite collapses any @babylonjsmarket/arcade/Surface-style import back to ../Surface/Surface.
A --source example
When your seed lives outside <cwd>/src/components/<Name>/ — for example in a demo project under packages/demo/:
--source applies to one seed only. The transitive closure walk is skipped — when you point at an arbitrary directory, the tool can't reliably reason about which other components the project considers siblings, so it copies just the seed itself.
A practical consequence: if your --source seed imports ../Sibling/, the import won't be rewritten and the file will end up with a broken ../ reference in the pro package. Co-inject the sibling explicitly, or refactor the import out of the seed before running.
A viz example
A panel-debugger seed:
Dry-run:
Output:
The extends PanelDebuggerSystem tipped the target to viz. The .ts file landed in ecs/; the .viz.tsx landed in solid/. Each layer's index.ts gained one export * from './<File>';. The root src/index.ts is left alone — by convention, named re-exports there are author-curated.
What's next
The next section, publish as a product, explains how bjs inject fits into the larger publishing flow: how curator review picks up your submission and how a future new-product wizard will close the loop from a member's CLI to a live marketplace product.