@jasonshimmy/vite-plugin-cer-app 0.9.0 → 0.10.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.
- package/CHANGELOG.md +4 -0
- package/ROADMAP.md +17 -5
- package/commits.txt +1 -1
- package/dist/cli/adapters/netlify.d.ts +24 -0
- package/dist/cli/adapters/netlify.d.ts.map +1 -0
- package/dist/cli/adapters/netlify.js +266 -0
- package/dist/cli/adapters/netlify.js.map +1 -0
- package/dist/cli/adapters/vercel.d.ts +20 -0
- package/dist/cli/adapters/vercel.d.ts.map +1 -0
- package/dist/cli/adapters/vercel.js +217 -0
- package/dist/cli/adapters/vercel.js.map +1 -0
- package/dist/cli/commands/adapt.d.ts +3 -0
- package/dist/cli/commands/adapt.d.ts.map +1 -0
- package/dist/cli/commands/adapt.js +25 -0
- package/dist/cli/commands/adapt.js.map +1 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +19 -0
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/types/config.d.ts +11 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js.map +1 -1
- package/docs/cli.md +52 -0
- package/e2e/scripts/clean.mjs +5 -1
- package/package.json +3 -1
- package/src/__tests__/cli/adapters/netlify-bridge.integration.test.ts +138 -0
- package/src/__tests__/cli/adapters/netlify.test.ts +225 -0
- package/src/__tests__/cli/adapters/vercel.test.ts +233 -0
- package/src/cli/adapters/netlify.ts +295 -0
- package/src/cli/adapters/vercel.ts +272 -0
- package/src/cli/commands/adapt.ts +27 -0
- package/src/cli/commands/build.ts +19 -0
- package/src/cli/index.ts +2 -0
- package/src/types/config.ts +11 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
|
+
## [v0.10.0] - 2026-03-22
|
|
5
|
+
|
|
6
|
+
- feat: add adapters for Vercel and Netlify deployment platforms (7112bf6)
|
|
7
|
+
|
|
4
8
|
## [v0.9.0] - 2026-03-21
|
|
5
9
|
|
|
6
10
|
- feat: add useCookie and useSeoMeta composables with tests (6536902)
|
package/ROADMAP.md
CHANGED
|
@@ -222,7 +222,7 @@ changes.
|
|
|
222
222
|
|
|
223
223
|
---
|
|
224
224
|
|
|
225
|
-
### 10.2 Vercel adapter
|
|
225
|
+
### 10.2 Vercel adapter ✅
|
|
226
226
|
|
|
227
227
|
**Problem:** Vercel expects functions in `.vercel/output/functions/` with a
|
|
228
228
|
specific manifest format.
|
|
@@ -233,11 +233,23 @@ Static assets are moved to `.vercel/output/static/`.
|
|
|
233
233
|
|
|
234
234
|
**Complexity:** Medium. Mostly file system manipulation.
|
|
235
235
|
|
|
236
|
+
**Files:**
|
|
237
|
+
- `src/cli/adapters/vercel.ts` — Vercel Build Output API v3 adapter
|
|
238
|
+
- `src/cli/commands/adapt.ts` — `cer-app adapt` command
|
|
239
|
+
- `src/types/config.ts` — `adapter` field on `CerAppConfig`
|
|
240
|
+
- `src/cli/commands/build.ts` — auto-runs adapter post-build
|
|
241
|
+
|
|
236
242
|
---
|
|
237
243
|
|
|
238
|
-
### 10.3 Netlify adapter
|
|
244
|
+
### 10.3 Netlify adapter ✅
|
|
239
245
|
|
|
240
|
-
Similar to Vercel but targets Netlify Functions
|
|
246
|
+
Similar to Vercel but targets Netlify Functions v2 format. Writes a bridge
|
|
247
|
+
function (`netlify/functions/ssr.mjs`) that converts between the Web
|
|
248
|
+
`Request`/`Response` API and the Node.js-style handler. Responses are
|
|
249
|
+
buffered (no streaming — Netlify Functions limitation).
|
|
250
|
+
|
|
251
|
+
**Files:**
|
|
252
|
+
- `src/cli/adapters/netlify.ts` — Netlify adapter
|
|
241
253
|
|
|
242
254
|
---
|
|
243
255
|
|
|
@@ -271,8 +283,8 @@ canonical URL. No new infrastructure needed — all forwarded to `<head>`.
|
|
|
271
283
|
| 9.1 | Client-side route middleware (navigation guards) | 🟡 High | ✅ |
|
|
272
284
|
| 9.2 | `useCookie()` composable | 🟡 High | ✅ |
|
|
273
285
|
| 10.1 | Cloudflare Workers adapter | 🟢 Medium | 🔜 |
|
|
274
|
-
| 10.2 | Vercel adapter | 🟢 Medium |
|
|
275
|
-
| 10.3 | Netlify adapter | 🟢 Medium |
|
|
286
|
+
| 10.2 | Vercel adapter | 🟢 Medium | ✅ |
|
|
287
|
+
| 10.3 | Netlify adapter | 🟢 Medium | ✅ |
|
|
276
288
|
| 11.1 | DevTools overlay | 🟢 Medium | ❌ |
|
|
277
289
|
| 11.2 | i18n | 🟢 Medium | 🔜 |
|
|
278
290
|
| 11.3 | `useSeoMeta()` | 🟢 Medium | ✅ |
|
package/commits.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
- feat: add
|
|
1
|
+
- feat: add adapters for Vercel and Netlify deployment platforms (7112bf6)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Netlify adapter.
|
|
3
|
+
*
|
|
4
|
+
* Transforms the cer-app `dist/` output into the files needed to deploy on
|
|
5
|
+
* Netlify.
|
|
6
|
+
*
|
|
7
|
+
* SSR mode:
|
|
8
|
+
* - Writes a `netlify/functions/ssr.mjs` bridge that converts Netlify
|
|
9
|
+
* Functions v2 Web API (Request/Response) to the Node.js-style handler
|
|
10
|
+
* used by the cer-app server bundle. Handles /api/* routing inline.
|
|
11
|
+
* - Creates `.netlify/publish/` with content-hashed assets only (no HTML),
|
|
12
|
+
* so Netlify's CDN serves static files and everything else falls through
|
|
13
|
+
* to the SSR function.
|
|
14
|
+
* - Writes `netlify.toml` with the publish directory and catch-all redirect.
|
|
15
|
+
*
|
|
16
|
+
* SPA/SSG mode:
|
|
17
|
+
* - Writes `netlify.toml` that points to the correct publish directory and
|
|
18
|
+
* adds a SPA fallback redirect (/* → /index.html).
|
|
19
|
+
*
|
|
20
|
+
* Netlify Functions v2 limitation: responses are buffered (no streaming).
|
|
21
|
+
* All other behaviour (cookies, headers, API routes, ISR) is fully supported.
|
|
22
|
+
*/
|
|
23
|
+
export declare function runNetlifyAdapter(root: string): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=netlify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"netlify.d.ts","sourceRoot":"","sources":["../../../src/cli/adapters/netlify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AA8IH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BnE"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Netlify adapter.
|
|
3
|
+
*
|
|
4
|
+
* Transforms the cer-app `dist/` output into the files needed to deploy on
|
|
5
|
+
* Netlify.
|
|
6
|
+
*
|
|
7
|
+
* SSR mode:
|
|
8
|
+
* - Writes a `netlify/functions/ssr.mjs` bridge that converts Netlify
|
|
9
|
+
* Functions v2 Web API (Request/Response) to the Node.js-style handler
|
|
10
|
+
* used by the cer-app server bundle. Handles /api/* routing inline.
|
|
11
|
+
* - Creates `.netlify/publish/` with content-hashed assets only (no HTML),
|
|
12
|
+
* so Netlify's CDN serves static files and everything else falls through
|
|
13
|
+
* to the SSR function.
|
|
14
|
+
* - Writes `netlify.toml` with the publish directory and catch-all redirect.
|
|
15
|
+
*
|
|
16
|
+
* SPA/SSG mode:
|
|
17
|
+
* - Writes `netlify.toml` that points to the correct publish directory and
|
|
18
|
+
* adds a SPA fallback redirect (/* → /index.html).
|
|
19
|
+
*
|
|
20
|
+
* Netlify Functions v2 limitation: responses are buffered (no streaming).
|
|
21
|
+
* All other behaviour (cookies, headers, API routes, ISR) is fully supported.
|
|
22
|
+
*/
|
|
23
|
+
import { join } from 'pathe';
|
|
24
|
+
import { existsSync, mkdirSync, writeFileSync, copyFileSync, cpSync, readdirSync, statSync, rmSync, } from 'node:fs';
|
|
25
|
+
// ─── SSR function bridge template ────────────────────────────────────────────
|
|
26
|
+
/**
|
|
27
|
+
* Netlify Functions v2 bridge.
|
|
28
|
+
*
|
|
29
|
+
* The function:
|
|
30
|
+
* 1. Converts the incoming Web API Request to a mock Node.js IncomingMessage.
|
|
31
|
+
* 2. Creates a mock ServerResponse that collects chunks and resolves a Promise
|
|
32
|
+
* with a Web API Response once `end()` is called.
|
|
33
|
+
* 3. Routes /api/* to the API handlers, everything else to the SSR handler.
|
|
34
|
+
*
|
|
35
|
+
* The server bundle is imported using a path relative to the function file
|
|
36
|
+
* location: netlify/functions/ssr.mjs → ../../dist/server/server.js.
|
|
37
|
+
*/
|
|
38
|
+
const NETLIFY_SSR_BRIDGE = `\
|
|
39
|
+
// Auto-generated by @jasonshimmy/vite-plugin-cer-app — do not edit.
|
|
40
|
+
import { Readable } from 'node:stream'
|
|
41
|
+
import { handler, apiRoutes } from '../../dist/server/server.js'
|
|
42
|
+
|
|
43
|
+
function matchApiPattern(pattern, urlPath) {
|
|
44
|
+
const pp = pattern.split('/')
|
|
45
|
+
const up = urlPath.split('/')
|
|
46
|
+
if (pp.length !== up.length) return null
|
|
47
|
+
const params = {}
|
|
48
|
+
for (let i = 0; i < pp.length; i++) {
|
|
49
|
+
if (pp[i].startsWith(':')) params[pp[i].slice(1)] = decodeURIComponent(up[i])
|
|
50
|
+
else if (pp[i] !== up[i]) return null
|
|
51
|
+
}
|
|
52
|
+
return params
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function toNodeRequest(webReq) {
|
|
56
|
+
const url = new URL(webReq.url)
|
|
57
|
+
const hasBody = webReq.body !== null
|
|
58
|
+
&& webReq.method !== 'GET'
|
|
59
|
+
&& webReq.method !== 'HEAD'
|
|
60
|
+
const body = hasBody ? Buffer.from(await webReq.arrayBuffer()) : null
|
|
61
|
+
const req = Object.assign(
|
|
62
|
+
body ? Readable.from([body]) : Readable.from([]),
|
|
63
|
+
{
|
|
64
|
+
url: url.pathname + url.search,
|
|
65
|
+
method: webReq.method,
|
|
66
|
+
headers: Object.fromEntries(webReq.headers.entries()),
|
|
67
|
+
},
|
|
68
|
+
)
|
|
69
|
+
return req
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function createNodeResponse() {
|
|
73
|
+
const chunks = []
|
|
74
|
+
const headers = {}
|
|
75
|
+
let _resolve
|
|
76
|
+
let _ended = false
|
|
77
|
+
const promise = new Promise((res) => { _resolve = res })
|
|
78
|
+
const res = {
|
|
79
|
+
statusCode: 200,
|
|
80
|
+
setHeader(name, value) { headers[name.toLowerCase()] = String(value) },
|
|
81
|
+
getHeader(name) { return headers[name.toLowerCase()] },
|
|
82
|
+
removeHeader(name) { delete headers[name.toLowerCase()] },
|
|
83
|
+
write(chunk) {
|
|
84
|
+
if (_ended) return
|
|
85
|
+
chunks.push(typeof chunk === 'string' ? Buffer.from(chunk, 'utf-8') : Buffer.from(chunk))
|
|
86
|
+
},
|
|
87
|
+
end(chunk) {
|
|
88
|
+
if (_ended) return
|
|
89
|
+
_ended = true
|
|
90
|
+
if (chunk) {
|
|
91
|
+
chunks.push(typeof chunk === 'string' ? Buffer.from(chunk, 'utf-8') : Buffer.from(chunk))
|
|
92
|
+
}
|
|
93
|
+
_resolve(new Response(Buffer.concat(chunks), { status: res.statusCode, headers }))
|
|
94
|
+
},
|
|
95
|
+
// Helpers expected by API route handlers.
|
|
96
|
+
json(data) {
|
|
97
|
+
this.setHeader('Content-Type', 'application/json; charset=utf-8')
|
|
98
|
+
this.end(JSON.stringify(data))
|
|
99
|
+
},
|
|
100
|
+
status(code) { this.statusCode = code; return this },
|
|
101
|
+
get writableEnded() { return _ended },
|
|
102
|
+
}
|
|
103
|
+
return { res, promise }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export default async (webReq) => {
|
|
107
|
+
const url = new URL(webReq.url)
|
|
108
|
+
const urlPath = url.pathname
|
|
109
|
+
const method = webReq.method ?? 'GET'
|
|
110
|
+
|
|
111
|
+
// Route /api/* requests to the API handlers exported by the server bundle.
|
|
112
|
+
if (urlPath.startsWith('/api/')) {
|
|
113
|
+
for (const route of (apiRoutes ?? [])) {
|
|
114
|
+
const params = matchApiPattern(route.path, urlPath)
|
|
115
|
+
if (params !== null) {
|
|
116
|
+
const nodeReq = await toNodeRequest(webReq)
|
|
117
|
+
nodeReq.params = params
|
|
118
|
+
const { res, promise } = createNodeResponse()
|
|
119
|
+
const fn = route.handlers[method.toLowerCase()]
|
|
120
|
+
?? route.handlers[method.toUpperCase()]
|
|
121
|
+
?? route.handlers['default']
|
|
122
|
+
if (typeof fn === 'function') {
|
|
123
|
+
// Use Promise.resolve() so the catch works for both sync and async handlers.
|
|
124
|
+
Promise.resolve(fn(nodeReq, res)).catch(() => {
|
|
125
|
+
if (!res.writableEnded) {
|
|
126
|
+
res.statusCode = 500
|
|
127
|
+
res.end(JSON.stringify({ error: 'Internal Server Error' }))
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
return promise
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return new Response('Not Found', { status: 404 })
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// All other requests: SSR.
|
|
138
|
+
const nodeReq = await toNodeRequest(webReq)
|
|
139
|
+
const { res, promise } = createNodeResponse()
|
|
140
|
+
handler(nodeReq, res).catch(() => {
|
|
141
|
+
if (!res.writableEnded) {
|
|
142
|
+
res.statusCode = 500
|
|
143
|
+
res.end('Internal Server Error')
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
return promise
|
|
147
|
+
}
|
|
148
|
+
`;
|
|
149
|
+
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
150
|
+
export async function runNetlifyAdapter(root) {
|
|
151
|
+
const distDir = join(root, 'dist');
|
|
152
|
+
if (!existsSync(distDir)) {
|
|
153
|
+
throw new Error(`[cer-app] No dist/ directory found at ${distDir}. Run 'cer-app build' first.`);
|
|
154
|
+
}
|
|
155
|
+
const serverBundle = join(distDir, 'server/server.js');
|
|
156
|
+
const ssgManifest = join(distDir, 'ssg-manifest.json');
|
|
157
|
+
const isSSR = existsSync(serverBundle) && !existsSync(ssgManifest);
|
|
158
|
+
if (isSSR) {
|
|
159
|
+
_buildSSR(root, distDir);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
_buildStatic(root, distDir);
|
|
163
|
+
}
|
|
164
|
+
console.log('[cer-app] Netlify adapter complete.');
|
|
165
|
+
if (isSSR) {
|
|
166
|
+
console.log(' Function: netlify/functions/ssr.mjs');
|
|
167
|
+
console.log(' Static: .netlify/publish/');
|
|
168
|
+
console.log(' Deploy with: netlify deploy');
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
console.log(' Deploy with: netlify deploy');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// ─── SSR output ───────────────────────────────────────────────────────────────
|
|
175
|
+
function _buildSSR(root, distDir) {
|
|
176
|
+
// Write the SSR bridge function.
|
|
177
|
+
const functionsDir = join(root, 'netlify/functions');
|
|
178
|
+
mkdirSync(functionsDir, { recursive: true });
|
|
179
|
+
writeFileSync(join(functionsDir, 'ssr.mjs'), NETLIFY_SSR_BRIDGE);
|
|
180
|
+
// Build the static publish directory with assets only (no HTML).
|
|
181
|
+
// Netlify serves files in the publish directory as static responses first.
|
|
182
|
+
// By omitting index.html we ensure all HTML routes fall through to the
|
|
183
|
+
// SSR function instead of being served as stale static files.
|
|
184
|
+
const publishDir = join(root, '.netlify/publish');
|
|
185
|
+
if (existsSync(publishDir)) {
|
|
186
|
+
rmSync(publishDir, { recursive: true, force: true });
|
|
187
|
+
}
|
|
188
|
+
_copyClientAssets(join(distDir, 'client'), publishDir);
|
|
189
|
+
// netlify.toml: publish directory + cache headers + catch-all to SSR function.
|
|
190
|
+
writeFileSync(join(root, 'netlify.toml'), `# Auto-generated by @jasonshimmy/vite-plugin-cer-app — do not edit.
|
|
191
|
+
[build]
|
|
192
|
+
publish = ".netlify/publish"
|
|
193
|
+
|
|
194
|
+
[[headers]]
|
|
195
|
+
for = "/assets/*"
|
|
196
|
+
[headers.values]
|
|
197
|
+
Cache-Control = "public, max-age=31536000, immutable"
|
|
198
|
+
|
|
199
|
+
[[redirects]]
|
|
200
|
+
from = "/*"
|
|
201
|
+
to = "/.netlify/functions/ssr"
|
|
202
|
+
status = 200
|
|
203
|
+
`);
|
|
204
|
+
}
|
|
205
|
+
// ─── Static (SPA / SSG) output ────────────────────────────────────────────────
|
|
206
|
+
function _buildStatic(root, distDir) {
|
|
207
|
+
const clientDir = join(distDir, 'client');
|
|
208
|
+
let publishDir;
|
|
209
|
+
if (existsSync(clientDir)) {
|
|
210
|
+
// SSG: HTML in dist/, assets in dist/client/. Merge into .netlify/publish/.
|
|
211
|
+
publishDir = join(root, '.netlify/publish');
|
|
212
|
+
if (existsSync(publishDir)) {
|
|
213
|
+
rmSync(publishDir, { recursive: true, force: true });
|
|
214
|
+
}
|
|
215
|
+
// Copy pre-rendered HTML from dist/ (skip client/ sub-dir and manifest).
|
|
216
|
+
cpSync(distDir, publishDir, {
|
|
217
|
+
recursive: true,
|
|
218
|
+
filter: (src) => src !== clientDir && !src.endsWith('ssg-manifest.json'),
|
|
219
|
+
});
|
|
220
|
+
// Copy assets from dist/client/ into the publish root.
|
|
221
|
+
_copyClientAssets(clientDir, publishDir);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
// SPA: everything is in dist/ at correct URL paths already.
|
|
225
|
+
publishDir = 'dist';
|
|
226
|
+
}
|
|
227
|
+
// netlify.toml: SPA fallback redirect.
|
|
228
|
+
writeFileSync(join(root, 'netlify.toml'), `# Auto-generated by @jasonshimmy/vite-plugin-cer-app — do not edit.
|
|
229
|
+
[build]
|
|
230
|
+
publish = "${publishDir.startsWith(root) ? publishDir.slice(root.length + 1) : publishDir}"
|
|
231
|
+
|
|
232
|
+
[[headers]]
|
|
233
|
+
for = "/assets/*"
|
|
234
|
+
[headers.values]
|
|
235
|
+
Cache-Control = "public, max-age=31536000, immutable"
|
|
236
|
+
|
|
237
|
+
[[redirects]]
|
|
238
|
+
from = "/*"
|
|
239
|
+
to = "/index.html"
|
|
240
|
+
status = 200
|
|
241
|
+
`);
|
|
242
|
+
}
|
|
243
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
244
|
+
/**
|
|
245
|
+
* Copies content-hashed assets and other public files (favicon, robots.txt, …)
|
|
246
|
+
* from the client dist directory into `destDir`.
|
|
247
|
+
* Skips index.html so it is never served as a static file in SSR mode.
|
|
248
|
+
*/
|
|
249
|
+
function _copyClientAssets(clientDir, destDir) {
|
|
250
|
+
if (!existsSync(clientDir))
|
|
251
|
+
return;
|
|
252
|
+
mkdirSync(destDir, { recursive: true });
|
|
253
|
+
for (const entry of readdirSync(clientDir)) {
|
|
254
|
+
if (entry === 'index.html')
|
|
255
|
+
continue;
|
|
256
|
+
const src = join(clientDir, entry);
|
|
257
|
+
const dest = join(destDir, entry);
|
|
258
|
+
if (statSync(src).isDirectory()) {
|
|
259
|
+
cpSync(src, dest, { recursive: true });
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
copyFileSync(src, dest);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=netlify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"netlify.js","sourceRoot":"","sources":["../../../src/cli/adapters/netlify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel Build Output API v3 adapter.
|
|
3
|
+
*
|
|
4
|
+
* Transforms the cer-app `dist/` output into a `.vercel/output/` directory
|
|
5
|
+
* that can be deployed with `vercel deploy --prebuilt`.
|
|
6
|
+
*
|
|
7
|
+
* SSR mode:
|
|
8
|
+
* - Copies dist/server/server.js into a Vercel Serverless Function
|
|
9
|
+
* - Copies dist/client/index.html alongside the server bundle so the
|
|
10
|
+
* `../client/index.html` relative path in the bundle resolves correctly
|
|
11
|
+
* - Copies content-hashed assets to .vercel/output/static/ for CDN delivery
|
|
12
|
+
* - Writes a launcher (index.js) that routes /api/* to the API handlers and
|
|
13
|
+
* forwards all other requests to the SSR handler
|
|
14
|
+
*
|
|
15
|
+
* SPA/SSG mode:
|
|
16
|
+
* - Copies all static files to .vercel/output/static/
|
|
17
|
+
* - Writes a config.json with SPA fallback (serve index.html for unknown paths)
|
|
18
|
+
*/
|
|
19
|
+
export declare function runVercelAdapter(root: string): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=vercel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel.d.ts","sourceRoot":"","sources":["../../../src/cli/adapters/vercel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAqFH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BlE"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel Build Output API v3 adapter.
|
|
3
|
+
*
|
|
4
|
+
* Transforms the cer-app `dist/` output into a `.vercel/output/` directory
|
|
5
|
+
* that can be deployed with `vercel deploy --prebuilt`.
|
|
6
|
+
*
|
|
7
|
+
* SSR mode:
|
|
8
|
+
* - Copies dist/server/server.js into a Vercel Serverless Function
|
|
9
|
+
* - Copies dist/client/index.html alongside the server bundle so the
|
|
10
|
+
* `../client/index.html` relative path in the bundle resolves correctly
|
|
11
|
+
* - Copies content-hashed assets to .vercel/output/static/ for CDN delivery
|
|
12
|
+
* - Writes a launcher (index.js) that routes /api/* to the API handlers and
|
|
13
|
+
* forwards all other requests to the SSR handler
|
|
14
|
+
*
|
|
15
|
+
* SPA/SSG mode:
|
|
16
|
+
* - Copies all static files to .vercel/output/static/
|
|
17
|
+
* - Writes a config.json with SPA fallback (serve index.html for unknown paths)
|
|
18
|
+
*/
|
|
19
|
+
import { join } from 'pathe';
|
|
20
|
+
import { existsSync, mkdirSync, writeFileSync, copyFileSync, cpSync, readdirSync, statSync, rmSync, } from 'node:fs';
|
|
21
|
+
// ─── Launcher template ────────────────────────────────────────────────────────
|
|
22
|
+
/**
|
|
23
|
+
* Vercel serverless function entry point.
|
|
24
|
+
* Handles /api/* routing and falls through to the SSR handler for all other
|
|
25
|
+
* requests. Imported by Vercel's Node.js launcher via the handler export.
|
|
26
|
+
*
|
|
27
|
+
* The server bundle lives at ./server/server.js so its own ../client/index.html
|
|
28
|
+
* reference resolves to ./client/index.html — both files are copied into the
|
|
29
|
+
* function directory by the adapter.
|
|
30
|
+
*/
|
|
31
|
+
const VERCEL_LAUNCHER = `\
|
|
32
|
+
// Auto-generated by @jasonshimmy/vite-plugin-cer-app — do not edit.
|
|
33
|
+
import { handler, apiRoutes } from './server/server.js'
|
|
34
|
+
|
|
35
|
+
function matchApiPattern(pattern, urlPath) {
|
|
36
|
+
const pp = pattern.split('/')
|
|
37
|
+
const up = urlPath.split('/')
|
|
38
|
+
if (pp.length !== up.length) return null
|
|
39
|
+
const params = {}
|
|
40
|
+
for (let i = 0; i < pp.length; i++) {
|
|
41
|
+
if (pp[i].startsWith(':')) params[pp[i].slice(1)] = decodeURIComponent(up[i])
|
|
42
|
+
else if (pp[i] !== up[i]) return null
|
|
43
|
+
}
|
|
44
|
+
return params
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default async function cerAppHandler(req, res) {
|
|
48
|
+
const urlPath = (req.url ?? '/').split('?')[0]
|
|
49
|
+
const method = req.method ?? 'GET'
|
|
50
|
+
|
|
51
|
+
// Route /api/* to API handlers exported by the server bundle.
|
|
52
|
+
if (urlPath.startsWith('/api/')) {
|
|
53
|
+
for (const route of (apiRoutes ?? [])) {
|
|
54
|
+
const params = matchApiPattern(route.path, urlPath)
|
|
55
|
+
if (params !== null) {
|
|
56
|
+
req.params = params
|
|
57
|
+
res.json = function (data) {
|
|
58
|
+
this.setHeader('Content-Type', 'application/json; charset=utf-8')
|
|
59
|
+
this.end(JSON.stringify(data))
|
|
60
|
+
}
|
|
61
|
+
res.status = function (code) { this.statusCode = code; return this }
|
|
62
|
+
const fn = route.handlers[method.toLowerCase()]
|
|
63
|
+
?? route.handlers[method.toUpperCase()]
|
|
64
|
+
?? route.handlers['default']
|
|
65
|
+
if (typeof fn === 'function') {
|
|
66
|
+
try {
|
|
67
|
+
await fn(req, res)
|
|
68
|
+
} catch {
|
|
69
|
+
if (!res.writableEnded) {
|
|
70
|
+
res.statusCode = 500
|
|
71
|
+
res.setHeader('Content-Type', 'application/json')
|
|
72
|
+
res.end(JSON.stringify({ error: 'Internal Server Error' }))
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
res.statusCode = 404
|
|
80
|
+
res.end('Not Found')
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// All other requests: SSR.
|
|
85
|
+
await handler(req, res)
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
89
|
+
export async function runVercelAdapter(root) {
|
|
90
|
+
const distDir = join(root, 'dist');
|
|
91
|
+
if (!existsSync(distDir)) {
|
|
92
|
+
throw new Error(`[cer-app] No dist/ directory found at ${distDir}. Run 'cer-app build' first.`);
|
|
93
|
+
}
|
|
94
|
+
const serverBundle = join(distDir, 'server/server.js');
|
|
95
|
+
const ssgManifest = join(distDir, 'ssg-manifest.json');
|
|
96
|
+
const isSSR = existsSync(serverBundle) && !existsSync(ssgManifest);
|
|
97
|
+
const outDir = join(root, '.vercel/output');
|
|
98
|
+
// Clean existing Vercel output to avoid stale files.
|
|
99
|
+
if (existsSync(outDir)) {
|
|
100
|
+
rmSync(outDir, { recursive: true, force: true });
|
|
101
|
+
}
|
|
102
|
+
mkdirSync(outDir, { recursive: true });
|
|
103
|
+
if (isSSR) {
|
|
104
|
+
_buildSSR(distDir, outDir);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
_buildStatic(distDir, outDir);
|
|
108
|
+
}
|
|
109
|
+
console.log('[cer-app] Vercel adapter complete — output written to .vercel/output/');
|
|
110
|
+
console.log(' Deploy with: vercel deploy --prebuilt');
|
|
111
|
+
}
|
|
112
|
+
// ─── SSR output ───────────────────────────────────────────────────────────────
|
|
113
|
+
function _buildSSR(distDir, outDir) {
|
|
114
|
+
const funcDir = join(outDir, 'functions/index.func');
|
|
115
|
+
// Maintain the server/ → ../client/ relative layout so the server bundle's
|
|
116
|
+
// built-in `../client/index.html` reference resolves correctly.
|
|
117
|
+
mkdirSync(join(funcDir, 'server'), { recursive: true });
|
|
118
|
+
mkdirSync(join(funcDir, 'client'), { recursive: true });
|
|
119
|
+
// Server bundle.
|
|
120
|
+
copyFileSync(join(distDir, 'server/server.js'), join(funcDir, 'server/server.js'));
|
|
121
|
+
// Client HTML template (referenced as ../client/index.html from server.js).
|
|
122
|
+
const clientHtml = join(distDir, 'client/index.html');
|
|
123
|
+
if (existsSync(clientHtml)) {
|
|
124
|
+
copyFileSync(clientHtml, join(funcDir, 'client/index.html'));
|
|
125
|
+
}
|
|
126
|
+
// Launcher: routes API requests + forwards the rest to the SSR handler.
|
|
127
|
+
writeFileSync(join(funcDir, 'index.js'), VERCEL_LAUNCHER);
|
|
128
|
+
// package.json: mark the function directory as ESM so Node.js treats .js
|
|
129
|
+
// files as ES modules (the server bundle uses import.meta.url, etc.).
|
|
130
|
+
writeFileSync(join(funcDir, 'package.json'), '{"type":"module"}\n');
|
|
131
|
+
// Vercel function config.
|
|
132
|
+
writeFileSync(join(funcDir, '.vc-config.json'), JSON.stringify({
|
|
133
|
+
runtime: 'nodejs20.x',
|
|
134
|
+
handler: 'index.js',
|
|
135
|
+
launcherType: 'Nodejs',
|
|
136
|
+
}, null, 2) + '\n');
|
|
137
|
+
// Static assets: content-hashed files served by the Vercel CDN.
|
|
138
|
+
const staticDir = join(outDir, 'static');
|
|
139
|
+
_copyClientAssets(join(distDir, 'client'), staticDir);
|
|
140
|
+
// Vercel output routes config.
|
|
141
|
+
writeFileSync(join(outDir, 'config.json'), JSON.stringify({
|
|
142
|
+
version: 3,
|
|
143
|
+
routes: [
|
|
144
|
+
// Apply immutable cache headers to content-hashed assets then continue
|
|
145
|
+
// (so the filesystem handler can still serve them as static files).
|
|
146
|
+
{
|
|
147
|
+
src: '/assets/(.*)',
|
|
148
|
+
headers: { 'cache-control': 'public, max-age=31536000, immutable' },
|
|
149
|
+
continue: true,
|
|
150
|
+
},
|
|
151
|
+
// Serve matched static files from .vercel/output/static/.
|
|
152
|
+
{ handle: 'filesystem' },
|
|
153
|
+
// Fall through to the SSR function for all other paths.
|
|
154
|
+
{ src: '/(.*)', dest: '/index' },
|
|
155
|
+
],
|
|
156
|
+
}, null, 2) + '\n');
|
|
157
|
+
}
|
|
158
|
+
// ─── Static (SPA / SSG) output ────────────────────────────────────────────────
|
|
159
|
+
function _buildStatic(distDir, outDir) {
|
|
160
|
+
const staticDir = join(outDir, 'static');
|
|
161
|
+
mkdirSync(staticDir, { recursive: true });
|
|
162
|
+
const clientDir = join(distDir, 'client');
|
|
163
|
+
if (existsSync(clientDir)) {
|
|
164
|
+
// SSG: pre-rendered HTML lives in dist/, assets in dist/client/.
|
|
165
|
+
// Copy HTML (and any other root-level files) from dist/ — skip client/ dir
|
|
166
|
+
// and ssg-manifest.json which are build artifacts, not deployable content.
|
|
167
|
+
cpSync(distDir, staticDir, {
|
|
168
|
+
recursive: true,
|
|
169
|
+
filter: (src) => src !== clientDir && !src.endsWith('ssg-manifest.json'),
|
|
170
|
+
});
|
|
171
|
+
// Copy assets and other non-HTML files from dist/client/ into static root
|
|
172
|
+
// so URLs like /assets/main.abc123.js resolve correctly.
|
|
173
|
+
_copyClientAssets(clientDir, staticDir);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
// SPA: everything is in dist/ at the correct paths already.
|
|
177
|
+
cpSync(distDir, staticDir, { recursive: true });
|
|
178
|
+
}
|
|
179
|
+
// Vercel output config: serve static files, fall back to index.html (SPA).
|
|
180
|
+
writeFileSync(join(outDir, 'config.json'), JSON.stringify({
|
|
181
|
+
version: 3,
|
|
182
|
+
routes: [
|
|
183
|
+
{
|
|
184
|
+
src: '/assets/(.*)',
|
|
185
|
+
headers: { 'cache-control': 'public, max-age=31536000, immutable' },
|
|
186
|
+
continue: true,
|
|
187
|
+
},
|
|
188
|
+
{ handle: 'filesystem' },
|
|
189
|
+
// SPA fallback: serve index.html for any unmatched path.
|
|
190
|
+
{ src: '/(.*)', dest: '/index.html' },
|
|
191
|
+
],
|
|
192
|
+
}, null, 2) + '\n');
|
|
193
|
+
}
|
|
194
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
195
|
+
/**
|
|
196
|
+
* Copies Vite-hashed assets and any other public files (favicon, robots.txt, …)
|
|
197
|
+
* from the client dist directory into `destDir`.
|
|
198
|
+
* Skips index.html — that is handled by the SSR function or SSG pre-rendering.
|
|
199
|
+
*/
|
|
200
|
+
function _copyClientAssets(clientDir, destDir) {
|
|
201
|
+
if (!existsSync(clientDir))
|
|
202
|
+
return;
|
|
203
|
+
mkdirSync(destDir, { recursive: true });
|
|
204
|
+
for (const entry of readdirSync(clientDir)) {
|
|
205
|
+
if (entry === 'index.html')
|
|
206
|
+
continue; // served by function / SSG HTML
|
|
207
|
+
const src = join(clientDir, entry);
|
|
208
|
+
const dest = join(destDir, entry);
|
|
209
|
+
if (statSync(src).isDirectory()) {
|
|
210
|
+
cpSync(src, dest, { recursive: true });
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
copyFileSync(src, dest);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=vercel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../../src/cli/adapters/vercel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapt.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/adapt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKnC,wBAAgB,YAAY,IAAI,OAAO,CAqBtC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { resolve } from 'pathe';
|
|
3
|
+
import { runVercelAdapter } from '../adapters/vercel.js';
|
|
4
|
+
import { runNetlifyAdapter } from '../adapters/netlify.js';
|
|
5
|
+
export function adaptCommand() {
|
|
6
|
+
return new Command('adapt')
|
|
7
|
+
.description('Adapt the production build for a deployment platform')
|
|
8
|
+
.requiredOption('--platform <platform>', 'Target platform: vercel or netlify')
|
|
9
|
+
.option('--root <root>', 'Project root directory', process.cwd())
|
|
10
|
+
.action(async (options) => {
|
|
11
|
+
const root = resolve(options.root);
|
|
12
|
+
switch (options.platform) {
|
|
13
|
+
case 'vercel':
|
|
14
|
+
await runVercelAdapter(root);
|
|
15
|
+
break;
|
|
16
|
+
case 'netlify':
|
|
17
|
+
await runNetlifyAdapter(root);
|
|
18
|
+
break;
|
|
19
|
+
default:
|
|
20
|
+
console.error(`[cer-app] Unknown platform: "${options.platform}". Supported: vercel, netlify`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=adapt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapt.js","sourceRoot":"","sources":["../../../src/cli/commands/adapt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE1D,MAAM,UAAU,YAAY;IAC1B,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;SACxB,WAAW,CAAC,sDAAsD,CAAC;SACnE,cAAc,CAAC,uBAAuB,EAAE,oCAAoC,CAAC;SAC7E,MAAM,CAAC,eAAe,EAAE,wBAAwB,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;SAChE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACxB,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QAClC,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;YACzB,KAAK,QAAQ;gBACX,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAA;gBAC5B,MAAK;YACP,KAAK,SAAS;gBACZ,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAA;gBAC7B,MAAK;YACP;gBACE,OAAO,CAAC,KAAK,CACX,gCAAgC,OAAO,CAAC,QAAQ,+BAA+B,CAChF,CAAA;gBACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACnB,CAAC;IACH,CAAC,CAAC,CAAA;AACN,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAgFnC,wBAAgB,YAAY,IAAI,OAAO,CA0EtC"}
|