@chidchanun/bcp 0.1.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 (66) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/LICENSE +21 -0
  3. package/README.md +241 -0
  4. package/docs/caching.md +76 -0
  5. package/docs/configuration.md +97 -0
  6. package/docs/deployment.md +74 -0
  7. package/docs/getting-started.md +82 -0
  8. package/docs/middleware.md +58 -0
  9. package/docs/releasing.md +299 -0
  10. package/docs/routing.md +103 -0
  11. package/docs/security.md +57 -0
  12. package/package.json +68 -0
  13. package/packages/bundler/src/client-islands.ts +1457 -0
  14. package/packages/bundler/src/incremental-context.ts +206 -0
  15. package/packages/bundler/src/index.ts +1991 -0
  16. package/packages/bundler/src/module-graph.ts +317 -0
  17. package/packages/bundler/src/partial-hydration.ts +414 -0
  18. package/packages/bundler/src/production.ts +974 -0
  19. package/packages/bundler/src/server-production-middleware.ts +447 -0
  20. package/packages/bundler/src/server-production.ts +1193 -0
  21. package/packages/bundler/src/special-files.ts +131 -0
  22. package/packages/cache/src/index.ts +761 -0
  23. package/packages/cli/bin/bcp.mjs +93 -0
  24. package/packages/cli/src/args.ts +305 -0
  25. package/packages/cli/src/bootstrap.ts +514 -0
  26. package/packages/cli/src/index.ts +504 -0
  27. package/packages/cli/src/version.ts +45 -0
  28. package/packages/client/src/cache.ts +11 -0
  29. package/packages/client/src/config.ts +18 -0
  30. package/packages/client/src/error-boundary.tsx +149 -0
  31. package/packages/client/src/hydration.ts +3 -0
  32. package/packages/client/src/index.tsx +57 -0
  33. package/packages/client/src/islands.tsx +315 -0
  34. package/packages/client/src/metadata.ts +281 -0
  35. package/packages/client/src/navigation-loading.ts +52 -0
  36. package/packages/client/src/navigation-state.ts +80 -0
  37. package/packages/client/src/not-found.ts +34 -0
  38. package/packages/client/src/persistent-layout-runtime.ts +273 -0
  39. package/packages/client/src/router-v2.tsx +969 -0
  40. package/packages/client/src/router.tsx +1 -0
  41. package/packages/config/src/index.ts +1038 -0
  42. package/packages/env/src/index.ts +593 -0
  43. package/packages/router/src/advanced-router.ts +1032 -0
  44. package/packages/router/src/index.ts +1 -0
  45. package/packages/server/src/compression.ts +249 -0
  46. package/packages/server/src/dev-document-metadata.ts +154 -0
  47. package/packages/server/src/dev-hmr.ts +225 -0
  48. package/packages/server/src/index.ts +2265 -0
  49. package/packages/server/src/metadata.ts +478 -0
  50. package/packages/server/src/middleware-dev-server.ts +260 -0
  51. package/packages/server/src/middleware-loader.ts +140 -0
  52. package/packages/server/src/middleware-proxy.ts +516 -0
  53. package/packages/server/src/middleware.ts +704 -0
  54. package/packages/server/src/navigation-payload.ts +471 -0
  55. package/packages/server/src/production-server.ts +1746 -0
  56. package/packages/server/src/response-cache-proxy.ts +828 -0
  57. package/packages/server/src/security-proxy.ts +406 -0
  58. package/packages/server/src/security.ts +451 -0
  59. package/packages/server/src/standalone-production-runtime-v2.ts +2047 -0
  60. package/packages/server/src/standalone-production-runtime-v3.ts +250 -0
  61. package/packages/server/src/standalone-production-runtime-v4.ts +289 -0
  62. package/packages/server/src/standalone-production-runtime-v5.ts +289 -0
  63. package/packages/server/src/standalone-production-runtime.ts +1951 -0
  64. package/packages/server/src/standalone-production-server.ts +6 -0
  65. package/packages/server/src/static-assets.ts +262 -0
  66. package/packages/server/src/static-dev-server.ts +854 -0
