@nitronjs/framework 0.3.2 → 0.3.4
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/cli/njs.js +1 -1
- package/lib/Build/CssBuilder.js +152 -136
- package/lib/Build/FileAnalyzer.js +52 -35
- package/lib/Build/Manager.js +144 -41
- package/lib/Build/ModuleGraph.js +84 -0
- package/lib/Build/PropUsageAnalyzer.js +2 -2
- package/lib/Build/plugins.js +35 -0
- package/lib/Console/Commands/DevCommand.js +301 -258
- package/lib/Date/DateTime.js +22 -2
- package/lib/Dev/DevIndicator.js +2 -1
- package/lib/HMR/Server.js +93 -170
- package/lib/Runtime/Entry.js +3 -0
- package/lib/View/Client/hmr-client.js +217 -219
- package/lib/View/Client/refresh-entry.cjs +1 -0
- package/lib/View/View.js +12 -7
- package/lib/index.d.ts +2 -0
- package/package.json +8 -6
- /package/skeleton/database/migrations/{2025_01_01_00_00_users.js → 2025_01_01_00_00_create_users.js} +0 -0
|
@@ -1,258 +1,301 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import dotenv from "dotenv";
|
|
3
|
-
import { spawn } from "child_process";
|
|
4
|
-
import
|
|
5
|
-
import path from "path";
|
|
6
|
-
import fs from "fs";
|
|
7
|
-
import Paths from "../../Core/Paths.js";
|
|
8
|
-
import Environment from "../../Core/Environment.js";
|
|
9
|
-
import Builder from "../../Build/Manager.js";
|
|
10
|
-
import Layout from "../../View/Layout.js";
|
|
11
|
-
|
|
12
|
-
dotenv.config({ quiet: true });
|
|
13
|
-
Environment.setDev(true);
|
|
14
|
-
|
|
15
|
-
const C = { r: "\x1b[0m", d: "\x1b[2m", bold: "\x1b[1m", red: "\x1b[31m", g: "\x1b[32m", y: "\x1b[33m", b: "\x1b[34m", m: "\x1b[35m", c: "\x1b[36m" };
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
this.#proc.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
async
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import dotenv from "dotenv";
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
import watcher from "@parcel/watcher";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import Paths from "../../Core/Paths.js";
|
|
8
|
+
import Environment from "../../Core/Environment.js";
|
|
9
|
+
import Builder from "../../Build/Manager.js";
|
|
10
|
+
import Layout from "../../View/Layout.js";
|
|
11
|
+
|
|
12
|
+
dotenv.config({ quiet: true });
|
|
13
|
+
Environment.setDev(true);
|
|
14
|
+
|
|
15
|
+
const C = { r: "\x1b[0m", d: "\x1b[2m", bold: "\x1b[1m", red: "\x1b[31m", g: "\x1b[32m", y: "\x1b[33m", b: "\x1b[34m", m: "\x1b[35m", c: "\x1b[36m" };
|
|
16
|
+
|
|
17
|
+
const ICONS = {
|
|
18
|
+
info: `${C.b}i${C.r}`,
|
|
19
|
+
ok: `${C.g}✓${C.r}`,
|
|
20
|
+
err: `${C.red}✗${C.r}`,
|
|
21
|
+
build: `${C.y}○${C.r}`,
|
|
22
|
+
hmr: `${C.m}●${C.r}`,
|
|
23
|
+
watch: `${C.c}◐${C.r}`
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const PATTERNS = {
|
|
27
|
+
views: ["resources/views/**/*.tsx", "resources/views/**/*.ts"],
|
|
28
|
+
css: ["resources/css/**/*.css"],
|
|
29
|
+
restart: ["config/**/*.js", "app/Kernel.js", "app/Models/**/*.js", "routes/**/*.js", "app.js", "app/Controllers/**/*.js", "app/Middlewares/**/*.js"],
|
|
30
|
+
framework: ["lib/View/Templates/**/*.tsx"]
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const IGNORE_PATTERN = /node_modules|\.git|build[/\\]|storage[/\\]|\.nitron/;
|
|
34
|
+
|
|
35
|
+
class DevServer {
|
|
36
|
+
#proc = null;
|
|
37
|
+
#building = false;
|
|
38
|
+
#builder = new Builder();
|
|
39
|
+
#debounce = { build: null, restart: null };
|
|
40
|
+
#banner = null;
|
|
41
|
+
#subscriptions = [];
|
|
42
|
+
#moduleGraph = null;
|
|
43
|
+
|
|
44
|
+
#log(icon, msg, extra) {
|
|
45
|
+
const time = new Date().toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
|
46
|
+
const iconStr = ICONS[icon] || ICONS.info;
|
|
47
|
+
const extraStr = extra ? ` ${C.d}(${extra})${C.r}` : "";
|
|
48
|
+
console.log(`${C.d}${time}${C.r} ${iconStr} ${msg}${extraStr}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async #build(only = null) {
|
|
52
|
+
if (this.#building) return { success: false, skipped: true };
|
|
53
|
+
this.#building = true;
|
|
54
|
+
this.#log("build", only ? `Building ${only}...` : "Building...");
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const result = await this.#builder.run(only, true, true);
|
|
58
|
+
this.#building = false;
|
|
59
|
+
|
|
60
|
+
if (result.success) {
|
|
61
|
+
this.#log("ok", "Build completed", `${result.time}ms`);
|
|
62
|
+
|
|
63
|
+
if (result.moduleGraph) {
|
|
64
|
+
this.#moduleGraph = result.moduleGraph;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.#log("err", "Build failed");
|
|
69
|
+
if (result.error) this.#send("error", { message: result.error });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
this.#building = false;
|
|
76
|
+
this.#log("err", `Build error: ${e.message}`);
|
|
77
|
+
this.#send("error", { message: e.message });
|
|
78
|
+
return { success: false, error: e.message };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async #start() {
|
|
83
|
+
return new Promise(resolve => {
|
|
84
|
+
this.#proc = spawn(process.execPath, [path.join(Paths.framework, "lib/Runtime/Entry.js")], {
|
|
85
|
+
cwd: process.cwd(),
|
|
86
|
+
stdio: ["inherit", "inherit", "inherit", "ipc"],
|
|
87
|
+
env: { ...process.env, __NITRON_DEV__: "true" }
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
this.#proc.on("error", e => this.#log("err", e.message));
|
|
91
|
+
|
|
92
|
+
this.#proc.on("close", code => {
|
|
93
|
+
if (code && code !== 0) this.#log("err", `Exit code ${code}`);
|
|
94
|
+
this.#proc = null;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
this.#proc.on("message", msg => {
|
|
98
|
+
if (msg?.type === "banner") this.#banner = msg.text;
|
|
99
|
+
if (msg?.type === "ready") resolve();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
setTimeout(resolve, 5000);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
#send(type, data) {
|
|
107
|
+
if (this.#proc?.connected) {
|
|
108
|
+
this.#proc.send({ type, ...data });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async #stop() {
|
|
113
|
+
if (!this.#proc) return;
|
|
114
|
+
|
|
115
|
+
return new Promise(resolve => {
|
|
116
|
+
const timeout = setTimeout(() => { this.#proc?.kill("SIGKILL"); resolve(); }, 5000);
|
|
117
|
+
this.#proc.once("close", () => { clearTimeout(timeout); resolve(); });
|
|
118
|
+
this.#proc.kill("SIGTERM");
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#rel(p, base = Paths.project) {
|
|
123
|
+
return path.relative(base, p).replace(/\\/g, "/");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
#match(p, patterns, base = Paths.project) {
|
|
127
|
+
const rel = this.#rel(p, base);
|
|
128
|
+
|
|
129
|
+
return patterns.some(pat => {
|
|
130
|
+
if (pat.includes("**")) {
|
|
131
|
+
const [prefix, suffix] = pat.split("**");
|
|
132
|
+
const dir = prefix.replace(/\/$/, "");
|
|
133
|
+
const ext = suffix.replace(/^\/?\*/, "");
|
|
134
|
+
return rel.startsWith(dir) && rel.endsWith(ext);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return rel === pat;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async #onChange(filePath) {
|
|
142
|
+
const rel = this.#rel(filePath);
|
|
143
|
+
|
|
144
|
+
if (this.#match(filePath, PATTERNS.framework, Paths.framework)) {
|
|
145
|
+
this.#log("build", `Framework: ${this.#rel(filePath, Paths.framework)}`);
|
|
146
|
+
clearTimeout(this.#debounce.build);
|
|
147
|
+
this.#debounce.build = setTimeout(async () => {
|
|
148
|
+
const r = await this.#build();
|
|
149
|
+
if (r.success) this.#send("reload", { reason: "Framework template changed" });
|
|
150
|
+
}, 50);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (this.#match(filePath, PATTERNS.css)) {
|
|
155
|
+
this.#log("hmr", rel);
|
|
156
|
+
clearTimeout(this.#debounce.build);
|
|
157
|
+
this.#debounce.build = setTimeout(async () => {
|
|
158
|
+
const r = await this.#build("css");
|
|
159
|
+
if (r.success) this.#send("change", { changeType: "css", file: rel });
|
|
160
|
+
}, 50);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (this.#match(filePath, PATTERNS.views)) {
|
|
165
|
+
this.#log("hmr", rel);
|
|
166
|
+
clearTimeout(this.#debounce.build);
|
|
167
|
+
this.#debounce.build = setTimeout(async () => {
|
|
168
|
+
const r = await this.#build();
|
|
169
|
+
|
|
170
|
+
if (!r.success) return;
|
|
171
|
+
|
|
172
|
+
if (this.#moduleGraph && r.viewsChanged) {
|
|
173
|
+
const boundaries = this.#moduleGraph.getAffectedBoundaries(filePath);
|
|
174
|
+
const clientBoundaries = [...boundaries].filter(b => this.#moduleGraph.isClientComponent(b));
|
|
175
|
+
|
|
176
|
+
if (clientBoundaries.length > 0 && r.rebuiltChunks?.length) {
|
|
177
|
+
this.#send("fast-refresh", {
|
|
178
|
+
chunks: r.rebuiltChunks,
|
|
179
|
+
cssChanged: r.cssChanged || false,
|
|
180
|
+
timestamp: Date.now()
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
this.#send("change", {
|
|
185
|
+
changeType: detectChangeType(filePath),
|
|
186
|
+
cssChanged: r.cssChanged || false,
|
|
187
|
+
file: rel
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else if (r.viewsChanged && r.rebuiltChunks?.length) {
|
|
192
|
+
this.#send("fast-refresh", {
|
|
193
|
+
chunks: r.rebuiltChunks,
|
|
194
|
+
cssChanged: r.cssChanged || false,
|
|
195
|
+
timestamp: Date.now()
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
else if (r.viewsChanged) {
|
|
199
|
+
this.#send("reload", { reason: "Client component changed" });
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
this.#send("change", {
|
|
203
|
+
changeType: detectChangeType(filePath),
|
|
204
|
+
cssChanged: r.cssChanged || false,
|
|
205
|
+
file: rel
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}, 50);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (this.#match(filePath, PATTERNS.restart)) {
|
|
213
|
+
this.#log("build", rel, "restart required");
|
|
214
|
+
clearTimeout(this.#debounce.restart);
|
|
215
|
+
this.#debounce.restart = setTimeout(async () => {
|
|
216
|
+
await this.#build();
|
|
217
|
+
await this.#stop();
|
|
218
|
+
await this.#start();
|
|
219
|
+
this.#send("reload", { reason: "Server restarted" });
|
|
220
|
+
}, 100);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
this.#log("info", `Unmatched: ${rel}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async #watch() {
|
|
228
|
+
const candidates = [
|
|
229
|
+
path.join(Paths.project, "resources/views"),
|
|
230
|
+
path.join(Paths.project, "resources/css"),
|
|
231
|
+
path.join(Paths.project, "config"),
|
|
232
|
+
path.join(Paths.project, "app"),
|
|
233
|
+
path.join(Paths.project, "routes"),
|
|
234
|
+
path.join(Paths.framework, "lib/View/Templates")
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
const watchPaths = candidates.filter(p => fs.existsSync(p));
|
|
238
|
+
|
|
239
|
+
if (!watchPaths.length) {
|
|
240
|
+
this.#log("err", "No watch paths found");
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
for (const dir of watchPaths) {
|
|
245
|
+
const sub = await watcher.subscribe(dir, (err, events) => {
|
|
246
|
+
if (err) {
|
|
247
|
+
this.#log("err", `Watch error: ${err.message}`);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
for (const event of events) {
|
|
252
|
+
if (IGNORE_PATTERN.test(event.path)) continue;
|
|
253
|
+
if (event.type === "delete") continue;
|
|
254
|
+
|
|
255
|
+
this.#onChange(event.path);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
this.#subscriptions.push(sub);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
this.#log("watch", `Watching ${watchPaths.length} directories`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async start() {
|
|
266
|
+
await this.#build();
|
|
267
|
+
await this.#start();
|
|
268
|
+
await this.#watch();
|
|
269
|
+
|
|
270
|
+
if (this.#banner) console.log(this.#banner);
|
|
271
|
+
|
|
272
|
+
const exit = async () => {
|
|
273
|
+
console.log();
|
|
274
|
+
|
|
275
|
+
for (const sub of this.#subscriptions) {
|
|
276
|
+
await sub.unsubscribe();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
await this.#builder.dispose();
|
|
280
|
+
await this.#stop();
|
|
281
|
+
process.exitCode = 0;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
process.on("SIGINT", exit);
|
|
285
|
+
process.on("SIGTERM", exit);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function detectChangeType(filePath) {
|
|
290
|
+
if (Layout.isLayout(filePath)) return "layout";
|
|
291
|
+
|
|
292
|
+
return "page";
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export default async function Dev() {
|
|
296
|
+
await new DevServer().start();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (process.argv[1]?.endsWith("DevCommand.js")) {
|
|
300
|
+
Dev().catch(e => { console.error(e); process.exitCode = 1; });
|
|
301
|
+
}
|
package/lib/Date/DateTime.js
CHANGED
|
@@ -111,7 +111,18 @@ class DateTime {
|
|
|
111
111
|
date.setHours(date.getHours() + hours);
|
|
112
112
|
return date.toISOString().slice(0, 19).replace('T', ' ');
|
|
113
113
|
}
|
|
114
|
-
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Add minutes to current date
|
|
117
|
+
* @param {number} minutes - Number of minutes to add
|
|
118
|
+
* @returns {string} SQL formatted datetime
|
|
119
|
+
*/
|
|
120
|
+
static addMinutes(minutes) {
|
|
121
|
+
const date = this.#getDate();
|
|
122
|
+
date.setMinutes(date.getMinutes() + minutes);
|
|
123
|
+
return date.toISOString().slice(0, 19).replace('T', ' ');
|
|
124
|
+
}
|
|
125
|
+
|
|
115
126
|
/**
|
|
116
127
|
* Subtract days from current date
|
|
117
128
|
* @param {number} days - Number of days to subtract
|
|
@@ -120,7 +131,7 @@ class DateTime {
|
|
|
120
131
|
static subDays(days) {
|
|
121
132
|
return this.addDays(-days);
|
|
122
133
|
}
|
|
123
|
-
|
|
134
|
+
|
|
124
135
|
/**
|
|
125
136
|
* Subtract hours from current date
|
|
126
137
|
* @param {number} hours - Number of hours to subtract
|
|
@@ -129,6 +140,15 @@ class DateTime {
|
|
|
129
140
|
static subHours(hours) {
|
|
130
141
|
return this.addHours(-hours);
|
|
131
142
|
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Subtract minutes from current date
|
|
146
|
+
* @param {number} minutes - Number of minutes to subtract
|
|
147
|
+
* @returns {string} SQL formatted datetime
|
|
148
|
+
*/
|
|
149
|
+
static subMinutes(minutes) {
|
|
150
|
+
return this.addMinutes(-minutes);
|
|
151
|
+
}
|
|
132
152
|
}
|
|
133
153
|
|
|
134
154
|
export default DateTime;
|
package/lib/Dev/DevIndicator.js
CHANGED
|
@@ -29,7 +29,8 @@ class DevIndicator {
|
|
|
29
29
|
const route = devData?.route;
|
|
30
30
|
const timing = devData ? buildTimingSummary(devData) : null;
|
|
31
31
|
const mwCount = devData?.middlewareTiming?.length || 0;
|
|
32
|
-
const
|
|
32
|
+
const hasProps = devData?.rawProps && Object.keys(devData.rawProps).length > 0;
|
|
33
|
+
const propsInfo = hasProps ? buildPropsSummary(devData) : null;
|
|
33
34
|
|
|
34
35
|
const totalMs = timing ? parseFloat(timing.total) : 0;
|
|
35
36
|
const timingWarnClass = totalMs >= 300 ? " ndi-critical" : totalMs >= 100 ? " ndi-warn" : "";
|