@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,326 +0,0 @@
1
- /**
2
- * Netlify adapter.
3
- *
4
- * Transforms the cer-app `dist/` output into the files needed to deploy on
5
- * Netlify.
6
- *
7
- * SSR mode:
8
- * - Writes a `netlify/functions/ssr.mjs` bridge that converts Netlify
9
- * Functions v2 Web API (Request/Response) to the Node.js-style handler
10
- * used by the cer-app server bundle. Handles /api/* routing inline.
11
- * - Creates `.netlify/publish/` with content-hashed assets only (no HTML),
12
- * so Netlify's CDN serves static files and everything else falls through
13
- * to the SSR function.
14
- * - Writes `netlify.toml` with the publish directory and catch-all redirect.
15
- *
16
- * SPA/SSG mode:
17
- * - Writes `netlify.toml` that points to the correct publish directory and
18
- * adds a SPA fallback redirect (/* → /index.html).
19
- *
20
- * Responses are streamed via the Web Streams TransformStream API — chunks written
21
- * by the SSR handler are forwarded to the client as they arrive rather than being
22
- * buffered until the full page is rendered.
23
- * All other behaviour (cookies, headers, API routes, ISR) is fully supported.
24
- */
25
-
26
- import { join } from 'pathe'
27
- import {
28
- existsSync,
29
- mkdirSync,
30
- writeFileSync,
31
- copyFileSync,
32
- cpSync,
33
- readdirSync,
34
- statSync,
35
- rmSync,
36
- } from 'node:fs'
37
-
38
- // ─── SSR function bridge template ────────────────────────────────────────────
39
-
40
- /**
41
- * Netlify Functions v2 bridge.
42
- *
43
- * The function:
44
- * 1. Converts the incoming Web API Request to a mock Node.js IncomingMessage.
45
- * 2. Creates a streaming mock ServerResponse backed by a TransformStream.
46
- * Chunks written via res.write() are enqueued into the stream immediately;
47
- * res.end() closes the stream and resolves the Promise with a streaming
48
- * Web API Response. The platform receives the Response body as a ReadableStream
49
- * so bytes flow to the client as they are written — no full-page buffering.
50
- * 3. Routes /api/* to the API handlers, everything else to the SSR handler.
51
- *
52
- * The server bundle is imported using a path relative to the function file
53
- * location: netlify/functions/ssr.mjs → ../../dist/server/server.js.
54
- */
55
- const NETLIFY_SSR_BRIDGE = `\
56
- // Auto-generated by @jasonshimmy/vite-plugin-cer-app — do not edit.
57
- import { Readable } from 'node:stream'
58
- import { isrHandler, apiRoutes, runServerMiddleware, runWithRequestContext } from '../../dist/server/server.js'
59
-
60
- function matchApiPattern(pattern, urlPath) {
61
- const pp = pattern.split('/')
62
- const up = urlPath.split('/')
63
- if (pp.length !== up.length) return null
64
- const params = {}
65
- for (let i = 0; i < pp.length; i++) {
66
- if (pp[i].startsWith(':')) params[pp[i].slice(1)] = decodeURIComponent(up[i])
67
- else if (pp[i] !== up[i]) return null
68
- }
69
- return params
70
- }
71
-
72
- function parseQuery(search) {
73
- if (!search) return {}
74
- const qs = search.startsWith('?') ? search.slice(1) : search
75
- const result = {}
76
- for (const part of qs.split('&')) {
77
- if (!part) continue
78
- const eqIdx = part.indexOf('=')
79
- if (eqIdx === -1) result[decodeURIComponent(part)] = ''
80
- else result[decodeURIComponent(part.slice(0, eqIdx))] = decodeURIComponent(part.slice(eqIdx + 1))
81
- }
82
- return result
83
- }
84
-
85
- async function toNodeRequest(webReq) {
86
- const url = new URL(webReq.url)
87
- const hasBody = webReq.body !== null
88
- && webReq.method !== 'GET'
89
- && webReq.method !== 'HEAD'
90
- const body = hasBody ? Buffer.from(await webReq.arrayBuffer()) : null
91
- const req = Object.assign(
92
- body ? Readable.from([body]) : Readable.from([]),
93
- {
94
- url: url.pathname + url.search,
95
- method: webReq.method,
96
- headers: Object.fromEntries(webReq.headers.entries()),
97
- },
98
- )
99
- req.query = parseQuery(url.search)
100
- if (body !== null) {
101
- const ct = webReq.headers.get('content-type') ?? ''
102
- if (ct.includes('application/json')) {
103
- try { req.body = JSON.parse(body.toString('utf-8')) } catch { req.body = undefined }
104
- } else {
105
- req.body = body
106
- }
107
- }
108
- return req
109
- }
110
-
111
- function createNodeResponse() {
112
- const { readable, writable } = new TransformStream()
113
- const writer = writable.getWriter()
114
- const encoder = new TextEncoder()
115
- const headers = {}
116
- let _resolve
117
- let _ended = false
118
- const promise = new Promise((res) => { _resolve = res })
119
- const res = {
120
- statusCode: 200,
121
- setHeader(name, value) { headers[name.toLowerCase()] = String(value) },
122
- getHeader(name) { return headers[name.toLowerCase()] },
123
- removeHeader(name) { delete headers[name.toLowerCase()] },
124
- write(chunk) {
125
- if (_ended) return
126
- void writer.write(typeof chunk === 'string' ? encoder.encode(chunk) : chunk).catch(() => {})
127
- },
128
- end(chunk) {
129
- if (_ended) return
130
- _ended = true
131
- if (chunk) void writer.write(typeof chunk === 'string' ? encoder.encode(chunk) : chunk).catch(() => {})
132
- void writer.close().catch(() => {})
133
- _resolve(new Response(readable, { status: res.statusCode, headers }))
134
- },
135
- // Helpers expected by API route handlers.
136
- json(data) {
137
- this.setHeader('Content-Type', 'application/json; charset=utf-8')
138
- this.end(JSON.stringify(data))
139
- },
140
- status(code) { this.statusCode = code; return this },
141
- get writableEnded() { return _ended },
142
- }
143
- return { res, promise }
144
- }
145
-
146
- export default async (webReq) => {
147
- const url = new URL(webReq.url)
148
- const urlPath = url.pathname
149
- const method = webReq.method ?? 'GET'
150
-
151
- // Convert to Node.js-style req/res upfront so server middleware can run.
152
- const nodeReq = await toNodeRequest(webReq)
153
- const { res, promise } = createNodeResponse()
154
-
155
- // Run server middleware chain (auth, logging, CORS, etc.).
156
- if (!(await runServerMiddleware(nodeReq, res))) return promise
157
-
158
- // Route /api/* requests to the API handlers exported by the server bundle.
159
- if (urlPath.startsWith('/api/')) {
160
- for (const route of (apiRoutes ?? [])) {
161
- const params = matchApiPattern(route.path, urlPath)
162
- if (params !== null) {
163
- nodeReq.params = params
164
- const fn = route.handlers[method.toLowerCase()]
165
- ?? route.handlers[method.toUpperCase()]
166
- ?? route.handlers['default']
167
- if (typeof fn === 'function') {
168
- // Wrap in request context so useCookie / useSession work in API handlers.
169
- runWithRequestContext(nodeReq, res, () => Promise.resolve(fn(nodeReq, res))).catch(() => {
170
- if (!res.writableEnded) {
171
- res.statusCode = 500
172
- res.end(JSON.stringify({ error: 'Internal Server Error' }))
173
- }
174
- })
175
- return promise
176
- }
177
- }
178
- }
179
- return new Response('Not Found', { status: 404 })
180
- }
181
-
182
- // All other requests: SSR (with ISR for routes that declare meta.ssg.revalidate).
183
- isrHandler(nodeReq, res).catch(() => {
184
- if (!res.writableEnded) {
185
- res.statusCode = 500
186
- res.end('Internal Server Error')
187
- }
188
- })
189
- return promise
190
- }
191
- `
192
-
193
- // ─── Public API ───────────────────────────────────────────────────────────────
194
-
195
- export async function runNetlifyAdapter(root: string): Promise<void> {
196
- const distDir = join(root, 'dist')
197
- if (!existsSync(distDir)) {
198
- throw new Error(
199
- `[cer-app] No dist/ directory found at ${distDir}. Run 'cer-app build' first.`,
200
- )
201
- }
202
-
203
- const serverBundle = join(distDir, 'server/server.js')
204
- const ssgManifest = join(distDir, 'ssg-manifest.json')
205
- const isSSR = existsSync(serverBundle) && !existsSync(ssgManifest)
206
-
207
- if (isSSR) {
208
- _buildSSR(root, distDir)
209
- } else {
210
- _buildStatic(root, distDir)
211
- }
212
-
213
- console.log('[cer-app] Netlify adapter complete.')
214
- if (isSSR) {
215
- console.log(' Function: netlify/functions/ssr.mjs')
216
- console.log(' Static: .netlify/publish/')
217
- console.log(' Deploy with: netlify deploy')
218
- } else {
219
- console.log(' Deploy with: netlify deploy')
220
- }
221
- }
222
-
223
- // ─── SSR output ───────────────────────────────────────────────────────────────
224
-
225
- function _buildSSR(root: string, distDir: string): void {
226
- // Write the SSR bridge function.
227
- const functionsDir = join(root, 'netlify/functions')
228
- mkdirSync(functionsDir, { recursive: true })
229
- writeFileSync(join(functionsDir, 'ssr.mjs'), NETLIFY_SSR_BRIDGE)
230
-
231
- // Build the static publish directory with assets only (no HTML).
232
- // Netlify serves files in the publish directory as static responses first.
233
- // By omitting index.html we ensure all HTML routes fall through to the
234
- // SSR function instead of being served as stale static files.
235
- const publishDir = join(root, '.netlify/publish')
236
- if (existsSync(publishDir)) {
237
- rmSync(publishDir, { recursive: true, force: true })
238
- }
239
- _copyClientAssets(join(distDir, 'client'), publishDir)
240
-
241
- // netlify.toml: publish directory + cache headers + catch-all to SSR function.
242
- writeFileSync(
243
- join(root, 'netlify.toml'),
244
- `# Auto-generated by @jasonshimmy/vite-plugin-cer-app — do not edit.
245
- [build]
246
- publish = ".netlify/publish"
247
-
248
- [[headers]]
249
- for = "/assets/*"
250
- [headers.values]
251
- Cache-Control = "public, max-age=31536000, immutable"
252
-
253
- [[redirects]]
254
- from = "/*"
255
- to = "/.netlify/functions/ssr"
256
- status = 200
257
- `,
258
- )
259
- }
260
-
261
- // ─── Static (SPA / SSG) output ────────────────────────────────────────────────
262
-
263
- function _buildStatic(root: string, distDir: string): void {
264
- const clientDir = join(distDir, 'client')
265
- let publishDir: string
266
-
267
- if (existsSync(clientDir)) {
268
- // SSG: HTML in dist/, assets in dist/client/. Merge into .netlify/publish/.
269
- publishDir = join(root, '.netlify/publish')
270
- if (existsSync(publishDir)) {
271
- rmSync(publishDir, { recursive: true, force: true })
272
- }
273
- // Copy pre-rendered HTML from dist/ (skip client/ sub-dir and manifest).
274
- cpSync(distDir, publishDir, {
275
- recursive: true,
276
- filter: (src) =>
277
- src !== clientDir && !src.endsWith('ssg-manifest.json'),
278
- })
279
- // Copy assets from dist/client/ into the publish root.
280
- _copyClientAssets(clientDir, publishDir)
281
- } else {
282
- // SPA: everything is in dist/ at correct URL paths already.
283
- publishDir = 'dist'
284
- }
285
-
286
- // netlify.toml: SPA fallback redirect.
287
- writeFileSync(
288
- join(root, 'netlify.toml'),
289
- `# Auto-generated by @jasonshimmy/vite-plugin-cer-app — do not edit.
290
- [build]
291
- publish = "${publishDir.startsWith(root) ? publishDir.slice(root.length + 1) : publishDir}"
292
-
293
- [[headers]]
294
- for = "/assets/*"
295
- [headers.values]
296
- Cache-Control = "public, max-age=31536000, immutable"
297
-
298
- [[redirects]]
299
- from = "/*"
300
- to = "/index.html"
301
- status = 200
302
- `,
303
- )
304
- }
305
-
306
- // ─── Helpers ──────────────────────────────────────────────────────────────────
307
-
308
- /**
309
- * Copies content-hashed assets and other public files (favicon, robots.txt, …)
310
- * from the client dist directory into `destDir`.
311
- * Skips index.html so it is never served as a static file in SSR mode.
312
- */
313
- function _copyClientAssets(clientDir: string, destDir: string): void {
314
- if (!existsSync(clientDir)) return
315
- mkdirSync(destDir, { recursive: true })
316
- for (const entry of readdirSync(clientDir)) {
317
- if (entry === 'index.html') continue
318
- const src = join(clientDir, entry)
319
- const dest = join(destDir, entry)
320
- if (statSync(src).isDirectory()) {
321
- cpSync(src, dest, { recursive: true })
322
- } else {
323
- copyFileSync(src, dest)
324
- }
325
- }
326
- }
@@ -1,311 +0,0 @@
1
- /**
2
- * Vercel Build Output API v3 adapter.
3
- *
4
- * Transforms the cer-app `dist/` output into a `.vercel/output/` directory
5
- * that can be deployed with `vercel deploy --prebuilt`.
6
- *
7
- * SSR mode:
8
- * - Copies dist/server/server.js into a Vercel Serverless Function
9
- * - Copies dist/client/index.html alongside the server bundle so the
10
- * `../client/index.html` relative path in the bundle resolves correctly
11
- * - Copies content-hashed assets to .vercel/output/static/ for CDN delivery
12
- * - Writes a launcher (index.js) that routes /api/* to the API handlers and
13
- * forwards all other requests to the SSR handler
14
- *
15
- * SPA/SSG mode:
16
- * - Copies all static files to .vercel/output/static/
17
- * - Writes a config.json with SPA fallback (serve index.html for unknown paths)
18
- */
19
-
20
- import { join } from 'pathe'
21
- import {
22
- existsSync,
23
- mkdirSync,
24
- writeFileSync,
25
- copyFileSync,
26
- cpSync,
27
- readdirSync,
28
- statSync,
29
- rmSync,
30
- } from 'node:fs'
31
-
32
- // ─── Launcher template ────────────────────────────────────────────────────────
33
-
34
- /**
35
- * Vercel serverless function entry point.
36
- * Handles /api/* routing and falls through to the SSR handler for all other
37
- * requests. Imported by Vercel's Node.js launcher via the handler export.
38
- *
39
- * The server bundle lives at ./server/server.js so its own ../client/index.html
40
- * reference resolves to ./client/index.html — both files are copied into the
41
- * function directory by the adapter.
42
- */
43
- const VERCEL_LAUNCHER = `\
44
- // Auto-generated by @jasonshimmy/vite-plugin-cer-app — do not edit.
45
- import { handler, isrHandler, apiRoutes, runServerMiddleware, runWithRequestContext } from './server/server.js'
46
-
47
- function matchApiPattern(pattern, urlPath) {
48
- const pp = pattern.split('/')
49
- const up = urlPath.split('/')
50
- if (pp.length !== up.length) return null
51
- const params = {}
52
- for (let i = 0; i < pp.length; i++) {
53
- if (pp[i].startsWith(':')) params[pp[i].slice(1)] = decodeURIComponent(up[i])
54
- else if (pp[i] !== up[i]) return null
55
- }
56
- return params
57
- }
58
-
59
- function parseQuery(url) {
60
- const qIndex = url.indexOf('?')
61
- if (qIndex === -1) return {}
62
- const qs = url.slice(qIndex + 1)
63
- const result = {}
64
- for (const part of qs.split('&')) {
65
- if (!part) continue
66
- const eqIdx = part.indexOf('=')
67
- if (eqIdx === -1) result[decodeURIComponent(part)] = ''
68
- else result[decodeURIComponent(part.slice(0, eqIdx))] = decodeURIComponent(part.slice(eqIdx + 1))
69
- }
70
- return result
71
- }
72
-
73
- async function readBody(req) {
74
- return new Promise((resolve, reject) => {
75
- const chunks = []
76
- req.on('data', chunk => chunks.push(Buffer.from(chunk)))
77
- req.on('end', () => resolve(Buffer.concat(chunks)))
78
- req.on('error', reject)
79
- })
80
- }
81
-
82
- async function parseBody(req) {
83
- const contentType = req.headers['content-type'] ?? ''
84
- const method = req.method?.toUpperCase() ?? 'GET'
85
- if (!['POST', 'PUT', 'PATCH'].includes(method)) return undefined
86
- const buf = await readBody(req)
87
- if (contentType.includes('application/json')) {
88
- try { return JSON.parse(buf.toString('utf-8')) } catch { return undefined }
89
- }
90
- return buf
91
- }
92
-
93
- export default async function cerAppHandler(req, res) {
94
- // Run server middleware chain (auth, logging, CORS, etc.).
95
- if (!(await runServerMiddleware(req, res))) return
96
-
97
- const urlPath = (req.url ?? '/').split('?')[0]
98
- const method = req.method ?? 'GET'
99
-
100
- // Route /api/* to API handlers exported by the server bundle.
101
- if (urlPath.startsWith('/api/')) {
102
- for (const route of (apiRoutes ?? [])) {
103
- const params = matchApiPattern(route.path, urlPath)
104
- if (params !== null) {
105
- req.params = params
106
- req.query = parseQuery(req.url ?? '/')
107
- req.body = await parseBody(req)
108
- res.json = function (data) {
109
- this.setHeader('Content-Type', 'application/json; charset=utf-8')
110
- this.end(JSON.stringify(data))
111
- }
112
- res.status = function (code) { this.statusCode = code; return this }
113
- const fn = route.handlers[method.toLowerCase()]
114
- ?? route.handlers[method.toUpperCase()]
115
- ?? route.handlers['default']
116
- if (typeof fn === 'function') {
117
- try {
118
- await runWithRequestContext(req, res, () => Promise.resolve(fn(req, res)))
119
- } catch {
120
- if (!res.writableEnded) {
121
- res.statusCode = 500
122
- res.setHeader('Content-Type', 'application/json')
123
- res.end(JSON.stringify({ error: 'Internal Server Error' }))
124
- }
125
- }
126
- return
127
- }
128
- }
129
- }
130
- res.statusCode = 404
131
- res.end('Not Found')
132
- return
133
- }
134
-
135
- // All other requests: SSR (with ISR for routes that declare meta.ssg.revalidate).
136
- await isrHandler(req, res)
137
- }
138
- `
139
-
140
- // ─── Public API ───────────────────────────────────────────────────────────────
141
-
142
- export async function runVercelAdapter(root: string): Promise<void> {
143
- const distDir = join(root, 'dist')
144
- if (!existsSync(distDir)) {
145
- throw new Error(
146
- `[cer-app] No dist/ directory found at ${distDir}. Run 'cer-app build' first.`,
147
- )
148
- }
149
-
150
- const serverBundle = join(distDir, 'server/server.js')
151
- const ssgManifest = join(distDir, 'ssg-manifest.json')
152
- const isSSR = existsSync(serverBundle) && !existsSync(ssgManifest)
153
-
154
- const outDir = join(root, '.vercel/output')
155
-
156
- // Clean existing Vercel output to avoid stale files.
157
- if (existsSync(outDir)) {
158
- rmSync(outDir, { recursive: true, force: true })
159
- }
160
- mkdirSync(outDir, { recursive: true })
161
-
162
- if (isSSR) {
163
- _buildSSR(distDir, outDir)
164
- } else {
165
- _buildStatic(distDir, outDir)
166
- }
167
-
168
- console.log('[cer-app] Vercel adapter complete — output written to .vercel/output/')
169
- console.log(' Deploy with: vercel deploy --prebuilt')
170
- }
171
-
172
- // ─── SSR output ───────────────────────────────────────────────────────────────
173
-
174
- function _buildSSR(distDir: string, outDir: string): void {
175
- const funcDir = join(outDir, 'functions/index.func')
176
-
177
- // Maintain the server/ → ../client/ relative layout so the server bundle's
178
- // built-in `../client/index.html` reference resolves correctly.
179
- mkdirSync(join(funcDir, 'server'), { recursive: true })
180
- mkdirSync(join(funcDir, 'client'), { recursive: true })
181
-
182
- // Server bundle.
183
- copyFileSync(join(distDir, 'server/server.js'), join(funcDir, 'server/server.js'))
184
-
185
- // Client HTML template (referenced as ../client/index.html from server.js).
186
- const clientHtml = join(distDir, 'client/index.html')
187
- if (existsSync(clientHtml)) {
188
- copyFileSync(clientHtml, join(funcDir, 'client/index.html'))
189
- }
190
-
191
- // Launcher: routes API requests + forwards the rest to the SSR handler.
192
- writeFileSync(join(funcDir, 'index.js'), VERCEL_LAUNCHER)
193
-
194
- // package.json: mark the function directory as ESM so Node.js treats .js
195
- // files as ES modules (the server bundle uses import.meta.url, etc.).
196
- writeFileSync(join(funcDir, 'package.json'), '{"type":"module"}\n')
197
-
198
- // Vercel function config.
199
- writeFileSync(
200
- join(funcDir, '.vc-config.json'),
201
- JSON.stringify(
202
- {
203
- runtime: 'nodejs20.x',
204
- handler: 'index.js',
205
- launcherType: 'Nodejs',
206
- },
207
- null,
208
- 2,
209
- ) + '\n',
210
- )
211
-
212
- // Static assets: content-hashed files served by the Vercel CDN.
213
- const staticDir = join(outDir, 'static')
214
- _copyClientAssets(join(distDir, 'client'), staticDir)
215
-
216
- // Vercel output routes config.
217
- writeFileSync(
218
- join(outDir, 'config.json'),
219
- JSON.stringify(
220
- {
221
- version: 3,
222
- routes: [
223
- // Apply immutable cache headers to content-hashed assets then continue
224
- // (so the filesystem handler can still serve them as static files).
225
- {
226
- src: '/assets/(.*)',
227
- headers: { 'cache-control': 'public, max-age=31536000, immutable' },
228
- continue: true,
229
- },
230
- // Serve matched static files from .vercel/output/static/.
231
- { handle: 'filesystem' },
232
- // Fall through to the SSR function for all other paths.
233
- { src: '/(.*)', dest: '/index' },
234
- ],
235
- },
236
- null,
237
- 2,
238
- ) + '\n',
239
- )
240
- }
241
-
242
- // ─── Static (SPA / SSG) output ────────────────────────────────────────────────
243
-
244
- function _buildStatic(distDir: string, outDir: string): void {
245
- const staticDir = join(outDir, 'static')
246
- mkdirSync(staticDir, { recursive: true })
247
-
248
- const clientDir = join(distDir, 'client')
249
-
250
- if (existsSync(clientDir)) {
251
- // SSG: pre-rendered HTML lives in dist/, assets in dist/client/.
252
- // Copy HTML (and any other root-level files) from dist/ — skip client/ dir
253
- // and ssg-manifest.json which are build artifacts, not deployable content.
254
- cpSync(distDir, staticDir, {
255
- recursive: true,
256
- filter: (src) =>
257
- src !== clientDir && !src.endsWith('ssg-manifest.json'),
258
- })
259
- // Copy assets and other non-HTML files from dist/client/ into static root
260
- // so URLs like /assets/main.abc123.js resolve correctly.
261
- _copyClientAssets(clientDir, staticDir)
262
- } else {
263
- // SPA: everything is in dist/ at the correct paths already.
264
- cpSync(distDir, staticDir, { recursive: true })
265
- }
266
-
267
- // Vercel output config: serve static files, fall back to index.html (SPA).
268
- writeFileSync(
269
- join(outDir, 'config.json'),
270
- JSON.stringify(
271
- {
272
- version: 3,
273
- routes: [
274
- {
275
- src: '/assets/(.*)',
276
- headers: { 'cache-control': 'public, max-age=31536000, immutable' },
277
- continue: true,
278
- },
279
- { handle: 'filesystem' },
280
- // SPA fallback: serve index.html for any unmatched path.
281
- { src: '/(.*)', dest: '/index.html' },
282
- ],
283
- },
284
- null,
285
- 2,
286
- ) + '\n',
287
- )
288
- }
289
-
290
- // ─── Helpers ──────────────────────────────────────────────────────────────────
291
-
292
- /**
293
- * Copies Vite-hashed assets and any other public files (favicon, robots.txt, …)
294
- * from the client dist directory into `destDir`.
295
- * Skips index.html — that is handled by the SSR function or SSG pre-rendering.
296
- */
297
- function _copyClientAssets(clientDir: string, destDir: string): void {
298
- if (!existsSync(clientDir)) return
299
- mkdirSync(destDir, { recursive: true })
300
-
301
- for (const entry of readdirSync(clientDir)) {
302
- if (entry === 'index.html') continue // served by function / SSG HTML
303
- const src = join(clientDir, entry)
304
- const dest = join(destDir, entry)
305
- if (statSync(src).isDirectory()) {
306
- cpSync(src, dest, { recursive: true })
307
- } else {
308
- copyFileSync(src, dest)
309
- }
310
- }
311
- }