@@ -0,0 +1,447 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import {
4
+ fileURLToPath,
5
+ } from "node:url";
6
+
7
+ import {
8
+ build,
9
+ } from "esbuild";
10
+
11
+ import {
12
+ createServerEnvironmentDefines,
13
+ } from "../../env/src/index.js";
14
+
15
+ import {
16
+ findProjectMiddlewareFile,
17
+ } from "../../server/src/middleware-loader.js";
18
+
19
+ import type {
20
+ ApiRoute,
21
+ Route,
22
+ } from "../../router/src/index.js";
23
+
24
+ import type {
25
+ RevalidateValue,
26
+ } from "../../cache/src/index.js";
27
+
28
+ import type {
29
+ PartialHydrationBuildManifest,
30
+ } from "./partial-hydration.js";
31
+
32
+ import {
33
+ buildProductionServer as buildBaseProductionServer,
34
+ type ProductionServerBuildResult,
35
+ } from "./server-production.js";
36
+
37
+ interface CacheManifestRoute {
38
+ pathname: string;
39
+ segments: Route["segments"];
40
+ revalidate: RevalidateValue;
41
+ }
42
+
43
+ interface CacheManifest {
44
+ version: 1;
45
+ pages: CacheManifestRoute[];
46
+ apiRoutes: CacheManifestRoute[];
47
+ }
48
+
49
+ export async function buildProductionServer(
50
+ pageRoutes: Route[],
51
+ apiRoutes: ApiRoute[],
52
+ rootDirectory: string,
53
+ clientManifest: PartialHydrationBuildManifest
54
+ ): Promise<ProductionServerBuildResult> {
55
+ const baseResult =
56
+ await buildBaseProductionServer(
57
+ pageRoutes,
58
+ apiRoutes,
59
+ rootDirectory,
60
+ clientManifest
61
+ );
62
+
63
+ await bundleServerCacheRuntime(
64
+ baseResult.serverFile,
65
+ rootDirectory
66
+ );
67
+
68
+ const serverDirectory =
69
+ path.dirname(
70
+ baseResult.serverFile
71
+ );
72
+
73
+ writeCacheManifest(
74
+ pageRoutes,
75
+ apiRoutes,
76
+ serverDirectory
77
+ );
78
+
79
+ await buildProductionMiddleware(
80
+ rootDirectory,
81
+ serverDirectory
82
+ );
83
+
84
+ return {
85
+ ...baseResult,
86
+ size:
87
+ fs.statSync(
88
+ baseResult.serverFile
89
+ ).size,
90
+ };
91
+ }
92
+
93
+ async function bundleServerCacheRuntime(
94
+ serverFile: string,
95
+ rootDirectory: string
96
+ ): Promise<void> {
97
+ const temporaryFile =
98
+ `${serverFile}.cache-runtime.mjs`;
99
+
100
+ const sourceMaps =
101
+ process.env
102
+ .BCP_BUILD_SOURCE_MAPS ===
103
+ "1";
104
+
105
+ const frameworkDirectory =
106
+ path.dirname(
107
+ fileURLToPath(
108
+ import.meta.url
109
+ )
110
+ );
111
+
112
+ const frameworkCacheEntry =
113
+ path.resolve(
114
+ frameworkDirectory,
115
+ "../../cache/src/index.ts"
116
+ );
117
+
118
+ try {
119
+ await build({
120
+ absWorkingDir:
121
+ rootDirectory,
122
+ entryPoints: [
123
+ serverFile,
124
+ ],
125
+ outfile:
126
+ temporaryFile,
127
+ bundle:
128
+ true,
129
+ platform:
130
+ "node",
131
+ format:
132
+ "esm",
133
+ target: [
134
+ "node24",
135
+ ],
136
+ minify:
137
+ process.env
138
+ .BCP_BUILD_MINIFY !==
139
+ "0",
140
+ sourcemap:
141
+ sourceMaps
142
+ ? "inline"
143
+ : false,
144
+ treeShaking:
145
+ true,
146
+ packages:
147
+ "external",
148
+ plugins: [
149
+ {
150
+ name:
151
+ "bcp-cache-runtime",
152
+ setup(buildApi) {
153
+ buildApi.onResolve(
154
+ {
155
+ filter:
156
+ /^bcp\/cache$/,
157
+ },
158
+ () => ({
159
+ path:
160
+ frameworkCacheEntry,
161
+ })
162
+ );
163
+ },
164
+ },
165
+ ],
166
+ legalComments:
167
+ "none",
168
+ logLevel:
169
+ "silent",
170
+ });
171
+
172
+ fs.copyFileSync(
173
+ temporaryFile,
174
+ serverFile
175
+ );
176
+
177
+ /*
178
+ * The base server build may have emitted an external map. The final
179
+ * cache-runtime rebundle uses an inline map so a stale server.mjs.map
180
+ * must not be shipped next to the rewritten bundle.
181
+ */
182
+ fs.rmSync(
183
+ `${serverFile}.map`,
184
+ {
185
+ force: true,
186
+ }
187
+ );
188
+ } finally {
189
+ fs.rmSync(
190
+ temporaryFile,
191
+ {
192
+ force: true,
193
+ }
194
+ );
195
+ fs.rmSync(
196
+ `${temporaryFile}.map`,
197
+ {
198
+ force: true,
199
+ }
200
+ );
201
+ }
202
+ }
203
+
204
+ function writeCacheManifest(
205
+ pageRoutes: Route[],
206
+ apiRoutes: ApiRoute[],
207
+ serverDirectory: string
208
+ ): void {
209
+ const manifest:
210
+ CacheManifest = {
211
+ version: 1,
212
+ pages:
213
+ collectCacheRoutes(
214
+ pageRoutes
215
+ ),
216
+ apiRoutes:
217
+ collectCacheRoutes(
218
+ apiRoutes
219
+ ),
220
+ };
221
+
222
+ const outputFile =
223
+ path.join(
224
+ serverDirectory,
225
+ "cache-manifest.json"
226
+ );
227
+
228
+ fs.writeFileSync(
229
+ outputFile,
230
+ JSON.stringify(
231
+ manifest,
232
+ null,
233
+ 2
234
+ ),
235
+ "utf8"
236
+ );
237
+
238
+ console.log(
239
+ `[BCP Build] Cache routes: ${manifest.pages.length} page(s), ${manifest.apiRoutes.length} API route(s)`
240
+ );
241
+ }
242
+
243
+ function collectCacheRoutes<
244
+ T extends Route | ApiRoute
245
+ >(
246
+ routes: T[]
247
+ ): CacheManifestRoute[] {
248
+ const result:
249
+ CacheManifestRoute[] = [];
250
+
251
+ for (const route of routes) {
252
+ const revalidate =
253
+ readRouteRevalidate(
254
+ route.filePath
255
+ );
256
+
257
+ if (
258
+ revalidate === undefined ||
259
+ revalidate === 0
260
+ ) {
261
+ continue;
262
+ }
263
+
264
+ result.push({
265
+ pathname:
266
+ route.pathname,
267
+ segments:
268
+ route.segments,
269
+ revalidate,
270
+ });
271
+ }
272
+
273
+ return result;
274
+ }
275
+
276
+ function readRouteRevalidate(
277
+ filePath: string
278
+ ): RevalidateValue | undefined {
279
+ const source =
280
+ fs.readFileSync(
281
+ filePath,
282
+ "utf8"
283
+ );
284
+
285
+ const declaration =
286
+ /\bexport\s+const\s+revalidate\b/.test(
287
+ source
288
+ );
289
+
290
+ if (!declaration) {
291
+ return undefined;
292
+ }
293
+
294
+ const match =
295
+ /\bexport\s+const\s+revalidate\s*(?::[^=;]+)?=\s*(false|(?:\d+(?:\.\d+)?))\s*;?/.exec(
296
+ source
297
+ );
298
+
299
+ if (!match) {
300
+ throw new Error(
301
+ `BCP Framework: revalidate in "${filePath}" must be a literal false or non-negative number.`
302
+ );
303
+ }
304
+
305
+ if (
306
+ match[1] === "false"
307
+ ) {
308
+ return false;
309
+ }
310
+
311
+ const value =
312
+ Number(
313
+ match[1]
314
+ );
315
+
316
+ if (
317
+ !Number.isFinite(value) ||
318
+ value < 0
319
+ ) {
320
+ throw new Error(
321
+ `BCP Framework: invalid revalidate value in "${filePath}".`
322
+ );
323
+ }
324
+
325
+ return value;
326
+ }
327
+
328
+ async function buildProductionMiddleware(
329
+ rootDirectory: string,
330
+ serverDirectory: string
331
+ ): Promise<void> {
332
+ const outputFile =
333
+ path.join(
334
+ serverDirectory,
335
+ "middleware.mjs"
336
+ );
337
+
338
+ fs.rmSync(
339
+ outputFile,
340
+ {
341
+ force: true,
342
+ }
343
+ );
344
+
345
+ const middlewareFile =
346
+ findProjectMiddlewareFile(
347
+ rootDirectory
348
+ );
349
+
350
+ if (!middlewareFile) {
351
+ console.log(
352
+ "[BCP Build] Middleware: none"
353
+ );
354
+ return;
355
+ }
356
+
357
+ const frameworkDirectory =
358
+ path.dirname(
359
+ fileURLToPath(
360
+ import.meta.url
361
+ )
362
+ );
363
+
364
+ const frameworkMiddlewareEntry =
365
+ path.resolve(
366
+ frameworkDirectory,
367
+ "../../server/src/middleware.ts"
368
+ );
369
+
370
+ const frameworkCacheEntry =
371
+ path.resolve(
372
+ frameworkDirectory,
373
+ "../../cache/src/index.ts"
374
+ );
375
+
376
+ await build({
377
+ absWorkingDir:
378
+ rootDirectory,
379
+ entryPoints: [
380
+ middlewareFile,
381
+ ],
382
+ outfile:
383
+ outputFile,
384
+ bundle:
385
+ true,
386
+ platform:
387
+ "node",
388
+ format:
389
+ "esm",
390
+ target: [
391
+ "node24",
392
+ ],
393
+ minify:
394
+ process.env
395
+ .BCP_BUILD_MINIFY !==
396
+ "0",
397
+ sourcemap:
398
+ process.env
399
+ .BCP_BUILD_SOURCE_MAPS ===
400
+ "1",
401
+ treeShaking:
402
+ true,
403
+ packages:
404
+ "external",
405
+ plugins: [
406
+ {
407
+ name:
408
+ "bcp-middleware-runtime",
409
+ setup(buildApi) {
410
+ buildApi.onResolve(
411
+ {
412
+ filter:
413
+ /^bcp\/middleware$/,
414
+ },
415
+ () => ({
416
+ path:
417
+ frameworkMiddlewareEntry,
418
+ })
419
+ );
420
+
421
+ buildApi.onResolve(
422
+ {
423
+ filter:
424
+ /^bcp\/cache$/,
425
+ },
426
+ () => ({
427
+ path:
428
+ frameworkCacheEntry,
429
+ })
430
+ );
431
+ },
432
+ },
433
+ ],
434
+ define:
435
+ createServerEnvironmentDefines(
436
+ "production"
437
+ ),
438
+ legalComments:
439
+ "none",
440
+ logLevel:
441
+ "silent",
442
+ });
443
+
444
+ console.log(
445
+ `[BCP Build] Middleware: ${path.relative(rootDirectory, middlewareFile).replace(/\\/g, "/")} -> ${path.relative(rootDirectory, outputFile).replace(/\\/g, "/")}`
446
+ );
447
+ }