@jasonshimmy/vite-plugin-cer-app 0.28.2 → 0.29.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 (184) hide show
  1. package/README.md +2 -0
  2. package/dist/cli/adapters/cloudflare.d.ts +2 -2
  3. package/dist/cli/adapters/cloudflare.d.ts.map +1 -1
  4. package/dist/cli/adapters/cloudflare.js +83 -159
  5. package/dist/cli/adapters/cloudflare.js.map +1 -1
  6. package/dist/cli/adapters/deployment-mode.d.ts +10 -0
  7. package/dist/cli/adapters/deployment-mode.d.ts.map +1 -0
  8. package/dist/cli/adapters/deployment-mode.js +59 -0
  9. package/dist/cli/adapters/deployment-mode.js.map +1 -0
  10. package/dist/cli/adapters/netlify.d.ts +5 -4
  11. package/dist/cli/adapters/netlify.d.ts.map +1 -1
  12. package/dist/cli/adapters/netlify.js +26 -150
  13. package/dist/cli/adapters/netlify.js.map +1 -1
  14. package/dist/cli/adapters/vercel.d.ts +2 -3
  15. package/dist/cli/adapters/vercel.d.ts.map +1 -1
  16. package/dist/cli/adapters/vercel.js +36 -111
  17. package/dist/cli/adapters/vercel.js.map +1 -1
  18. package/dist/cli/adapters/web-bridge.d.ts +3 -0
  19. package/dist/cli/adapters/web-bridge.d.ts.map +1 -0
  20. package/dist/cli/adapters/web-bridge.js +110 -0
  21. package/dist/cli/adapters/web-bridge.js.map +1 -0
  22. package/dist/cli/checks/lighthouse.d.ts +7 -1
  23. package/dist/cli/checks/lighthouse.d.ts.map +1 -1
  24. package/dist/cli/checks/lighthouse.js +49 -44
  25. package/dist/cli/checks/lighthouse.js.map +1 -1
  26. package/dist/cli/checks/links.js.map +1 -1
  27. package/dist/cli/checks/performance.d.ts +3 -0
  28. package/dist/cli/checks/performance.d.ts.map +1 -1
  29. package/dist/cli/checks/performance.js +56 -15
  30. package/dist/cli/checks/performance.js.map +1 -1
  31. package/dist/cli/checks/quality.d.ts +35 -0
  32. package/dist/cli/checks/quality.d.ts.map +1 -0
  33. package/dist/cli/checks/quality.js +72 -0
  34. package/dist/cli/checks/quality.js.map +1 -0
  35. package/dist/cli/checks/seo.d.ts +9 -0
  36. package/dist/cli/checks/seo.d.ts.map +1 -0
  37. package/dist/cli/checks/seo.js +40 -0
  38. package/dist/cli/checks/seo.js.map +1 -0
  39. package/dist/cli/commands/adapt.js.map +1 -1
  40. package/dist/cli/commands/build.d.ts.map +1 -1
  41. package/dist/cli/commands/build.js +2 -51
  42. package/dist/cli/commands/build.js.map +1 -1
  43. package/dist/cli/commands/check.d.ts.map +1 -1
  44. package/dist/cli/commands/check.js +12 -0
  45. package/dist/cli/commands/check.js.map +1 -1
  46. package/dist/cli/commands/dev.d.ts +4 -0
  47. package/dist/cli/commands/dev.d.ts.map +1 -1
  48. package/dist/cli/commands/dev.js +1 -72
  49. package/dist/cli/commands/dev.js.map +1 -1
  50. package/dist/cli/commands/generate.d.ts +1 -4
  51. package/dist/cli/commands/generate.d.ts.map +1 -1
  52. package/dist/cli/commands/generate.js +4 -73
  53. package/dist/cli/commands/generate.js.map +1 -1
  54. package/dist/cli/commands/preview-paths.d.ts +3 -0
  55. package/dist/cli/commands/preview-paths.d.ts.map +1 -0
  56. package/dist/cli/commands/preview-paths.js +8 -0
  57. package/dist/cli/commands/preview-paths.js.map +1 -0
  58. package/dist/cli/commands/preview.d.ts +11 -0
  59. package/dist/cli/commands/preview.d.ts.map +1 -1
  60. package/dist/cli/commands/preview.js +124 -458
  61. package/dist/cli/commands/preview.js.map +1 -1
  62. package/dist/cli/config.d.ts +3 -0
  63. package/dist/cli/config.d.ts.map +1 -0
  64. package/dist/cli/config.js +68 -0
  65. package/dist/cli/config.js.map +1 -0
  66. package/dist/cli/create/index.js.map +1 -1
  67. package/dist/cli/create/templates/spa/package.json.tpl +1 -1
  68. package/dist/cli/create/templates/ssg/package.json.tpl +1 -1
  69. package/dist/cli/create/templates/ssr/package.json.tpl +1 -1
  70. package/dist/plugin/build-ssg.d.ts.map +1 -1
  71. package/dist/plugin/build-ssg.js +81 -15
  72. package/dist/plugin/build-ssg.js.map +1 -1
  73. package/dist/plugin/build-ssr.d.ts.map +1 -1
  74. package/dist/plugin/build-ssr.js +3 -1
  75. package/dist/plugin/build-ssr.js.map +1 -1
  76. package/dist/plugin/content/scanner.d.ts.map +1 -1
  77. package/dist/plugin/content/scanner.js +2 -7
  78. package/dist/plugin/content/scanner.js.map +1 -1
  79. package/dist/plugin/dev-server.d.ts +8 -0
  80. package/dist/plugin/dev-server.d.ts.map +1 -1
  81. package/dist/plugin/dev-server.js.map +1 -1
  82. package/dist/plugin/dts-generator.d.ts.map +1 -1
  83. package/dist/plugin/dts-generator.js +4 -1
  84. package/dist/plugin/dts-generator.js.map +1 -1
  85. package/dist/plugin/file-scanner.d.ts +4 -0
  86. package/dist/plugin/file-scanner.d.ts.map +1 -0
  87. package/dist/plugin/file-scanner.js +59 -0
  88. package/dist/plugin/file-scanner.js.map +1 -0
  89. package/dist/plugin/html-post-process.d.ts +1 -1
  90. package/dist/plugin/html-post-process.d.ts.map +1 -1
  91. package/dist/plugin/html-post-process.js +7 -5
  92. package/dist/plugin/html-post-process.js.map +1 -1
  93. package/dist/plugin/index.d.ts +1 -4
  94. package/dist/plugin/index.d.ts.map +1 -1
  95. package/dist/plugin/index.js +26 -8
  96. package/dist/plugin/index.js.map +1 -1
  97. package/dist/plugin/path-utils.js.map +1 -1
  98. package/dist/plugin/scanner.d.ts +2 -2
  99. package/dist/plugin/scanner.d.ts.map +1 -1
  100. package/dist/plugin/scanner.js +4 -10
  101. package/dist/plugin/scanner.js.map +1 -1
  102. package/dist/plugin/server-content.d.ts +3 -0
  103. package/dist/plugin/server-content.d.ts.map +1 -0
  104. package/dist/plugin/server-content.js +25 -0
  105. package/dist/plugin/server-content.js.map +1 -0
  106. package/dist/plugin/transforms/auto-import.d.ts.map +1 -1
  107. package/dist/plugin/transforms/auto-import.js +6 -5
  108. package/dist/plugin/transforms/auto-import.js.map +1 -1
  109. package/dist/plugin/transforms/head-inject.d.ts.map +1 -1
  110. package/dist/plugin/virtual/composables.js +1 -1
  111. package/dist/plugin/virtual/composables.js.map +1 -1
  112. package/dist/plugin/virtual/content-components.d.ts +3 -1
  113. package/dist/plugin/virtual/content-components.d.ts.map +1 -1
  114. package/dist/plugin/virtual/content-components.js +105 -22
  115. package/dist/plugin/virtual/content-components.js.map +1 -1
  116. package/dist/plugin/virtual/layouts.js +1 -1
  117. package/dist/plugin/virtual/layouts.js.map +1 -1
  118. package/dist/plugin/virtual/middleware.js +1 -1
  119. package/dist/plugin/virtual/middleware.js.map +1 -1
  120. package/dist/plugin/virtual/plugins.js +1 -1
  121. package/dist/plugin/virtual/plugins.js.map +1 -1
  122. package/dist/plugin/virtual/routes.js +1 -1
  123. package/dist/plugin/virtual/routes.js.map +1 -1
  124. package/dist/plugin/virtual/server-api.js +1 -1
  125. package/dist/plugin/virtual/server-api.js.map +1 -1
  126. package/dist/plugin/virtual/server-middleware.js +1 -1
  127. package/dist/plugin/virtual/server-middleware.js.map +1 -1
  128. package/dist/runtime/app-template.d.ts.map +1 -1
  129. package/dist/runtime/app-template.js +18 -8
  130. package/dist/runtime/app-template.js.map +1 -1
  131. package/dist/runtime/composables/index.d.ts +2 -0
  132. package/dist/runtime/composables/index.d.ts.map +1 -1
  133. package/dist/runtime/composables/index.js +1 -0
  134. package/dist/runtime/composables/index.js.map +1 -1
  135. package/dist/runtime/composables/render-content.d.ts +10 -0
  136. package/dist/runtime/composables/render-content.d.ts.map +1 -0
  137. package/dist/runtime/composables/render-content.js +21 -0
  138. package/dist/runtime/composables/render-content.js.map +1 -0
  139. package/dist/runtime/composables/use-active-headings.js.map +1 -1
  140. package/dist/runtime/composables/use-auth.d.ts.map +1 -1
  141. package/dist/runtime/composables/use-auth.js +14 -6
  142. package/dist/runtime/composables/use-auth.js.map +1 -1
  143. package/dist/runtime/composables/use-content-page.d.ts +2 -0
  144. package/dist/runtime/composables/use-content-page.d.ts.map +1 -1
  145. package/dist/runtime/composables/use-content-page.js +1 -1
  146. package/dist/runtime/composables/use-content-page.js.map +1 -1
  147. package/dist/runtime/composables/use-content.d.ts.map +1 -1
  148. package/dist/runtime/composables/use-content.js.map +1 -1
  149. package/dist/runtime/composables/use-cookie.js.map +1 -1
  150. package/dist/runtime/composables/use-runtime-config.js.map +1 -1
  151. package/dist/runtime/composables/use-session.js.map +1 -1
  152. package/dist/runtime/content/client.d.ts +2 -3
  153. package/dist/runtime/content/client.d.ts.map +1 -1
  154. package/dist/runtime/content/client.js +4 -5
  155. package/dist/runtime/content/client.js.map +1 -1
  156. package/dist/runtime/entry-server-template.d.ts.map +1 -1
  157. package/dist/runtime/entry-server-template.js +45 -15
  158. package/dist/runtime/entry-server-template.js.map +1 -1
  159. package/dist/runtime/isr-handler.d.ts +4 -15
  160. package/dist/runtime/isr-handler.d.ts.map +1 -1
  161. package/dist/runtime/isr-handler.js +64 -45
  162. package/dist/runtime/isr-handler.js.map +1 -1
  163. package/dist/runtime/request-dispatcher.d.ts +31 -0
  164. package/dist/runtime/request-dispatcher.d.ts.map +1 -0
  165. package/dist/runtime/request-dispatcher.js +152 -0
  166. package/dist/runtime/request-dispatcher.js.map +1 -0
  167. package/dist/runtime/route-matching.d.ts +20 -0
  168. package/dist/runtime/route-matching.d.ts.map +1 -0
  169. package/dist/runtime/route-matching.js +45 -0
  170. package/dist/runtime/route-matching.js.map +1 -0
  171. package/dist/types/api.d.ts +3 -0
  172. package/dist/types/api.d.ts.map +1 -1
  173. package/dist/types/config.d.ts +27 -0
  174. package/dist/types/config.d.ts.map +1 -1
  175. package/dist/types/config.js.map +1 -1
  176. package/dist/types/content.d.ts +2 -0
  177. package/dist/types/content.d.ts.map +1 -1
  178. package/dist/types/page.d.ts +11 -7
  179. package/dist/types/page.d.ts.map +1 -1
  180. package/package.json +30 -16
  181. package/dist/cli/commands/preview-isr.d.ts +0 -66
  182. package/dist/cli/commands/preview-isr.d.ts.map +0 -1
  183. package/dist/cli/commands/preview-isr.js +0 -132
  184. package/dist/cli/commands/preview-isr.js.map +0 -1
