@jasonshimmy/vite-plugin-cer-app 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/copilot-instructions.md +130 -0
- package/.github/workflows/publish.yml +206 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +10 -0
- package/IMPLEMENTATION_PLAN.md +391 -0
- package/README.md +231 -0
- package/VITE_PLUGIN_FRAMEWORK_PLAN.md +594 -0
- package/commits.txt +3 -0
- package/dist/__tests__/plugin/path-utils.test.d.ts +2 -0
- package/dist/__tests__/plugin/path-utils.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/path-utils.test.js +305 -0
- package/dist/__tests__/plugin/path-utils.test.js.map +1 -0
- package/dist/__tests__/plugin/scanner.test.d.ts +2 -0
- package/dist/__tests__/plugin/scanner.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/scanner.test.js +143 -0
- package/dist/__tests__/plugin/scanner.test.js.map +1 -0
- package/dist/__tests__/plugin/transforms/auto-import.test.d.ts +2 -0
- package/dist/__tests__/plugin/transforms/auto-import.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/transforms/auto-import.test.js +151 -0
- package/dist/__tests__/plugin/transforms/auto-import.test.js.map +1 -0
- package/dist/__tests__/plugin/transforms/head-inject.test.d.ts +2 -0
- package/dist/__tests__/plugin/transforms/head-inject.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/transforms/head-inject.test.js +151 -0
- package/dist/__tests__/plugin/transforms/head-inject.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/components.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/components.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/components.test.js +47 -0
- package/dist/__tests__/plugin/virtual/components.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/composables.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/composables.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/composables.test.js +48 -0
- package/dist/__tests__/plugin/virtual/composables.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/layouts.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/layouts.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/layouts.test.js +59 -0
- package/dist/__tests__/plugin/virtual/layouts.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/middleware.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/middleware.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/middleware.test.js +58 -0
- package/dist/__tests__/plugin/virtual/middleware.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/plugins.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/plugins.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/plugins.test.js +73 -0
- package/dist/__tests__/plugin/virtual/plugins.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/routes.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/routes.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/routes.test.js +167 -0
- package/dist/__tests__/plugin/virtual/routes.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/server-api.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/server-api.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/server-api.test.js +72 -0
- package/dist/__tests__/plugin/virtual/server-api.test.js.map +1 -0
- package/dist/__tests__/runtime/use-head.test.d.ts +2 -0
- package/dist/__tests__/runtime/use-head.test.d.ts.map +1 -0
- package/dist/__tests__/runtime/use-head.test.js +202 -0
- package/dist/__tests__/runtime/use-head.test.js.map +1 -0
- package/dist/__tests__/runtime/use-page-data.test.d.ts +2 -0
- package/dist/__tests__/runtime/use-page-data.test.d.ts.map +1 -0
- package/dist/__tests__/runtime/use-page-data.test.js +41 -0
- package/dist/__tests__/runtime/use-page-data.test.js.map +1 -0
- package/dist/cli/commands/build.d.ts +3 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +103 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +3 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +92 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +7 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +72 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/preview.d.ts +3 -0
- package/dist/cli/commands/preview.d.ts.map +1 -0
- package/dist/cli/commands/preview.js +191 -0
- package/dist/cli/commands/preview.js.map +1 -0
- package/dist/cli/create/index.d.ts +3 -0
- package/dist/cli/create/index.d.ts.map +1 -0
- package/dist/cli/create/index.js +184 -0
- package/dist/cli/create/index.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +17 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin/build-ssg.d.ts +12 -0
- package/dist/plugin/build-ssg.d.ts.map +1 -0
- package/dist/plugin/build-ssg.js +212 -0
- package/dist/plugin/build-ssg.js.map +1 -0
- package/dist/plugin/build-ssr.d.ts +10 -0
- package/dist/plugin/build-ssr.d.ts.map +1 -0
- package/dist/plugin/build-ssr.js +139 -0
- package/dist/plugin/build-ssr.js.map +1 -0
- package/dist/plugin/dev-server.d.ts +46 -0
- package/dist/plugin/dev-server.d.ts.map +1 -0
- package/dist/plugin/dev-server.js +194 -0
- package/dist/plugin/dev-server.js.map +1 -0
- package/dist/plugin/dts-generator.d.ts +27 -0
- package/dist/plugin/dts-generator.d.ts.map +1 -0
- package/dist/plugin/dts-generator.js +180 -0
- package/dist/plugin/dts-generator.js.map +1 -0
- package/dist/plugin/index.d.ts +13 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +298 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/path-utils.d.ts +57 -0
- package/dist/plugin/path-utils.d.ts.map +1 -0
- package/dist/plugin/path-utils.js +160 -0
- package/dist/plugin/path-utils.js.map +1 -0
- package/dist/plugin/scanner.d.ts +17 -0
- package/dist/plugin/scanner.d.ts.map +1 -0
- package/dist/plugin/scanner.js +54 -0
- package/dist/plugin/scanner.js.map +1 -0
- package/dist/plugin/transforms/auto-import.d.ts +14 -0
- package/dist/plugin/transforms/auto-import.d.ts.map +1 -0
- package/dist/plugin/transforms/auto-import.js +154 -0
- package/dist/plugin/transforms/auto-import.js.map +1 -0
- package/dist/plugin/transforms/head-inject.d.ts +29 -0
- package/dist/plugin/transforms/head-inject.d.ts.map +1 -0
- package/dist/plugin/transforms/head-inject.js +127 -0
- package/dist/plugin/transforms/head-inject.js.map +1 -0
- package/dist/plugin/virtual/components.d.ts +6 -0
- package/dist/plugin/virtual/components.d.ts.map +1 -0
- package/dist/plugin/virtual/components.js +22 -0
- package/dist/plugin/virtual/components.js.map +1 -0
- package/dist/plugin/virtual/composables.d.ts +6 -0
- package/dist/plugin/virtual/composables.d.ts.map +1 -0
- package/dist/plugin/virtual/composables.js +22 -0
- package/dist/plugin/virtual/composables.js.map +1 -0
- package/dist/plugin/virtual/error.d.ts +13 -0
- package/dist/plugin/virtual/error.d.ts.map +1 -0
- package/dist/plugin/virtual/error.js +32 -0
- package/dist/plugin/virtual/error.js.map +1 -0
- package/dist/plugin/virtual/layouts.d.ts +6 -0
- package/dist/plugin/virtual/layouts.d.ts.map +1 -0
- package/dist/plugin/virtual/layouts.js +33 -0
- package/dist/plugin/virtual/layouts.js.map +1 -0
- package/dist/plugin/virtual/loading.d.ts +11 -0
- package/dist/plugin/virtual/loading.d.ts.map +1 -0
- package/dist/plugin/virtual/loading.js +30 -0
- package/dist/plugin/virtual/loading.js.map +1 -0
- package/dist/plugin/virtual/middleware.d.ts +6 -0
- package/dist/plugin/virtual/middleware.d.ts.map +1 -0
- package/dist/plugin/virtual/middleware.js +36 -0
- package/dist/plugin/virtual/middleware.js.map +1 -0
- package/dist/plugin/virtual/plugins.d.ts +6 -0
- package/dist/plugin/virtual/plugins.d.ts.map +1 -0
- package/dist/plugin/virtual/plugins.js +30 -0
- package/dist/plugin/virtual/plugins.js.map +1 -0
- package/dist/plugin/virtual/routes.d.ts +16 -0
- package/dist/plugin/virtual/routes.d.ts.map +1 -0
- package/dist/plugin/virtual/routes.js +131 -0
- package/dist/plugin/virtual/routes.js.map +1 -0
- package/dist/plugin/virtual/server-api.d.ts +6 -0
- package/dist/plugin/virtual/server-api.d.ts.map +1 -0
- package/dist/plugin/virtual/server-api.js +41 -0
- package/dist/plugin/virtual/server-api.js.map +1 -0
- package/dist/plugin/virtual/server-middleware.d.ts +6 -0
- package/dist/plugin/virtual/server-middleware.d.ts.map +1 -0
- package/dist/plugin/virtual/server-middleware.js +36 -0
- package/dist/plugin/virtual/server-middleware.js.map +1 -0
- package/dist/runtime/app-template.d.ts +10 -0
- package/dist/runtime/app-template.d.ts.map +1 -0
- package/dist/runtime/app-template.js +143 -0
- package/dist/runtime/app-template.js.map +1 -0
- package/dist/runtime/composables/index.d.ts +4 -0
- package/dist/runtime/composables/index.d.ts.map +1 -0
- package/dist/runtime/composables/index.js +3 -0
- package/dist/runtime/composables/index.js.map +1 -0
- package/dist/runtime/composables/use-head.d.ts +30 -0
- package/dist/runtime/composables/use-head.d.ts.map +1 -0
- package/dist/runtime/composables/use-head.js +182 -0
- package/dist/runtime/composables/use-head.js.map +1 -0
- package/dist/runtime/composables/use-page-data.d.ts +32 -0
- package/dist/runtime/composables/use-page-data.d.ts.map +1 -0
- package/dist/runtime/composables/use-page-data.js +41 -0
- package/dist/runtime/composables/use-page-data.js.map +1 -0
- package/dist/runtime/entry-client-template.d.ts +8 -0
- package/dist/runtime/entry-client-template.d.ts.map +1 -0
- package/dist/runtime/entry-client-template.js +18 -0
- package/dist/runtime/entry-client-template.js.map +1 -0
- package/dist/runtime/entry-server-template.d.ts +9 -0
- package/dist/runtime/entry-server-template.d.ts.map +1 -0
- package/dist/runtime/entry-server-template.js +72 -0
- package/dist/runtime/entry-server-template.js.map +1 -0
- package/dist/types/api.d.ts +16 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +2 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/config.d.ts +32 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +4 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/middleware.d.ts +6 -0
- package/dist/types/middleware.d.ts.map +1 -0
- package/dist/types/middleware.js +2 -0
- package/dist/types/middleware.js.map +1 -0
- package/dist/types/page.d.ts +21 -0
- package/dist/types/page.d.ts.map +1 -0
- package/dist/types/page.js +2 -0
- package/dist/types/page.js.map +1 -0
- package/dist/types/plugin.d.ts +12 -0
- package/dist/types/plugin.d.ts.map +1 -0
- package/dist/types/plugin.js +2 -0
- package/dist/types/plugin.js.map +1 -0
- package/docs/cli.md +233 -0
- package/docs/components.md +114 -0
- package/docs/composables.md +99 -0
- package/docs/configuration.md +270 -0
- package/docs/data-loading.md +165 -0
- package/docs/getting-started.md +235 -0
- package/docs/head-management.md +206 -0
- package/docs/layouts.md +112 -0
- package/docs/middleware.md +140 -0
- package/docs/plugins.md +138 -0
- package/docs/rendering-modes.md +251 -0
- package/docs/routing.md +180 -0
- package/docs/server-api.md +172 -0
- package/docs/testing.md +462 -0
- package/package.json +75 -0
- package/src/__tests__/plugin/path-utils.test.ts +399 -0
- package/src/__tests__/plugin/scanner.test.ts +172 -0
- package/src/__tests__/plugin/transforms/auto-import.test.ts +229 -0
- package/src/__tests__/plugin/transforms/head-inject.test.ts +178 -0
- package/src/__tests__/plugin/virtual/components.test.ts +56 -0
- package/src/__tests__/plugin/virtual/composables.test.ts +57 -0
- package/src/__tests__/plugin/virtual/layouts.test.ts +70 -0
- package/src/__tests__/plugin/virtual/middleware.test.ts +68 -0
- package/src/__tests__/plugin/virtual/plugins.test.ts +84 -0
- package/src/__tests__/plugin/virtual/routes.test.ts +202 -0
- package/src/__tests__/plugin/virtual/server-api.test.ts +85 -0
- package/src/__tests__/runtime/use-head.test.ts +243 -0
- package/src/__tests__/runtime/use-page-data.test.ts +45 -0
- package/src/cli/commands/build.ts +114 -0
- package/src/cli/commands/dev.ts +101 -0
- package/src/cli/commands/generate.ts +81 -0
- package/src/cli/commands/preview.ts +218 -0
- package/src/cli/create/index.ts +250 -0
- package/src/cli/create/templates/spa/app/app.ts.tpl +74 -0
- package/src/cli/create/templates/spa/app/layouts/default.ts.tpl +15 -0
- package/src/cli/create/templates/spa/app/pages/index.ts.tpl +8 -0
- package/src/cli/create/templates/spa/cer.config.ts.tpl +6 -0
- package/src/cli/create/templates/spa/index.html.tpl +12 -0
- package/src/cli/create/templates/spa/package.json.tpl +18 -0
- package/src/cli/create/templates/ssg/app/app.ts.tpl +74 -0
- package/src/cli/create/templates/ssg/app/layouts/default.ts.tpl +15 -0
- package/src/cli/create/templates/ssg/app/pages/index.ts.tpl +17 -0
- package/src/cli/create/templates/ssg/cer.config.ts.tpl +13 -0
- package/src/cli/create/templates/ssg/index.html.tpl +12 -0
- package/src/cli/create/templates/ssg/package.json.tpl +19 -0
- package/src/cli/create/templates/ssr/app/app.ts.tpl +74 -0
- package/src/cli/create/templates/ssr/app/layouts/default.ts.tpl +15 -0
- package/src/cli/create/templates/ssr/app/pages/index.ts.tpl +8 -0
- package/src/cli/create/templates/ssr/cer.config.ts.tpl +10 -0
- package/src/cli/create/templates/ssr/index.html.tpl +12 -0
- package/src/cli/create/templates/ssr/package.json.tpl +18 -0
- package/src/cli/index.ts +20 -0
- package/src/index.ts +13 -0
- package/src/plugin/build-ssg.ts +259 -0
- package/src/plugin/build-ssr.ts +147 -0
- package/src/plugin/dev-server.ts +266 -0
- package/src/plugin/dts-generator.ts +214 -0
- package/src/plugin/index.ts +330 -0
- package/src/plugin/path-utils.ts +186 -0
- package/src/plugin/scanner.ts +65 -0
- package/src/plugin/transforms/auto-import.ts +190 -0
- package/src/plugin/transforms/head-inject.ts +161 -0
- package/src/plugin/virtual/components.ts +28 -0
- package/src/plugin/virtual/composables.ts +28 -0
- package/src/plugin/virtual/error.ts +34 -0
- package/src/plugin/virtual/layouts.ts +41 -0
- package/src/plugin/virtual/loading.ts +33 -0
- package/src/plugin/virtual/middleware.ts +45 -0
- package/src/plugin/virtual/plugins.ts +36 -0
- package/src/plugin/virtual/routes.ts +147 -0
- package/src/plugin/virtual/server-api.ts +52 -0
- package/src/plugin/virtual/server-middleware.ts +44 -0
- package/src/runtime/app-template.ts +142 -0
- package/src/runtime/composables/index.ts +3 -0
- package/src/runtime/composables/use-head.ts +204 -0
- package/src/runtime/composables/use-page-data.ts +39 -0
- package/src/runtime/entry-client-template.ts +17 -0
- package/src/runtime/entry-server-template.ts +71 -0
- package/src/types/api.ts +19 -0
- package/src/types/config.ts +39 -0
- package/src/types/index.ts +6 -0
- package/src/types/middleware.ts +16 -0
- package/src/types/page.ts +29 -0
- package/src/types/plugin.ts +13 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +29 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
# vite-plugin-cer-app — Implementation Plan
|
|
2
|
+
|
|
3
|
+
Gaps identified by comparing the framework against Nuxt 3 and Next.js 14.
|
|
4
|
+
Items are ordered by priority. Each section notes the files touched, the
|
|
5
|
+
design rationale, and whether it is in scope for the current sprint.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Status Key
|
|
10
|
+
|
|
11
|
+
| Symbol | Meaning |
|
|
12
|
+
|--------|---------|
|
|
13
|
+
| ✅ | Done |
|
|
14
|
+
| 🔨 | In progress |
|
|
15
|
+
| 📋 | Planned (this sprint) |
|
|
16
|
+
| 🔜 | Next sprint |
|
|
17
|
+
| ❌ | Deferred / out of scope |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Phase 1 — Bugs (sprint 1)
|
|
22
|
+
|
|
23
|
+
### 1.1 Fix `entry-server-template.ts` phantom import ✅
|
|
24
|
+
|
|
25
|
+
**Problem:** The template imports `virtual:cer-ssr-config` which is never
|
|
26
|
+
generated, crashing any custom `entry-server.ts`.
|
|
27
|
+
|
|
28
|
+
**Fix:** Inline the config object directly (same pattern `build-ssr.ts` uses).
|
|
29
|
+
|
|
30
|
+
**Files:** `src/runtime/entry-server-template.ts`
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
### 1.2 Remove dead `middleware` re-export from SSR entry ✅
|
|
35
|
+
|
|
36
|
+
**Problem:** Static `import middleware from 'virtual:cer-middleware'` in the
|
|
37
|
+
server entry caused a Vite warning about dynamic/static import conflict.
|
|
38
|
+
|
|
39
|
+
**Fix:** Remove the static import; routes handle middleware via their own
|
|
40
|
+
`beforeEnter` dynamic import.
|
|
41
|
+
|
|
42
|
+
**Files:** `src/runtime/entry-server-template.ts`, `src/plugin/build-ssr.ts`
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Phase 2 — Missing Wiring (sprint 1)
|
|
47
|
+
|
|
48
|
+
### 2.1 Layout wrapping ✅
|
|
49
|
+
|
|
50
|
+
**Problem:** `virtual:cer-layouts` is imported in both the SPA bootstrap and
|
|
51
|
+
the SSR entry but is never consumed. Pages with `export const meta = { layout:
|
|
52
|
+
'minimal' }` are silently ignored — every page renders without any layout.
|
|
53
|
+
|
|
54
|
+
**Design:**
|
|
55
|
+
|
|
56
|
+
*Build time:* `routes.ts` already reads each page file to extract middleware
|
|
57
|
+
names. Add `extractLayout()` (same regex approach) and include the result as
|
|
58
|
+
`meta: { layout: '...' }` on each route object.
|
|
59
|
+
|
|
60
|
+
*SPA / client runtime:* Register a `<cer-layout-view>` component inside
|
|
61
|
+
`app-template.ts` (after `initRouter`). It closes over `router` and `layouts`.
|
|
62
|
+
On every navigation it reads `router.matchRoute(path).route.meta.layout`,
|
|
63
|
+
looks up the tag name in the `layouts` map, and returns a VNode:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
{ tag: 'layout-default', props: {}, children: [{ tag: 'router-view', ... }] }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The vdom diff preserves the layout element across navigations that stay on the
|
|
70
|
+
same layout, so the header/footer are never unmounted.
|
|
71
|
+
|
|
72
|
+
`index.html` uses `<cer-layout-view>` instead of `<router-view>` directly.
|
|
73
|
+
|
|
74
|
+
*SSR runtime:* No reactive component needed. In the `vnodeFactory` inside
|
|
75
|
+
`build-ssr.ts` and `entry-server-template.ts`, match the request URL to a
|
|
76
|
+
route, read `meta.layout`, and wrap the vnode:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import layouts from 'virtual:cer-layouts'
|
|
80
|
+
|
|
81
|
+
const vnodeFactory = (req) => {
|
|
82
|
+
const router = initRouter({ routes, initialUrl: req.url ?? '/' })
|
|
83
|
+
const { route } = router.matchRoute(router.getCurrent().path)
|
|
84
|
+
const layoutName = (route as any)?.meta?.layout ?? 'default'
|
|
85
|
+
const layoutTag = (layouts as Record<string, string>)[layoutName]
|
|
86
|
+
const inner = html`<router-view></router-view>`
|
|
87
|
+
return {
|
|
88
|
+
vnode: layoutTag ? { tag: layoutTag, props: {}, children: [inner] } : inner,
|
|
89
|
+
router,
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Files:**
|
|
95
|
+
- `src/plugin/virtual/routes.ts` — add `extractLayout`, emit `meta`
|
|
96
|
+
- `src/runtime/app-template.ts` — register `cer-layout-view`, use `cer-layout-view` in html
|
|
97
|
+
- `src/plugin/build-ssr.ts` — layout-aware vnodeFactory
|
|
98
|
+
- `src/runtime/entry-server-template.ts` — layout-aware vnodeFactory
|
|
99
|
+
- Scaffold templates — `index.html` → `<cer-layout-view>`
|
|
100
|
+
- Demo `index.html` — same swap
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### 2.2 404 / not-found page convention ✅
|
|
105
|
+
|
|
106
|
+
**Problem:** No automatic fallback. The catch-all `[...all].ts` convention
|
|
107
|
+
works but requires the user to know the naming convention.
|
|
108
|
+
|
|
109
|
+
**Design:** In `routes.ts`, after scanning pages, check whether a file named
|
|
110
|
+
`404.ts` (or `404/index.ts`) exists. If it does, treat it as the canonical
|
|
111
|
+
not-found route: override its generated path to `/:all*` so it always sorts
|
|
112
|
+
last and catches every unmatched URL. The tag name becomes `page-404`.
|
|
113
|
+
|
|
114
|
+
Users no longer need to know the `[...all]` convention — they just create
|
|
115
|
+
`app/pages/404.ts`.
|
|
116
|
+
|
|
117
|
+
**Files:** `src/plugin/virtual/routes.ts`
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Phase 3 — New Conventions (sprint 1)
|
|
122
|
+
|
|
123
|
+
### 3.1 Loading state — `app/loading.ts` ✅
|
|
124
|
+
|
|
125
|
+
**Problem:** While a lazy route chunk is loading (`load()` pending), the user
|
|
126
|
+
sees a blank page.
|
|
127
|
+
|
|
128
|
+
**Design:**
|
|
129
|
+
|
|
130
|
+
*Convention:* If `app/loading.ts` exists, it must export a custom element
|
|
131
|
+
named `page-loading` (same auto-import rules apply).
|
|
132
|
+
|
|
133
|
+
*Virtual module:* `virtual:cer-loading` exports a boolean `hasLoading` and
|
|
134
|
+
the tag name `loadingTag` (or `null`).
|
|
135
|
+
|
|
136
|
+
*Bootstrap:* In `app-template.ts`, wrap `router.push` and `router.replace` to
|
|
137
|
+
set a reactive `isNavigating` ref. The `cer-layout-view` component reads this
|
|
138
|
+
ref and renders `<page-loading>` in place of the normal layout+page tree while
|
|
139
|
+
navigation is pending.
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
// In cer-layout-view render fn:
|
|
143
|
+
if (isNavigating.value && loadingTag) {
|
|
144
|
+
return { tag: loadingTag, props: {}, children: [] }
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Files:**
|
|
149
|
+
- `src/plugin/virtual/loading.ts` — new generator
|
|
150
|
+
- `src/plugin/index.ts` — register `virtual:cer-loading`
|
|
151
|
+
- `src/runtime/app-template.ts` — import loading module, wrap push/replace,
|
|
152
|
+
update cer-layout-view
|
|
153
|
+
- `src/plugin/dts-generator.ts` — add module declaration
|
|
154
|
+
- Demo — add `app/loading.ts`
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
### 3.2 Error page — `app/error.ts` ✅
|
|
159
|
+
|
|
160
|
+
**Problem:** An uncaught error in any component (or during navigation) leaves
|
|
161
|
+
the user with a blank screen and no recovery path.
|
|
162
|
+
|
|
163
|
+
**Design:**
|
|
164
|
+
|
|
165
|
+
*Convention:* If `app/error.ts` exists, it must export a custom element named
|
|
166
|
+
`page-error`. It receives `error` and `reset` attributes.
|
|
167
|
+
|
|
168
|
+
*Virtual module:* `virtual:cer-error` exports `hasError` and `errorTag`.
|
|
169
|
+
|
|
170
|
+
*Bootstrap:* In `app-template.ts`, wrap navigation and component rendering in
|
|
171
|
+
a try/catch. On error, set a reactive `currentError` ref. The `cer-layout-view`
|
|
172
|
+
component reads `currentError` and renders `<page-error error="…">` when set.
|
|
173
|
+
Provide a global `resetError()` function that clears `currentError` and
|
|
174
|
+
re-navigates to the current path.
|
|
175
|
+
|
|
176
|
+
**Files:**
|
|
177
|
+
- `src/plugin/virtual/error.ts` — new generator
|
|
178
|
+
- `src/plugin/index.ts` — register `virtual:cer-error`
|
|
179
|
+
- `src/runtime/app-template.ts` — import error module, error state, reset fn
|
|
180
|
+
- `src/plugin/dts-generator.ts` — add module declaration
|
|
181
|
+
- Demo — add `app/error.ts`
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Phase 4 — DX Improvements (sprint 1)
|
|
186
|
+
|
|
187
|
+
### 4.1 TypeScript path aliases ✅
|
|
188
|
+
|
|
189
|
+
**Problem:** Users must use relative imports (`../../composables/useAuth`) when
|
|
190
|
+
the conventional `~/composables/useAuth` alias is far more ergonomic.
|
|
191
|
+
|
|
192
|
+
**Design:** On every `configureServer` and `buildStart`, write a
|
|
193
|
+
`cer-tsconfig.json` to the project root:
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"compilerOptions": {
|
|
198
|
+
"paths": {
|
|
199
|
+
"~/*": ["./app/*"],
|
|
200
|
+
"~/pages/*": ["./app/pages/*"],
|
|
201
|
+
"~/layouts/*": ["./app/layouts/*"],
|
|
202
|
+
"~/components/*": ["./app/components/*"],
|
|
203
|
+
"~/composables/*": ["./app/composables/*"],
|
|
204
|
+
"~/plugins/*": ["./app/plugins/*"],
|
|
205
|
+
"~/middleware/*": ["./app/middleware/*"],
|
|
206
|
+
"~/assets/*": ["./app/assets/*"]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Users extend it from `tsconfig.json`:
|
|
213
|
+
```json
|
|
214
|
+
{ "extends": "./cer-tsconfig.json" }
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Document this in `getting-started.md`. Do NOT auto-mutate the user's existing
|
|
218
|
+
`tsconfig.json` (destructive, opinionated).
|
|
219
|
+
|
|
220
|
+
**Files:**
|
|
221
|
+
- `src/plugin/dts-generator.ts` — add `writeTsconfigPaths()`
|
|
222
|
+
- `src/plugin/index.ts` — call in `configureServer` + `buildStart`
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Phase 5 — Data Loader Hydration (sprint 2)
|
|
227
|
+
|
|
228
|
+
### 5.1 Expose loader from `load()` ✅
|
|
229
|
+
|
|
230
|
+
**Problem:** The `loader` export from page files is called during SSR/SSG
|
|
231
|
+
(`build-ssg.ts` calls it via `ssrLoadModule`) but the result is never
|
|
232
|
+
serialized into the HTML. Clients refetch data on hydration.
|
|
233
|
+
|
|
234
|
+
**Design:**
|
|
235
|
+
|
|
236
|
+
*Route generation:* Change `load()` to return `{ default: tagName, loader: mod.loader }`:
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
const loadFn = `() => import(${filePath}).then(mod => ({
|
|
240
|
+
default: ${tagName},
|
|
241
|
+
loader: mod.loader ?? null,
|
|
242
|
+
}))`
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
*SSR entry:* After `router.push(url)`, call the matched route's `load()` to
|
|
246
|
+
get the loader function. Invoke it with `{ params, query, req }`. Inject the
|
|
247
|
+
serialized result as `window.__CER_DATA__` into the HTML `<head>`:
|
|
248
|
+
|
|
249
|
+
```html
|
|
250
|
+
<script>window.__CER_DATA__ = {"title":"Hello","body":"…"}</script>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
*Client:* In `entry-client-template.ts`, read `window.__CER_DATA__` and pass
|
|
254
|
+
it as initial props to the matched page component before hydration.
|
|
255
|
+
|
|
256
|
+
*Component:* Page components call `useProps()` — on first render in the browser
|
|
257
|
+
the props come from `window.__CER_DATA__` (if present), bypassing the `loader`
|
|
258
|
+
fetch.
|
|
259
|
+
|
|
260
|
+
**Complexity:** Medium-high. Requires threading loader calls through the SSR
|
|
261
|
+
handler and the SSG pipeline. Deferred to sprint 2.
|
|
262
|
+
|
|
263
|
+
**Files:**
|
|
264
|
+
- `src/plugin/virtual/routes.ts` — expose loader in load() return value
|
|
265
|
+
- `src/runtime/entry-server-template.ts` — call loader, inject __CER_DATA__
|
|
266
|
+
- `src/plugin/build-ssr.ts` — same in generated server entry
|
|
267
|
+
- `src/plugin/build-ssg.ts` — inject __CER_DATA__ into rendered HTML
|
|
268
|
+
- `src/runtime/entry-client-template.ts` — read __CER_DATA__ on boot
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Phase 6 — Production Features (sprint 2–3)
|
|
273
|
+
|
|
274
|
+
### 6.1 ISR — Incremental Static Regeneration 🔜
|
|
275
|
+
|
|
276
|
+
**Problem:** `ssg.fallback: true` is in the config but not implemented.
|
|
277
|
+
Without ISR, large sites must either SSR every request or rebuild on every
|
|
278
|
+
content change.
|
|
279
|
+
|
|
280
|
+
**Design:** Implement a two-layer cache in the SSR preview server:
|
|
281
|
+
- First request for an unknown path: render server-side, cache result in memory
|
|
282
|
+
(or on disk), serve rendered HTML.
|
|
283
|
+
- Subsequent requests within the TTL: serve cached HTML.
|
|
284
|
+
- After TTL expires: serve stale HTML, re-render in background, update cache.
|
|
285
|
+
|
|
286
|
+
Requires a `revalidate` option per route (via `meta.ssg.revalidate: 60`).
|
|
287
|
+
|
|
288
|
+
**Files:**
|
|
289
|
+
- `src/cli/commands/preview.ts` — ISR cache layer
|
|
290
|
+
- `src/plugin/virtual/routes.ts` — include `meta.ssg.revalidate` in route
|
|
291
|
+
- `src/types/page.ts` — add `revalidate` to `PageSsgConfig`
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
### 6.2 Nested layouts 🔜
|
|
296
|
+
|
|
297
|
+
**Problem:** All pages share a single layout level. A `/admin/*` section with
|
|
298
|
+
its own sidebar inside the root layout requires copy-pasting layout structure
|
|
299
|
+
today.
|
|
300
|
+
|
|
301
|
+
**Design:** Introduce layout nesting via directory-level `_layout.ts` files:
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
app/layouts/default.ts ← root layout (has <slot>)
|
|
305
|
+
app/layouts/admin.ts ← admin layout (has <slot>)
|
|
306
|
+
app/pages/admin/_layout.ts ← layout override for /admin/* subtree
|
|
307
|
+
app/pages/admin/dashboard.ts
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
The framework builds a layout chain. `cer-layout-view` renders the chain from
|
|
311
|
+
outermost to innermost, each wrapping the next in its `<slot>`.
|
|
312
|
+
|
|
313
|
+
**Complexity:** High. Requires recursive layout resolution and changes to the
|
|
314
|
+
router's route objects.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
### 6.3 Per-route render strategy 🔜
|
|
319
|
+
|
|
320
|
+
**Problem:** All routes use the same mode (SPA / SSR / SSG). Nuxt's route
|
|
321
|
+
rules allow mixing: some routes rendered statically, some on the server, some
|
|
322
|
+
as SPA.
|
|
323
|
+
|
|
324
|
+
**Design:** Add `meta.render: 'static' | 'server' | 'spa'` to page files.
|
|
325
|
+
Build pipeline splits pages by strategy and applies the right renderer to each.
|
|
326
|
+
|
|
327
|
+
**Complexity:** Very high. Requires splitting the build pipeline.
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
### 6.4 Link prefetching 🔜
|
|
332
|
+
|
|
333
|
+
**Problem:** `<router-link>` doesn't prefetch route chunks on hover/visible.
|
|
334
|
+
|
|
335
|
+
**Design:** Add an `IntersectionObserver` to `router-link` that calls
|
|
336
|
+
`route.load()` when the link enters the viewport. The component-loader cache
|
|
337
|
+
(LRU) already exists — prefetching just populates it early.
|
|
338
|
+
|
|
339
|
+
This is a runtime change, not a plugin change.
|
|
340
|
+
|
|
341
|
+
**Files:** `custom-elements/src/lib/router/instance.ts` — update router-link
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
### 6.5 Route transitions ❌
|
|
346
|
+
|
|
347
|
+
The runtime already has `transition-group-handler.ts` and `transition-utils.ts`.
|
|
348
|
+
The framework just needs a convention for configuring them via `meta.transition`.
|
|
349
|
+
Deferred until layout wrapping is stable.
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## Phase 7 — Ecosystem (sprint 4+)
|
|
354
|
+
|
|
355
|
+
### 7.1 DevTools overlay ❌
|
|
356
|
+
|
|
357
|
+
Browser extension / overlay showing current route, matched layout, active
|
|
358
|
+
middleware, virtual module contents.
|
|
359
|
+
|
|
360
|
+
### 7.2 i18n integration ❌
|
|
361
|
+
|
|
362
|
+
Convention for `app/i18n/` locale files. Auto-injected `useI18n()` composable.
|
|
363
|
+
`cer-app i18n extract` CLI command.
|
|
364
|
+
|
|
365
|
+
### 7.3 Edge runtime adapter ❌
|
|
366
|
+
|
|
367
|
+
Cloudflare Workers / Deno Deploy adapter. Requires replacing Node's
|
|
368
|
+
`createStreamingSSRHandler` with a Web Streams equivalent.
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Summary Table
|
|
373
|
+
|
|
374
|
+
| # | Item | Sprint | Status |
|
|
375
|
+
|---|------|--------|--------|
|
|
376
|
+
| 1.1 | Fix entry-server-template phantom import | 1 | ✅ |
|
|
377
|
+
| 1.2 | Remove dead middleware re-export | 1 | ✅ |
|
|
378
|
+
| 2.1 | Layout wrapping (SPA + SSR) | 1 | ✅ |
|
|
379
|
+
| 2.2 | 404 page convention | 1 | ✅ |
|
|
380
|
+
| 3.1 | Loading state — `app/loading.ts` | 1 | ✅ |
|
|
381
|
+
| 3.2 | Error page — `app/error.ts` | 1 | ✅ |
|
|
382
|
+
| 4.1 | TypeScript path aliases | 1 | ✅ |
|
|
383
|
+
| 5.1 | Data loader hydration | 2 | ✅ |
|
|
384
|
+
| 6.1 | ISR | 2–3 | 🔜 |
|
|
385
|
+
| 6.2 | Nested layouts | 2–3 | 🔜 |
|
|
386
|
+
| 6.3 | Per-route render strategy | 3 | 🔜 |
|
|
387
|
+
| 6.4 | Link prefetching | 3 | 🔜 |
|
|
388
|
+
| 6.5 | Route transitions | 3 | ❌ |
|
|
389
|
+
| 7.1 | DevTools | 4+ | ❌ |
|
|
390
|
+
| 7.2 | i18n | 4+ | ❌ |
|
|
391
|
+
| 7.3 | Edge runtime | 4+ | ❌ |
|
package/README.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# vite-plugin-cer-app
|
|
2
|
+
|
|
3
|
+
A Nuxt/Next.js-style meta-framework built on top of [`@jasonshimmy/custom-elements-runtime`](https://github.com/jasonshimmy/custom-elements-runtime). Turns any Vite project into a full-stack application with file-based routing, server-side rendering, static site generation, server API routes, and more — all through native Web Components.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **File-based routing** — `app/pages/` directory maps directly to routes
|
|
10
|
+
- **Layouts** — `app/layouts/` with `<slot>` composition
|
|
11
|
+
- **Three rendering modes** — SPA, SSR (streaming), and SSG
|
|
12
|
+
- **Server API routes** — `server/api/` with per-method handlers (`GET`, `POST`, …)
|
|
13
|
+
- **Auto-imports** — runtime APIs (`component`, `html`, `ref`, …) injected automatically in page files
|
|
14
|
+
- **Data loading** — `loader` export per page; serialized server→client via `window.__CER_DATA__`
|
|
15
|
+
- **`useHead()`** — document head management (title, meta, OG tags) with SSR injection
|
|
16
|
+
- **App plugins** — ordered plugin loading with DI via `provide`/`inject`
|
|
17
|
+
- **Route middleware** — global and per-page guards
|
|
18
|
+
- **Server middleware** — CORS, auth, and other HTTP-level middleware
|
|
19
|
+
- **JIT CSS** — Tailwind-compatible, build-time via the runtime's `cerPlugin`
|
|
20
|
+
- **HMR** — virtual module invalidation when pages/components are added or removed
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npm install -D vite-plugin-cer-app
|
|
28
|
+
npm install @jasonshimmy/custom-elements-runtime
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Add the plugin to `vite.config.ts`:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
// vite.config.ts
|
|
35
|
+
import { defineConfig } from 'vite'
|
|
36
|
+
import { cerApp } from 'vite-plugin-cer-app'
|
|
37
|
+
|
|
38
|
+
export default defineConfig({
|
|
39
|
+
plugins: [cerApp()],
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or use a `cer.config.ts` alongside `vite.config.ts` (the CLI reads this automatically):
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
// cer.config.ts
|
|
47
|
+
import { defineConfig } from 'vite-plugin-cer-app'
|
|
48
|
+
|
|
49
|
+
export default defineConfig({
|
|
50
|
+
mode: 'spa', // 'spa' | 'ssr' | 'ssg'
|
|
51
|
+
})
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Quickstart with the CLI
|
|
57
|
+
|
|
58
|
+
The fastest path is scaffolding a new project:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
npx create-cer-app my-app
|
|
62
|
+
# → choose spa / ssr / ssg
|
|
63
|
+
cd my-app
|
|
64
|
+
npm install
|
|
65
|
+
npm run dev
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or install the CLI globally:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
npm install -g vite-plugin-cer-app
|
|
72
|
+
cer-app dev
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Project Structure
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
my-app/
|
|
81
|
+
├── app/ # All client-side app code
|
|
82
|
+
│ ├── pages/
|
|
83
|
+
│ │ ├── index.ts # → route /
|
|
84
|
+
│ │ ├── about.ts # → route /about
|
|
85
|
+
│ │ ├── blog/
|
|
86
|
+
│ │ │ ├── index.ts # → route /blog
|
|
87
|
+
│ │ │ └── [slug].ts # → route /blog/:slug
|
|
88
|
+
│ │ └── [...all].ts # → catch-all /*
|
|
89
|
+
│ ├── layouts/
|
|
90
|
+
│ │ └── default.ts # Default layout wrapper
|
|
91
|
+
│ ├── components/ # Auto-registered custom elements
|
|
92
|
+
│ ├── composables/ # Auto-imported composables
|
|
93
|
+
│ ├── plugins/ # App plugins (01.store.ts → loaded first)
|
|
94
|
+
│ └── middleware/ # Global route middleware
|
|
95
|
+
├── server/
|
|
96
|
+
│ ├── api/
|
|
97
|
+
│ │ ├── users/
|
|
98
|
+
│ │ │ ├── index.ts # GET/POST /api/users
|
|
99
|
+
│ │ │ └── [id].ts # GET/PUT/DELETE /api/users/:id
|
|
100
|
+
│ │ └── health.ts # GET /api/health
|
|
101
|
+
│ └── middleware/ # Server-only HTTP middleware
|
|
102
|
+
├── public/ # Copied as-is to dist/
|
|
103
|
+
├── index.html # HTML entry
|
|
104
|
+
└── cer.config.ts # Framework config
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Pages
|
|
110
|
+
|
|
111
|
+
Every file in `app/pages/` defines a custom element and optionally exports page metadata and a data loader:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
// app/pages/blog/[slug].ts
|
|
115
|
+
|
|
116
|
+
// component, html, useProps are auto-imported — no import statement needed
|
|
117
|
+
component('page-blog-slug', () => {
|
|
118
|
+
const props = useProps({ slug: '' })
|
|
119
|
+
|
|
120
|
+
return html`
|
|
121
|
+
<div class="prose">
|
|
122
|
+
<h1>${props.slug}</h1>
|
|
123
|
+
</div>
|
|
124
|
+
`
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// Optional: page metadata
|
|
128
|
+
export const meta = {
|
|
129
|
+
layout: 'default',
|
|
130
|
+
middleware: ['auth'],
|
|
131
|
+
hydrate: 'load',
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Optional: server-side data loader
|
|
135
|
+
export const loader = async ({ params }) => {
|
|
136
|
+
const post = await fetch(`/api/posts/${params.slug}`).then(r => r.json())
|
|
137
|
+
return { post }
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### File → Route mapping
|
|
142
|
+
|
|
143
|
+
| File | Route |
|
|
144
|
+
|---|---|
|
|
145
|
+
| `app/pages/index.ts` | `/` |
|
|
146
|
+
| `app/pages/about.ts` | `/about` |
|
|
147
|
+
| `app/pages/blog/index.ts` | `/blog` |
|
|
148
|
+
| `app/pages/blog/[slug].ts` | `/blog/:slug` |
|
|
149
|
+
| `app/pages/[...all].ts` | `/*` |
|
|
150
|
+
| `app/pages/(auth)/login.ts` | `/login` (group prefix stripped) |
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Layouts
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
// app/layouts/default.ts
|
|
158
|
+
component('layout-default', () => {
|
|
159
|
+
return html`
|
|
160
|
+
<header><nav>...</nav></header>
|
|
161
|
+
<main><slot></slot></main>
|
|
162
|
+
<footer>...</footer>
|
|
163
|
+
`
|
|
164
|
+
})
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The framework wraps each route's content inside the layout declared by `meta.layout`. Defaults to `'default'` if the file exists.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Server API Routes
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
// server/api/users/[id].ts
|
|
175
|
+
import type { ApiHandler } from 'vite-plugin-cer-app/types'
|
|
176
|
+
|
|
177
|
+
export const GET: ApiHandler = async (req, res) => {
|
|
178
|
+
res.json({ id: req.params.id })
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export const DELETE: ApiHandler = async (req, res) => {
|
|
182
|
+
res.status(204).end()
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## `useHead()`
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
import { useHead } from 'vite-plugin-cer-app/composables'
|
|
192
|
+
|
|
193
|
+
component('page-about', () => {
|
|
194
|
+
useHead({
|
|
195
|
+
title: 'About Us',
|
|
196
|
+
meta: [
|
|
197
|
+
{ name: 'description', content: 'Learn more about us.' },
|
|
198
|
+
{ property: 'og:title', content: 'About Us' },
|
|
199
|
+
],
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
return html`<h1>About</h1>`
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Documentation
|
|
209
|
+
|
|
210
|
+
| Guide | Description |
|
|
211
|
+
|---|---|
|
|
212
|
+
| [Getting Started](docs/getting-started.md) | Installation, scaffolding, first app |
|
|
213
|
+
| [Configuration](docs/configuration.md) | All `cer.config.ts` options |
|
|
214
|
+
| [Routing](docs/routing.md) | File-based routing, dynamic segments, route groups |
|
|
215
|
+
| [Layouts](docs/layouts.md) | Layout system and `<slot>` composition |
|
|
216
|
+
| [Components](docs/components.md) | Auto-registered custom elements |
|
|
217
|
+
| [Composables](docs/composables.md) | Auto-imported composables |
|
|
218
|
+
| [Plugins](docs/plugins.md) | App plugin system and DI |
|
|
219
|
+
| [Middleware](docs/middleware.md) | Route guards and server middleware |
|
|
220
|
+
| [Server API Routes](docs/server-api.md) | HTTP handlers in `server/api/` |
|
|
221
|
+
| [Data Loading](docs/data-loading.md) | Page loaders and SSR data hydration |
|
|
222
|
+
| [Head Management](docs/head-management.md) | `useHead()` reference |
|
|
223
|
+
| [Rendering Modes](docs/rendering-modes.md) | SPA, SSR, and SSG in detail |
|
|
224
|
+
| [CLI Reference](docs/cli.md) | `cer-app` and `create-cer-app` commands |
|
|
225
|
+
| [Manual Testing Guide](docs/testing.md) | How to test every feature end-to-end |
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
MIT
|