@jasonshimmy/vite-plugin-cer-app 0.23.2 → 0.25.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 (404) hide show
  1. package/dist/cli/adapters/netlify.js +4 -2
  2. package/dist/cli/adapters/netlify.js.map +1 -1
  3. package/dist/cli/checks/lighthouse.d.ts +26 -0
  4. package/dist/cli/checks/lighthouse.d.ts.map +1 -0
  5. package/dist/cli/checks/lighthouse.js +121 -0
  6. package/dist/cli/checks/lighthouse.js.map +1 -0
  7. package/dist/cli/checks/links.d.ts +12 -0
  8. package/dist/cli/checks/links.d.ts.map +1 -0
  9. package/dist/cli/checks/links.js +89 -0
  10. package/dist/cli/checks/links.js.map +1 -0
  11. package/dist/cli/checks/performance.d.ts +17 -0
  12. package/dist/cli/checks/performance.d.ts.map +1 -0
  13. package/dist/cli/checks/performance.js +61 -0
  14. package/dist/cli/checks/performance.js.map +1 -0
  15. package/dist/cli/commands/check.d.ts +3 -0
  16. package/dist/cli/commands/check.d.ts.map +1 -0
  17. package/dist/cli/commands/check.js +85 -0
  18. package/dist/cli/commands/check.js.map +1 -0
  19. package/dist/cli/commands/preview.d.ts.map +1 -1
  20. package/dist/cli/commands/preview.js +4 -2
  21. package/dist/cli/commands/preview.js.map +1 -1
  22. package/dist/cli/create/index.d.ts +11 -1
  23. package/dist/cli/create/index.d.ts.map +1 -1
  24. package/dist/cli/create/index.js +135 -65
  25. package/dist/cli/create/index.js.map +1 -1
  26. package/dist/cli/create/templates/spa/package.json.tpl +5 -3
  27. package/dist/cli/create/templates/ssg/cer.config.ts.tpl +2 -0
  28. package/dist/cli/create/templates/ssg/package.json.tpl +5 -3
  29. package/dist/cli/create/templates/ssr/package.json.tpl +5 -3
  30. package/dist/cli/index.js +2 -0
  31. package/dist/cli/index.js.map +1 -1
  32. package/dist/index.d.ts +4 -2
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/plugin/build-ssg.d.ts +22 -6
  35. package/dist/plugin/build-ssg.d.ts.map +1 -1
  36. package/dist/plugin/build-ssg.js +226 -17
  37. package/dist/plugin/build-ssg.js.map +1 -1
  38. package/dist/plugin/content/index.d.ts +1 -1
  39. package/dist/plugin/content/index.d.ts.map +1 -1
  40. package/dist/plugin/content/index.js +8 -8
  41. package/dist/plugin/content/index.js.map +1 -1
  42. package/dist/plugin/content/parser.d.ts +3 -3
  43. package/dist/plugin/content/parser.d.ts.map +1 -1
  44. package/dist/plugin/content/parser.js +103 -24
  45. package/dist/plugin/content/parser.js.map +1 -1
  46. package/dist/plugin/dev-server.d.ts +4 -1
  47. package/dist/plugin/dev-server.d.ts.map +1 -1
  48. package/dist/plugin/dev-server.js.map +1 -1
  49. package/dist/plugin/dts-generator.d.ts.map +1 -1
  50. package/dist/plugin/dts-generator.js +50 -8
  51. package/dist/plugin/dts-generator.js.map +1 -1
  52. package/dist/plugin/generated-dir.d.ts +2 -0
  53. package/dist/plugin/generated-dir.d.ts.map +1 -1
  54. package/dist/plugin/generated-dir.js +17 -6
  55. package/dist/plugin/generated-dir.js.map +1 -1
  56. package/dist/plugin/html-post-process.d.ts +7 -0
  57. package/dist/plugin/html-post-process.d.ts.map +1 -1
  58. package/dist/plugin/html-post-process.js +26 -0
  59. package/dist/plugin/html-post-process.js.map +1 -1
  60. package/dist/plugin/index.d.ts.map +1 -1
  61. package/dist/plugin/index.js +60 -13
  62. package/dist/plugin/index.js.map +1 -1
  63. package/dist/plugin/transforms/auto-import.d.ts.map +1 -1
  64. package/dist/plugin/transforms/auto-import.js +7 -0
  65. package/dist/plugin/transforms/auto-import.js.map +1 -1
  66. package/dist/plugin/transforms/prune-runtime-css.d.ts +8 -0
  67. package/dist/plugin/transforms/prune-runtime-css.d.ts.map +1 -0
  68. package/dist/plugin/transforms/prune-runtime-css.js +40 -0
  69. package/dist/plugin/transforms/prune-runtime-css.js.map +1 -0
  70. package/dist/runtime/app-template.d.ts +1 -1
  71. package/dist/runtime/app-template.d.ts.map +1 -1
  72. package/dist/runtime/app-template.js +206 -36
  73. package/dist/runtime/app-template.js.map +1 -1
  74. package/dist/runtime/composables/index.d.ts +4 -0
  75. package/dist/runtime/composables/index.d.ts.map +1 -1
  76. package/dist/runtime/composables/index.js +2 -0
  77. package/dist/runtime/composables/index.js.map +1 -1
  78. package/dist/runtime/composables/use-active-headings.d.ts +24 -0
  79. package/dist/runtime/composables/use-active-headings.d.ts.map +1 -0
  80. package/dist/runtime/composables/use-active-headings.js +112 -0
  81. package/dist/runtime/composables/use-active-headings.js.map +1 -0
  82. package/dist/runtime/composables/use-content-page.d.ts +45 -0
  83. package/dist/runtime/composables/use-content-page.d.ts.map +1 -0
  84. package/dist/runtime/composables/use-content-page.js +181 -0
  85. package/dist/runtime/composables/use-content-page.js.map +1 -0
  86. package/dist/runtime/composables/use-content-search.d.ts.map +1 -1
  87. package/dist/runtime/composables/use-content-search.js +1 -5
  88. package/dist/runtime/composables/use-content-search.js.map +1 -1
  89. package/dist/runtime/composables/use-fetch.d.ts +2 -1
  90. package/dist/runtime/composables/use-fetch.d.ts.map +1 -1
  91. package/dist/runtime/composables/use-fetch.js +112 -43
  92. package/dist/runtime/composables/use-fetch.js.map +1 -1
  93. package/dist/runtime/composables/use-route.d.ts.map +1 -1
  94. package/dist/runtime/composables/use-route.js +39 -11
  95. package/dist/runtime/composables/use-route.js.map +1 -1
  96. package/dist/runtime/content/client.js +2 -2
  97. package/dist/runtime/content/client.js.map +1 -1
  98. package/dist/runtime/entry-server-template.d.ts +16 -1
  99. package/dist/runtime/entry-server-template.d.ts.map +1 -1
  100. package/dist/runtime/entry-server-template.js +120 -13
  101. package/dist/runtime/entry-server-template.js.map +1 -1
  102. package/dist/types/config.d.ts +45 -4
  103. package/dist/types/config.d.ts.map +1 -1
  104. package/dist/types/config.js.map +1 -1
  105. package/dist/types/content.d.ts +18 -1
  106. package/dist/types/content.d.ts.map +1 -1
  107. package/dist/types/middleware.d.ts +2 -2
  108. package/dist/types/middleware.d.ts.map +1 -1
  109. package/package.json +22 -19
  110. package/.github/copilot-instructions.md +0 -169
  111. package/.github/workflows/publish.yml +0 -257
  112. package/.nvmrc +0 -1
  113. package/CHANGELOG.md +0 -236
  114. package/CLAUDE.md +0 -113
  115. package/commits.txt +0 -1
  116. package/cypress.config.ts +0 -16
  117. package/docs/authentication.md +0 -285
  118. package/docs/cli.md +0 -324
  119. package/docs/components.md +0 -237
  120. package/docs/composables.md +0 -907
  121. package/docs/configuration.md +0 -696
  122. package/docs/content.md +0 -570
  123. package/docs/data-loading.md +0 -336
  124. package/docs/getting-started.md +0 -237
  125. package/docs/head-management.md +0 -212
  126. package/docs/i18n.md +0 -159
  127. package/docs/layouts.md +0 -224
  128. package/docs/middleware.md +0 -234
  129. package/docs/plugins.md +0 -170
  130. package/docs/rendering-modes.md +0 -377
  131. package/docs/routing.md +0 -485
  132. package/docs/server-api.md +0 -253
  133. package/docs/testing.md +0 -455
  134. package/docs/use-fetch.md +0 -183
  135. package/e2e/cypress/e2e/api.cy.ts +0 -123
  136. package/e2e/cypress/e2e/async-component.cy.ts +0 -27
  137. package/e2e/cypress/e2e/component-splitting.cy.ts +0 -50
  138. package/e2e/cypress/e2e/content.cy.ts +0 -425
  139. package/e2e/cypress/e2e/cookie.cy.ts +0 -85
  140. package/e2e/cypress/e2e/data.cy.ts +0 -158
  141. package/e2e/cypress/e2e/error-boundary.cy.ts +0 -88
  142. package/e2e/cypress/e2e/fouc.cy.ts +0 -254
  143. package/e2e/cypress/e2e/group-meta.cy.ts +0 -36
  144. package/e2e/cypress/e2e/head.cy.ts +0 -89
  145. package/e2e/cypress/e2e/i18n.cy.ts +0 -79
  146. package/e2e/cypress/e2e/interactive.cy.ts +0 -137
  147. package/e2e/cypress/e2e/isr-nested-runtime.cy.ts +0 -102
  148. package/e2e/cypress/e2e/jit-css-custom-colors.cy.ts +0 -43
  149. package/e2e/cypress/e2e/middleware-chain.cy.ts +0 -89
  150. package/e2e/cypress/e2e/middleware-status.cy.ts +0 -30
  151. package/e2e/cypress/e2e/middleware.cy.ts +0 -45
  152. package/e2e/cypress/e2e/navigate.cy.ts +0 -39
  153. package/e2e/cypress/e2e/observability.cy.ts +0 -77
  154. package/e2e/cypress/e2e/per-route-error.cy.ts +0 -42
  155. package/e2e/cypress/e2e/per-route-render.cy.ts +0 -70
  156. package/e2e/cypress/e2e/preview-hardening.cy.ts +0 -88
  157. package/e2e/cypress/e2e/routes.cy.ts +0 -128
  158. package/e2e/cypress/e2e/seo-meta.cy.ts +0 -108
  159. package/e2e/cypress/e2e/server-middleware.cy.ts +0 -46
  160. package/e2e/cypress/e2e/session-rotation.cy.ts +0 -25
  161. package/e2e/cypress/e2e/session.cy.ts +0 -73
  162. package/e2e/cypress/e2e/ssr-render-error.cy.ts +0 -68
  163. package/e2e/cypress/e2e/synthetic-404.cy.ts +0 -45
  164. package/e2e/cypress/e2e/use-page-data.cy.ts +0 -122
  165. package/e2e/cypress/e2e/use-state.cy.ts +0 -138
  166. package/e2e/cypress/support/commands.ts +0 -120
  167. package/e2e/cypress/support/e2e.ts +0 -10
  168. package/e2e/cypress/tsconfig.json +0 -14
  169. package/e2e/kitchen-sink/app/components/ks-badge.ts +0 -8
  170. package/e2e/kitchen-sink/app/composables/useKsCounter.ts +0 -9
  171. package/e2e/kitchen-sink/app/error.ts +0 -18
  172. package/e2e/kitchen-sink/app/layouts/admin.ts +0 -13
  173. package/e2e/kitchen-sink/app/layouts/default.ts +0 -25
  174. package/e2e/kitchen-sink/app/layouts/group.ts +0 -9
  175. package/e2e/kitchen-sink/app/layouts/minimal.ts +0 -7
  176. package/e2e/kitchen-sink/app/loading.ts +0 -9
  177. package/e2e/kitchen-sink/app/middleware/auth.ts +0 -9
  178. package/e2e/kitchen-sink/app/middleware/custom-log.ts +0 -4
  179. package/e2e/kitchen-sink/app/middleware/group-auth.ts +0 -7
  180. package/e2e/kitchen-sink/app/middleware/logger.ts +0 -14
  181. package/e2e/kitchen-sink/app/pages/(auth)/login.ts +0 -13
  182. package/e2e/kitchen-sink/app/pages/(auth)/protected.ts +0 -16
  183. package/e2e/kitchen-sink/app/pages/404.ts +0 -9
  184. package/e2e/kitchen-sink/app/pages/about.ts +0 -17
  185. package/e2e/kitchen-sink/app/pages/admin/_layout.ts +0 -1
  186. package/e2e/kitchen-sink/app/pages/admin/dashboard.ts +0 -11
  187. package/e2e/kitchen-sink/app/pages/async-component-error-test.ts +0 -18
  188. package/e2e/kitchen-sink/app/pages/async-component-test.ts +0 -18
  189. package/e2e/kitchen-sink/app/pages/blog/[slug].ts +0 -59
  190. package/e2e/kitchen-sink/app/pages/blog/index.ts +0 -51
  191. package/e2e/kitchen-sink/app/pages/content-blog.ts +0 -37
  192. package/e2e/kitchen-sink/app/pages/content-doc.ts +0 -42
  193. package/e2e/kitchen-sink/app/pages/content-fallback.ts +0 -36
  194. package/e2e/kitchen-sink/app/pages/content-guides.ts +0 -35
  195. package/e2e/kitchen-sink/app/pages/content-index.ts +0 -39
  196. package/e2e/kitchen-sink/app/pages/content-search.ts +0 -36
  197. package/e2e/kitchen-sink/app/pages/cookie-test.ts +0 -22
  198. package/e2e/kitchen-sink/app/pages/counter.ts +0 -17
  199. package/e2e/kitchen-sink/app/pages/group-auth-blocked.ts +0 -9
  200. package/e2e/kitchen-sink/app/pages/group-meta-test/_layout.ts +0 -6
  201. package/e2e/kitchen-sink/app/pages/group-meta-test/custom-mw.ts +0 -14
  202. package/e2e/kitchen-sink/app/pages/group-meta-test/page.ts +0 -9
  203. package/e2e/kitchen-sink/app/pages/group-meta-test/protected.ts +0 -8
  204. package/e2e/kitchen-sink/app/pages/head.ts +0 -20
  205. package/e2e/kitchen-sink/app/pages/i18n-test.ts +0 -14
  206. package/e2e/kitchen-sink/app/pages/index.ts +0 -27
  207. package/e2e/kitchen-sink/app/pages/isr-test.ts +0 -17
  208. package/e2e/kitchen-sink/app/pages/items/[id].ts +0 -20
  209. package/e2e/kitchen-sink/app/pages/jit-css-custom-colors-test.ts +0 -14
  210. package/e2e/kitchen-sink/app/pages/loader-error-test.ts +0 -13
  211. package/e2e/kitchen-sink/app/pages/loader-props-test.ts +0 -19
  212. package/e2e/kitchen-sink/app/pages/loader-response-error-test.ts +0 -13
  213. package/e2e/kitchen-sink/app/pages/middleware-chain-test.ts +0 -12
  214. package/e2e/kitchen-sink/app/pages/middleware-status-test.ts +0 -9
  215. package/e2e/kitchen-sink/app/pages/navigate-test.ts +0 -16
  216. package/e2e/kitchen-sink/app/pages/observability-test.ts +0 -25
  217. package/e2e/kitchen-sink/app/pages/per-route-error-test.error.ts +0 -13
  218. package/e2e/kitchen-sink/app/pages/per-route-error-test.ts +0 -15
  219. package/e2e/kitchen-sink/app/pages/plugin-dsd-test.ts +0 -10
  220. package/e2e/kitchen-sink/app/pages/render-error-test.ts +0 -12
  221. package/e2e/kitchen-sink/app/pages/render-server-test.ts +0 -12
  222. package/e2e/kitchen-sink/app/pages/render-spa-test.ts +0 -12
  223. package/e2e/kitchen-sink/app/pages/route-info.ts +0 -14
  224. package/e2e/kitchen-sink/app/pages/seo-test.ts +0 -23
  225. package/e2e/kitchen-sink/app/pages/session-rotation-test.ts +0 -25
  226. package/e2e/kitchen-sink/app/pages/use-state-test.ts +0 -37
  227. package/e2e/kitchen-sink/app/plugins/01.setup.ts +0 -7
  228. package/e2e/kitchen-sink/app/plugins/02.ks-plugin-components.ts +0 -27
  229. package/e2e/kitchen-sink/cer-auto-imports.d.ts +0 -51
  230. package/e2e/kitchen-sink/cer-env.d.ts +0 -35
  231. package/e2e/kitchen-sink/cer-tsconfig.json +0 -30
  232. package/e2e/kitchen-sink/cer.config.ts +0 -43
  233. package/e2e/kitchen-sink/content/01.guides/01.index.md +0 -8
  234. package/e2e/kitchen-sink/content/01.guides/02.intro.md +0 -8
  235. package/e2e/kitchen-sink/content/01.guides/10.advanced.md +0 -8
  236. package/e2e/kitchen-sink/content/blog/2026-04-01-hello.md +0 -26
  237. package/e2e/kitchen-sink/content/blog/2026-04-02-draft.md +0 -10
  238. package/e2e/kitchen-sink/content/blog/index.md +0 -8
  239. package/e2e/kitchen-sink/content/blog/no-frontmatter.md +0 -7
  240. package/e2e/kitchen-sink/content/docs/getting-started.md +0 -48
  241. package/e2e/kitchen-sink/content/index.md +0 -16
  242. package/e2e/kitchen-sink/server/api/echo.ts +0 -12
  243. package/e2e/kitchen-sink/server/api/health.ts +0 -3
  244. package/e2e/kitchen-sink/server/api/posts/[slug].ts +0 -11
  245. package/e2e/kitchen-sink/server/api/posts/index.ts +0 -5
  246. package/e2e/kitchen-sink/server/api/session.ts +0 -23
  247. package/e2e/kitchen-sink/server/data/posts.ts +0 -21
  248. package/e2e/kitchen-sink/server/middleware/01-headers.ts +0 -6
  249. package/e2e/kitchen-sink/server/middleware/02-status-test.ts +0 -10
  250. package/e2e/kitchen-sink/tsconfig.json +0 -3
  251. package/e2e/scripts/clean.mjs +0 -14
  252. package/eslint.config.ts +0 -22
  253. package/src/__tests__/cli/adapters/cloudflare-worker.integration.test.ts +0 -132
  254. package/src/__tests__/cli/adapters/cloudflare.test.ts +0 -332
  255. package/src/__tests__/cli/adapters/netlify-bridge.integration.test.ts +0 -139
  256. package/src/__tests__/cli/adapters/netlify.test.ts +0 -286
  257. package/src/__tests__/cli/adapters/vercel-launcher.integration.test.ts +0 -129
  258. package/src/__tests__/cli/adapters/vercel.test.ts +0 -263
  259. package/src/__tests__/cli/preview-hardening.test.ts +0 -265
  260. package/src/__tests__/cli/preview-isr.test.ts +0 -320
  261. package/src/__tests__/index.test.ts +0 -21
  262. package/src/__tests__/plugin/app-template.test.ts +0 -166
  263. package/src/__tests__/plugin/build-ssg-render.test.ts +0 -155
  264. package/src/__tests__/plugin/build-ssg.test.ts +0 -598
  265. package/src/__tests__/plugin/build-ssr.test.ts +0 -192
  266. package/src/__tests__/plugin/cer-app-plugin.test.ts +0 -552
  267. package/src/__tests__/plugin/content/emitter.test.ts +0 -117
  268. package/src/__tests__/plugin/content/loader.test.ts +0 -154
  269. package/src/__tests__/plugin/content/parser.test.ts +0 -381
  270. package/src/__tests__/plugin/content/path-utils.test.ts +0 -69
  271. package/src/__tests__/plugin/content/search.test.ts +0 -119
  272. package/src/__tests__/plugin/dev-server.test.ts +0 -813
  273. package/src/__tests__/plugin/dts-generator.test.ts +0 -394
  274. package/src/__tests__/plugin/entry-server-template.test.ts +0 -385
  275. package/src/__tests__/plugin/generated-dir.test.ts +0 -144
  276. package/src/__tests__/plugin/html-post-process.test.ts +0 -146
  277. package/src/__tests__/plugin/path-utils.test.ts +0 -411
  278. package/src/__tests__/plugin/resolve-config.test.ts +0 -233
  279. package/src/__tests__/plugin/scanner.test.ts +0 -186
  280. package/src/__tests__/plugin/transforms/auto-import.test.ts +0 -510
  281. package/src/__tests__/plugin/transforms/head-inject.test.ts +0 -178
  282. package/src/__tests__/plugin/virtual/composables.test.ts +0 -57
  283. package/src/__tests__/plugin/virtual/content-components.test.ts +0 -66
  284. package/src/__tests__/plugin/virtual/error.test.ts +0 -71
  285. package/src/__tests__/plugin/virtual/layouts.test.ts +0 -70
  286. package/src/__tests__/plugin/virtual/loading.test.ts +0 -72
  287. package/src/__tests__/plugin/virtual/middleware.test.ts +0 -83
  288. package/src/__tests__/plugin/virtual/plugins.test.ts +0 -129
  289. package/src/__tests__/plugin/virtual/routes.test.ts +0 -884
  290. package/src/__tests__/plugin/virtual/server-api.test.ts +0 -164
  291. package/src/__tests__/plugin/virtual/server-middleware.test.ts +0 -102
  292. package/src/__tests__/runtime/app-template.test.ts +0 -208
  293. package/src/__tests__/runtime/define-middleware.test.ts +0 -64
  294. package/src/__tests__/runtime/define-server-middleware.test.ts +0 -54
  295. package/src/__tests__/runtime/entry-client-template.test.ts +0 -61
  296. package/src/__tests__/runtime/isr-handler.test.ts +0 -338
  297. package/src/__tests__/runtime/oauth/handler.test.ts +0 -358
  298. package/src/__tests__/runtime/oauth/providers.test.ts +0 -47
  299. package/src/__tests__/runtime/oauth-index.test.ts +0 -43
  300. package/src/__tests__/runtime/use-auth.test.ts +0 -169
  301. package/src/__tests__/runtime/use-content-search-composable.test.ts +0 -405
  302. package/src/__tests__/runtime/use-content-search.test.ts +0 -163
  303. package/src/__tests__/runtime/use-content.test.ts +0 -226
  304. package/src/__tests__/runtime/use-cookie.test.ts +0 -277
  305. package/src/__tests__/runtime/use-fetch-component.test.ts +0 -214
  306. package/src/__tests__/runtime/use-fetch.test.ts +0 -377
  307. package/src/__tests__/runtime/use-head.test.ts +0 -311
  308. package/src/__tests__/runtime/use-inject-client.test.ts +0 -67
  309. package/src/__tests__/runtime/use-inject.test.ts +0 -66
  310. package/src/__tests__/runtime/use-locale.test.ts +0 -152
  311. package/src/__tests__/runtime/use-navigate.test.ts +0 -121
  312. package/src/__tests__/runtime/use-page-data.test.ts +0 -298
  313. package/src/__tests__/runtime/use-route.test.ts +0 -156
  314. package/src/__tests__/runtime/use-runtime-config.test.ts +0 -182
  315. package/src/__tests__/runtime/use-seo-meta.test.ts +0 -109
  316. package/src/__tests__/runtime/use-session.test.ts +0 -320
  317. package/src/__tests__/runtime/use-state.test.ts +0 -340
  318. package/src/__tests__/types/config.test.ts +0 -18
  319. package/src/cli/adapters/cloudflare.ts +0 -335
  320. package/src/cli/adapters/netlify.ts +0 -326
  321. package/src/cli/adapters/vercel.ts +0 -311
  322. package/src/cli/commands/adapt.ts +0 -84
  323. package/src/cli/commands/build.ts +0 -165
  324. package/src/cli/commands/dev.ts +0 -125
  325. package/src/cli/commands/generate.ts +0 -91
  326. package/src/cli/commands/preview-isr.ts +0 -172
  327. package/src/cli/commands/preview.ts +0 -523
  328. package/src/cli/create/index.ts +0 -275
  329. package/src/cli/create/templates/shared/.gitignore.tpl +0 -25
  330. package/src/cli/create/templates/shared/app/layouts/default.ts.tpl +0 -15
  331. package/src/cli/create/templates/shared/app/pages/index.ts.tpl +0 -8
  332. package/src/cli/create/templates/shared/index.html.tpl +0 -12
  333. package/src/cli/create/templates/shared/tsconfig.json.tpl +0 -6
  334. package/src/cli/create/templates/spa/cer.config.ts.tpl +0 -6
  335. package/src/cli/create/templates/spa/package.json.tpl +0 -18
  336. package/src/cli/create/templates/ssg/cer.config.ts.tpl +0 -10
  337. package/src/cli/create/templates/ssg/package.json.tpl +0 -19
  338. package/src/cli/create/templates/ssr/cer.config.ts.tpl +0 -6
  339. package/src/cli/create/templates/ssr/package.json.tpl +0 -18
  340. package/src/cli/index.ts +0 -22
  341. package/src/index.ts +0 -20
  342. package/src/plugin/build-ssg.ts +0 -403
  343. package/src/plugin/build-ssr.ts +0 -134
  344. package/src/plugin/content/emitter.ts +0 -50
  345. package/src/plugin/content/index.ts +0 -233
  346. package/src/plugin/content/parser.ts +0 -259
  347. package/src/plugin/content/path-utils.ts +0 -55
  348. package/src/plugin/content/scanner.ts +0 -26
  349. package/src/plugin/content/search.ts +0 -28
  350. package/src/plugin/dev-server.ts +0 -379
  351. package/src/plugin/dts-generator.ts +0 -273
  352. package/src/plugin/generated-dir.ts +0 -121
  353. package/src/plugin/html-post-process.ts +0 -96
  354. package/src/plugin/index.ts +0 -584
  355. package/src/plugin/path-utils.ts +0 -186
  356. package/src/plugin/scanner.ts +0 -65
  357. package/src/plugin/transforms/auto-import.ts +0 -206
  358. package/src/plugin/transforms/head-inject.ts +0 -161
  359. package/src/plugin/virtual/composables.ts +0 -28
  360. package/src/plugin/virtual/content-components.ts +0 -83
  361. package/src/plugin/virtual/error.ts +0 -34
  362. package/src/plugin/virtual/layouts.ts +0 -41
  363. package/src/plugin/virtual/loading.ts +0 -32
  364. package/src/plugin/virtual/middleware.ts +0 -45
  365. package/src/plugin/virtual/plugins.ts +0 -46
  366. package/src/plugin/virtual/routes.ts +0 -514
  367. package/src/plugin/virtual/server-api.ts +0 -85
  368. package/src/plugin/virtual/server-middleware.ts +0 -44
  369. package/src/runtime/app-template.ts +0 -377
  370. package/src/runtime/composables/define-middleware.ts +0 -17
  371. package/src/runtime/composables/define-server-middleware.ts +0 -18
  372. package/src/runtime/composables/index.ts +0 -27
  373. package/src/runtime/composables/use-auth.ts +0 -125
  374. package/src/runtime/composables/use-content-search.ts +0 -205
  375. package/src/runtime/composables/use-content.ts +0 -146
  376. package/src/runtime/composables/use-cookie.ts +0 -135
  377. package/src/runtime/composables/use-fetch.ts +0 -438
  378. package/src/runtime/composables/use-head.ts +0 -234
  379. package/src/runtime/composables/use-inject.ts +0 -49
  380. package/src/runtime/composables/use-locale.ts +0 -109
  381. package/src/runtime/composables/use-navigate.ts +0 -58
  382. package/src/runtime/composables/use-page-data.ts +0 -93
  383. package/src/runtime/composables/use-route.ts +0 -71
  384. package/src/runtime/composables/use-runtime-config.ts +0 -123
  385. package/src/runtime/composables/use-seo-meta.ts +0 -80
  386. package/src/runtime/composables/use-session.ts +0 -186
  387. package/src/runtime/composables/use-state.ts +0 -92
  388. package/src/runtime/content/client.ts +0 -168
  389. package/src/runtime/entry-client-template.ts +0 -39
  390. package/src/runtime/entry-server-template.ts +0 -493
  391. package/src/runtime/isr-handler.ts +0 -184
  392. package/src/runtime/oauth/handler.ts +0 -407
  393. package/src/runtime/oauth/index.ts +0 -49
  394. package/src/runtime/oauth/providers.ts +0 -38
  395. package/src/types/api.ts +0 -40
  396. package/src/types/config.ts +0 -379
  397. package/src/types/content.ts +0 -66
  398. package/src/types/index.ts +0 -7
  399. package/src/types/middleware.ts +0 -63
  400. package/src/types/page.ts +0 -117
  401. package/src/types/plugin.ts +0 -34
  402. package/tsconfig.build.json +0 -10
  403. package/tsconfig.json +0 -19
  404. package/vitest.config.ts +0 -33
