@jasonshimmy/vite-plugin-cer-app 0.23.1 → 0.24.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 (397) hide show
  1. package/dist/cli/checks/lighthouse.d.ts +26 -0
  2. package/dist/cli/checks/lighthouse.d.ts.map +1 -0
  3. package/dist/cli/checks/lighthouse.js +121 -0
  4. package/dist/cli/checks/lighthouse.js.map +1 -0
  5. package/dist/cli/checks/links.d.ts +12 -0
  6. package/dist/cli/checks/links.d.ts.map +1 -0
  7. package/dist/cli/checks/links.js +89 -0
  8. package/dist/cli/checks/links.js.map +1 -0
  9. package/dist/cli/checks/performance.d.ts +17 -0
  10. package/dist/cli/checks/performance.d.ts.map +1 -0
  11. package/dist/cli/checks/performance.js +61 -0
  12. package/dist/cli/checks/performance.js.map +1 -0
  13. package/dist/cli/commands/check.d.ts +3 -0
  14. package/dist/cli/commands/check.d.ts.map +1 -0
  15. package/dist/cli/commands/check.js +85 -0
  16. package/dist/cli/commands/check.js.map +1 -0
  17. package/dist/cli/commands/preview.d.ts.map +1 -1
  18. package/dist/cli/commands/preview.js +37 -4
  19. package/dist/cli/commands/preview.js.map +1 -1
  20. package/dist/cli/create/index.d.ts +11 -1
  21. package/dist/cli/create/index.d.ts.map +1 -1
  22. package/dist/cli/create/index.js +135 -65
  23. package/dist/cli/create/index.js.map +1 -1
  24. package/dist/cli/create/templates/spa/package.json.tpl +5 -3
  25. package/dist/cli/create/templates/ssg/cer.config.ts.tpl +2 -0
  26. package/dist/cli/create/templates/ssg/package.json.tpl +5 -3
  27. package/dist/cli/create/templates/ssr/package.json.tpl +5 -3
  28. package/dist/cli/index.js +2 -0
  29. package/dist/cli/index.js.map +1 -1
  30. package/dist/index.d.ts +3 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/plugin/build-ssg.d.ts +22 -4
  33. package/dist/plugin/build-ssg.d.ts.map +1 -1
  34. package/dist/plugin/build-ssg.js +273 -13
  35. package/dist/plugin/build-ssg.js.map +1 -1
  36. package/dist/plugin/content/parser.d.ts.map +1 -1
  37. package/dist/plugin/content/parser.js +54 -10
  38. package/dist/plugin/content/parser.js.map +1 -1
  39. package/dist/plugin/dev-server.d.ts +6 -1
  40. package/dist/plugin/dev-server.d.ts.map +1 -1
  41. package/dist/plugin/dev-server.js.map +1 -1
  42. package/dist/plugin/dts-generator.d.ts.map +1 -1
  43. package/dist/plugin/dts-generator.js +50 -8
  44. package/dist/plugin/dts-generator.js.map +1 -1
  45. package/dist/plugin/generated-dir.d.ts +2 -0
  46. package/dist/plugin/generated-dir.d.ts.map +1 -1
  47. package/dist/plugin/generated-dir.js +17 -6
  48. package/dist/plugin/generated-dir.js.map +1 -1
  49. package/dist/plugin/html-post-process.d.ts +35 -0
  50. package/dist/plugin/html-post-process.d.ts.map +1 -0
  51. package/dist/plugin/html-post-process.js +112 -0
  52. package/dist/plugin/html-post-process.js.map +1 -0
  53. package/dist/plugin/index.d.ts +9 -0
  54. package/dist/plugin/index.d.ts.map +1 -1
  55. package/dist/plugin/index.js +89 -14
  56. package/dist/plugin/index.js.map +1 -1
  57. package/dist/plugin/transforms/auto-import.d.ts.map +1 -1
  58. package/dist/plugin/transforms/auto-import.js +7 -0
  59. package/dist/plugin/transforms/auto-import.js.map +1 -1
  60. package/dist/plugin/transforms/prune-runtime-css.d.ts +8 -0
  61. package/dist/plugin/transforms/prune-runtime-css.d.ts.map +1 -0
  62. package/dist/plugin/transforms/prune-runtime-css.js +40 -0
  63. package/dist/plugin/transforms/prune-runtime-css.js.map +1 -0
  64. package/dist/runtime/app-template.d.ts +1 -4
  65. package/dist/runtime/app-template.d.ts.map +1 -1
  66. package/dist/runtime/app-template.js +211 -40
  67. package/dist/runtime/app-template.js.map +1 -1
  68. package/dist/runtime/composables/index.d.ts +4 -0
  69. package/dist/runtime/composables/index.d.ts.map +1 -1
  70. package/dist/runtime/composables/index.js +2 -0
  71. package/dist/runtime/composables/index.js.map +1 -1
  72. package/dist/runtime/composables/use-active-headings.d.ts +24 -0
  73. package/dist/runtime/composables/use-active-headings.d.ts.map +1 -0
  74. package/dist/runtime/composables/use-active-headings.js +112 -0
  75. package/dist/runtime/composables/use-active-headings.js.map +1 -0
  76. package/dist/runtime/composables/use-content-page.d.ts +45 -0
  77. package/dist/runtime/composables/use-content-page.d.ts.map +1 -0
  78. package/dist/runtime/composables/use-content-page.js +179 -0
  79. package/dist/runtime/composables/use-content-page.js.map +1 -0
  80. package/dist/runtime/composables/use-content-search.d.ts.map +1 -1
  81. package/dist/runtime/composables/use-content-search.js +1 -5
  82. package/dist/runtime/composables/use-content-search.js.map +1 -1
  83. package/dist/runtime/composables/use-fetch.d.ts +2 -1
  84. package/dist/runtime/composables/use-fetch.d.ts.map +1 -1
  85. package/dist/runtime/composables/use-fetch.js +112 -43
  86. package/dist/runtime/composables/use-fetch.js.map +1 -1
  87. package/dist/runtime/composables/use-head.d.ts.map +1 -1
  88. package/dist/runtime/composables/use-head.js +30 -6
  89. package/dist/runtime/composables/use-head.js.map +1 -1
  90. package/dist/runtime/composables/use-route.d.ts.map +1 -1
  91. package/dist/runtime/composables/use-route.js +39 -11
  92. package/dist/runtime/composables/use-route.js.map +1 -1
  93. package/dist/runtime/content/client.js +2 -2
  94. package/dist/runtime/content/client.js.map +1 -1
  95. package/dist/runtime/entry-server-template.d.ts +16 -1
  96. package/dist/runtime/entry-server-template.d.ts.map +1 -1
  97. package/dist/runtime/entry-server-template.js +120 -13
  98. package/dist/runtime/entry-server-template.js.map +1 -1
  99. package/dist/types/config.d.ts +53 -4
  100. package/dist/types/config.d.ts.map +1 -1
  101. package/dist/types/config.js.map +1 -1
  102. package/dist/types/middleware.d.ts +2 -2
  103. package/dist/types/middleware.d.ts.map +1 -1
  104. package/package.json +22 -19
  105. package/.github/copilot-instructions.md +0 -169
  106. package/.github/workflows/publish.yml +0 -257
  107. package/.nvmrc +0 -1
  108. package/CHANGELOG.md +0 -232
  109. package/CLAUDE.md +0 -113
  110. package/commits.txt +0 -1
  111. package/cypress.config.ts +0 -16
  112. package/docs/authentication.md +0 -285
  113. package/docs/cli.md +0 -324
  114. package/docs/components.md +0 -237
  115. package/docs/composables.md +0 -907
  116. package/docs/configuration.md +0 -667
  117. package/docs/content.md +0 -570
  118. package/docs/data-loading.md +0 -336
  119. package/docs/getting-started.md +0 -237
  120. package/docs/head-management.md +0 -212
  121. package/docs/i18n.md +0 -159
  122. package/docs/layouts.md +0 -224
  123. package/docs/middleware.md +0 -234
  124. package/docs/plugins.md +0 -170
  125. package/docs/rendering-modes.md +0 -377
  126. package/docs/routing.md +0 -485
  127. package/docs/server-api.md +0 -253
  128. package/docs/testing.md +0 -455
  129. package/docs/use-fetch.md +0 -183
  130. package/e2e/cypress/e2e/api.cy.ts +0 -123
  131. package/e2e/cypress/e2e/async-component.cy.ts +0 -27
  132. package/e2e/cypress/e2e/component-splitting.cy.ts +0 -50
  133. package/e2e/cypress/e2e/content.cy.ts +0 -425
  134. package/e2e/cypress/e2e/cookie.cy.ts +0 -85
  135. package/e2e/cypress/e2e/data.cy.ts +0 -158
  136. package/e2e/cypress/e2e/error-boundary.cy.ts +0 -88
  137. package/e2e/cypress/e2e/fouc.cy.ts +0 -254
  138. package/e2e/cypress/e2e/group-meta.cy.ts +0 -36
  139. package/e2e/cypress/e2e/head.cy.ts +0 -89
  140. package/e2e/cypress/e2e/i18n.cy.ts +0 -79
  141. package/e2e/cypress/e2e/interactive.cy.ts +0 -137
  142. package/e2e/cypress/e2e/isr-nested-runtime.cy.ts +0 -102
  143. package/e2e/cypress/e2e/jit-css-custom-colors.cy.ts +0 -43
  144. package/e2e/cypress/e2e/middleware-chain.cy.ts +0 -89
  145. package/e2e/cypress/e2e/middleware-status.cy.ts +0 -30
  146. package/e2e/cypress/e2e/middleware.cy.ts +0 -45
  147. package/e2e/cypress/e2e/navigate.cy.ts +0 -39
  148. package/e2e/cypress/e2e/observability.cy.ts +0 -77
  149. package/e2e/cypress/e2e/per-route-error.cy.ts +0 -42
  150. package/e2e/cypress/e2e/per-route-render.cy.ts +0 -70
  151. package/e2e/cypress/e2e/preview-hardening.cy.ts +0 -88
  152. package/e2e/cypress/e2e/routes.cy.ts +0 -128
  153. package/e2e/cypress/e2e/seo-meta.cy.ts +0 -108
  154. package/e2e/cypress/e2e/server-middleware.cy.ts +0 -46
  155. package/e2e/cypress/e2e/session-rotation.cy.ts +0 -25
  156. package/e2e/cypress/e2e/session.cy.ts +0 -73
  157. package/e2e/cypress/e2e/ssr-render-error.cy.ts +0 -68
  158. package/e2e/cypress/e2e/synthetic-404.cy.ts +0 -45
  159. package/e2e/cypress/e2e/use-page-data.cy.ts +0 -122
  160. package/e2e/cypress/e2e/use-state.cy.ts +0 -138
  161. package/e2e/cypress/support/commands.ts +0 -120
  162. package/e2e/cypress/support/e2e.ts +0 -10
  163. package/e2e/cypress/tsconfig.json +0 -14
  164. package/e2e/kitchen-sink/app/components/ks-badge.ts +0 -8
  165. package/e2e/kitchen-sink/app/composables/useKsCounter.ts +0 -9
  166. package/e2e/kitchen-sink/app/error.ts +0 -18
  167. package/e2e/kitchen-sink/app/layouts/admin.ts +0 -13
  168. package/e2e/kitchen-sink/app/layouts/default.ts +0 -25
  169. package/e2e/kitchen-sink/app/layouts/group.ts +0 -9
  170. package/e2e/kitchen-sink/app/layouts/minimal.ts +0 -7
  171. package/e2e/kitchen-sink/app/loading.ts +0 -9
  172. package/e2e/kitchen-sink/app/middleware/auth.ts +0 -9
  173. package/e2e/kitchen-sink/app/middleware/custom-log.ts +0 -4
  174. package/e2e/kitchen-sink/app/middleware/group-auth.ts +0 -7
  175. package/e2e/kitchen-sink/app/middleware/logger.ts +0 -14
  176. package/e2e/kitchen-sink/app/pages/(auth)/login.ts +0 -13
  177. package/e2e/kitchen-sink/app/pages/(auth)/protected.ts +0 -16
  178. package/e2e/kitchen-sink/app/pages/404.ts +0 -9
  179. package/e2e/kitchen-sink/app/pages/about.ts +0 -17
  180. package/e2e/kitchen-sink/app/pages/admin/_layout.ts +0 -1
  181. package/e2e/kitchen-sink/app/pages/admin/dashboard.ts +0 -11
  182. package/e2e/kitchen-sink/app/pages/async-component-error-test.ts +0 -18
  183. package/e2e/kitchen-sink/app/pages/async-component-test.ts +0 -18
  184. package/e2e/kitchen-sink/app/pages/blog/[slug].ts +0 -59
  185. package/e2e/kitchen-sink/app/pages/blog/index.ts +0 -51
  186. package/e2e/kitchen-sink/app/pages/content-blog.ts +0 -37
  187. package/e2e/kitchen-sink/app/pages/content-doc.ts +0 -42
  188. package/e2e/kitchen-sink/app/pages/content-fallback.ts +0 -36
  189. package/e2e/kitchen-sink/app/pages/content-guides.ts +0 -35
  190. package/e2e/kitchen-sink/app/pages/content-index.ts +0 -39
  191. package/e2e/kitchen-sink/app/pages/content-search.ts +0 -36
  192. package/e2e/kitchen-sink/app/pages/cookie-test.ts +0 -22
  193. package/e2e/kitchen-sink/app/pages/counter.ts +0 -17
  194. package/e2e/kitchen-sink/app/pages/group-auth-blocked.ts +0 -9
  195. package/e2e/kitchen-sink/app/pages/group-meta-test/_layout.ts +0 -6
  196. package/e2e/kitchen-sink/app/pages/group-meta-test/custom-mw.ts +0 -14
  197. package/e2e/kitchen-sink/app/pages/group-meta-test/page.ts +0 -9
  198. package/e2e/kitchen-sink/app/pages/group-meta-test/protected.ts +0 -8
  199. package/e2e/kitchen-sink/app/pages/head.ts +0 -20
  200. package/e2e/kitchen-sink/app/pages/i18n-test.ts +0 -14
  201. package/e2e/kitchen-sink/app/pages/index.ts +0 -27
  202. package/e2e/kitchen-sink/app/pages/isr-test.ts +0 -17
  203. package/e2e/kitchen-sink/app/pages/items/[id].ts +0 -20
  204. package/e2e/kitchen-sink/app/pages/jit-css-custom-colors-test.ts +0 -14
  205. package/e2e/kitchen-sink/app/pages/loader-error-test.ts +0 -13
  206. package/e2e/kitchen-sink/app/pages/loader-props-test.ts +0 -19
  207. package/e2e/kitchen-sink/app/pages/loader-response-error-test.ts +0 -13
  208. package/e2e/kitchen-sink/app/pages/middleware-chain-test.ts +0 -12
  209. package/e2e/kitchen-sink/app/pages/middleware-status-test.ts +0 -9
  210. package/e2e/kitchen-sink/app/pages/navigate-test.ts +0 -16
  211. package/e2e/kitchen-sink/app/pages/observability-test.ts +0 -25
  212. package/e2e/kitchen-sink/app/pages/per-route-error-test.error.ts +0 -13
  213. package/e2e/kitchen-sink/app/pages/per-route-error-test.ts +0 -15
  214. package/e2e/kitchen-sink/app/pages/plugin-dsd-test.ts +0 -10
  215. package/e2e/kitchen-sink/app/pages/render-error-test.ts +0 -12
  216. package/e2e/kitchen-sink/app/pages/render-server-test.ts +0 -12
  217. package/e2e/kitchen-sink/app/pages/render-spa-test.ts +0 -12
  218. package/e2e/kitchen-sink/app/pages/route-info.ts +0 -14
  219. package/e2e/kitchen-sink/app/pages/seo-test.ts +0 -23
  220. package/e2e/kitchen-sink/app/pages/session-rotation-test.ts +0 -25
  221. package/e2e/kitchen-sink/app/pages/use-state-test.ts +0 -37
  222. package/e2e/kitchen-sink/app/plugins/01.setup.ts +0 -7
  223. package/e2e/kitchen-sink/app/plugins/02.ks-plugin-components.ts +0 -27
  224. package/e2e/kitchen-sink/cer-auto-imports.d.ts +0 -51
  225. package/e2e/kitchen-sink/cer-env.d.ts +0 -35
  226. package/e2e/kitchen-sink/cer-tsconfig.json +0 -30
  227. package/e2e/kitchen-sink/cer.config.ts +0 -43
  228. package/e2e/kitchen-sink/content/01.guides/01.index.md +0 -8
  229. package/e2e/kitchen-sink/content/01.guides/02.intro.md +0 -8
  230. package/e2e/kitchen-sink/content/01.guides/10.advanced.md +0 -8
  231. package/e2e/kitchen-sink/content/blog/2026-04-01-hello.md +0 -26
  232. package/e2e/kitchen-sink/content/blog/2026-04-02-draft.md +0 -10
  233. package/e2e/kitchen-sink/content/blog/index.md +0 -8
  234. package/e2e/kitchen-sink/content/blog/no-frontmatter.md +0 -7
  235. package/e2e/kitchen-sink/content/docs/getting-started.md +0 -48
  236. package/e2e/kitchen-sink/content/index.md +0 -16
  237. package/e2e/kitchen-sink/server/api/echo.ts +0 -12
  238. package/e2e/kitchen-sink/server/api/health.ts +0 -3
  239. package/e2e/kitchen-sink/server/api/posts/[slug].ts +0 -11
  240. package/e2e/kitchen-sink/server/api/posts/index.ts +0 -5
  241. package/e2e/kitchen-sink/server/api/session.ts +0 -23
  242. package/e2e/kitchen-sink/server/data/posts.ts +0 -21
  243. package/e2e/kitchen-sink/server/middleware/01-headers.ts +0 -6
  244. package/e2e/kitchen-sink/server/middleware/02-status-test.ts +0 -10
  245. package/e2e/kitchen-sink/tsconfig.json +0 -3
  246. package/e2e/scripts/clean.mjs +0 -14
  247. package/eslint.config.ts +0 -22
  248. package/src/__tests__/cli/adapters/cloudflare-worker.integration.test.ts +0 -132
  249. package/src/__tests__/cli/adapters/cloudflare.test.ts +0 -332
  250. package/src/__tests__/cli/adapters/netlify-bridge.integration.test.ts +0 -139
  251. package/src/__tests__/cli/adapters/netlify.test.ts +0 -286
  252. package/src/__tests__/cli/adapters/vercel-launcher.integration.test.ts +0 -129
  253. package/src/__tests__/cli/adapters/vercel.test.ts +0 -263
  254. package/src/__tests__/cli/preview-hardening.test.ts +0 -265
  255. package/src/__tests__/cli/preview-isr.test.ts +0 -320
  256. package/src/__tests__/index.test.ts +0 -21
  257. package/src/__tests__/plugin/app-template.test.ts +0 -112
  258. package/src/__tests__/plugin/build-ssg-render.test.ts +0 -155
  259. package/src/__tests__/plugin/build-ssg.test.ts +0 -598
  260. package/src/__tests__/plugin/build-ssr.test.ts +0 -192
  261. package/src/__tests__/plugin/cer-app-plugin.test.ts +0 -552
  262. package/src/__tests__/plugin/content/emitter.test.ts +0 -117
  263. package/src/__tests__/plugin/content/loader.test.ts +0 -154
  264. package/src/__tests__/plugin/content/parser.test.ts +0 -381
  265. package/src/__tests__/plugin/content/path-utils.test.ts +0 -69
  266. package/src/__tests__/plugin/content/search.test.ts +0 -119
  267. package/src/__tests__/plugin/dev-server.test.ts +0 -813
  268. package/src/__tests__/plugin/dts-generator.test.ts +0 -394
  269. package/src/__tests__/plugin/entry-server-template.test.ts +0 -385
  270. package/src/__tests__/plugin/generated-dir.test.ts +0 -144
  271. package/src/__tests__/plugin/path-utils.test.ts +0 -411
  272. package/src/__tests__/plugin/resolve-config.test.ts +0 -200
  273. package/src/__tests__/plugin/scanner.test.ts +0 -186
  274. package/src/__tests__/plugin/transforms/auto-import.test.ts +0 -510
  275. package/src/__tests__/plugin/transforms/head-inject.test.ts +0 -178
  276. package/src/__tests__/plugin/virtual/composables.test.ts +0 -57
  277. package/src/__tests__/plugin/virtual/content-components.test.ts +0 -66
  278. package/src/__tests__/plugin/virtual/error.test.ts +0 -71
  279. package/src/__tests__/plugin/virtual/layouts.test.ts +0 -70
  280. package/src/__tests__/plugin/virtual/loading.test.ts +0 -72
  281. package/src/__tests__/plugin/virtual/middleware.test.ts +0 -83
  282. package/src/__tests__/plugin/virtual/plugins.test.ts +0 -129
  283. package/src/__tests__/plugin/virtual/routes.test.ts +0 -884
  284. package/src/__tests__/plugin/virtual/server-api.test.ts +0 -164
  285. package/src/__tests__/plugin/virtual/server-middleware.test.ts +0 -102
  286. package/src/__tests__/runtime/app-template.test.ts +0 -198
  287. package/src/__tests__/runtime/define-middleware.test.ts +0 -64
  288. package/src/__tests__/runtime/define-server-middleware.test.ts +0 -54
  289. package/src/__tests__/runtime/entry-client-template.test.ts +0 -61
  290. package/src/__tests__/runtime/isr-handler.test.ts +0 -338
  291. package/src/__tests__/runtime/oauth/handler.test.ts +0 -358
  292. package/src/__tests__/runtime/oauth/providers.test.ts +0 -47
  293. package/src/__tests__/runtime/oauth-index.test.ts +0 -43
  294. package/src/__tests__/runtime/use-auth.test.ts +0 -169
  295. package/src/__tests__/runtime/use-content-search-composable.test.ts +0 -405
  296. package/src/__tests__/runtime/use-content-search.test.ts +0 -163
  297. package/src/__tests__/runtime/use-content.test.ts +0 -226
  298. package/src/__tests__/runtime/use-cookie.test.ts +0 -277
  299. package/src/__tests__/runtime/use-fetch-component.test.ts +0 -214
  300. package/src/__tests__/runtime/use-fetch.test.ts +0 -377
  301. package/src/__tests__/runtime/use-head.test.ts +0 -266
  302. package/src/__tests__/runtime/use-inject-client.test.ts +0 -67
  303. package/src/__tests__/runtime/use-inject.test.ts +0 -66
  304. package/src/__tests__/runtime/use-locale.test.ts +0 -152
  305. package/src/__tests__/runtime/use-navigate.test.ts +0 -121
  306. package/src/__tests__/runtime/use-page-data.test.ts +0 -298
  307. package/src/__tests__/runtime/use-route.test.ts +0 -156
  308. package/src/__tests__/runtime/use-runtime-config.test.ts +0 -182
  309. package/src/__tests__/runtime/use-seo-meta.test.ts +0 -109
  310. package/src/__tests__/runtime/use-session.test.ts +0 -320
  311. package/src/__tests__/runtime/use-state.test.ts +0 -340
  312. package/src/__tests__/types/config.test.ts +0 -18
  313. package/src/cli/adapters/cloudflare.ts +0 -335
  314. package/src/cli/adapters/netlify.ts +0 -326
  315. package/src/cli/adapters/vercel.ts +0 -311
  316. package/src/cli/commands/adapt.ts +0 -84
  317. package/src/cli/commands/build.ts +0 -165
  318. package/src/cli/commands/dev.ts +0 -125
  319. package/src/cli/commands/generate.ts +0 -91
  320. package/src/cli/commands/preview-isr.ts +0 -172
  321. package/src/cli/commands/preview.ts +0 -489
  322. package/src/cli/create/index.ts +0 -275
  323. package/src/cli/create/templates/shared/.gitignore.tpl +0 -25
  324. package/src/cli/create/templates/shared/app/layouts/default.ts.tpl +0 -15
  325. package/src/cli/create/templates/shared/app/pages/index.ts.tpl +0 -8
  326. package/src/cli/create/templates/shared/index.html.tpl +0 -12
  327. package/src/cli/create/templates/shared/tsconfig.json.tpl +0 -6
  328. package/src/cli/create/templates/spa/cer.config.ts.tpl +0 -6
  329. package/src/cli/create/templates/spa/package.json.tpl +0 -18
  330. package/src/cli/create/templates/ssg/cer.config.ts.tpl +0 -10
  331. package/src/cli/create/templates/ssg/package.json.tpl +0 -19
  332. package/src/cli/create/templates/ssr/cer.config.ts.tpl +0 -6
  333. package/src/cli/create/templates/ssr/package.json.tpl +0 -18
  334. package/src/cli/index.ts +0 -22
  335. package/src/index.ts +0 -20
  336. package/src/plugin/build-ssg.ts +0 -336
  337. package/src/plugin/build-ssr.ts +0 -134
  338. package/src/plugin/content/emitter.ts +0 -50
  339. package/src/plugin/content/index.ts +0 -233
  340. package/src/plugin/content/parser.ts +0 -259
  341. package/src/plugin/content/path-utils.ts +0 -55
  342. package/src/plugin/content/scanner.ts +0 -26
  343. package/src/plugin/content/search.ts +0 -28
  344. package/src/plugin/dev-server.ts +0 -377
  345. package/src/plugin/dts-generator.ts +0 -273
  346. package/src/plugin/generated-dir.ts +0 -121
  347. package/src/plugin/index.ts +0 -553
  348. package/src/plugin/path-utils.ts +0 -186
  349. package/src/plugin/scanner.ts +0 -65
  350. package/src/plugin/transforms/auto-import.ts +0 -206
  351. package/src/plugin/transforms/head-inject.ts +0 -161
  352. package/src/plugin/virtual/composables.ts +0 -28
  353. package/src/plugin/virtual/content-components.ts +0 -83
  354. package/src/plugin/virtual/error.ts +0 -34
  355. package/src/plugin/virtual/layouts.ts +0 -41
  356. package/src/plugin/virtual/loading.ts +0 -32
  357. package/src/plugin/virtual/middleware.ts +0 -45
  358. package/src/plugin/virtual/plugins.ts +0 -46
  359. package/src/plugin/virtual/routes.ts +0 -514
  360. package/src/plugin/virtual/server-api.ts +0 -85
  361. package/src/plugin/virtual/server-middleware.ts +0 -44
  362. package/src/runtime/app-template.ts +0 -380
  363. package/src/runtime/composables/define-middleware.ts +0 -17
  364. package/src/runtime/composables/define-server-middleware.ts +0 -18
  365. package/src/runtime/composables/index.ts +0 -27
  366. package/src/runtime/composables/use-auth.ts +0 -125
  367. package/src/runtime/composables/use-content-search.ts +0 -205
  368. package/src/runtime/composables/use-content.ts +0 -146
  369. package/src/runtime/composables/use-cookie.ts +0 -135
  370. package/src/runtime/composables/use-fetch.ts +0 -438
  371. package/src/runtime/composables/use-head.ts +0 -212
  372. package/src/runtime/composables/use-inject.ts +0 -49
  373. package/src/runtime/composables/use-locale.ts +0 -109
  374. package/src/runtime/composables/use-navigate.ts +0 -58
  375. package/src/runtime/composables/use-page-data.ts +0 -93
  376. package/src/runtime/composables/use-route.ts +0 -71
  377. package/src/runtime/composables/use-runtime-config.ts +0 -123
  378. package/src/runtime/composables/use-seo-meta.ts +0 -80
  379. package/src/runtime/composables/use-session.ts +0 -186
  380. package/src/runtime/composables/use-state.ts +0 -92
  381. package/src/runtime/content/client.ts +0 -168
  382. package/src/runtime/entry-client-template.ts +0 -39
  383. package/src/runtime/entry-server-template.ts +0 -493
  384. package/src/runtime/isr-handler.ts +0 -184
  385. package/src/runtime/oauth/handler.ts +0 -407
  386. package/src/runtime/oauth/index.ts +0 -49
  387. package/src/runtime/oauth/providers.ts +0 -38
  388. package/src/types/api.ts +0 -40
  389. package/src/types/config.ts +0 -371
  390. package/src/types/content.ts +0 -66
  391. package/src/types/index.ts +0 -7
  392. package/src/types/middleware.ts +0 -63
  393. package/src/types/page.ts +0 -117
  394. package/src/types/plugin.ts +0 -34
  395. package/tsconfig.build.json +0 -10
  396. package/tsconfig.json +0 -19
  397. package/vitest.config.ts +0 -33
