@mokup/server 1.1.6 → 1.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/dist/fetch-server.cjs +39 -167
- package/dist/fetch-server.d.cts +1 -1
- package/dist/fetch-server.d.mts +1 -1
- package/dist/fetch-server.d.ts +1 -1
- package/dist/fetch-server.mjs +17 -145
- package/dist/index.cjs +140 -11
- package/dist/index.d.cts +28 -19
- package/dist/index.d.mts +28 -19
- package/dist/index.d.ts +28 -19
- package/dist/index.mjs +139 -12
- package/dist/node.cjs +5 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.mts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.mjs +5 -1
- package/dist/shared/{server.CyVIKPsp.d.cts → server.DLPB_I9q.d.cts} +11 -1
- package/dist/shared/{server.CyVIKPsp.d.mts → server.DLPB_I9q.d.mts} +11 -1
- package/dist/shared/{server.CyVIKPsp.d.ts → server.DLPB_I9q.d.ts} +11 -1
- package/dist/worker-node.cjs +14 -6
- package/dist/worker-node.d.cts +2 -1
- package/dist/worker-node.d.mts +2 -1
- package/dist/worker-node.d.ts +2 -1
- package/dist/worker-node.mjs +11 -4
- package/package.json +4 -4
package/dist/fetch-server.cjs
CHANGED
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
const pathe = require('@mokup/shared/pathe');
|
|
4
4
|
const node_fs = require('node:fs');
|
|
5
5
|
const node_module = require('node:module');
|
|
6
|
-
const
|
|
6
|
+
const playgroundGrouping = require('@mokup/shared/playground-grouping');
|
|
7
7
|
const runtime = require('@mokup/runtime');
|
|
8
|
+
const pathUtils = require('@mokup/shared/path-utils');
|
|
9
|
+
const timing = require('@mokup/shared/timing');
|
|
8
10
|
const node_buffer = require('node:buffer');
|
|
9
11
|
const node_url = require('node:url');
|
|
10
12
|
const esbuild = require('@mokup/shared/esbuild');
|
|
13
|
+
const process = require('node:process');
|
|
11
14
|
const jsoncParser = require('@mokup/shared/jsonc-parser');
|
|
12
15
|
const hono = require('@mokup/shared/hono');
|
|
13
16
|
|
|
@@ -117,92 +120,6 @@ async function readPlaygroundAsset(distDir, relPath) {
|
|
|
117
120
|
return new Response(content, { headers: { "Content-Type": contentType } });
|
|
118
121
|
}
|
|
119
122
|
|
|
120
|
-
function toPosixPath(value) {
|
|
121
|
-
return value.replace(/\\/g, "/");
|
|
122
|
-
}
|
|
123
|
-
function normalizePath(value) {
|
|
124
|
-
return toPosixPath(pathe.normalize(value));
|
|
125
|
-
}
|
|
126
|
-
function isAncestor(parent, child) {
|
|
127
|
-
const normalizedParent = normalizePath(parent).replace(/\/$/, "");
|
|
128
|
-
const normalizedChild = normalizePath(child);
|
|
129
|
-
return normalizedChild === normalizedParent || normalizedChild.startsWith(`${normalizedParent}/`);
|
|
130
|
-
}
|
|
131
|
-
function resolveGroupRoot(dirs, serverRoot) {
|
|
132
|
-
if (!dirs || dirs.length === 0) {
|
|
133
|
-
return serverRoot ?? process.cwd();
|
|
134
|
-
}
|
|
135
|
-
if (serverRoot) {
|
|
136
|
-
const normalizedRoot = normalizePath(serverRoot);
|
|
137
|
-
const canUseRoot = dirs.every((dir) => isAncestor(normalizedRoot, dir));
|
|
138
|
-
if (canUseRoot) {
|
|
139
|
-
return normalizedRoot;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (dirs.length === 1) {
|
|
143
|
-
return normalizePath(pathe.dirname(dirs[0]));
|
|
144
|
-
}
|
|
145
|
-
let common = normalizePath(dirs[0]);
|
|
146
|
-
for (const dir of dirs.slice(1)) {
|
|
147
|
-
const normalizedDir = normalizePath(dir);
|
|
148
|
-
while (common && !isAncestor(common, normalizedDir)) {
|
|
149
|
-
const parent = normalizePath(pathe.dirname(common));
|
|
150
|
-
if (parent === common) {
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
common = parent;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
if (!common || common === "/") {
|
|
157
|
-
return serverRoot ?? process.cwd();
|
|
158
|
-
}
|
|
159
|
-
return common;
|
|
160
|
-
}
|
|
161
|
-
function resolveGroups(dirs, root) {
|
|
162
|
-
const groups = [];
|
|
163
|
-
const seen = /* @__PURE__ */ new Set();
|
|
164
|
-
for (const dir of dirs) {
|
|
165
|
-
const normalized = normalizePath(dir);
|
|
166
|
-
if (seen.has(normalized)) {
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
seen.add(normalized);
|
|
170
|
-
const rel = toPosixPath(pathe.relative(root, normalized));
|
|
171
|
-
const label = rel && !rel.startsWith("..") ? rel : normalized;
|
|
172
|
-
groups.push({
|
|
173
|
-
key: normalized,
|
|
174
|
-
label,
|
|
175
|
-
path: normalized
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
return groups;
|
|
179
|
-
}
|
|
180
|
-
function resolveRouteGroup(routeFile, groups) {
|
|
181
|
-
if (groups.length === 0) {
|
|
182
|
-
return void 0;
|
|
183
|
-
}
|
|
184
|
-
const normalizedFile = toPosixPath(pathe.normalize(routeFile));
|
|
185
|
-
let matched;
|
|
186
|
-
for (const group of groups) {
|
|
187
|
-
if (normalizedFile === group.path || normalizedFile.startsWith(`${group.path}/`)) {
|
|
188
|
-
if (!matched || group.path.length > matched.path.length) {
|
|
189
|
-
matched = group;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return matched;
|
|
194
|
-
}
|
|
195
|
-
function formatRouteFile(file, root) {
|
|
196
|
-
if (!root) {
|
|
197
|
-
return toPosixPath(file);
|
|
198
|
-
}
|
|
199
|
-
const rel = toPosixPath(pathe.relative(root, file));
|
|
200
|
-
if (!rel || rel.startsWith("..")) {
|
|
201
|
-
return toPosixPath(file);
|
|
202
|
-
}
|
|
203
|
-
return rel;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
123
|
const disabledReasonSet = /* @__PURE__ */ new Set([
|
|
207
124
|
"disabled",
|
|
208
125
|
"disabled-dir",
|
|
@@ -229,10 +146,10 @@ function normalizeIgnoredReason(reason) {
|
|
|
229
146
|
return "unknown";
|
|
230
147
|
}
|
|
231
148
|
function toPlaygroundRoute(route, root, groups) {
|
|
232
|
-
const matchedGroup = resolveRouteGroup(route.file, groups);
|
|
233
|
-
const preSources = route.middlewares?.filter((entry) => entry.position === "pre").map((entry) => formatRouteFile(entry.source, root)) ?? [];
|
|
234
|
-
const postSources = route.middlewares?.filter((entry) => entry.position === "post").map((entry) => formatRouteFile(entry.source, root)) ?? [];
|
|
235
|
-
const normalSources = route.middlewares?.filter((entry) => entry.position !== "pre" && entry.position !== "post").map((entry) => formatRouteFile(entry.source, root)) ?? [];
|
|
149
|
+
const matchedGroup = playgroundGrouping.resolveRouteGroup(route.file, groups);
|
|
150
|
+
const preSources = route.middlewares?.filter((entry) => entry.position === "pre").map((entry) => playgroundGrouping.formatRouteFile(entry.source, root)) ?? [];
|
|
151
|
+
const postSources = route.middlewares?.filter((entry) => entry.position === "post").map((entry) => playgroundGrouping.formatRouteFile(entry.source, root)) ?? [];
|
|
152
|
+
const normalSources = route.middlewares?.filter((entry) => entry.position !== "pre" && entry.position !== "post").map((entry) => playgroundGrouping.formatRouteFile(entry.source, root)) ?? [];
|
|
236
153
|
const combinedSources = [
|
|
237
154
|
...preSources,
|
|
238
155
|
...normalSources,
|
|
@@ -241,7 +158,7 @@ function toPlaygroundRoute(route, root, groups) {
|
|
|
241
158
|
return {
|
|
242
159
|
method: route.method,
|
|
243
160
|
url: route.template,
|
|
244
|
-
file: formatRouteFile(route.file, root),
|
|
161
|
+
file: playgroundGrouping.formatRouteFile(route.file, root),
|
|
245
162
|
type: typeof route.handler === "function" ? "handler" : "static",
|
|
246
163
|
status: route.status,
|
|
247
164
|
delay: route.delay,
|
|
@@ -258,9 +175,9 @@ function toPlaygroundRoute(route, root, groups) {
|
|
|
258
175
|
};
|
|
259
176
|
}
|
|
260
177
|
function toPlaygroundDisabledRoute(route, root, groups) {
|
|
261
|
-
const matchedGroup = resolveRouteGroup(route.file, groups);
|
|
178
|
+
const matchedGroup = playgroundGrouping.resolveRouteGroup(route.file, groups);
|
|
262
179
|
const disabled = {
|
|
263
|
-
file: formatRouteFile(route.file, root),
|
|
180
|
+
file: playgroundGrouping.formatRouteFile(route.file, root),
|
|
264
181
|
reason: normalizeDisabledReason(route.reason)
|
|
265
182
|
};
|
|
266
183
|
if (typeof route.method !== "undefined") {
|
|
@@ -276,9 +193,9 @@ function toPlaygroundDisabledRoute(route, root, groups) {
|
|
|
276
193
|
return disabled;
|
|
277
194
|
}
|
|
278
195
|
function toPlaygroundIgnoredRoute(route, root, groups) {
|
|
279
|
-
const matchedGroup = resolveRouteGroup(route.file, groups);
|
|
196
|
+
const matchedGroup = playgroundGrouping.resolveRouteGroup(route.file, groups);
|
|
280
197
|
const ignored = {
|
|
281
|
-
file: formatRouteFile(route.file, root),
|
|
198
|
+
file: playgroundGrouping.formatRouteFile(route.file, root),
|
|
282
199
|
reason: normalizeIgnoredReason(route.reason)
|
|
283
200
|
};
|
|
284
201
|
if (matchedGroup) {
|
|
@@ -288,9 +205,9 @@ function toPlaygroundIgnoredRoute(route, root, groups) {
|
|
|
288
205
|
return ignored;
|
|
289
206
|
}
|
|
290
207
|
function toPlaygroundConfigFile(entry, root, groups) {
|
|
291
|
-
const matchedGroup = resolveRouteGroup(entry.file, groups);
|
|
208
|
+
const matchedGroup = playgroundGrouping.resolveRouteGroup(entry.file, groups);
|
|
292
209
|
const configFile = {
|
|
293
|
-
file: formatRouteFile(entry.file, root)
|
|
210
|
+
file: playgroundGrouping.formatRouteFile(entry.file, root)
|
|
294
211
|
};
|
|
295
212
|
if (matchedGroup) {
|
|
296
213
|
configFile.groupKey = matchedGroup.key;
|
|
@@ -327,8 +244,8 @@ function registerPlaygroundRoutes(params) {
|
|
|
327
244
|
params.app.get(`${playgroundPath}/`, () => serveIndex());
|
|
328
245
|
params.app.get(`${playgroundPath}/index.html`, () => serveIndex());
|
|
329
246
|
params.app.get(`${playgroundPath}/routes`, (c) => {
|
|
330
|
-
const baseRoot = resolveGroupRoot(params.dirs, params.root);
|
|
331
|
-
const groups = resolveGroups(params.dirs, baseRoot);
|
|
247
|
+
const baseRoot = playgroundGrouping.resolveGroupRoot(params.dirs, params.root);
|
|
248
|
+
const groups = playgroundGrouping.resolveGroups(params.dirs, baseRoot);
|
|
332
249
|
return c.json({
|
|
333
250
|
basePath: playgroundPath,
|
|
334
251
|
root: baseRoot,
|
|
@@ -394,59 +311,10 @@ function resolveDirs(dir, root) {
|
|
|
394
311
|
);
|
|
395
312
|
return Array.from(new Set(normalized));
|
|
396
313
|
}
|
|
397
|
-
function createDebouncer(delayMs, fn) {
|
|
398
|
-
let timer = null;
|
|
399
|
-
return () => {
|
|
400
|
-
if (timer) {
|
|
401
|
-
clearTimeout(timer);
|
|
402
|
-
}
|
|
403
|
-
timer = setTimeout(() => {
|
|
404
|
-
timer = null;
|
|
405
|
-
fn();
|
|
406
|
-
}, delayMs);
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
function toPosix(value) {
|
|
410
|
-
return value.replace(/\\/g, "/");
|
|
411
|
-
}
|
|
412
|
-
function isInDirs(file, dirs) {
|
|
413
|
-
const normalized = toPosix(file);
|
|
414
|
-
return dirs.some((dir) => {
|
|
415
|
-
const normalizedDir = toPosix(dir).replace(/\/$/, "");
|
|
416
|
-
return normalized === normalizedDir || normalized.startsWith(`${normalizedDir}/`);
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
function testPatterns(patterns, value) {
|
|
420
|
-
const list = Array.isArray(patterns) ? patterns : [patterns];
|
|
421
|
-
return list.some((pattern) => pattern.test(value));
|
|
422
|
-
}
|
|
423
|
-
function matchesFilter(file, include, exclude) {
|
|
424
|
-
const normalized = toPosix(file);
|
|
425
|
-
if (exclude && testPatterns(exclude, normalized)) {
|
|
426
|
-
return false;
|
|
427
|
-
}
|
|
428
|
-
if (include) {
|
|
429
|
-
return testPatterns(include, normalized);
|
|
430
|
-
}
|
|
431
|
-
return true;
|
|
432
|
-
}
|
|
433
314
|
function normalizeIgnorePrefix(value, fallback = ["."]) {
|
|
434
315
|
const list = typeof value === "undefined" ? fallback : Array.isArray(value) ? value : [value];
|
|
435
316
|
return list.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
436
317
|
}
|
|
437
|
-
function hasIgnoredPrefix(file, rootDir, prefixes) {
|
|
438
|
-
if (prefixes.length === 0) {
|
|
439
|
-
return false;
|
|
440
|
-
}
|
|
441
|
-
const relativePath = toPosix(pathe.relative(rootDir, file));
|
|
442
|
-
const segments = relativePath.split("/");
|
|
443
|
-
return segments.some(
|
|
444
|
-
(segment) => prefixes.some((prefix) => segment.startsWith(prefix))
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
function delay(ms) {
|
|
448
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
449
|
-
}
|
|
450
318
|
|
|
451
319
|
const jsonExtensions = /* @__PURE__ */ new Set([".json", ".jsonc"]);
|
|
452
320
|
function resolveTemplate(template, prefix) {
|
|
@@ -482,7 +350,7 @@ function stripMethodSuffix(base) {
|
|
|
482
350
|
};
|
|
483
351
|
}
|
|
484
352
|
function deriveRouteFromFile(file, rootDir, logger) {
|
|
485
|
-
const rel = toPosix(pathe.relative(rootDir, file));
|
|
353
|
+
const rel = pathUtils.toPosix(pathe.relative(rootDir, file));
|
|
486
354
|
const ext = pathe.extname(rel);
|
|
487
355
|
const withoutExt = rel.slice(0, rel.length - ext.length);
|
|
488
356
|
const dir = pathe.dirname(withoutExt);
|
|
@@ -498,7 +366,7 @@ function deriveRouteFromFile(file, rootDir, logger) {
|
|
|
498
366
|
return null;
|
|
499
367
|
}
|
|
500
368
|
const joined = dir === "." ? name : pathe.join(dir, name);
|
|
501
|
-
const segments = toPosix(joined).split("/");
|
|
369
|
+
const segments = pathUtils.toPosix(joined).split("/");
|
|
502
370
|
if (segments.at(-1) === "index") {
|
|
503
371
|
segments.pop();
|
|
504
372
|
}
|
|
@@ -681,12 +549,16 @@ async function loadConfig(file, logger) {
|
|
|
681
549
|
if (!mod) {
|
|
682
550
|
return null;
|
|
683
551
|
}
|
|
684
|
-
const
|
|
552
|
+
const raw = mod?.default ?? mod;
|
|
553
|
+
const value = isPromise(raw) ? await raw : raw;
|
|
685
554
|
if (!value || typeof value !== "object") {
|
|
686
555
|
return null;
|
|
687
556
|
}
|
|
688
557
|
return value;
|
|
689
558
|
}
|
|
559
|
+
function isPromise(value) {
|
|
560
|
+
return !!value && typeof value.then === "function";
|
|
561
|
+
}
|
|
690
562
|
function normalizeMiddlewares(value, source, logger, position) {
|
|
691
563
|
if (!value) {
|
|
692
564
|
return [];
|
|
@@ -854,26 +726,26 @@ async function collectFiles(dirs) {
|
|
|
854
726
|
}
|
|
855
727
|
return files;
|
|
856
728
|
}
|
|
857
|
-
function
|
|
729
|
+
function isConfigFile(file) {
|
|
858
730
|
if (file.endsWith(".d.ts")) {
|
|
859
731
|
return false;
|
|
860
732
|
}
|
|
861
|
-
|
|
733
|
+
const base = pathe.basename(file);
|
|
734
|
+
if (!base.startsWith("index.config.")) {
|
|
862
735
|
return false;
|
|
863
736
|
}
|
|
864
737
|
const ext = pathe.extname(file).toLowerCase();
|
|
865
|
-
return
|
|
738
|
+
return configExtensions.includes(ext);
|
|
866
739
|
}
|
|
867
|
-
function
|
|
740
|
+
function isSupportedFile(file) {
|
|
868
741
|
if (file.endsWith(".d.ts")) {
|
|
869
742
|
return false;
|
|
870
743
|
}
|
|
871
|
-
|
|
872
|
-
if (!base.startsWith("index.config.")) {
|
|
744
|
+
if (isConfigFile(file)) {
|
|
873
745
|
return false;
|
|
874
746
|
}
|
|
875
747
|
const ext = pathe.extname(file).toLowerCase();
|
|
876
|
-
return
|
|
748
|
+
return supportedExtensions.has(ext);
|
|
877
749
|
}
|
|
878
750
|
|
|
879
751
|
function isUnknownFileExtensionError(error) {
|
|
@@ -1062,7 +934,7 @@ async function scanRoutes(params) {
|
|
|
1062
934
|
continue;
|
|
1063
935
|
}
|
|
1064
936
|
const effectiveIgnorePrefix = typeof config.ignorePrefix !== "undefined" ? normalizeIgnorePrefix(config.ignorePrefix, []) : globalIgnorePrefix;
|
|
1065
|
-
if (hasIgnoredPrefix(fileInfo.file, fileInfo.rootDir, effectiveIgnorePrefix)) {
|
|
937
|
+
if (pathUtils.hasIgnoredPrefix(fileInfo.file, fileInfo.rootDir, effectiveIgnorePrefix)) {
|
|
1066
938
|
if (shouldCollectSkip && isSupportedFile(fileInfo.file)) {
|
|
1067
939
|
const resolved = resolveSkipRoute({
|
|
1068
940
|
file: fileInfo.file,
|
|
@@ -1081,14 +953,14 @@ async function scanRoutes(params) {
|
|
|
1081
953
|
}
|
|
1082
954
|
const effectiveInclude = typeof config.include !== "undefined" ? config.include : params.include;
|
|
1083
955
|
const effectiveExclude = typeof config.exclude !== "undefined" ? config.exclude : params.exclude;
|
|
1084
|
-
if (!matchesFilter(fileInfo.file, effectiveInclude, effectiveExclude)) {
|
|
956
|
+
if (!pathUtils.matchesFilter(fileInfo.file, effectiveInclude, effectiveExclude)) {
|
|
1085
957
|
if (shouldCollectSkip) {
|
|
1086
958
|
const resolved = resolveSkipRoute({
|
|
1087
959
|
file: fileInfo.file,
|
|
1088
960
|
rootDir: fileInfo.rootDir,
|
|
1089
961
|
prefix: params.prefix
|
|
1090
962
|
});
|
|
1091
|
-
const reason = effectiveExclude && matchesFilter(fileInfo.file, void 0, effectiveExclude) ? "exclude" : "include";
|
|
963
|
+
const reason = effectiveExclude && pathUtils.matchesFilter(fileInfo.file, void 0, effectiveExclude) ? "exclude" : "include";
|
|
1092
964
|
params.onSkip?.(buildSkipInfo(fileInfo.file, reason, resolved));
|
|
1093
965
|
}
|
|
1094
966
|
continue;
|
|
@@ -1259,7 +1131,7 @@ function createFinalizeMiddleware(route, onResponse) {
|
|
|
1259
1131
|
const response = await next();
|
|
1260
1132
|
const resolved = resolveResponse(response, c.res);
|
|
1261
1133
|
if (route.delay && route.delay > 0) {
|
|
1262
|
-
await delay(route.delay);
|
|
1134
|
+
await timing.delay(route.delay);
|
|
1263
1135
|
}
|
|
1264
1136
|
const overridden = applyRouteOverrides(resolved, route);
|
|
1265
1137
|
c.res = overridden;
|
|
@@ -1495,17 +1367,17 @@ async function createChokidarWatcher(params) {
|
|
|
1495
1367
|
const { default: chokidar } = await import('@mokup/shared/chokidar');
|
|
1496
1368
|
const watcher = chokidar.watch(params.dirs, { ignoreInitial: true });
|
|
1497
1369
|
watcher.on("add", (file) => {
|
|
1498
|
-
if (isInDirs(file, params.dirs)) {
|
|
1370
|
+
if (pathUtils.isInDirs(file, params.dirs)) {
|
|
1499
1371
|
params.onChange();
|
|
1500
1372
|
}
|
|
1501
1373
|
});
|
|
1502
1374
|
watcher.on("change", (file) => {
|
|
1503
|
-
if (isInDirs(file, params.dirs)) {
|
|
1375
|
+
if (pathUtils.isInDirs(file, params.dirs)) {
|
|
1504
1376
|
params.onChange();
|
|
1505
1377
|
}
|
|
1506
1378
|
});
|
|
1507
1379
|
watcher.on("unlink", (file) => {
|
|
1508
|
-
if (isInDirs(file, params.dirs)) {
|
|
1380
|
+
if (pathUtils.isInDirs(file, params.dirs)) {
|
|
1509
1381
|
params.onChange();
|
|
1510
1382
|
}
|
|
1511
1383
|
});
|
|
@@ -1629,7 +1501,7 @@ async function createFetchServer(options = {}) {
|
|
|
1629
1501
|
if (wsHandler && playgroundConfig.enabled) {
|
|
1630
1502
|
app.get(`${playgroundConfig.path}/ws`, wsHandler);
|
|
1631
1503
|
}
|
|
1632
|
-
const scheduleRefresh = createDebouncer(80, () => {
|
|
1504
|
+
const scheduleRefresh = timing.createDebouncer(80, () => {
|
|
1633
1505
|
void refreshRoutes();
|
|
1634
1506
|
});
|
|
1635
1507
|
const watcher = await createWatcher({
|
package/dist/fetch-server.d.cts
CHANGED
package/dist/fetch-server.d.mts
CHANGED
package/dist/fetch-server.d.ts
CHANGED
package/dist/fetch-server.mjs
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { join, normalize, extname,
|
|
1
|
+
import { join, normalize, extname, resolve, isAbsolute, relative, dirname, basename } from '@mokup/shared/pathe';
|
|
2
2
|
import { promises } from 'node:fs';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
|
-
import
|
|
4
|
+
import { resolveRouteGroup, formatRouteFile, resolveGroupRoot, resolveGroups } from '@mokup/shared/playground-grouping';
|
|
5
5
|
import { compareRouteScore, parseRouteTemplate } from '@mokup/runtime';
|
|
6
|
+
import { toPosix, hasIgnoredPrefix, matchesFilter, isInDirs } from '@mokup/shared/path-utils';
|
|
7
|
+
import { delay, createDebouncer } from '@mokup/shared/timing';
|
|
6
8
|
import { Buffer } from 'node:buffer';
|
|
7
9
|
import { pathToFileURL } from 'node:url';
|
|
8
10
|
import { build } from '@mokup/shared/esbuild';
|
|
11
|
+
import process, { cwd } from 'node:process';
|
|
9
12
|
import { parse } from '@mokup/shared/jsonc-parser';
|
|
10
13
|
import { Hono, PatternRouter } from '@mokup/shared/hono';
|
|
11
14
|
|
|
@@ -110,92 +113,6 @@ async function readPlaygroundAsset(distDir, relPath) {
|
|
|
110
113
|
return new Response(content, { headers: { "Content-Type": contentType } });
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
function toPosixPath(value) {
|
|
114
|
-
return value.replace(/\\/g, "/");
|
|
115
|
-
}
|
|
116
|
-
function normalizePath(value) {
|
|
117
|
-
return toPosixPath(normalize(value));
|
|
118
|
-
}
|
|
119
|
-
function isAncestor(parent, child) {
|
|
120
|
-
const normalizedParent = normalizePath(parent).replace(/\/$/, "");
|
|
121
|
-
const normalizedChild = normalizePath(child);
|
|
122
|
-
return normalizedChild === normalizedParent || normalizedChild.startsWith(`${normalizedParent}/`);
|
|
123
|
-
}
|
|
124
|
-
function resolveGroupRoot(dirs, serverRoot) {
|
|
125
|
-
if (!dirs || dirs.length === 0) {
|
|
126
|
-
return serverRoot ?? cwd();
|
|
127
|
-
}
|
|
128
|
-
if (serverRoot) {
|
|
129
|
-
const normalizedRoot = normalizePath(serverRoot);
|
|
130
|
-
const canUseRoot = dirs.every((dir) => isAncestor(normalizedRoot, dir));
|
|
131
|
-
if (canUseRoot) {
|
|
132
|
-
return normalizedRoot;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (dirs.length === 1) {
|
|
136
|
-
return normalizePath(dirname(dirs[0]));
|
|
137
|
-
}
|
|
138
|
-
let common = normalizePath(dirs[0]);
|
|
139
|
-
for (const dir of dirs.slice(1)) {
|
|
140
|
-
const normalizedDir = normalizePath(dir);
|
|
141
|
-
while (common && !isAncestor(common, normalizedDir)) {
|
|
142
|
-
const parent = normalizePath(dirname(common));
|
|
143
|
-
if (parent === common) {
|
|
144
|
-
break;
|
|
145
|
-
}
|
|
146
|
-
common = parent;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
if (!common || common === "/") {
|
|
150
|
-
return serverRoot ?? cwd();
|
|
151
|
-
}
|
|
152
|
-
return common;
|
|
153
|
-
}
|
|
154
|
-
function resolveGroups(dirs, root) {
|
|
155
|
-
const groups = [];
|
|
156
|
-
const seen = /* @__PURE__ */ new Set();
|
|
157
|
-
for (const dir of dirs) {
|
|
158
|
-
const normalized = normalizePath(dir);
|
|
159
|
-
if (seen.has(normalized)) {
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
seen.add(normalized);
|
|
163
|
-
const rel = toPosixPath(relative(root, normalized));
|
|
164
|
-
const label = rel && !rel.startsWith("..") ? rel : normalized;
|
|
165
|
-
groups.push({
|
|
166
|
-
key: normalized,
|
|
167
|
-
label,
|
|
168
|
-
path: normalized
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
return groups;
|
|
172
|
-
}
|
|
173
|
-
function resolveRouteGroup(routeFile, groups) {
|
|
174
|
-
if (groups.length === 0) {
|
|
175
|
-
return void 0;
|
|
176
|
-
}
|
|
177
|
-
const normalizedFile = toPosixPath(normalize(routeFile));
|
|
178
|
-
let matched;
|
|
179
|
-
for (const group of groups) {
|
|
180
|
-
if (normalizedFile === group.path || normalizedFile.startsWith(`${group.path}/`)) {
|
|
181
|
-
if (!matched || group.path.length > matched.path.length) {
|
|
182
|
-
matched = group;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return matched;
|
|
187
|
-
}
|
|
188
|
-
function formatRouteFile(file, root) {
|
|
189
|
-
if (!root) {
|
|
190
|
-
return toPosixPath(file);
|
|
191
|
-
}
|
|
192
|
-
const rel = toPosixPath(relative(root, file));
|
|
193
|
-
if (!rel || rel.startsWith("..")) {
|
|
194
|
-
return toPosixPath(file);
|
|
195
|
-
}
|
|
196
|
-
return rel;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
116
|
const disabledReasonSet = /* @__PURE__ */ new Set([
|
|
200
117
|
"disabled",
|
|
201
118
|
"disabled-dir",
|
|
@@ -387,59 +304,10 @@ function resolveDirs(dir, root) {
|
|
|
387
304
|
);
|
|
388
305
|
return Array.from(new Set(normalized));
|
|
389
306
|
}
|
|
390
|
-
function createDebouncer(delayMs, fn) {
|
|
391
|
-
let timer = null;
|
|
392
|
-
return () => {
|
|
393
|
-
if (timer) {
|
|
394
|
-
clearTimeout(timer);
|
|
395
|
-
}
|
|
396
|
-
timer = setTimeout(() => {
|
|
397
|
-
timer = null;
|
|
398
|
-
fn();
|
|
399
|
-
}, delayMs);
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
function toPosix(value) {
|
|
403
|
-
return value.replace(/\\/g, "/");
|
|
404
|
-
}
|
|
405
|
-
function isInDirs(file, dirs) {
|
|
406
|
-
const normalized = toPosix(file);
|
|
407
|
-
return dirs.some((dir) => {
|
|
408
|
-
const normalizedDir = toPosix(dir).replace(/\/$/, "");
|
|
409
|
-
return normalized === normalizedDir || normalized.startsWith(`${normalizedDir}/`);
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
function testPatterns(patterns, value) {
|
|
413
|
-
const list = Array.isArray(patterns) ? patterns : [patterns];
|
|
414
|
-
return list.some((pattern) => pattern.test(value));
|
|
415
|
-
}
|
|
416
|
-
function matchesFilter(file, include, exclude) {
|
|
417
|
-
const normalized = toPosix(file);
|
|
418
|
-
if (exclude && testPatterns(exclude, normalized)) {
|
|
419
|
-
return false;
|
|
420
|
-
}
|
|
421
|
-
if (include) {
|
|
422
|
-
return testPatterns(include, normalized);
|
|
423
|
-
}
|
|
424
|
-
return true;
|
|
425
|
-
}
|
|
426
307
|
function normalizeIgnorePrefix(value, fallback = ["."]) {
|
|
427
308
|
const list = typeof value === "undefined" ? fallback : Array.isArray(value) ? value : [value];
|
|
428
309
|
return list.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
429
310
|
}
|
|
430
|
-
function hasIgnoredPrefix(file, rootDir, prefixes) {
|
|
431
|
-
if (prefixes.length === 0) {
|
|
432
|
-
return false;
|
|
433
|
-
}
|
|
434
|
-
const relativePath = toPosix(relative(rootDir, file));
|
|
435
|
-
const segments = relativePath.split("/");
|
|
436
|
-
return segments.some(
|
|
437
|
-
(segment) => prefixes.some((prefix) => segment.startsWith(prefix))
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
function delay(ms) {
|
|
441
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
442
|
-
}
|
|
443
311
|
|
|
444
312
|
const jsonExtensions = /* @__PURE__ */ new Set([".json", ".jsonc"]);
|
|
445
313
|
function resolveTemplate(template, prefix) {
|
|
@@ -674,12 +542,16 @@ async function loadConfig(file, logger) {
|
|
|
674
542
|
if (!mod) {
|
|
675
543
|
return null;
|
|
676
544
|
}
|
|
677
|
-
const
|
|
545
|
+
const raw = mod?.default ?? mod;
|
|
546
|
+
const value = isPromise(raw) ? await raw : raw;
|
|
678
547
|
if (!value || typeof value !== "object") {
|
|
679
548
|
return null;
|
|
680
549
|
}
|
|
681
550
|
return value;
|
|
682
551
|
}
|
|
552
|
+
function isPromise(value) {
|
|
553
|
+
return !!value && typeof value.then === "function";
|
|
554
|
+
}
|
|
683
555
|
function normalizeMiddlewares(value, source, logger, position) {
|
|
684
556
|
if (!value) {
|
|
685
557
|
return [];
|
|
@@ -847,26 +719,26 @@ async function collectFiles(dirs) {
|
|
|
847
719
|
}
|
|
848
720
|
return files;
|
|
849
721
|
}
|
|
850
|
-
function
|
|
722
|
+
function isConfigFile(file) {
|
|
851
723
|
if (file.endsWith(".d.ts")) {
|
|
852
724
|
return false;
|
|
853
725
|
}
|
|
854
|
-
|
|
726
|
+
const base = basename(file);
|
|
727
|
+
if (!base.startsWith("index.config.")) {
|
|
855
728
|
return false;
|
|
856
729
|
}
|
|
857
730
|
const ext = extname(file).toLowerCase();
|
|
858
|
-
return
|
|
731
|
+
return configExtensions.includes(ext);
|
|
859
732
|
}
|
|
860
|
-
function
|
|
733
|
+
function isSupportedFile(file) {
|
|
861
734
|
if (file.endsWith(".d.ts")) {
|
|
862
735
|
return false;
|
|
863
736
|
}
|
|
864
|
-
|
|
865
|
-
if (!base.startsWith("index.config.")) {
|
|
737
|
+
if (isConfigFile(file)) {
|
|
866
738
|
return false;
|
|
867
739
|
}
|
|
868
740
|
const ext = extname(file).toLowerCase();
|
|
869
|
-
return
|
|
741
|
+
return supportedExtensions.has(ext);
|
|
870
742
|
}
|
|
871
743
|
|
|
872
744
|
function isUnknownFileExtensionError(error) {
|