@@ -1,813 +0,0 @@
1
- import { vi, describe, it, expect, beforeEach } from 'vitest'
2
- import { EventEmitter } from 'node:events'
3
- vi.mock('node:fs', () => ({
4
- existsSync: vi.fn().mockReturnValue(false),
5
- readFileSync: vi.fn().mockReturnValue('<html>shell</html>'),
6
- }))
7
-
8
- import { existsSync, readFileSync } from 'node:fs'
9
- import { configureCerDevServer } from '../../plugin/dev-server.js'
10
- import type { ResolvedCerConfig } from '../../plugin/dev-server.js'
11
-
12
- // ─── Helpers ─────────────────────────────────────────────────────────────────
13
-
14
- /**
15
- * Creates a minimal mock IncomingMessage.
16
- * Body events are emitted via setImmediate so they fire after readBody
17
- * registers its 'data'/'end' listeners (which happens inside the async
18
- * middleware after several awaited ssrLoadModule calls).
19
- */
20
- function createReq(opts: {
21
- url?: string
22
- method?: string
23
- headers?: Record<string, string>
24
- body?: string
25
- } = {}) {
26
- const emitter = new EventEmitter()
27
- setImmediate(() => {
28
- if (opts.body !== undefined) emitter.emit('data', Buffer.from(opts.body))
29
- emitter.emit('end')
30
- })
31
- return Object.assign(emitter, {
32
- url: opts.url ?? '/',
33
- method: opts.method ?? 'GET',
34
- headers: opts.headers ?? {},
35
- })
36
- }
37
-
38
- function createRes() {
39
- const res: any = { statusCode: 200, setHeader: vi.fn(), end: vi.fn() }
40
- return res
41
- }
42
-
43
- function makeConfig(overrides: Partial<ResolvedCerConfig> = {}): ResolvedCerConfig {
44
- return {
45
- mode: 'spa',
46
- root: '/project',
47
- srcDir: '/project/app',
48
- pagesDir: '/project/app/pages',
49
- layoutsDir: '/project/app/layouts',
50
- componentsDir: '/project/app/components',
51
- composablesDir: '/project/app/composables',
52
- pluginsDir: '/project/app/plugins',
53
- middlewareDir: '/project/app/middleware',
54
- serverApiDir: '/project/server/api',
55
- serverMiddlewareDir: '/project/server/middleware',
56
- port: 3000,
57
- ssg: { routes: 'auto', concurrency: 2, fallback: false },
58
- router: {},
59
- jitCss: { content: [], extendedColors: false },
60
- autoImports: { components: true, composables: true, directives: true, runtime: true },
61
- ...overrides,
62
- } as ResolvedCerConfig
63
- }
64
-
65
- type MockServer = {
66
- middlewares: { use: ReturnType<typeof vi.fn> }
67
- ssrLoadModule: ReturnType<typeof vi.fn>
68
- ssrFixStacktrace: ReturnType<typeof vi.fn>
69
- transformIndexHtml: ReturnType<typeof vi.fn>
70
- }
71
-
72
- function makeServer(): { server: MockServer; getMiddleware: () => Function } {
73
- const registered: Function[] = []
74
- const server: MockServer = {
75
- middlewares: { use: vi.fn((fn: Function) => registered.push(fn)) },
76
- ssrLoadModule: vi.fn(async (path: string) => {
77
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
78
- return { apiRoutes: [] }
79
- }),
80
- ssrFixStacktrace: vi.fn(),
81
- transformIndexHtml: vi.fn(async (_url: string, html: string) => html),
82
- }
83
- return { server, getMiddleware: () => registered[0] }
84
- }
85
-
86
- beforeEach(() => {
87
- vi.mocked(existsSync).mockReturnValue(false)
88
- vi.mocked(readFileSync).mockReturnValue('<html>shell</html>' as never)
89
- })
90
-
91
- // ─── Registration ─────────────────────────────────────────────────────────────
92
-
93
- describe('configureCerDevServer — registration', () => {
94
- it('registers exactly one middleware via server.middlewares.use', () => {
95
- const { server } = makeServer()
96
- configureCerDevServer(server as any, makeConfig())
97
- expect(server.middlewares.use).toHaveBeenCalledTimes(1)
98
- expect(typeof (server.middlewares.use as any).mock.calls[0][0]).toBe('function')
99
- })
100
- })
101
-
102
- // ─── Non-API pass-through ─────────────────────────────────────────────────────
103
-
104
- describe('configureCerDevServer — non-API pass-through', () => {
105
- it('calls next() for a non-HTML request with no matching routes', async () => {
106
- const { server, getMiddleware } = makeServer()
107
- configureCerDevServer(server as any, makeConfig())
108
- const next = vi.fn()
109
- await getMiddleware()(createReq({ url: '/assets/main.js', headers: { accept: 'application/javascript' } }), createRes(), next)
110
- expect(next).toHaveBeenCalled()
111
- })
112
-
113
- it('calls next() when ssrLoadModule throws (module not ready)', async () => {
114
- const { server, getMiddleware } = makeServer()
115
- server.ssrLoadModule.mockRejectedValue(new Error('not ready'))
116
- configureCerDevServer(server as any, makeConfig())
117
- const next = vi.fn()
118
- await getMiddleware()(createReq({ url: '/test' }), createRes(), next)
119
- expect(next).toHaveBeenCalled()
120
- })
121
- })
122
-
123
- // ─── SPA shell handling ──────────────────────────────────────────────────────
124
-
125
- describe('configureCerDevServer — SPA shell handling', () => {
126
- it('serves the transformed SPA shell for direct HTML navigations in spa mode', async () => {
127
- const { server, getMiddleware } = makeServer()
128
- server.transformIndexHtml.mockResolvedValue('<html>spa shell</html>')
129
- vi.mocked(existsSync).mockReturnValue(true)
130
- vi.mocked(readFileSync).mockReturnValue('<html>source shell</html>' as never)
131
- const res = createRes()
132
-
133
- configureCerDevServer(
134
- server as any,
135
- makeConfig({ mode: 'spa' }),
136
- )
137
-
138
- const next = vi.fn()
139
- await getMiddleware()(
140
- createReq({ url: '/music/effects/pro-co/rat', headers: { accept: 'text/html' } }),
141
- res,
142
- next,
143
- )
144
-
145
- expect(server.transformIndexHtml).toHaveBeenCalled()
146
- expect(res.statusCode).toBe(200)
147
- expect(res.setHeader).toHaveBeenCalledWith('Content-Type', 'text/html; charset=utf-8')
148
- expect(res.end).toHaveBeenCalledWith('<html>spa shell</html>')
149
- expect(next).not.toHaveBeenCalled()
150
- })
151
-
152
- it('does not serve the SPA shell for API requests in spa mode', async () => {
153
- const { server, getMiddleware } = makeServer()
154
- configureCerDevServer(
155
- server as any,
156
- makeConfig({ mode: 'spa' }),
157
- )
158
-
159
- const next = vi.fn()
160
- await getMiddleware()(
161
- createReq({ url: '/api/missing', headers: { accept: 'text/html' } }),
162
- createRes(),
163
- next,
164
- )
165
-
166
- expect(server.transformIndexHtml).not.toHaveBeenCalled()
167
- expect(next).toHaveBeenCalled()
168
- })
169
- })
170
-
171
- // ─── API route matching ───────────────────────────────────────────────────────
172
-
173
- describe('configureCerDevServer — API route matching', () => {
174
- function makeServerWithRoute(route: { path: string; handlers: Record<string, unknown> }) {
175
- const { server, getMiddleware } = makeServer()
176
- server.ssrLoadModule.mockImplementation(async (path: string) => {
177
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
178
- return { apiRoutes: [route] }
179
- })
180
- configureCerDevServer(server as any, makeConfig())
181
- return getMiddleware()
182
- }
183
-
184
- it('calls GET handler for matching path', async () => {
185
- const handler = vi.fn((req: any, res: any) => res.end('ok'))
186
- const middleware = makeServerWithRoute({ path: '/api/health', handlers: { get: handler } })
187
- const next = vi.fn()
188
- await middleware(createReq({ url: '/api/health' }), createRes(), next)
189
- expect(handler).toHaveBeenCalled()
190
- expect(next).not.toHaveBeenCalled()
191
- })
192
-
193
- it('calls uppercase GET handler when lowercase is absent', async () => {
194
- const handler = vi.fn((req: any, res: any) => res.end('ok'))
195
- const middleware = makeServerWithRoute({ path: '/api/health', handlers: { GET: handler } })
196
- await middleware(createReq({ url: '/api/health' }), createRes(), vi.fn())
197
- expect(handler).toHaveBeenCalled()
198
- })
199
-
200
- it('calls default handler as fallback', async () => {
201
- const handler = vi.fn((req: any, res: any) => res.end('ok'))
202
- const middleware = makeServerWithRoute({ path: '/api/health', handlers: { default: handler } })
203
- await middleware(createReq({ url: '/api/health' }), createRes(), vi.fn())
204
- expect(handler).toHaveBeenCalled()
205
- })
206
-
207
- it('calls next() when no handler matches the HTTP method', async () => {
208
- const middleware = makeServerWithRoute({ path: '/api/health', handlers: {} })
209
- const next = vi.fn()
210
- await middleware(createReq({ url: '/api/health' }), createRes(), next)
211
- expect(next).toHaveBeenCalled()
212
- })
213
-
214
- it('does not match when segment counts differ', async () => {
215
- const handler = vi.fn()
216
- const middleware = makeServerWithRoute({ path: '/api/users/:id', handlers: { get: handler } })
217
- const next = vi.fn()
218
- await middleware(createReq({ url: '/api/users' }), createRes(), next)
219
- expect(handler).not.toHaveBeenCalled()
220
- expect(next).toHaveBeenCalled()
221
- })
222
-
223
- it('does not match when a static segment differs', async () => {
224
- const handler = vi.fn()
225
- const middleware = makeServerWithRoute({ path: '/api/posts', handlers: { get: handler } })
226
- const next = vi.fn()
227
- await middleware(createReq({ url: '/api/users' }), createRes(), next)
228
- expect(handler).not.toHaveBeenCalled()
229
- })
230
-
231
- it('extracts dynamic route params and attaches to req', async () => {
232
- let capturedReq: any
233
- const handler = vi.fn((req: any, res: any) => { capturedReq = req; res.end('ok') })
234
- const middleware = makeServerWithRoute({ path: '/api/users/:id', handlers: { get: handler } })
235
- await middleware(createReq({ url: '/api/users/42' }), createRes(), vi.fn())
236
- expect(capturedReq.params).toEqual({ id: '42' })
237
- })
238
-
239
- it('attaches parsed query string to req', async () => {
240
- let capturedReq: any
241
- const handler = vi.fn((req: any, res: any) => { capturedReq = req; res.end('ok') })
242
- const middleware = makeServerWithRoute({ path: '/api/search', handlers: { get: handler } })
243
- await middleware(createReq({ url: '/api/search?q=hello&page=2' }), createRes(), vi.fn())
244
- expect(capturedReq.query).toEqual({ q: 'hello', page: '2' })
245
- })
246
-
247
- it('attaches empty query object when no query string', async () => {
248
- let capturedReq: any
249
- const handler = vi.fn((req: any, res: any) => { capturedReq = req; res.end('ok') })
250
- const middleware = makeServerWithRoute({ path: '/api/health', handlers: { get: handler } })
251
- await middleware(createReq({ url: '/api/health' }), createRes(), vi.fn())
252
- expect(capturedReq.query).toEqual({})
253
- })
254
-
255
- it('handles query param with no value (flag style ?active)', async () => {
256
- let capturedReq: any
257
- const handler = vi.fn((req: any, res: any) => { capturedReq = req; res.end('ok') })
258
- const middleware = makeServerWithRoute({ path: '/api/items', handlers: { get: handler } })
259
- await middleware(createReq({ url: '/api/items?active' }), createRes(), vi.fn())
260
- expect(capturedReq.query).toEqual({ active: '' })
261
- })
262
-
263
- it('skips empty segments from double-ampersand query strings', async () => {
264
- let capturedReq: any
265
- const handler = vi.fn((req: any, res: any) => { capturedReq = req; res.end('ok') })
266
- const middleware = makeServerWithRoute({ path: '/api/items', handlers: { get: handler } })
267
- await middleware(createReq({ url: '/api/items?a=1&&b=2' }), createRes(), vi.fn())
268
- expect(capturedReq.query).toEqual({ a: '1', b: '2' })
269
- })
270
-
271
- it('decodes URL-encoded route params', async () => {
272
- let capturedReq: any
273
- const handler = vi.fn((req: any, res: any) => { capturedReq = req; res.end('ok') })
274
- const middleware = makeServerWithRoute({ path: '/api/posts/:slug', handlers: { get: handler } })
275
- await middleware(createReq({ url: '/api/posts/hello%20world' }), createRes(), vi.fn())
276
- expect(capturedReq.params.slug).toBe('hello world')
277
- })
278
-
279
- it('returns 500 when API handler throws', async () => {
280
- const handler = vi.fn(() => { throw new Error('oops') })
281
- const { server, getMiddleware } = makeServer()
282
- server.ssrLoadModule.mockImplementation(async (path: string) => {
283
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
284
- return { apiRoutes: [{ path: '/api/boom', handlers: { get: handler } }] }
285
- })
286
- configureCerDevServer(server as any, makeConfig())
287
- const res = createRes()
288
- await getMiddleware()(createReq({ url: '/api/boom' }), res, vi.fn())
289
- expect(res.statusCode).toBe(500)
290
- expect(res.end).toHaveBeenCalled()
291
- })
292
- })
293
-
294
- // ─── Augmented response helpers ───────────────────────────────────────────────
295
-
296
- describe('configureCerDevServer — augmented response helpers', () => {
297
- function makeServerWithRoute(handler: Function) {
298
- const { server, getMiddleware } = makeServer()
299
- server.ssrLoadModule.mockImplementation(async (path: string) => {
300
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
301
- return { apiRoutes: [{ path: '/api/test', handlers: { get: handler } }] }
302
- })
303
- configureCerDevServer(server as any, makeConfig())
304
- return getMiddleware()
305
- }
306
-
307
- it('res.json() serializes data and sets Content-Type', async () => {
308
- const res = createRes()
309
- const middleware = makeServerWithRoute((req: any, r: any) => r.json({ ok: true }))
310
- await middleware(createReq({ url: '/api/test' }), res, vi.fn())
311
- expect(res.setHeader).toHaveBeenCalledWith('Content-Type', 'application/json')
312
- expect(res.end).toHaveBeenCalledWith('{"ok":true}')
313
- })
314
-
315
- it('res.status() sets statusCode and is chainable', async () => {
316
- const res = createRes()
317
- const middleware = makeServerWithRoute((req: any, r: any) => r.status(404).end('Not Found'))
318
- await middleware(createReq({ url: '/api/test' }), res, vi.fn())
319
- expect(res.statusCode).toBe(404)
320
- expect(res.end).toHaveBeenCalledWith('Not Found')
321
- })
322
- })
323
-
324
- // ─── POST body parsing ────────────────────────────────────────────────────────
325
-
326
- describe('configureCerDevServer — POST body parsing', () => {
327
- it('parses JSON body for POST request', async () => {
328
- let capturedBody: unknown
329
- const { server, getMiddleware } = makeServer()
330
- server.ssrLoadModule.mockImplementation(async (path: string) => {
331
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
332
- return {
333
- apiRoutes: [{
334
- path: '/api/items',
335
- handlers: { post: (req: any, res: any) => { capturedBody = req.body; res.end('ok') } },
336
- }],
337
- }
338
- })
339
- configureCerDevServer(server as any, makeConfig())
340
- const req = createReq({
341
- url: '/api/items',
342
- method: 'POST',
343
- headers: { 'content-type': 'application/json' },
344
- body: JSON.stringify({ name: 'test' }),
345
- })
346
- await getMiddleware()(req, createRes(), vi.fn())
347
- expect(capturedBody).toEqual({ name: 'test' })
348
- })
349
-
350
- it('returns raw Buffer body for POST with non-JSON content-type', async () => {
351
- let capturedBody: unknown
352
- const { server, getMiddleware } = makeServer()
353
- server.ssrLoadModule.mockImplementation(async (path: string) => {
354
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
355
- return {
356
- apiRoutes: [{
357
- path: '/api/upload',
358
- handlers: { post: (req: any, res: any) => { capturedBody = req.body; res.end('ok') } },
359
- }],
360
- }
361
- })
362
- configureCerDevServer(server as any, makeConfig())
363
- const req = createReq({
364
- url: '/api/upload',
365
- method: 'POST',
366
- headers: { 'content-type': 'text/plain' },
367
- body: 'raw text',
368
- })
369
- await getMiddleware()(req, createRes(), vi.fn())
370
- expect(Buffer.isBuffer(capturedBody)).toBe(true)
371
- })
372
-
373
- it('attaches undefined body for GET requests', async () => {
374
- let capturedBody: unknown = 'sentinel'
375
- const { server, getMiddleware } = makeServer()
376
- server.ssrLoadModule.mockImplementation(async (path: string) => {
377
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
378
- return {
379
- apiRoutes: [{
380
- path: '/api/items',
381
- handlers: { get: (req: any, res: any) => { capturedBody = req.body; res.end('ok') } },
382
- }],
383
- }
384
- })
385
- configureCerDevServer(server as any, makeConfig())
386
- await getMiddleware()(createReq({ url: '/api/items' }), createRes(), vi.fn())
387
- expect(capturedBody).toBeUndefined()
388
- })
389
- })
390
-
391
- // ─── Server middleware execution ──────────────────────────────────────────────
392
-
393
- describe('configureCerDevServer — server middleware', () => {
394
- it('runs server middleware before API routes', async () => {
395
- const callOrder: string[] = []
396
- const smHandler = vi.fn((_req: any, _res: any, next: () => void) => {
397
- callOrder.push('sm')
398
- next()
399
- })
400
- const apiHandler = vi.fn((req: any, res: any) => { callOrder.push('api'); res.end('ok') })
401
- const { server, getMiddleware } = makeServer()
402
- server.ssrLoadModule.mockImplementation(async (path: string) => {
403
- if (path.includes('server-middleware')) return { serverMiddleware: [{ name: 'test', handler: smHandler }] }
404
- return { apiRoutes: [{ path: '/api/health', handlers: { get: apiHandler } }] }
405
- })
406
- configureCerDevServer(server as any, makeConfig())
407
- await getMiddleware()(createReq({ url: '/api/health' }), createRes(), vi.fn())
408
- expect(callOrder).toEqual(['sm', 'api'])
409
- })
410
-
411
- it('stops processing when server middleware does not call next()', async () => {
412
- const smHandler = vi.fn((_req: any, res: any) => { res.end('blocked') }) // no next() call
413
- const apiHandler = vi.fn()
414
- const { server, getMiddleware } = makeServer()
415
- server.ssrLoadModule.mockImplementation(async (path: string) => {
416
- if (path.includes('server-middleware')) return { serverMiddleware: [{ name: 'block', handler: smHandler }] }
417
- return { apiRoutes: [{ path: '/api/health', handlers: { get: apiHandler } }] }
418
- })
419
- configureCerDevServer(server as any, makeConfig())
420
- await getMiddleware()(createReq({ url: '/api/health' }), createRes(), vi.fn())
421
- expect(apiHandler).not.toHaveBeenCalled()
422
- })
423
-
424
- it('sends 500 and stops chain when middleware calls next(err)', async () => {
425
- const smHandler = vi.fn((_req: any, _res: any, next: (err?: unknown) => void) => {
426
- next(new Error('auth failed'))
427
- })
428
- const apiHandler = vi.fn()
429
- const { server, getMiddleware } = makeServer()
430
- server.ssrLoadModule.mockImplementation(async (path: string) => {
431
- if (path.includes('server-middleware')) return { serverMiddleware: [{ name: 'err', handler: smHandler }] }
432
- return { apiRoutes: [{ path: '/api/health', handlers: { get: apiHandler } }] }
433
- })
434
- configureCerDevServer(server as any, makeConfig())
435
- const res = createRes()
436
- await getMiddleware()(createReq({ url: '/api/health' }), res, vi.fn())
437
- expect(res.statusCode).toBe(500)
438
- expect(res.end).toHaveBeenCalledWith('Internal Server Error')
439
- expect(apiHandler).not.toHaveBeenCalled()
440
- })
441
-
442
- it('sends 500 and stops chain when middleware throws', async () => {
443
- const smHandler = vi.fn(() => { throw new Error('unexpected') })
444
- const apiHandler = vi.fn()
445
- const { server, getMiddleware } = makeServer()
446
- server.ssrLoadModule.mockImplementation(async (path: string) => {
447
- if (path.includes('server-middleware')) return { serverMiddleware: [{ name: 'throw', handler: smHandler }] }
448
- return { apiRoutes: [{ path: '/api/health', handlers: { get: apiHandler } }] }
449
- })
450
- configureCerDevServer(server as any, makeConfig())
451
- const res = createRes()
452
- await getMiddleware()(createReq({ url: '/api/health' }), res, vi.fn())
453
- expect(res.statusCode).toBe(500)
454
- expect(apiHandler).not.toHaveBeenCalled()
455
- })
456
-
457
- it('handles async middleware that returns a Promise and calls next() after awaiting', async () => {
458
- const callOrder: string[] = []
459
- const smHandler = vi.fn((_req: any, _res: any, next: () => void) =>
460
- new Promise<void>((resolve) => {
461
- // Simulate async work before calling next
462
- setImmediate(() => { callOrder.push('sm'); next(); resolve() })
463
- }),
464
- )
465
- const apiHandler = vi.fn((_req: any, res: any) => { callOrder.push('api'); res.end('ok') })
466
- const { server, getMiddleware } = makeServer()
467
- server.ssrLoadModule.mockImplementation(async (path: string) => {
468
- if (path.includes('server-middleware')) return { serverMiddleware: [{ name: 'async', handler: smHandler }] }
469
- return { apiRoutes: [{ path: '/api/health', handlers: { get: apiHandler } }] }
470
- })
471
- configureCerDevServer(server as any, makeConfig())
472
- await getMiddleware()(createReq({ url: '/api/health' }), createRes(), vi.fn())
473
- expect(callOrder).toEqual(['sm', 'api'])
474
- })
475
-
476
- it('handles async middleware that rejects (returns a rejected Promise)', async () => {
477
- const smHandler = vi.fn(() => Promise.reject(new Error('async failure')))
478
- const apiHandler = vi.fn()
479
- const { server, getMiddleware } = makeServer()
480
- server.ssrLoadModule.mockImplementation(async (path: string) => {
481
- if (path.includes('server-middleware')) return { serverMiddleware: [{ name: 'async-throw', handler: smHandler }] }
482
- return { apiRoutes: [{ path: '/api/health', handlers: { get: apiHandler } }] }
483
- })
484
- configureCerDevServer(server as any, makeConfig())
485
- const res = createRes()
486
- await getMiddleware()(createReq({ url: '/api/health' }), res, vi.fn())
487
- expect(res.statusCode).toBe(500)
488
- expect(apiHandler).not.toHaveBeenCalled()
489
- })
490
- })
491
-
492
- // ─── SSR mode ─────────────────────────────────────────────────────────────────
493
-
494
- describe('configureCerDevServer — SSR mode', () => {
495
- it('invokes SSR handler for HTML requests in ssr mode', async () => {
496
- const ssrHandler = vi.fn(async (req: any, res: any) => res.end('<html>SSR</html>'))
497
- const { server, getMiddleware } = makeServer()
498
- server.ssrLoadModule.mockImplementation(async (path: string) => {
499
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
500
- if (path.includes('server-api')) return { apiRoutes: [] }
501
- // SSR entry module
502
- return { handler: ssrHandler }
503
- })
504
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
505
- const res = createRes()
506
- await getMiddleware()(
507
- createReq({ url: '/', headers: { accept: 'text/html' } }),
508
- res,
509
- vi.fn(),
510
- )
511
- expect(ssrHandler).toHaveBeenCalled()
512
- })
513
-
514
- it('invokes SSR handler for HTML requests in ssg mode', async () => {
515
- const ssrHandler = vi.fn(async (req: any, res: any) => res.end('<html>SSG-DEV</html>'))
516
- const { server, getMiddleware } = makeServer()
517
- server.ssrLoadModule.mockImplementation(async (path: string) => {
518
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
519
- if (path.includes('server-api')) return { apiRoutes: [] }
520
- return { handler: ssrHandler }
521
- })
522
- configureCerDevServer(server as any, makeConfig({ mode: 'ssg' }))
523
- const res = createRes()
524
- await getMiddleware()(
525
- createReq({ url: '/', headers: { accept: 'text/html' } }),
526
- res,
527
- vi.fn(),
528
- )
529
- expect(ssrHandler).toHaveBeenCalled()
530
- })
531
-
532
- it('does not invoke SSR handler in spa mode', async () => {
533
- const ssrHandler = vi.fn()
534
- const { server, getMiddleware } = makeServer()
535
- server.ssrLoadModule.mockImplementation(async (path: string) => {
536
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
537
- if (path.includes('server-api')) return { apiRoutes: [] }
538
- return { handler: ssrHandler }
539
- })
540
- server.transformIndexHtml.mockResolvedValue('<html>spa shell</html>')
541
- vi.mocked(existsSync).mockReturnValue(true)
542
- vi.mocked(readFileSync).mockReturnValue('<html>source shell</html>' as never)
543
- configureCerDevServer(
544
- server as any,
545
- makeConfig({ mode: 'spa' }),
546
- )
547
- const next = vi.fn()
548
- const res = createRes()
549
- await getMiddleware()(
550
- createReq({ url: '/', headers: { accept: 'text/html' } }),
551
- res,
552
- next,
553
- )
554
- expect(ssrHandler).not.toHaveBeenCalled()
555
- expect(res.end).toHaveBeenCalledWith('<html>spa shell</html>')
556
- expect(next).not.toHaveBeenCalled()
557
- })
558
-
559
- it('does not invoke SSR handler for non-HTML requests (e.g. assets)', async () => {
560
- const ssrHandler = vi.fn()
561
- const { server, getMiddleware } = makeServer()
562
- server.ssrLoadModule.mockImplementation(async (path: string) => {
563
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
564
- if (path.includes('server-api')) return { apiRoutes: [] }
565
- return { handler: ssrHandler }
566
- })
567
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
568
- const next = vi.fn()
569
- await getMiddleware()(
570
- createReq({ url: '/assets/main.js', headers: { accept: 'application/javascript' } }),
571
- createRes(),
572
- next,
573
- )
574
- expect(ssrHandler).not.toHaveBeenCalled()
575
- expect(next).toHaveBeenCalled()
576
- })
577
-
578
- it('does not invoke SSR handler for non-HTML requests in ssg mode', async () => {
579
- const ssrHandler = vi.fn()
580
- const { server, getMiddleware } = makeServer()
581
- server.ssrLoadModule.mockImplementation(async (path: string) => {
582
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
583
- if (path.includes('server-api')) return { apiRoutes: [] }
584
- return { handler: ssrHandler }
585
- })
586
- configureCerDevServer(server as any, makeConfig({ mode: 'ssg' }))
587
- const next = vi.fn()
588
- await getMiddleware()(
589
- createReq({ url: '/assets/main.js', headers: { accept: 'application/javascript' } }),
590
- createRes(),
591
- next,
592
- )
593
- expect(ssrHandler).not.toHaveBeenCalled()
594
- expect(next).toHaveBeenCalled()
595
- })
596
-
597
- it('returns 500 and error text when SSR handler throws', async () => {
598
- const { server, getMiddleware } = makeServer()
599
- server.ssrLoadModule.mockImplementation(async (path: string) => {
600
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
601
- if (path.includes('server-api')) return { apiRoutes: [] }
602
- return { handler: async () => { throw new Error('render failed') } }
603
- })
604
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
605
- const res = createRes()
606
- await getMiddleware()(createReq({ url: '/', headers: { accept: 'text/html' } }), res, vi.fn())
607
- expect(res.statusCode).toBe(500)
608
- })
609
-
610
- it('calls next() for non-HTML in ssr mode without API match', async () => {
611
- const { server, getMiddleware } = makeServer()
612
- server.ssrLoadModule.mockImplementation(async (path: string) => {
613
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
614
- return { apiRoutes: [] }
615
- })
616
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
617
- const next = vi.fn()
618
- await getMiddleware()(
619
- createReq({ url: '/favicon.ico', headers: { accept: 'image/x-icon' } }),
620
- createRes(),
621
- next,
622
- )
623
- expect(next).toHaveBeenCalled()
624
- })
625
-
626
- it('uses renderFn fallback when SSR entry exports render instead of handler', async () => {
627
- const renderFn = vi.fn().mockResolvedValue({ html: '<div>rendered</div>' })
628
- const { server, getMiddleware } = makeServer()
629
- ;(server as any).transformIndexHtml = vi.fn().mockResolvedValue(
630
- '<!DOCTYPE html><html><body><div id="app"></div></body></html>',
631
- )
632
- server.ssrLoadModule.mockImplementation(async (path: string) => {
633
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
634
- if (path.includes('server-api')) return { apiRoutes: [] }
635
- return { render: renderFn }
636
- })
637
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
638
- const res = createRes()
639
- await getMiddleware()(createReq({ url: '/', headers: { accept: 'text/html' } }), res, vi.fn())
640
- expect(renderFn).toHaveBeenCalled()
641
- expect(res.end).toHaveBeenCalledWith(expect.stringContaining('<div>rendered</div>'))
642
- })
643
-
644
- it('accepts html request when url is / even without text/html accept header', async () => {
645
- const ssrHandler = vi.fn(async (req: any, res: any) => res.end('<html>home</html>'))
646
- const { server, getMiddleware } = makeServer()
647
- server.ssrLoadModule.mockImplementation(async (path: string) => {
648
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
649
- if (path.includes('server-api')) return { apiRoutes: [] }
650
- return { handler: ssrHandler }
651
- })
652
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
653
- // url='/' triggers acceptsHtml without needing accept header
654
- await getMiddleware()(createReq({ url: '/' }), createRes(), vi.fn())
655
- expect(ssrHandler).toHaveBeenCalled()
656
- })
657
- })
658
-
659
- // ─── Per-route render mode (SSR/SSG mode) ────────────────────────────────────
660
-
661
- describe("configureCerDevServer — per-route render mode in SSR", () => {
662
- /** Build a server where virtual:cer-routes returns the given routes array. */
663
- function makeServerWithRoutes(
664
- pageRoutes: Array<{ path: string; meta?: Record<string, unknown> }>,
665
- ssrHandler = vi.fn(async (_req: any, res: any) => res.end('<html>SSR</html>')),
666
- mode: 'ssr' | 'ssg' = 'ssr',
667
- ) {
668
- const { server, getMiddleware } = makeServer()
669
- server.ssrLoadModule.mockImplementation(async (path: string) => {
670
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
671
- if (path.includes('server-api') || path.includes('cer-server-api')) return { apiRoutes: [] }
672
- if (path.includes('cer-routes')) return { default: pageRoutes }
673
- return { handler: ssrHandler }
674
- })
675
- configureCerDevServer(server as any, makeConfig({ mode }))
676
- return { middleware: getMiddleware(), ssrHandler }
677
- }
678
-
679
- it("calls next() instead of SSR for a route with render: 'spa'", async () => {
680
- const { middleware, ssrHandler } = makeServerWithRoutes([
681
- { path: '/spa-page', meta: { render: 'spa' } },
682
- ])
683
- const next = vi.fn()
684
- await middleware(
685
- createReq({ url: '/spa-page', headers: { accept: 'text/html' } }),
686
- createRes(),
687
- next,
688
- )
689
- expect(next).toHaveBeenCalled()
690
- expect(ssrHandler).not.toHaveBeenCalled()
691
- })
692
-
693
- it("calls next() instead of SSR for a route with render: 'spa' in ssg mode", async () => {
694
- const { middleware, ssrHandler } = makeServerWithRoutes(
695
- [{ path: '/spa-page', meta: { render: 'spa' } }],
696
- vi.fn(async (_req: any, res: any) => res.end('<html>SSG</html>')),
697
- 'ssg',
698
- )
699
- const next = vi.fn()
700
- await middleware(
701
- createReq({ url: '/spa-page', headers: { accept: 'text/html' } }),
702
- createRes(),
703
- next,
704
- )
705
- expect(next).toHaveBeenCalled()
706
- expect(ssrHandler).not.toHaveBeenCalled()
707
- })
708
-
709
- it("proceeds to SSR for a route with render: 'server'", async () => {
710
- const { middleware, ssrHandler } = makeServerWithRoutes([
711
- { path: '/server-page', meta: { render: 'server' } },
712
- ])
713
- await middleware(
714
- createReq({ url: '/server-page', headers: { accept: 'text/html' } }),
715
- createRes(),
716
- vi.fn(),
717
- )
718
- expect(ssrHandler).toHaveBeenCalled()
719
- })
720
-
721
- it("proceeds to SSR for a route with no render meta", async () => {
722
- const { middleware, ssrHandler } = makeServerWithRoutes([
723
- { path: '/normal-page' },
724
- ])
725
- await middleware(
726
- createReq({ url: '/normal-page', headers: { accept: 'text/html' } }),
727
- createRes(),
728
- vi.fn(),
729
- )
730
- expect(ssrHandler).toHaveBeenCalled()
731
- })
732
-
733
- it("falls back to SSR when routes module throws during render mode check", async () => {
734
- const ssrHandler = vi.fn(async (_req: any, res: any) => res.end('<html>SSR</html>'))
735
- const { server, getMiddleware } = makeServer()
736
- server.ssrLoadModule.mockImplementation(async (path: string) => {
737
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
738
- if (path.includes('server-api') || path.includes('cer-server-api')) return { apiRoutes: [] }
739
- if (path.includes('cer-routes')) throw new Error('module not ready')
740
- return { handler: ssrHandler }
741
- })
742
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
743
- await getMiddleware()(
744
- createReq({ url: '/some-page', headers: { accept: 'text/html' } }),
745
- createRes(),
746
- vi.fn(),
747
- )
748
- // Despite the routes module throwing, SSR should still run
749
- expect(ssrHandler).toHaveBeenCalled()
750
- })
751
-
752
- it("does not load routes module in SPA mode (no render mode check needed)", async () => {
753
- const { server, getMiddleware } = makeServer()
754
- configureCerDevServer(server as any, makeConfig({ mode: 'spa' }))
755
- await getMiddleware()(
756
- createReq({ url: '/about', headers: { accept: 'text/html' } }),
757
- createRes(),
758
- vi.fn(),
759
- )
760
- const routesCalls = (server.ssrLoadModule as any).mock.calls.filter(
761
- ([p]: [string]) => p.includes('cer-routes'),
762
- )
763
- expect(routesCalls).toHaveLength(0)
764
- })
765
-
766
- it("loads routes module in ssg mode to check per-route render mode", async () => {
767
- const ssrHandler = vi.fn(async (_req: any, res: any) => res.end('<html>SSG</html>'))
768
- const { server, getMiddleware } = makeServer()
769
- server.ssrLoadModule.mockImplementation(async (path: string) => {
770
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
771
- if (path.includes('server-api') || path.includes('cer-server-api')) return { apiRoutes: [] }
772
- if (path.includes('cer-routes')) return { default: [] }
773
- return { handler: ssrHandler }
774
- })
775
- configureCerDevServer(server as any, makeConfig({ mode: 'ssg' }))
776
- await getMiddleware()(
777
- createReq({ url: '/about', headers: { accept: 'text/html' } }),
778
- createRes(),
779
- vi.fn(),
780
- )
781
- const routesCalls = (server.ssrLoadModule as any).mock.calls.filter(
782
- ([p]: [string]) => p.includes('cer-routes'),
783
- )
784
- expect(routesCalls.length).toBeGreaterThan(0)
785
- })
786
- })
787
-
788
- // ─── parseBody edge cases ─────────────────────────────────────────────────────
789
-
790
- describe('configureCerDevServer — malformed JSON body', () => {
791
- it('returns undefined when POST body is malformed JSON', async () => {
792
- let capturedBody: unknown = 'sentinel'
793
- const { server, getMiddleware } = makeServer()
794
- server.ssrLoadModule.mockImplementation(async (path: string) => {
795
- if (path.includes('server-middleware')) return { serverMiddleware: [] }
796
- return {
797
- apiRoutes: [{
798
- path: '/api/items',
799
- handlers: { post: (req: any, res: any) => { capturedBody = req.body; res.end('ok') } },
800
- }],
801
- }
802
- })
803
- configureCerDevServer(server as any, makeConfig())
804
- const req = createReq({
805
- url: '/api/items',
806
- method: 'POST',
807
- headers: { 'content-type': 'application/json' },
808
- body: '{ invalid json }',
809
- })
810
- await getMiddleware()(req, createRes(), vi.fn())
811
- expect(capturedBody).toBeUndefined()
812
- })
813
- })