logo

Babylon.js Market

Members Only

Target Selection

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

bjs inject routes into one of two namespaces. The choice — arcade or viz — is the first thing the tool resolves, and the rest of the run depends on it.

arcade vs viz

The arcade namespace is the per-component layout of @babylonjsmarket/arcade. Each component lives in its own folder under src/components/<Name>/ and gets a lazy resolver in src/registry.ts. This is the same layout as @babylonjsmarket/arcade itself — same folder shape, same scene-loader convention, same eject behaviour. Components that do something in the game world (a new physics body type, a new camera mode, a new enemy AI brain) belong here.

The viz namespace is @babylonjsmarket/viz, the debug/visualization layer over @babylonjsmarket/ecs. It's flat, not per-component: every file lives in one of three layers — core, solid, or ecs — and the layer's index.ts re-exports it. Components that visualize, debug, or step through ECS state (a Solid debug panel, a custom State stepper, a panel-debugging System) belong here.

The split exists because viz depends on Solid and a few debug-only utilities that an arcade component shouldn't pull in. Mixing them would force every consumer of arcade to ship Solid even when they don't render any panels.

When to use which

Pick arcade when:

  • Your component is a Component+System pair that ticks every frame.
  • The System reads/writes mesh handles, physics bodies, or scene state.
  • The Component data is the kind of thing you'd describe in scene JSON.

Pick viz when:

  • Your code subclasses PanelDebuggerSystem or StateStepperSystem.
  • Your code imports anything from @babylonjsmarket/viz.
  • Your code is a Solid panel that reads ECS state but doesn't mutate the world.

The two are not mutually exclusive at the project level — your project can promote some components to arcade and others to viz. But each bjs inject run targets exactly one namespace.

Auto-detect rules

When you don't pass --target, the tool reads every .ts/.tsx in the seed's closure and applies this check, in order:

TypeScript
// From src/cli/detect-target.ts:
for (const { source } of seedFiles) {
  if (/from\s+['"]@babylonjsmarket\/viz['"]/.test(source)) return "viz";
  if (/extends\s+(PanelDebuggerSystem|StateStepperSystem)\b/.test(source)) return "viz";
}
return "arcade";

In plain English:

  1. Any file in the closure imports from @babylonjsmarket/vizviz.
  2. Any file extends PanelDebuggerSystem or StateStepperSystemviz.
  3. Otherwise → arcade.

A seed with no viz markers always routes to arcade. A seed with a single Solid panel that imports from viz routes to viz — even if most of the seed is plain.

Multi-seed runs

When you pass several names in one run, every seed must individually agree on the target. The tool re-runs detection per-seed and aborts on mismatch:

text
Auto-detect disagrees across seeds: "Bouncer" → arcade, others → viz.
Pass --target explicitly or split into separate runs.

Two valid responses: pass --target to force the call (rare — usually means one seed is in the wrong target), or run the two seeds separately.

Overriding the target

Terminal
bjs inject Foo --target arcade
bjs inject Foo --target viz

--target skips auto-detection entirely. Use it when:

  • You want certainty and don't want the tool's heuristic involved.
  • A seed is on the boundary and the auto-detect picks wrong.
  • You're injecting a seed via --source that the auto-detect can't reason about confidently.

An unknown target value is rejected:

text
Unknown --target whatever. Use arcade or viz.

Viz layer routing

Once the target is viz, every file is classified into one of three layers. The classifier is in src/cli/detect-target.ts:

TypeScript
//   1. *.test.ts(x)  → route alongside the subject file
//   2. *.tsx that imports from 'solid-js'  → 'solid'
//   3. extends (PanelDebuggerSystem|StateStepperSystem|System) or filename
//      ends in 'System.ts'  → 'ecs'
//   4. extends Component and no Solid import  → 'ecs'
//   5. contains `definePanel(` and no Solid import  → 'core'
//   6. fallback  → 'core'

The order matters. A .tsx file that imports solid-js lands in solid even if it also extends Component. A file named FooSystem.ts lands in ecs even if it doesn't subclass anything.

Test files travel with their subject. Foo.test.ts looks up Foo.ts or Foo.tsx in the same seed, takes the subject's layer, and rides along. When the subject isn't co-injected, tests default to core — meaning your test ships, but you'll want to double-check it lands somewhere sensible.

Overriding the layer

Terminal
bjs inject MyPanelDebugger --target viz --layer solid

--layer only applies to viz and forces every file in the seed (and its closure) into one layer. Use it when:

  • Auto-classification splits files across layers and you want one folder.
  • You're shipping a seed that doesn't fit the heuristic cleanly.

The classifier is usually right. Reach for --layer after a dry-run shows files going somewhere wrong.

An unknown layer value is rejected:

text
Unknown --layer foo. Use core, solid, or ecs.

What the layers mean in viz

  • core/ — renderer-free, framework-free utilities. Pure-TypeScript helpers, definePanel factories without Solid wrappers, state schemas. The bottom of the stack.
  • solid/ — Solid components that render panels. .tsx files that import solid-js. The presentation layer.
  • ecs/ — System and Component subclasses that integrate with the ecs framework. *System.ts, classes extending System or Component. The integration layer.

A typical viz seed touches more than one layer: a .core.ts of pure logic, a .ts with a System, a .viz.tsx for the panel. The classifier splits them per file; barrels per layer pick them up.

What's next

The next section, inject in practice, runs a worked example end to end with the dry-run plan and the file diffs.

Was this page helpful?

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

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search