@nitronjs/framework 0.1.24 → 0.2.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/lib/Build/CssBuilder.js +129 -0
- package/lib/Build/FileAnalyzer.js +395 -0
- package/lib/Build/HydrationBuilder.js +173 -0
- package/lib/Build/Manager.js +290 -943
- package/lib/Build/colors.js +10 -0
- package/lib/Build/jsxRuntime.js +116 -0
- package/lib/Build/plugins.js +264 -0
- package/lib/Console/Commands/BuildCommand.js +6 -5
- package/lib/Console/Commands/DevCommand.js +151 -311
- package/lib/Console/Stubs/page-hydration-dev.tsx +72 -0
- package/lib/Console/Stubs/page-hydration.tsx +9 -10
- package/lib/Console/Stubs/vendor-dev.tsx +50 -0
- package/lib/Core/Environment.js +29 -2
- package/lib/Core/Paths.js +12 -4
- package/lib/Database/Drivers/MySQLDriver.js +5 -4
- package/lib/Database/QueryBuilder.js +2 -3
- package/lib/Filesystem/Manager.js +32 -7
- package/lib/HMR/Server.js +87 -0
- package/lib/Http/Server.js +9 -5
- package/lib/Logging/Manager.js +68 -18
- package/lib/Route/Loader.js +3 -4
- package/lib/Route/Manager.js +24 -3
- package/lib/Runtime/Entry.js +26 -1
- package/lib/Session/File.js +18 -7
- package/lib/View/Client/hmr-client.js +166 -0
- package/lib/View/Client/spa.js +142 -0
- package/lib/View/Layout.js +94 -0
- package/lib/View/Manager.js +390 -46
- package/lib/index.d.ts +55 -0
- package/package.json +2 -1
- package/skeleton/.env.example +0 -2
- package/skeleton/app/Controllers/HomeController.js +27 -3
- package/skeleton/config/app.js +15 -14
- package/skeleton/config/session.js +1 -1
- package/skeleton/globals.d.ts +3 -63
- package/skeleton/resources/views/Site/Home.tsx +274 -50
- package/skeleton/tsconfig.json +5 -1
|
@@ -3,383 +3,223 @@ import dotenv from "dotenv";
|
|
|
3
3
|
import { spawn } from "child_process";
|
|
4
4
|
import chokidar from "chokidar";
|
|
5
5
|
import path from "path";
|
|
6
|
+
import fs from "fs";
|
|
6
7
|
import Paths from "../../Core/Paths.js";
|
|
8
|
+
import Environment from "../../Core/Environment.js";
|
|
9
|
+
import Builder from "../../Build/Manager.js";
|
|
7
10
|
|
|
8
11
|
dotenv.config({ quiet: true });
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
bold: "\x1b[1m",
|
|
20
|
-
dim: "\x1b[2m",
|
|
21
|
-
red: "\x1b[31m",
|
|
22
|
-
green: "\x1b[32m",
|
|
23
|
-
yellow: "\x1b[33m",
|
|
24
|
-
blue: "\x1b[34m",
|
|
25
|
-
magenta: "\x1b[35m",
|
|
26
|
-
cyan: "\x1b[36m"
|
|
12
|
+
Environment.setDev(true);
|
|
13
|
+
|
|
14
|
+
const C = { r: "\x1b[0m", d: "\x1b[2m", red: "\x1b[31m", g: "\x1b[32m", y: "\x1b[33m", b: "\x1b[34m", m: "\x1b[35m", c: "\x1b[36m" };
|
|
15
|
+
const ICONS = {
|
|
16
|
+
info: `${C.b}ℹ${C.r} `,
|
|
17
|
+
ok: `${C.g}✓${C.r} `,
|
|
18
|
+
err: `${C.red}✗${C.r} `,
|
|
19
|
+
build: `${C.m}⟳${C.r} `,
|
|
20
|
+
hmr: `${C.c}⚡${C.r}`,
|
|
21
|
+
watch: `${C.y}○${C.r} `
|
|
27
22
|
};
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"resources/views/**/*.tsx",
|
|
35
|
-
"resources/views/**/*.ts",
|
|
36
|
-
"resources/css/**/*.css",
|
|
37
|
-
"app/Controllers/*.js",
|
|
38
|
-
"app/Middlewares/*.js",
|
|
39
|
-
"app/Middlewares/**/*.js"
|
|
40
|
-
],
|
|
41
|
-
|
|
42
|
-
// Files that trigger a full server restart
|
|
43
|
-
fullRestart: [
|
|
44
|
-
"config/**/*.js",
|
|
45
|
-
"app/Kernel.js",
|
|
46
|
-
"app/Models/**/*.js",
|
|
47
|
-
"routes/**/*.js",
|
|
48
|
-
"app.js"
|
|
49
|
-
],
|
|
50
|
-
|
|
51
|
-
// Framework files (relative to FRAMEWORK_DIR) - trigger build only
|
|
52
|
-
frameworkBuildOnly: [
|
|
53
|
-
"lib/View/Templates/**/*.tsx",
|
|
54
|
-
"lib/View/Templates/**/*.ts"
|
|
55
|
-
],
|
|
56
|
-
|
|
57
|
-
// Ignored patterns
|
|
58
|
-
ignore: [
|
|
59
|
-
"**/node_modules/**",
|
|
60
|
-
"**/build/**",
|
|
61
|
-
"**/storage/app/public/**",
|
|
62
|
-
"**/storage/framework/**",
|
|
63
|
-
"**/storage/logs/**",
|
|
64
|
-
"**/.nitron/**",
|
|
65
|
-
"**/.git/**",
|
|
66
|
-
"**/package-lock.json"
|
|
67
|
-
]
|
|
24
|
+
const PATTERNS = {
|
|
25
|
+
views: ["resources/views/**/*.tsx"],
|
|
26
|
+
css: ["resources/css/**/*.css"],
|
|
27
|
+
restart: ["config/**/*.js", "app/Kernel.js", "app/Models/**/*.js", "routes/**/*.js", "app.js", "app/Controllers/**/*.js", "app/Middlewares/**/*.js"],
|
|
28
|
+
framework: ["lib/View/Templates/**/*.tsx"]
|
|
68
29
|
};
|
|
69
30
|
|
|
70
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
71
|
-
// Dev Server Class
|
|
72
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
73
|
-
|
|
74
31
|
class DevServer {
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
#
|
|
79
|
-
|
|
80
|
-
#
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
constructor() {
|
|
84
|
-
//
|
|
32
|
+
#proc = null;
|
|
33
|
+
#building = false;
|
|
34
|
+
#builder = new Builder();
|
|
35
|
+
#debounce = { build: null, restart: null };
|
|
36
|
+
|
|
37
|
+
#log(icon, msg, extra) {
|
|
38
|
+
const t = `${C.d}${new Date().toLocaleTimeString()}${C.r}`;
|
|
39
|
+
console.log(`${t} ${ICONS[icon] || ICONS.info} ${msg}${extra ? ` ${C.d}(${extra})${C.r}` : ""}`);
|
|
85
40
|
}
|
|
86
41
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
42
|
+
async #build(only = null) {
|
|
43
|
+
if (this.#building) return { success: false, skipped: true };
|
|
44
|
+
this.#building = true;
|
|
45
|
+
this.#log("build", only ? `Building ${only}...` : "Building...");
|
|
90
46
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const icons = {
|
|
96
|
-
info: `${COLORS.blue}[i]${COLORS.reset}`,
|
|
97
|
-
success: `${COLORS.green}[✓]${COLORS.reset}`,
|
|
98
|
-
warning: `${COLORS.yellow}[!]${COLORS.reset}`,
|
|
99
|
-
error: `${COLORS.red}[✗]${COLORS.reset}`,
|
|
100
|
-
build: `${COLORS.magenta}[~]${COLORS.reset}`,
|
|
101
|
-
reload: `${COLORS.cyan}[↻]${COLORS.reset}`,
|
|
102
|
-
watch: `${COLORS.yellow}[○]${COLORS.reset}`
|
|
103
|
-
};
|
|
47
|
+
try {
|
|
48
|
+
const result = await this.#builder.run(only, true, true);
|
|
49
|
+
this.#building = false;
|
|
104
50
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
51
|
+
if (result.success) {
|
|
52
|
+
this.#log("ok", "Build completed", `${result.time}ms`);
|
|
53
|
+
} else {
|
|
54
|
+
this.#log("err", "Build failed");
|
|
55
|
+
if (result.error) this.#send("error", { message: result.error });
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
} catch (e) {
|
|
59
|
+
this.#building = false;
|
|
60
|
+
this.#log("err", `Build error: ${e.message}`);
|
|
61
|
+
this.#send("error", { message: e.message });
|
|
62
|
+
return { success: false, error: e.message };
|
|
110
63
|
}
|
|
111
|
-
|
|
112
|
-
console.log(output);
|
|
113
64
|
}
|
|
114
65
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.#pendingBuild = true;
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
this.#isBuilding = true;
|
|
126
|
-
const startTime = Date.now();
|
|
127
|
-
this.#log("build", "Building...");
|
|
128
|
-
|
|
129
|
-
return new Promise((resolve) => {
|
|
130
|
-
const buildScript = path.join(FRAMEWORK_DIR, "lib/Console/Commands/BuildCommand.js");
|
|
131
|
-
const args = [buildScript];
|
|
132
|
-
if (buildArg) args.push(buildArg);
|
|
133
|
-
|
|
134
|
-
const buildProcess = spawn(process.execPath, args, {
|
|
135
|
-
cwd: PROJECT_DIR,
|
|
136
|
-
stdio: ["inherit", "pipe", "pipe"]
|
|
66
|
+
async #start() {
|
|
67
|
+
return new Promise(resolve => {
|
|
68
|
+
this.#proc = spawn(process.execPath, [path.join(Paths.framework, "lib/Runtime/Entry.js")], {
|
|
69
|
+
cwd: process.cwd(),
|
|
70
|
+
stdio: ["inherit", "inherit", "inherit", "ipc"],
|
|
71
|
+
env: { ...process.env, __NITRON_DEV__: "true" }
|
|
137
72
|
});
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
output += data.toString();
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
buildProcess.stderr.on("data", (data) => {
|
|
146
|
-
output += data.toString();
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
buildProcess.on("close", (code) => {
|
|
150
|
-
this.#isBuilding = false;
|
|
151
|
-
const duration = Date.now() - startTime;
|
|
152
|
-
|
|
153
|
-
if (code === 0) {
|
|
154
|
-
this.#log("success", `Build completed`, `${duration}ms`);
|
|
155
|
-
|
|
156
|
-
if (this.#pendingBuild) {
|
|
157
|
-
this.#pendingBuild = false;
|
|
158
|
-
this.#runBuild().then(resolve);
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
resolve(true);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
this.#log("error", "Build failed");
|
|
166
|
-
console.log(output);
|
|
167
|
-
resolve(false);
|
|
168
|
-
}
|
|
73
|
+
this.#proc.on("error", e => this.#log("err", e.message));
|
|
74
|
+
this.#proc.on("close", code => {
|
|
75
|
+
if (code && code !== 0) this.#log("err", `Exit code ${code}`);
|
|
76
|
+
this.#proc = null;
|
|
169
77
|
});
|
|
78
|
+
setTimeout(resolve, 500);
|
|
170
79
|
});
|
|
171
80
|
}
|
|
172
81
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
async #startServer() {
|
|
178
|
-
return new Promise((resolve) => {
|
|
179
|
-
const entryScript = path.join(FRAMEWORK_DIR, "lib/Runtime/Entry.js");
|
|
180
|
-
|
|
181
|
-
this.#serverProcess = spawn(process.execPath, [entryScript], {
|
|
182
|
-
cwd: PROJECT_DIR,
|
|
183
|
-
stdio: ["inherit", "inherit", "inherit"],
|
|
184
|
-
env: { ...process.env, APP_DEV: "true" }
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
this.#serverProcess.on("error", (err) => {
|
|
188
|
-
this.#log("error", `Server error: ${err.message}`);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
this.#serverProcess.on("close", (code) => {
|
|
192
|
-
if (code !== 0 && code !== null && !this.#pendingRestart) {
|
|
193
|
-
this.#log("error", `Server exited with code ${code}`);
|
|
194
|
-
}
|
|
195
|
-
this.#serverProcess = null;
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
setTimeout(() => resolve(), 500);
|
|
199
|
-
});
|
|
82
|
+
#send(type, data) {
|
|
83
|
+
if (this.#proc?.connected) {
|
|
84
|
+
this.#proc.send({ type, ...data });
|
|
85
|
+
}
|
|
200
86
|
}
|
|
201
87
|
|
|
202
|
-
async #
|
|
203
|
-
if (!this.#
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (this.#serverProcess) {
|
|
209
|
-
this.#serverProcess.kill("SIGKILL");
|
|
210
|
-
}
|
|
211
|
-
resolve();
|
|
212
|
-
}, 5000);
|
|
213
|
-
|
|
214
|
-
this.#serverProcess.once("close", () => {
|
|
215
|
-
clearTimeout(forceKillTimer);
|
|
216
|
-
resolve();
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
this.#serverProcess.kill("SIGTERM");
|
|
88
|
+
async #stop() {
|
|
89
|
+
if (!this.#proc) return;
|
|
90
|
+
return new Promise(resolve => {
|
|
91
|
+
const timeout = setTimeout(() => { this.#proc?.kill("SIGKILL"); resolve(); }, 5000);
|
|
92
|
+
this.#proc.once("close", () => { clearTimeout(timeout); resolve(); });
|
|
93
|
+
this.#proc.kill("SIGTERM");
|
|
220
94
|
});
|
|
221
95
|
}
|
|
222
96
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
this.#pendingRestart = true;
|
|
227
|
-
await this.#stopServer();
|
|
228
|
-
await this.#startServer();
|
|
229
|
-
this.#pendingRestart = false;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
#getRelativePath(filePath, baseDir = PROJECT_DIR) {
|
|
233
|
-
return path.relative(baseDir, filePath).replace(/\\/g, "/");
|
|
97
|
+
#rel(p, base = Paths.project) {
|
|
98
|
+
return path.relative(base, p).replace(/\\/g, "/");
|
|
234
99
|
}
|
|
235
100
|
|
|
236
|
-
#
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
.replace(
|
|
242
|
-
.replace(
|
|
243
|
-
.
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
.replace(/<<ANYCHAR>>/g, ".*"); // restore ** placeholder
|
|
247
|
-
|
|
248
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
249
|
-
return regex.test(relativePath);
|
|
101
|
+
#match(p, patterns, base = Paths.project) {
|
|
102
|
+
const rel = this.#rel(p, base);
|
|
103
|
+
return patterns.some(pat => {
|
|
104
|
+
if (pat.includes("**")) {
|
|
105
|
+
const [prefix, suffix] = pat.split("**");
|
|
106
|
+
const dir = prefix.replace(/\/$/, "");
|
|
107
|
+
const ext = suffix.replace(/^\/?\*/, "");
|
|
108
|
+
return rel.startsWith(dir) && rel.endsWith(ext);
|
|
109
|
+
}
|
|
110
|
+
return rel === pat;
|
|
250
111
|
});
|
|
251
112
|
}
|
|
252
113
|
|
|
253
|
-
async #
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
this.#
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
114
|
+
async #onChange(filePath) {
|
|
115
|
+
const rel = this.#rel(filePath);
|
|
116
|
+
|
|
117
|
+
if (this.#match(filePath, PATTERNS.framework, Paths.framework)) {
|
|
118
|
+
this.#log("build", `Framework: ${this.#rel(filePath, Paths.framework)}`);
|
|
119
|
+
clearTimeout(this.#debounce.build);
|
|
120
|
+
this.#debounce.build = setTimeout(async () => {
|
|
121
|
+
const r = await this.#build();
|
|
122
|
+
if (r.success) this.#send("reload", { reason: "Framework template changed" });
|
|
123
|
+
}, 100);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (this.#match(filePath, PATTERNS.css)) {
|
|
128
|
+
this.#log("hmr", rel);
|
|
129
|
+
clearTimeout(this.#debounce.build);
|
|
130
|
+
this.#debounce.build = setTimeout(async () => {
|
|
131
|
+
const r = await this.#build("css");
|
|
132
|
+
if (r.success) this.#send("css", { filePath });
|
|
264
133
|
}, 100);
|
|
265
|
-
|
|
266
134
|
return;
|
|
267
135
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
this.#
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
this.#buildDebounceTimer = setTimeout(async () => {
|
|
279
|
-
await this.#runBuild(buildArg);
|
|
136
|
+
|
|
137
|
+
if (this.#match(filePath, PATTERNS.views)) {
|
|
138
|
+
this.#log("hmr", rel);
|
|
139
|
+
clearTimeout(this.#debounce.build);
|
|
140
|
+
this.#debounce.build = setTimeout(async () => {
|
|
141
|
+
const r = await this.#build();
|
|
142
|
+
if (r.success) {
|
|
143
|
+
if (r.cssChanged) this.#send("css", {});
|
|
144
|
+
this.#send("view", { filePath });
|
|
145
|
+
}
|
|
280
146
|
}, 100);
|
|
281
|
-
|
|
282
147
|
return;
|
|
283
148
|
}
|
|
284
149
|
|
|
285
|
-
if (this.#
|
|
286
|
-
this.#log("
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
await this.#
|
|
291
|
-
await this.#
|
|
150
|
+
if (this.#match(filePath, PATTERNS.restart)) {
|
|
151
|
+
this.#log("build", rel, "restart required");
|
|
152
|
+
clearTimeout(this.#debounce.restart);
|
|
153
|
+
this.#debounce.restart = setTimeout(async () => {
|
|
154
|
+
await this.#build();
|
|
155
|
+
await this.#stop();
|
|
156
|
+
await this.#start();
|
|
157
|
+
this.#send("reload", { reason: "Server restarted" });
|
|
292
158
|
}, 200);
|
|
293
|
-
|
|
294
159
|
return;
|
|
295
160
|
}
|
|
161
|
+
|
|
162
|
+
this.#log("info", `Unmatched: ${rel}`);
|
|
296
163
|
}
|
|
297
164
|
|
|
298
|
-
#
|
|
299
|
-
const
|
|
300
|
-
path.join(
|
|
301
|
-
path.join(
|
|
302
|
-
path.join(
|
|
303
|
-
path.join(
|
|
304
|
-
path.join(
|
|
305
|
-
path.join(
|
|
306
|
-
path.join(PROJECT_DIR, "app.js")
|
|
165
|
+
#watch() {
|
|
166
|
+
const candidates = [
|
|
167
|
+
path.join(Paths.project, "resources/views"),
|
|
168
|
+
path.join(Paths.project, "resources/css"),
|
|
169
|
+
path.join(Paths.project, "config"),
|
|
170
|
+
path.join(Paths.project, "app"),
|
|
171
|
+
path.join(Paths.project, "routes"),
|
|
172
|
+
path.join(Paths.framework, "lib/View/Templates")
|
|
307
173
|
];
|
|
308
174
|
|
|
175
|
+
const watchPaths = candidates.filter(p => fs.existsSync(p));
|
|
176
|
+
|
|
177
|
+
if (!watchPaths.length) {
|
|
178
|
+
this.#log("err", "No watch paths found");
|
|
179
|
+
return { close: () => {} };
|
|
180
|
+
}
|
|
181
|
+
|
|
309
182
|
const watcher = chokidar.watch(watchPaths, {
|
|
310
|
-
ignored: [
|
|
311
|
-
/node_modules/,
|
|
312
|
-
/\.git/,
|
|
313
|
-
/build[\\\/]/,
|
|
314
|
-
/storage[\\\/]app[\\\/]public/,
|
|
315
|
-
/storage[\\\/]framework/,
|
|
316
|
-
/storage[\\\/]logs/,
|
|
317
|
-
/\.nitron/,
|
|
318
|
-
/package-lock\.json$/
|
|
319
|
-
],
|
|
183
|
+
ignored: [/node_modules/, /\.git/, /build[\\/]/, /storage[\\/]/, /\.nitron/],
|
|
320
184
|
persistent: true,
|
|
321
185
|
ignoreInitial: true,
|
|
322
|
-
usePolling:
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
186
|
+
usePolling: true,
|
|
187
|
+
interval: 100,
|
|
188
|
+
binaryInterval: 100,
|
|
189
|
+
awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 20 }
|
|
327
190
|
});
|
|
328
191
|
|
|
329
|
-
watcher.on("change",
|
|
330
|
-
watcher.on("add",
|
|
331
|
-
watcher.on("
|
|
332
|
-
|
|
192
|
+
watcher.on("change", p => this.#onChange(p));
|
|
193
|
+
watcher.on("add", p => this.#onChange(p));
|
|
194
|
+
watcher.on("error", e => this.#log("err", `Watch error: ${e.message}`));
|
|
333
195
|
watcher.on("ready", () => {
|
|
334
|
-
|
|
335
|
-
this.#watcherReady = true;
|
|
336
|
-
this.#log("watch", "Watching for file changes...");
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
watcher.on("error", (error) => {
|
|
341
|
-
this.#log("error", `Watcher error: ${error.message}`);
|
|
196
|
+
this.#log("watch", `Watching ${watchPaths.length} directories`);
|
|
342
197
|
});
|
|
343
198
|
|
|
344
199
|
return watcher;
|
|
345
200
|
}
|
|
346
201
|
|
|
347
202
|
async start() {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
this.#log("error", "Initial build failed. Watching for changes...");
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Start server
|
|
355
|
-
await this.#startServer();
|
|
356
|
-
|
|
357
|
-
// Setup file watcher
|
|
358
|
-
const watcher = this.#setupWatcher();
|
|
203
|
+
await this.#build();
|
|
204
|
+
await this.#start();
|
|
205
|
+
const watcher = this.#watch();
|
|
359
206
|
|
|
360
|
-
|
|
361
|
-
const cleanup = async () => {
|
|
207
|
+
const exit = async () => {
|
|
362
208
|
console.log();
|
|
363
209
|
watcher.close();
|
|
364
|
-
await this.#
|
|
210
|
+
await this.#stop();
|
|
365
211
|
process.exit(0);
|
|
366
212
|
};
|
|
367
213
|
|
|
368
|
-
process.on("SIGINT",
|
|
369
|
-
process.on("SIGTERM",
|
|
214
|
+
process.on("SIGINT", exit);
|
|
215
|
+
process.on("SIGTERM", exit);
|
|
370
216
|
}
|
|
371
217
|
}
|
|
372
218
|
|
|
373
219
|
export default async function Dev() {
|
|
374
|
-
|
|
375
|
-
await devServer.start();
|
|
220
|
+
await new DevServer().start();
|
|
376
221
|
}
|
|
377
222
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if (isMain) {
|
|
381
|
-
Dev().catch(err => {
|
|
382
|
-
console.error(err);
|
|
383
|
-
process.exit(1);
|
|
384
|
-
});
|
|
223
|
+
if (process.argv[1]?.endsWith("DevCommand.js")) {
|
|
224
|
+
Dev().catch(e => { console.error(e); process.exit(1); });
|
|
385
225
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
|
|
4
|
+
// __COMPONENT_IMPORTS__
|
|
5
|
+
|
|
6
|
+
const componentManifest: Record<string, React.ComponentType<any>> = {};
|
|
7
|
+
|
|
8
|
+
// __COMPONENT_MANIFEST__
|
|
9
|
+
|
|
10
|
+
const w = window as any;
|
|
11
|
+
const prevRefreshReg = w.$RefreshReg$;
|
|
12
|
+
const prevRefreshSig = w.$RefreshSig$;
|
|
13
|
+
|
|
14
|
+
if (typeof w.$RefreshReg$ === "function") {
|
|
15
|
+
const moduleId = "__NITRON_MODULE_ID__";
|
|
16
|
+
w.$RefreshReg$ = (type: any, id: string) => {
|
|
17
|
+
const fullId = moduleId + "_" + id;
|
|
18
|
+
if (prevRefreshReg) prevRefreshReg(type, fullId);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (typeof w.$RefreshSig$ === "function") {
|
|
23
|
+
w.$RefreshSig$ = prevRefreshSig;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// __COMPONENT_REGISTRATIONS__
|
|
27
|
+
|
|
28
|
+
function mount() {
|
|
29
|
+
const props = (window as any).__NITRON_PROPS__ || {};
|
|
30
|
+
if (!(window as any).__NITRON_ROOTS__) (window as any).__NITRON_ROOTS__ = new Map();
|
|
31
|
+
|
|
32
|
+
const islands = document.querySelectorAll<HTMLElement>("[data-cid]");
|
|
33
|
+
|
|
34
|
+
islands.forEach(element => {
|
|
35
|
+
const componentName = element.dataset.island;
|
|
36
|
+
const componentId = element.dataset.cid;
|
|
37
|
+
|
|
38
|
+
if (!componentName || !componentId) return;
|
|
39
|
+
|
|
40
|
+
const Component = componentManifest[componentName];
|
|
41
|
+
if (!Component) return;
|
|
42
|
+
|
|
43
|
+
const componentProps = props[componentId] || {};
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const roots = (window as any).__NITRON_ROOTS__ as Map<HTMLElement, any>;
|
|
47
|
+
let root = roots.get(element);
|
|
48
|
+
if (!root) {
|
|
49
|
+
root = createRoot(element);
|
|
50
|
+
roots.set(element, root);
|
|
51
|
+
}
|
|
52
|
+
root.render(React.createElement(Component, componentProps));
|
|
53
|
+
} catch {
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
delete (window as any).__NITRON_PROPS__;
|
|
58
|
+
|
|
59
|
+
if ((window as any).__NITRON_REFRESH__) {
|
|
60
|
+
(window as any).__NITRON_REFRESH__.performReactRefresh();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const isHmrUpdate = (window as any).__NITRON_HMR_UPDATE__;
|
|
65
|
+
|
|
66
|
+
if (!isHmrUpdate) {
|
|
67
|
+
if (document.readyState === "loading") {
|
|
68
|
+
document.addEventListener("DOMContentLoaded", mount);
|
|
69
|
+
} else {
|
|
70
|
+
mount();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -6,6 +6,7 @@ import { createRoot } from "react-dom/client";
|
|
|
6
6
|
declare global {
|
|
7
7
|
interface Window {
|
|
8
8
|
__NITRON_PROPS__?: Record<string, any>;
|
|
9
|
+
__NITRON_ROOTS__?: Map<HTMLElement, any>;
|
|
9
10
|
}
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -15,6 +16,7 @@ const componentManifest: Record<string, React.ComponentType<any>> = {};
|
|
|
15
16
|
|
|
16
17
|
function mount() {
|
|
17
18
|
const props = window.__NITRON_PROPS__ || {};
|
|
19
|
+
if (!window.__NITRON_ROOTS__) window.__NITRON_ROOTS__ = new Map();
|
|
18
20
|
|
|
19
21
|
const islands = document.querySelectorAll<HTMLElement>("[data-cid]");
|
|
20
22
|
|
|
@@ -22,22 +24,19 @@ function mount() {
|
|
|
22
24
|
const componentName = element.dataset.island;
|
|
23
25
|
const componentId = element.dataset.cid;
|
|
24
26
|
|
|
25
|
-
if (!componentName || !componentId)
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
27
|
+
if (!componentName || !componentId) return;
|
|
28
28
|
|
|
29
29
|
const Component = componentManifest[componentName];
|
|
30
|
-
|
|
31
|
-
if (!Component) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
30
|
+
if (!Component) return;
|
|
34
31
|
|
|
35
32
|
const componentProps = props[componentId] || {};
|
|
36
33
|
|
|
37
34
|
try {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
let root = window.__NITRON_ROOTS__.get(element);
|
|
36
|
+
if (!root) {
|
|
37
|
+
root = createRoot(element);
|
|
38
|
+
window.__NITRON_ROOTS__.set(element, root);
|
|
39
|
+
}
|
|
41
40
|
root.render(React.createElement(Component, componentProps));
|
|
42
41
|
} catch {
|
|
43
42
|
}
|