@swissjs/swite 0.3.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/.changeset/config.json +11 -0
- package/.github/workflows/ci.yml +59 -0
- package/.github/workflows/publish.yml +50 -0
- package/.github/workflows/release.yml +53 -0
- package/BUILD_ANALYSIS.md +89 -0
- package/BUILD_STRATEGY.md +75 -0
- package/CHANGELOG.md +53 -0
- package/DIRECTIVE.md +488 -0
- package/__tests__/css-extraction.test.ts +261 -0
- package/__tests__/css-injection-integration.test.ts +247 -0
- package/__tests__/css-middleware.test.ts +191 -0
- package/__tests__/import-rewriter-bug.test.ts +135 -0
- package/dist/builder.d.ts +36 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +772 -0
- package/dist/cache/compilation-cache.d.ts +33 -0
- package/dist/cache/compilation-cache.d.ts.map +1 -0
- package/dist/cache/compilation-cache.js +130 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +85 -0
- package/dist/config-loader.d.ts +8 -0
- package/dist/config-loader.d.ts.map +1 -0
- package/dist/config-loader.js +40 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +7 -0
- package/dist/dev/pythonDevManager.d.ts +12 -0
- package/dist/dev/pythonDevManager.d.ts.map +1 -0
- package/dist/dev/pythonDevManager.js +85 -0
- package/dist/env.d.ts +19 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +112 -0
- package/dist/handlers/base-handler.d.ts +21 -0
- package/dist/handlers/base-handler.d.ts.map +1 -0
- package/dist/handlers/base-handler.js +38 -0
- package/dist/handlers/js-handler.d.ts +10 -0
- package/dist/handlers/js-handler.d.ts.map +1 -0
- package/dist/handlers/js-handler.js +87 -0
- package/dist/handlers/mjs-handler.d.ts +8 -0
- package/dist/handlers/mjs-handler.d.ts.map +1 -0
- package/dist/handlers/mjs-handler.js +44 -0
- package/dist/handlers/node-module-handler.d.ts +16 -0
- package/dist/handlers/node-module-handler.d.ts.map +1 -0
- package/dist/handlers/node-module-handler.js +267 -0
- package/dist/handlers/ts-handler.d.ts +11 -0
- package/dist/handlers/ts-handler.d.ts.map +1 -0
- package/dist/handlers/ts-handler.js +120 -0
- package/dist/handlers/ui-handler.d.ts +12 -0
- package/dist/handlers/ui-handler.d.ts.map +1 -0
- package/dist/handlers/ui-handler.js +182 -0
- package/dist/handlers/uix-handler.d.ts +12 -0
- package/dist/handlers/uix-handler.d.ts.map +1 -0
- package/dist/handlers/uix-handler.js +135 -0
- package/dist/hmr.d.ts +20 -0
- package/dist/hmr.d.ts.map +1 -0
- package/dist/hmr.js +265 -0
- package/dist/import-rewriter.d.ts +3 -0
- package/dist/import-rewriter.d.ts.map +1 -0
- package/dist/import-rewriter.js +351 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/middleware/hmr-routes.d.ts +12 -0
- package/dist/middleware/hmr-routes.d.ts.map +1 -0
- package/dist/middleware/hmr-routes.js +97 -0
- package/dist/middleware/middleware-setup.d.ts +23 -0
- package/dist/middleware/middleware-setup.d.ts.map +1 -0
- package/dist/middleware/middleware-setup.js +596 -0
- package/dist/middleware/static-files.d.ts +15 -0
- package/dist/middleware/static-files.d.ts.map +1 -0
- package/dist/middleware/static-files.js +585 -0
- package/dist/proxy/SwiteProxyError.d.ts +6 -0
- package/dist/proxy/SwiteProxyError.d.ts.map +1 -0
- package/dist/proxy/SwiteProxyError.js +9 -0
- package/dist/proxy/proxyToPython.d.ts +28 -0
- package/dist/proxy/proxyToPython.d.ts.map +1 -0
- package/dist/proxy/proxyToPython.js +66 -0
- package/dist/resolver/bare-import-resolver.d.ts +9 -0
- package/dist/resolver/bare-import-resolver.d.ts.map +1 -0
- package/dist/resolver/bare-import-resolver.js +363 -0
- package/dist/resolver/symlink-registry.d.ts +13 -0
- package/dist/resolver/symlink-registry.d.ts.map +1 -0
- package/dist/resolver/symlink-registry.js +98 -0
- package/dist/resolver/url-resolver.d.ts +11 -0
- package/dist/resolver/url-resolver.d.ts.map +1 -0
- package/dist/resolver/url-resolver.js +268 -0
- package/dist/resolver/workspace-package-resolver.d.ts +10 -0
- package/dist/resolver/workspace-package-resolver.d.ts.map +1 -0
- package/dist/resolver/workspace-package-resolver.js +185 -0
- package/dist/resolver.d.ts +17 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +191 -0
- package/dist/router/file-router.d.ts +19 -0
- package/dist/router/file-router.d.ts.map +1 -0
- package/dist/router/file-router.js +114 -0
- package/dist/server.d.ts +22 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +122 -0
- package/dist/utils/cdn-fallback.d.ts +14 -0
- package/dist/utils/cdn-fallback.d.ts.map +1 -0
- package/dist/utils/cdn-fallback.js +36 -0
- package/dist/utils/file-path-resolver.d.ts +9 -0
- package/dist/utils/file-path-resolver.d.ts.map +1 -0
- package/dist/utils/file-path-resolver.js +187 -0
- package/dist/utils/generate-import-map-cli.d.ts +3 -0
- package/dist/utils/generate-import-map-cli.d.ts.map +1 -0
- package/dist/utils/generate-import-map-cli.js +32 -0
- package/dist/utils/generate-import-map.d.ts +21 -0
- package/dist/utils/generate-import-map.d.ts.map +1 -0
- package/dist/utils/generate-import-map.js +119 -0
- package/dist/utils/package-finder.d.ts +24 -0
- package/dist/utils/package-finder.d.ts.map +1 -0
- package/dist/utils/package-finder.js +161 -0
- package/dist/utils/package-registry.d.ts +36 -0
- package/dist/utils/package-registry.d.ts.map +1 -0
- package/dist/utils/package-registry.js +159 -0
- package/dist/utils/workspace.d.ts +6 -0
- package/dist/utils/workspace.d.ts.map +1 -0
- package/dist/utils/workspace.js +65 -0
- package/docs/IMPORT_REWRITING.md +164 -0
- package/docs/IMPORT_REWRITING_TROUBLESHOOTING.md +139 -0
- package/docs/PATH_RESOLUTION_GUIDE.md +221 -0
- package/package.json +49 -0
- package/src/adapters/proxy/SwiteProxyError.ts +12 -0
- package/src/adapters/proxy/proxyToPython.ts +88 -0
- package/src/build-engine/builder.ts +960 -0
- package/src/cli.ts +109 -0
- package/src/config/config-loader.ts +46 -0
- package/src/config/config.ts +34 -0
- package/src/config/env.ts +98 -0
- package/src/dev-engine/handlers/base-handler.ts +68 -0
- package/src/dev-engine/handlers/js-handler.ts +134 -0
- package/src/dev-engine/handlers/mjs-handler.ts +65 -0
- package/src/dev-engine/handlers/node-module-handler.ts +339 -0
- package/src/dev-engine/handlers/ts-handler.ts +143 -0
- package/src/dev-engine/handlers/ui-handler.ts +105 -0
- package/src/dev-engine/handlers/uix-handler.ts +90 -0
- package/src/dev-engine/hmr/hmr-client-template.ts +122 -0
- package/src/dev-engine/hmr/hmr.ts +173 -0
- package/src/dev-engine/middleware/hmr-routes.ts +120 -0
- package/src/dev-engine/middleware/middleware-setup.ts +351 -0
- package/src/dev-engine/middleware/static-files.ts +728 -0
- package/src/dev-engine/pythonDevManager.ts +116 -0
- package/src/dev-engine/router/file-router.ts +164 -0
- package/src/dev-engine/server.ts +152 -0
- package/src/index.ts +26 -0
- package/src/internal/cache/compilation-cache.ts +182 -0
- package/src/internal/generate-import-map-cli.ts +40 -0
- package/src/internal/generate-import-map.ts +154 -0
- package/src/kernel/package-finder.ts +164 -0
- package/src/kernel/package-registry.ts +198 -0
- package/src/kernel/workspace.ts +62 -0
- package/src/resolution/bare-import-resolver.ts +400 -0
- package/src/resolution/cdn/cdn-fallback.ts +37 -0
- package/src/resolution/path/file-path-resolver.ts +190 -0
- package/src/resolution/path/path-fixup.ts +19 -0
- package/src/resolution/resolver.ts +198 -0
- package/src/resolution/rewriting/import-rewriter.ts +237 -0
- package/src/resolution/symlink-registry.ts +114 -0
- package/src/resolution/url-resolver.ts +231 -0
- package/src/resolution/workspace-package-resolver.ts +94 -0
- package/tsconfig.json +37 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
* SWITE - SWISS Development Server
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
import { promises as fs } from "node:fs";
|
|
7
|
+
import { UiCompiler } from "@kibologic/compiler";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import { rewriteImports } from "../import-rewriter.js";
|
|
10
|
+
import { compilationCache } from "../cache/compilation-cache.js";
|
|
11
|
+
import { BaseHandler, setDevHeaders, } from "./base-handler.js";
|
|
12
|
+
export class UIHandler extends BaseHandler {
|
|
13
|
+
constructor(context) {
|
|
14
|
+
super(context);
|
|
15
|
+
this.compiler = new UiCompiler();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Extract dependencies from compiled code (import paths)
|
|
19
|
+
*/
|
|
20
|
+
async getDependencies(compiled) {
|
|
21
|
+
const deps = [];
|
|
22
|
+
const importPattern = /(?:import|from|export).*['"]([^'"]+)['"]/g;
|
|
23
|
+
let match;
|
|
24
|
+
while ((match = importPattern.exec(compiled)) !== null) {
|
|
25
|
+
const specifier = match[1];
|
|
26
|
+
// Only track absolute paths and workspace paths (not relative)
|
|
27
|
+
if (specifier.startsWith("/") || specifier.startsWith("@")) {
|
|
28
|
+
try {
|
|
29
|
+
// Try to resolve to actual file path
|
|
30
|
+
const resolved = await this.context.resolver.resolve(specifier, "");
|
|
31
|
+
if (resolved && !resolved.startsWith("http")) {
|
|
32
|
+
deps.push(resolved);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Ignore resolution errors for dependency tracking
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return deps;
|
|
41
|
+
}
|
|
42
|
+
async handle(url, res) {
|
|
43
|
+
const filePath = await this.resolveFilePath(url);
|
|
44
|
+
console.log(chalk.blue(`[.ui] ${url} → ${filePath}`));
|
|
45
|
+
// Check if file exists
|
|
46
|
+
try {
|
|
47
|
+
await fs.access(filePath);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.error(chalk.red(`[.ui] File not found: ${filePath}`));
|
|
51
|
+
throw new Error(`File not found: ${url} (resolved to: ${filePath})`);
|
|
52
|
+
}
|
|
53
|
+
// Check cache first
|
|
54
|
+
let cached = await compilationCache.get(filePath, (compiled) => this.getDependencies(compiled));
|
|
55
|
+
if (cached) {
|
|
56
|
+
console.log(chalk.yellow(`[.ui] Cache hit for ${url}`));
|
|
57
|
+
console.log(chalk.yellow(`[.ui] Cached content starts with: ${cached.substring(0, 50).replace(/\n/g, "\\n")}`));
|
|
58
|
+
// CRITICAL: Strip /swiss-lib/ paths from cached code too
|
|
59
|
+
// Old cache entries may have /swiss-lib/ paths from before the fix
|
|
60
|
+
if (cached.includes("/swiss-lib/")) {
|
|
61
|
+
console.log(chalk.yellow(`[.ui] Fixing /swiss-lib/ paths in cached code for ${url}`));
|
|
62
|
+
cached = cached.replace(/\/swiss-lib\/packages\//g, "/swiss-packages/");
|
|
63
|
+
cached = cached.replace(/\/swiss-lib\//g, "/swiss-packages/");
|
|
64
|
+
// Also fix in import statements and URLs (preserve quote type)
|
|
65
|
+
cached = cached.replace(/(['"])\/swiss-lib\//g, '$1/swiss-packages/');
|
|
66
|
+
}
|
|
67
|
+
setDevHeaders(res);
|
|
68
|
+
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
|
|
69
|
+
res.setHeader("Content-Length", Buffer.byteLength(cached, "utf-8"));
|
|
70
|
+
res.end(cached, "utf-8");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Cache miss - compile
|
|
74
|
+
const source = await fs.readFile(filePath, "utf-8");
|
|
75
|
+
console.log(chalk.yellow(`[.ui] Compiling file: ${filePath}`));
|
|
76
|
+
console.log(chalk.yellow(`[.ui] Source starts with: ${source.substring(0, 50).replace(/\n/g, "\\n")}`));
|
|
77
|
+
let compiled = await this.compiler.compileAsync(source, filePath);
|
|
78
|
+
// Transform TypeScript output to plain JavaScript via esbuild
|
|
79
|
+
const esbuild = await import("esbuild");
|
|
80
|
+
const tsResult = await esbuild.transform(compiled, {
|
|
81
|
+
loader: "ts",
|
|
82
|
+
format: "esm",
|
|
83
|
+
target: "esnext",
|
|
84
|
+
sourcefile: filePath,
|
|
85
|
+
});
|
|
86
|
+
compiled = tsResult.code;
|
|
87
|
+
console.log(chalk.yellow(`[.ui] Compiled result starts with: ${compiled.substring(0, 50).replace(/\n/g, "\\n")}`));
|
|
88
|
+
// CRITICAL: Strip /swiss-lib/ paths from compiled output BEFORE import rewriting
|
|
89
|
+
// The compiler may output /swiss-lib/ paths directly in the code
|
|
90
|
+
if (compiled.includes("/swiss-lib/")) {
|
|
91
|
+
const beforeSwissLibFix = compiled;
|
|
92
|
+
// Fix all variations of /swiss-lib/ paths
|
|
93
|
+
compiled = compiled.replace(/\/swiss-lib\/packages\//g, "/swiss-packages/");
|
|
94
|
+
compiled = compiled.replace(/\/swiss-lib\//g, "/swiss-packages/");
|
|
95
|
+
// Also fix in import statements and URLs (preserve quote type)
|
|
96
|
+
compiled = compiled.replace(/(['"])\/swiss-lib\//g, '$1/swiss-packages/');
|
|
97
|
+
if (beforeSwissLibFix !== compiled) {
|
|
98
|
+
console.log(chalk.yellow(`[.ui] Fixed /swiss-lib/ paths in compiled output for ${url}`));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Strip CSS imports - they should be handled as static assets, not modules
|
|
102
|
+
// Use aggressive regex that matches all CSS import patterns
|
|
103
|
+
const beforeCssStrip = compiled;
|
|
104
|
+
// Pattern 1: Match standalone import statements for CSS (with or without semicolon, on their own line)
|
|
105
|
+
compiled = compiled.replace(/^\s*import\s+.*?\.css\s*['"];?\s*$/gm, '');
|
|
106
|
+
// Pattern 2: Match CSS imports anywhere in the code (more aggressive) - including without quotes
|
|
107
|
+
compiled = compiled.replace(/\bimport\s+.*?\.css\s*['"];?/g, '');
|
|
108
|
+
compiled = compiled.replace(/\bimport\s+['"].*?\.css['"];?/g, '');
|
|
109
|
+
// Pattern 3: Match dynamic imports for CSS
|
|
110
|
+
compiled = compiled.replace(/import\s*\(\s*['"].*?\.css['"]\s*\)/g, '');
|
|
111
|
+
// Pattern 4: Match CSS imports with different quote styles and whitespace
|
|
112
|
+
compiled = compiled.replace(/import\s+['"](.*?\.css)['"];?/g, '');
|
|
113
|
+
if (beforeCssStrip !== compiled) {
|
|
114
|
+
console.log(chalk.blue(`[.ui] Stripped CSS imports from ${url}`));
|
|
115
|
+
}
|
|
116
|
+
// Debug: Check for bare imports (only match actual bare imports, not paths)
|
|
117
|
+
// Pattern matches: import ... from "@package/name" or import("@package/name")
|
|
118
|
+
// But NOT: import ... from "/path/to/@package/name" (already a path)
|
|
119
|
+
const bareImportPattern = /(?:import|from|export).*['"](@[^'"]+\/[^'"]+)(?!\/)[^'"]*['"]/;
|
|
120
|
+
if (bareImportPattern.test(compiled)) {
|
|
121
|
+
console.log(chalk.yellow(`[.ui] WARNING: Compiled code contains bare imports: ${url}`));
|
|
122
|
+
}
|
|
123
|
+
const rewritten = await rewriteImports(compiled, filePath, this.context.resolver);
|
|
124
|
+
// FINAL SAFETY: Strip any remaining /swiss-lib/ paths after import rewriting
|
|
125
|
+
// This catches any paths that might have slipped through
|
|
126
|
+
let finalCode = rewritten;
|
|
127
|
+
if (finalCode.includes("/swiss-lib/")) {
|
|
128
|
+
const beforeFinal = finalCode;
|
|
129
|
+
const count = (beforeFinal.match(/\/swiss-lib\//g) || []).length;
|
|
130
|
+
console.log(chalk.red(`[.ui] 🚨 FINAL PASS TRIGGERED: Found ${count} /swiss-lib/ paths in ${url}`));
|
|
131
|
+
// Multiple passes to catch all variations
|
|
132
|
+
finalCode = finalCode.replace(/\/swiss-lib\/packages\//g, "/swiss-packages/");
|
|
133
|
+
finalCode = finalCode.replace(/\/swiss-lib\//g, "/swiss-packages/");
|
|
134
|
+
finalCode = finalCode.replace(/(['"])\/swiss-lib\//g, '$1/swiss-packages/');
|
|
135
|
+
if (beforeFinal !== finalCode) {
|
|
136
|
+
const afterCount = (finalCode.match(/\/swiss-lib\//g) || []).length;
|
|
137
|
+
console.log(chalk.green(`[.ui] ✅ FINAL PASS: Fixed ${count} /swiss-lib/ paths (${afterCount} remaining) in ${url}`));
|
|
138
|
+
if (afterCount > 0) {
|
|
139
|
+
console.log(chalk.red(`[.ui] ❌ STILL HAS /swiss-lib/: ${finalCode.substring(0, 500)}`));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
console.log(chalk.red(`[.ui] ❌ FINAL PASS FAILED: No changes made!`));
|
|
144
|
+
console.log(chalk.yellow(`[.ui] Sample: ${beforeFinal.substring(0, 200)}`));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Store in cache
|
|
148
|
+
await compilationCache.set(filePath, compiled, finalCode, (compiled) => this.getDependencies(compiled));
|
|
149
|
+
// Debug: Verify no bare imports remain after rewriting
|
|
150
|
+
if (bareImportPattern.test(finalCode)) {
|
|
151
|
+
console.log(chalk.red(`[.ui] ERROR: Bare imports still present after rewriting: ${url}`));
|
|
152
|
+
const matches = Array.from(rewritten.matchAll(/(?:import|from|export).*['"](@[^'"]+\/[^'"]+)(?!\/)[^'"]*['"]/g));
|
|
153
|
+
for (const match of matches.slice(0, 3)) {
|
|
154
|
+
console.log(chalk.red(`[.ui] Unresolved import: ${match[1]}`));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Set headers BEFORE sending response
|
|
158
|
+
setDevHeaders(res);
|
|
159
|
+
// Explicitly set Content-Type to ensure it's not overridden
|
|
160
|
+
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
|
|
161
|
+
// Log the Content-Type being sent
|
|
162
|
+
console.log(chalk.green(`[.ui] Sending response with Content-Type: ${res.getHeader("Content-Type")}`));
|
|
163
|
+
console.log(chalk.green(`[.ui] Response body length: ${rewritten.length} chars`));
|
|
164
|
+
console.log(chalk.green(`[.ui] Response body preview: ${rewritten.substring(0, 100)}...`));
|
|
165
|
+
// Send response - use res.end() to ensure headers are final
|
|
166
|
+
// Double-check Content-Type one more time
|
|
167
|
+
const finalContentType = res.getHeader("Content-Type");
|
|
168
|
+
if (finalContentType !== "application/javascript; charset=utf-8") {
|
|
169
|
+
console.error(chalk.red(`[.ui] ⚠️ Content-Type is wrong before send! Expected: application/javascript, Got: ${finalContentType}`));
|
|
170
|
+
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
|
|
171
|
+
}
|
|
172
|
+
console.log(chalk.green(`[.ui] Final Content-Type: ${res.getHeader("Content-Type")}`));
|
|
173
|
+
console.log(chalk.green(`[.ui] Sending ${finalCode.length} bytes of JavaScript`));
|
|
174
|
+
// Use res.end() instead of res.send() to have more control
|
|
175
|
+
res.setHeader("Content-Length", Buffer.byteLength(finalCode, "utf-8"));
|
|
176
|
+
res.end(finalCode, "utf-8");
|
|
177
|
+
// Ensure headersSent is set (should be set by res.end(), but verify)
|
|
178
|
+
if (!res.headersSent) {
|
|
179
|
+
console.error(chalk.red(`[.ui] ⚠️ res.end() called but headersSent is still false!`));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Response } from "express";
|
|
2
|
+
import { BaseHandler, type HandlerContext } from "./base-handler.js";
|
|
3
|
+
export declare class UIXHandler extends BaseHandler {
|
|
4
|
+
private compiler;
|
|
5
|
+
constructor(context: HandlerContext);
|
|
6
|
+
/**
|
|
7
|
+
* Extract dependencies from compiled code (import paths)
|
|
8
|
+
*/
|
|
9
|
+
private getDependencies;
|
|
10
|
+
handle(url: string, res: Response): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=uix-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uix-handler.d.ts","sourceRoot":"","sources":["../../src/handlers/uix-handler.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAMxC,OAAO,EACL,WAAW,EAEX,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAE3B,qBAAa,UAAW,SAAQ,WAAW;IACzC,OAAO,CAAC,QAAQ,CAAoB;gBAExB,OAAO,EAAE,cAAc;IAInC;;OAEG;YACW,eAAe;IAwBvB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CAuIxD"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Themba Mzumara
|
|
3
|
+
* SWITE - SWISS Development Server
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
import { promises as fs } from "node:fs";
|
|
7
|
+
import { UiCompiler } from "@kibologic/compiler";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import { rewriteImports } from "../import-rewriter.js";
|
|
10
|
+
import { compilationCache } from "../cache/compilation-cache.js";
|
|
11
|
+
import { BaseHandler, setDevHeaders, } from "./base-handler.js";
|
|
12
|
+
export class UIXHandler extends BaseHandler {
|
|
13
|
+
constructor(context) {
|
|
14
|
+
super(context);
|
|
15
|
+
this.compiler = new UiCompiler();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Extract dependencies from compiled code (import paths)
|
|
19
|
+
*/
|
|
20
|
+
async getDependencies(compiled) {
|
|
21
|
+
const deps = [];
|
|
22
|
+
const importPattern = /(?:import|from|export).*['"]([^'"]+)['"]/g;
|
|
23
|
+
let match;
|
|
24
|
+
while ((match = importPattern.exec(compiled)) !== null) {
|
|
25
|
+
const specifier = match[1];
|
|
26
|
+
// Only track absolute paths and workspace paths (not relative)
|
|
27
|
+
if (specifier.startsWith("/") || specifier.startsWith("@")) {
|
|
28
|
+
try {
|
|
29
|
+
const resolved = await this.context.resolver.resolve(specifier, "");
|
|
30
|
+
if (resolved && !resolved.startsWith("http")) {
|
|
31
|
+
deps.push(resolved);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Ignore resolution errors
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return deps;
|
|
40
|
+
}
|
|
41
|
+
async handle(url, res) {
|
|
42
|
+
const filePath = await this.resolveFilePath(url);
|
|
43
|
+
console.log(chalk.blue(`[.uix] ${url}`));
|
|
44
|
+
// Check cache first
|
|
45
|
+
let cached = await compilationCache.get(filePath, (compiled) => this.getDependencies(compiled));
|
|
46
|
+
if (cached) {
|
|
47
|
+
// CRITICAL: Strip /swiss-lib/ paths from cached code too
|
|
48
|
+
// Old cache entries may have /swiss-lib/ paths from before the fix
|
|
49
|
+
if (cached.includes("/swiss-lib/")) {
|
|
50
|
+
console.log(chalk.yellow(`[.uix] Fixing /swiss-lib/ paths in cached code for ${url}`));
|
|
51
|
+
cached = cached.replace(/\/swiss-lib\/packages\//g, "/swiss-packages/");
|
|
52
|
+
cached = cached.replace(/\/swiss-lib\//g, "/swiss-packages/");
|
|
53
|
+
// Also fix in import statements and URLs (preserve quote type)
|
|
54
|
+
cached = cached.replace(/(['"])\/swiss-lib\//g, '$1/swiss-packages/');
|
|
55
|
+
}
|
|
56
|
+
setDevHeaders(res);
|
|
57
|
+
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
|
|
58
|
+
res.send(cached);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Cache miss - compile
|
|
62
|
+
const source = await fs.readFile(filePath, "utf-8");
|
|
63
|
+
let compiled = await this.compiler.compileAsync(source, filePath);
|
|
64
|
+
// Transform TypeScript output to plain JavaScript via esbuild
|
|
65
|
+
const esbuild = await import("esbuild");
|
|
66
|
+
const tsResult = await esbuild.transform(compiled, {
|
|
67
|
+
loader: "ts",
|
|
68
|
+
format: "esm",
|
|
69
|
+
target: "esnext",
|
|
70
|
+
sourcefile: filePath,
|
|
71
|
+
});
|
|
72
|
+
compiled = tsResult.code;
|
|
73
|
+
// CRITICAL: Strip /swiss-lib/ paths from compiled output BEFORE import rewriting
|
|
74
|
+
// The compiler may output /swiss-lib/ paths directly in the code
|
|
75
|
+
if (compiled.includes("/swiss-lib/")) {
|
|
76
|
+
const beforeSwissLibFix = compiled;
|
|
77
|
+
// Fix all variations of /swiss-lib/ paths
|
|
78
|
+
compiled = compiled.replace(/\/swiss-lib\/packages\//g, "/swiss-packages/");
|
|
79
|
+
compiled = compiled.replace(/\/swiss-lib\//g, "/swiss-packages/");
|
|
80
|
+
// Also fix in import statements and URLs (preserve quote type)
|
|
81
|
+
compiled = compiled.replace(/(['"])\/swiss-lib\//g, '$1/swiss-packages/');
|
|
82
|
+
if (beforeSwissLibFix !== compiled) {
|
|
83
|
+
console.log(chalk.yellow(`[.uix] Fixed /swiss-lib/ paths in compiled output for ${url}`));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Strip CSS imports - they should be handled as static assets, not modules
|
|
87
|
+
// Use aggressive regex that matches all CSS import patterns
|
|
88
|
+
const beforeCssStrip = compiled;
|
|
89
|
+
// Pattern 1: Match standalone import statements for CSS (with or without semicolon, on their own line)
|
|
90
|
+
compiled = compiled.replace(/^\s*import\s+.*?\.css\s*['"];?\s*$/gm, '');
|
|
91
|
+
// Pattern 2: Match CSS imports anywhere in the code (more aggressive) - including without quotes
|
|
92
|
+
compiled = compiled.replace(/\bimport\s+.*?\.css\s*['"];?/g, '');
|
|
93
|
+
compiled = compiled.replace(/\bimport\s+['"].*?\.css['"];?/g, '');
|
|
94
|
+
// Pattern 3: Match dynamic imports for CSS
|
|
95
|
+
compiled = compiled.replace(/import\s*\(\s*['"].*?\.css['"]\s*\)/g, '');
|
|
96
|
+
// Pattern 4: Match CSS imports with different quote styles and whitespace
|
|
97
|
+
compiled = compiled.replace(/import\s+['"](.*?\.css)['"];?/g, '');
|
|
98
|
+
if (beforeCssStrip !== compiled) {
|
|
99
|
+
console.log(chalk.blue(`[.uix] Stripped CSS imports from ${url}`));
|
|
100
|
+
}
|
|
101
|
+
// Debug: Check for bare imports (only match actual bare imports, not paths)
|
|
102
|
+
// Pattern matches: import ... from "@package/name" or import("@package/name")
|
|
103
|
+
// But NOT: import ... from "/path/to/@package/name" (already a path)
|
|
104
|
+
const bareImportPattern = /(?:import|from|export).*['"](@[^'"]+\/[^'"]+)(?!\/)[^'"]*['"]/;
|
|
105
|
+
if (bareImportPattern.test(compiled)) {
|
|
106
|
+
console.log(chalk.yellow(`[.uix] WARNING: Compiled code contains bare imports: ${url}`));
|
|
107
|
+
}
|
|
108
|
+
const rewritten = await rewriteImports(compiled, filePath, this.context.resolver);
|
|
109
|
+
// FINAL SAFETY: Strip any remaining /swiss-lib/ paths after import rewriting
|
|
110
|
+
// This catches any paths that might have slipped through
|
|
111
|
+
let finalCode = rewritten;
|
|
112
|
+
if (finalCode.includes("/swiss-lib/")) {
|
|
113
|
+
const beforeFinal = finalCode;
|
|
114
|
+
finalCode = finalCode.replace(/\/swiss-lib\/packages\//g, "/swiss-packages/");
|
|
115
|
+
finalCode = finalCode.replace(/\/swiss-lib\//g, "/swiss-packages/");
|
|
116
|
+
finalCode = finalCode.replace(/(['"])\/swiss-lib\//g, '$1/swiss-packages/');
|
|
117
|
+
if (beforeFinal !== finalCode) {
|
|
118
|
+
console.log(chalk.red(`[.uix] ⚠️ FINAL PASS: Fixed ${beforeFinal.split('/swiss-lib/').length - 1} remaining /swiss-lib/ paths in ${url}`));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Store in cache
|
|
122
|
+
await compilationCache.set(filePath, compiled, finalCode, (compiled) => this.getDependencies(compiled));
|
|
123
|
+
// Debug: Verify no bare imports remain after rewriting
|
|
124
|
+
if (bareImportPattern.test(finalCode)) {
|
|
125
|
+
console.log(chalk.red(`[.uix] ERROR: Bare imports still present after rewriting: ${url}`));
|
|
126
|
+
const matches = Array.from(rewritten.matchAll(/(?:import|from|export).*['"](@[^'"]+\/[^'"]+)(?!\/)[^'"]*['"]/g));
|
|
127
|
+
for (const match of matches.slice(0, 3)) {
|
|
128
|
+
console.log(chalk.red(`[.uix] Unresolved import: ${match[1]}`));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
setDevHeaders(res);
|
|
132
|
+
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
|
|
133
|
+
res.send(finalCode);
|
|
134
|
+
}
|
|
135
|
+
}
|
package/dist/hmr.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare class HMREngine {
|
|
2
|
+
private root;
|
|
3
|
+
private wss;
|
|
4
|
+
private watcher?;
|
|
5
|
+
private clients;
|
|
6
|
+
private port;
|
|
7
|
+
constructor(root: string, hmrPort?: number);
|
|
8
|
+
initialize(): Promise<void>;
|
|
9
|
+
private checkPortAvailable;
|
|
10
|
+
private setupWebSocket;
|
|
11
|
+
private findFreePort;
|
|
12
|
+
getPort(): number;
|
|
13
|
+
start(): Promise<void>;
|
|
14
|
+
notifyChange(filePath: string): void;
|
|
15
|
+
getClientScript(): string;
|
|
16
|
+
private getUpdateType;
|
|
17
|
+
private broadcast;
|
|
18
|
+
stop(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=hmr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"AASA,qBAAa,SAAS;IAOlB,OAAO,CAAC,IAAI;IANd,OAAO,CAAC,GAAG,CAAmB;IAC9B,OAAO,CAAC,OAAO,CAAC,CAAqB;IACrC,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,IAAI,CAAS;gBAGX,IAAI,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM;IAOZ,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAiBnB,kBAAkB;IAUhC,OAAO,CAAC,cAAc;YAYR,YAAY;IAmB1B,OAAO,IAAI,MAAM;IAIX,KAAK;IA4BX,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAYpC,eAAe,IAAI,MAAM;IAyIzB,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,SAAS;IAcX,IAAI;CAIX"}
|
package/dist/hmr.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* HMR Engine for SWITE
|
|
3
|
+
*/
|
|
4
|
+
import * as chokidar from "chokidar";
|
|
5
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
6
|
+
import * as net from "net";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
export class HMREngine {
|
|
9
|
+
constructor(root, hmrPort) {
|
|
10
|
+
this.root = root;
|
|
11
|
+
this.clients = new Set();
|
|
12
|
+
this.port = hmrPort || 24678;
|
|
13
|
+
// WebSocketServer will be created in initialize() method
|
|
14
|
+
// This allows async port checking before server creation
|
|
15
|
+
}
|
|
16
|
+
async initialize() {
|
|
17
|
+
// Check if port is available, if not find a free one
|
|
18
|
+
const isAvailable = await this.checkPortAvailable(this.port);
|
|
19
|
+
if (!isAvailable) {
|
|
20
|
+
console.warn(chalk.yellow(`[HMR] Port ${this.port} is in use, finding free port...`));
|
|
21
|
+
this.port = await this.findFreePort();
|
|
22
|
+
}
|
|
23
|
+
this.wss = new WebSocketServer({ port: this.port });
|
|
24
|
+
this.setupWebSocket();
|
|
25
|
+
console.log(chalk.green(`[HMR] WebSocket server started on port ${this.port}`));
|
|
26
|
+
}
|
|
27
|
+
async checkPortAvailable(port) {
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
const server = net.createServer();
|
|
30
|
+
server.listen(port, () => {
|
|
31
|
+
server.close(() => resolve(true));
|
|
32
|
+
});
|
|
33
|
+
server.on("error", () => resolve(false));
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
setupWebSocket() {
|
|
37
|
+
this.wss.on("connection", (ws) => {
|
|
38
|
+
this.clients.add(ws);
|
|
39
|
+
console.log(chalk.green("[HMR] Client connected"));
|
|
40
|
+
ws.on("close", () => {
|
|
41
|
+
this.clients.delete(ws);
|
|
42
|
+
console.log(chalk.gray("[HMR] Client disconnected"));
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async findFreePort() {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
const server = net.createServer();
|
|
49
|
+
server.listen(0, () => {
|
|
50
|
+
const address = server.address();
|
|
51
|
+
const port = address && typeof address === "object" ? address.port : null;
|
|
52
|
+
server.close(() => {
|
|
53
|
+
if (port) {
|
|
54
|
+
resolve(port);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
reject(new Error("Could not find free port"));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
server.on("error", reject);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
getPort() {
|
|
65
|
+
return this.port;
|
|
66
|
+
}
|
|
67
|
+
async start() {
|
|
68
|
+
this.watcher = chokidar.watch(this.root, {
|
|
69
|
+
ignored: ["**/node_modules/**", "**/.git/**", "**/dist/**"],
|
|
70
|
+
ignoreInitial: true,
|
|
71
|
+
awaitWriteFinish: {
|
|
72
|
+
stabilityThreshold: 100,
|
|
73
|
+
pollInterval: 100,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
this.watcher.on("change", (filePath) => {
|
|
77
|
+
console.log(chalk.yellow(`[HMR] ${filePath} changed`));
|
|
78
|
+
// Determine file type and update type
|
|
79
|
+
const fileExt = filePath.split(".").pop()?.toLowerCase();
|
|
80
|
+
const updateType = this.getUpdateType(fileExt, filePath);
|
|
81
|
+
this.broadcast({
|
|
82
|
+
type: "update",
|
|
83
|
+
path: filePath,
|
|
84
|
+
updateType,
|
|
85
|
+
timestamp: Date.now(),
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
console.log(chalk.green("[HMR] Watching for file changes..."));
|
|
89
|
+
}
|
|
90
|
+
notifyChange(filePath) {
|
|
91
|
+
const fileExt = filePath.split(".").pop()?.toLowerCase();
|
|
92
|
+
const updateType = this.getUpdateType(fileExt, filePath);
|
|
93
|
+
this.broadcast({
|
|
94
|
+
type: "update",
|
|
95
|
+
path: filePath,
|
|
96
|
+
updateType,
|
|
97
|
+
timestamp: Date.now(),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
getClientScript() {
|
|
101
|
+
return `
|
|
102
|
+
// SWITE HMR Client
|
|
103
|
+
console.log('[SWITE] HMR enabled');
|
|
104
|
+
|
|
105
|
+
const socket = new WebSocket('ws://localhost:${this.port}');
|
|
106
|
+
const moduleGraph = new Map<string, Set<string>>();
|
|
107
|
+
const hotModules = new Map<string, any>();
|
|
108
|
+
|
|
109
|
+
socket.addEventListener('open', () => {
|
|
110
|
+
console.log('[SWITE] HMR connected');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
socket.addEventListener('message', (event) => {
|
|
114
|
+
const data = JSON.parse(event.data);
|
|
115
|
+
|
|
116
|
+
if (data.type === 'update') {
|
|
117
|
+
console.log('[SWITE] Processing update:', data.path, 'Type:', data.updateType);
|
|
118
|
+
|
|
119
|
+
if (data.updateType === 'style') {
|
|
120
|
+
// Hot swap CSS
|
|
121
|
+
updateStyles();
|
|
122
|
+
console.log('[SWITE] Styles hot updated');
|
|
123
|
+
} else if (data.updateType === 'hot') {
|
|
124
|
+
// Hot reload component
|
|
125
|
+
const moduleName = extractModuleName(data.path);
|
|
126
|
+
|
|
127
|
+
if (moduleName && hotModules.has(moduleName)) {
|
|
128
|
+
const oldModule = hotModules.get(moduleName);
|
|
129
|
+
try {
|
|
130
|
+
invalidateModule(moduleName);
|
|
131
|
+
invalidateDependents(moduleName);
|
|
132
|
+
|
|
133
|
+
const updatedModule = await import(data.path + '?t=' + Date.now());
|
|
134
|
+
hotModules.set(moduleName, updatedModule);
|
|
135
|
+
|
|
136
|
+
updateComponent(moduleName, updatedModule);
|
|
137
|
+
console.log('[SWITE] Component hot updated:', moduleName);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error('[SWITE] Hot update failed:', error);
|
|
140
|
+
window.location.reload();
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
console.log('[SWITE] New component detected, reloading page');
|
|
144
|
+
window.location.reload();
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
// Full reload for everything else
|
|
148
|
+
console.log('[SWITE] Full page reload required');
|
|
149
|
+
window.location.reload();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
function updateStyles() {
|
|
155
|
+
// Find all style and link tags
|
|
156
|
+
const styles = document.querySelectorAll('link[rel="stylesheet"], style');
|
|
157
|
+
styles.forEach(style => {
|
|
158
|
+
if (style.tagName === 'LINK' && style.getAttribute('href')) {
|
|
159
|
+
const href = style.getAttribute('href');
|
|
160
|
+
if (href && !href.includes('?t=')) {
|
|
161
|
+
// Add timestamp to force reload
|
|
162
|
+
style.setAttribute('href', href + '?t=' + Date.now());
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function extractModuleName(path: string): string | null {
|
|
169
|
+
// Extract module name from file path
|
|
170
|
+
const parts = path.split('/');
|
|
171
|
+
const fileName = parts[parts.length - 1];
|
|
172
|
+
|
|
173
|
+
if (fileName) {
|
|
174
|
+
const nameWithoutExt = fileName.replace(/.[^.]+$/, "");
|
|
175
|
+
return nameWithoutExt;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function invalidateModule(moduleName: string) {
|
|
182
|
+
// Clear module from cache
|
|
183
|
+
if (typeof window !== 'undefined' && (window as any).__swiss_modules__) {
|
|
184
|
+
delete (window as any).__swiss_modules__[moduleName];
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function invalidateDependents(moduleName: string) {
|
|
189
|
+
const dependents = moduleGraph.get(moduleName);
|
|
190
|
+
if (dependents) {
|
|
191
|
+
for (const dependent of dependents) {
|
|
192
|
+
invalidateModule(dependent);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function updateComponent(moduleName: string, newModule: any) {
|
|
198
|
+
// Find and update component instances
|
|
199
|
+
if (typeof window !== 'undefined' && (window as any).__swiss_instances__) {
|
|
200
|
+
const instances = (window as any).__swiss_instances__[moduleName];
|
|
201
|
+
if (instances && Array.isArray(instances)) {
|
|
202
|
+
instances.forEach(instance => {
|
|
203
|
+
// Update component state if it has update method
|
|
204
|
+
if (instance && typeof instance.update === 'function') {
|
|
205
|
+
instance.update(newModule.default || newModule);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
socket.addEventListener('close', () => {
|
|
213
|
+
console.log('[SWITE] HMR disconnected');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
socket.addEventListener('error', (error) => {
|
|
217
|
+
console.error('[SWITE] HMR error:', error);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Register module for hot reloading
|
|
221
|
+
if (typeof window !== 'undefined') {
|
|
222
|
+
(window as any).__swiss_modules__ = (window as any).__swiss_modules__ || {};
|
|
223
|
+
(window as any).__swiss_instances__ = (window as any).__swiss_instances__ || {};
|
|
224
|
+
|
|
225
|
+
// Auto-register current module
|
|
226
|
+
const currentScript = document.currentScript;
|
|
227
|
+
if (currentScript && currentScript.src) {
|
|
228
|
+
const moduleName = extractModuleName(currentScript.src);
|
|
229
|
+
if (moduleName) {
|
|
230
|
+
(window as any).__swiss_modules__[moduleName] = true;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
`;
|
|
235
|
+
}
|
|
236
|
+
getUpdateType(fileExt, filePath) {
|
|
237
|
+
if (!fileExt || !filePath)
|
|
238
|
+
return "reload";
|
|
239
|
+
// CSS files can be hot-swapped
|
|
240
|
+
if (fileExt === "css" || fileExt === "scss" || fileExt === "sass") {
|
|
241
|
+
return "style";
|
|
242
|
+
}
|
|
243
|
+
// Component files can be hot-reloaded
|
|
244
|
+
if (["js", "ts", "jsx", "tsx"].includes(fileExt)) {
|
|
245
|
+
// Check if it's in components directory
|
|
246
|
+
if (filePath.includes("/components/") || filePath.includes("/pages/")) {
|
|
247
|
+
return "hot";
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Everything else requires full reload
|
|
251
|
+
return "reload";
|
|
252
|
+
}
|
|
253
|
+
broadcast(message) {
|
|
254
|
+
const payload = JSON.stringify(message);
|
|
255
|
+
this.clients.forEach((client) => {
|
|
256
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
257
|
+
client.send(payload);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
async stop() {
|
|
262
|
+
await this.watcher?.close();
|
|
263
|
+
this.wss.close();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-rewriter.d.ts","sourceRoot":"","sources":["../src/import-rewriter.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAM/C,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,CAAC,CAkbjB"}
|