@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,5 +1,9 @@
1
1
  import type * as http from "node:http";
2
2
 
3
+ import {
4
+ createLogger,
5
+ } from "./logger.js";
6
+
3
7
  export const DEV_EVENTS_PATH =
4
8
  "/_bcp/dev-events";
5
9
 
@@ -12,11 +16,28 @@ const INTERNAL_API_PATHS =
12
16
  ]);
13
17
 
14
18
  const HTTP_REQUEST_LOG_PATTERN =
15
- /^(?:\[bcp\]\s+)?(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+(\S+)\s+\d{3}\s+\d+(?:\.\d+)?ms$/i;
19
+ /^(?:\[bcp\]\s+)?(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+(\S+)\s+(\d{3})\s+(\d+(?:\.\d+)?)ms$/i;
20
+
21
+ const TIMING_LOG_PATTERN =
22
+ /^(Import|SSR):\s+(\d+(?:\.\d+)?)ms$/i;
16
23
 
17
24
  let requestLogFilterInstalled =
18
25
  false;
19
26
 
27
+ export interface DevHttpRequestLog {
28
+ method: string;
29
+ path: string;
30
+ status: number;
31
+ durationMs: number;
32
+ }
33
+
34
+ export type DevTimingLog = {
35
+ event:
36
+ | "module.import"
37
+ | "ssr.render";
38
+ durationMs: number;
39
+ };
40
+
20
41
  export function shouldLogDevApiPath(
21
42
  pathname: string
22
43
  ): boolean {
@@ -39,20 +60,73 @@ export function shouldLogDevApiPath(
39
60
  );
40
61
  }
41
62
 
42
- export function shouldWriteDevConsoleLine(
63
+ export function parseDevHttpRequestLog(
43
64
  line: string
44
- ): boolean {
65
+ ): DevHttpRequestLog | null {
45
66
  const match =
46
67
  HTTP_REQUEST_LOG_PATTERN.exec(
47
68
  line.trim()
48
69
  );
49
70
 
50
71
  if (!match) {
72
+ return null;
73
+ }
74
+
75
+ return {
76
+ method:
77
+ match[1].toUpperCase(),
78
+ path:
79
+ match[2],
80
+ status:
81
+ Number(
82
+ match[3]
83
+ ),
84
+ durationMs:
85
+ Number(
86
+ match[4]
87
+ ),
88
+ };
89
+ }
90
+
91
+ export function parseDevTimingLog(
92
+ line: string
93
+ ): DevTimingLog | null {
94
+ const match =
95
+ TIMING_LOG_PATTERN.exec(
96
+ line.trim()
97
+ );
98
+
99
+ if (!match) {
100
+ return null;
101
+ }
102
+
103
+ return {
104
+ event:
105
+ match[1].toLowerCase() ===
106
+ "ssr"
107
+ ? "ssr.render"
108
+ : "module.import",
109
+ durationMs:
110
+ Number(
111
+ match[2]
112
+ ),
113
+ };
114
+ }
115
+
116
+ export function shouldWriteDevConsoleLine(
117
+ line: string
118
+ ): boolean {
119
+ const request =
120
+ parseDevHttpRequestLog(
121
+ line
122
+ );
123
+
124
+ if (!request) {
51
125
  return true;
52
126
  }
53
127
 
54
128
  return shouldLogDevApiPath(
55
- match[1]
129
+ request.path
56
130
  );
57
131
  }
58
132
 
@@ -68,18 +142,66 @@ function installRequestLogFilter(): void {
68
142
  console.log.bind(
69
143
  console
70
144
  );
145
+ const devLogger =
146
+ createLogger({
147
+ name:
148
+ "bcp-dev",
149
+ write(
150
+ line
151
+ ) {
152
+ originalLog(
153
+ line
154
+ );
155
+ },
156
+ });
71
157
 
72
158
  console.log = (
73
159
  ...args: unknown[]
74
160
  ) => {
75
161
  if (
76
162
  args.length === 1 &&
77
- typeof args[0] === "string" &&
78
- !shouldWriteDevConsoleLine(
79
- args[0]
80
- )
163
+ typeof args[0] === "string"
81
164
  ) {
82
- return;
165
+ const request =
166
+ parseDevHttpRequestLog(
167
+ args[0]
168
+ );
169
+
170
+ if (request) {
171
+ if (
172
+ shouldLogDevApiPath(
173
+ request.path
174
+ )
175
+ ) {
176
+ devLogger.info(
177
+ "HTTP request",
178
+ {
179
+ event:
180
+ "http.request",
181
+ ...request,
182
+ }
183
+ );
184
+ }
185
+
186
+ return;
187
+ }
188
+
189
+ const timing =
190
+ parseDevTimingLog(
191
+ args[0]
192
+ );
193
+
194
+ if (timing) {
195
+ devLogger.debug(
196
+ timing.event ===
197
+ "ssr.render"
198
+ ? "SSR render"
199
+ : "Module import",
200
+ timing
201
+ );
202
+
203
+ return;
204
+ }
83
205
  }
84
206
 
85
207
  originalLog(
@@ -0,0 +1,91 @@
1
+ export interface DevRouteGraphEntry {
2
+ pathname: string;
3
+ filePath: string;
4
+ layouts: string[];
5
+ }
6
+
7
+ export function hasDevClientRouteGraphChanged(
8
+ currentRoutes: DevRouteGraphEntry[],
9
+ nextRoutes: DevRouteGraphEntry[]
10
+ ): boolean {
11
+ if (
12
+ currentRoutes.length !==
13
+ nextRoutes.length
14
+ ) {
15
+ return true;
16
+ }
17
+
18
+ const current =
19
+ normalizeRouteGraph(
20
+ currentRoutes
21
+ );
22
+ const next =
23
+ normalizeRouteGraph(
24
+ nextRoutes
25
+ );
26
+
27
+ for (
28
+ let index = 0;
29
+ index < current.length;
30
+ index++
31
+ ) {
32
+ const left =
33
+ current[index];
34
+ const right =
35
+ next[index];
36
+
37
+ if (
38
+ left.pathname !==
39
+ right.pathname ||
40
+ left.filePath !==
41
+ right.filePath ||
42
+ left.layouts.length !==
43
+ right.layouts.length
44
+ ) {
45
+ return true;
46
+ }
47
+
48
+ for (
49
+ let layoutIndex = 0;
50
+ layoutIndex <
51
+ left.layouts.length;
52
+ layoutIndex++
53
+ ) {
54
+ if (
55
+ left.layouts[
56
+ layoutIndex
57
+ ] !==
58
+ right.layouts[
59
+ layoutIndex
60
+ ]
61
+ ) {
62
+ return true;
63
+ }
64
+ }
65
+ }
66
+
67
+ return false;
68
+ }
69
+
70
+ function normalizeRouteGraph(
71
+ routes: DevRouteGraphEntry[]
72
+ ): DevRouteGraphEntry[] {
73
+ return routes
74
+ .map(
75
+ (route) => ({
76
+ pathname:
77
+ route.pathname,
78
+ filePath:
79
+ route.filePath,
80
+ layouts: [
81
+ ...route.layouts,
82
+ ],
83
+ })
84
+ )
85
+ .sort(
86
+ (left, right) =>
87
+ left.pathname.localeCompare(
88
+ right.pathname
89
+ )
90
+ );
91
+ }