@jasonshimmy/vite-plugin-cer-app 0.23.2 → 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 (392) 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/create/index.d.ts +11 -1
  18. package/dist/cli/create/index.d.ts.map +1 -1
  19. package/dist/cli/create/index.js +135 -65
  20. package/dist/cli/create/index.js.map +1 -1
  21. package/dist/cli/create/templates/spa/package.json.tpl +5 -3
  22. package/dist/cli/create/templates/ssg/cer.config.ts.tpl +2 -0
  23. package/dist/cli/create/templates/ssg/package.json.tpl +5 -3
  24. package/dist/cli/create/templates/ssr/package.json.tpl +5 -3
  25. package/dist/cli/index.js +2 -0
  26. package/dist/cli/index.js.map +1 -1
  27. package/dist/index.d.ts +3 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/plugin/build-ssg.d.ts +22 -6
  30. package/dist/plugin/build-ssg.d.ts.map +1 -1
  31. package/dist/plugin/build-ssg.js +227 -17
  32. package/dist/plugin/build-ssg.js.map +1 -1
  33. package/dist/plugin/content/parser.d.ts.map +1 -1
  34. package/dist/plugin/content/parser.js +54 -10
  35. package/dist/plugin/content/parser.js.map +1 -1
  36. package/dist/plugin/dev-server.d.ts +4 -1
  37. package/dist/plugin/dev-server.d.ts.map +1 -1
  38. package/dist/plugin/dev-server.js.map +1 -1
  39. package/dist/plugin/dts-generator.d.ts.map +1 -1
  40. package/dist/plugin/dts-generator.js +50 -8
  41. package/dist/plugin/dts-generator.js.map +1 -1
  42. package/dist/plugin/generated-dir.d.ts +2 -0
  43. package/dist/plugin/generated-dir.d.ts.map +1 -1
  44. package/dist/plugin/generated-dir.js +17 -6
  45. package/dist/plugin/generated-dir.js.map +1 -1
  46. package/dist/plugin/html-post-process.d.ts +6 -0
  47. package/dist/plugin/html-post-process.d.ts.map +1 -1
  48. package/dist/plugin/html-post-process.js +24 -0
  49. package/dist/plugin/html-post-process.js.map +1 -1
  50. package/dist/plugin/index.d.ts.map +1 -1
  51. package/dist/plugin/index.js +60 -13
  52. package/dist/plugin/index.js.map +1 -1
  53. package/dist/plugin/transforms/auto-import.d.ts.map +1 -1
  54. package/dist/plugin/transforms/auto-import.js +7 -0
  55. package/dist/plugin/transforms/auto-import.js.map +1 -1
  56. package/dist/plugin/transforms/prune-runtime-css.d.ts +8 -0
  57. package/dist/plugin/transforms/prune-runtime-css.d.ts.map +1 -0
  58. package/dist/plugin/transforms/prune-runtime-css.js +40 -0
  59. package/dist/plugin/transforms/prune-runtime-css.js.map +1 -0
  60. package/dist/runtime/app-template.d.ts +1 -1
  61. package/dist/runtime/app-template.d.ts.map +1 -1
  62. package/dist/runtime/app-template.js +206 -36
  63. package/dist/runtime/app-template.js.map +1 -1
  64. package/dist/runtime/composables/index.d.ts +4 -0
  65. package/dist/runtime/composables/index.d.ts.map +1 -1
  66. package/dist/runtime/composables/index.js +2 -0
  67. package/dist/runtime/composables/index.js.map +1 -1
  68. package/dist/runtime/composables/use-active-headings.d.ts +24 -0
  69. package/dist/runtime/composables/use-active-headings.d.ts.map +1 -0
  70. package/dist/runtime/composables/use-active-headings.js +112 -0
  71. package/dist/runtime/composables/use-active-headings.js.map +1 -0
  72. package/dist/runtime/composables/use-content-page.d.ts +45 -0
  73. package/dist/runtime/composables/use-content-page.d.ts.map +1 -0
  74. package/dist/runtime/composables/use-content-page.js +179 -0
  75. package/dist/runtime/composables/use-content-page.js.map +1 -0
  76. package/dist/runtime/composables/use-content-search.d.ts.map +1 -1
  77. package/dist/runtime/composables/use-content-search.js +1 -5
  78. package/dist/runtime/composables/use-content-search.js.map +1 -1
  79. package/dist/runtime/composables/use-fetch.d.ts +2 -1
  80. package/dist/runtime/composables/use-fetch.d.ts.map +1 -1
  81. package/dist/runtime/composables/use-fetch.js +112 -43
  82. package/dist/runtime/composables/use-fetch.js.map +1 -1
  83. package/dist/runtime/composables/use-route.d.ts.map +1 -1
  84. package/dist/runtime/composables/use-route.js +39 -11
  85. package/dist/runtime/composables/use-route.js.map +1 -1
  86. package/dist/runtime/content/client.js +2 -2
  87. package/dist/runtime/content/client.js.map +1 -1
  88. package/dist/runtime/entry-server-template.d.ts +16 -1
  89. package/dist/runtime/entry-server-template.d.ts.map +1 -1
  90. package/dist/runtime/entry-server-template.js +120 -13
  91. package/dist/runtime/entry-server-template.js.map +1 -1
  92. package/dist/types/config.d.ts +45 -4
  93. package/dist/types/config.d.ts.map +1 -1
  94. package/dist/types/config.js.map +1 -1
  95. package/dist/types/middleware.d.ts +2 -2
  96. package/dist/types/middleware.d.ts.map +1 -1
  97. package/package.json +22 -19
  98. package/.github/copilot-instructions.md +0 -169
  99. package/.github/workflows/publish.yml +0 -257
  100. package/.nvmrc +0 -1
  101. package/CHANGELOG.md +0 -236
  102. package/CLAUDE.md +0 -113
  103. package/commits.txt +0 -1
  104. package/cypress.config.ts +0 -16
  105. package/docs/authentication.md +0 -285
  106. package/docs/cli.md +0 -324
  107. package/docs/components.md +0 -237
  108. package/docs/composables.md +0 -907
  109. package/docs/configuration.md +0 -696
  110. package/docs/content.md +0 -570
  111. package/docs/data-loading.md +0 -336
  112. package/docs/getting-started.md +0 -237
  113. package/docs/head-management.md +0 -212
  114. package/docs/i18n.md +0 -159
  115. package/docs/layouts.md +0 -224
  116. package/docs/middleware.md +0 -234
  117. package/docs/plugins.md +0 -170
  118. package/docs/rendering-modes.md +0 -377
  119. package/docs/routing.md +0 -485
  120. package/docs/server-api.md +0 -253
  121. package/docs/testing.md +0 -455
  122. package/docs/use-fetch.md +0 -183
  123. package/e2e/cypress/e2e/api.cy.ts +0 -123
  124. package/e2e/cypress/e2e/async-component.cy.ts +0 -27
  125. package/e2e/cypress/e2e/component-splitting.cy.ts +0 -50
  126. package/e2e/cypress/e2e/content.cy.ts +0 -425
  127. package/e2e/cypress/e2e/cookie.cy.ts +0 -85
  128. package/e2e/cypress/e2e/data.cy.ts +0 -158
  129. package/e2e/cypress/e2e/error-boundary.cy.ts +0 -88
  130. package/e2e/cypress/e2e/fouc.cy.ts +0 -254
  131. package/e2e/cypress/e2e/group-meta.cy.ts +0 -36
  132. package/e2e/cypress/e2e/head.cy.ts +0 -89
  133. package/e2e/cypress/e2e/i18n.cy.ts +0 -79
  134. package/e2e/cypress/e2e/interactive.cy.ts +0 -137
  135. package/e2e/cypress/e2e/isr-nested-runtime.cy.ts +0 -102
  136. package/e2e/cypress/e2e/jit-css-custom-colors.cy.ts +0 -43
  137. package/e2e/cypress/e2e/middleware-chain.cy.ts +0 -89
  138. package/e2e/cypress/e2e/middleware-status.cy.ts +0 -30
  139. package/e2e/cypress/e2e/middleware.cy.ts +0 -45
  140. package/e2e/cypress/e2e/navigate.cy.ts +0 -39
  141. package/e2e/cypress/e2e/observability.cy.ts +0 -77
  142. package/e2e/cypress/e2e/per-route-error.cy.ts +0 -42
  143. package/e2e/cypress/e2e/per-route-render.cy.ts +0 -70
  144. package/e2e/cypress/e2e/preview-hardening.cy.ts +0 -88
  145. package/e2e/cypress/e2e/routes.cy.ts +0 -128
  146. package/e2e/cypress/e2e/seo-meta.cy.ts +0 -108
  147. package/e2e/cypress/e2e/server-middleware.cy.ts +0 -46
  148. package/e2e/cypress/e2e/session-rotation.cy.ts +0 -25
  149. package/e2e/cypress/e2e/session.cy.ts +0 -73
  150. package/e2e/cypress/e2e/ssr-render-error.cy.ts +0 -68
  151. package/e2e/cypress/e2e/synthetic-404.cy.ts +0 -45
  152. package/e2e/cypress/e2e/use-page-data.cy.ts +0 -122
  153. package/e2e/cypress/e2e/use-state.cy.ts +0 -138
  154. package/e2e/cypress/support/commands.ts +0 -120
  155. package/e2e/cypress/support/e2e.ts +0 -10
  156. package/e2e/cypress/tsconfig.json +0 -14
  157. package/e2e/kitchen-sink/app/components/ks-badge.ts +0 -8
  158. package/e2e/kitchen-sink/app/composables/useKsCounter.ts +0 -9
  159. package/e2e/kitchen-sink/app/error.ts +0 -18
  160. package/e2e/kitchen-sink/app/layouts/admin.ts +0 -13
  161. package/e2e/kitchen-sink/app/layouts/default.ts +0 -25
  162. package/e2e/kitchen-sink/app/layouts/group.ts +0 -9
  163. package/e2e/kitchen-sink/app/layouts/minimal.ts +0 -7
  164. package/e2e/kitchen-sink/app/loading.ts +0 -9
  165. package/e2e/kitchen-sink/app/middleware/auth.ts +0 -9
  166. package/e2e/kitchen-sink/app/middleware/custom-log.ts +0 -4
  167. package/e2e/kitchen-sink/app/middleware/group-auth.ts +0 -7
  168. package/e2e/kitchen-sink/app/middleware/logger.ts +0 -14
  169. package/e2e/kitchen-sink/app/pages/(auth)/login.ts +0 -13
  170. package/e2e/kitchen-sink/app/pages/(auth)/protected.ts +0 -16
  171. package/e2e/kitchen-sink/app/pages/404.ts +0 -9
  172. package/e2e/kitchen-sink/app/pages/about.ts +0 -17
  173. package/e2e/kitchen-sink/app/pages/admin/_layout.ts +0 -1
  174. package/e2e/kitchen-sink/app/pages/admin/dashboard.ts +0 -11
  175. package/e2e/kitchen-sink/app/pages/async-component-error-test.ts +0 -18
  176. package/e2e/kitchen-sink/app/pages/async-component-test.ts +0 -18
  177. package/e2e/kitchen-sink/app/pages/blog/[slug].ts +0 -59
  178. package/e2e/kitchen-sink/app/pages/blog/index.ts +0 -51
  179. package/e2e/kitchen-sink/app/pages/content-blog.ts +0 -37
  180. package/e2e/kitchen-sink/app/pages/content-doc.ts +0 -42
  181. package/e2e/kitchen-sink/app/pages/content-fallback.ts +0 -36
  182. package/e2e/kitchen-sink/app/pages/content-guides.ts +0 -35
  183. package/e2e/kitchen-sink/app/pages/content-index.ts +0 -39
  184. package/e2e/kitchen-sink/app/pages/content-search.ts +0 -36
  185. package/e2e/kitchen-sink/app/pages/cookie-test.ts +0 -22
  186. package/e2e/kitchen-sink/app/pages/counter.ts +0 -17
  187. package/e2e/kitchen-sink/app/pages/group-auth-blocked.ts +0 -9
  188. package/e2e/kitchen-sink/app/pages/group-meta-test/_layout.ts +0 -6
  189. package/e2e/kitchen-sink/app/pages/group-meta-test/custom-mw.ts +0 -14
  190. package/e2e/kitchen-sink/app/pages/group-meta-test/page.ts +0 -9
  191. package/e2e/kitchen-sink/app/pages/group-meta-test/protected.ts +0 -8
  192. package/e2e/kitchen-sink/app/pages/head.ts +0 -20
  193. package/e2e/kitchen-sink/app/pages/i18n-test.ts +0 -14
  194. package/e2e/kitchen-sink/app/pages/index.ts +0 -27
  195. package/e2e/kitchen-sink/app/pages/isr-test.ts +0 -17
  196. package/e2e/kitchen-sink/app/pages/items/[id].ts +0 -20
  197. package/e2e/kitchen-sink/app/pages/jit-css-custom-colors-test.ts +0 -14
  198. package/e2e/kitchen-sink/app/pages/loader-error-test.ts +0 -13
  199. package/e2e/kitchen-sink/app/pages/loader-props-test.ts +0 -19
  200. package/e2e/kitchen-sink/app/pages/loader-response-error-test.ts +0 -13
  201. package/e2e/kitchen-sink/app/pages/middleware-chain-test.ts +0 -12
  202. package/e2e/kitchen-sink/app/pages/middleware-status-test.ts +0 -9
  203. package/e2e/kitchen-sink/app/pages/navigate-test.ts +0 -16
  204. package/e2e/kitchen-sink/app/pages/observability-test.ts +0 -25
  205. package/e2e/kitchen-sink/app/pages/per-route-error-test.error.ts +0 -13
  206. package/e2e/kitchen-sink/app/pages/per-route-error-test.ts +0 -15
  207. package/e2e/kitchen-sink/app/pages/plugin-dsd-test.ts +0 -10
  208. package/e2e/kitchen-sink/app/pages/render-error-test.ts +0 -12
  209. package/e2e/kitchen-sink/app/pages/render-server-test.ts +0 -12
  210. package/e2e/kitchen-sink/app/pages/render-spa-test.ts +0 -12
  211. package/e2e/kitchen-sink/app/pages/route-info.ts +0 -14
  212. package/e2e/kitchen-sink/app/pages/seo-test.ts +0 -23
  213. package/e2e/kitchen-sink/app/pages/session-rotation-test.ts +0 -25
  214. package/e2e/kitchen-sink/app/pages/use-state-test.ts +0 -37
  215. package/e2e/kitchen-sink/app/plugins/01.setup.ts +0 -7
  216. package/e2e/kitchen-sink/app/plugins/02.ks-plugin-components.ts +0 -27
  217. package/e2e/kitchen-sink/cer-auto-imports.d.ts +0 -51
  218. package/e2e/kitchen-sink/cer-env.d.ts +0 -35
  219. package/e2e/kitchen-sink/cer-tsconfig.json +0 -30
  220. package/e2e/kitchen-sink/cer.config.ts +0 -43
  221. package/e2e/kitchen-sink/content/01.guides/01.index.md +0 -8
  222. package/e2e/kitchen-sink/content/01.guides/02.intro.md +0 -8
  223. package/e2e/kitchen-sink/content/01.guides/10.advanced.md +0 -8
  224. package/e2e/kitchen-sink/content/blog/2026-04-01-hello.md +0 -26
  225. package/e2e/kitchen-sink/content/blog/2026-04-02-draft.md +0 -10
  226. package/e2e/kitchen-sink/content/blog/index.md +0 -8
  227. package/e2e/kitchen-sink/content/blog/no-frontmatter.md +0 -7
  228. package/e2e/kitchen-sink/content/docs/getting-started.md +0 -48
  229. package/e2e/kitchen-sink/content/index.md +0 -16
  230. package/e2e/kitchen-sink/server/api/echo.ts +0 -12
  231. package/e2e/kitchen-sink/server/api/health.ts +0 -3
  232. package/e2e/kitchen-sink/server/api/posts/[slug].ts +0 -11
  233. package/e2e/kitchen-sink/server/api/posts/index.ts +0 -5
  234. package/e2e/kitchen-sink/server/api/session.ts +0 -23
  235. package/e2e/kitchen-sink/server/data/posts.ts +0 -21
  236. package/e2e/kitchen-sink/server/middleware/01-headers.ts +0 -6
  237. package/e2e/kitchen-sink/server/middleware/02-status-test.ts +0 -10
  238. package/e2e/kitchen-sink/tsconfig.json +0 -3
  239. package/e2e/scripts/clean.mjs +0 -14
  240. package/eslint.config.ts +0 -22
  241. package/src/__tests__/cli/adapters/cloudflare-worker.integration.test.ts +0 -132
  242. package/src/__tests__/cli/adapters/cloudflare.test.ts +0 -332
  243. package/src/__tests__/cli/adapters/netlify-bridge.integration.test.ts +0 -139
  244. package/src/__tests__/cli/adapters/netlify.test.ts +0 -286
  245. package/src/__tests__/cli/adapters/vercel-launcher.integration.test.ts +0 -129
  246. package/src/__tests__/cli/adapters/vercel.test.ts +0 -263
  247. package/src/__tests__/cli/preview-hardening.test.ts +0 -265
  248. package/src/__tests__/cli/preview-isr.test.ts +0 -320
  249. package/src/__tests__/index.test.ts +0 -21
  250. package/src/__tests__/plugin/app-template.test.ts +0 -166
  251. package/src/__tests__/plugin/build-ssg-render.test.ts +0 -155
  252. package/src/__tests__/plugin/build-ssg.test.ts +0 -598
  253. package/src/__tests__/plugin/build-ssr.test.ts +0 -192
  254. package/src/__tests__/plugin/cer-app-plugin.test.ts +0 -552
  255. package/src/__tests__/plugin/content/emitter.test.ts +0 -117
  256. package/src/__tests__/plugin/content/loader.test.ts +0 -154
  257. package/src/__tests__/plugin/content/parser.test.ts +0 -381
  258. package/src/__tests__/plugin/content/path-utils.test.ts +0 -69
  259. package/src/__tests__/plugin/content/search.test.ts +0 -119
  260. package/src/__tests__/plugin/dev-server.test.ts +0 -813
  261. package/src/__tests__/plugin/dts-generator.test.ts +0 -394
  262. package/src/__tests__/plugin/entry-server-template.test.ts +0 -385
  263. package/src/__tests__/plugin/generated-dir.test.ts +0 -144
  264. package/src/__tests__/plugin/html-post-process.test.ts +0 -146
  265. package/src/__tests__/plugin/path-utils.test.ts +0 -411
  266. package/src/__tests__/plugin/resolve-config.test.ts +0 -233
  267. package/src/__tests__/plugin/scanner.test.ts +0 -186
  268. package/src/__tests__/plugin/transforms/auto-import.test.ts +0 -510
  269. package/src/__tests__/plugin/transforms/head-inject.test.ts +0 -178
  270. package/src/__tests__/plugin/virtual/composables.test.ts +0 -57
  271. package/src/__tests__/plugin/virtual/content-components.test.ts +0 -66
  272. package/src/__tests__/plugin/virtual/error.test.ts +0 -71
  273. package/src/__tests__/plugin/virtual/layouts.test.ts +0 -70
  274. package/src/__tests__/plugin/virtual/loading.test.ts +0 -72
  275. package/src/__tests__/plugin/virtual/middleware.test.ts +0 -83
  276. package/src/__tests__/plugin/virtual/plugins.test.ts +0 -129
  277. package/src/__tests__/plugin/virtual/routes.test.ts +0 -884
  278. package/src/__tests__/plugin/virtual/server-api.test.ts +0 -164
  279. package/src/__tests__/plugin/virtual/server-middleware.test.ts +0 -102
  280. package/src/__tests__/runtime/app-template.test.ts +0 -208
  281. package/src/__tests__/runtime/define-middleware.test.ts +0 -64
  282. package/src/__tests__/runtime/define-server-middleware.test.ts +0 -54
  283. package/src/__tests__/runtime/entry-client-template.test.ts +0 -61
  284. package/src/__tests__/runtime/isr-handler.test.ts +0 -338
  285. package/src/__tests__/runtime/oauth/handler.test.ts +0 -358
  286. package/src/__tests__/runtime/oauth/providers.test.ts +0 -47
  287. package/src/__tests__/runtime/oauth-index.test.ts +0 -43
  288. package/src/__tests__/runtime/use-auth.test.ts +0 -169
  289. package/src/__tests__/runtime/use-content-search-composable.test.ts +0 -405
  290. package/src/__tests__/runtime/use-content-search.test.ts +0 -163
  291. package/src/__tests__/runtime/use-content.test.ts +0 -226
  292. package/src/__tests__/runtime/use-cookie.test.ts +0 -277
  293. package/src/__tests__/runtime/use-fetch-component.test.ts +0 -214
  294. package/src/__tests__/runtime/use-fetch.test.ts +0 -377
  295. package/src/__tests__/runtime/use-head.test.ts +0 -311
  296. package/src/__tests__/runtime/use-inject-client.test.ts +0 -67
  297. package/src/__tests__/runtime/use-inject.test.ts +0 -66
  298. package/src/__tests__/runtime/use-locale.test.ts +0 -152
  299. package/src/__tests__/runtime/use-navigate.test.ts +0 -121
  300. package/src/__tests__/runtime/use-page-data.test.ts +0 -298
  301. package/src/__tests__/runtime/use-route.test.ts +0 -156
  302. package/src/__tests__/runtime/use-runtime-config.test.ts +0 -182
  303. package/src/__tests__/runtime/use-seo-meta.test.ts +0 -109
  304. package/src/__tests__/runtime/use-session.test.ts +0 -320
  305. package/src/__tests__/runtime/use-state.test.ts +0 -340
  306. package/src/__tests__/types/config.test.ts +0 -18
  307. package/src/cli/adapters/cloudflare.ts +0 -335
  308. package/src/cli/adapters/netlify.ts +0 -326
  309. package/src/cli/adapters/vercel.ts +0 -311
  310. package/src/cli/commands/adapt.ts +0 -84
  311. package/src/cli/commands/build.ts +0 -165
  312. package/src/cli/commands/dev.ts +0 -125
  313. package/src/cli/commands/generate.ts +0 -91
  314. package/src/cli/commands/preview-isr.ts +0 -172
  315. package/src/cli/commands/preview.ts +0 -523
  316. package/src/cli/create/index.ts +0 -275
  317. package/src/cli/create/templates/shared/.gitignore.tpl +0 -25
  318. package/src/cli/create/templates/shared/app/layouts/default.ts.tpl +0 -15
  319. package/src/cli/create/templates/shared/app/pages/index.ts.tpl +0 -8
  320. package/src/cli/create/templates/shared/index.html.tpl +0 -12
  321. package/src/cli/create/templates/shared/tsconfig.json.tpl +0 -6
  322. package/src/cli/create/templates/spa/cer.config.ts.tpl +0 -6
  323. package/src/cli/create/templates/spa/package.json.tpl +0 -18
  324. package/src/cli/create/templates/ssg/cer.config.ts.tpl +0 -10
  325. package/src/cli/create/templates/ssg/package.json.tpl +0 -19
  326. package/src/cli/create/templates/ssr/cer.config.ts.tpl +0 -6
  327. package/src/cli/create/templates/ssr/package.json.tpl +0 -18
  328. package/src/cli/index.ts +0 -22
  329. package/src/index.ts +0 -20
  330. package/src/plugin/build-ssg.ts +0 -403
  331. package/src/plugin/build-ssr.ts +0 -134
  332. package/src/plugin/content/emitter.ts +0 -50
  333. package/src/plugin/content/index.ts +0 -233
  334. package/src/plugin/content/parser.ts +0 -259
  335. package/src/plugin/content/path-utils.ts +0 -55
  336. package/src/plugin/content/scanner.ts +0 -26
  337. package/src/plugin/content/search.ts +0 -28
  338. package/src/plugin/dev-server.ts +0 -379
  339. package/src/plugin/dts-generator.ts +0 -273
  340. package/src/plugin/generated-dir.ts +0 -121
  341. package/src/plugin/html-post-process.ts +0 -96
  342. package/src/plugin/index.ts +0 -584
  343. package/src/plugin/path-utils.ts +0 -186
  344. package/src/plugin/scanner.ts +0 -65
  345. package/src/plugin/transforms/auto-import.ts +0 -206
  346. package/src/plugin/transforms/head-inject.ts +0 -161
  347. package/src/plugin/virtual/composables.ts +0 -28
  348. package/src/plugin/virtual/content-components.ts +0 -83
  349. package/src/plugin/virtual/error.ts +0 -34
  350. package/src/plugin/virtual/layouts.ts +0 -41
  351. package/src/plugin/virtual/loading.ts +0 -32
  352. package/src/plugin/virtual/middleware.ts +0 -45
  353. package/src/plugin/virtual/plugins.ts +0 -46
  354. package/src/plugin/virtual/routes.ts +0 -514
  355. package/src/plugin/virtual/server-api.ts +0 -85
  356. package/src/plugin/virtual/server-middleware.ts +0 -44
  357. package/src/runtime/app-template.ts +0 -377
  358. package/src/runtime/composables/define-middleware.ts +0 -17
  359. package/src/runtime/composables/define-server-middleware.ts +0 -18
  360. package/src/runtime/composables/index.ts +0 -27
  361. package/src/runtime/composables/use-auth.ts +0 -125
  362. package/src/runtime/composables/use-content-search.ts +0 -205
  363. package/src/runtime/composables/use-content.ts +0 -146
  364. package/src/runtime/composables/use-cookie.ts +0 -135
  365. package/src/runtime/composables/use-fetch.ts +0 -438
  366. package/src/runtime/composables/use-head.ts +0 -234
  367. package/src/runtime/composables/use-inject.ts +0 -49
  368. package/src/runtime/composables/use-locale.ts +0 -109
  369. package/src/runtime/composables/use-navigate.ts +0 -58
  370. package/src/runtime/composables/use-page-data.ts +0 -93
  371. package/src/runtime/composables/use-route.ts +0 -71
  372. package/src/runtime/composables/use-runtime-config.ts +0 -123
  373. package/src/runtime/composables/use-seo-meta.ts +0 -80
  374. package/src/runtime/composables/use-session.ts +0 -186
  375. package/src/runtime/composables/use-state.ts +0 -92
  376. package/src/runtime/content/client.ts +0 -168
  377. package/src/runtime/entry-client-template.ts +0 -39
  378. package/src/runtime/entry-server-template.ts +0 -493
  379. package/src/runtime/isr-handler.ts +0 -184
  380. package/src/runtime/oauth/handler.ts +0 -407
  381. package/src/runtime/oauth/index.ts +0 -49
  382. package/src/runtime/oauth/providers.ts +0 -38
  383. package/src/types/api.ts +0 -40
  384. package/src/types/config.ts +0 -379
  385. package/src/types/content.ts +0 -66
  386. package/src/types/index.ts +0 -7
  387. package/src/types/middleware.ts +0 -63
  388. package/src/types/page.ts +0 -117
  389. package/src/types/plugin.ts +0 -34
  390. package/tsconfig.build.json +0 -10
  391. package/tsconfig.json +0 -19
  392. package/vitest.config.ts +0 -33
