@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,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,3 @@
1
+ - fix: add nvmrc (7065201)
2
+ - feat: add publishing capability (812152b)
3
+ - initial commit (43a8365)
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=path-utils.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-utils.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/plugin/path-utils.test.ts"],"names":[],"mappings":""}