@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,594 @@
|
|
|
1
|
+
# Vite Plugin Framework Plan: `vite-plugin-cer-app`
|
|
2
|
+
|
|
3
|
+
A Nuxt/Next.js-style meta-framework built on top of `@jasonshimmy/custom-elements-runtime`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Library Audit Summary
|
|
8
|
+
|
|
9
|
+
### What the library already provides
|
|
10
|
+
|
|
11
|
+
| Capability | Status | Details |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Reactivity (`ref`, `computed`, `watch`) | ✅ Full | Complete reactive system |
|
|
14
|
+
| SSR (DSD, streaming, hydration strategies) | ✅ Full | `renderToString*`, `renderToStream`, per-component `hydrate` |
|
|
15
|
+
| Client-side routing | ✅ Partial | Manual `useRouter()` config — **no file-based routing** |
|
|
16
|
+
| Component composition (hooks, provide/inject) | ✅ Full | React-style API |
|
|
17
|
+
| JIT CSS + Shadow DOM styling | ✅ Full | Tailwind-compatible, build-time via `cerPlugin` |
|
|
18
|
+
| SSR middleware adapters (Express/Fastify/Hono) | ✅ Full | `createSSRHandler`, `createStreamingSSRHandler` |
|
|
19
|
+
| Existing Vite plugin infrastructure | ✅ Full | `cerPlugin`, `cerJITCSS`, virtual modules |
|
|
20
|
+
| Global state + event bus | ✅ Partial | `createStore`, `GlobalEventBus` — no devtools/middleware |
|
|
21
|
+
| Error boundaries + suspense | ✅ Partial | Component-level only — no global 404 handler |
|
|
22
|
+
| TypeScript | ✅ Full | Strict mode, complete type coverage |
|
|
23
|
+
|
|
24
|
+
### Gaps requiring framework-layer solutions
|
|
25
|
+
|
|
26
|
+
| Gap | Impact | Framework Solution |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| No file-based routing | High | Glob `pages/` at build time, auto-generate `Route[]` |
|
|
29
|
+
| No layout system | High | `layouts/` directory convention + `<cer-keep-alive>` |
|
|
30
|
+
| No data loaders | High | Route-level `loader` export + SSR→client serialization |
|
|
31
|
+
| No auto-imports | Medium | Vite plugin: resolve `component`, `html`, hooks automatically |
|
|
32
|
+
| No API/server routes | High | Glob `server/api/` and register handlers on dev server |
|
|
33
|
+
| No meta/head management | Medium | `useHead()` composable wrapping document title/meta |
|
|
34
|
+
| No static generation (SSG) | High | Crawl routes at build, call `renderToString*` per route |
|
|
35
|
+
| No global middleware | Medium | Plugin-level `onBeforeRender`/`onAfterRender` hooks |
|
|
36
|
+
| No plugin runtime system | Medium | `defineAppPlugin` convention loaded from `plugins/` dir |
|
|
37
|
+
| No CLI tooling | Medium | `cer-app` CLI wrapping Vite dev/build commands |
|
|
38
|
+
|
|
39
|
+
### Verdict
|
|
40
|
+
|
|
41
|
+
> **The library can support this.** Its SSR pipeline, streaming renderer, router, composable system, and existing Vite plugin are all strong foundations. Every gap is a build-time or thin runtime addition — none require patching library internals.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Plugin Overview
|
|
46
|
+
|
|
47
|
+
**Package name:** `vite-plugin-cer-app`
|
|
48
|
+
|
|
49
|
+
The plugin transforms any Vite project into a full-stack application framework by:
|
|
50
|
+
|
|
51
|
+
1. Scanning conventional directories at build time
|
|
52
|
+
2. Auto-generating virtual modules for routes, layouts, middleware, plugins, and API handlers
|
|
53
|
+
3. Orchestrating dev server (SSR middleware + HMR) and production build (SPA / SSR / SSG)
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Directory Convention
|
|
58
|
+
|
|
59
|
+
Follows Nuxt 4's layout: client-side app code lives under `app/`, server code at the root.
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
my-app/
|
|
63
|
+
├── app/
|
|
64
|
+
│ ├── app.ts # App bootstrap (optional — auto-generated if absent)
|
|
65
|
+
│ ├── pages/
|
|
66
|
+
│ │ ├── index.ts # → /
|
|
67
|
+
│ │ ├── about.ts # → /about
|
|
68
|
+
│ │ ├── blog/
|
|
69
|
+
│ │ │ ├── index.ts # → /blog
|
|
70
|
+
│ │ │ └── [slug].ts # → /blog/:slug
|
|
71
|
+
│ │ └── [...catchAll].ts # → /* (catch-all / 404)
|
|
72
|
+
│ ├── layouts/
|
|
73
|
+
│ │ ├── default.ts # Default layout (wraps pages without layout: '...')
|
|
74
|
+
│ │ └── minimal.ts # Named layout
|
|
75
|
+
│ ├── components/ # Auto-imported custom elements
|
|
76
|
+
│ │ └── ui/
|
|
77
|
+
│ │ └── my-button.ts # → <my-button> auto-registered
|
|
78
|
+
│ ├── composables/ # Auto-imported composables
|
|
79
|
+
│ │ └── useTheme.ts
|
|
80
|
+
│ ├── plugins/ # App plugins loaded before render
|
|
81
|
+
│ │ └── 01.store.ts # Numbered prefix → load order
|
|
82
|
+
│ └── middleware/ # Global route middleware
|
|
83
|
+
│ └── auth.ts
|
|
84
|
+
├── server/
|
|
85
|
+
│ ├── api/
|
|
86
|
+
│ │ ├── users/
|
|
87
|
+
│ │ │ ├── index.ts # GET/POST /api/users
|
|
88
|
+
│ │ │ └── [id].ts # GET/PUT/DELETE /api/users/:id
|
|
89
|
+
│ │ └── health.ts # GET /api/health
|
|
90
|
+
│ └── middleware/ # Server-only middleware (CORS, auth, etc.)
|
|
91
|
+
│ └── cors.ts
|
|
92
|
+
├── public/ # Copied as-is to dist/
|
|
93
|
+
│ └── favicon.ico
|
|
94
|
+
└── cer.config.ts # Framework config file
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Configuration File (`cer.config.ts`)
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { defineConfig } from 'vite-plugin-cer-app';
|
|
103
|
+
|
|
104
|
+
export default defineConfig({
|
|
105
|
+
// Rendering mode
|
|
106
|
+
mode: 'ssr', // 'spa' | 'ssr' | 'ssg'
|
|
107
|
+
|
|
108
|
+
// SSG options (only used in 'ssg' mode)
|
|
109
|
+
ssg: {
|
|
110
|
+
routes: 'auto', // 'auto' | string[] — crawl or explicit list
|
|
111
|
+
concurrency: 4, // Pages rendered in parallel
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
// Router options (passed to useRouter())
|
|
115
|
+
router: {
|
|
116
|
+
base: '/',
|
|
117
|
+
scrollToFragment: { enabled: true, offset: 0 },
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
// JIT CSS options (passed to cerPlugin())
|
|
121
|
+
jitCss: {
|
|
122
|
+
content: ['./app/pages/**/*.ts', './app/components/**/*.ts', './app/layouts/**/*.ts'],
|
|
123
|
+
extendedColors: false,
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
// SSR render options (passed to renderToString*())
|
|
127
|
+
ssr: {
|
|
128
|
+
dsd: true,
|
|
129
|
+
streaming: false,
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// Auto-import namespaces
|
|
133
|
+
autoImports: {
|
|
134
|
+
components: true, // Auto-register components/ as custom elements
|
|
135
|
+
composables: true, // Auto-import composables/ as named exports
|
|
136
|
+
directives: true, // Auto-import `when`, `each`, `match` etc.
|
|
137
|
+
runtime: true, // Auto-import `component`, `html`, `ref`, etc.
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
// Vite dev server port
|
|
141
|
+
port: 3000,
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Virtual Modules
|
|
148
|
+
|
|
149
|
+
The plugin resolves these virtual module IDs at build and dev time:
|
|
150
|
+
|
|
151
|
+
| Virtual ID | Contents |
|
|
152
|
+
|---|---|
|
|
153
|
+
| `virtual:cer-routes` | Auto-generated `Route[]` from `app/pages/` |
|
|
154
|
+
| `virtual:cer-layouts` | Map of layout name → layout component from `app/layouts/` |
|
|
155
|
+
| `virtual:cer-components` | Auto-registration calls for `app/components/` |
|
|
156
|
+
| `virtual:cer-composables` | Re-export barrel for `app/composables/` |
|
|
157
|
+
| `virtual:cer-plugins` | Sorted `app/plugins/` list for sequential loading |
|
|
158
|
+
| `virtual:cer-middleware` | Global middleware chain from `app/middleware/` |
|
|
159
|
+
| `virtual:cer-server-api` | API route map from `server/api/` for dev server + SSR |
|
|
160
|
+
| `virtual:cer-app-config` | Resolved `cer.config.ts` for runtime |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Page Convention
|
|
165
|
+
|
|
166
|
+
Every file in `app/pages/` exports a `component()` definition. Optional metadata exports customize behavior:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// app/pages/blog/[slug].ts
|
|
170
|
+
|
|
171
|
+
import { component, html, ref, useProps } from '@jasonshimmy/custom-elements-runtime';
|
|
172
|
+
import type { PageMeta, PageLoader } from 'vite-plugin-cer-app/types';
|
|
173
|
+
|
|
174
|
+
// Required: define the custom element
|
|
175
|
+
component('page-blog-slug', () => {
|
|
176
|
+
const props = useProps({ slug: '' });
|
|
177
|
+
|
|
178
|
+
return html`
|
|
179
|
+
<div class="prose">
|
|
180
|
+
<h1>${props.slug}</h1>
|
|
181
|
+
</div>
|
|
182
|
+
`;
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Optional: page-level metadata
|
|
186
|
+
export const meta: PageMeta = {
|
|
187
|
+
layout: 'default', // Which layout to use (default: 'default')
|
|
188
|
+
middleware: ['auth'], // Named middleware to run before this page
|
|
189
|
+
hydrate: 'load', // Per-page hydration strategy
|
|
190
|
+
ssg: {
|
|
191
|
+
paths: async () => [ // SSG dynamic path generation
|
|
192
|
+
{ params: { slug: 'hello-world' } },
|
|
193
|
+
{ params: { slug: 'second-post' } },
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// Optional: server-side data loader
|
|
199
|
+
export const loader: PageLoader = async (ctx) => {
|
|
200
|
+
const { params, req } = ctx;
|
|
201
|
+
const post = await fetch(`/api/posts/${params.slug}`).then(r => r.json());
|
|
202
|
+
return { post }; // Serialized → injected as props on the page element
|
|
203
|
+
};
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### File → Route Mapping
|
|
207
|
+
|
|
208
|
+
| File | Route Path |
|
|
209
|
+
|---|---|
|
|
210
|
+
| `app/pages/index.ts` | `/` |
|
|
211
|
+
| `app/pages/about.ts` | `/about` |
|
|
212
|
+
| `app/pages/blog/index.ts` | `/blog` |
|
|
213
|
+
| `app/pages/blog/[slug].ts` | `/blog/:slug` |
|
|
214
|
+
| `app/pages/[...all].ts` | `/*` (catch-all) |
|
|
215
|
+
| `app/pages/(auth)/login.ts` | `/login` (parenthesized = route group, no path prefix) |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Layout Convention
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
// app/layouts/default.ts
|
|
223
|
+
|
|
224
|
+
import { component, html } from '@jasonshimmy/custom-elements-runtime';
|
|
225
|
+
|
|
226
|
+
component('layout-default', () => {
|
|
227
|
+
return html`
|
|
228
|
+
<app-header></app-header>
|
|
229
|
+
<main>
|
|
230
|
+
<slot></slot> <!-- page content renders here -->
|
|
231
|
+
</main>
|
|
232
|
+
<app-footer></app-footer>
|
|
233
|
+
`;
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The framework wraps each `<router-view>` output inside the layout declared in `meta.layout`. Layout switching is handled through `<cer-keep-alive>` to preserve DOM state on navigation.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Server API Routes
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// server/api/users/[id].ts (server/ stays at project root, not inside app/)
|
|
245
|
+
|
|
246
|
+
import type { ApiHandler } from 'vite-plugin-cer-app/types';
|
|
247
|
+
|
|
248
|
+
// Named exports per HTTP method
|
|
249
|
+
export const GET: ApiHandler = async (req, res) => {
|
|
250
|
+
const user = await db.user.findOne(req.params.id);
|
|
251
|
+
res.json(user);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
export const PUT: ApiHandler = async (req, res) => {
|
|
255
|
+
const updated = await db.user.update(req.params.id, req.body);
|
|
256
|
+
res.json(updated);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export const DELETE: ApiHandler = async (req, res) => {
|
|
260
|
+
await db.user.delete(req.params.id);
|
|
261
|
+
res.status(204).end();
|
|
262
|
+
};
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
API routes are registered:
|
|
266
|
+
- **Dev mode**: As Vite dev server middleware
|
|
267
|
+
- **SSR mode**: As route handlers in the generated server entry
|
|
268
|
+
- **SPA mode**: As Vite preview server middleware (or deployed separately)
|
|
269
|
+
- **SSG mode**: Optionally called at build time to generate JSON data files
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Server Middleware
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
// server/middleware/cors.ts (server/ stays at project root, not inside app/)
|
|
277
|
+
|
|
278
|
+
import type { ServerMiddleware } from 'vite-plugin-cer-app/types';
|
|
279
|
+
|
|
280
|
+
const cors: ServerMiddleware = (req, res, next) => {
|
|
281
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
282
|
+
next();
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
export default cors;
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Global Route Middleware
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
// app/middleware/auth.ts
|
|
294
|
+
|
|
295
|
+
import type { RouteMiddleware } from 'vite-plugin-cer-app/types';
|
|
296
|
+
|
|
297
|
+
const auth: RouteMiddleware = async (to, from, next) => {
|
|
298
|
+
const session = await getSession();
|
|
299
|
+
if (!session) {
|
|
300
|
+
next('/login');
|
|
301
|
+
} else {
|
|
302
|
+
next();
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
export default auth;
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Global middleware runs before every route. Named middleware (referenced in `meta.middleware`) runs only for the matching page.
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## App Plugins
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
// app/plugins/01.store.ts
|
|
317
|
+
|
|
318
|
+
import type { AppPlugin } from 'vite-plugin-cer-app/types';
|
|
319
|
+
import { createStore } from '@jasonshimmy/custom-elements-runtime/store';
|
|
320
|
+
|
|
321
|
+
export default {
|
|
322
|
+
name: 'app-store',
|
|
323
|
+
setup(app) {
|
|
324
|
+
const store = createStore({ user: null, theme: 'light' });
|
|
325
|
+
app.provide('store', store);
|
|
326
|
+
},
|
|
327
|
+
} satisfies AppPlugin;
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Plugins are loaded in filename order (numeric prefix recommended). They receive an `app` context with `provide` for DI and `router` for guard registration.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## `useHead()` Composable
|
|
335
|
+
|
|
336
|
+
Built into the framework runtime, not the library:
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
import { useHead } from 'vite-plugin-cer-app/composables';
|
|
340
|
+
|
|
341
|
+
component('page-about', () => {
|
|
342
|
+
useHead({
|
|
343
|
+
title: 'About Us',
|
|
344
|
+
meta: [
|
|
345
|
+
{ name: 'description', content: 'Learn more about our team.' },
|
|
346
|
+
{ property: 'og:title', content: 'About Us' },
|
|
347
|
+
],
|
|
348
|
+
link: [
|
|
349
|
+
{ rel: 'canonical', href: 'https://example.com/about' },
|
|
350
|
+
],
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
return html`<h1>About</h1>`;
|
|
354
|
+
});
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
In SSR mode, `useHead()` calls are collected during `renderToString*` and injected into the `<head>` of the HTML shell. In client mode, they imperatively update `document.title` / meta tags.
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## Data Loading & Hydration
|
|
362
|
+
|
|
363
|
+
The page `loader` export solves the SSR→client data hydration problem:
|
|
364
|
+
|
|
365
|
+
### SSR Flow
|
|
366
|
+
1. Server receives request for `/blog/hello-world`
|
|
367
|
+
2. Plugin matches route → finds `app/pages/blog/[slug].ts`
|
|
368
|
+
3. Calls `loader({ params: { slug: 'hello-world' }, req })`
|
|
369
|
+
4. Serializes return value as `window.__CER_DATA__` in HTML `<script>`
|
|
370
|
+
5. Calls `renderToStringWithJITCSSDSD(html`<page-blog-slug slug="hello-world" post="...">`)`
|
|
371
|
+
6. Streams or sends full HTML
|
|
372
|
+
|
|
373
|
+
### Client Hydration Flow
|
|
374
|
+
1. Browser receives full HTML (DSD means zero FOUC)
|
|
375
|
+
2. Runtime reads `window.__CER_DATA__` and passes it as props / injected context
|
|
376
|
+
3. Components attach to pre-rendered DOM — no refetch required
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Rendering Modes
|
|
381
|
+
|
|
382
|
+
### SPA Mode (`mode: 'spa'`)
|
|
383
|
+
|
|
384
|
+
- Vite builds a standard client-only bundle
|
|
385
|
+
- `index.html` shell with `<div id="app">` replaced by `<app-root>`
|
|
386
|
+
- `virtual:cer-routes` injects all routes into client-side router
|
|
387
|
+
- No SSR, no server entry
|
|
388
|
+
- Dev: standard Vite HMR
|
|
389
|
+
- Build output: `dist/` with `index.html` + assets
|
|
390
|
+
|
|
391
|
+
### SSR Mode (`mode: 'ssr'`)
|
|
392
|
+
|
|
393
|
+
- Generates a **server entry** (`dist/server/entry.js`) + **client entry** (`dist/client/`)
|
|
394
|
+
- Server entry exports a `handler(req, res)` using `createStreamingSSRHandler`
|
|
395
|
+
- Automatically wires: page matching → layout wrapping → data loading → `renderToStream`
|
|
396
|
+
- Compatible with Node.js servers (Express, Fastify, Hono) and edge runtimes
|
|
397
|
+
- Vite SSR build handles `ssr: true` correctly for server bundle
|
|
398
|
+
- Dev: Vite dev server with `transformIndexHtml` and SSR middleware
|
|
399
|
+
|
|
400
|
+
### SSG Mode (`mode: 'ssg'`)
|
|
401
|
+
|
|
402
|
+
- Runs SSR render for every route at build time
|
|
403
|
+
- Dynamic routes: calls `meta.ssg.paths()` per page to enumerate paths
|
|
404
|
+
- Writes each route to `dist/<path>/index.html`
|
|
405
|
+
- API routes that are `loader`-only can be inlined as JSON or omitted
|
|
406
|
+
- Uses `renderToStringWithJITCSSDSD` for DSD output with embedded CSS
|
|
407
|
+
- Falls back to SSR for routes not enumerated at build time (optional)
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
## Implementation Phases
|
|
412
|
+
|
|
413
|
+
### Phase 1 — Core Plugin Infrastructure ✅ Complete
|
|
414
|
+
|
|
415
|
+
**Goal**: Get a basic SPA working with file-based routing.
|
|
416
|
+
|
|
417
|
+
- [x] Scaffold `vite-plugin-cer-app` package with `definePlugin`, `defineConfig`
|
|
418
|
+
- [x] Resolve `app/` directory relative to project root (configurable via `srcDir` option)
|
|
419
|
+
- [x] Implement `app/pages/` scanner → generate `virtual:cer-routes` as `Route[]`
|
|
420
|
+
- [x] File-name → route-path transformer (index, dynamic `[param]`, catch-all `[...rest]`)
|
|
421
|
+
- [x] Route group support `(groupName)/` (path prefix stripped)
|
|
422
|
+
- [x] Auto-register `app/components/` via `virtual:cer-components`
|
|
423
|
+
- [x] Auto-import `app/composables/` via `virtual:cer-composables`
|
|
424
|
+
- [x] Auto-import runtime API (`component`, `html`, `ref`, etc.) — no manual imports required
|
|
425
|
+
- [x] Generate `app/app.ts` entry if absent: bootstrap, register built-ins, init router
|
|
426
|
+
- [x] SPA build mode: output `index.html` with `<router-view>` + bundled assets
|
|
427
|
+
- [x] Include `@jasonshimmy/custom-elements-runtime/css` (CSS variables + reset) by default
|
|
428
|
+
- [x] Mount `createDOMJITCSS()` automatically for light-DOM JIT CSS in browser
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
### Phase 2 — Layouts + Metadata ⚠️ Partial
|
|
433
|
+
|
|
434
|
+
**Goal**: Layout system and per-page metadata work end-to-end.
|
|
435
|
+
|
|
436
|
+
- [x] `app/layouts/` scanner → `virtual:cer-layouts` map (registers layout custom elements)
|
|
437
|
+
- [ ] Layout wrapper: detect `meta.layout` from matched page, wrap `<router-view>` output in the layout element
|
|
438
|
+
- [ ] `<cer-keep-alive>` integration to preserve layout DOM between navigations
|
|
439
|
+
- [x] `meta` export type + validation (TypeScript) — `PageMeta` type exported
|
|
440
|
+
- [x] `useHead()` composable — client-side: imperatively updates DOM; SSR: collects into array
|
|
441
|
+
- [x] Head injection in HTML shell during SSR / SSG — `beginHeadCollection`/`endHeadCollection` + `serializeHeadTags`
|
|
442
|
+
|
|
443
|
+
**Note**: Layouts are registered and their tag names are exported via `virtual:cer-layouts`, but the layout-wrapping logic (reading `meta.layout` and wrapping the page component in the layout element) is not yet wired into `router-view` or the server entry.
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
### Phase 3 — SSR Mode ✅ Complete
|
|
448
|
+
|
|
449
|
+
**Goal**: Full server-side rendering with DSD, streaming, and data loading.
|
|
450
|
+
|
|
451
|
+
- [x] SSR-mode Vite config: dual build (client + server bundles via `buildSSR`)
|
|
452
|
+
- [x] Server entry generator: auto-generated in `build-ssr.ts` with concurrent-safe per-request router threading
|
|
453
|
+
- [x] `loader` export detection + type (`PageLoader<Params, Data>`) — types defined, detection in routes
|
|
454
|
+
- [x] Streaming SSR: `createStreamingSSRHandler` with `{ vnode, router }` factory — concurrent-safe
|
|
455
|
+
- [x] Dev server middleware: intercepts HTML requests, loads `entry-server.ts` via `ssrLoadModule`
|
|
456
|
+
- [x] 404 page: `app/pages/[...all].ts` catch-all convention supported
|
|
457
|
+
- [ ] SSR data serialization: `window.__CER_DATA__` injection (loader data not yet serialized to HTML)
|
|
458
|
+
- [ ] Client-side data rehydration: read `__CER_DATA__` and pass as props before hydration
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
### Phase 4 — Server API Routes ✅ Complete
|
|
463
|
+
|
|
464
|
+
**Goal**: `server/api/` routes work in dev, SSR, and SSG.
|
|
465
|
+
|
|
466
|
+
- [x] `server/api/` scanner → `virtual:cer-server-api` route map
|
|
467
|
+
- [x] File-name → API path transformer (same rules as pages, under `/api/`)
|
|
468
|
+
- [x] HTTP method export detection (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`)
|
|
469
|
+
- [x] Dev: API routes registered as `configureServer` middleware
|
|
470
|
+
- [x] SSR: API route handlers included in server entry
|
|
471
|
+
- [x] `server/middleware/` scanner → `virtual:cer-server-middleware`, applied before API routes
|
|
472
|
+
- [x] Request/response helpers: `req.params`, `req.query`, `req.body`, `res.json()`, `res.status()`
|
|
473
|
+
- [x] Error handling: uncaught errors → `{ error: 'Internal Server Error' }` JSON with 500
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
### Phase 5 — Global Middleware + Plugins ✅ Complete
|
|
478
|
+
|
|
479
|
+
**Goal**: Auth guards, plugins, and DI work across the app.
|
|
480
|
+
|
|
481
|
+
- [x] `app/middleware/` scanner → `virtual:cer-middleware`
|
|
482
|
+
- [x] Middleware chain integration into router `beforeEnter` guard via `virtual:cer-middleware`
|
|
483
|
+
- [x] `meta.middleware` named middleware resolution (supported in `virtual:cer-routes`)
|
|
484
|
+
- [x] `app/plugins/` scanner → sorted `virtual:cer-plugins` (numeric prefix → alphabetical)
|
|
485
|
+
- [x] `AppPlugin` interface + `app` context (`provide`, `router`, `config`)
|
|
486
|
+
- [x] Plugin load order: numeric prefix sorts first, then alphabetical
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
### Phase 6 — SSG Mode ✅ Complete
|
|
491
|
+
|
|
492
|
+
**Goal**: Static site generation with dynamic route enumeration.
|
|
493
|
+
|
|
494
|
+
- [x] SSG build: runs SSR render per route, writes `dist/<path>/index.html`
|
|
495
|
+
- [x] `meta.ssg.paths()` enumeration for dynamic routes
|
|
496
|
+
- [x] Concurrency limit (`ssg.concurrency`, default 4) with `Promise.allSettled` — concurrent-safe via per-request router threading
|
|
497
|
+
- [x] Automatic route crawling from `app/pages/` (static routes auto-discovered, `ssg.routes: 'auto'`)
|
|
498
|
+
- [x] Build manifest: `ssg-manifest.json` with generated pages + any per-page errors
|
|
499
|
+
- [ ] API routes called at build → output JSON to `dist/api/<route>/index.json` (opt-in)
|
|
500
|
+
- [ ] ISR-style: fallback to SSR for unenumerated routes (opt-in, `ssg.fallback` config exists but not implemented)
|
|
501
|
+
|
|
502
|
+
---
|
|
503
|
+
|
|
504
|
+
### Phase 7 — CLI Tooling ✅ Complete
|
|
505
|
+
|
|
506
|
+
**Goal**: `npx create-cer-app` and `cer-app dev/build/preview` commands.
|
|
507
|
+
|
|
508
|
+
- [x] `create-cer-app`: scaffold from template with mode selection (SPA / SSR / SSG)
|
|
509
|
+
- [x] `cer-app dev`: wraps `vite dev` with SSR middleware attached
|
|
510
|
+
- [x] `cer-app build`: builds in correct mode (SPA / SSR dual-build / SSG)
|
|
511
|
+
- [x] `cer-app preview`: serves `dist/` with SSR handler or static files
|
|
512
|
+
- [x] `cer-app generate`: explicit SSG crawl + render (alias for build in SSG mode)
|
|
513
|
+
- [ ] TypeScript config auto-setup: path aliases for `~/pages`, `~/components`, `~/composables`
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## Technical Architecture
|
|
518
|
+
|
|
519
|
+
```
|
|
520
|
+
vite-plugin-cer-app/
|
|
521
|
+
├── src/
|
|
522
|
+
│ ├── plugin/
|
|
523
|
+
│ │ ├── index.ts # Main Vite plugin factory
|
|
524
|
+
│ │ ├── scanner.ts # Directory watchers — scans app/ and server/ separately
|
|
525
|
+
│ │ ├── virtual/
|
|
526
|
+
│ │ │ ├── routes.ts # virtual:cer-routes — scans app/pages/
|
|
527
|
+
│ │ │ ├── layouts.ts # virtual:cer-layouts — scans app/layouts/
|
|
528
|
+
│ │ │ ├── components.ts # virtual:cer-components — scans app/components/
|
|
529
|
+
│ │ │ ├── composables.ts # virtual:cer-composables — scans app/composables/
|
|
530
|
+
│ │ │ ├── plugins.ts # virtual:cer-plugins — scans app/plugins/
|
|
531
|
+
│ │ │ ├── middleware.ts # virtual:cer-middleware — scans app/middleware/
|
|
532
|
+
│ │ │ └── server-api.ts # virtual:cer-server-api — scans server/api/ (root)
|
|
533
|
+
│ │ ├── transforms/
|
|
534
|
+
│ │ │ ├── auto-import.ts # Auto-import injection into page/component files
|
|
535
|
+
│ │ │ └── head-inject.ts # <head> injection for SSR/SSG HTML output
|
|
536
|
+
│ │ ├── dev-server.ts # Vite configureServer — SSR middleware + API routes
|
|
537
|
+
│ │ ├── build-ssr.ts # Dual-build orchestration (client + server)
|
|
538
|
+
│ │ └── build-ssg.ts # SSG route crawling + render loop
|
|
539
|
+
│ ├── runtime/
|
|
540
|
+
│ │ ├── composables/
|
|
541
|
+
│ │ │ └── useHead.ts # Head management composable
|
|
542
|
+
│ │ ├── app.ts # App bootstrap template (placed in app/)
|
|
543
|
+
│ │ ├── entry-client.ts # Client entry template
|
|
544
|
+
│ │ └── entry-server.ts # Server entry template
|
|
545
|
+
│ ├── types/
|
|
546
|
+
│ │ ├── page.ts # PageMeta, PageLoader
|
|
547
|
+
│ │ ├── api.ts # ApiHandler, ApiContext
|
|
548
|
+
│ │ ├── plugin.ts # AppPlugin
|
|
549
|
+
│ │ ├── middleware.ts # RouteMiddleware, ServerMiddleware
|
|
550
|
+
│ │ └── config.ts # CerAppConfig, defineConfig (includes srcDir option)
|
|
551
|
+
│ └── cli/
|
|
552
|
+
│ ├── index.ts # CLI entrypoint
|
|
553
|
+
│ ├── commands/
|
|
554
|
+
│ │ ├── dev.ts
|
|
555
|
+
│ │ ├── build.ts
|
|
556
|
+
│ │ ├── preview.ts
|
|
557
|
+
│ │ └── generate.ts
|
|
558
|
+
│ └── create/
|
|
559
|
+
│ └── templates/ # Project scaffolding templates
|
|
560
|
+
│ ├── spa/
|
|
561
|
+
│ ├── ssr/
|
|
562
|
+
│ └── ssg/
|
|
563
|
+
└── package.json
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## Dependency Map
|
|
569
|
+
|
|
570
|
+
```
|
|
571
|
+
vite-plugin-cer-app
|
|
572
|
+
├── @jasonshimmy/custom-elements-runtime (peer — all rendering, reactivity, routing)
|
|
573
|
+
├── vite (peer — plugin host, build, dev server)
|
|
574
|
+
├── fast-glob (file scanning for pages/, components/, etc.)
|
|
575
|
+
├── chokidar (HMR: watch for added/removed route files)
|
|
576
|
+
├── magic-string (AST-free code transforms for auto-import)
|
|
577
|
+
└── pathe (cross-platform path utilities)
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
No heavy dependencies. Everything rendering-related delegates to the runtime library.
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## Known Risks & Mitigations
|
|
585
|
+
|
|
586
|
+
| Risk | Mitigation |
|
|
587
|
+
|---|---|
|
|
588
|
+
| HMR breaks when pages added/removed | Use `chokidar` in `configureServer`; invalidate virtual modules on change |
|
|
589
|
+
| Data loader props exceed URL-safe attribute limits | Serialize into `<script type="application/json" id="__cer-data">` inside shadow root |
|
|
590
|
+
| SSG misses dynamic routes | Warn + list unresolved routes; require explicit `ssg.paths()` for dynamic segments |
|
|
591
|
+
| Circular dependency between auto-imported composables | Document that composables must be side-effect-free at import time |
|
|
592
|
+
| SSR streaming + head injection timing | Collect `useHead()` calls in a pre-render pass, then stream body |
|
|
593
|
+
| Router `activeRouterProxy` race during SSR | Use `initialUrl` per-request (library already supports this) |
|
|
594
|
+
| Edge runtime compatibility | Ensure server entry uses only Web APIs; no Node.js-specific code paths |
|
package/commits.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-utils.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/plugin/path-utils.test.ts"],"names":[],"mappings":""}
|