# <%= appTitle %>

A multi-page Vue 3 application generated by `lhx-cli create -t vue3-mpa`. The
build pipeline is driven by [`@lhx-kit/vite-plugin`](../../packages/vite-plugin)
reading a single source of truth: `project.config.ts`.

## Quick start

```bash
pnpm install
pnpm dev                 # start all pages
lhx-cli dev --page=home  # start only one page
pnpm build               # build every registered page
```

- Dev server URLs: `/.lhx-kit/pages/home.html`, `/.lhx-kit/pages/settings.html`
- Production output: `dist/<page>/index.html` + `dist/shared/` (chunks shared across pages)

## Project layout

```
<%= projectName %>/
├── project.config.ts        # Single source of truth: pages, aliases, envs, proxy
├── template.html            # HTML shell (`{{ title }}` / `{{ entry }}` placeholders)
├── vite.config.ts           # `defineConfig({plugins: [lhxKit(), vue()]})`
├── tsconfig.json
├── biome.json               # Lint + format config (Biome)
├── .env.dev
├── .env.prod
└── src/
    ├── env.d.ts             # Type-level declarations (virtual module, *.vue)
    ├── components/          # Shared components (Heading / Card / Paragraph)
    └── pages/
        ├── home/
        │   ├── entry.ts     # Bootstraps + mounts the router
        │   ├── router.ts    # HashRouter with / and /about
        │   ├── render.json  # Renderer v1 schema for the landing view
        │   └── views/       # HomeLanding.vue, HomeAbout.vue
        └── settings/entry.ts  # Conventional Vue component (no renderer)
```

Default aliases (set by `@lhx-kit/config`):

| Alias        | Target          |
|--------------|-----------------|
| `@`          | `src/`          |
| `@pages`     | `src/pages/`    |
| `@components`| `src/components/`|

Declare additional aliases in `project.config.ts` under `aliases:` when needed.

## Common commands

```bash
# Info / doctor
lhx-cli info
lhx-cli doctor

# Pages
lhx-cli add page cashier --title='Cashier'    # ts-morph upserts project.config.ts
lhx-cli add page orders --offline             # also adds to offline.whitelistPages if present
lhx-cli dev --page=home --page=cashier        # filter pages (via LHX_PAGES)

# Other generators
lhx-cli add component PriceTag
lhx-cli add api products
lhx-cli add store user
lhx-cli add schema cashier
```

## Adding a renderer-driven page

`lhx-cli add page myPage` scaffolds the full layout in one shot:

```
src/pages/myPage/
├── entry.ts         # bootstrap + mount router
├── router.ts        # HashRouter with / and /about
├── render.json      # renderer schema for the landing view
└── views/
    ├── MyPageLanding.vue   # imports ../render.json + registry
    └── MyPageAbout.vue
```

Edit `render.json` to tweak the declarative tree. Register more components in
the Landing view's `createRegistry()` call to make them addressable from the
schema. `lhx-cli add schema myOtherPage` can also drop a blank `render.json`
into an existing page directory.

## Build optimizations (auto-applied)

`lhxKit()` automatically applies the following at `pnpm build`:

| Optimization | Default | How to override |
|---|---|---|
| `assetsInlineLimit` | `8 KB` | `vite.config.ts` `build.assetsInlineLimit` |
| `target` | `es2018` | `build.target` |
| `cssCodeSplit` | `true` | `build.cssCodeSplit` |
| `manualChunks` (1 chunk per top-level npm package) | on | rollupOptions.output.manualChunks |
| Drop `console.log`/`console.debug`/`console.trace` in production | on | `build.minify`, `esbuild.pure` |
| gzip + brotli pre-compression of every chunk ≥ 1 KB | on | `lhxKit({compress: false})` |
| Soft warning for chunks > 50 KB | on | informational only |

## CDN externalisation (`window.LhxCdn`)

Enable `cdn` in `project.config.ts` to ship runtime deps from a public CDN
with onerror chaining and a local-vendor safety net:

```ts
cdn: {
  enabled: true,
  fallback: 'local',         // CDN fail → dynamic-import sibling vendor chunk
  globalNamespace: 'LhxCdn', // window property name (rename to avoid clashes)
  entries: [
    {name: 'vue', urls: ['https://unpkg.com/vue@3.5.13/dist/vue.runtime.global.prod.js']},
    {name: 'vue-router', depends: ['vue'], urls: ['https://unpkg.com/vue-router@4.4.5/dist/vue-router.global.prod.js']}
  ]
}
```

The loader exposes a small public API at `window[<globalNamespace>]`:

```ts
// Wait for one or more CDN deps before doing something
await window.LhxCdn.whenReady(['vue', 'vue-router']);

// Subscribe to lifecycle events for telemetry / UX
window.LhxCdn.on('fallback', ({name, url}) => {
  console.warn(`[cdn] ${name} fell back to local vendor: ${url}`);
});
window.LhxCdn.on('failed',   ({name, reason}) => report('cdn-failed', {name, reason}));
window.LhxCdn.on('ready',    ({names}) => console.log('all cdn deps ready', names));

// Read current state
window.LhxCdn.state.vue;  // 'pending' | 'ok' | 'fallback' | 'failed'
```

Offline packages (`lhx-cli offline build`) automatically blank out the CDN
URL list and force the local-vendor path so the app keeps working with no
network at all.

## Offline packaging (future)

When ready, create `offline.config.ts` next to `project.config.ts`, declare
`whitelistPages` and `versions`, then run:

```bash
lhx-cli offline build --hybrid-type=prod
```

See the lhx-kit docs for the contract details.

## Resources

- 🐛 https://github.com/juwenzhang/lhx-kit/issues
- 📖 https://juwenzhang.github.io/lhx-kit/