package/README.md CHANGED
@@ -273,3 +273,5 @@ For a read-only CI/release check, use `npm run check:runtime`. It exits non-zero
273
273
  ## License
274
274
 
275
275
  MIT
276
+
277
+ See [production content, islands and quality checks](docs/production-content.md) for complete Markdown SSR, selective content imports, Vite preview, static CSS and release gates.
@@ -9,8 +9,8 @@
9
9
  * generated `_worker.js` (Cloudflare Pages Advanced Mode convention).
10
10
  * The worker sets globalThis.__CER_CLIENT_TEMPLATE__ before dynamically
11
11
  * importing the server bundle, bypassing the node:fs call in server.js.
12
- * - Copies content-hashed assets and other public files to dist/ alongside
13
- * _worker.js — Cloudflare Pages serves them from the CDN before the worker.
12
+ * - Bundles the server graph into _worker.js and removes server intermediates.
13
+ * - Copies public files to dist/; the worker forwards asset requests to ASSETS.
14
14
  * - Writes `wrangler.toml` with nodejs_compat for AsyncLocalStorage + streams.
15
15
  *
16
16
  * SPA/SSG mode:
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../../src/cli/adapters/cloudflare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AA2LH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BtE"}
1
+ {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../../src/cli/adapters/cloudflare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAwGH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBtE"}
@@ -9,8 +9,8 @@
9
9
  * generated `_worker.js` (Cloudflare Pages Advanced Mode convention).
10
10
  * The worker sets globalThis.__CER_CLIENT_TEMPLATE__ before dynamically
11
11
  * importing the server bundle, bypassing the node:fs call in server.js.
12
- * - Copies content-hashed assets and other public files to dist/ alongside
13
- * _worker.js — Cloudflare Pages serves them from the CDN before the worker.
12
+ * - Bundles the server graph into _worker.js and removes server intermediates.
13
+ * - Copies public files to dist/; the worker forwards asset requests to ASSETS.
14
14
  * - Writes `wrangler.toml` with nodejs_compat for AsyncLocalStorage + streams.
15
15
  *
16
16
  * SPA/SSG mode:
@@ -24,7 +24,11 @@
24
24
  * https://developers.cloudflare.com/workers/runtime-apis/nodejs/
25
25
  */
26
26
  import { join } from 'pathe';
27
- import { existsSync, mkdirSync, writeFileSync, readFileSync, copyFileSync, cpSync, readdirSync, statSync, } from 'node:fs';
27
+ import { requiresServer, prerenderedPaths, prerenderedFallback } from './deployment-mode.js';
28
+ import { WEB_BRIDGE } from './web-bridge.js';
29
+ import { build } from 'vite';
30
+ import { gzipSync } from 'node:zlib';
31
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, copyFileSync, cpSync, readdirSync, statSync, rmSync, } from 'node:fs';
28
32
  // ─── Worker bridge template ───────────────────────────────────────────────────
