@jasonshimmy/vite-plugin-cer-app 0.2.0 → 0.4.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 (93) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +2 -0
  3. package/commits.txt +1 -1
  4. package/dist/cli/create/index.js +7 -3
  5. package/dist/cli/create/index.js.map +1 -1
  6. package/dist/cli/create/templates/spa/.gitignore.tpl +25 -0
  7. package/dist/cli/create/templates/spa/index.html.tpl +1 -1
  8. package/dist/cli/create/templates/ssg/.gitignore.tpl +25 -0
  9. package/dist/cli/create/templates/ssg/index.html.tpl +1 -1
  10. package/dist/cli/create/templates/ssr/.gitignore.tpl +25 -0
  11. package/dist/cli/create/templates/ssr/cer.config.ts.tpl +0 -1
  12. package/dist/cli/create/templates/ssr/index.html.tpl +1 -1
  13. package/dist/plugin/build-ssr.d.ts.map +1 -1
  14. package/dist/plugin/build-ssr.js +18 -0
  15. package/dist/plugin/build-ssr.js.map +1 -1
  16. package/dist/plugin/dev-server.d.ts +0 -1
  17. package/dist/plugin/dev-server.d.ts.map +1 -1
  18. package/dist/plugin/dts-generator.js +1 -1
  19. package/dist/plugin/dts-generator.js.map +1 -1
  20. package/dist/plugin/generated-dir.d.ts +5 -11
  21. package/dist/plugin/generated-dir.d.ts.map +1 -1
  22. package/dist/plugin/generated-dir.js +43 -31
  23. package/dist/plugin/generated-dir.js.map +1 -1
  24. package/dist/plugin/index.d.ts.map +1 -1
  25. package/dist/plugin/index.js +9 -1
  26. package/dist/plugin/index.js.map +1 -1
  27. package/dist/plugin/transforms/auto-import.js +2 -2
  28. package/dist/plugin/transforms/auto-import.js.map +1 -1
  29. package/dist/runtime/app-template.d.ts +5 -4
  30. package/dist/runtime/app-template.d.ts.map +1 -1
  31. package/dist/runtime/app-template.js +6 -5
  32. package/dist/runtime/app-template.js.map +1 -1
  33. package/dist/runtime/composables/index.d.ts +1 -0
  34. package/dist/runtime/composables/index.d.ts.map +1 -1
  35. package/dist/runtime/composables/index.js +1 -0
  36. package/dist/runtime/composables/index.js.map +1 -1
  37. package/dist/runtime/composables/use-inject.d.ts +29 -0
  38. package/dist/runtime/composables/use-inject.d.ts.map +1 -0
  39. package/dist/runtime/composables/use-inject.js +48 -0
  40. package/dist/runtime/composables/use-inject.js.map +1 -0
  41. package/dist/runtime/entry-server-template.d.ts +1 -1
  42. package/dist/runtime/entry-server-template.d.ts.map +1 -1
  43. package/dist/runtime/entry-server-template.js +20 -0
  44. package/dist/runtime/entry-server-template.js.map +1 -1
  45. package/dist/types/config.d.ts +0 -1
  46. package/dist/types/config.d.ts.map +1 -1
  47. package/dist/types/config.js.map +1 -1
  48. package/docs/cli.md +1 -1
  49. package/docs/composables.md +37 -0
  50. package/docs/configuration.md +2 -11
  51. package/docs/getting-started.md +2 -100
  52. package/docs/plugins.md +23 -15
  53. package/docs/rendering-modes.md +3 -4
  54. package/docs/testing.md +3 -3
  55. package/e2e/kitchen-sink/app/pages/(auth)/protected.ts +1 -5
  56. package/e2e/kitchen-sink/cer-auto-imports.d.ts +1 -0
  57. package/package.json +1 -1
  58. package/src/__tests__/plugin/build-ssr.test.ts +10 -0
  59. package/src/__tests__/plugin/cer-app-plugin.test.ts +15 -0
  60. package/src/__tests__/plugin/dev-server.test.ts +1 -1
  61. package/src/__tests__/plugin/dts-generator.test.ts +5 -0
  62. package/src/__tests__/plugin/entry-server-template.test.ts +24 -0
  63. package/src/__tests__/plugin/generated-dir.test.ts +8 -39
  64. package/src/__tests__/plugin/resolve-config.test.ts +0 -5
  65. package/src/__tests__/plugin/transforms/auto-import.test.ts +7 -0
  66. package/src/__tests__/runtime/use-inject-client.test.ts +67 -0
  67. package/src/__tests__/runtime/use-inject.test.ts +66 -0
  68. package/src/__tests__/types/config.test.ts +1 -1
  69. package/src/cli/create/index.ts +12 -8
  70. package/src/cli/create/templates/spa/.gitignore.tpl +25 -0
  71. package/src/cli/create/templates/spa/index.html.tpl +1 -1
  72. package/src/cli/create/templates/ssg/.gitignore.tpl +25 -0
  73. package/src/cli/create/templates/ssg/index.html.tpl +1 -1
  74. package/src/cli/create/templates/ssr/.gitignore.tpl +25 -0
  75. package/src/cli/create/templates/ssr/cer.config.ts.tpl +0 -1
  76. package/src/cli/create/templates/ssr/index.html.tpl +1 -1
  77. package/src/plugin/build-ssr.ts +18 -0
  78. package/src/plugin/dev-server.ts +1 -1
  79. package/src/plugin/dts-generator.ts +1 -1
  80. package/src/plugin/generated-dir.ts +44 -31
  81. package/src/plugin/index.ts +9 -1
  82. package/src/plugin/transforms/auto-import.ts +2 -2
  83. package/src/runtime/app-template.ts +6 -5
  84. package/src/runtime/composables/index.ts +1 -0
  85. package/src/runtime/composables/use-inject.ts +49 -0
  86. package/src/runtime/entry-server-template.ts +20 -0
  87. package/src/types/config.ts +0 -1
  88. package/dist/cli/create/templates/spa/app/app.ts.tpl +0 -93
  89. package/dist/cli/create/templates/ssg/app/app.ts.tpl +0 -97
  90. package/dist/cli/create/templates/ssr/app/app.ts.tpl +0 -97
  91. package/src/cli/create/templates/spa/app/app.ts.tpl +0 -93
  92. package/src/cli/create/templates/ssg/app/app.ts.tpl +0 -97
  93. package/src/cli/create/templates/ssr/app/app.ts.tpl +0 -97
