Packs#

Packs bundle reusable source files and Unity assets into a single ScriptableObject. Drop one onto a JSRunner and its files become importable modules, its objects become accessible from JavaScript.

Quick Example#

A theme pack bundles a stylesheet and some Unity assets:

Pack: "theme"
  Files:    variables.uss
  Objects:  logo -> LogoTexture, font -> InterFont

In your app:

import variables from "./@packs/theme/variables.uss"
compileStyleSheet(variables, "theme/variables.uss")

function Header() {
    const { logo, font } = __pack("theme")

    return (
        <View className="header">
            <Image image={logo} />
            <Label text="My App" style={{ unityFontDefinition: font }} />
        </View>
    )
}

Setup#

  1. Create: Right-click in the Project window, Create > OneJS > Pack
  2. Configure: Set a slug (e.g., theme), add files and/or objects in the inspector
  3. Add to JSRunner: Drag the asset into the Packs tab; files extract to disk as soon as it is assigned (and automatically on first run and during builds)

Files are extracted to @packs/{slug}/ inside your working directory, along with a .d.ts file for autocomplete. Automatic extraction never overwrites an existing folder; after changing a pack, use the tab's delete and extract buttons to refresh it. Give the pack a Version (e.g. 1.0.0) and bump it whenever its files change: the tab then flags stale extractions as Outdated, and a console warning points at them on the next run.

Accessing Files#

Pack files are regular source files that esbuild can import. Import them by relative path (there is no package alias, so the path is relative to the importing file):

import { ColorWheel } from "./@packs/color-picker/index"
import variables from "./@packs/theme/variables.uss"   // plain .uss imports as text
compileStyleSheet(variables, "theme/variables.uss")

Accessing Objects#

Object entries are available at runtime as properties on the pack:

const palette = __pack("color-picker").palette    // Texture2D
const config = __pack("color-picker").config      // ScriptableObject

Each pack auto-generates a .d.ts file, so you get full autocomplete and type checking for object keys.

Namespaces#

Optional namespaces organize packs like npm scoped packages. Set the Namespace field in the inspector:

__pack("color-picker")              // no namespace
__pack("@acme/color-picker")        // with namespace "acme"

Namespaced packs extract to @packs/@{namespace}/{slug}/.

Premade Packs#

You can consume a pack someone else made the same way: add the asset to the Packs tab and import from the extracted folder. The Asset Store version of OneJS ships six under the singtaa namespace: three onejs-ui theme packs and three sample packs. Themes register in one line via onejs:themes; a sample exports components:

import "onejs:themes"  // registers every extracted theme pack
import { Inventory } from "./@packs/@singtaa/inventory/inventory"

onejs:themes (the themesPlugin() from onejs-unity/esbuild, in scaffolded configs) picks up any extracted module named *Theme.ts, so name your module that way if you author a theme pack and want it auto-registered. See Premade Themes for the full walkthrough.

Builds#

The build processor extracts pack files automatically during Unity builds, overwriting the extracted folders with the pack's current content. Treat extracted files as generated output: keep your own edits in your own source files. Pack objects are injected into the JavaScript context at startup, so __pack() is available immediately when your app runs.

Upgrading From Cartridges#

Packs used to be called UI Cartridges. Nothing you made needs to change:

  • Your cartridge assets load as packs, with their files and objects intact.
  • A JSRunner made before the rename keeps extracting to @cartridges/, for new packs too, so the imports your app already has keep resolving. A JSRunner you add now uses @packs/.
  • __cart() still works, as the same function as __pack().
  • C# code that names UICartridge, CartridgeUtils or the other old names still compiles, with an obsolete warning saying what to use instead. A field typed UICartridge keeps the pack it holds.

One thing does not carry over: ScriptableObject.CreateInstance<UICartridge>() returns null. Create a Pack instead.