29
33
  /**
30
34
  * Generates the _worker.js content with the client HTML inlined.
@@ -41,7 +45,7 @@ import { existsSync, mkdirSync, writeFileSync, readFileSync, copyFileSync, cpSyn
41
45
  * buffered until the full page is rendered.
42
46
  * - Exports `{ fetch }` — the Cloudflare Pages _worker.js module format.
43
47
  */
44
- function generateWorkerBridge(clientHtml) {
48
+ function generateWorkerBridge(clientHtml, paths, fallback) {
45
49
  // Escape the HTML for embedding in a JS template literal.
46
50
  const escaped = clientHtml.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
47
51
  return `\
@@ -49,146 +53,58 @@ function generateWorkerBridge(clientHtml) {
49
53
  // Cloudflare Pages _worker.js — Advanced Mode SSR bridge.
50
54
  // Requires compatibility_flags = ["nodejs_compat"] in wrangler.toml.
51
55
  import { Readable } from 'node:stream'
56
+ import { EventEmitter } from 'node:events'
52
57
 
53
58
  // Inline the client HTML template so the server bundle does not need node:fs.
54
59
  // Must be set before the dynamic import below resolves.
55
60
  globalThis.__CER_CLIENT_TEMPLATE__ = \`${escaped}\`
56
61
 
57
- const { isrHandler, apiRoutes, runServerMiddleware, runWithRequestContext } = await import('./server/server.js')
58
-
59
- function matchApiPattern(pattern, urlPath) {
60
- const pp = pattern.split('/')
61
- const up = urlPath.split('/')
62
- if (pp.length !== up.length) return null
63
- const params = {}
64
- for (let i = 0; i < pp.length; i++) {
65
- if (pp[i].startsWith(':')) params[pp[i].slice(1)] = decodeURIComponent(up[i])
66
- else if (pp[i] !== up[i]) return null
67
- }
68
- return params
69
- }
70
-
71
- function parseQuery(search) {
72
- if (!search) return {}
73
- const qs = search.startsWith('?') ? search.slice(1) : search
74
- const result = {}
75
- for (const part of qs.split('&')) {
76
- if (!part) continue
77
- const eqIdx = part.indexOf('=')
78
- if (eqIdx === -1) result[decodeURIComponent(part)] = ''
79
- else result[decodeURIComponent(part.slice(0, eqIdx))] = decodeURIComponent(part.slice(eqIdx + 1))
80
- }
81
- return result
82
- }
83
-
84
- async function toNodeRequest(webReq) {
85
- const url = new URL(webReq.url)
86
- const hasBody = webReq.body !== null
87
- && webReq.method !== 'GET'
88
- && webReq.method !== 'HEAD'
89
- const body = hasBody ? Buffer.from(await webReq.arrayBuffer()) : null
90
- const req = Object.assign(
91
- body ? Readable.from([body]) : Readable.from([]),
92
- {
93
- url: url.pathname + url.search,
94
- method: webReq.method,
95
- headers: Object.fromEntries(webReq.headers.entries()),
96
- },
97
- )
98
- req.query = parseQuery(url.search)
99
- if (body !== null) {
100
- const ct = webReq.headers.get('content-type') ?? ''
101
- if (ct.includes('application/json')) {
102
- try { req.body = JSON.parse(body.toString('utf-8')) } catch { req.body = undefined }
103
- } else {
104
- req.body = body
62
+ const app = await import('./server/server.js')
63
+ ${WEB_BRIDGE}
64
+ const paths = ${JSON.stringify(paths)}
65
+ const dispatchers = new WeakMap()
66
+ async function readDocument(path, req, assets) {
67
+ let url = new URL(path, 'https://' + req.headers.host)
68
+ for (let redirects = 0; redirects < 5; redirects++) {
69
+ const response = await assets.fetch(new Request(url))
70
+ const location = response.headers.get('location')
71
+ if (response.status >= 300 && response.status < 400 && location) {
72
+ await response.body?.cancel()
73
+ const next = new URL(location, url)
74
+ if (next.origin !== url.origin) return null
75
+ url = next
76
+ continue
105
77
  }
78
+ if (response.status !== 200) { await response.body?.cancel(); return null }
79
+ return response.text()
106
80
  }
107
- return req
81
+ return null
108
82
  }
109
-
110
- function createNodeResponse() {
111
- const { readable, writable } = new TransformStream()
112
- const writer = writable.getWriter()
113
- const encoder = new TextEncoder()
114
- const headers = {}
115
- let _resolve
116
- let _ended = false
117
- const promise = new Promise((res) => { _resolve = res })
118
- const res = {
119
- statusCode: 200,
120
- setHeader(name, value) { headers[name.toLowerCase()] = String(value) },
121
- getHeader(name) { return headers[name.toLowerCase()] },
122
- removeHeader(name) { delete headers[name.toLowerCase()] },
123
- write(chunk) {
124
- if (_ended) return
125
- void writer.write(typeof chunk === 'string' ? encoder.encode(chunk) : chunk).catch(() => {})
126
- },
127
- end(chunk) {
128
- if (_ended) return
129
- _ended = true
130
- if (chunk) void writer.write(typeof chunk === 'string' ? encoder.encode(chunk) : chunk).catch(() => {})
131
- void writer.close().catch(() => {})
132
- _resolve(new Response(readable, { status: res.statusCode, headers }))
133
- },
134
- json(data) {
135
- this.setHeader('Content-Type', 'application/json; charset=utf-8')
136
- this.end(JSON.stringify(data))
137
- },
138
- status(code) { this.statusCode = code; return this },
139
- get writableEnded() { return _ended },
140
- }
141
- return { res, promise }
83
+ function dispatcher(assets) {
84
+ if (!assets || !app.createDispatchRequest) return app.dispatchRequest
85
+ if (!dispatchers.has(assets)) dispatchers.set(assets, app.createDispatchRequest({ prerendered: {
86
+ paths, fallback: ${JSON.stringify(fallback)},
87
+ read: (path, req) => readDocument(path === '/' ? '/index.html' : path.split('/').map(encodeURIComponent).join('/') + '/index.html', req, assets),
88
+ notFound: (req) => readDocument('/404.html', req, assets),
89
+ } }))
90
+ return dispatchers.get(assets)
142
91
  }
143
92
 
144
- async function handleRequest(webReq) {
145
- const url = new URL(webReq.url)
146
- const urlPath = url.pathname
147
- const method = webReq.method ?? 'GET'
148
-
149
- const nodeReq = await toNodeRequest(webReq)
150
- const { res, promise } = createNodeResponse()
151
-
152
- // Run server middleware chain (auth, logging, CORS, etc.).
153
- if (!(await runServerMiddleware(nodeReq, res))) return promise
154
-
155
- // Route /api/* requests to the API handlers exported by the server bundle.
156
- if (urlPath.startsWith('/api/')) {
157
- for (const route of (apiRoutes ?? [])) {
158
- const params = matchApiPattern(route.path, urlPath)
159
- if (params !== null) {
160
- nodeReq.params = params
161
- const fn = route.handlers[method.toLowerCase()]
162
- ?? route.handlers[method.toUpperCase()]
163
- ?? route.handlers['default']
164
- if (typeof fn === 'function') {
165
- // Wrap in request context so useCookie / useSession work in API handlers.
166
- runWithRequestContext(nodeReq, res, () => Promise.resolve(fn(nodeReq, res))).catch(() => {
167
- if (!res.writableEnded) {
168
- res.statusCode = 500
169
- res.end(JSON.stringify({ error: 'Internal Server Error' }))
170
- }
171
- })
172
- return promise
173
- }
174
- }
93
+ export default {
94
+ async fetch(request, env, _ctx) {
95
+ let path
96
+ try { path = decodeURIComponent(new URL(request.url).pathname) }
97
+ catch { return new Response('Bad Request', { status: 400 }) }
98
+ if (['server', 'client', '.vite'].includes(path.split('/')[1]) || ['/ssg-manifest.json', '/cer-client-manifest.json', '/cer-startup-manifest.json'].includes(path)) {
99
+ return new Response('Not Found', { status: 404 })
175
100
  }
176
- return new Response('Not Found', { status: 404 })
177
- }
178
-
179
- // All other requests: SSR (with ISR for routes that declare meta.ssg.revalidate).
180
- isrHandler(nodeReq, res).catch(() => {
181
- if (!res.writableEnded) {
182
- res.statusCode = 500
183
- res.end('Internal Server Error')
101
+ // Advanced-mode workers own all requests, including static assets.
102
+ if (env?.ASSETS && (request.method === 'GET' || request.method === 'HEAD') && !path.startsWith('/api/')) {
103
+ const asset = await env.ASSETS.fetch(request)
104
+ if (asset.status < 400 && !asset.headers.get('content-type')?.includes('text/html')) return asset
105
+ await asset.body?.cancel()
184
106
  }
185
- })
186
- return promise
187
- }
188
-
189
- export default {
190
- async fetch(request, _env, _ctx) {
191
- return handleRequest(request)
107
+ return handleRequest(request, dispatcher(env?.ASSETS))
192
108
  },
193
109
  }
194
110
  `;
@@ -199,11 +115,9 @@ export async function runCloudflareAdapter(root) {
199
115
  if (!existsSync(distDir)) {
200
116
  throw new Error(`[cer-app] No dist/ directory found at ${distDir}. Run 'cer-app build' first.`);
201
117
  }
202
- const serverBundle = join(distDir, 'server/server.js');
203
- const ssgManifest = join(distDir, 'ssg-manifest.json');
204
- const isSSR = existsSync(serverBundle) && !existsSync(ssgManifest);
118
+ const isSSR = requiresServer(distDir);
205
119
  if (isSSR) {
206
- _buildSSR(root, distDir);
120
+ await _buildSSR(root, distDir);
207
121
  }
208
122
  else {
209
123
  _buildStatic(root, distDir);
@@ -219,34 +133,39 @@ export async function runCloudflareAdapter(root) {
219
133
  }
220
134
  }
221
135
  // ─── SSR output ───────────────────────────────────────────────────────────────
222
- function _buildSSR(root, distDir) {
136
+ async function _buildSSR(root, distDir) {
223
137
  const clientHtmlPath = join(distDir, 'client/index.html');
224
138
  const clientHtml = existsSync(clientHtmlPath)
225
139
  ? readFileSync(clientHtmlPath, 'utf-8')
226
140
  : '';
227
141
  // Write the worker bridge with inlined client HTML.
228
142
  const workerPath = join(distDir, '_worker.js');
229
- writeFileSync(workerPath, generateWorkerBridge(clientHtml));
230
- // P1-4: Warn when the worker file approaches or exceeds Cloudflare's size limits.
231
- // Free plan: 1 MB compressed; Paid plan: 10 MB compressed. Check uncompressed as
232
- // a conservative proxy — the compressed size will always be smaller.
233
- const workerSizeBytes = statSync(workerPath).size;
234
- const WARN_LIMIT = 900_000; // 900 KB — approaching Free plan limit
235
- const ERROR_LIMIT = 9_000_000; // 9 MB — approaching Paid plan limit
236
- if (workerSizeBytes > ERROR_LIMIT) {
237
- console.error(`[cer-app] Cloudflare _worker.js is ${(workerSizeBytes / 1e6).toFixed(1)} MB — ` +
238
- `exceeds the ${(ERROR_LIMIT / 1e6).toFixed(0)} MB limit. Deployment will likely fail.`);
239
- process.exit(1);
240
- }
241
- else if (workerSizeBytes > WARN_LIMIT) {
242
- console.warn(`[cer-app] Cloudflare _worker.js is ${(workerSizeBytes / 1e3).toFixed(0)} KB — ` +
243
- `approaching the Cloudflare Free plan 1 MB limit. Consider the Paid plan or reducing bundle size.`);
244
- }
143
+ writeFileSync(workerPath, generateWorkerBridge(clientHtml, prerenderedPaths(distDir), prerenderedFallback(distDir)));
144
+ // Bundle everything into the platform-reserved worker file. Leaving server/
145
+ // in the public asset tree could expose source when the platform fails open.
146
+ const result = await build({
147
+ root, configFile: false, logLevel: 'error', ssr: { noExternal: true },
148
+ build: { ssr: workerPath, write: false, target: 'es2022', minify: false,
149
+ rollupOptions: { output: { inlineDynamicImports: true, entryFileNames: '_worker.js' } },
150
+ },
151
+ });
152
+ const output = (Array.isArray(result) ? result[0] : result);
153
+ const worker = output.output.find((item) => item.type === 'chunk')?.code;
154
+ if (!worker)
155
+ throw new Error('[cer-app] Cloudflare bundling produced no worker');
156
+ writeFileSync(workerPath, worker);
157
+ const compressed = gzipSync(worker).byteLength;
158
+ console.log(`[cer-app] Cloudflare worker: ${Buffer.byteLength(worker)} bytes raw / ${compressed} bytes gzip`);
159
+ // This is a framework advisory, not a claim about mutable platform plan limits.
160
+ if (compressed > 1024 * 1024)
161
+ console.warn('[cer-app] Cloudflare worker exceeds the 1 MiB gzip advisory budget; verify current platform size, startup and CPU limits before deployment.');
245
162
  // Copy assets from dist/client/ into dist/ (at the same URL paths).
246
- // Cloudflare Pages CDN serves files in the deploy directory as static first;
247
- // requests that don't match a static file fall through to _worker.js.
163
+ // Advanced-mode owns all requests and forwards public assets through ASSETS.
248
164
  // We deliberately skip index.html so HTML requests hit the SSR worker.
249
165
  _copyClientAssets(join(distDir, 'client'), distDir);
166
+ for (const name of ['server', 'client', '.vite', 'ssg-manifest.json', 'cer-client-manifest.json', 'cer-startup-manifest.json'])
167
+ rmSync(join(distDir, name), { recursive: true, force: true });
168
+ writeFileSync(join(distDir, '_routes.json'), JSON.stringify({ version: 1, include: ['/*'], exclude: [] }, null, 2));
250
169
  // wrangler.toml: nodejs_compat for AsyncLocalStorage + node:stream support.
251
170
  _writeWranglerToml(root, true);
252
171
  }
@@ -258,6 +177,10 @@ function _buildStatic(root, distDir) {
258
177
  // Move assets alongside the HTML so Cloudflare Pages serves them correctly.
259
178
  _copyClientAssets(clientDir, distDir);
260
179
  }
180
+ // Static deployment has no worker to protect build-only files.
181
+ for (const name of ['server', 'client', '.vite', 'ssg-manifest.json', 'cer-client-manifest.json', 'cer-startup-manifest.json']) {
182
+ rmSync(join(distDir, name), { recursive: true, force: true });
183
+ }
261
184
  // SPA: everything is already in dist/ — nothing to reorganise.
262
185
  _writeWranglerToml(root, false);
263
186
  }
@@ -266,14 +189,15 @@ function _writeWranglerToml(root, ssrMode) {
266
189
  const lines = [
267
190
  '# Auto-generated by @jasonshimmy/vite-plugin-cer-app — do not edit.',
268
191
  'name = "cer-app"',
269
- 'compatibility_date = "2024-09-23"',
192
+ // node:fs imports and a full process implementation are used by CER's
193
+ // server graph even though document/content reads use embedded snapshots.
194
+ 'compatibility_date = "2025-09-15"',
270
195
  ];
271
196
  if (ssrMode) {
272
197
  lines.push('compatibility_flags = ["nodejs_compat"]');
273
198
  }
274
199
  lines.push('');
275
- lines.push('[pages_build_output_dir]');
276
- lines.push('dir = "dist"');
200
+ lines.push('pages_build_output_dir = "dist"');
277
201
  lines.push('');
278
202
  writeFileSync(join(root, 'wrangler.toml'), lines.join('\n'));
279
203
  }
@@ -287,7 +211,7 @@ function _copyClientAssets(clientDir, destDir) {
287
211
  return;
288
212
  mkdirSync(destDir, { recursive: true });
289
213
  for (const entry of readdirSync(clientDir)) {
290
- if (entry === 'index.html')
214
+ if (entry === 'index.html' || entry === '.vite')
291
215
  continue;
292
216
  const src = join(clientDir, entry);
293
217
  const dest = join(destDir, entry);
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../src/cli/adapters/cloudflare.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../src/cli/adapters/cloudflare.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export declare function prerenderedPaths(distDir: string): string[];
2
+ export declare function prerenderedFallback(distDir: string): boolean | undefined;
3
+ export declare function copyPrerenderedDocuments(distDir: string, target: string): void;
4
+ /** Keep build-generated public metadata available in hybrid deployments. */
5
+ export declare function copyGeneratedPublicFiles(distDir: string, target: string): void;
6
+ export declare function nodeDispatcherSource(distDir: string, documentRoot: string): string;
7
+ /** A retained server is optional for pure SSG, but required for hybrid/fallback. */
8
+ export declare function requiresServer(distDir: string): boolean;
9
+ export declare function isPublicBuildFile(distDir: string, path: string): boolean;
10
+ //# sourceMappingURL=deployment-mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment-mode.d.ts","sourceRoot":"","sources":["../../../src/cli/adapters/deployment-mode.ts"],"names":[],"mappings":"AAIA,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAK1D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAGxE;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAQ9E;AAED,4EAA4E;AAC5E,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAM9E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAOlF;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CASvD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAIxE"}
@@ -0,0 +1,59 @@
1
+ import { existsSync, readFileSync, mkdirSync, copyFileSync } from 'node:fs';
2
+ import { join } from 'pathe';
3
+ import { normalizeDocumentPath } from '../../runtime/request-dispatcher.js';
4
+ export function prerenderedPaths(distDir) {
5
+ const file = join(distDir, 'ssg-manifest.json');
6
+ if (!existsSync(file))
7
+ return [];
8
+ const manifest = JSON.parse(readFileSync(file, 'utf8'));
9
+ return (manifest.paths ?? []).map(normalizeDocumentPath);
10
+ }
11
+ export function prerenderedFallback(distDir) {
12
+ const file = join(distDir, 'ssg-manifest.json');
13
+ return existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')).fallback === true : undefined;
14
+ }
15
+ export function copyPrerenderedDocuments(distDir, target) {
16
+ mkdirSync(target, { recursive: true });
17
+ for (const path of prerenderedPaths(distDir)) {
18
+ const file = path === '/' ? 'index.html' : path.slice(1) + '/index.html';
19
+ mkdirSync(join(target, file, '..'), { recursive: true });
20
+ copyFileSync(join(distDir, file), join(target, file));
21
+ }
22
+ if (existsSync(join(distDir, '404.html')))
23
+ copyFileSync(join(distDir, '404.html'), join(target, '404.html'));
24
+ }
25
+ /** Keep build-generated public metadata available in hybrid deployments. */
26
+ export function copyGeneratedPublicFiles(distDir, target) {
27
+ for (const file of ['sitemap.xml', 'robots.txt', 'form-dummy/index.html']) {
28
+ if (!existsSync(join(distDir, file)))
29
+ continue;
30
+ mkdirSync(join(target, file, '..'), { recursive: true });
31
+ copyFileSync(join(distDir, file), join(target, file));
32
+ }
33
+ }
34
+ export function nodeDispatcherSource(distDir, documentRoot) {
35
+ return `const dispatchRequest = app.createDispatchRequest ? app.createDispatchRequest({ prerendered: {
36
+ paths: ${JSON.stringify(prerenderedPaths(distDir))},
37
+ fallback: ${JSON.stringify(prerenderedFallback(distDir))},
38
+ notFound: () => { try { return readFileSync(new URL(${JSON.stringify(documentRoot + '404.html')}, import.meta.url), 'utf8') } catch { return null } },
39
+ read: (path) => readFileSync(new URL(${JSON.stringify(documentRoot)} + (path === '/' ? 'index.html' : path.slice(1).split('/').map(encodeURIComponent).join('/') + '/index.html'), import.meta.url), 'utf8'),
40
+ } }) : app.dispatchRequest\n`;
41
+ }
42
+ /** A retained server is optional for pure SSG, but required for hybrid/fallback. */
43
+ export function requiresServer(distDir) {
44
+ const manifestPath = join(distDir, 'ssg-manifest.json');
45
+ const manifest = existsSync(manifestPath)
46
+ ? JSON.parse(readFileSync(manifestPath, 'utf-8'))
47
+ : null;
48
+ const required = !manifest || !!(manifest.hybrid || manifest.fallback);
49
+ const serverExists = existsSync(join(distDir, 'server/server.js'));
50
+ if (manifest && required && !serverExists)
51
+ throw new Error('[cer-app] Hybrid/fallback build is missing its server bundle. Rebuild before adapting.');
52
+ return required && serverExists;
53
+ }
54
+ export function isPublicBuildFile(distDir, path) {
55
+ const relative = path.slice(distDir.length + 1);
56
+ return !['server', 'client', '.vite'].some((name) => relative === name || relative.startsWith(`${name}/`)) &&
57
+ !/^(?:ssg-manifest|cer-client-manifest|cer-startup-manifest)\.json$/.test(relative);
58
+ }
59
+ //# sourceMappingURL=deployment-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment-mode.js","sourceRoot":"","sources":["../../../src/cli/adapters/deployment-mode.ts"],"names":[],"mappings":"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"}
@@ -7,15 +7,16 @@
7
7
  * SSR mode:
8
8
  * - Writes a `netlify/functions/ssr.mjs` bridge that converts Netlify
9
9
  * Functions v2 Web API (Request/Response) to the Node.js-style handler
10
- * used by the cer-app server bundle. Handles /api/* routing inline.
11
- * - Creates `.netlify/publish/` with content-hashed assets only (no HTML),
10
+ * used by the cer-app server bundle. Delegates routing to dispatchRequest.
11
+ * - Creates `.netlify/publish/` with public assets and generated metadata
12
+ * (including form registration HTML, but excluding routed documents),
12
13
  * so Netlify's CDN serves static files and everything else falls through
13
14
  * to the SSR function.
14
15
  * - Writes `netlify.toml` with the publish directory and catch-all redirect.
15
16
  *
16
17
  * SPA/SSG mode:
17
- * - Writes `netlify.toml` that points to the correct publish directory and
18
- * adds a SPA fallback redirect (/* → /index.html).
18
+ * - Writes `netlify.toml` with a filtered SSG publish directory and 404
19
+ * fallback, or a SPA publish directory and app-shell fallback.
19
20
  *
20
21
  * Responses are streamed via the Web Streams TransformStream API — chunks written
21
22
  * by the SSR handler are forwarded to the client as they arrive rather than being
@@ -1 +1 @@
1
- {"version":3,"file":"netlify.d.ts","sourceRoot":"","sources":["../../../src/cli/adapters/netlify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AA2KH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BnE"}
1
+ {"version":3,"file":"netlify.d.ts","sourceRoot":"","sources":["../../../src/cli/adapters/netlify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AA+CH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBnE"}