@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.
Files changed (299) hide show
  1. package/.github/copilot-instructions.md +130 -0
  2. package/.github/workflows/publish.yml +206 -0
  3. package/.nvmrc +1 -0
  4. package/CHANGELOG.md +10 -0
  5. package/IMPLEMENTATION_PLAN.md +391 -0
  6. package/README.md +231 -0
  7. package/VITE_PLUGIN_FRAMEWORK_PLAN.md +594 -0
  8. package/commits.txt +3 -0
  9. package/dist/__tests__/plugin/path-utils.test.d.ts +2 -0
  10. package/dist/__tests__/plugin/path-utils.test.d.ts.map +1 -0
  11. package/dist/__tests__/plugin/path-utils.test.js +305 -0
  12. package/dist/__tests__/plugin/path-utils.test.js.map +1 -0
  13. package/dist/__tests__/plugin/scanner.test.d.ts +2 -0
  14. package/dist/__tests__/plugin/scanner.test.d.ts.map +1 -0
  15. package/dist/__tests__/plugin/scanner.test.js +143 -0
  16. package/dist/__tests__/plugin/scanner.test.js.map +1 -0
  17. package/dist/__tests__/plugin/transforms/auto-import.test.d.ts +2 -0
  18. package/dist/__tests__/plugin/transforms/auto-import.test.d.ts.map +1 -0
  19. package/dist/__tests__/plugin/transforms/auto-import.test.js +151 -0
  20. package/dist/__tests__/plugin/transforms/auto-import.test.js.map +1 -0
  21. package/dist/__tests__/plugin/transforms/head-inject.test.d.ts +2 -0
  22. package/dist/__tests__/plugin/transforms/head-inject.test.d.ts.map +1 -0
  23. package/dist/__tests__/plugin/transforms/head-inject.test.js +151 -0
  24. package/dist/__tests__/plugin/transforms/head-inject.test.js.map +1 -0
  25. package/dist/__tests__/plugin/virtual/components.test.d.ts +2 -0
  26. package/dist/__tests__/plugin/virtual/components.test.d.ts.map +1 -0
  27. package/dist/__tests__/plugin/virtual/components.test.js +47 -0
  28. package/dist/__tests__/plugin/virtual/components.test.js.map +1 -0
  29. package/dist/__tests__/plugin/virtual/composables.test.d.ts +2 -0
  30. package/dist/__tests__/plugin/virtual/composables.test.d.ts.map +1 -0
  31. package/dist/__tests__/plugin/virtual/composables.test.js +48 -0
  32. package/dist/__tests__/plugin/virtual/composables.test.js.map +1 -0
  33. package/dist/__tests__/plugin/virtual/layouts.test.d.ts +2 -0
  34. package/dist/__tests__/plugin/virtual/layouts.test.d.ts.map +1 -0
  35. package/dist/__tests__/plugin/virtual/layouts.test.js +59 -0
  36. package/dist/__tests__/plugin/virtual/layouts.test.js.map +1 -0
  37. package/dist/__tests__/plugin/virtual/middleware.test.d.ts +2 -0
  38. package/dist/__tests__/plugin/virtual/middleware.test.d.ts.map +1 -0
  39. package/dist/__tests__/plugin/virtual/middleware.test.js +58 -0
  40. package/dist/__tests__/plugin/virtual/middleware.test.js.map +1 -0
  41. package/dist/__tests__/plugin/virtual/plugins.test.d.ts +2 -0
  42. package/dist/__tests__/plugin/virtual/plugins.test.d.ts.map +1 -0
  43. package/dist/__tests__/plugin/virtual/plugins.test.js +73 -0
  44. package/dist/__tests__/plugin/virtual/plugins.test.js.map +1 -0
  45. package/dist/__tests__/plugin/virtual/routes.test.d.ts +2 -0
  46. package/dist/__tests__/plugin/virtual/routes.test.d.ts.map +1 -0
  47. package/dist/__tests__/plugin/virtual/routes.test.js +167 -0
  48. package/dist/__tests__/plugin/virtual/routes.test.js.map +1 -0
  49. package/dist/__tests__/plugin/virtual/server-api.test.d.ts +2 -0
  50. package/dist/__tests__/plugin/virtual/server-api.test.d.ts.map +1 -0
  51. package/dist/__tests__/plugin/virtual/server-api.test.js +72 -0
  52. package/dist/__tests__/plugin/virtual/server-api.test.js.map +1 -0
  53. package/dist/__tests__/runtime/use-head.test.d.ts +2 -0
  54. package/dist/__tests__/runtime/use-head.test.d.ts.map +1 -0
  55. package/dist/__tests__/runtime/use-head.test.js +202 -0
  56. package/dist/__tests__/runtime/use-head.test.js.map +1 -0
  57. package/dist/__tests__/runtime/use-page-data.test.d.ts +2 -0
  58. package/dist/__tests__/runtime/use-page-data.test.d.ts.map +1 -0
  59. package/dist/__tests__/runtime/use-page-data.test.js +41 -0
  60. package/dist/__tests__/runtime/use-page-data.test.js.map +1 -0
  61. package/dist/cli/commands/build.d.ts +3 -0
  62. package/dist/cli/commands/build.d.ts.map +1 -0
  63. package/dist/cli/commands/build.js +103 -0
  64. package/dist/cli/commands/build.js.map +1 -0
  65. package/dist/cli/commands/dev.d.ts +3 -0
  66. package/dist/cli/commands/dev.d.ts.map +1 -0
  67. package/dist/cli/commands/dev.js +92 -0
  68. package/dist/cli/commands/dev.js.map +1 -0
  69. package/dist/cli/commands/generate.d.ts +7 -0
  70. package/dist/cli/commands/generate.d.ts.map +1 -0
  71. package/dist/cli/commands/generate.js +72 -0
  72. package/dist/cli/commands/generate.js.map +1 -0
  73. package/dist/cli/commands/preview.d.ts +3 -0
  74. package/dist/cli/commands/preview.d.ts.map +1 -0
  75. package/dist/cli/commands/preview.js +191 -0
  76. package/dist/cli/commands/preview.js.map +1 -0
  77. package/dist/cli/create/index.d.ts +3 -0
  78. package/dist/cli/create/index.d.ts.map +1 -0
  79. package/dist/cli/create/index.js +184 -0
  80. package/dist/cli/create/index.js.map +1 -0
  81. package/dist/cli/index.d.ts +3 -0
  82. package/dist/cli/index.d.ts.map +1 -0
  83. package/dist/cli/index.js +17 -0
  84. package/dist/cli/index.js.map +1 -0
  85. package/dist/index.d.ts +9 -0
  86. package/dist/index.d.ts.map +1 -0
  87. package/dist/index.js +4 -0
  88. package/dist/index.js.map +1 -0
  89. package/dist/plugin/build-ssg.d.ts +12 -0
  90. package/dist/plugin/build-ssg.d.ts.map +1 -0
  91. package/dist/plugin/build-ssg.js +212 -0
  92. package/dist/plugin/build-ssg.js.map +1 -0
  93. package/dist/plugin/build-ssr.d.ts +10 -0
  94. package/dist/plugin/build-ssr.d.ts.map +1 -0
  95. package/dist/plugin/build-ssr.js +139 -0
  96. package/dist/plugin/build-ssr.js.map +1 -0
  97. package/dist/plugin/dev-server.d.ts +46 -0
  98. package/dist/plugin/dev-server.d.ts.map +1 -0
  99. package/dist/plugin/dev-server.js +194 -0
  100. package/dist/plugin/dev-server.js.map +1 -0
  101. package/dist/plugin/dts-generator.d.ts +27 -0
  102. package/dist/plugin/dts-generator.d.ts.map +1 -0
  103. package/dist/plugin/dts-generator.js +180 -0
  104. package/dist/plugin/dts-generator.js.map +1 -0
  105. package/dist/plugin/index.d.ts +13 -0
  106. package/dist/plugin/index.d.ts.map +1 -0
  107. package/dist/plugin/index.js +298 -0
  108. package/dist/plugin/index.js.map +1 -0
  109. package/dist/plugin/path-utils.d.ts +57 -0
  110. package/dist/plugin/path-utils.d.ts.map +1 -0
  111. package/dist/plugin/path-utils.js +160 -0
  112. package/dist/plugin/path-utils.js.map +1 -0
  113. package/dist/plugin/scanner.d.ts +17 -0
  114. package/dist/plugin/scanner.d.ts.map +1 -0
  115. package/dist/plugin/scanner.js +54 -0
  116. package/dist/plugin/scanner.js.map +1 -0
  117. package/dist/plugin/transforms/auto-import.d.ts +14 -0
  118. package/dist/plugin/transforms/auto-import.d.ts.map +1 -0
  119. package/dist/plugin/transforms/auto-import.js +154 -0
  120. package/dist/plugin/transforms/auto-import.js.map +1 -0
  121. package/dist/plugin/transforms/head-inject.d.ts +29 -0
  122. package/dist/plugin/transforms/head-inject.d.ts.map +1 -0
  123. package/dist/plugin/transforms/head-inject.js +127 -0
  124. package/dist/plugin/transforms/head-inject.js.map +1 -0
  125. package/dist/plugin/virtual/components.d.ts +6 -0
  126. package/dist/plugin/virtual/components.d.ts.map +1 -0
  127. package/dist/plugin/virtual/components.js +22 -0
  128. package/dist/plugin/virtual/components.js.map +1 -0
  129. package/dist/plugin/virtual/composables.d.ts +6 -0
  130. package/dist/plugin/virtual/composables.d.ts.map +1 -0
  131. package/dist/plugin/virtual/composables.js +22 -0
  132. package/dist/plugin/virtual/composables.js.map +1 -0
  133. package/dist/plugin/virtual/error.d.ts +13 -0
  134. package/dist/plugin/virtual/error.d.ts.map +1 -0
  135. package/dist/plugin/virtual/error.js +32 -0
  136. package/dist/plugin/virtual/error.js.map +1 -0
  137. package/dist/plugin/virtual/layouts.d.ts +6 -0
  138. package/dist/plugin/virtual/layouts.d.ts.map +1 -0
  139. package/dist/plugin/virtual/layouts.js +33 -0
  140. package/dist/plugin/virtual/layouts.js.map +1 -0
  141. package/dist/plugin/virtual/loading.d.ts +11 -0
  142. package/dist/plugin/virtual/loading.d.ts.map +1 -0
  143. package/dist/plugin/virtual/loading.js +30 -0
  144. package/dist/plugin/virtual/loading.js.map +1 -0
  145. package/dist/plugin/virtual/middleware.d.ts +6 -0
  146. package/dist/plugin/virtual/middleware.d.ts.map +1 -0
  147. package/dist/plugin/virtual/middleware.js +36 -0
  148. package/dist/plugin/virtual/middleware.js.map +1 -0
  149. package/dist/plugin/virtual/plugins.d.ts +6 -0
  150. package/dist/plugin/virtual/plugins.d.ts.map +1 -0
  151. package/dist/plugin/virtual/plugins.js +30 -0
  152. package/dist/plugin/virtual/plugins.js.map +1 -0
  153. package/dist/plugin/virtual/routes.d.ts +16 -0
  154. package/dist/plugin/virtual/routes.d.ts.map +1 -0
  155. package/dist/plugin/virtual/routes.js +131 -0
  156. package/dist/plugin/virtual/routes.js.map +1 -0
  157. package/dist/plugin/virtual/server-api.d.ts +6 -0
  158. package/dist/plugin/virtual/server-api.d.ts.map +1 -0
  159. package/dist/plugin/virtual/server-api.js +41 -0
  160. package/dist/plugin/virtual/server-api.js.map +1 -0
  161. package/dist/plugin/virtual/server-middleware.d.ts +6 -0
  162. package/dist/plugin/virtual/server-middleware.d.ts.map +1 -0
  163. package/dist/plugin/virtual/server-middleware.js +36 -0
  164. package/dist/plugin/virtual/server-middleware.js.map +1 -0
  165. package/dist/runtime/app-template.d.ts +10 -0
  166. package/dist/runtime/app-template.d.ts.map +1 -0
  167. package/dist/runtime/app-template.js +143 -0
  168. package/dist/runtime/app-template.js.map +1 -0
  169. package/dist/runtime/composables/index.d.ts +4 -0
  170. package/dist/runtime/composables/index.d.ts.map +1 -0
  171. package/dist/runtime/composables/index.js +3 -0
  172. package/dist/runtime/composables/index.js.map +1 -0
  173. package/dist/runtime/composables/use-head.d.ts +30 -0
  174. package/dist/runtime/composables/use-head.d.ts.map +1 -0
  175. package/dist/runtime/composables/use-head.js +182 -0
  176. package/dist/runtime/composables/use-head.js.map +1 -0
  177. package/dist/runtime/composables/use-page-data.d.ts +32 -0
  178. package/dist/runtime/composables/use-page-data.d.ts.map +1 -0
  179. package/dist/runtime/composables/use-page-data.js +41 -0
  180. package/dist/runtime/composables/use-page-data.js.map +1 -0
  181. package/dist/runtime/entry-client-template.d.ts +8 -0
  182. package/dist/runtime/entry-client-template.d.ts.map +1 -0
  183. package/dist/runtime/entry-client-template.js +18 -0
  184. package/dist/runtime/entry-client-template.js.map +1 -0
  185. package/dist/runtime/entry-server-template.d.ts +9 -0
  186. package/dist/runtime/entry-server-template.d.ts.map +1 -0
  187. package/dist/runtime/entry-server-template.js +72 -0
  188. package/dist/runtime/entry-server-template.js.map +1 -0
  189. package/dist/types/api.d.ts +16 -0
  190. package/dist/types/api.d.ts.map +1 -0
  191. package/dist/types/api.js +2 -0
  192. package/dist/types/api.js.map +1 -0
  193. package/dist/types/config.d.ts +32 -0
  194. package/dist/types/config.d.ts.map +1 -0
  195. package/dist/types/config.js +4 -0
  196. package/dist/types/config.js.map +1 -0
  197. package/dist/types/index.d.ts +7 -0
  198. package/dist/types/index.d.ts.map +1 -0
  199. package/dist/types/index.js +2 -0
  200. package/dist/types/index.js.map +1 -0
  201. package/dist/types/middleware.d.ts +6 -0
  202. package/dist/types/middleware.d.ts.map +1 -0
  203. package/dist/types/middleware.js +2 -0
  204. package/dist/types/middleware.js.map +1 -0
  205. package/dist/types/page.d.ts +21 -0
  206. package/dist/types/page.d.ts.map +1 -0
  207. package/dist/types/page.js +2 -0
  208. package/dist/types/page.js.map +1 -0
  209. package/dist/types/plugin.d.ts +12 -0
  210. package/dist/types/plugin.d.ts.map +1 -0
  211. package/dist/types/plugin.js +2 -0
  212. package/dist/types/plugin.js.map +1 -0
  213. package/docs/cli.md +233 -0
  214. package/docs/components.md +114 -0
  215. package/docs/composables.md +99 -0
  216. package/docs/configuration.md +270 -0
  217. package/docs/data-loading.md +165 -0
  218. package/docs/getting-started.md +235 -0
  219. package/docs/head-management.md +206 -0
  220. package/docs/layouts.md +112 -0
  221. package/docs/middleware.md +140 -0
  222. package/docs/plugins.md +138 -0
  223. package/docs/rendering-modes.md +251 -0
  224. package/docs/routing.md +180 -0
  225. package/docs/server-api.md +172 -0
  226. package/docs/testing.md +462 -0
  227. package/package.json +75 -0
  228. package/src/__tests__/plugin/path-utils.test.ts +399 -0
  229. package/src/__tests__/plugin/scanner.test.ts +172 -0
  230. package/src/__tests__/plugin/transforms/auto-import.test.ts +229 -0
  231. package/src/__tests__/plugin/transforms/head-inject.test.ts +178 -0
  232. package/src/__tests__/plugin/virtual/components.test.ts +56 -0
  233. package/src/__tests__/plugin/virtual/composables.test.ts +57 -0
  234. package/src/__tests__/plugin/virtual/layouts.test.ts +70 -0
  235. package/src/__tests__/plugin/virtual/middleware.test.ts +68 -0
  236. package/src/__tests__/plugin/virtual/plugins.test.ts +84 -0
  237. package/src/__tests__/plugin/virtual/routes.test.ts +202 -0
  238. package/src/__tests__/plugin/virtual/server-api.test.ts +85 -0
  239. package/src/__tests__/runtime/use-head.test.ts +243 -0
  240. package/src/__tests__/runtime/use-page-data.test.ts +45 -0
  241. package/src/cli/commands/build.ts +114 -0
  242. package/src/cli/commands/dev.ts +101 -0
  243. package/src/cli/commands/generate.ts +81 -0
  244. package/src/cli/commands/preview.ts +218 -0
  245. package/src/cli/create/index.ts +250 -0
  246. package/src/cli/create/templates/spa/app/app.ts.tpl +74 -0
  247. package/src/cli/create/templates/spa/app/layouts/default.ts.tpl +15 -0
  248. package/src/cli/create/templates/spa/app/pages/index.ts.tpl +8 -0
  249. package/src/cli/create/templates/spa/cer.config.ts.tpl +6 -0
  250. package/src/cli/create/templates/spa/index.html.tpl +12 -0
  251. package/src/cli/create/templates/spa/package.json.tpl +18 -0
  252. package/src/cli/create/templates/ssg/app/app.ts.tpl +74 -0
  253. package/src/cli/create/templates/ssg/app/layouts/default.ts.tpl +15 -0
  254. package/src/cli/create/templates/ssg/app/pages/index.ts.tpl +17 -0
  255. package/src/cli/create/templates/ssg/cer.config.ts.tpl +13 -0
  256. package/src/cli/create/templates/ssg/index.html.tpl +12 -0
  257. package/src/cli/create/templates/ssg/package.json.tpl +19 -0
  258. package/src/cli/create/templates/ssr/app/app.ts.tpl +74 -0
  259. package/src/cli/create/templates/ssr/app/layouts/default.ts.tpl +15 -0
  260. package/src/cli/create/templates/ssr/app/pages/index.ts.tpl +8 -0
  261. package/src/cli/create/templates/ssr/cer.config.ts.tpl +10 -0
  262. package/src/cli/create/templates/ssr/index.html.tpl +12 -0
  263. package/src/cli/create/templates/ssr/package.json.tpl +18 -0
  264. package/src/cli/index.ts +20 -0
  265. package/src/index.ts +13 -0
  266. package/src/plugin/build-ssg.ts +259 -0
  267. package/src/plugin/build-ssr.ts +147 -0
  268. package/src/plugin/dev-server.ts +266 -0
  269. package/src/plugin/dts-generator.ts +214 -0
  270. package/src/plugin/index.ts +330 -0
  271. package/src/plugin/path-utils.ts +186 -0
  272. package/src/plugin/scanner.ts +65 -0
  273. package/src/plugin/transforms/auto-import.ts +190 -0
  274. package/src/plugin/transforms/head-inject.ts +161 -0
  275. package/src/plugin/virtual/components.ts +28 -0
  276. package/src/plugin/virtual/composables.ts +28 -0
  277. package/src/plugin/virtual/error.ts +34 -0
  278. package/src/plugin/virtual/layouts.ts +41 -0
  279. package/src/plugin/virtual/loading.ts +33 -0
  280. package/src/plugin/virtual/middleware.ts +45 -0
  281. package/src/plugin/virtual/plugins.ts +36 -0
  282. package/src/plugin/virtual/routes.ts +147 -0
  283. package/src/plugin/virtual/server-api.ts +52 -0
  284. package/src/plugin/virtual/server-middleware.ts +44 -0
  285. package/src/runtime/app-template.ts +142 -0
  286. package/src/runtime/composables/index.ts +3 -0
  287. package/src/runtime/composables/use-head.ts +204 -0
  288. package/src/runtime/composables/use-page-data.ts +39 -0
  289. package/src/runtime/entry-client-template.ts +17 -0
  290. package/src/runtime/entry-server-template.ts +71 -0
  291. package/src/types/api.ts +19 -0
  292. package/src/types/config.ts +39 -0
  293. package/src/types/index.ts +6 -0
  294. package/src/types/middleware.ts +16 -0
  295. package/src/types/page.ts +29 -0
  296. package/src/types/plugin.ts +13 -0
  297. package/tsconfig.build.json +10 -0
  298. package/tsconfig.json +19 -0
  299. 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