@buenojs/bueno 0.8.3 → 0.8.5
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/README.md +136 -16
- package/dist/cli/{index.js → bin.js} +3036 -1421
- package/dist/container/index.js +250 -0
- package/dist/context/index.js +219 -0
- package/dist/database/index.js +493 -0
- package/dist/frontend/index.js +7697 -0
- package/dist/health/index.js +364 -0
- package/dist/i18n/index.js +345 -0
- package/dist/index.js +11043 -6482
- package/dist/jobs/index.js +819 -0
- package/dist/lock/index.js +367 -0
- package/dist/logger/index.js +281 -0
- package/dist/metrics/index.js +289 -0
- package/dist/middleware/index.js +77 -0
- package/dist/migrations/index.js +571 -0
- package/dist/modules/index.js +3346 -0
- package/dist/notification/index.js +484 -0
- package/dist/observability/index.js +331 -0
- package/dist/openapi/index.js +776 -0
- package/dist/orm/index.js +1356 -0
- package/dist/router/index.js +886 -0
- package/dist/rpc/index.js +691 -0
- package/dist/schema/index.js +400 -0
- package/dist/telemetry/index.js +595 -0
- package/dist/template/index.js +640 -0
- package/dist/templates/index.js +640 -0
- package/dist/testing/index.js +1111 -0
- package/dist/types/index.js +60 -0
- package/package.json +121 -27
- package/src/cache/index.ts +2 -1
- package/src/cli/bin.ts +2 -2
- package/src/cli/commands/build.ts +183 -165
- package/src/cli/commands/dev.ts +96 -89
- package/src/cli/commands/generate.ts +142 -111
- package/src/cli/commands/help.ts +20 -16
- package/src/cli/commands/index.ts +3 -6
- package/src/cli/commands/migration.ts +124 -105
- package/src/cli/commands/new.ts +392 -438
- package/src/cli/commands/start.ts +81 -79
- package/src/cli/core/args.ts +68 -50
- package/src/cli/core/console.ts +89 -95
- package/src/cli/core/index.ts +4 -4
- package/src/cli/core/prompt.ts +65 -62
- package/src/cli/core/spinner.ts +23 -20
- package/src/cli/index.ts +46 -38
- package/src/cli/templates/database/index.ts +61 -0
- package/src/cli/templates/database/mysql.ts +14 -0
- package/src/cli/templates/database/none.ts +16 -0
- package/src/cli/templates/database/postgresql.ts +14 -0
- package/src/cli/templates/database/sqlite.ts +14 -0
- package/src/cli/templates/deploy.ts +29 -26
- package/src/cli/templates/docker.ts +41 -30
- package/src/cli/templates/frontend/index.ts +63 -0
- package/src/cli/templates/frontend/none.ts +17 -0
- package/src/cli/templates/frontend/react.ts +140 -0
- package/src/cli/templates/frontend/solid.ts +134 -0
- package/src/cli/templates/frontend/svelte.ts +131 -0
- package/src/cli/templates/frontend/vue.ts +130 -0
- package/src/cli/templates/generators/index.ts +339 -0
- package/src/cli/templates/generators/types.ts +56 -0
- package/src/cli/templates/index.ts +35 -2
- package/src/cli/templates/project/api.ts +81 -0
- package/src/cli/templates/project/default.ts +140 -0
- package/src/cli/templates/project/fullstack.ts +111 -0
- package/src/cli/templates/project/index.ts +95 -0
- package/src/cli/templates/project/minimal.ts +45 -0
- package/src/cli/templates/project/types.ts +94 -0
- package/src/cli/templates/project/website.ts +263 -0
- package/src/cli/utils/fs.ts +55 -41
- package/src/cli/utils/index.ts +3 -2
- package/src/cli/utils/strings.ts +47 -33
- package/src/cli/utils/version.ts +47 -0
- package/src/config/env-validation.ts +100 -0
- package/src/config/env.ts +169 -41
- package/src/config/index.ts +28 -20
- package/src/config/loader.ts +25 -16
- package/src/config/merge.ts +21 -10
- package/src/config/types.ts +545 -25
- package/src/config/validation.ts +215 -7
- package/src/container/forward-ref.ts +22 -22
- package/src/container/index.ts +34 -12
- package/src/context/index.ts +11 -1
- package/src/database/index.ts +7 -190
- package/src/database/orm/builder.ts +457 -0
- package/src/database/orm/casts/index.ts +130 -0
- package/src/database/orm/casts/types.ts +25 -0
- package/src/database/orm/compiler.ts +304 -0
- package/src/database/orm/hooks/index.ts +114 -0
- package/src/database/orm/index.ts +61 -0
- package/src/database/orm/model-registry.ts +59 -0
- package/src/database/orm/model.ts +821 -0
- package/src/database/orm/relationships/base.ts +146 -0
- package/src/database/orm/relationships/belongs-to-many.ts +179 -0
- package/src/database/orm/relationships/belongs-to.ts +56 -0
- package/src/database/orm/relationships/has-many.ts +45 -0
- package/src/database/orm/relationships/has-one.ts +41 -0
- package/src/database/orm/relationships/index.ts +11 -0
- package/src/database/orm/scopes/index.ts +55 -0
- package/src/events/__tests__/event-system.test.ts +235 -0
- package/src/events/config.ts +238 -0
- package/src/events/example-usage.ts +185 -0
- package/src/events/index.ts +278 -0
- package/src/events/manager.ts +385 -0
- package/src/events/registry.ts +182 -0
- package/src/events/types.ts +124 -0
- package/src/frontend/api-routes.ts +65 -23
- package/src/frontend/bundler.ts +76 -34
- package/src/frontend/console-client.ts +2 -2
- package/src/frontend/console-stream.ts +94 -38
- package/src/frontend/dev-server.ts +94 -46
- package/src/frontend/file-router.ts +61 -19
- package/src/frontend/frameworks/index.ts +37 -10
- package/src/frontend/frameworks/react.ts +10 -8
- package/src/frontend/frameworks/solid.ts +11 -9
- package/src/frontend/frameworks/svelte.ts +15 -9
- package/src/frontend/frameworks/vue.ts +13 -11
- package/src/frontend/hmr-client.ts +12 -10
- package/src/frontend/hmr.ts +146 -103
- package/src/frontend/index.ts +14 -5
- package/src/frontend/islands.ts +41 -22
- package/src/frontend/isr.ts +59 -37
- package/src/frontend/layout.ts +36 -21
- package/src/frontend/ssr/react.ts +74 -27
- package/src/frontend/ssr/solid.ts +54 -20
- package/src/frontend/ssr/svelte.ts +48 -14
- package/src/frontend/ssr/vue.ts +50 -18
- package/src/frontend/ssr.ts +83 -39
- package/src/frontend/types.ts +91 -56
- package/src/health/index.ts +21 -9
- package/src/i18n/engine.ts +305 -0
- package/src/i18n/index.ts +38 -0
- package/src/i18n/loader.ts +218 -0
- package/src/i18n/middleware.ts +164 -0
- package/src/i18n/negotiator.ts +162 -0
- package/src/i18n/types.ts +158 -0
- package/src/index.ts +179 -27
- package/src/jobs/drivers/memory.ts +315 -0
- package/src/jobs/drivers/redis.ts +459 -0
- package/src/jobs/index.ts +30 -0
- package/src/jobs/queue.ts +281 -0
- package/src/jobs/types.ts +295 -0
- package/src/jobs/worker.ts +380 -0
- package/src/logger/index.ts +1 -3
- package/src/logger/transports/index.ts +62 -22
- package/src/metrics/index.ts +25 -16
- package/src/migrations/index.ts +9 -0
- package/src/modules/filters.ts +13 -17
- package/src/modules/guards.ts +49 -26
- package/src/modules/index.ts +409 -298
- package/src/modules/interceptors.ts +58 -20
- package/src/modules/lazy.ts +11 -19
- package/src/modules/lifecycle.ts +15 -7
- package/src/modules/metadata.ts +15 -5
- package/src/modules/pipes.ts +94 -72
- package/src/notification/channels/base.ts +68 -0
- package/src/notification/channels/email.ts +105 -0
- package/src/notification/channels/push.ts +104 -0
- package/src/notification/channels/sms.ts +105 -0
- package/src/notification/channels/whatsapp.ts +104 -0
- package/src/notification/index.ts +48 -0
- package/src/notification/service.ts +354 -0
- package/src/notification/types.ts +344 -0
- package/src/observability/__tests__/observability.test.ts +483 -0
- package/src/observability/breadcrumbs.ts +114 -0
- package/src/observability/index.ts +136 -0
- package/src/observability/interceptor.ts +85 -0
- package/src/observability/service.ts +303 -0
- package/src/observability/trace.ts +37 -0
- package/src/observability/types.ts +196 -0
- package/src/openapi/__tests__/decorators.test.ts +335 -0
- package/src/openapi/__tests__/document-builder.test.ts +285 -0
- package/src/openapi/__tests__/route-scanner.test.ts +334 -0
- package/src/openapi/__tests__/schema-generator.test.ts +275 -0
- package/src/openapi/decorators.ts +328 -0
- package/src/openapi/document-builder.ts +274 -0
- package/src/openapi/index.ts +112 -0
- package/src/openapi/metadata.ts +112 -0
- package/src/openapi/route-scanner.ts +289 -0
- package/src/openapi/schema-generator.ts +256 -0
- package/src/openapi/swagger-module.ts +166 -0
- package/src/openapi/types.ts +398 -0
- package/src/orm/index.ts +10 -0
- package/src/rpc/index.ts +3 -1
- package/src/schema/index.ts +9 -0
- package/src/security/index.ts +15 -6
- package/src/ssg/index.ts +9 -8
- package/src/telemetry/index.ts +76 -22
- package/src/template/index.ts +7 -0
- package/src/templates/engine.ts +224 -0
- package/src/templates/index.ts +9 -0
- package/src/templates/loader.ts +331 -0
- package/src/templates/renderers/markdown.ts +212 -0
- package/src/templates/renderers/simple.ts +269 -0
- package/src/templates/types.ts +154 -0
- package/src/testing/index.ts +100 -27
- package/src/types/optional-deps.d.ts +347 -187
- package/src/validation/index.ts +92 -2
- package/src/validation/schemas.ts +536 -0
- package/tests/integration/fullstack.test.ts +4 -4
- package/tests/unit/database.test.ts +2 -72
- package/tests/unit/env-validation.test.ts +166 -0
- package/tests/unit/events.test.ts +910 -0
- package/tests/unit/i18n.test.ts +455 -0
- package/tests/unit/jobs.test.ts +493 -0
- package/tests/unit/notification.test.ts +988 -0
- package/tests/unit/observability.test.ts +453 -0
- package/tests/unit/orm/builder.test.ts +323 -0
- package/tests/unit/orm/casts.test.ts +179 -0
- package/tests/unit/orm/compiler.test.ts +220 -0
- package/tests/unit/orm/eager-loading.test.ts +285 -0
- package/tests/unit/orm/hooks.test.ts +191 -0
- package/tests/unit/orm/model.test.ts +373 -0
- package/tests/unit/orm/relationships.test.ts +303 -0
- package/tests/unit/orm/scopes.test.ts +74 -0
- package/tests/unit/templates-simple.test.ts +53 -0
- package/tests/unit/templates.test.ts +454 -0
- package/tests/unit/validation.test.ts +18 -24
- package/tsconfig.json +11 -3
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* including Vue SFC support, JSX configuration, and Vue-specific defines.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {
|
|
8
|
+
import type { BuildPlugin, FrameworkBuildConfig } from "../types.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Vue-specific build plugins
|
|
@@ -20,26 +20,28 @@ export function getVueBuildConfig(): FrameworkBuildConfig {
|
|
|
20
20
|
return {
|
|
21
21
|
// Vue uses classic JSX runtime
|
|
22
22
|
jsxRuntime: "classic",
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
// Vue-specific file extensions
|
|
25
25
|
extensions: [".vue", ".jsx", ".tsx", ".js", ".ts"],
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
// Vue-specific plugins
|
|
28
28
|
plugins: vuePlugins,
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
// Vue-specific global defines
|
|
31
31
|
define: {
|
|
32
32
|
// Vue production mode flag
|
|
33
|
-
"process.env.NODE_ENV": JSON.stringify(
|
|
33
|
+
"process.env.NODE_ENV": JSON.stringify(
|
|
34
|
+
process.env.NODE_ENV || "development",
|
|
35
|
+
),
|
|
34
36
|
// Vue 3 specific features
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
__VUE_OPTIONS_API__: "true",
|
|
38
|
+
__VUE_PROD_DEVTOOLS__: "false",
|
|
39
|
+
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false",
|
|
38
40
|
},
|
|
39
|
-
|
|
41
|
+
|
|
40
42
|
// External dependencies
|
|
41
43
|
external: [],
|
|
42
|
-
|
|
44
|
+
|
|
43
45
|
// Loader configurations for Vue files
|
|
44
46
|
loaders: {
|
|
45
47
|
".vue": "js", // Vue SFCs are compiled to JS
|
|
@@ -89,4 +91,4 @@ export const vueFrameworkMeta: {
|
|
|
89
91
|
needsRefreshRuntime: false, // Vue has built-in HMR
|
|
90
92
|
supportsHMR: true,
|
|
91
93
|
supportsSSR: true,
|
|
92
|
-
};
|
|
94
|
+
};
|
|
@@ -637,7 +637,7 @@ export function getHMRClientScript(options?: {
|
|
|
637
637
|
if (options?.port) {
|
|
638
638
|
return HMR_CLIENT_SCRIPT.replace(
|
|
639
639
|
"return parseInt(window.location.port || '3000', 10) + 1;",
|
|
640
|
-
`return ${options.port}
|
|
640
|
+
`return ${options.port};`,
|
|
641
641
|
);
|
|
642
642
|
}
|
|
643
643
|
return HMR_CLIENT_SCRIPT;
|
|
@@ -648,16 +648,18 @@ export function getHMRClientScript(options?: {
|
|
|
648
648
|
*/
|
|
649
649
|
export function injectHMRScript(html: string, port?: number): string {
|
|
650
650
|
const script = getHMRClientScript({ port });
|
|
651
|
-
|
|
651
|
+
|
|
652
652
|
// Find the </head> or </body> tag to inject before
|
|
653
653
|
const headMatch = html.match(/<\/head>/i);
|
|
654
654
|
const bodyMatch = html.match(/<\/body>/i);
|
|
655
|
-
|
|
656
|
-
const injectionPoint = headMatch
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
655
|
+
|
|
656
|
+
const injectionPoint = headMatch
|
|
657
|
+
? headMatch.index! + headMatch[0].length
|
|
658
|
+
: bodyMatch
|
|
659
|
+
? bodyMatch.index! + bodyMatch[0].length
|
|
660
|
+
: html.length;
|
|
661
|
+
|
|
662
|
+
const scriptTag = `<script data-hmr-port="${port || ""}">${script}</script>`;
|
|
663
|
+
|
|
662
664
|
return html.slice(0, injectionPoint) + scriptTag + html.slice(injectionPoint);
|
|
663
|
-
}
|
|
665
|
+
}
|
package/src/frontend/hmr.ts
CHANGED
|
@@ -7,29 +7,35 @@
|
|
|
7
7
|
* @module frontend/hmr
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { type Logger, createLogger } from "../logger/index.js";
|
|
11
|
+
import { HMR_CLIENT_SCRIPT } from "./hmr-client.js";
|
|
11
12
|
import type {
|
|
13
|
+
FileChangeEvent,
|
|
14
|
+
FrontendFramework,
|
|
12
15
|
HMRClient,
|
|
16
|
+
HMRClientMessage,
|
|
13
17
|
HMRConfig,
|
|
14
|
-
HMRUpdate,
|
|
15
|
-
HMRUpdateError,
|
|
16
18
|
HMRDependencyNode,
|
|
17
|
-
HMRClientMessage,
|
|
18
19
|
HMRServerMessage,
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
HMRUpdate,
|
|
21
|
+
HMRUpdateError,
|
|
21
22
|
} from "./types.js";
|
|
22
|
-
import { HMR_CLIENT_SCRIPT } from "./hmr-client.js";
|
|
23
23
|
|
|
24
24
|
// ============= Constants =============
|
|
25
25
|
|
|
26
26
|
const DEFAULT_DEBOUNCE_MS = 100;
|
|
27
|
-
const DEFAULT_IGNORE_PATTERNS = [
|
|
27
|
+
const DEFAULT_IGNORE_PATTERNS = [
|
|
28
|
+
"node_modules",
|
|
29
|
+
".git",
|
|
30
|
+
"dist",
|
|
31
|
+
"build",
|
|
32
|
+
".bun",
|
|
33
|
+
];
|
|
28
34
|
|
|
29
35
|
// ============= File Watcher Types =============
|
|
30
36
|
|
|
31
37
|
interface FileWatchEvent {
|
|
32
|
-
event:
|
|
38
|
+
event: "create" | "update" | "delete";
|
|
33
39
|
filePath: string;
|
|
34
40
|
}
|
|
35
41
|
|
|
@@ -56,9 +62,13 @@ export class HMRManager {
|
|
|
56
62
|
private debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
57
63
|
private framework: FrontendFramework;
|
|
58
64
|
private devServerPort: number;
|
|
59
|
-
private watcher: ReturnType<typeof import(
|
|
65
|
+
private watcher: ReturnType<typeof import("fs").watch> | null = null;
|
|
60
66
|
|
|
61
|
-
constructor(
|
|
67
|
+
constructor(
|
|
68
|
+
framework: FrontendFramework,
|
|
69
|
+
devServerPort: number,
|
|
70
|
+
config?: Partial<HMRConfig>,
|
|
71
|
+
) {
|
|
62
72
|
this.framework = framework;
|
|
63
73
|
this.devServerPort = devServerPort;
|
|
64
74
|
this.config = this.normalizeConfig(config);
|
|
@@ -114,7 +124,7 @@ export class HMRManager {
|
|
|
114
124
|
*/
|
|
115
125
|
handleUpgrade(request: Request): WebSocket | null {
|
|
116
126
|
const url = new URL(request.url);
|
|
117
|
-
|
|
127
|
+
|
|
118
128
|
if (url.pathname !== "/_hmr") {
|
|
119
129
|
return null;
|
|
120
130
|
}
|
|
@@ -136,17 +146,22 @@ export class HMRManager {
|
|
|
136
146
|
},
|
|
137
147
|
});
|
|
138
148
|
|
|
139
|
-
this.logger.info(
|
|
140
|
-
|
|
149
|
+
this.logger.info(
|
|
150
|
+
`HMR WebSocket server started on port ${this.config.port}`,
|
|
151
|
+
);
|
|
152
|
+
|
|
141
153
|
return null; // The server handles the WebSocket directly
|
|
142
154
|
}
|
|
143
155
|
|
|
144
156
|
/**
|
|
145
157
|
* Handle fetch requests for WebSocket server
|
|
146
158
|
*/
|
|
147
|
-
private handleWebSocketFetch(
|
|
159
|
+
private handleWebSocketFetch(
|
|
160
|
+
request: Request,
|
|
161
|
+
server: any,
|
|
162
|
+
): Response | undefined {
|
|
148
163
|
const url = new URL(request.url);
|
|
149
|
-
|
|
164
|
+
|
|
150
165
|
if (url.pathname === "/_hmr") {
|
|
151
166
|
const success = server.upgrade(request);
|
|
152
167
|
if (success) {
|
|
@@ -154,7 +169,7 @@ export class HMRManager {
|
|
|
154
169
|
}
|
|
155
170
|
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
156
171
|
}
|
|
157
|
-
|
|
172
|
+
|
|
158
173
|
return new Response("Not found", { status: 404 });
|
|
159
174
|
}
|
|
160
175
|
|
|
@@ -168,12 +183,12 @@ export class HMRManager {
|
|
|
168
183
|
ws: ws,
|
|
169
184
|
subscribedFiles: new Set(),
|
|
170
185
|
};
|
|
171
|
-
|
|
186
|
+
|
|
172
187
|
this.clients.set(clientId, client);
|
|
173
188
|
ws.data = { clientId };
|
|
174
|
-
|
|
189
|
+
|
|
175
190
|
this.logger.debug(`HMR client connected: ${clientId}`);
|
|
176
|
-
|
|
191
|
+
|
|
177
192
|
// Send connected message
|
|
178
193
|
this.sendToClient(ws, {
|
|
179
194
|
type: "connected",
|
|
@@ -199,31 +214,33 @@ export class HMRManager {
|
|
|
199
214
|
try {
|
|
200
215
|
const data: HMRClientMessage = JSON.parse(message.toString());
|
|
201
216
|
const clientId = ws.data?.clientId;
|
|
202
|
-
|
|
217
|
+
|
|
203
218
|
if (!clientId) {
|
|
204
219
|
return;
|
|
205
220
|
}
|
|
206
|
-
|
|
221
|
+
|
|
207
222
|
const client = this.clients.get(clientId);
|
|
208
223
|
if (!client) {
|
|
209
224
|
return;
|
|
210
225
|
}
|
|
211
|
-
|
|
226
|
+
|
|
212
227
|
switch (data.type) {
|
|
213
228
|
case "subscribe":
|
|
214
229
|
client.subscribedFiles.add(data.fileId);
|
|
215
230
|
this.logger.debug(`Client ${clientId} subscribed to: ${data.fileId}`);
|
|
216
231
|
break;
|
|
217
|
-
|
|
232
|
+
|
|
218
233
|
case "unsubscribe":
|
|
219
234
|
client.subscribedFiles.delete(data.fileId);
|
|
220
|
-
this.logger.debug(
|
|
235
|
+
this.logger.debug(
|
|
236
|
+
`Client ${clientId} unsubscribed from: ${data.fileId}`,
|
|
237
|
+
);
|
|
221
238
|
break;
|
|
222
|
-
|
|
239
|
+
|
|
223
240
|
case "ping":
|
|
224
241
|
this.sendToClient(ws, { type: "pong" });
|
|
225
242
|
break;
|
|
226
|
-
|
|
243
|
+
|
|
227
244
|
case "module-accepted":
|
|
228
245
|
this.handleModuleAccepted(data.moduleId, data.dependencies);
|
|
229
246
|
break;
|
|
@@ -247,21 +264,23 @@ export class HMRManager {
|
|
|
247
264
|
*/
|
|
248
265
|
broadcastUpdate(update: HMRUpdate): void {
|
|
249
266
|
const message = JSON.stringify(update);
|
|
250
|
-
|
|
267
|
+
|
|
251
268
|
for (const client of this.clients.values()) {
|
|
252
269
|
// Check if client is subscribed to any of the changed files
|
|
253
|
-
const isSubscribed = update.changes.some(
|
|
254
|
-
|
|
270
|
+
const isSubscribed = update.changes.some((file) =>
|
|
271
|
+
client.subscribedFiles.has(file),
|
|
255
272
|
);
|
|
256
|
-
|
|
273
|
+
|
|
257
274
|
if (isSubscribed || update.type === "reload" || update.type === "error") {
|
|
258
275
|
if (client.ws.readyState === WebSocket.OPEN) {
|
|
259
276
|
client.ws.send(message);
|
|
260
277
|
}
|
|
261
278
|
}
|
|
262
279
|
}
|
|
263
|
-
|
|
264
|
-
this.logger.debug(
|
|
280
|
+
|
|
281
|
+
this.logger.debug(
|
|
282
|
+
`Broadcasted ${update.type} update for: ${update.fileId}`,
|
|
283
|
+
);
|
|
265
284
|
}
|
|
266
285
|
|
|
267
286
|
/**
|
|
@@ -271,32 +290,36 @@ export class HMRManager {
|
|
|
271
290
|
if (!this.config.enabled) {
|
|
272
291
|
return;
|
|
273
292
|
}
|
|
274
|
-
|
|
293
|
+
|
|
275
294
|
this.logger.info(`Starting file watcher for: ${rootDir}`);
|
|
276
|
-
|
|
295
|
+
|
|
277
296
|
// Use Node's fs.watch for file watching
|
|
278
|
-
const fs = require(
|
|
279
|
-
const path = require(
|
|
280
|
-
|
|
297
|
+
const fs = require("fs");
|
|
298
|
+
const path = require("path");
|
|
299
|
+
|
|
281
300
|
try {
|
|
282
|
-
this.watcher = fs.watch(
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
301
|
+
this.watcher = fs.watch(
|
|
302
|
+
rootDir,
|
|
303
|
+
{ recursive: true },
|
|
304
|
+
(eventType: string, filename: string | null) => {
|
|
305
|
+
if (!filename) return;
|
|
306
|
+
|
|
307
|
+
const filePath = path.join(rootDir, filename);
|
|
308
|
+
|
|
309
|
+
if (eventType === "rename") {
|
|
310
|
+
// Check if file exists to determine if it's create or delete
|
|
311
|
+
try {
|
|
312
|
+
fs.accessSync(filePath, fs.constants.F_OK);
|
|
313
|
+
this.handleFileChange(filePath, "create");
|
|
314
|
+
} catch {
|
|
315
|
+
this.handleFileChange(filePath, "delete");
|
|
316
|
+
}
|
|
317
|
+
} else if (eventType === "change") {
|
|
318
|
+
this.handleFileChange(filePath, "update");
|
|
294
319
|
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
});
|
|
299
|
-
|
|
320
|
+
},
|
|
321
|
+
);
|
|
322
|
+
|
|
300
323
|
this.logger.info("File watcher started");
|
|
301
324
|
} catch (error) {
|
|
302
325
|
this.logger.error("Failed to start file watcher", error);
|
|
@@ -317,21 +340,24 @@ export class HMRManager {
|
|
|
317
340
|
/**
|
|
318
341
|
* Handle a file change event
|
|
319
342
|
*/
|
|
320
|
-
private handleFileChange(
|
|
343
|
+
private handleFileChange(
|
|
344
|
+
filePath: string,
|
|
345
|
+
event: "create" | "update" | "delete",
|
|
346
|
+
): void {
|
|
321
347
|
// Check if file should be ignored
|
|
322
348
|
if (this.shouldIgnoreFile(filePath)) {
|
|
323
349
|
return;
|
|
324
350
|
}
|
|
325
|
-
|
|
351
|
+
|
|
326
352
|
this.logger.debug(`File ${event}: ${filePath}`);
|
|
327
|
-
|
|
353
|
+
|
|
328
354
|
// Add to pending updates
|
|
329
355
|
this.pendingUpdates.set(filePath, {
|
|
330
356
|
path: filePath,
|
|
331
357
|
event,
|
|
332
358
|
timestamp: Date.now(),
|
|
333
359
|
});
|
|
334
|
-
|
|
360
|
+
|
|
335
361
|
// Debounce updates
|
|
336
362
|
this.scheduleDebouncedUpdate();
|
|
337
363
|
}
|
|
@@ -346,11 +372,22 @@ export class HMRManager {
|
|
|
346
372
|
return true;
|
|
347
373
|
}
|
|
348
374
|
}
|
|
349
|
-
|
|
375
|
+
|
|
350
376
|
// Only watch relevant file types
|
|
351
|
-
const relevantExtensions = [
|
|
377
|
+
const relevantExtensions = [
|
|
378
|
+
".js",
|
|
379
|
+
".jsx",
|
|
380
|
+
".ts",
|
|
381
|
+
".tsx",
|
|
382
|
+
".css",
|
|
383
|
+
".scss",
|
|
384
|
+
".sass",
|
|
385
|
+
".less",
|
|
386
|
+
".vue",
|
|
387
|
+
".svelte",
|
|
388
|
+
];
|
|
352
389
|
const ext = filePath.substring(filePath.lastIndexOf("."));
|
|
353
|
-
|
|
390
|
+
|
|
354
391
|
return !relevantExtensions.includes(ext);
|
|
355
392
|
}
|
|
356
393
|
|
|
@@ -361,7 +398,7 @@ export class HMRManager {
|
|
|
361
398
|
if (this.debounceTimer) {
|
|
362
399
|
clearTimeout(this.debounceTimer);
|
|
363
400
|
}
|
|
364
|
-
|
|
401
|
+
|
|
365
402
|
this.debounceTimer = setTimeout(() => {
|
|
366
403
|
this.processPendingUpdates();
|
|
367
404
|
}, this.config.debounceMs);
|
|
@@ -374,16 +411,16 @@ export class HMRManager {
|
|
|
374
411
|
if (this.pendingUpdates.size === 0) {
|
|
375
412
|
return;
|
|
376
413
|
}
|
|
377
|
-
|
|
414
|
+
|
|
378
415
|
const updates = Array.from(this.pendingUpdates.values());
|
|
379
416
|
this.pendingUpdates.clear();
|
|
380
|
-
|
|
417
|
+
|
|
381
418
|
// Group updates by type
|
|
382
419
|
const changedFiles = updates.map((u) => u.path);
|
|
383
|
-
|
|
420
|
+
|
|
384
421
|
// Determine update type based on file changes
|
|
385
422
|
const updateType = this.determineUpdateType(changedFiles);
|
|
386
|
-
|
|
423
|
+
|
|
387
424
|
// Create and broadcast update
|
|
388
425
|
const update: HMRUpdate = {
|
|
389
426
|
type: updateType,
|
|
@@ -391,7 +428,7 @@ export class HMRManager {
|
|
|
391
428
|
timestamp: Date.now(),
|
|
392
429
|
changes: changedFiles,
|
|
393
430
|
};
|
|
394
|
-
|
|
431
|
+
|
|
395
432
|
this.broadcastUpdate(update);
|
|
396
433
|
}
|
|
397
434
|
|
|
@@ -402,17 +439,21 @@ export class HMRManager {
|
|
|
402
439
|
// Check if any changed file requires a full reload
|
|
403
440
|
for (const file of changedFiles) {
|
|
404
441
|
const ext = file.substring(file.lastIndexOf("."));
|
|
405
|
-
|
|
442
|
+
|
|
406
443
|
// Configuration files always require full reload
|
|
407
|
-
if (
|
|
444
|
+
if (
|
|
445
|
+
file.includes("config") ||
|
|
446
|
+
file.endsWith(".config.js") ||
|
|
447
|
+
file.endsWith(".config.ts")
|
|
448
|
+
) {
|
|
408
449
|
return "reload";
|
|
409
450
|
}
|
|
410
|
-
|
|
451
|
+
|
|
411
452
|
// HTML files require full reload
|
|
412
453
|
if (ext === ".html") {
|
|
413
454
|
return "reload";
|
|
414
455
|
}
|
|
415
|
-
|
|
456
|
+
|
|
416
457
|
// Check dependency graph for breaking changes
|
|
417
458
|
const node = this.dependencyGraph.get(file);
|
|
418
459
|
if (node && node.importedBy.size > 0) {
|
|
@@ -421,7 +462,7 @@ export class HMRManager {
|
|
|
421
462
|
// In a full implementation, we'd analyze the actual changes
|
|
422
463
|
}
|
|
423
464
|
}
|
|
424
|
-
|
|
465
|
+
|
|
425
466
|
return "update";
|
|
426
467
|
}
|
|
427
468
|
|
|
@@ -434,7 +475,7 @@ export class HMRManager {
|
|
|
434
475
|
if (node) {
|
|
435
476
|
// Update imports
|
|
436
477
|
node.imports = new Set(dependencies);
|
|
437
|
-
|
|
478
|
+
|
|
438
479
|
// Update reverse dependencies
|
|
439
480
|
for (const dep of dependencies) {
|
|
440
481
|
const depNode = this.dependencyGraph.get(dep);
|
|
@@ -443,7 +484,7 @@ export class HMRManager {
|
|
|
443
484
|
}
|
|
444
485
|
}
|
|
445
486
|
}
|
|
446
|
-
|
|
487
|
+
|
|
447
488
|
this.logger.debug(`Module accepted: ${moduleId}`);
|
|
448
489
|
}
|
|
449
490
|
|
|
@@ -457,7 +498,7 @@ export class HMRManager {
|
|
|
457
498
|
importedBy: new Set(),
|
|
458
499
|
lastModified: Date.now(),
|
|
459
500
|
};
|
|
460
|
-
|
|
501
|
+
|
|
461
502
|
// Update reverse dependencies
|
|
462
503
|
for (const imp of imports) {
|
|
463
504
|
const importedNode = this.dependencyGraph.get(imp);
|
|
@@ -465,7 +506,7 @@ export class HMRManager {
|
|
|
465
506
|
importedNode.importedBy.add(filePath);
|
|
466
507
|
}
|
|
467
508
|
}
|
|
468
|
-
|
|
509
|
+
|
|
469
510
|
this.dependencyGraph.set(filePath, node);
|
|
470
511
|
}
|
|
471
512
|
|
|
@@ -477,7 +518,7 @@ export class HMRManager {
|
|
|
477
518
|
if (!node) {
|
|
478
519
|
return [];
|
|
479
520
|
}
|
|
480
|
-
|
|
521
|
+
|
|
481
522
|
return Array.from(node.importedBy);
|
|
482
523
|
}
|
|
483
524
|
|
|
@@ -489,7 +530,7 @@ export class HMRManager {
|
|
|
489
530
|
if (!node) {
|
|
490
531
|
return [];
|
|
491
532
|
}
|
|
492
|
-
|
|
533
|
+
|
|
493
534
|
return Array.from(node.imports);
|
|
494
535
|
}
|
|
495
536
|
|
|
@@ -497,13 +538,14 @@ export class HMRManager {
|
|
|
497
538
|
* Broadcast an error to all clients
|
|
498
539
|
*/
|
|
499
540
|
broadcastError(error: Error | HMRUpdateError): void {
|
|
500
|
-
const updateError: HMRUpdateError =
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
541
|
+
const updateError: HMRUpdateError =
|
|
542
|
+
error instanceof Error
|
|
543
|
+
? {
|
|
544
|
+
message: error.message,
|
|
545
|
+
stack: error.stack,
|
|
546
|
+
}
|
|
547
|
+
: error;
|
|
548
|
+
|
|
507
549
|
const update: HMRUpdate = {
|
|
508
550
|
type: "error",
|
|
509
551
|
fileId: updateError.file || "unknown",
|
|
@@ -511,7 +553,7 @@ export class HMRManager {
|
|
|
511
553
|
changes: [],
|
|
512
554
|
error: updateError,
|
|
513
555
|
};
|
|
514
|
-
|
|
556
|
+
|
|
515
557
|
this.broadcastUpdate(update);
|
|
516
558
|
}
|
|
517
559
|
|
|
@@ -553,7 +595,7 @@ export class HMRManager {
|
|
|
553
595
|
client.ws.close();
|
|
554
596
|
}
|
|
555
597
|
}
|
|
556
|
-
|
|
598
|
+
|
|
557
599
|
this.clients.clear();
|
|
558
600
|
this.logger.info("All HMR clients disconnected");
|
|
559
601
|
}
|
|
@@ -566,12 +608,12 @@ export class HMRManager {
|
|
|
566
608
|
this.disconnectAll();
|
|
567
609
|
this.dependencyGraph.clear();
|
|
568
610
|
this.pendingUpdates.clear();
|
|
569
|
-
|
|
611
|
+
|
|
570
612
|
if (this.debounceTimer) {
|
|
571
613
|
clearTimeout(this.debounceTimer);
|
|
572
614
|
this.debounceTimer = null;
|
|
573
615
|
}
|
|
574
|
-
|
|
616
|
+
|
|
575
617
|
this.logger.info("HMR manager stopped");
|
|
576
618
|
}
|
|
577
619
|
|
|
@@ -658,7 +700,7 @@ export class HMRManager {
|
|
|
658
700
|
export function createHMRManager(
|
|
659
701
|
framework: FrontendFramework,
|
|
660
702
|
devServerPort: number,
|
|
661
|
-
config?: Partial<HMRConfig
|
|
703
|
+
config?: Partial<HMRConfig>,
|
|
662
704
|
): HMRManager {
|
|
663
705
|
return new HMRManager(framework, devServerPort, config);
|
|
664
706
|
}
|
|
@@ -676,7 +718,7 @@ export function isHMRBoundary(filePath: string, content: string): boolean {
|
|
|
676
718
|
/import\.meta\.hot\.accept/,
|
|
677
719
|
/if\s*\(\s*import\.meta\.hot\s*\)/,
|
|
678
720
|
];
|
|
679
|
-
|
|
721
|
+
|
|
680
722
|
return patterns.some((pattern) => pattern.test(content));
|
|
681
723
|
}
|
|
682
724
|
|
|
@@ -685,29 +727,30 @@ export function isHMRBoundary(filePath: string, content: string): boolean {
|
|
|
685
727
|
*/
|
|
686
728
|
export function parseImports(content: string, filePath: string): string[] {
|
|
687
729
|
const imports: string[] = [];
|
|
688
|
-
|
|
730
|
+
|
|
689
731
|
// Match ES6 imports
|
|
690
|
-
const es6Pattern =
|
|
732
|
+
const es6Pattern =
|
|
733
|
+
/import\s+(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+)?['"]([^'"]+)['"]/g;
|
|
691
734
|
let match;
|
|
692
|
-
|
|
735
|
+
|
|
693
736
|
while ((match = es6Pattern.exec(content)) !== null) {
|
|
694
737
|
imports.push(resolveImport(match[1], filePath));
|
|
695
738
|
}
|
|
696
|
-
|
|
739
|
+
|
|
697
740
|
// Match dynamic imports
|
|
698
741
|
const dynamicPattern = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
699
|
-
|
|
742
|
+
|
|
700
743
|
while ((match = dynamicPattern.exec(content)) !== null) {
|
|
701
744
|
imports.push(resolveImport(match[1], filePath));
|
|
702
745
|
}
|
|
703
|
-
|
|
746
|
+
|
|
704
747
|
// Match CommonJS requires
|
|
705
748
|
const requirePattern = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
706
|
-
|
|
749
|
+
|
|
707
750
|
while ((match = requirePattern.exec(content)) !== null) {
|
|
708
751
|
imports.push(resolveImport(match[1], filePath));
|
|
709
752
|
}
|
|
710
|
-
|
|
753
|
+
|
|
711
754
|
return imports;
|
|
712
755
|
}
|
|
713
756
|
|
|
@@ -719,10 +762,10 @@ function resolveImport(importPath: string, fromFile: string): string {
|
|
|
719
762
|
if (!importPath.startsWith(".") && !importPath.startsWith("/")) {
|
|
720
763
|
return importPath;
|
|
721
764
|
}
|
|
722
|
-
|
|
765
|
+
|
|
723
766
|
// Resolve relative paths
|
|
724
767
|
const dir = fromFile.substring(0, fromFile.lastIndexOf("/"));
|
|
725
768
|
const resolved = new URL(importPath, `file://${dir}/`).pathname;
|
|
726
|
-
|
|
769
|
+
|
|
727
770
|
return resolved;
|
|
728
|
-
}
|
|
771
|
+
}
|
package/src/frontend/index.ts
CHANGED
|
@@ -79,19 +79,28 @@ export { DevServer, createDevServer } from "./dev-server.js";
|
|
|
79
79
|
|
|
80
80
|
// ============= HMR =============
|
|
81
81
|
|
|
82
|
-
export {
|
|
83
|
-
|
|
82
|
+
export {
|
|
83
|
+
HMRManager,
|
|
84
|
+
createHMRManager,
|
|
85
|
+
isHMRBoundary,
|
|
86
|
+
parseImports,
|
|
87
|
+
} from "./hmr.js";
|
|
88
|
+
export {
|
|
89
|
+
HMR_CLIENT_SCRIPT,
|
|
90
|
+
getHMRClientScript,
|
|
91
|
+
injectHMRScript,
|
|
92
|
+
} from "./hmr-client.js";
|
|
84
93
|
|
|
85
94
|
// ============= Console Stream =============
|
|
86
95
|
|
|
87
96
|
export {
|
|
88
97
|
ConsoleStreamManager,
|
|
89
98
|
createConsoleStreamManager,
|
|
90
|
-
injectConsoleScript
|
|
99
|
+
injectConsoleScript,
|
|
91
100
|
} from "./console-stream.js";
|
|
92
101
|
export {
|
|
93
102
|
CONSOLE_CLIENT_SCRIPT,
|
|
94
|
-
getConsoleClientScript
|
|
103
|
+
getConsoleClientScript,
|
|
95
104
|
} from "./console-client.js";
|
|
96
105
|
|
|
97
106
|
// ============= Bundler =============
|
|
@@ -339,4 +348,4 @@ export {
|
|
|
339
348
|
isAPIRouteFile,
|
|
340
349
|
isMiddlewareFile,
|
|
341
350
|
getModuleMethods,
|
|
342
|
-
} from "./api-routes.js";
|
|
351
|
+
} from "./api-routes.js";
|