@@ -1,438 +0,0 @@
1
- /**
2
- * useFetch — isomorphic data fetching composable.
3
- *
4
- * Behaves differently based on the calling context:
5
- *
6
- * **Component context** (inside a `component()` render function):
7
- * Uses `createComposable` internally so that `useOnConnected` is bound to the
8
- * calling component's lifecycle. Returns reactive `ReactiveState` refs for
9
- * `data`, `pending`, and `error` — the component re-renders automatically
10
- * when the fetch settles. The fetch is triggered by `useOnConnected` (i.e.
11
- * when the component mounts), unless `lazy: true` / `server: false`.
12
- *
13
- * **Loader / SSR context** (inside an async `load()` or middleware):
14
- * Returns a *thenable* `UseFetchResult` — you can `await useFetch(...)` to
15
- * block SSR rendering until data is ready. The result is serialised into
16
- * `window.__CER_FETCH_DATA__` for client hydration.
17
- *
18
- * @example
19
- * ```ts
20
- * // app/pages/posts.ts — loader context (SSR + client navigation)
21
- * export const loader = async () => {
22
- * const { data: posts } = await useFetch<Post[]>('/api/posts')
23
- * return { posts }
24
- * }
25
- * ```
26
- *
27
- * @example
28
- * ```ts
29
- * // Inside a component — reactive, auto-fetches on mount
30
- * component('post-list', () => {
31
- * const { data: posts, pending, error } = useFetch<Post[]>('/api/posts')
32
- *
33
- * return html`
34
- * ${pending.value ? html`<p>Loading…</p>` : ''}
35
- * ${error.value ? html`<p>Error: ${error.value.message}</p>` : ''}
36
- * ${posts.value?.map(p => html`<li>${p.title}</li>`)}
37
- * `
38
- * })
39
- * ```
40
- *
41
- * @example
42
- * ```ts
43
- * // Lazy — skip auto-fetch, call refresh() manually
44
- * component('post-list', () => {
45
- * const { data: posts, refresh } = useFetch<Post[]>('/api/posts', { lazy: true })
46
- *
47
- * useOnConnected(async () => {
48
- * await refresh()
49
- * })
50
- * })
51
- * ```
52
- */
53
-
54
- import { createComposable, getCurrentComponentContext, ref, useOnConnected } from '@jasonshimmy/custom-elements-runtime'
55
- import type { ReactiveState } from '@jasonshimmy/custom-elements-runtime'
56
-
57
- // ─── Types ────────────────────────────────────────────────────────────────────
58
-
59
- /**
60
- * Return value when `useFetch` is called inside a component render function.
61
- * All fields are reactive (`ReactiveState`) — the component re-renders
62
- * automatically when the fetch settles or `refresh()` is called.
63
- */
64
- export interface UseFetchReactiveReturn<T = unknown> {
65
- /** Reactive fetch result. Access via `.data.value`. */
66
- data: ReactiveState<T | null>
67
- /** `true` while the request is in-flight. */
68
- pending: ReactiveState<boolean>
69
- /** Set when the request fails; `null` on success. */
70
- error: ReactiveState<Error | null>
71
- /** Re-issues the fetch and updates `data`, `pending`, and `error`. */
72
- refresh(): Promise<void>
73
- }
74
-
75
- /** Options for `useFetch()`. Controls caching, SSR behaviour, data transformation, and HTTP request details. */
76
- export interface UseFetchOptions<T = unknown> {
77
- /**
78
- * Unique cache key. Defaults to the full URL string (including query params).
79
- *
80
- * On the server, calls sharing the same key within one request reuse the
81
- * first result without issuing a second network request. On the client,
82
- * the key is used to consume the hydrated `window.__CER_FETCH_DATA__` entry
83
- * exactly once — subsequent calls with the same key issue a fresh fetch.
84
- */
85
- key?: string
86
- /**
87
- * When `true`, skip the SSR fetch entirely and only fetch on the client.
88
- * Equivalent to `server: false`.
89
- *
90
- * In component context this also skips the `useOnConnected` auto-fetch —
91
- * call `refresh()` manually when you want the request to fire.
92
- */
93
- lazy?: boolean
94
- /**
95
- * When `false`, equivalent to `lazy: true` — skip SSR, fetch client-side.
96
- */
97
- server?: boolean
98
- /**
99
- * Factory that returns the initial value before the fetch completes.
100
- * Called once per `useFetch()` invocation. Defaults to `() => null`.
101
- *
102
- * @example `{ default: () => [] }` — start with an empty array
103
- */
104
- default?: () => T
105
- /**
106
- * Transform the raw JSON response before storing / returning it.
107
- * Applied after `pick` (if both are set).
108
- */
109
- transform?: (data: unknown) => T
110
- /**
111
- * Pick a subset of keys from an object response.
112
- * Applied before `transform`. Keys absent in the response become
113
- * `undefined` in the result.
114
- */
115
- pick?: string[]
116
- /** HTTP method. Defaults to `'GET'`. */
117
- method?: string
118
- /**
119
- * Request body for POST / PUT / PATCH. Serialised to JSON automatically;
120
- * sets `Content-Type: application/json` if not already provided.
121
- */
122
- body?: unknown
123
- /** Additional request headers merged with auto-generated ones. */
124
- headers?: Record<string, string>
125
- /**
126
- * Query parameters appended to the URL via `URLSearchParams`.
127
- * Included in the default cache `key`.
128
- */
129
- query?: Record<string, string>
130
- }
131
-
132
- export interface UseFetchReturn<T = unknown> {
133
- data: T | null
134
- pending: boolean
135
- error: Error | null
136
- /** Re-issues the fetch and updates `data`, `pending`, and `error` in place. */
137
- refresh(): Promise<UseFetchReturn<T>>
138
- }
139
-
140
- /** A UseFetchReturn that is also awaitable (resolves once the fetch completes). */
141
- export type UseFetchResult<T = unknown> = UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
142
-
143
- // ─── Client-side in-flight deduplication ─────────────────────────────────────
144
- // P2-3: When two components (or two concurrent refresh() calls) fetch the same
145
- // key simultaneously, only one HTTP request is issued. The second caller awaits
146
- // the shared Promise and applies its own transform to the raw response.
147
- // The map is module-level so all component instances share it.
148
- // Keys are removed when the Promise settles (resolve or reject).
149
- const _inflight = new Map<string, Promise<unknown>>()
150
-
151
- // ─── Helpers ──────────────────────────────────────────────────────────────────
152
-
153
- // Access fetch via globalThis so that vi.stubGlobal('fetch', mock) works in tests.
154
- // A direct `fetch(...)` call in an ESM module may bind to the runtime's fetch
155
- // at module-evaluation time and bypass globalThis mutations.
156
- function _fetch(url: string, init?: RequestInit): Promise<Response> {
157
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
- return ((globalThis as any)['fetch'] as typeof fetch)(url, init)
159
- }
160
-
161
- function applyPick(data: unknown, pick: string[]): unknown {
162
- if (typeof data !== 'object' || data === null) return data
163
- return Object.fromEntries(pick.map((k) => [k, (data as Record<string, unknown>)[k]]))
164
- }
165
-
166
- function applyTransform<T>(raw: unknown, options?: UseFetchOptions<T>): T {
167
- let result = raw
168
- if (options?.pick) result = applyPick(result, options.pick)
169
- if (options?.transform) result = options.transform(result)
170
- return result as T
171
- }
172
-
173
- function buildInit(options?: UseFetchOptions): RequestInit {
174
- const init: RequestInit = { method: options?.method ?? 'GET' }
175
- const headers: Record<string, string> = { ...options?.headers }
176
- if (options?.body !== undefined) {
177
- init.body = JSON.stringify(options.body)
178
- headers['Content-Type'] = 'application/json'
179
- }
180
- if (Object.keys(headers).length > 0) init.headers = headers
181
- return init
182
- }
183
-
184
- function appendQuery(url: string, query?: Record<string, string>): string {
185
- if (!query || Object.keys(query).length === 0) return url
186
- const sep = url.includes('?') ? '&' : '?'
187
- return url + sep + new URLSearchParams(query).toString()
188
- }
189
-
190
- function makeResult<T>(
191
- state: UseFetchReturn<T>,
192
- settling: Promise<void> | null,
193
- ): UseFetchResult<T> {
194
- // Return a wrapper with live getters so callers see state mutations (e.g.
195
- // after doClientFetch sets state.data), PLUS a custom .then so the result
196
- // is directly awaitable.
197
- //
198
- // Critically, we do NOT add .then to `state` itself — doing so would cause
199
- // async functions that `return state` (e.g. doClientFetch) to deadlock:
200
- // the async function's Promise would try to assimilate state as a thenable,
201
- // calling state.then which waits for settling, which can't resolve until
202
- // the async function finishes — a circular dependency.
203
- return {
204
- get data() { return state.data },
205
- get pending() { return state.pending },
206
- get error() { return state.error },
207
- get refresh() { return state.refresh },
208
- then(
209
- onFulfilled?: (v: UseFetchReturn<T>) => unknown,
210
- onRejected?: (e: unknown) => unknown,
211
- ) {
212
- const p = settling ?? Promise.resolve()
213
- // Remove .then from this wrapper BEFORE resolving so the Promise
214
- // machinery won't treat it as a thenable when resolve(this) is called.
215
- // After deletion the wrapper becomes a plain UseFetchReturn<T> with
216
- // live getters — callers can still read .data, .error, etc. and get
217
- // up-to-date values (e.g. after .refresh()).
218
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
219
- delete (this as any).then
220
- const self = this as UseFetchReturn<T>
221
- return p.then(() => {
222
- if (onFulfilled) return onFulfilled(self)
223
- }, onRejected)
224
- },
225
- } as UseFetchResult<T>
226
- }
227
-
228
- // ─── Component-context composable factory ─────────────────────────────────────
229
-
230
- /**
231
- * Called when `useFetch` detects it is running inside a component render
232
- * function. The factory is created fresh per call so each component instance
233
- * gets isolated reactive state; `createComposable` binds lifecycle hooks
234
- * (`useOnConnected`) to the calling component.
235
- */
236
- function makeComponentFetch<T>(
237
- url: string | (() => string),
238
- options: UseFetchOptions<T> | undefined,
239
- ctx: Record<string, unknown>,
240
- ): UseFetchReactiveReturn<T> {
241
- const factory = createComposable((): UseFetchReactiveReturn<T> => {
242
- const isLazy = options?.lazy === true || options?.server === false
243
- const resolvedUrl = appendQuery(
244
- typeof url === 'function' ? url() : url,
245
- options?.query,
246
- )
247
-
248
- const data = ref<T | null>(options?.default ? options.default() : null)
249
- const pending = ref(false)
250
- const error = ref<Error | null>(null)
251
-
252
- const doFetch = async (): Promise<void> => {
253
- pending.value = true
254
- error.value = null
255
- try {
256
- const res = await _fetch(resolvedUrl, buildInit(options))
257
- if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`)
258
- const raw = await res.json()
259
- data.value = applyTransform<T>(raw, options)
260
- } catch (err) {
261
- error.value = err instanceof Error ? err : new Error(String(err))
262
- } finally {
263
- pending.value = false
264
- }
265
- }
266
-
267
- if (!isLazy) {
268
- useOnConnected(doFetch)
269
- }
270
-
271
- return { data, pending, error, refresh: doFetch }
272
- })
273
-
274
- return factory(ctx)
275
- }
276
-
277
- // ─── Core implementation ──────────────────────────────────────────────────────
278
-
279
- /**
280
- * Isomorphic data fetching composable.
281
- *
282
- * - **Inside a `component()` render function**: returns reactive
283
- * {@link UseFetchReactiveReturn} — `data`, `pending`, and `error` are
284
- * `ReactiveState` refs that trigger re-renders automatically. The fetch
285
- * fires via `useOnConnected` unless `lazy: true`.
286
- * - **Inside a loader / SSR context**: returns a thenable
287
- * {@link UseFetchResult} — `await useFetch(...)` blocks rendering until data
288
- * is ready.
289
- */
290
- export function useFetch<T = unknown>(
291
- url: string | (() => string),
292
- options?: UseFetchOptions<T>,
293
- ): UseFetchResult<T> | UseFetchReactiveReturn<T> {
294
- // ── Component context ───────────────────────────────────────────────────────
295
- // When called inside a component render function, use createComposable so
296
- // that useOnConnected is bound to the calling component's lifecycle and
297
- // state is isolated per component instance.
298
- const componentCtx = getCurrentComponentContext()
299
- if (componentCtx) {
300
- return makeComponentFetch(url, options, componentCtx)
301
- }
302
- const g = globalThis as Record<string, unknown>
303
- const resolvedUrl = appendQuery(
304
- typeof url === 'function' ? url() : url,
305
- options?.query,
306
- )
307
- const key = options?.key ?? resolvedUrl
308
- const isLazy = options?.lazy === true || options?.server === false
309
- const defaultValue = (options?.default ? options.default() : null) as T | null
310
-
311
- // ── Server path ─────────────────────────────────────────────────────────────
312
- // __CER_FETCH_STORE__ is only present in Node.js (tree-shaken from client bundle).
313
- const fetchStoreAls = g['__CER_FETCH_STORE__'] as { getStore(): unknown } | undefined
314
- if (fetchStoreAls) {
315
- const fetchMap = fetchStoreAls.getStore() as Map<string, unknown> | null
316
-
317
- if (fetchMap) {
318
- // Already fetched in this request — return cached result synchronously.
319
- if (fetchMap.has(key)) {
320
- const state: UseFetchReturn<T> = {
321
- data: applyTransform(fetchMap.get(key), options),
322
- pending: false,
323
- error: null,
324
- refresh: async () => state,
325
- }
326
- return makeResult(state, null)
327
- }
328
-
329
- if (isLazy) {
330
- const state: UseFetchReturn<T> = {
331
- data: defaultValue,
332
- pending: false,
333
- error: null,
334
- refresh: async () => state,
335
- }
336
- return makeResult(state, null)
337
- }
338
-
339
- // Perform the SSR fetch.
340
- const state: UseFetchReturn<T> = {
341
- data: defaultValue,
342
- pending: true,
343
- error: null,
344
- refresh: async () => state,
345
- }
346
-
347
- const settling = (async () => {
348
- try {
349
- const res = await _fetch(resolvedUrl, buildInit(options))
350
- if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`)
351
- const raw = await res.json()
352
- const transformed = applyTransform<T>(raw, options)
353
- // Store in per-request map so the same key isn't fetched twice.
354
- fetchMap.set(key, transformed)
355
- // Also write to the flat accumulator that the entry-server serialises.
356
- const acc = (g['__CER_FETCH_DATA__'] ?? {}) as Record<string, unknown>
357
- acc[key] = transformed
358
- ;(g as Record<string, unknown>)['__CER_FETCH_DATA__'] = acc
359
- state.data = transformed
360
- state.pending = false
361
- } catch (err) {
362
- state.error = err instanceof Error ? err : new Error(String(err))
363
- state.pending = false
364
- }
365
- })()
366
-
367
- return makeResult(state, settling)
368
- }
369
- }
370
-
371
- // ── Client hydration path ───────────────────────────────────────────────────
372
- // Read the pre-fetched data injected by the server into window.__CER_FETCH_DATA__.
373
- const fetchData = g['__CER_FETCH_DATA__'] as Record<string, unknown> | undefined
374
- if (fetchData && key in fetchData) {
375
- const raw = fetchData[key]
376
- // Consume the key — next navigation will fetch fresh.
377
- delete fetchData[key]
378
- const hydrated = applyTransform<T>(raw, options)
379
- const state: UseFetchReturn<T> = {
380
- data: hydrated,
381
- pending: false,
382
- error: null,
383
- refresh: () => doClientFetch(key, resolvedUrl, options, state),
384
- }
385
- return makeResult(state, null)
386
- }
387
-
388
- // ── Client fetch ─────────────────────────────────────────────────────────────
389
- const state: UseFetchReturn<T> = {
390
- data: defaultValue,
391
- pending: !isLazy,
392
- error: null,
393
- refresh: async () => state,
394
- }
395
- state.refresh = () => doClientFetch(key, resolvedUrl, options, state)
396
-
397
- let settling: Promise<void> | null = null
398
- if (!isLazy) {
399
- settling = doClientFetch(key, resolvedUrl, options, state).then(() => undefined)
400
- }
401
-
402
- return makeResult(state, settling)
403
- }
404
-
405
- async function doClientFetch<T>(
406
- key: string,
407
- resolvedUrl: string,
408
- options: UseFetchOptions<T> | undefined,
409
- state: UseFetchReturn<T>,
410
- ): Promise<UseFetchReturn<T>> {
411
- // P2-3: Deduplicate concurrent requests for the same key.
412
- // If another request for this key is already in-flight, share its raw Promise.
413
- let rawPromise: Promise<unknown>
414
-
415
- if (_inflight.has(key)) {
416
- rawPromise = _inflight.get(key)!
417
- } else {
418
- state.pending = true
419
- state.error = null
420
- rawPromise = _fetch(resolvedUrl, buildInit(options))
421
- .then(async (res) => {
422
- if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`)
423
- return res.json()
424
- })
425
- .finally(() => { _inflight.delete(key) })
426
- _inflight.set(key, rawPromise)
427
- }
428
-
429
- try {
430
- const raw = await rawPromise
431
- state.data = applyTransform<T>(raw, options)
432
- state.pending = false
433
- } catch (err) {
434
- state.error = err instanceof Error ? err : new Error(String(err))
435
- state.pending = false
436
- }
437
- return state
438
- }
@@ -1,212 +0,0 @@
1
- /**
2
- * Describes the `<head>` tags to inject for a page or component.
3
- * Pass to `useHead()`. All fields are optional; only set fields are emitted.
4
- */
5
- export interface HeadInput {
6
- title?: string
7
- meta?: Array<Record<string, string>>
8
- link?: Array<Record<string, string>>
9
- script?: Array<Record<string, string>>
10
- style?: Array<Record<string, string>>
11
- }
12
-
13
- // SSR: store collector on globalThis so all module instances (server entry +
14
- // dynamically-imported page chunks) share the same reference. A module-level
15
- // variable would create separate instances per chunk, breaking the singleton.
16
- const _g = globalThis as Record<string, unknown>
17
- const _KEY = '__CER_HEAD_COLLECTOR__'
18
-
19
- /**
20
- * Begin collecting head tags for an SSR render pass.
21
- * Call this before invoking the render function.
22
- */
23
- export function beginHeadCollection(): void {
24
- _g[_KEY] = [] as HeadInput[]
25
- }
26
-
27
- /**
28
- * End collecting head tags and return the collected inputs.
29
- * Resets the collector to null.
30
- */
31
- export function endHeadCollection(): HeadInput[] {
32
- const collected = (_g[_KEY] as HeadInput[]) ?? []
33
- _g[_KEY] = null
34
- return collected
35
- }
36
-
37
- /**
38
- * Serializes an array of HeadInput objects into an HTML string
39
- * suitable for injection into the document <head>.
40
- */
41
- export function serializeHeadTags(heads: HeadInput[]): string {
42
- const parts: string[] = []
43
-
44
- // Merge titles — last one wins
45
- let title: string | undefined
46
- for (const head of heads) {
47
- if (head.title !== undefined) {
48
- title = head.title
49
- }
50
- }
51
- if (title !== undefined) {
52
- parts.push(`<title>${escapeHtml(title)}</title>`)
53
- }
54
-
55
- // Merge meta tags, deduplicate by name/property/charset
56
- const metaMap = new Map<string, Record<string, string>>()
57
- for (const head of heads) {
58
- if (head.meta) {
59
- for (const meta of head.meta) {
60
- const key = meta.name ?? meta.property ?? meta.charset ?? JSON.stringify(meta)
61
- metaMap.set(key, meta)
62
- }
63
- }
64
- }
65
- for (const attrs of metaMap.values()) {
66
- parts.push(`<meta ${renderAttrs(attrs)}>`)
67
- }
68
-
69
- // Merge link tags, deduplicate by rel+href
70
- const linkMap = new Map<string, Record<string, string>>()
71
- for (const head of heads) {
72
- if (head.link) {
73
- for (const link of head.link) {
74
- const key = `${link.rel ?? ''}:${link.href ?? ''}`
75
- linkMap.set(key, link)
76
- }
77
- }
78
- }
79
- for (const attrs of linkMap.values()) {
80
- parts.push(`<link ${renderAttrs(attrs)}>`)
81
- }
82
-
83
- // Script tags
84
- for (const head of heads) {
85
- if (head.script) {
86
- for (const script of head.script) {
87
- const { innerHTML, ...rest } = script as Record<string, string>
88
- if (innerHTML !== undefined) {
89
- parts.push(`<script ${renderAttrs(rest)}>${innerHTML}</script>`)
90
- } else {
91
- parts.push(`<script ${renderAttrs(rest)}></script>`)
92
- }
93
- }
94
- }
95
- }
96
-
97
- // Style tags
98
- for (const head of heads) {
99
- if (head.style) {
100
- for (const style of head.style) {
101
- const { innerHTML, ...rest } = style as Record<string, string>
102
- if (innerHTML !== undefined) {
103
- parts.push(`<style ${renderAttrs(rest)}>${innerHTML}</style>`)
104
- } else {
105
- parts.push(`<style ${renderAttrs(rest)}></style>`)
106
- }
107
- }
108
- }
109
- }
110
-
111
- return parts.join('\n')
112
- }
113
-
114
- /**
115
- * useHead composable.
116
- *
117
- * - During SSR: pushes to the request-scoped collector (set up via beginHeadCollection)
118
- * - On the client: imperatively updates document.title and meta/link tags
119
- */
120
- export function useHead(input: HeadInput): void {
121
- const collector = _g[_KEY] as HeadInput[] | null | undefined
122
- if (collector != null) {
123
- // SSR mode: push to the shared globalThis collector
124
- collector.push(input)
125
- } else if (typeof document !== 'undefined') {
126
- // Client-side
127
- if (input.title !== undefined) {
128
- document.title = input.title
129
- }
130
-
131
- if (input.meta) {
132
- for (const meta of input.meta) {
133
- const name = meta.name ?? meta.property
134
- if (name) {
135
- // Try to update existing meta, or create a new one
136
- let el = document.querySelector(
137
- meta.name ? `meta[name="${meta.name}"]` : `meta[property="${meta.property}"]`,
138
- )
139
- if (!el) {
140
- el = document.createElement('meta')
141
- document.head.appendChild(el)
142
- }
143
- for (const [k, v] of Object.entries(meta)) {
144
- el.setAttribute(k, v)
145
- }
146
- }
147
- }
148
- }
149
-
150
- if (input.link) {
151
- for (const link of input.link) {
152
- const rel = link.rel
153
- const href = link.href
154
- if (rel && href) {
155
- let el = document.querySelector(`link[rel="${rel}"][href="${href}"]`)
156
- if (!el) {
157
- el = document.createElement('link')
158
- document.head.appendChild(el)
159
- }
160
- for (const [k, v] of Object.entries(link)) {
161
- el.setAttribute(k, v)
162
- }
163
- }
164
- }
165
- }
166
-
167
- if (input.script) {
168
- for (const script of input.script) {
169
- const { innerHTML, src, ...rest } = script as Record<string, string>
170
- // Only add if not already present (by src)
171
- if (src && !document.querySelector(`script[src="${src}"]`)) {
172
- const el = document.createElement('script')
173
- el.src = src
174
- for (const [k, v] of Object.entries(rest)) {
175
- el.setAttribute(k, v)
176
- }
177
- document.head.appendChild(el)
178
- } else if (innerHTML && !src) {
179
- const el = document.createElement('script')
180
- el.textContent = innerHTML
181
- for (const [k, v] of Object.entries(rest)) {
182
- el.setAttribute(k, v)
183
- }
184
- document.head.appendChild(el)
185
- }
186
- }
187
- }
188
- }
189
- }
190
-
191
- // ─── Helpers ────────────────────────────────────────────────────────────────
192
-
193
- function escapeHtml(str: string): string {
194
- return str
195
- .replace(/&/g, '&amp;')
196
- .replace(/</g, '&lt;')
197
- .replace(/>/g, '&gt;')
198
- }
199
-
200
- function escapeAttr(value: string): string {
201
- return value
202
- .replace(/&/g, '&amp;')
203
- .replace(/"/g, '&quot;')
204
- .replace(/</g, '&lt;')
205
- .replace(/>/g, '&gt;')
206
- }
207
-
208
- function renderAttrs(attrs: Record<string, string>): string {
209
- return Object.entries(attrs)
210
- .map(([k, v]) => (v === '' ? k : `${k}="${escapeAttr(v)}"`))
211
- .join(' ')
212
- }
@@ -1,49 +0,0 @@
1
- import { inject } from '@jasonshimmy/custom-elements-runtime'
2
-
3
- const _g = globalThis as Record<string, unknown>
4
- const _PROVIDES_KEY = '__cerPluginProvides'
5
-
6
- /**
7
- * useInject — reads a value provided by a plugin via plugin.setup()'s provide().
8
- *
9
- * Works consistently across all rendering modes:
10
- *
11
- * - **SPA/Client**: Uses inject() from the component context tree (established
12
- * by cer-layout-view calling provide() for each plugin-provided value).
13
- *
14
- * - **SSR/SSG**: Reads from globalThis.__cerPluginProvides, populated when the
15
- * server entry runs plugin.setup() before rendering.
16
- *
17
- * @example
18
- * ```ts
19
- * // In a plugin (app/plugins/my-plugin.ts):
20
- * export default {
21
- * name: 'my-plugin',
22
- * setup({ provide }) {
23
- * provide('my-service', { greet: () => 'hello' })
24
- * }
25
- * }
26
- *
27
- * // In a component:
28
- * component('my-page', () => {
29
- * const service = useInject<{ greet(): string }>('my-service')
30
- * })
31
- * ```
32
- */
33
- export function useInject<T = unknown>(key: string, defaultValue?: T): T | undefined {
34
- // Server-side (SSR/SSG): read from the global plugin provides map.
35
- // __cerPluginProvides is populated by the server entry before the render pass.
36
- if (typeof document === 'undefined') {
37
- const pluginProvides = _g[_PROVIDES_KEY] as Map<PropertyKey, unknown> | undefined
38
- const value = pluginProvides?.get(key)
39
- return value !== undefined ? (value as T) : defaultValue
40
- }
41
-
42
- // Client-side: inject() walks the component context tree established by
43
- // cer-layout-view's provide() calls. Falls back to __cerPluginProvides for
44
- // reads before cer-layout-view mounts (e.g. during plugin-registered components).
45
- const value = inject<T>(key)
46
- if (value !== undefined) return value
47
- const pluginProvides = _g[_PROVIDES_KEY] as Map<PropertyKey, unknown> | undefined
48
- return (pluginProvides?.get(key) as T | undefined) ?? defaultValue
49
- }