@@ -1 +1 @@
1
- {"version":3,"file":"auto-import.js","sourceRoot":"","sources":["../../../src/plugin/transforms/auto-import.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auto-import.js","sourceRoot":"","sources":["../../../src/plugin/transforms/auto-import.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  /**
2
- * Template string for the default `.cer/app.ts` client entry point.
2
+ * Template string for `.cer/app.ts` — the framework client entry point.
3
3
  *
4
- * Written to `.cer/app.ts` when the consumer does not provide `app/app.ts`.
5
- * Consumers can override by creating their own `app/app.ts`.
4
+ * Always written to `.cer/app.ts` on every dev/build so consumers
5
+ * automatically receive the latest bootstrap code on plugin update.
6
+ * This file is gitignored and should never be edited directly.
6
7
  */
7
- export declare const APP_ENTRY_TEMPLATE = "// AUTO-GENERATED by @jasonshimmy/vite-plugin-cer-app\n// This is the default client entry point. Create app/app.ts to override it.\n\nimport '@jasonshimmy/custom-elements-runtime/css'\nimport 'virtual:cer-jit-css'\nimport 'virtual:cer-components'\nimport routes from 'virtual:cer-routes'\nimport layouts from 'virtual:cer-layouts'\nimport plugins from 'virtual:cer-plugins'\nimport { hasLoading, loadingTag } from 'virtual:cer-loading'\nimport { hasError, errorTag } from 'virtual:cer-error'\nimport {\n component,\n ref,\n provide,\n useOnConnected,\n useOnDisconnected,\n registerBuiltinComponents,\n} from '@jasonshimmy/custom-elements-runtime'\nimport { initRouter } from '@jasonshimmy/custom-elements-runtime/router'\nimport { enableJITCSS } from '@jasonshimmy/custom-elements-runtime/jit-css'\n\nregisterBuiltinComponents()\nenableJITCSS()\n\nconst router = initRouter({ routes })\n\n// \u2500\u2500\u2500 Navigation state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst isNavigating = ref(false)\nconst currentError = ref(null)\n\nconst resetError = (): void => {\n currentError.value = null\n void router.replace(router.getCurrent().path)\n}\n;(globalThis as Record<string, unknown>).resetError = resetError\n\nconst _push = router.push.bind(router)\nconst _replace = router.replace.bind(router)\n\nrouter.push = async (path) => {\n isNavigating.value = true\n currentError.value = null\n try {\n await _push(path)\n } catch (err) {\n currentError.value = err instanceof Error ? err.message : String(err)\n } finally {\n isNavigating.value = false\n }\n}\n\nrouter.replace = async (path) => {\n isNavigating.value = true\n currentError.value = null\n try {\n await _replace(path)\n } catch (err) {\n currentError.value = err instanceof Error ? err.message : String(err)\n } finally {\n isNavigating.value = false\n }\n}\n\n// \u2500\u2500\u2500 Plugins \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// Collect plugin-provided values so cer-layout-view can forward them into\n// the component context tree via the real provide() hook (which inject() walks).\n// Declared BEFORE component('cer-layout-view') to avoid a temporal dead zone\n// ReferenceError: customElements.define() upgrades existing DOM elements\n// synchronously, calling the render function immediately.\nconst _pluginProvides = new Map<string, unknown>()\n// Expose plugin provides globally so page components can read them synchronously\n// regardless of render order.\n;(globalThis as Record<string, unknown>).__cerPluginProvides = _pluginProvides\n\n// \u2500\u2500\u2500 <cer-layout-view> \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ncomponent('cer-layout-view', () => {\n // Forward plugin-provided values into the component context so inject() in\n // any descendant component can resolve them by walking up the DOM tree.\n for (const [key, value] of _pluginProvides) {\n provide(key, value)\n }\n\n const current = ref(router.getCurrent())\n let unsub: (() => void) | undefined\n\n useOnConnected(() => {\n unsub = router.subscribe((s: typeof current.value) => { current.value = s })\n })\n useOnDisconnected(() => { unsub?.(); unsub = undefined })\n\n if (currentError.value !== null) {\n if (hasError && errorTag) {\n return { tag: errorTag, props: { attrs: { error: String(currentError.value) } }, children: [] }\n }\n return { tag: 'div', props: { attrs: { style: 'padding:2rem;font-family:monospace' } }, children: String(currentError.value) }\n }\n\n if (isNavigating.value && hasLoading && loadingTag) {\n return { tag: loadingTag, props: {}, children: [] }\n }\n\n const matched = router.matchRoute(current.value.path)\n const routeMeta = matched?.route?.meta as { layout?: string } | undefined\n const layoutName = routeMeta?.layout ?? 'default'\n const layoutTag = (layouts as Record<string, string>)[layoutName]\n const routerView = { tag: 'router-view', props: {}, children: [] }\n\n if (layoutTag) return { tag: layoutTag, props: {}, children: [routerView] }\n return routerView\n})\n\nfor (const plugin of plugins) {\n if (plugin && typeof plugin.setup === 'function') {\n await plugin.setup({\n router,\n provide: (key: string, value: unknown) => { _pluginProvides.set(key, value) },\n config: {},\n })\n }\n}\n\n// \u2500\u2500\u2500 Pre-load initial route \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Download the current page's route chunk AFTER plugins run so that\n// cer-layout-view's first render (which calls provide()) completes before\n// page components are defined and their renders are scheduled. This ensures\n// inject() in child components can find values stored by provide().\n\nif (typeof window !== 'undefined') {\n const _initMatch = router.matchRoute(window.location.pathname)\n if (_initMatch?.route?.load) {\n try { await _initMatch.route.load() } catch { /* non-fatal */ }\n }\n}\n\n// \u2500\u2500\u2500 Initial navigation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nif (typeof window !== 'undefined') {\n // Use the original (unwrapped) replace so isNavigating stays false during\n // the initial paint \u2014 the loading component must not flash over pre-rendered content.\n await _replace(window.location.pathname + window.location.search + window.location.hash)\n // Clear SSR loader data after initial navigation so subsequent client-side\n // navigations don't accidentally reuse stale server data.\n delete (globalThis as Record<string, unknown>).__CER_DATA__\n}\n\nexport { router }\n";
8
+ export declare const APP_ENTRY_TEMPLATE = "// AUTO-GENERATED by @jasonshimmy/vite-plugin-cer-app \u2014 do not edit.\n// Regenerated automatically on every dev server start and build.\n\nimport '@jasonshimmy/custom-elements-runtime/css'\nimport 'virtual:cer-jit-css'\nimport 'virtual:cer-components'\nimport routes from 'virtual:cer-routes'\nimport layouts from 'virtual:cer-layouts'\nimport plugins from 'virtual:cer-plugins'\nimport { hasLoading, loadingTag } from 'virtual:cer-loading'\nimport { hasError, errorTag } from 'virtual:cer-error'\nimport {\n component,\n ref,\n provide,\n useOnConnected,\n useOnDisconnected,\n registerBuiltinComponents,\n} from '@jasonshimmy/custom-elements-runtime'\nimport { initRouter } from '@jasonshimmy/custom-elements-runtime/router'\nimport { enableJITCSS } from '@jasonshimmy/custom-elements-runtime/jit-css'\n\nregisterBuiltinComponents()\nenableJITCSS()\n\nconst router = initRouter({ routes })\n\n// \u2500\u2500\u2500 Navigation state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst isNavigating = ref(false)\nconst currentError = ref(null)\n\nconst resetError = (): void => {\n currentError.value = null\n void router.replace(router.getCurrent().path)\n}\n;(globalThis as Record<string, unknown>).resetError = resetError\n\nconst _push = router.push.bind(router)\nconst _replace = router.replace.bind(router)\n\nrouter.push = async (path) => {\n isNavigating.value = true\n currentError.value = null\n try {\n await _push(path)\n } catch (err) {\n currentError.value = err instanceof Error ? err.message : String(err)\n } finally {\n isNavigating.value = false\n }\n}\n\nrouter.replace = async (path) => {\n isNavigating.value = true\n currentError.value = null\n try {\n await _replace(path)\n } catch (err) {\n currentError.value = err instanceof Error ? err.message : String(err)\n } finally {\n isNavigating.value = false\n }\n}\n\n// \u2500\u2500\u2500 Plugins \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// Collect plugin-provided values so cer-layout-view can forward them into\n// the component context tree via the real provide() hook (which inject() walks).\n// Declared BEFORE component('cer-layout-view') to avoid a temporal dead zone\n// ReferenceError: customElements.define() upgrades existing DOM elements\n// synchronously, calling the render function immediately.\nconst _pluginProvides = new Map<string, unknown>()\n// Expose plugin provides globally so page components can read them synchronously\n// regardless of render order.\n;(globalThis as Record<string, unknown>).__cerPluginProvides = _pluginProvides\n\n// \u2500\u2500\u2500 <cer-layout-view> \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ncomponent('cer-layout-view', () => {\n // Forward plugin-provided values into the component context so inject() in\n // any descendant component can resolve them by walking up the DOM tree.\n for (const [key, value] of _pluginProvides) {\n provide(key, value)\n }\n\n const current = ref(router.getCurrent())\n let unsub: (() => void) | undefined\n\n useOnConnected(() => {\n unsub = router.subscribe((s: typeof current.value) => { current.value = s })\n })\n useOnDisconnected(() => { unsub?.(); unsub = undefined })\n\n if (currentError.value !== null) {\n if (hasError && errorTag) {\n return { tag: errorTag, props: { attrs: { error: String(currentError.value) } }, children: [] }\n }\n return { tag: 'div', props: { attrs: { style: 'padding:2rem;font-family:monospace' } }, children: String(currentError.value) }\n }\n\n if (isNavigating.value && hasLoading && loadingTag) {\n return { tag: loadingTag, props: {}, children: [] }\n }\n\n const matched = router.matchRoute(current.value.path)\n const routeMeta = matched?.route?.meta as { layout?: string } | undefined\n const layoutName = routeMeta?.layout ?? 'default'\n const layoutTag = (layouts as Record<string, string>)[layoutName]\n const routerView = { tag: 'router-view', props: {}, children: [] }\n\n if (layoutTag) return { tag: layoutTag, props: {}, children: [routerView] }\n return routerView\n})\n\nfor (const plugin of plugins) {\n if (plugin && typeof plugin.setup === 'function') {\n await plugin.setup({\n router,\n provide: (key: string, value: unknown) => { _pluginProvides.set(key, value) },\n config: {},\n })\n }\n}\n\n// \u2500\u2500\u2500 Pre-load initial route \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Download the current page's route chunk AFTER plugins run so that\n// cer-layout-view's first render (which calls provide()) completes before\n// page components are defined and their renders are scheduled. This ensures\n// inject() in child components can find values stored by provide().\n\nif (typeof window !== 'undefined') {\n const _initMatch = router.matchRoute(window.location.pathname)\n if (_initMatch?.route?.load) {\n try { await _initMatch.route.load() } catch { /* non-fatal */ }\n }\n}\n\n// \u2500\u2500\u2500 Initial navigation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nif (typeof window !== 'undefined') {\n // Use the original (unwrapped) replace so isNavigating stays false during\n // the initial paint \u2014 the loading component must not flash over pre-rendered content.\n await _replace(window.location.pathname + window.location.search + window.location.hash)\n // Clear SSR loader data after initial navigation so subsequent client-side\n // navigations don't accidentally reuse stale server data.\n delete (globalThis as Record<string, unknown>).__CER_DATA__\n}\n\nexport { router }\n";
8
9
  //# sourceMappingURL=app-template.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-template.d.ts","sourceRoot":"","sources":["../../src/runtime/app-template.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,82NAsJ9B,CAAA"}
1
+ {"version":3,"file":"app-template.d.ts","sourceRoot":"","sources":["../../src/runtime/app-template.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,u3NAsJ9B,CAAA"}
@@ -1,11 +1,12 @@
1
1
  /**
2
- * Template string for the default `.cer/app.ts` client entry point.
2
+ * Template string for `.cer/app.ts` — the framework client entry point.
3
3
  *
4
- * Written to `.cer/app.ts` when the consumer does not provide `app/app.ts`.
5
- * Consumers can override by creating their own `app/app.ts`.
4
+ * Always written to `.cer/app.ts` on every dev/build so consumers
5
+ * automatically receive the latest bootstrap code on plugin update.
6
+ * This file is gitignored and should never be edited directly.
6
7
  */
7
- export const APP_ENTRY_TEMPLATE = `// AUTO-GENERATED by @jasonshimmy/vite-plugin-cer-app
8
- // This is the default client entry point. Create app/app.ts to override it.
8
+ export const APP_ENTRY_TEMPLATE = `// AUTO-GENERATED by @jasonshimmy/vite-plugin-cer-app — do not edit.
9
+ // Regenerated automatically on every dev server start and build.
9
10
 
10
11
  import '@jasonshimmy/custom-elements-runtime/css'
11
12
  import 'virtual:cer-jit-css'
@@ -1 +1 @@
1
- {"version":3,"file":"app-template.js","sourceRoot":"","sources":["../../src/runtime/app-template.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsJjC,CAAA"}
1
+ {"version":3,"file":"app-template.js","sourceRoot":"","sources":["../../src/runtime/app-template.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsJjC,CAAA"}
@@ -1,4 +1,5 @@
1
1
  export { useHead, beginHeadCollection, endHeadCollection, serializeHeadTags } from './use-head.js';
2
2
  export type { HeadInput } from './use-head.js';
3
3
  export { usePageData } from './use-page-data.js';
4
+ export { useInject } from './use-inject.js';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/runtime/composables/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAClG,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/runtime/composables/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAClG,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA"}
@@ -1,3 +1,4 @@
1
1
  export { useHead, beginHeadCollection, endHeadCollection, serializeHeadTags } from './use-head.js';
2
2
  export { usePageData } from './use-page-data.js';
3
+ export { useInject } from './use-inject.js';
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/runtime/composables/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAElG,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/runtime/composables/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAElG,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * useInject — reads a value provided by a plugin via plugin.setup()'s provide().
3
+ *
4
+ * Works consistently across all rendering modes:
5
+ *
6
+ * - **SPA/Client**: Uses inject() from the component context tree (established
7
+ * by cer-layout-view calling provide() for each plugin-provided value).
8
+ *
9
+ * - **SSR/SSG**: Reads from globalThis.__cerPluginProvides, populated when the
10
+ * server entry runs plugin.setup() before rendering.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * // In a plugin (app/plugins/my-plugin.ts):
15
+ * export default {
16
+ * name: 'my-plugin',
17
+ * setup({ provide }) {
18
+ * provide('my-service', { greet: () => 'hello' })
19
+ * }
20
+ * }
21
+ *
22
+ * // In a component:
23
+ * component('my-page', () => {
24
+ * const service = useInject<{ greet(): string }>('my-service')
25
+ * })
26
+ * ```
27
+ */
28
+ export declare function useInject<T = unknown>(key: string, defaultValue?: T): T | undefined;
29
+ //# sourceMappingURL=use-inject.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-inject.d.ts","sourceRoot":"","sources":["../../../src/runtime/composables/use-inject.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAgBnF"}
@@ -0,0 +1,48 @@
1
+ import { inject } from '@jasonshimmy/custom-elements-runtime';
2
+ const _g = globalThis;
3
+ const _PROVIDES_KEY = '__cerPluginProvides';
4
+ /**
5
+ * useInject — reads a value provided by a plugin via plugin.setup()'s provide().
6
+ *
7
+ * Works consistently across all rendering modes:
8
+ *
9
+ * - **SPA/Client**: Uses inject() from the component context tree (established
10
+ * by cer-layout-view calling provide() for each plugin-provided value).
11
+ *
12
+ * - **SSR/SSG**: Reads from globalThis.__cerPluginProvides, populated when the
13
+ * server entry runs plugin.setup() before rendering.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * // In a plugin (app/plugins/my-plugin.ts):
18
+ * export default {
19
+ * name: 'my-plugin',
20
+ * setup({ provide }) {
21
+ * provide('my-service', { greet: () => 'hello' })
22
+ * }
23
+ * }
24
+ *
25
+ * // In a component:
26
+ * component('my-page', () => {
27
+ * const service = useInject<{ greet(): string }>('my-service')
28
+ * })
29
+ * ```
30
+ */
31
+ export function useInject(key, defaultValue) {
32
+ // Server-side (SSR/SSG): read from the global plugin provides map.
33
+ // __cerPluginProvides is populated by the server entry before the render pass.
34
+ if (typeof document === 'undefined') {
35
+ const pluginProvides = _g[_PROVIDES_KEY];
36
+ const value = pluginProvides?.get(key);
37
+ return value !== undefined ? value : defaultValue;
38
+ }
39
+ // Client-side: inject() walks the component context tree established by
40
+ // cer-layout-view's provide() calls. Falls back to __cerPluginProvides for
41
+ // reads before cer-layout-view mounts (e.g. during plugin-registered components).
42
+ const value = inject(key);
43
+ if (value !== undefined)
44
+ return value;
45
+ const pluginProvides = _g[_PROVIDES_KEY];
46
+ return pluginProvides?.get(key) ?? defaultValue;
47
+ }
48
+ //# sourceMappingURL=use-inject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-inject.js","sourceRoot":"","sources":["../../../src/runtime/composables/use-inject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sCAAsC,CAAA;AAE7D,MAAM,EAAE,GAAG,UAAqC,CAAA;AAChD,MAAM,aAAa,GAAG,qBAAqB,CAAA;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,SAAS,CAAc,GAAW,EAAE,YAAgB;IAClE,mEAAmE;IACnE,+EAA+E;IAC/E,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,MAAM,cAAc,GAAG,EAAE,CAAC,aAAa,CAA0C,CAAA;QACjF,MAAM,KAAK,GAAG,cAAc,EAAE,GAAG,CAAC,GAAG,CAAC,CAAA;QACtC,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAE,KAAW,CAAC,CAAC,CAAC,YAAY,CAAA;IAC1D,CAAC;IAED,wEAAwE;IACxE,2EAA2E;IAC3E,kFAAkF;IAClF,MAAM,KAAK,GAAG,MAAM,CAAI,GAAG,CAAC,CAAA;IAC5B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IACrC,MAAM,cAAc,GAAG,EAAE,CAAC,aAAa,CAA0C,CAAA;IACjF,OAAQ,cAAc,EAAE,GAAG,CAAC,GAAG,CAAmB,IAAI,YAAY,CAAA;AACpE,CAAC"}
@@ -5,5 +5,5 @@
5
5
  * wires up the routing, and exports a handler compatible with
6
6
  * Express/Fastify/Node http.
7
7
  */
8
- export declare const ENTRY_SERVER_TEMPLATE = "// Server-side entry \u2014 AUTO-GENERATED by @jasonshimmy/vite-plugin-cer-app\nimport { readFileSync, existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { AsyncLocalStorage } from 'node:async_hooks'\nimport 'virtual:cer-components'\nimport routes from 'virtual:cer-routes'\nimport layouts from 'virtual:cer-layouts'\nimport plugins from 'virtual:cer-plugins'\nimport apiRoutes from 'virtual:cer-server-api'\nimport { registerBuiltinComponents } from '@jasonshimmy/custom-elements-runtime'\nimport { registerEntityMap, DSD_POLYFILL_SCRIPT } from '@jasonshimmy/custom-elements-runtime/ssr'\nimport entitiesJson from '@jasonshimmy/custom-elements-runtime/entities.json'\nimport { initRouter } from '@jasonshimmy/custom-elements-runtime/router'\nimport { createSSRHandler } from '@jasonshimmy/custom-elements-runtime/ssr-middleware'\n\nregisterBuiltinComponents()\n\n// Pre-load the full HTML entity map so named entities like &mdash; decode\n// correctly during SSR. Without this the bundled runtime falls back to a\n// minimal set (&lt;, &gt;, &amp; \u2026) and re-escapes everything else.\nregisterEntityMap(entitiesJson)\n\n// Async-local storage for request-scoped SSR loader data.\n// Using AsyncLocalStorage ensures concurrent SSR renders (e.g. SSG with\n// concurrency > 1) never see each other's data \u2014 each request's async chain\n// carries its own store value, so usePageData() is always race-condition-free.\nconst _cerDataStore = new AsyncLocalStorage()\n// Expose the store so the usePageData() composable can read it server-side.\n;(globalThis).__CER_DATA_STORE__ = _cerDataStore\n\n// Load the Vite-built client index.html (dist/client/index.html) so every SSR\n// response includes the client-side scripts needed for hydration and routing.\n// The server bundle lives at dist/server/server.js, so ../client resolves correctly.\nconst _clientTemplatePath = join(dirname(fileURLToPath(import.meta.url)), '../client/index.html')\nconst _clientTemplate = existsSync(_clientTemplatePath)\n ? readFileSync(_clientTemplatePath, 'utf-8')\n : null\n\n// Merge the SSR handler's full HTML document with the Vite client shell so the\n// final page contains both pre-rendered DSD content and the client bundle scripts.\nfunction _mergeWithClientTemplate(ssrHtml, clientTemplate) {\n const headTag = '<head>', headCloseTag = '</head>'\n const bodyTag = '<body>', bodyCloseTag = '</body>'\n const headStart = ssrHtml.indexOf(headTag)\n const headEnd = ssrHtml.indexOf(headCloseTag)\n const bodyStart = ssrHtml.indexOf(bodyTag)\n const bodyEnd = ssrHtml.lastIndexOf(bodyCloseTag)\n const ssrHead = headStart >= 0 && headEnd > headStart\n ? ssrHtml.slice(headStart + headTag.length, headEnd).trim() : ''\n const ssrBody = bodyStart >= 0 && bodyEnd > bodyStart\n ? ssrHtml.slice(bodyStart + bodyTag.length, bodyEnd).trim() : ssrHtml\n // Hoist only top-level <style id=...> elements (cer-ssr-jit, cer-ssr-global)\n // from the SSR body into the document <head>. Plain <style> blocks without\n // an id attribute belong to shadow DOM templates and must stay in place \u2014\n // hoisting them to <head> breaks shadow DOM style encapsulation (document\n // styles do not pierce shadow roots), which is the root cause of FOUC.\n const headParts = ssrHead ? [ssrHead] : []\n let ssrBodyContent = ssrBody\n let pos = 0\n while (pos < ssrBodyContent.length) {\n const styleOpen = ssrBodyContent.indexOf('<style id=', pos)\n if (styleOpen < 0) break\n const styleClose = ssrBodyContent.indexOf('</style>', styleOpen)\n if (styleClose < 0) break\n headParts.push(ssrBodyContent.slice(styleOpen, styleClose + 8))\n ssrBodyContent = ssrBodyContent.slice(0, styleOpen) + ssrBodyContent.slice(styleClose + 8)\n pos = styleOpen\n }\n ssrBodyContent = ssrBodyContent.trim()\n // Inject the pre-rendered layout+page as light DOM of the app mount element\n // so it is visible before JS boots, then the client router takes over.\n let merged = clientTemplate\n if (merged.includes('<cer-layout-view></cer-layout-view>')) {\n merged = merged.replace('<cer-layout-view></cer-layout-view>',\n '<cer-layout-view>' + ssrBodyContent + '</cer-layout-view>')\n } else if (merged.includes('<div id=\"app\"></div>')) {\n merged = merged.replace('<div id=\"app\"></div>',\n '<div id=\"app\">' + ssrBodyContent + '</div>')\n }\n const headAdditions = headParts.filter(Boolean).join('\\n')\n if (headAdditions) {\n // If SSR provides a <title>, replace the client template's <title> so the\n // SSR title wins (client template title is the fallback default).\n if (headAdditions.includes('<title>')) {\n merged = merged.replace(/<title>[^<]*<\\/title>/, '')\n }\n merged = merged.replace('</head>', headAdditions + '\\n</head>')\n }\n return merged\n}\n\n/**\n * Per-request VNode factory \u2014 initializes a fresh router at the request URL,\n * resolves the active layout from the matched route's meta, pre-loads the\n * matched page component (bypassing the async router-view so DSD renders\n * synchronously), calls the route's data loader (if any), and injects the\n * serialized result into the document head as window.__CER_DATA__ for\n * client-side hydration.\n *\n * createStreamingSSRHandler threads the router through each component's SSR\n * context so concurrent renders never share state.\n */\nconst vnodeFactory = async (req) => {\n const router = initRouter({ routes, initialUrl: req.url ?? '/' })\n const current = router.getCurrent()\n const { route, params } = router.matchRoute(current.path)\n const layoutName = route?.meta?.layout ?? 'default'\n const layoutTag = layouts[layoutName]\n\n // Pre-load the page module so we can embed the component tag directly.\n // This avoids the async router-view (which injects content via script tags\n // and breaks Declarative Shadow DOM on initial parse).\n let pageVnode = { tag: 'div', props: {}, children: [] }\n let head\n if (route?.load) {\n try {\n const mod = await route.load()\n const pageTag = mod.default\n if (pageTag) {\n pageVnode = { tag: pageTag, props: { attrs: { ...params } }, children: [] }\n }\n if (typeof mod.loader === 'function') {\n const query = current.query ?? {}\n const data = await mod.loader({ params, query, req })\n if (data !== undefined && data !== null) {\n // Make data available to usePageData() during the SSR render pass.\n // enterWith() scopes the value to the current async context so\n // concurrent renders (SSG concurrency > 1) never share data.\n _cerDataStore.enterWith(data)\n head = `<script>window.__CER_DATA__ = ${JSON.stringify(data)}</script>`\n }\n }\n } catch {\n // Non-fatal: loader errors fall back to an empty page; client will refetch.\n }\n }\n\n const vnode = layoutTag\n ? { tag: layoutTag, props: {}, children: [pageVnode] }\n : pageVnode\n\n return { vnode, router, head }\n}\n\n// Capture the raw SSR handler and wrap it to merge the response with the\n// Vite client template before sending \u2014 this injects the JS/CSS asset bundles\n// so the browser can hydrate and enable client-side routing.\nconst _rawHandler = createSSRHandler(vnodeFactory, {\n render: { dsd: true, dsdPolyfill: false },\n})\n\n/**\n * The main request handler.\n * Compatible with Express, Fastify, and Node's raw http.createServer.\n *\n * Each request is run inside a fresh _cerDataStore.run() context so that\n * concurrent renders (e.g. SSG with concurrency > 1) get isolated stores.\n * vnodeFactory calls _cerDataStore.enterWith(loaderData) from within this\n * context, making the data visible to usePageData() during SSR rendering\n * without any global-state races.\n */\nexport const handler = async (req, res) => {\n if (!_clientTemplate) {\n // No client template \u2014 run handler normally, then inject DSD polyfill.\n let _html = ''\n await _cerDataStore.run(null, async () => {\n await _rawHandler(req, { setHeader: () => {}, end: (body) => { _html = body } })\n })\n // Inject DSD polyfill at end of <body>, outside any custom element light DOM.\n const _final = _html.includes('</body>')\n ? _html.replace('</body>', DSD_POLYFILL_SCRIPT + '</body>')\n : _html + DSD_POLYFILL_SCRIPT\n res.setHeader('Content-Type', 'text/html; charset=utf-8')\n return res.end(_final)\n }\n let _capturedHtml = ''\n // Wrap _rawHandler in an isolated async-local-storage context so that\n // vnodeFactory's enterWith() call is scoped to this request only.\n await _cerDataStore.run(null, async () => {\n // Omit write() to force the non-streaming collect-then-end code path.\n await _rawHandler(req, { setHeader: () => {}, end: (body) => { _capturedHtml = body } })\n })\n let _merged = _mergeWithClientTemplate(_capturedHtml, _clientTemplate)\n // Inject DSD polyfill at end of <body>, outside <cer-layout-view> light DOM.\n _merged = _merged.includes('</body>')\n ? _merged.replace('</body>', DSD_POLYFILL_SCRIPT + '</body>')\n : _merged + DSD_POLYFILL_SCRIPT\n res.setHeader('Content-Type', 'text/html; charset=utf-8')\n res.end(_merged)\n}\n\nexport { apiRoutes, plugins, layouts, routes }\nexport default handler\n";
8
+ export declare const ENTRY_SERVER_TEMPLATE = "// Server-side entry \u2014 AUTO-GENERATED by @jasonshimmy/vite-plugin-cer-app\nimport { readFileSync, existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { AsyncLocalStorage } from 'node:async_hooks'\nimport 'virtual:cer-components'\nimport routes from 'virtual:cer-routes'\nimport layouts from 'virtual:cer-layouts'\nimport plugins from 'virtual:cer-plugins'\nimport apiRoutes from 'virtual:cer-server-api'\nimport { registerBuiltinComponents } from '@jasonshimmy/custom-elements-runtime'\nimport { registerEntityMap, DSD_POLYFILL_SCRIPT } from '@jasonshimmy/custom-elements-runtime/ssr'\nimport entitiesJson from '@jasonshimmy/custom-elements-runtime/entities.json'\nimport { initRouter } from '@jasonshimmy/custom-elements-runtime/router'\nimport { createSSRHandler } from '@jasonshimmy/custom-elements-runtime/ssr-middleware'\n\nregisterBuiltinComponents()\n\n// Pre-load the full HTML entity map so named entities like &mdash; decode\n// correctly during SSR. Without this the bundled runtime falls back to a\n// minimal set (&lt;, &gt;, &amp; \u2026) and re-escapes everything else.\nregisterEntityMap(entitiesJson)\n\n// Run plugins once at server startup so their provide() values are available\n// to useInject() during every SSR render pass. Stored on globalThis so all\n// dynamically-imported page chunks share the same reference (same pattern as\n// __CER_HEAD_COLLECTOR__ and __CER_DATA_STORE__).\nconst _pluginProvides = new Map()\n;(globalThis).__cerPluginProvides = _pluginProvides\nconst _pluginsReady = (async () => {\n const _bootstrapRouter = initRouter({ routes })\n for (const plugin of plugins) {\n if (plugin && typeof plugin.setup === 'function') {\n await plugin.setup({\n router: _bootstrapRouter,\n provide: (key, value) => _pluginProvides.set(key, value),\n config: {},\n })\n }\n }\n})()\n\n// Async-local storage for request-scoped SSR loader data.\n// Using AsyncLocalStorage ensures concurrent SSR renders (e.g. SSG with\n// concurrency > 1) never see each other's data \u2014 each request's async chain\n// carries its own store value, so usePageData() is always race-condition-free.\nconst _cerDataStore = new AsyncLocalStorage()\n// Expose the store so the usePageData() composable can read it server-side.\n;(globalThis).__CER_DATA_STORE__ = _cerDataStore\n\n// Load the Vite-built client index.html (dist/client/index.html) so every SSR\n// response includes the client-side scripts needed for hydration and routing.\n// The server bundle lives at dist/server/server.js, so ../client resolves correctly.\nconst _clientTemplatePath = join(dirname(fileURLToPath(import.meta.url)), '../client/index.html')\nconst _clientTemplate = existsSync(_clientTemplatePath)\n ? readFileSync(_clientTemplatePath, 'utf-8')\n : null\n\n// Merge the SSR handler's full HTML document with the Vite client shell so the\n// final page contains both pre-rendered DSD content and the client bundle scripts.\nfunction _mergeWithClientTemplate(ssrHtml, clientTemplate) {\n const headTag = '<head>', headCloseTag = '</head>'\n const bodyTag = '<body>', bodyCloseTag = '</body>'\n const headStart = ssrHtml.indexOf(headTag)\n const headEnd = ssrHtml.indexOf(headCloseTag)\n const bodyStart = ssrHtml.indexOf(bodyTag)\n const bodyEnd = ssrHtml.lastIndexOf(bodyCloseTag)\n const ssrHead = headStart >= 0 && headEnd > headStart\n ? ssrHtml.slice(headStart + headTag.length, headEnd).trim() : ''\n const ssrBody = bodyStart >= 0 && bodyEnd > bodyStart\n ? ssrHtml.slice(bodyStart + bodyTag.length, bodyEnd).trim() : ssrHtml\n // Hoist only top-level <style id=...> elements (cer-ssr-jit, cer-ssr-global)\n // from the SSR body into the document <head>. Plain <style> blocks without\n // an id attribute belong to shadow DOM templates and must stay in place \u2014\n // hoisting them to <head> breaks shadow DOM style encapsulation (document\n // styles do not pierce shadow roots), which is the root cause of FOUC.\n const headParts = ssrHead ? [ssrHead] : []\n let ssrBodyContent = ssrBody\n let pos = 0\n while (pos < ssrBodyContent.length) {\n const styleOpen = ssrBodyContent.indexOf('<style id=', pos)\n if (styleOpen < 0) break\n const styleClose = ssrBodyContent.indexOf('</style>', styleOpen)\n if (styleClose < 0) break\n headParts.push(ssrBodyContent.slice(styleOpen, styleClose + 8))\n ssrBodyContent = ssrBodyContent.slice(0, styleOpen) + ssrBodyContent.slice(styleClose + 8)\n pos = styleOpen\n }\n ssrBodyContent = ssrBodyContent.trim()\n // Inject the pre-rendered layout+page as light DOM of the app mount element\n // so it is visible before JS boots, then the client router takes over.\n let merged = clientTemplate\n if (merged.includes('<cer-layout-view></cer-layout-view>')) {\n merged = merged.replace('<cer-layout-view></cer-layout-view>',\n '<cer-layout-view>' + ssrBodyContent + '</cer-layout-view>')\n } else if (merged.includes('<div id=\"app\"></div>')) {\n merged = merged.replace('<div id=\"app\"></div>',\n '<div id=\"app\">' + ssrBodyContent + '</div>')\n }\n const headAdditions = headParts.filter(Boolean).join('\\n')\n if (headAdditions) {\n // If SSR provides a <title>, replace the client template's <title> so the\n // SSR title wins (client template title is the fallback default).\n if (headAdditions.includes('<title>')) {\n merged = merged.replace(/<title>[^<]*<\\/title>/, '')\n }\n merged = merged.replace('</head>', headAdditions + '\\n</head>')\n }\n return merged\n}\n\n/**\n * Per-request VNode factory \u2014 initializes a fresh router at the request URL,\n * resolves the active layout from the matched route's meta, pre-loads the\n * matched page component (bypassing the async router-view so DSD renders\n * synchronously), calls the route's data loader (if any), and injects the\n * serialized result into the document head as window.__CER_DATA__ for\n * client-side hydration.\n *\n * createStreamingSSRHandler threads the router through each component's SSR\n * context so concurrent renders never share state.\n */\nconst vnodeFactory = async (req) => {\n await _pluginsReady\n const router = initRouter({ routes, initialUrl: req.url ?? '/' })\n const current = router.getCurrent()\n const { route, params } = router.matchRoute(current.path)\n const layoutName = route?.meta?.layout ?? 'default'\n const layoutTag = layouts[layoutName]\n\n // Pre-load the page module so we can embed the component tag directly.\n // This avoids the async router-view (which injects content via script tags\n // and breaks Declarative Shadow DOM on initial parse).\n let pageVnode = { tag: 'div', props: {}, children: [] }\n let head\n if (route?.load) {\n try {\n const mod = await route.load()\n const pageTag = mod.default\n if (pageTag) {\n pageVnode = { tag: pageTag, props: { attrs: { ...params } }, children: [] }\n }\n if (typeof mod.loader === 'function') {\n const query = current.query ?? {}\n const data = await mod.loader({ params, query, req })\n if (data !== undefined && data !== null) {\n // Make data available to usePageData() during the SSR render pass.\n // enterWith() scopes the value to the current async context so\n // concurrent renders (SSG concurrency > 1) never share data.\n _cerDataStore.enterWith(data)\n head = `<script>window.__CER_DATA__ = ${JSON.stringify(data)}</script>`\n }\n }\n } catch {\n // Non-fatal: loader errors fall back to an empty page; client will refetch.\n }\n }\n\n const vnode = layoutTag\n ? { tag: layoutTag, props: {}, children: [pageVnode] }\n : pageVnode\n\n return { vnode, router, head }\n}\n\n// Capture the raw SSR handler and wrap it to merge the response with the\n// Vite client template before sending \u2014 this injects the JS/CSS asset bundles\n// so the browser can hydrate and enable client-side routing.\nconst _rawHandler = createSSRHandler(vnodeFactory, {\n render: { dsd: true, dsdPolyfill: false },\n})\n\n/**\n * The main request handler.\n * Compatible with Express, Fastify, and Node's raw http.createServer.\n *\n * Each request is run inside a fresh _cerDataStore.run() context so that\n * concurrent renders (e.g. SSG with concurrency > 1) get isolated stores.\n * vnodeFactory calls _cerDataStore.enterWith(loaderData) from within this\n * context, making the data visible to usePageData() during SSR rendering\n * without any global-state races.\n */\nexport const handler = async (req, res) => {\n if (!_clientTemplate) {\n // No client template \u2014 run handler normally, then inject DSD polyfill.\n let _html = ''\n await _cerDataStore.run(null, async () => {\n await _rawHandler(req, { setHeader: () => {}, end: (body) => { _html = body } })\n })\n // Inject DSD polyfill at end of <body>, outside any custom element light DOM.\n const _final = _html.includes('</body>')\n ? _html.replace('</body>', DSD_POLYFILL_SCRIPT + '</body>')\n : _html + DSD_POLYFILL_SCRIPT\n res.setHeader('Content-Type', 'text/html; charset=utf-8')\n return res.end(_final)\n }\n let _capturedHtml = ''\n // Wrap _rawHandler in an isolated async-local-storage context so that\n // vnodeFactory's enterWith() call is scoped to this request only.\n await _cerDataStore.run(null, async () => {\n // Omit write() to force the non-streaming collect-then-end code path.\n await _rawHandler(req, { setHeader: () => {}, end: (body) => { _capturedHtml = body } })\n })\n let _merged = _mergeWithClientTemplate(_capturedHtml, _clientTemplate)\n // Inject DSD polyfill at end of <body>, outside <cer-layout-view> light DOM.\n _merged = _merged.includes('</body>')\n ? _merged.replace('</body>', DSD_POLYFILL_SCRIPT + '</body>')\n : _merged + DSD_POLYFILL_SCRIPT\n res.setHeader('Content-Type', 'text/html; charset=utf-8')\n res.end(_merged)\n}\n\nexport { apiRoutes, plugins, layouts, routes }\nexport default handler\n";
9
9
  //# sourceMappingURL=entry-server-template.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"entry-server-template.d.ts","sourceRoot":"","sources":["../../src/runtime/entry-server-template.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,2qSAkMjC,CAAA"}
1
+ {"version":3,"file":"entry-server-template.d.ts","sourceRoot":"","sources":["../../src/runtime/entry-server-template.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,m6TAsNjC,CAAA"}
@@ -28,6 +28,25 @@ registerBuiltinComponents()
28
28
  // minimal set (&lt;, &gt;, &amp; …) and re-escapes everything else.
29
29
  registerEntityMap(entitiesJson)
30
30
 
31
+ // Run plugins once at server startup so their provide() values are available
32
+ // to useInject() during every SSR render pass. Stored on globalThis so all
33
+ // dynamically-imported page chunks share the same reference (same pattern as
34
+ // __CER_HEAD_COLLECTOR__ and __CER_DATA_STORE__).
35
+ const _pluginProvides = new Map()
36
+ ;(globalThis).__cerPluginProvides = _pluginProvides
37
+ const _pluginsReady = (async () => {
38
+ const _bootstrapRouter = initRouter({ routes })
39
+ for (const plugin of plugins) {
40
+ if (plugin && typeof plugin.setup === 'function') {
41
+ await plugin.setup({
42
+ router: _bootstrapRouter,
43
+ provide: (key, value) => _pluginProvides.set(key, value),
44
+ config: {},
45
+ })
46
+ }
47
+ }
48
+ })()
49
+
31
50
  // Async-local storage for request-scoped SSR loader data.
32
51
  // Using AsyncLocalStorage ensures concurrent SSR renders (e.g. SSG with
33
52
  // concurrency > 1) never see each other's data — each request's async chain
@@ -109,6 +128,7 @@ function _mergeWithClientTemplate(ssrHtml, clientTemplate) {
109
128
  * context so concurrent renders never share state.
110
129
  */
111
130
  const vnodeFactory = async (req) => {
131
+ await _pluginsReady
112
132
  const router = initRouter({ routes, initialUrl: req.url ?? '/' })
113
133
  const current = router.getCurrent()
114
134
  const { route, params } = router.matchRoute(current.path)
@@ -1 +1 @@
1
- {"version":3,"file":"entry-server-template.js","sourceRoot":"","sources":["../../src/runtime/entry-server-template.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkMpC,CAAA"}
1
+ {"version":3,"file":"entry-server-template.js","sourceRoot":"","sources":["../../src/runtime/entry-server-template.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsNpC,CAAA"}
@@ -10,7 +10,6 @@ export interface JitCssConfig {
10
10
  }
11
11
  export interface SsrConfig {
12
12
  dsd?: boolean;
13
- streaming?: boolean;
14
13
  }
15
14
  export interface AutoImportsConfig {
16
15
  components?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6CAA6C,CAAA;AAE/E,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAA;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,SAAS,CAAA;IACf,MAAM,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,CAAC,CAAA;IACxD,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,GAAG,CAAC,EAAE,SAAS,CAAA;IACf,WAAW,CAAC,EAAE,iBAAiB,CAAA;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAE/D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6CAA6C,CAAA;AAE/E,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAA;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,SAAS,CAAA;IACf,MAAM,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,CAAC,CAAA;IACxD,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,GAAG,CAAC,EAAE,SAAS,CAAA;IACf,WAAW,CAAC,EAAE,iBAAiB,CAAA;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAE/D"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAoCA,MAAM,UAAU,YAAY,CAAC,MAAoB;IAC/C,OAAO,MAAM,CAAA;AACf,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAmCA,MAAM,UAAU,YAAY,CAAC,MAAoB;IAC/C,OAAO,MAAM,CAAA;AACf,CAAC"}
package/docs/cli.md CHANGED
@@ -187,7 +187,7 @@ my-app/
187
187
  | Mode | `cer.config.ts` | `package.json` scripts |
188
188
  |---|---|---|
189
189
  | SPA | `mode: 'spa'` | `dev`, `build`, `preview` |
190
- | SSR | `mode: 'ssr'`, `ssr.streaming: true` | `dev`, `build`, `preview --ssr` |
190
+ | SSR | `mode: 'ssr'`, `ssr.dsd: true` | `dev`, `build`, `preview --ssr` |
191
191
  | SSG | `mode: 'ssg'`, `ssg.routes: 'auto'` | `dev`, `build`, `preview`, `generate` |
192
192
 
193
193
  ---
@@ -104,3 +104,40 @@ export function useSession() { return session }
104
104
  ```
105
105
 
106
106
  Use `useOnConnected` or lazy initialization inside the function body for side effects.
107
+
108
+ ---
109
+
110
+ ## Built-in framework composables
111
+
112
+ These composables are provided by the framework and auto-imported alongside the runtime. They do **not** live in `app/composables/` — they are injected from `@jasonshimmy/vite-plugin-cer-app/composables`.
113
+
114
+ ### `useHead(input)`
115
+
116
+ Sets document head tags (`<title>`, `<meta>`, `<link>`, etc.). Works in SPA, SSR, and SSG modes. See [head-management.md](./head-management.md).
117
+
118
+ ### `usePageData<T>()`
119
+
120
+ Returns the serialized loader data for the current page, hydrated from `window.__CER_DATA__` on the client or from the per-request `AsyncLocalStorage` context during SSR/SSG. See [data-loading.md](./data-loading.md).
121
+
122
+ ### `useInject<T>(key, defaultValue?)`
123
+
124
+ Reads a value provided by a plugin via `app.provide(key, value)`. Works consistently in all rendering modes:
125
+
126
+ - **SPA / client** — resolves via `inject()` from the component context tree.
127
+ - **SSR / SSG** — reads from `globalThis.__cerPluginProvides`, populated by the server entry before the first render.
128
+
129
+ ```ts
130
+ // app/pages/dashboard.ts
131
+ component('page-dashboard', () => {
132
+ const store = useInject<Store>('store')
133
+ return html`<p>Count: ${store?.state.count ?? 0}</p>`
134
+ })
135
+ ```
136
+
137
+ If you need it outside auto-imported directories, import explicitly:
138
+
139
+ ```ts
140
+ import { useInject } from '@jasonshimmy/vite-plugin-cer-app/composables'
141
+ ```
142
+
143
+ > **Note:** Prefer `useInject` over the raw `inject()` primitive whenever reading plugin-provided values. Raw `inject()` works in SPA mode but returns `undefined` in SSR and SSG because the server renders components without `<cer-layout-view>`'s provide context.
@@ -13,7 +13,6 @@ export default defineConfig({
13
13
 
14
14
  ssr: {
15
15
  dsd: true,
16
- streaming: false,
17
16
  },
18
17
 
19
18
  ssg: {
@@ -87,8 +86,7 @@ Controls SSR rendering behavior.
87
86
 
88
87
  ```ts
89
88
  ssr: {
90
- dsd: true, // Emit Declarative Shadow DOM
91
- streaming: false // Reserved for future use
89
+ dsd: true, // Emit Declarative Shadow DOM
92
90
  }
93
91
  ```
94
92
 
@@ -99,13 +97,6 @@ ssr: {
99
97
 
100
98
  When `true`, renders components with [Declarative Shadow DOM](https://developer.chrome.com/docs/css-ui/declarative-shadow-dom) markup. This eliminates Flash of Unstyled Content (FOUC) because styles are embedded directly in the HTML.
101
99
 
102
- ### `ssr.streaming`
103
-
104
- **Type:** `boolean`
105
- **Default:** `false`
106
-
107
- Reserved for future use. Currently, the SSR renderer always collects the full HTML string before sending the response.
108
-
109
100
  ---
110
101
 
111
102
  ## `ssg` options
@@ -247,7 +238,7 @@ export default defineConfig({
247
238
  plugins: [
248
239
  cerApp({
249
240
  mode: 'ssr',
250
- ssr: { dsd: true, streaming: true },
241
+ ssr: { dsd: true },
251
242
  }),
252
243
  ],
253
244
  })
@@ -128,110 +128,12 @@ component('layout-default', () => {
128
128
  </head>
129
129
  <body>
130
130
  <cer-layout-view></cer-layout-view>
131
- <script type="module" src="/app/app.ts"></script>
131
+ <script type="module" src="/.cer/app.ts"></script>
132
132
  </body>
133
133
  </html>
134
134
  ```
135
135
 
136
- ### 7. Create `app/app.ts` (auto-generated if absent)
137
-
138
- The framework generates this file when you scaffold a new project. It bootstraps the router, registers all auto-discovered components, runs plugins, and mounts the app:
139
-
140
- ```ts
141
- // app/app.ts
142
- import '@jasonshimmy/custom-elements-runtime/css'
143
- import 'virtual:cer-jit-css'
144
- import 'virtual:cer-components'
145
- import routes from 'virtual:cer-routes'
146
- import layouts from 'virtual:cer-layouts'
147
- import plugins from 'virtual:cer-plugins'
148
- import { hasLoading, loadingTag } from 'virtual:cer-loading'
149
- import { hasError, errorTag } from 'virtual:cer-error'
150
- import {
151
- component, ref, provide,
152
- useOnConnected, useOnDisconnected,
153
- registerBuiltinComponents,
154
- } from '@jasonshimmy/custom-elements-runtime'
155
- import { initRouter } from '@jasonshimmy/custom-elements-runtime/router'
156
- import { enableJITCSS } from '@jasonshimmy/custom-elements-runtime/jit-css'
157
-
158
- registerBuiltinComponents()
159
- enableJITCSS()
160
-
161
- const router = initRouter({ routes })
162
-
163
- const isNavigating = ref(false)
164
- const currentError = ref(null)
165
- ;(globalThis as any).resetError = () => {
166
- currentError.value = null
167
- router.replace(router.getCurrent().path)
168
- }
169
-
170
- const _push = router.push.bind(router)
171
- const _replace = router.replace.bind(router)
172
- router.push = async (path) => {
173
- isNavigating.value = true; currentError.value = null
174
- try { await _push(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
175
- }
176
- router.replace = async (path) => {
177
- isNavigating.value = true; currentError.value = null
178
- try { await _replace(path) } catch (err) { currentError.value = err instanceof Error ? err.message : String(err) } finally { isNavigating.value = false }
179
- }
180
-
181
- // _pluginProvides is populated by plugin setup and forwarded into the component
182
- // context tree via provide() inside cer-layout-view so inject() works in all modes.
183
- // Also exposed on globalThis for the SSG timing edge case — see docs/plugins.md.
184
- const _pluginProvides = new Map<string, unknown>()
185
- ;(globalThis as any).__cerPluginProvides = _pluginProvides
186
-
187
- component('cer-layout-view', () => {
188
- for (const [key, value] of _pluginProvides) { provide(key, value) }
189
-
190
- const current = ref(router.getCurrent())
191
- let unsub: (() => void) | undefined
192
- useOnConnected(() => { unsub = router.subscribe((s) => { current.value = s }) })
193
- useOnDisconnected(() => { unsub?.(); unsub = undefined })
194
-
195
- if (currentError.value !== null) {
196
- if (hasError && errorTag) return { tag: errorTag, props: { attrs: { error: String(currentError.value) } }, children: [] }
197
- return { tag: 'div', props: { attrs: { style: 'padding:2rem;font-family:monospace' } }, children: String(currentError.value) }
198
- }
199
- if (isNavigating.value && hasLoading && loadingTag) return { tag: loadingTag, props: {}, children: [] }
200
-
201
- const matched = router.matchRoute(current.value.path)
202
- const layoutName = (matched?.route as any)?.meta?.layout ?? 'default'
203
- const layoutTag = (layouts as Record<string, string>)[layoutName]
204
- const routerView = { tag: 'router-view', props: {}, children: [] }
205
- return layoutTag ? { tag: layoutTag, props: {}, children: [routerView] } : routerView
206
- })
207
-
208
- // Plugins run AFTER cer-layout-view is defined so provide() calls from plugins
209
- // are forwarded into the component tree on the very first render.
210
- for (const plugin of plugins ?? []) {
211
- if (plugin && typeof plugin.setup === 'function') {
212
- await plugin.setup({ router, provide: (key, value) => { _pluginProvides.set(key, value) }, config: {} })
213
- }
214
- }
215
-
216
- // Pre-load the current page's route chunk AFTER plugins run.
217
- // This ensures cer-layout-view's first render (and its provide() calls) completes
218
- // before page component modules are imported and their renders are scheduled.
219
- if (typeof window !== 'undefined') {
220
- const _initMatch = router.matchRoute(window.location.pathname)
221
- if (_initMatch?.route?.load) {
222
- try { await _initMatch.route.load() } catch { /* non-fatal */ }
223
- }
224
- }
225
-
226
- if (typeof window !== 'undefined') {
227
- await _replace(window.location.pathname + window.location.search + window.location.hash)
228
- delete (globalThis as any).__CER_DATA__
229
- }
230
-
231
- export { router }
232
- ```
233
-
234
- > **Note:** Do not move the plugin loop before `component('cer-layout-view', …)`. The layout component must be defined first so that when plugins call `app.provide()`, the values are available to the component tree from the very first render. See [Plugins](plugins.md) for details.
136
+ > **Note:** The framework bootstrap lives in `.cer/app.ts` and is regenerated automatically on every dev server start and build. You never edit or own this file — updates to the plugin propagate to it immediately, just like Nuxt's `.nuxt/` directory.
235
137
 
236
138
  ---
237
139
 
package/docs/plugins.md CHANGED
@@ -31,9 +31,9 @@ interface AppPlugin {
31
31
  }
32
32
 
33
33
  interface AppContext {
34
- provide(key: string, value: unknown): void
34
+ provide(key: PropertyKey, value: unknown): void
35
35
  router: Router
36
- config: ResolvedCerConfig
36
+ config: CerAppConfig
37
37
  }
38
38
  ```
39
39
 
@@ -75,8 +75,8 @@ export default {
75
75
  ```ts
76
76
  // app/pages/index.ts
77
77
  component('page-index', () => {
78
- const store = inject('store')
79
- const count = computed(() => store.state.count)
78
+ const store = useInject<Store>('store')
79
+ const count = computed(() => store?.state.count ?? 0)
80
80
 
81
81
  return html`<p>Count: ${count}</p>`
82
82
  })
@@ -120,20 +120,14 @@ export default {
120
120
 
121
121
  ---
122
122
 
123
- ## SSG and `inject()`
123
+ ## Reading provided values with `useInject`
124
124
 
125
- In SSG mode there is a timing subtlety: the router loads a page chunk and renders it before `<cer-layout-view>` has called `provide()`. This means `inject()` may return `undefined` on the first render in SSGeven though the plugin ran and called `app.provide()` correctly.
126
-
127
- To write pages that work correctly in **all three modes** (SPA, SSR, SSG), use `globalThis.__cerPluginProvides` as a synchronous fallback when `inject()` returns `undefined`:
125
+ To read values provided by a plugin, use `useInject` instead of the raw `inject()` from the runtime. `useInject` works correctly in **all three modes**SPA, SSR, and SSG:
128
126
 
129
127
  ```ts
130
128
  // app/pages/dashboard.ts
131
129
  component('page-dashboard', () => {
132
- // inject() resolves correctly in SPA and SSR modes.
133
- // In SSG the page chunk may render before cer-layout-view calls provide(),
134
- // so fall back to the global map that app/app.ts populates before any render.
135
- const pluginProvides = (globalThis as any).__cerPluginProvides as Map<string, unknown> | undefined
136
- const store = inject<Store>('store') ?? pluginProvides?.get('store') as Store | undefined
130
+ const store = useInject<Store>('store')
137
131
 
138
132
  if (!store) return html`<p>Loading…</p>`
139
133
 
@@ -141,7 +135,21 @@ component('page-dashboard', () => {
141
135
  })
142
136
  ```
143
137
 
144
- The `__cerPluginProvides` map is written by the bootstrapped `app/app.ts` before any route is rendered, so it is always available as a synchronous fallback regardless of render order.
138
+ `useInject` is auto-imported in `app/pages/`, `app/layouts/`, and `app/components/` no explicit import needed.
139
+
140
+ **Why not raw `inject()`?** In SSR and SSG modes the server renders the component tree directly, without `<cer-layout-view>` establishing the Vue-style provide context. `useInject` bridges this gap:
141
+
142
+ | Mode | How the value is resolved |
143
+ |------|--------------------------|
144
+ | SPA / client | `inject()` walks the component tree (provided by `<cer-layout-view>`) |
145
+ | SSR (dev & prod) | reads from `globalThis.__cerPluginProvides` (set by the server entry at startup) |
146
+ | SSG | same as SSR |
147
+
148
+ If you need `useInject` outside of auto-imported directories, import it explicitly:
149
+
150
+ ```ts
151
+ import { useInject } from '@jasonshimmy/vite-plugin-cer-app/composables'
152
+ ```
145
153
 
146
154
  ---
147
155
 
@@ -154,7 +162,7 @@ import plugins from 'virtual:cer-plugins'
154
162
  // plugins is an array of AppPlugin objects in load order
155
163
  ```
156
164
 
157
- In `app/app.ts`, plugins are executed sequentially before the router initializes:
165
+ In `.cer/app.ts` (the auto-generated framework entry), plugins are executed sequentially before the router initializes:
158
166
 
159
167
  ```ts
160
168
  for (const plugin of plugins) {