@chidchanun/bcp 0.1.22 → 0.1.24

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.
@@ -1,6 +1,12 @@
1
1
  import * as net from "node:net";
2
2
  import path from "node:path";
3
3
 
4
+ import chokidar from "chokidar";
5
+
6
+ import {
7
+ scanRoutes,
8
+ } from "../../router/src/index.js";
9
+
4
10
  import type {
5
11
  DevServerOptions,
6
12
  } from "./index.js";
@@ -9,6 +15,10 @@ import {
9
15
  createCriticalCssProxy,
10
16
  } from "./critical-css-proxy.js";
11
17
 
18
+ import {
19
+ hasDevClientRouteGraphChanged,
20
+ } from "./dev-route-graph.js";
21
+
12
22
  import {
13
23
  createFormActionDevProxy,
14
24
  } from "./form-action-dev-proxy.js";
@@ -29,6 +39,9 @@ import {
29
39
  createStaticDevServer,
30
40
  } from "./static-dev-server.js";
31
41
 
42
+ const ROUTE_GRAPH_DEBOUNCE_MS =
43
+ 120;
44
+
32
45
  export function createMiddlewareDevServer(
33
46
  options: DevServerOptions = {}
34
47
  ) {
@@ -46,6 +59,15 @@ export function createMiddlewareDevServer(
46
59
  process.cwd()
47
60
  );
48
61
 
62
+ const appDirectory =
63
+ path.join(
64
+ rootDirectory,
65
+ "app"
66
+ );
67
+
68
+ let applicationPort =
69
+ 0;
70
+
49
71
  let internalServer:
50
72
  ReturnType<
51
73
  typeof createStaticDevServer
@@ -76,6 +98,27 @@ export function createMiddlewareDevServer(
76
98
  > | null =
77
99
  null;
78
100
 
101
+ let routeWatcher:
102
+ ReturnType<
103
+ typeof chokidar.watch
104
+ > | null =
105
+ null;
106
+
107
+ let routeGraph =
108
+ scanRoutes(
109
+ appDirectory
110
+ );
111
+
112
+ let routeGraphTimer:
113
+ NodeJS.Timeout | null =
114
+ null;
115
+
116
+ let routeRestartRunning =
117
+ false;
118
+
119
+ let routeRestartQueued =
120
+ false;
121
+
79
122
  let started =
80
123
  false;
81
124
 
@@ -84,7 +127,7 @@ export function createMiddlewareDevServer(
84
127
  return;
85
128
  }
86
129
 
87
- const applicationPort =
130
+ applicationPort =
88
131
  await findFreePort();
89
132
  const actionPort =
90
133
  await findFreePort();
@@ -93,15 +136,13 @@ export function createMiddlewareDevServer(
93
136
  const securityPort =
94
137
  await findFreePort();
95
138
 
139
+ routeGraph =
140
+ scanRoutes(
141
+ appDirectory
142
+ );
143
+
96
144
  internalServer =
97
- createStaticDevServer({
98
- ...options,
99
- rootDirectory,
100
- port:
101
- applicationPort,
102
- hostname:
103
- "127.0.0.1",
104
- });
145
+ createInternalServer();
105
146
 
106
147
  await internalServer.start();
107
148
 
@@ -225,6 +266,8 @@ export function createMiddlewareDevServer(
225
266
  throw error;
226
267
  }
227
268
 
269
+ startRouteGraphWatcher();
270
+
228
271
  started =
229
272
  true;
230
273
 
@@ -244,6 +287,20 @@ export function createMiddlewareDevServer(
244
287
  }
245
288
 
246
289
  async function stop(): Promise<void> {
290
+ if (routeGraphTimer) {
291
+ clearTimeout(
292
+ routeGraphTimer
293
+ );
294
+ routeGraphTimer =
295
+ null;
296
+ }
297
+
298
+ if (routeWatcher) {
299
+ await routeWatcher.close();
300
+ routeWatcher =
301
+ null;
302
+ }
303
+
247
304
  if (criticalCssGateway) {
248
305
  await criticalCssGateway.stop();
249
306
  criticalCssGateway =
@@ -278,6 +335,157 @@ export function createMiddlewareDevServer(
278
335
  false;
279
336
  }
280
337
 
338
+ function createInternalServer() {
339
+ return createStaticDevServer({
340
+ ...options,
341
+ rootDirectory,
342
+ port:
343
+ applicationPort,
344
+ hostname:
345
+ "127.0.0.1",
346
+ });
347
+ }
348
+
349
+ function startRouteGraphWatcher(): void {
350
+ routeWatcher =
351
+ chokidar.watch(
352
+ appDirectory,
353
+ {
354
+ persistent: true,
355
+ ignoreInitial: true,
356
+ atomic: true,
357
+ ignored: (
358
+ watchedPath,
359
+ stats
360
+ ) => {
361
+ if (
362
+ stats?.isDirectory()
363
+ ) {
364
+ return false;
365
+ }
366
+
367
+ if (!stats) {
368
+ return false;
369
+ }
370
+
371
+ return !/\.[jt]sx?$/.test(
372
+ watchedPath
373
+ );
374
+ },
375
+ }
376
+ );
377
+
378
+ routeWatcher.on(
379
+ "all",
380
+ (
381
+ eventName
382
+ ) => {
383
+ if (
384
+ eventName === "add" ||
385
+ eventName === "change" ||
386
+ eventName === "unlink"
387
+ ) {
388
+ scheduleRouteGraphCheck();
389
+ }
390
+ }
391
+ );
392
+
393
+ routeWatcher.on(
394
+ "error",
395
+ (error) => {
396
+ console.error(
397
+ "[BCP Dev] Route graph watcher error:",
398
+ error
399
+ );
400
+ }
401
+ );
402
+ }
403
+
404
+ function scheduleRouteGraphCheck(): void {
405
+ if (routeGraphTimer) {
406
+ clearTimeout(
407
+ routeGraphTimer
408
+ );
409
+ }
410
+
411
+ routeGraphTimer =
412
+ setTimeout(
413
+ () => {
414
+ routeGraphTimer =
415
+ null;
416
+ void refreshRouteGraph();
417
+ },
418
+ ROUTE_GRAPH_DEBOUNCE_MS
419
+ );
420
+ }
421
+
422
+ async function refreshRouteGraph(): Promise<void> {
423
+ if (routeRestartRunning) {
424
+ routeRestartQueued =
425
+ true;
426
+ return;
427
+ }
428
+
429
+ const nextRouteGraph =
430
+ scanRoutes(
431
+ appDirectory
432
+ );
433
+
434
+ if (
435
+ !hasDevClientRouteGraphChanged(
436
+ routeGraph,
437
+ nextRouteGraph
438
+ )
439
+ ) {
440
+ return;
441
+ }
442
+
443
+ routeRestartRunning =
444
+ true;
445
+
446
+ try {
447
+ console.log(
448
+ "[BCP Dev] Page route graph changed; refreshing client bundler state..."
449
+ );
450
+
451
+ if (internalServer) {
452
+ await internalServer.stop();
453
+ internalServer =
454
+ null;
455
+ }
456
+
457
+ const nextServer =
458
+ createInternalServer();
459
+
460
+ await nextServer.start();
461
+
462
+ internalServer =
463
+ nextServer;
464
+ routeGraph =
465
+ nextRouteGraph;
466
+
467
+ console.log(
468
+ `[BCP Dev] Client bundle graph synchronized (${nextRouteGraph.length} route(s)).`
469
+ );
470
+ } catch (error) {
471
+ internalServer =
472
+ null;
473
+ console.error(
474
+ "[BCP Dev] Client bundle graph refresh failed:",
475
+ error
476
+ );
477
+ } finally {
478
+ routeRestartRunning =
479
+ false;
480
+
481
+ if (routeRestartQueued) {
482
+ routeRestartQueued =
483
+ false;
484
+ scheduleRouteGraphCheck();
485
+ }
486
+ }
487
+ }
488
+
281
489
  return {
282
490
  start,
283
491
  stop,