Case study

This case study follows the minimal hello example resource: first the complete source generated by clientx create, then a file-by-file explanation, then build and assembly into an app.

1. Generate hello

node scripts/clientx.mjs create hello --deps client

Output (excerpt):

[clientx] created resource "hello" at src/resource/hello/
  package.json  { name: @me/hello, clientx: { id: hello, kind: app, deps: [client], entry: register.ts } }
  register.ts   declareHello() + activateHello()
  i18n/zh.json + i18n/en.json  {}
[clientx] next: node scripts/clientx.mjs build --resources hello

2. Complete source

src/resource/hello/package.json

{
  "name": "@me/hello",
  "clientx": {
    "id": "hello",
    "kind": "app",
    "deps": ["client"],
    "entry": "register.ts"
  }
}

The clientx field describes the resource: id matches the directory, kind is app, deps declares a dependency on client (merged with source-import inference), and entry is always register.ts.

src/resource/hello/register.ts

// src/resource/hello/register.ts
// hello resource skeleton generated by clientx create.
// declareHello: synchronous identity registration (lightweight, main chunk).
// activateHello: asynchronous heavy imports (only when not disabled).
import { resourceRegistry } from '@clientx/runtime/lib/kernel/instance'
import type { Resource } from '@clientx/runtime/lib/kernel/types'

export const HELLO_RESOURCE: Resource = { id: 'hello', kind: 'app', source: { type: 'config' } }

// Dual convention: uniform declare/activate (loader preference) + named exports (dev/legacy).
export function declare(): void {
  resourceRegistry.declare(HELLO_RESOURCE)
}
export function declareHello(): void {
  declare()
}

export async function activate(): Promise<void> {
  // No heavy implementation in the skeleton; dynamically import business modules here.
}
export async function activateHello(): Promise<void> {
  await activate()
}

src/resource/hello/i18n/zh.json / en.json

{}

Locale bundle templates. They are built into dist/resource/hello/i18n/ and injected into i18next for enabled resources only.

3. Make hello do something

The skeleton builds and loads with the closure, but has no UI. Adding a page requires registering a route (lazy component) in declare and importing/registering capabilities or slots in activate. Follow the built-in resources (for example, msgx/register.ts):

export function declareHello(): void {
  resourceRegistry.declare(HELLO_RESOURCE)
  routeRegistry.register('hello', [
    { id: 'hello-page', path: 'hello', parent: ROUTES.app, component: () => import('./modules/hello-page') },
  ])
}

export async function activateHello(): Promise<void> {
  const { HelloBadge } = await import('./components/hello-badge')
  slotRegistry.register('nav:hello', HelloBadge)
}

Then write a page component in src/resource/hello/modules/hello-page.tsx. Resource code can use @clientx/*, allowed cross-resource imports (@/resource/client/*) and t().

4. Build hello

node scripts/clientx.mjs build --resources hello,client

Closure = [client, hello] (dependency first). Output:

dist/resource/client/**     # client resource bundle (dependency)
dist/resource/hello/        # hello resource bundle
dist/resource/manifest.json # { resources: [{ id: hello, entry: /resource/hello/register.js, deps: [client] }] }

5. Assemble hello into an app

Option A: dev server

node scripts/clientx.mjs run --resources client,hello

Open http://localhost:3007; the hello resource is declared/activated with the closure.

Option B: assemble from the download package

Download resource-hello-example.tar.gz (the source generated by clientx create hello --deps client) and extract it:

tar xzf resource-hello-example.tar.gz     # extracts hello/
cp -r hello/ <your-core>/src/resource/hello/

Then in the core repository:

cd <your-core>
npm install
node scripts/clientx.mjs build --resources client,hello
node scripts/clientx.mjs run --resources client,hello

6. Assemble the website

# Download core source → extract → npm install
node scripts/clientx.mjs build --resources client,sitex,portal   # full website (includes client)
node scripts/clientx.mjs run --resources client,sitex,portal     # local development

The portal resource provides the marketing home, downloads and changelog. Download artifacts are served from /downloads/ (clientx-core-source.tar.gz / clientx-core-prebuild.tar.gz / resource-<id>-source.tar.gz / resource-hello-example.tar.gz). The developer docs and help center are independent Astro static resources (docs / help) and do not enter the runtime resource closure.