@@ -1,233 +0,0 @@
1
- import { join } from 'pathe'
2
- import { existsSync } from 'node:fs'
3
- import type { Plugin, ViteDevServer, ResolvedConfig } from 'vite'
4
- import type { CerContentConfig } from '../../types/content.js'
5
- import type { ContentItem } from '../../types/content.js'
6
- import { scanContentFiles } from './scanner.js'
7
- import { parseContentFileAsync, toContentMeta } from './parser.js'
8
- import { emitContentFiles } from './emitter.js'
9
- import { buildSearchIndex } from './search.js'
10
- import type { IncomingMessage, ServerResponse } from 'node:http'
11
-
12
- /** The globalThis key used to share the in-memory content store with ContentClient. */
13
- export const CONTENT_STORE_KEY = '__CER_CONTENT_STORE__'
14
-
15
- /**
16
- * Resolves the absolute content directory from the framework config.
17
- * `dir` is relative to the project root (not the app source directory),
18
- * so `content/` sits alongside `app/`, `server/`, and `public/`.
19
- */
20
- export function resolveContentDir(root: string, contentConfig?: CerContentConfig): string {
21
- const dir = contentConfig?.dir ?? 'content'
22
- return join(root, dir)
23
- }
24
-
25
- /**
26
- * Loads all content files from `contentDir`, parses them concurrently, and
27
- * returns the full `ContentItem[]`. Excludes drafts unless `includeDrafts: true`
28
- * is set in the content config. Uses async I/O + `Promise.all` for concurrent
29
- * disk reads, which is significantly faster than sequential `readFileSync` at
30
- * 10k+ pages.
31
- */
32
- export async function loadContentStore(
33
- contentDir: string,
34
- isDraft: boolean,
35
- ): Promise<ContentItem[]> {
36
- if (!existsSync(contentDir)) return []
37
-
38
- const files = await scanContentFiles(contentDir)
39
-
40
- const results = await Promise.all(
41
- files.map(async (file) => {
42
- try {
43
- const item = await parseContentFileAsync(file, contentDir)
44
- // Skip drafts unless the user explicitly opted in via drafts: true
45
- if (!isDraft && item.draft === true) return null
46
- return item
47
- } catch (err) {
48
- // Warn and skip unparseable / invalid files so one bad file does not
49
- // abort the entire build. Invalid JSON files produce a descriptive
50
- // error from the parser; other errors are also surfaced here.
51
- console.warn(
52
- `[cer-app] Skipping content file (parse error): ${file.filePath}\n ${(err as Error).message}`,
53
- )
54
- return null
55
- }
56
- }),
57
- )
58
-
59
- return results.filter((item): item is ContentItem => item !== null)
60
- }
61
-
62
- // ─── Dev server derived-data cache ───────────────────────────────────────────
63
- // Re-computing the manifest JSON and search index on every request is O(n) at
64
- // 10k+ pages. Cache them as serialised strings, keyed by the store array
65
- // reference. When refreshStore() replaces the store, the reference changes
66
- // and the cache is automatically invalidated on the next request.
67
-
68
- let _devCacheStoreRef: ContentItem[] | null = null
69
- let _devCacheManifestJson: string | null = null
70
- let _devCacheSearchIndexJson: string | null = null
71
-
72
- function invalidateDevCaches(): void {
73
- _devCacheStoreRef = null
74
- _devCacheManifestJson = null
75
- _devCacheSearchIndexJson = null
76
- }
77
-
78
- function ensureDevCache(store: ContentItem[]): void {
79
- if (_devCacheStoreRef === store) return
80
- _devCacheStoreRef = store
81
- _devCacheManifestJson = JSON.stringify(store.map(toContentMeta))
82
- _devCacheSearchIndexJson = buildSearchIndex(store)
83
- }
84
-
85
- /**
86
- * Registers the `/_content/*` dev middleware that serves content from the
87
- * in-memory store populated by `buildStart`.
88
- */
89
- function registerDevMiddleware(server: ViteDevServer, _contentDir: string): void {
90
- server.middlewares.use(async (req: IncomingMessage, res: ServerResponse, next: () => void) => {
91
- const url = (req as { url?: string }).url ?? ''
92
- if (!url.startsWith('/_content/')) {
93
- next()
94
- return
95
- }
96
-
97
- const g = globalThis as Record<string, unknown>
98
- const store = g[CONTENT_STORE_KEY] as ContentItem[] | undefined
99
-
100
- if (!store) {
101
- res.statusCode = 503
102
- res.end('Content store not ready')
103
- return
104
- }
105
-
106
- // Rebuild derived caches if the store reference has changed (post-HMR).
107
- ensureDevCache(store)
108
-
109
- const suffix = url.slice('/_content/'.length).split('?')[0]
110
-
111
- if (suffix === 'manifest.json') {
112
- res.setHeader('Content-Type', 'application/json')
113
- res.setHeader('Cache-Control', 'no-store')
114
- res.end(_devCacheManifestJson!)
115
- return
116
- }
117
-
118
- if (suffix === 'search-index.json') {
119
- res.setHeader('Content-Type', 'application/json')
120
- res.setHeader('Cache-Control', 'no-store')
121
- res.end(_devCacheSearchIndexJson!)
122
- return
123
- }
124
-
125
- // Individual document: suffix is like "blog/hello.json" or "index.json"
126
- if (suffix.endsWith('.json')) {
127
- const rawPath = suffix.slice(0, -'.json'.length) // strip .json
128
- // Reverse the contentPathToFile mapping:
129
- // "index" → "/" and "blog/hello" → "/blog/hello"
130
- const _path = rawPath === 'index' ? '/' : '/' + rawPath
131
- const item = store.find((i) => i._path === _path)
132
- if (item) {
133
- res.setHeader('Content-Type', 'application/json')
134
- res.setHeader('Cache-Control', 'no-store')
135
- res.end(JSON.stringify(item))
136
- return
137
- }
138
- }
139
-
140
- res.statusCode = 404
141
- res.end('Not found')
142
- })
143
- }
144
-
145
- /**
146
- * `cerContent()` — Vite sub-plugin that provides the file-based content layer.
147
- *
148
- * - `buildStart`: scans and parses the content directory, populates `globalThis.__CER_CONTENT_STORE__`
149
- * - `configureServer`: registers `/_content/*` dev middleware from the same store; watches for HMR
150
- * - `closeBundle`: emits `_content/*.json` into the Vite output directory for this build
151
- * (e.g. `dist/client/_content/` for SSR client, `dist/_content/` for SPA/SSG)
152
- *
153
- * The content directory is resolved relative to the **project root** (not the
154
- * app source directory), so a default config produces `{root}/content/` — at
155
- * the same level as `app/`, `server/`, and `public/`.
156
- */
157
- export function cerContent(
158
- contentConfig?: CerContentConfig,
159
- ): Plugin {
160
- const contentDirName = contentConfig?.dir ?? 'content'
161
- const includeDrafts = contentConfig?.drafts ?? false
162
- // Resolved in configResolved; empty until then.
163
- let _resolvedContentDir = ''
164
- let _resolvedOutDir = ''
165
- let _isSsr = false
166
-
167
- const plugin: Plugin = {
168
- name: '@jasonshimmy/vite-plugin-cer-app:content',
169
-
170
- configResolved(resolvedConfig: ResolvedConfig) {
171
- // Content lives at {root}/{dir} — parallel to app/, server/, public/.'
172
- _resolvedContentDir = join(resolvedConfig.root, contentDirName)
173
- _resolvedOutDir = resolvedConfig.build.outDir
174
- _isSsr = !!resolvedConfig.build.ssr
175
- },
176
-
177
- async buildStart() {
178
- const items = await loadContentStore(_resolvedContentDir, includeDrafts)
179
- const g = globalThis as Record<string, unknown>
180
- g[CONTENT_STORE_KEY] = items
181
- },
182
-
183
- configureServer(server: ViteDevServer) {
184
- registerDevMiddleware(server, _resolvedContentDir)
185
-
186
- // HMR: re-parse on content file changes
187
- server.watcher.add(_resolvedContentDir)
188
-
189
- server.watcher.on('add', async (file: string) => {
190
- if (!file.startsWith(_resolvedContentDir)) return
191
- await refreshStore(_resolvedContentDir, includeDrafts)
192
- server.ws.send({ type: 'full-reload' })
193
- })
194
-
195
- server.watcher.on('change', async (file: string) => {
196
- if (!file.startsWith(_resolvedContentDir)) return
197
- await refreshStore(_resolvedContentDir, includeDrafts)
198
- server.ws.send({ type: 'full-reload' })
199
- })
200
-
201
- server.watcher.on('unlink', async (file: string) => {
202
- if (!file.startsWith(_resolvedContentDir)) return
203
- await refreshStore(_resolvedContentDir, includeDrafts)
204
- server.ws.send({ type: 'full-reload' })
205
- })
206
- },
207
-
208
- closeBundle() {
209
- // Only emit for the client/SPA/SSG build pass, not the SSR server bundle pass.
210
- // This prevents double scanning, parsing, and writing at build time.
211
- // The SSR runtime reads from dist/client/_content/ (or dist/_content/) at runtime.
212
- if (_isSsr) return
213
- const g = globalThis as Record<string, unknown>
214
- // Use the populated store; fall back to [] so the client never receives a 404 on
215
- // /_content/manifest.json even if buildStart failed to populate the store.
216
- const store = (g[CONTENT_STORE_KEY] as ContentItem[] | undefined) ?? []
217
-
218
- const searchIndex = buildSearchIndex(store)
219
- emitContentFiles(store, _resolvedOutDir, searchIndex)
220
- },
221
- }
222
-
223
- return plugin
224
- }
225
-
226
- async function refreshStore(contentDir: string, includeDrafts: boolean): Promise<void> {
227
- const items = await loadContentStore(contentDir, includeDrafts)
228
- const g = globalThis as Record<string, unknown>
229
- g[CONTENT_STORE_KEY] = items
230
- // Invalidate the dev middleware caches so the next request rebuilds manifest
231
- // and search-index from the updated store.
232
- invalidateDevCaches()
233
- }
@@ -1,259 +0,0 @@
1
- import matter from 'gray-matter'
2
- import { marked, type Token } from 'marked'
3
- import { readFileSync } from 'node:fs'
4
- import { readFile } from 'node:fs/promises'
5
- import type { ContentHeading, ContentItem, ContentMeta } from '../../types/content.js'
6
- import type { ContentFile } from './scanner.js'
7
- import { fileToContentPath } from './path-utils.js'
8
- import { relative } from 'pathe'
9
-
10
- // ─── Heading extraction ───────────────────────────────────────────────────────
11
-
12
- /** Slugify a plain-text heading string into a URL-safe id. */
13
- function slugify(text: string): string {
14
- return text
15
- .toLowerCase()
16
- .replace(/[^\w\s-]/g, '')
17
- .trim()
18
- .replace(/[\s_]+/g, '-')
19
- .replace(/-+/g, '-')
20
- }
21
-
22
- /**
23
- * Walks a marked token list and collects heading tokens.
24
- * Mutates heading tokens in place to add `id` attributes to the HTML output.
25
- */
26
- function extractHeadings(tokens: Token[]): ContentHeading[] {
27
- const headings: ContentHeading[] = []
28
-
29
- const walk = (tokenList: Token[]) => {
30
- for (const token of tokenList) {
31
- if (token.type === 'heading') {
32
- const text = token.text
33
- const id = slugify(text)
34
- headings.push({
35
- depth: token.depth as ContentHeading['depth'],
36
- id,
37
- text,
38
- })
39
- }
40
- // Walk nested tokens (e.g. list items, blockquote)
41
- if ('tokens' in token && Array.isArray(token.tokens)) {
42
- walk(token.tokens)
43
- }
44
- }
45
- }
46
-
47
- walk(tokens)
48
- return headings
49
- }
50
-
51
- // ─── Fallback title / description extraction ─────────────────────────────────
52
-
53
- /**
54
- * Converts a list of inline marked tokens to plain text by recursing into
55
- * formatted tokens (strong, em, link, etc.) and collecting their leaf text.
56
- * Used to derive readable fallback values from body content.
57
- */
58
- function inlineToPlainText(tokens: Token[]): string {
59
- let result = ''
60
- for (const t of tokens) {
61
- const children = (t as { tokens?: Token[] }).tokens
62
- if (Array.isArray(children) && children.length > 0) {
63
- result += inlineToPlainText(children)
64
- } else if (t.type === 'br') {
65
- result += ' '
66
- } else if ('text' in t && typeof (t as { text: string }).text === 'string') {
67
- result += (t as { text: string }).text
68
- }
69
- }
70
- return result
71
- }
72
-
73
- const DESCRIPTION_MAX_LEN = 160
74
-
75
- /**
76
- * Scans the top-level token list for a fallback `title` (first depth-1 heading)
77
- * and `description` (first paragraph). Both are `undefined` when no matching
78
- * token is found.
79
- *
80
- * These are applied only when the corresponding frontmatter field is absent, so
81
- * frontmatter always wins.
82
- */
83
- function extractFallbacks(tokens: Token[]): { title?: string; description?: string } {
84
- let title: string | undefined
85
- let description: string | undefined
86
-
87
- for (const token of tokens) {
88
- if (title === undefined && token.type === 'heading' && token.depth === 1) {
89
- const text = inlineToPlainText((token as { tokens: Token[] }).tokens ?? []).trim()
90
- if (text) title = text
91
- }
92
- if (description === undefined && token.type === 'paragraph') {
93
- const text = inlineToPlainText((token as { tokens: Token[] }).tokens ?? []).trim()
94
- if (text) {
95
- description =
96
- text.length > DESCRIPTION_MAX_LEN
97
- ? text.slice(0, DESCRIPTION_MAX_LEN).trimEnd() + '…'
98
- : text
99
- }
100
- }
101
- if (title !== undefined && description !== undefined) break
102
- }
103
-
104
- return { title, description }
105
- }
106
-
107
- // ─── Custom renderer: add id to heading tags ─────────────────────────────────
108
-
109
- const renderer = new marked.Renderer()
110
-
111
- renderer.heading = function ({ tokens, depth }) {
112
- const text = tokens.map((t) => ('text' in t ? (t.text as string) : '')).join('')
113
- const id = slugify(text)
114
- const level = depth as ContentHeading['depth']
115
- const innerHtml = marked.parseInline(tokens.map((t) => ('raw' in t ? t.raw : '')).join(''))
116
- return `<h${level} id="${id}">${innerHtml}</h${level}>\n`
117
- }
118
-
119
- // ─── Parser ───────────────────────────────────────────────────────────────────
120
-
121
- /**
122
- * Core parse logic shared by both the sync and async variants.
123
- * Accepts pre-read `raw` content so the caller controls I/O scheduling.
124
- *
125
- * Date normalization: gray-matter parses bare YAML dates (e.g. `date: 2026-04-03`)
126
- * as JavaScript `Date` objects. This causes a type mismatch — the in-memory server
127
- * store contains `Date` objects while the client, which reads via JSON.stringify/parse,
128
- * always gets ISO strings. All `Date` values are normalised to `YYYY-MM-DD` strings
129
- * here so both paths are consistent.
130
- */
131
- function parseContentFileFromRaw(
132
- file: ContentFile,
133
- contentDir: string,
134
- raw: string,
135
- ): ContentItem {
136
- const _path = fileToContentPath(file.filePath, contentDir)
137
- const _file = relative(contentDir, file.filePath)
138
-
139
- if (file.ext === 'json') {
140
- // Validate JSON so users get a clear error at build time rather than a
141
- // silently broken body string that only surfaces at render time.
142
- try {
143
- JSON.parse(raw)
144
- } catch (err) {
145
- throw new Error(
146
- `Invalid JSON in content file "${file.filePath}": ${(err as Error).message}`,
147
- )
148
- }
149
- return {
150
- _path,
151
- _file,
152
- _type: 'json',
153
- body: raw,
154
- toc: [],
155
- }
156
- }
157
-
158
- // ── Markdown ─────────────────────────────────────────────────────────────
159
- const parsed = matter(raw)
160
- const frontmatter = parsed.data as ContentMeta
161
- const content = parsed.content
162
-
163
- // Split on <!-- more --> for excerpt.
164
- // The marker is stripped from bodySource so it does not appear as an HTML
165
- // comment in the rendered body — spec says body is "all content minus the
166
- // marker itself".
167
- const MORE_MARKER = '<!-- more -->'
168
- const moreIndex = content.indexOf(MORE_MARKER)
169
- const hasMore = moreIndex !== -1
170
- const bodySource = hasMore
171
- ? content.slice(0, moreIndex) + content.slice(moreIndex + MORE_MARKER.length)
172
- : content
173
- const excerptSource = hasMore ? content.slice(0, moreIndex).trim() : null
174
-
175
- // Tokenise once and extract headings
176
- const lexer = new marked.Lexer()
177
- const tokens = lexer.lex(bodySource)
178
- const toc = extractHeadings(tokens)
179
-
180
- // Render full body with custom renderer (adds id= to headings)
181
- const body = marked.parser(tokens, { renderer }) as string
182
-
183
- // Render excerpt if present
184
- const excerpt = excerptSource !== null
185
- ? (marked.parse(excerptSource, { renderer }) as string)
186
- : undefined
187
-
188
- // Derive fallback title / description from body tokens when frontmatter
189
- // does not provide them. Frontmatter always wins — these only fill gaps.
190
- const fallbacks = extractFallbacks(tokens)
191
-
192
- const item: ContentItem = {
193
- ...frontmatter,
194
- _path,
195
- _file,
196
- _type: 'markdown',
197
- body,
198
- toc,
199
- }
200
-
201
- if (item.title === undefined && fallbacks.title !== undefined) {
202
- item.title = fallbacks.title
203
- }
204
- if (item.description === undefined && fallbacks.description !== undefined) {
205
- item.description = fallbacks.description
206
- }
207
-
208
- if (excerpt !== undefined) {
209
- item.excerpt = excerpt
210
- }
211
-
212
- // Normalise any Date objects introduced by gray-matter's YAML parser to
213
- // YYYY-MM-DD strings. Without this, the server in-memory store holds Date
214
- // objects while the client (after JSON round-trip) holds strings, causing
215
- // date comparisons in .where() predicates to silently misbehave server-side.
216
- for (const key of Object.keys(item)) {
217
- if ((item as Record<string, unknown>)[key] instanceof Date) {
218
- ;(item as Record<string, unknown>)[key] = ((item as Record<string, unknown>)[key] as Date)
219
- .toISOString()
220
- .split('T')[0]
221
- }
222
- }
223
-
224
- return item
225
- }
226
-
227
- /**
228
- * Parses a single content file synchronously and returns a `ContentItem`.
229
- * Prefer `parseContentFileAsync` in batch contexts (e.g. `loadContentStore`)
230
- * to allow concurrent I/O.
231
- */
232
- export function parseContentFile(
233
- file: ContentFile,
234
- contentDir: string,
235
- ): ContentItem {
236
- const raw = readFileSync(file.filePath, 'utf-8')
237
- return parseContentFileFromRaw(file, contentDir, raw)
238
- }
239
-
240
- /**
241
- * Async variant of `parseContentFile`. Uses `fs/promises.readFile` so multiple
242
- * calls can be awaited concurrently via `Promise.all`, overlapping disk I/O.
243
- */
244
- export async function parseContentFileAsync(
245
- file: ContentFile,
246
- contentDir: string,
247
- ): Promise<ContentItem> {
248
- const raw = await readFile(file.filePath, 'utf-8')
249
- return parseContentFileFromRaw(file, contentDir, raw)
250
- }
251
-
252
- /**
253
- * Strips body-only fields to produce a lean `ContentMeta` for the manifest.
254
- */
255
- export function toContentMeta(item: ContentItem): ContentMeta {
256
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
257
- const { _file, body, toc, excerpt, ...meta } = item
258
- return meta as ContentMeta
259
- }
@@ -1,55 +0,0 @@
1
- import { relative } from 'pathe'
2
-
3
- function stripNumericPrefix(segment: string): string {
4
- return segment.replace(/^\d+\./, '')
5
- }
6
-
7
- /**
8
- * Maps a content file path to a `_path` URL path.
9
- *
10
- * Rules:
11
- * - Strip the content dir prefix
12
- * - Strip the file extension
13
- * - Strip `NN.` numeric ordering prefixes from all path segments
14
- * - Strip `/index` suffix (so blog/index.md → /blog)
15
- * - Strip `YYYY-MM-DD-` date prefix from the final slug segment
16
- *
17
- * Examples:
18
- * index.md → /
19
- * 01.about.md → /about
20
- * about.md → /about
21
- * 01.blog/02.hello.md → /blog/hello
22
- * blog/index.md → /blog
23
- * blog/2026-04-03-hello.md → /blog/hello
24
- * docs/getting-started.md → /docs/getting-started
25
- * data/products.json → /data/products
26
- */
27
- export function fileToContentPath(filePath: string, contentDir: string): string {
28
- // Get path relative to contentDir, strip extension
29
- let rel = relative(contentDir, filePath)
30
- rel = rel.replace(/\.(md|json)$/, '')
31
-
32
- // Split into segments
33
- const segments = rel.split('/').map(stripNumericPrefix)
34
-
35
- // Strip date prefix (YYYY-MM-DD-) from the last segment after removing any
36
- // numeric ordering prefix (for example 01.2026-04-03-hello.md → /hello).
37
- const last = segments[segments.length - 1]
38
- const stripped = last.replace(/^\d{4}-\d{2}-\d{2}-/, '')
39
- segments[segments.length - 1] = stripped
40
-
41
- // Strip trailing 'index' segment (but keep bare 'index' → '/')
42
- if (segments.length > 1 && segments[segments.length - 1] === 'index') {
43
- segments.pop()
44
- }
45
-
46
- // If the only segment was 'index', produce root path
47
- if (segments.length === 1 && segments[0] === 'index') {
48
- return '/'
49
- }
50
-
51
- const path = '/' + segments.join('/')
52
- return path.replace(/\/+/g, '/')
53
- }
54
-
55
-
@@ -1,26 +0,0 @@
1
- import fg from 'fast-glob'
2
-
3
- export interface ContentFile {
4
- /** Absolute file path */
5
- filePath: string
6
- /** Extension without dot: 'md' | 'json' */
7
- ext: 'md' | 'json'
8
- }
9
-
10
- /**
11
- * Scans the content directory for all Markdown and JSON files.
12
- * Returns absolute file paths sorted alphabetically.
13
- */
14
- export async function scanContentFiles(contentDir: string): Promise<ContentFile[]> {
15
- const files = await fg('**/*.{md,json}', {
16
- cwd: contentDir,
17
- absolute: true,
18
- onlyFiles: true,
19
- ignore: ['**/node_modules/**', '**/.git/**'],
20
- })
21
-
22
- return files.sort().map((filePath) => ({
23
- filePath,
24
- ext: filePath.endsWith('.json') ? 'json' : 'md',
25
- }))
26
- }
@@ -1,28 +0,0 @@
1
- import MiniSearch from 'minisearch'
2
- import type { ContentItem } from '../../types/content.js'
3
-
4
- /**
5
- * Builds and serialises a MiniSearch full-text search index over `title` and `description`.
6
- *
7
- * Stored fields: `_path`, `title`, `description` — match `ContentSearchResult` exactly.
8
- * Returns the serialised index as a JSON string ready to be written to `search-index.json`.
9
- */
10
- export function buildSearchIndex(items: ContentItem[]): string {
11
- const index = new MiniSearch({
12
- fields: ['title', 'description'],
13
- storeFields: ['_path', 'title', 'description'],
14
- idField: '_path',
15
- })
16
-
17
- const docs = items
18
- .filter((item) => item.title !== undefined)
19
- .map((item) => ({
20
- _path: item._path,
21
- title: (item.title as string) ?? '',
22
- description: (item.description as string) ?? '',
23
- }))
24
-
25
- index.addAll(docs)
26
-
27
- return JSON.stringify(index)
28
- }