flet-box 0.0.0-stage ā 1.0.2
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/LICENSE +21 -0
- package/README.md +190 -2
- package/bin/cli.js +394 -0
- package/bin/commands/buildAndroid.js +200 -0
- package/bin/commands/buildIOS.js +237 -0
- package/bin/commands/create.js +406 -0
- package/bin/commands/createBundle.js +375 -0
- package/bin/commands/createComponent.js +115 -0
- package/bin/commands/createScreen.js +161 -0
- package/bin/commands/killServer.js +107 -0
- package/bin/commands/package.js +131 -0
- package/bin/commands/release.js +189 -0
- package/bin/commands/runBundle.js +62 -0
- package/bin/commands/runCloudflare.js +89 -0
- package/bin/commands/runNative.js +109 -0
- package/bin/commands/runServer.js +390 -0
- package/bin/ota/loader.js +361 -0
- package/bin/utils/bundleConfig.js +178 -0
- package/bin/utils/capacitor.js +137 -0
- package/bin/utils/colors.js +215 -0
- package/bin/utils/helpers.js +80 -0
- package/bin/utils/spinner.js +96 -0
- package/bin/utils/templates.js +1824 -0
- package/bin/utils/version.js +108 -0
- package/package.json +58 -4
- package/src/animations/AnimatedBox.js +607 -0
- package/src/animations/AnimatedText.js +105 -0
- package/src/animations/MatrixRain.js +220 -0
- package/src/animations/ParallaxBox.js +369 -0
- package/src/animations/animate.js +332 -0
- package/src/animations/index.js +25 -0
- package/src/app.js +41 -0
- package/src/assets/icon-1024-maskable.png +0 -0
- package/src/assets/icon-192-maskable.png +0 -0
- package/src/assets/icon-192.png +0 -0
- package/src/assets/icon-512-maskable.png +0 -0
- package/src/assets/icon-512.png +0 -0
- package/src/assets/logo.png +0 -0
- package/src/core/App.js +44 -0
- package/src/core/hmr-client.js +142 -0
- package/src/core/index.js +34 -0
- package/src/core/pwa.js +100 -0
- package/src/core/runApp.js +430 -0
- package/src/create-index.sh +237 -0
- package/src/fonts/MaterialIcons-Regular.woff2 +0 -0
- package/src/fonts/icons.css +23 -0
- package/src/index.d.ts +1930 -0
- package/src/index.js +288 -0
- package/src/navigations/AdaptiveScaffold.js +149 -0
- package/src/navigations/AppBar.js +331 -0
- package/src/navigations/BottomNavigation.js +298 -0
- package/src/navigations/CollapsibleSideBar.js +139 -0
- package/src/navigations/Drawer.js +333 -0
- package/src/navigations/DrawerItem.js +260 -0
- package/src/navigations/Router.js +475 -0
- package/src/navigations/Scaffold.js +537 -0
- package/src/navigations/Tabs.js +384 -0
- package/src/navigations/index.js +57 -0
- package/src/services/RamStore.js +213 -0
- package/src/services/Session.js +310 -0
- package/src/services/Storage.js +309 -0
- package/src/services/http.js +350 -0
- package/src/services/index.js +57 -0
- package/src/tools/animation.js +42 -0
- package/src/tools/array.js +95 -0
- package/src/tools/border.js +28 -0
- package/src/tools/clipboard.js +104 -0
- package/src/tools/color.js +329 -0
- package/src/tools/createList.js +215 -0
- package/src/tools/delay.js +55 -0
- package/src/tools/device.js +138 -0
- package/src/tools/dict.js +297 -0
- package/src/tools/dimensions.js +118 -0
- package/src/tools/filter.js +41 -0
- package/src/tools/flex.js +26 -0
- package/src/tools/gradient.js +34 -0
- package/src/tools/grid.js +57 -0
- package/src/tools/index.js +38 -0
- package/src/tools/mapList.js +107 -0
- package/src/tools/margin.js +93 -0
- package/src/tools/memo.js +60 -0
- package/src/tools/os.js +104 -0
- package/src/tools/padding.js +93 -0
- package/src/tools/print.js +69 -0
- package/src/tools/random.js +299 -0
- package/src/tools/ref.js +70 -0
- package/src/tools/rgba.js +4 -0
- package/src/tools/shadow.js +23 -0
- package/src/tools/string.js +85 -0
- package/src/tools/time.js +674 -0
- package/src/tools/transform.js +68 -0
- package/src/tools/transition.js +48 -0
- package/src/tools/useState.js +268 -0
- package/src/tools/uuid.js +42 -0
- package/src/utils/TextInputValidator.js +256 -0
- package/src/utils/composeUpdate.js +65 -0
- package/src/utils/getWidgetProps.js +35 -0
- package/src/utils/index.js +76 -0
- package/src/utils/markdownParser.js +368 -0
- package/src/utils/mediaTime.js +79 -0
- package/src/utils/navigation.js +263 -0
- package/src/utils/scriptInjector.js +73 -0
- package/src/utils/stackPosition.js +31 -0
- package/src/utils/stopWebRefresh.js +77 -0
- package/src/utils/styleInjector.js +38 -0
- package/src/utils/syntaxHighlight.js +339 -0
- package/src/utils/themes.js +347 -0
- package/src/utils/units.js +87 -0
- package/src/utils/visualEffects.js +169 -0
- package/src/widget-factory/addChildren.js +114 -0
- package/src/widget-factory/assignProps.js +256 -0
- package/src/widget-factory/createWidget.js +28 -0
- package/src/widget-factory/effects.js +132 -0
- package/src/widget-factory/index.js +7 -0
- package/src/widget-factory/lifecycle.js +210 -0
- package/src/widget-factory/makeParentable.js +89 -0
- package/src/widget-factory/pixTool.js +40 -0
- package/src/widget-factory/processProps.js +122 -0
- package/src/widget-factory/reactivity.js +98 -0
- package/src/widget-factory/remTool.js +34 -0
- package/src/widget-factory/stackPosition.js +87 -0
- package/src/widget-factory/stringTool.js +26 -0
- package/src/widget-factory/tools.js +249 -0
- package/src/widget-factory/translateProps.js +1049 -0
- package/src/widget-factory/warnings.js +284 -0
- package/src/widget-factory/widgetFactory.js +200 -0
- package/src/widgets/Accordion.js +562 -0
- package/src/widgets/AdMob.js +315 -0
- package/src/widgets/AdSense.js +268 -0
- package/src/widgets/AlertDialog.js +162 -0
- package/src/widgets/Audio.js +311 -0
- package/src/widgets/Avatar.js +210 -0
- package/src/widgets/Badge.js +189 -0
- package/src/widgets/BottomSheet.js +287 -0
- package/src/widgets/Button.js +247 -0
- package/src/widgets/Card.js +33 -0
- package/src/widgets/Carousel.js +301 -0
- package/src/widgets/Chart.js +632 -0
- package/src/widgets/Checkbox.js +106 -0
- package/src/widgets/Chip.js +153 -0
- package/src/widgets/CircularBar.js +352 -0
- package/src/widgets/CircularChart.js +549 -0
- package/src/widgets/CodeViewer.js +236 -0
- package/src/widgets/Column.js +29 -0
- package/src/widgets/Container.js +31 -0
- package/src/widgets/DataTable.js +257 -0
- package/src/widgets/Divider.js +93 -0
- package/src/widgets/DraggBox.js +115 -0
- package/src/widgets/Dropdown.js +505 -0
- package/src/widgets/DroppBox.js +180 -0
- package/src/widgets/FloatingActionButton.js +91 -0
- package/src/widgets/GridView.js +20 -0
- package/src/widgets/Icon.js +33 -0
- package/src/widgets/Image.js +15 -0
- package/src/widgets/Input.js +565 -0
- package/src/widgets/Inspector.js +254 -0
- package/src/widgets/InstallButton.js +110 -0
- package/src/widgets/ListTile.js +224 -0
- package/src/widgets/ListView.js +503 -0
- package/src/widgets/Markdown.js +1236 -0
- package/src/widgets/Modal.js +496 -0
- package/src/widgets/Pagination.js +195 -0
- package/src/widgets/ProgressBar.js +178 -0
- package/src/widgets/QRCode.js +467 -0
- package/src/widgets/Radio.js +132 -0
- package/src/widgets/Rating.js +261 -0
- package/src/widgets/Row.js +29 -0
- package/src/widgets/Skeleton.js +243 -0
- package/src/widgets/Slider.js +527 -0
- package/src/widgets/SnackBar.js +227 -0
- package/src/widgets/Stack.js +29 -0
- package/src/widgets/Stepper.js +296 -0
- package/src/widgets/Switch.js +102 -0
- package/src/widgets/Text.js +98 -0
- package/src/widgets/Tooltip.js +267 -0
- package/src/widgets/TreeView.js +370 -0
- package/src/widgets/Video.js +92 -0
- package/src/widgets/index.js +52 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
// bin/commands/runServer.js
|
|
2
|
+
import http from "http";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import readline from "readline";
|
|
6
|
+
import { WebSocketServer } from "ws";
|
|
7
|
+
import chokidar from "chokidar";
|
|
8
|
+
import { c, banner, rainbow, dot, typewriter } from "../utils/colors.js";
|
|
9
|
+
import { spinner } from "../utils/spinner.js";
|
|
10
|
+
|
|
11
|
+
// Ask for a port interactively (Enter = use the default)
|
|
12
|
+
const askPort = (message, fallback) =>
|
|
13
|
+
new Promise((resolve) => {
|
|
14
|
+
const rl = readline.createInterface({
|
|
15
|
+
input: process.stdin,
|
|
16
|
+
output: process.stdout,
|
|
17
|
+
});
|
|
18
|
+
rl.question(message, (answer) => {
|
|
19
|
+
rl.close();
|
|
20
|
+
const trimmed = (answer || "").trim();
|
|
21
|
+
if (trimmed === "") {
|
|
22
|
+
resolve(fallback);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const n = Number(trimmed);
|
|
26
|
+
if (Number.isInteger(n) && n > 0 && n < 65536) {
|
|
27
|
+
resolve(n);
|
|
28
|
+
} else {
|
|
29
|
+
console.warn(c("yellow", `ā ļø Invalid port "${trimmed}", using ${fallback}`));
|
|
30
|
+
resolve(fallback);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Detect if we are in the framework root directory
|
|
36
|
+
const isFrameworkRoot = (projectRoot) => {
|
|
37
|
+
const packageJson = path.join(projectRoot, "package.json");
|
|
38
|
+
if (fs.existsSync(packageJson)) {
|
|
39
|
+
try {
|
|
40
|
+
const pkg = JSON.parse(fs.readFileSync(packageJson, "utf-8"));
|
|
41
|
+
return pkg.name === "flet-box";
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const runDevServer = async (options = {}) => {
|
|
50
|
+
const {
|
|
51
|
+
spaMode = false,
|
|
52
|
+
hotReload = true,
|
|
53
|
+
port = process.env.PORT || 8000,
|
|
54
|
+
logRequests = process.env.LOG_REQUESTS !== "false",
|
|
55
|
+
} = options;
|
|
56
|
+
|
|
57
|
+
const projectRoot = process.cwd();
|
|
58
|
+
const clients = new Set();
|
|
59
|
+
let watcher = null;
|
|
60
|
+
let PORT = Number(port) || 8000;
|
|
61
|
+
|
|
62
|
+
console.log(banner("š” FletBox Server", spaMode ? "SPA + Hot Reload" : "Static"));
|
|
63
|
+
|
|
64
|
+
// Interactive port: if no --port was passed and there is a TTY, ask
|
|
65
|
+
if (options.askPort && process.stdin.isTTY) {
|
|
66
|
+
PORT = await askPort(
|
|
67
|
+
c("cyan", `š Server port [${PORT}]: `),
|
|
68
|
+
PORT,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Detectar si estamos en el framework
|
|
73
|
+
const isFramework = isFrameworkRoot(projectRoot);
|
|
74
|
+
|
|
75
|
+
const mimeTypes = {
|
|
76
|
+
".html": "text/html",
|
|
77
|
+
".css": "text/css",
|
|
78
|
+
".js": "text/javascript",
|
|
79
|
+
".mjs": "text/javascript",
|
|
80
|
+
".json": "application/json",
|
|
81
|
+
".png": "image/png",
|
|
82
|
+
".jpg": "image/jpeg",
|
|
83
|
+
".gif": "image/gif",
|
|
84
|
+
".svg": "image/svg+xml",
|
|
85
|
+
".ico": "image/x-icon",
|
|
86
|
+
".woff": "font/woff",
|
|
87
|
+
".woff2": "font/woff2",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const broadcast = (message) => {
|
|
91
|
+
const msg = typeof message === "string" ? message : JSON.stringify(message);
|
|
92
|
+
clients.forEach((client) => {
|
|
93
|
+
if (client.readyState === WebSocketServer.OPEN) {
|
|
94
|
+
client.send(msg);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const extractExports = (content) => {
|
|
100
|
+
const exports = [];
|
|
101
|
+
const patterns = [
|
|
102
|
+
/export\s+(?:const|let|var|function|class)\s+(\w+)/g,
|
|
103
|
+
/export\s+default\s+(?:function\s+)?(\w+)/g,
|
|
104
|
+
/export\s+\{\s*([^}]+)\s*\}/g,
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
for (const pattern of patterns) {
|
|
108
|
+
let match;
|
|
109
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
110
|
+
if (match[1]) {
|
|
111
|
+
if (match[1].includes(",")) {
|
|
112
|
+
const names = match[1].split(",").map((s) => s.trim());
|
|
113
|
+
exports.push(...names);
|
|
114
|
+
} else {
|
|
115
|
+
exports.push(match[1]);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return [...new Set(exports)];
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const getChangeType = (filePath, oldContent, newContent) => {
|
|
124
|
+
if (filePath.endsWith(".css")) return "css";
|
|
125
|
+
if (filePath.endsWith(".html")) return "full-reload";
|
|
126
|
+
|
|
127
|
+
if (oldContent && newContent) {
|
|
128
|
+
const oldExports = extractExports(oldContent);
|
|
129
|
+
const newExports = extractExports(newContent);
|
|
130
|
+
|
|
131
|
+
if (JSON.stringify(oldExports) !== JSON.stringify(newExports)) {
|
|
132
|
+
return "exports-changed";
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return "code-changed";
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const server = http.createServer((req, res) => {
|
|
139
|
+
try {
|
|
140
|
+
let url = req.url;
|
|
141
|
+
try {
|
|
142
|
+
url = decodeURIComponent(url);
|
|
143
|
+
} catch (e) {
|
|
144
|
+
url = req.url;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const safePath = path.normalize(url).replace(/^(\.\.[\/\\])+/, "");
|
|
148
|
+
let filePath = path.join(
|
|
149
|
+
projectRoot,
|
|
150
|
+
safePath === "/" ? "index.html" : safePath,
|
|
151
|
+
);
|
|
152
|
+
let ext = path.extname(filePath).toLowerCase();
|
|
153
|
+
|
|
154
|
+
fs.access(filePath, fs.constants.F_OK, (err) => {
|
|
155
|
+
// SPA fallback: if the file doesn't exist and has no extension, serve index.html
|
|
156
|
+
if (err && spaMode && (!ext || ext === "")) {
|
|
157
|
+
filePath = path.join(projectRoot, "index.html");
|
|
158
|
+
ext = ".html";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
fs.readFile(filePath, (err2, content) => {
|
|
162
|
+
if (err2) {
|
|
163
|
+
if (logRequests) {
|
|
164
|
+
console.log(`${c("red", "404")} ${req.method} ${url}`);
|
|
165
|
+
}
|
|
166
|
+
res.writeHead(404);
|
|
167
|
+
res.end("404 - File Not Found");
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const contentType = mimeTypes[ext] || "text/plain";
|
|
172
|
+
|
|
173
|
+
if (logRequests) {
|
|
174
|
+
const sizeKB = (content.length / 1024).toFixed(1);
|
|
175
|
+
console.log(
|
|
176
|
+
`${c("green", "200")} ${req.method} ${url} ā ${contentType} ${sizeKB}KB`,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Inyectar HMR solo en HTML
|
|
181
|
+
if (ext === ".html" && hotReload) {
|
|
182
|
+
let html = content.toString("utf-8");
|
|
183
|
+
|
|
184
|
+
let hmrScript;
|
|
185
|
+
if (isFramework) {
|
|
186
|
+
// ā
Estamos en el framework: usar ruta directa
|
|
187
|
+
hmrScript = `
|
|
188
|
+
<script type="module">
|
|
189
|
+
import { initHMR } from '/src/core/hmr-client.js';
|
|
190
|
+
initHMR();
|
|
191
|
+
</script>
|
|
192
|
+
`;
|
|
193
|
+
if (logRequests)
|
|
194
|
+
console.log(
|
|
195
|
+
c("blue", "š§ Framework mode: using direct HMR import"),
|
|
196
|
+
);
|
|
197
|
+
} else {
|
|
198
|
+
// ā
Normal project: import from the npm module
|
|
199
|
+
hmrScript = `
|
|
200
|
+
<script type="module">
|
|
201
|
+
import('flet-box').then(({ initHMR }) => {
|
|
202
|
+
if (typeof initHMR === 'function') {
|
|
203
|
+
initHMR();
|
|
204
|
+
}
|
|
205
|
+
}).catch(e => console.warn('HMR not available:', e));
|
|
206
|
+
</script>
|
|
207
|
+
`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (!html.includes("initHMR")) {
|
|
211
|
+
if (html.includes("</body>")) {
|
|
212
|
+
html = html.replace("</body>", `${hmrScript}</body>`);
|
|
213
|
+
} else {
|
|
214
|
+
html += hmrScript;
|
|
215
|
+
}
|
|
216
|
+
content = html;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
221
|
+
res.end(content);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.error("Server error:", error);
|
|
226
|
+
if (!res.headersSent) {
|
|
227
|
+
res.writeHead(500);
|
|
228
|
+
res.end("Internal Server Error");
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
let serverErrorHandled = false;
|
|
234
|
+
|
|
235
|
+
const stateDot = (on, color) => (on ? dot(color) : c("gray", "ā"));
|
|
236
|
+
let boot = null;
|
|
237
|
+
|
|
238
|
+
const printReady = async (p) => {
|
|
239
|
+
if (boot) boot.succeed(`Dev server running on port ${p}`);
|
|
240
|
+
console.log("");
|
|
241
|
+
await typewriter(`ā http://localhost:${p}`, { ms: 10, color: "#34d399" });
|
|
242
|
+
console.log(
|
|
243
|
+
` ${stateDot(spaMode, "#22d3ee")} ${c("gray", "SPA")} ${c("brightGreen", spaMode ? "ON " : "OFF")}`,
|
|
244
|
+
);
|
|
245
|
+
console.log(
|
|
246
|
+
` ${stateDot(hotReload, "#a855f7")} ${c("gray", "Hot Reload")} ${c("brightGreen", hotReload ? "ON " : "OFF")}`,
|
|
247
|
+
);
|
|
248
|
+
console.log(
|
|
249
|
+
` ${stateDot(logRequests, "#fbbf24")} ${c("gray", "Logging")} ${c("brightGreen", logRequests ? "ON " : "OFF")}`,
|
|
250
|
+
);
|
|
251
|
+
if (isFramework) {
|
|
252
|
+
console.log(`\n ${rainbow("š§ Framework mode ā direct HMR imports")}`);
|
|
253
|
+
}
|
|
254
|
+
console.log(c("gray", "\n Press Ctrl+C to stop\n"));
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const onServerError = async (err) => {
|
|
258
|
+
if (serverErrorHandled) return;
|
|
259
|
+
serverErrorHandled = true;
|
|
260
|
+
|
|
261
|
+
if (err && err.code === "EADDRINUSE") {
|
|
262
|
+
if (options.askPort && process.stdin.isTTY) {
|
|
263
|
+
if (boot) boot.stop();
|
|
264
|
+
boot = null;
|
|
265
|
+
const next = Number(PORT) + 1;
|
|
266
|
+
const newPort = await askPort(
|
|
267
|
+
c("yellow", `ā ļø Port ${PORT} in use. New port [${next}]: `),
|
|
268
|
+
next,
|
|
269
|
+
);
|
|
270
|
+
if (newPort !== PORT) {
|
|
271
|
+
console.log(c("blue", `š Retrying on port ${newPort}...`));
|
|
272
|
+
PORT = newPort;
|
|
273
|
+
serverErrorHandled = false;
|
|
274
|
+
server.listen(PORT, () => printReady(PORT));
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
console.error(c("red", `ā Port ${PORT} is already in use`));
|
|
279
|
+
console.log(
|
|
280
|
+
c(
|
|
281
|
+
"gray",
|
|
282
|
+
` ā Try another port: flet-box run-spa --port ${Number(PORT) + 1}`,
|
|
283
|
+
),
|
|
284
|
+
);
|
|
285
|
+
console.log(
|
|
286
|
+
c("gray", ` ā Or stop the process: lsof -ti tcp:${PORT} | xargs kill`),
|
|
287
|
+
);
|
|
288
|
+
} else {
|
|
289
|
+
if (boot) boot.fail("Server startup failed");
|
|
290
|
+
console.error(c("red", "ā Server error:"), err && err.message);
|
|
291
|
+
}
|
|
292
|
+
process.exit(1);
|
|
293
|
+
};
|
|
294
|
+
server.on("error", onServerError);
|
|
295
|
+
|
|
296
|
+
// WebSocket para HMR
|
|
297
|
+
if (hotReload) {
|
|
298
|
+
const wss = new WebSocketServer({ server });
|
|
299
|
+
wss.on("error", onServerError);
|
|
300
|
+
|
|
301
|
+
wss.on("connection", (ws) => {
|
|
302
|
+
clients.add(ws);
|
|
303
|
+
console.log(c("green", "š HMR client connected"));
|
|
304
|
+
ws.send(JSON.stringify({ type: "request-modules" }));
|
|
305
|
+
ws.on("message", (data) => {
|
|
306
|
+
try {
|
|
307
|
+
const message = JSON.parse(data.toString());
|
|
308
|
+
if (message.type === "register-modules") {
|
|
309
|
+
console.log(
|
|
310
|
+
c(
|
|
311
|
+
"gray",
|
|
312
|
+
`š¦ Registered ${message.modules?.length || 0} modules`,
|
|
313
|
+
),
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
} catch (e) {}
|
|
317
|
+
});
|
|
318
|
+
ws.on("close", () => {
|
|
319
|
+
clients.delete(ws);
|
|
320
|
+
console.log(c("gray", "š HMR client disconnected"));
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
const watchPaths = [
|
|
325
|
+
"src/**/*.js",
|
|
326
|
+
"src/**/*.mjs",
|
|
327
|
+
"src/**/*.css",
|
|
328
|
+
"index.html",
|
|
329
|
+
];
|
|
330
|
+
const moduleVersions = new Map();
|
|
331
|
+
|
|
332
|
+
watcher = chokidar.watch(watchPaths, {
|
|
333
|
+
ignored: /(^|[\/\\])\../,
|
|
334
|
+
persistent: true,
|
|
335
|
+
cwd: projectRoot,
|
|
336
|
+
ignoreInitial: true,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
watcher.on("change", async (filePath) => {
|
|
340
|
+
const fullPath = path.join(projectRoot, filePath);
|
|
341
|
+
try {
|
|
342
|
+
const newContent = fs.readFileSync(fullPath, "utf-8");
|
|
343
|
+
const oldContent = moduleVersions.get(filePath);
|
|
344
|
+
const changeType = getChangeType(filePath, oldContent, newContent);
|
|
345
|
+
moduleVersions.set(filePath, newContent);
|
|
346
|
+
console.log(c("yellow", `š Changed: ${filePath} (${changeType})`));
|
|
347
|
+
broadcast(
|
|
348
|
+
JSON.stringify({
|
|
349
|
+
type: "hmr",
|
|
350
|
+
file: filePath,
|
|
351
|
+
changeType,
|
|
352
|
+
timestamp: Date.now(),
|
|
353
|
+
}),
|
|
354
|
+
);
|
|
355
|
+
} catch (err) {
|
|
356
|
+
console.error(c("red", `Error reading file: ${filePath}`));
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
watcher.on("add", (filePath) => {
|
|
361
|
+
console.log(c("green", `ā Added: ${filePath}`));
|
|
362
|
+
broadcast(JSON.stringify({ type: "reload", reason: "file-added" }));
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
watcher.on("unlink", (filePath) => {
|
|
366
|
+
console.log(c("red", `ā Removed: ${filePath}`));
|
|
367
|
+
broadcast(JSON.stringify({ type: "reload", reason: "file-removed" }));
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
console.log(c("blue", "š Watching for file changes..."));
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Boot spinner (skipped when an interactive port prompt may still happen)
|
|
374
|
+
boot = options.askPort && process.stdin.isTTY ? null : spinner(`Starting ${spaMode ? "SPA" : "static"} server...`);
|
|
375
|
+
server.listen(PORT, () => printReady(PORT));
|
|
376
|
+
|
|
377
|
+
const cleanup = () => {
|
|
378
|
+
console.log(c("yellow", "\nš Shutting down..."));
|
|
379
|
+
if (watcher) watcher.close();
|
|
380
|
+
clients.forEach((client) => client.close());
|
|
381
|
+
server.close(() => process.exit(0));
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
process.on("SIGINT", cleanup);
|
|
385
|
+
process.on("SIGTERM", cleanup);
|
|
386
|
+
|
|
387
|
+
return server;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
export default runDevServer;
|