@mokup/server 1.0.3 → 1.1.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/index.cjs +288 -52
- package/dist/index.d.cts +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +282 -50
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const runtime = require('@mokup/runtime');
|
|
4
4
|
const fetch = require('./shared/server.BdTl0qJd.cjs');
|
|
5
|
-
const
|
|
5
|
+
const process = require('node:process');
|
|
6
6
|
const hono = require('@mokup/shared/hono');
|
|
7
7
|
const pathe = require('@mokup/shared/pathe');
|
|
8
8
|
const node_fs = require('node:fs');
|
|
@@ -13,6 +13,10 @@ const esbuild = require('@mokup/shared/esbuild');
|
|
|
13
13
|
const jsoncParser = require('@mokup/shared/jsonc-parser');
|
|
14
14
|
|
|
15
15
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
16
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
17
|
+
|
|
18
|
+
const process__default = /*#__PURE__*/_interopDefaultCompat(process);
|
|
19
|
+
|
|
16
20
|
function createConnectMiddleware(options) {
|
|
17
21
|
const runtime$1 = runtime.createRuntime(fetch.toRuntimeOptions(options));
|
|
18
22
|
const onNotFound = options.onNotFound ?? "next";
|
|
@@ -141,6 +145,20 @@ function matchesFilter(file, include, exclude) {
|
|
|
141
145
|
}
|
|
142
146
|
return true;
|
|
143
147
|
}
|
|
148
|
+
function normalizeIgnorePrefix(value, fallback = ["."]) {
|
|
149
|
+
const list = typeof value === "undefined" ? fallback : Array.isArray(value) ? value : [value];
|
|
150
|
+
return list.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
151
|
+
}
|
|
152
|
+
function hasIgnoredPrefix(file, rootDir, prefixes) {
|
|
153
|
+
if (prefixes.length === 0) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
const relativePath = toPosix(pathe.relative(rootDir, file));
|
|
157
|
+
const segments = relativePath.split("/");
|
|
158
|
+
return segments.some(
|
|
159
|
+
(segment) => prefixes.some((prefix) => segment.startsWith(prefix))
|
|
160
|
+
);
|
|
161
|
+
}
|
|
144
162
|
function delay(ms) {
|
|
145
163
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
146
164
|
}
|
|
@@ -344,7 +362,7 @@ function isAncestor(parent, child) {
|
|
|
344
362
|
}
|
|
345
363
|
function resolveGroupRoot(dirs, serverRoot) {
|
|
346
364
|
if (!dirs || dirs.length === 0) {
|
|
347
|
-
return serverRoot ??
|
|
365
|
+
return serverRoot ?? process.cwd();
|
|
348
366
|
}
|
|
349
367
|
if (serverRoot) {
|
|
350
368
|
const normalizedRoot = normalizePath(serverRoot);
|
|
@@ -368,10 +386,24 @@ function resolveGroupRoot(dirs, serverRoot) {
|
|
|
368
386
|
}
|
|
369
387
|
}
|
|
370
388
|
if (!common || common === "/") {
|
|
371
|
-
return serverRoot ??
|
|
389
|
+
return serverRoot ?? process.cwd();
|
|
372
390
|
}
|
|
373
391
|
return common;
|
|
374
392
|
}
|
|
393
|
+
const disabledReasonSet = /* @__PURE__ */ new Set([
|
|
394
|
+
"disabled",
|
|
395
|
+
"disabled-dir",
|
|
396
|
+
"exclude",
|
|
397
|
+
"ignore-prefix",
|
|
398
|
+
"include",
|
|
399
|
+
"unknown"
|
|
400
|
+
]);
|
|
401
|
+
function normalizeDisabledReason(reason) {
|
|
402
|
+
if (reason && disabledReasonSet.has(reason)) {
|
|
403
|
+
return reason;
|
|
404
|
+
}
|
|
405
|
+
return "unknown";
|
|
406
|
+
}
|
|
375
407
|
function formatRouteFile(file, root) {
|
|
376
408
|
if (!root) {
|
|
377
409
|
return toPosixPath(file);
|
|
@@ -432,6 +464,24 @@ function toPlaygroundRoute(route, root, groups) {
|
|
|
432
464
|
group: matchedGroup?.label
|
|
433
465
|
};
|
|
434
466
|
}
|
|
467
|
+
function toPlaygroundDisabledRoute(route, root, groups) {
|
|
468
|
+
const matchedGroup = resolveRouteGroup(route.file, groups);
|
|
469
|
+
const disabled = {
|
|
470
|
+
file: formatRouteFile(route.file, root),
|
|
471
|
+
reason: normalizeDisabledReason(route.reason)
|
|
472
|
+
};
|
|
473
|
+
if (typeof route.method !== "undefined") {
|
|
474
|
+
disabled.method = route.method;
|
|
475
|
+
}
|
|
476
|
+
if (typeof route.url !== "undefined") {
|
|
477
|
+
disabled.url = route.url;
|
|
478
|
+
}
|
|
479
|
+
if (matchedGroup) {
|
|
480
|
+
disabled.groupKey = matchedGroup.key;
|
|
481
|
+
disabled.group = matchedGroup.label;
|
|
482
|
+
}
|
|
483
|
+
return disabled;
|
|
484
|
+
}
|
|
435
485
|
function registerPlaygroundRoutes(params) {
|
|
436
486
|
if (!params.config.enabled) {
|
|
437
487
|
return;
|
|
@@ -469,7 +519,8 @@ function registerPlaygroundRoutes(params) {
|
|
|
469
519
|
root: baseRoot,
|
|
470
520
|
count: params.routes.length,
|
|
471
521
|
groups: groups.map((group) => ({ key: group.key, label: group.label })),
|
|
472
|
-
routes: params.routes.map((route) => toPlaygroundRoute(route, baseRoot, groups))
|
|
522
|
+
routes: params.routes.map((route) => toPlaygroundRoute(route, baseRoot, groups)),
|
|
523
|
+
disabled: (params.disabledRoutes ?? []).map((route) => toPlaygroundDisabledRoute(route, baseRoot, groups))
|
|
473
524
|
});
|
|
474
525
|
});
|
|
475
526
|
params.app.get(`${playgroundPath}/*`, async (c) => {
|
|
@@ -615,8 +666,79 @@ function sortRoutes(routes) {
|
|
|
615
666
|
});
|
|
616
667
|
}
|
|
617
668
|
|
|
669
|
+
let registerPromise = null;
|
|
670
|
+
let hasLoggedFailure = false;
|
|
671
|
+
async function ensureTsxRegister(logger) {
|
|
672
|
+
if (registerPromise) {
|
|
673
|
+
return registerPromise;
|
|
674
|
+
}
|
|
675
|
+
registerPromise = (async () => {
|
|
676
|
+
try {
|
|
677
|
+
const mod = await import('tsx/esm/api');
|
|
678
|
+
const setSourceMapsEnabled = process__default.setSourceMapsEnabled;
|
|
679
|
+
if (typeof setSourceMapsEnabled === "function") {
|
|
680
|
+
setSourceMapsEnabled(true);
|
|
681
|
+
}
|
|
682
|
+
if (typeof mod.register === "function") {
|
|
683
|
+
mod.register();
|
|
684
|
+
}
|
|
685
|
+
return true;
|
|
686
|
+
} catch (error) {
|
|
687
|
+
if (!hasLoggedFailure && logger) {
|
|
688
|
+
logger.warn(
|
|
689
|
+
"Failed to register tsx loader; falling back to bundled TS loader.",
|
|
690
|
+
error
|
|
691
|
+
);
|
|
692
|
+
hasLoggedFailure = true;
|
|
693
|
+
}
|
|
694
|
+
return false;
|
|
695
|
+
}
|
|
696
|
+
})();
|
|
697
|
+
return registerPromise;
|
|
698
|
+
}
|
|
699
|
+
|
|
618
700
|
const configExtensions = [".ts", ".js", ".mjs", ".cjs"];
|
|
619
|
-
|
|
701
|
+
function isUnknownFileExtensionError$1(error) {
|
|
702
|
+
if (!error || typeof error !== "object") {
|
|
703
|
+
return false;
|
|
704
|
+
}
|
|
705
|
+
const code = error.code;
|
|
706
|
+
if (code === "ERR_UNKNOWN_FILE_EXTENSION") {
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
const message = error.message;
|
|
710
|
+
return typeof message === "string" && message.includes("Unknown file extension");
|
|
711
|
+
}
|
|
712
|
+
async function loadTsModule$1(file, logger) {
|
|
713
|
+
const cacheBust = Date.now();
|
|
714
|
+
const fileUrl = `${node_url.pathToFileURL(file).href}?t=${cacheBust}`;
|
|
715
|
+
const registered = await ensureTsxRegister(logger);
|
|
716
|
+
if (registered) {
|
|
717
|
+
try {
|
|
718
|
+
return await import(fileUrl);
|
|
719
|
+
} catch (error) {
|
|
720
|
+
if (!isUnknownFileExtensionError$1(error)) {
|
|
721
|
+
throw error;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
const result = await esbuild.build({
|
|
726
|
+
entryPoints: [file],
|
|
727
|
+
bundle: true,
|
|
728
|
+
format: "esm",
|
|
729
|
+
platform: "node",
|
|
730
|
+
sourcemap: "inline",
|
|
731
|
+
target: "es2020",
|
|
732
|
+
write: false
|
|
733
|
+
});
|
|
734
|
+
const output = result.outputFiles[0];
|
|
735
|
+
const code = output?.text ?? "";
|
|
736
|
+
const dataUrl = `data:text/javascript;base64,${node_buffer.Buffer.from(code).toString(
|
|
737
|
+
"base64"
|
|
738
|
+
)}`;
|
|
739
|
+
return import(`${dataUrl}#${cacheBust}`);
|
|
740
|
+
}
|
|
741
|
+
async function loadModule$1(file, logger) {
|
|
620
742
|
const ext = configExtensions.find((extension) => file.endsWith(extension));
|
|
621
743
|
if (ext === ".cjs") {
|
|
622
744
|
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
@@ -627,21 +749,7 @@ async function loadModule$1(file) {
|
|
|
627
749
|
return import(`${node_url.pathToFileURL(file).href}?t=${Date.now()}`);
|
|
628
750
|
}
|
|
629
751
|
if (ext === ".ts") {
|
|
630
|
-
|
|
631
|
-
entryPoints: [file],
|
|
632
|
-
bundle: true,
|
|
633
|
-
format: "esm",
|
|
634
|
-
platform: "node",
|
|
635
|
-
sourcemap: "inline",
|
|
636
|
-
target: "es2020",
|
|
637
|
-
write: false
|
|
638
|
-
});
|
|
639
|
-
const output = result.outputFiles[0];
|
|
640
|
-
const code = output?.text ?? "";
|
|
641
|
-
const dataUrl = `data:text/javascript;base64,${node_buffer.Buffer.from(code).toString(
|
|
642
|
-
"base64"
|
|
643
|
-
)}`;
|
|
644
|
-
return import(`${dataUrl}#${Date.now()}`);
|
|
752
|
+
return loadTsModule$1(file, logger);
|
|
645
753
|
}
|
|
646
754
|
return null;
|
|
647
755
|
}
|
|
@@ -665,8 +773,8 @@ async function findConfigFile(dir, cache) {
|
|
|
665
773
|
cache.set(dir, null);
|
|
666
774
|
return null;
|
|
667
775
|
}
|
|
668
|
-
async function loadConfig(file) {
|
|
669
|
-
const mod = await loadModule$1(file);
|
|
776
|
+
async function loadConfig(file, logger) {
|
|
777
|
+
const mod = await loadModule$1(file, logger);
|
|
670
778
|
if (!mod) {
|
|
671
779
|
return null;
|
|
672
780
|
}
|
|
@@ -717,7 +825,7 @@ async function resolveDirectoryConfig(params) {
|
|
|
717
825
|
}
|
|
718
826
|
let config = configCache.get(configPath);
|
|
719
827
|
if (config === void 0) {
|
|
720
|
-
config = await loadConfig(configPath);
|
|
828
|
+
config = await loadConfig(configPath, logger);
|
|
721
829
|
configCache.set(configPath, config);
|
|
722
830
|
}
|
|
723
831
|
if (!config) {
|
|
@@ -736,6 +844,15 @@ async function resolveDirectoryConfig(params) {
|
|
|
736
844
|
if (typeof config.enabled === "boolean") {
|
|
737
845
|
merged.enabled = config.enabled;
|
|
738
846
|
}
|
|
847
|
+
if (typeof config.ignorePrefix !== "undefined") {
|
|
848
|
+
merged.ignorePrefix = config.ignorePrefix;
|
|
849
|
+
}
|
|
850
|
+
if (typeof config.include !== "undefined") {
|
|
851
|
+
merged.include = config.include;
|
|
852
|
+
}
|
|
853
|
+
if (typeof config.exclude !== "undefined") {
|
|
854
|
+
merged.exclude = config.exclude;
|
|
855
|
+
}
|
|
739
856
|
const normalized = normalizeMiddlewares(config.middleware, configPath, logger);
|
|
740
857
|
if (normalized.length > 0) {
|
|
741
858
|
merged.middlewares.push(...normalized);
|
|
@@ -789,7 +906,47 @@ function isSupportedFile(file) {
|
|
|
789
906
|
return supportedExtensions.has(ext);
|
|
790
907
|
}
|
|
791
908
|
|
|
792
|
-
|
|
909
|
+
function isUnknownFileExtensionError(error) {
|
|
910
|
+
if (!error || typeof error !== "object") {
|
|
911
|
+
return false;
|
|
912
|
+
}
|
|
913
|
+
const code = error.code;
|
|
914
|
+
if (code === "ERR_UNKNOWN_FILE_EXTENSION") {
|
|
915
|
+
return true;
|
|
916
|
+
}
|
|
917
|
+
const message = error.message;
|
|
918
|
+
return typeof message === "string" && message.includes("Unknown file extension");
|
|
919
|
+
}
|
|
920
|
+
async function loadTsModule(file, logger) {
|
|
921
|
+
const cacheBust = Date.now();
|
|
922
|
+
const fileUrl = `${node_url.pathToFileURL(file).href}?t=${cacheBust}`;
|
|
923
|
+
const registered = await ensureTsxRegister(logger);
|
|
924
|
+
if (registered) {
|
|
925
|
+
try {
|
|
926
|
+
return await import(fileUrl);
|
|
927
|
+
} catch (error) {
|
|
928
|
+
if (!isUnknownFileExtensionError(error)) {
|
|
929
|
+
throw error;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
const result = await esbuild.build({
|
|
934
|
+
entryPoints: [file],
|
|
935
|
+
bundle: true,
|
|
936
|
+
format: "esm",
|
|
937
|
+
platform: "node",
|
|
938
|
+
sourcemap: "inline",
|
|
939
|
+
target: "es2020",
|
|
940
|
+
write: false
|
|
941
|
+
});
|
|
942
|
+
const output = result.outputFiles[0];
|
|
943
|
+
const code = output?.text ?? "";
|
|
944
|
+
const dataUrl = `data:text/javascript;base64,${node_buffer.Buffer.from(code).toString(
|
|
945
|
+
"base64"
|
|
946
|
+
)}`;
|
|
947
|
+
return import(`${dataUrl}#${cacheBust}`);
|
|
948
|
+
}
|
|
949
|
+
async function loadModule(file, logger) {
|
|
793
950
|
const ext = pathe.extname(file).toLowerCase();
|
|
794
951
|
if (ext === ".cjs") {
|
|
795
952
|
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
@@ -800,21 +957,7 @@ async function loadModule(file) {
|
|
|
800
957
|
return import(`${node_url.pathToFileURL(file).href}?t=${Date.now()}`);
|
|
801
958
|
}
|
|
802
959
|
if (ext === ".ts") {
|
|
803
|
-
|
|
804
|
-
entryPoints: [file],
|
|
805
|
-
bundle: true,
|
|
806
|
-
format: "esm",
|
|
807
|
-
platform: "node",
|
|
808
|
-
sourcemap: "inline",
|
|
809
|
-
target: "es2020",
|
|
810
|
-
write: false
|
|
811
|
-
});
|
|
812
|
-
const output = result.outputFiles[0];
|
|
813
|
-
const code = output?.text ?? "";
|
|
814
|
-
const dataUrl = `data:text/javascript;base64,${node_buffer.Buffer.from(code).toString(
|
|
815
|
-
"base64"
|
|
816
|
-
)}`;
|
|
817
|
-
return import(`${dataUrl}#${Date.now()}`);
|
|
960
|
+
return loadTsModule(file, logger);
|
|
818
961
|
}
|
|
819
962
|
return null;
|
|
820
963
|
}
|
|
@@ -849,7 +992,7 @@ async function loadRules(file, logger) {
|
|
|
849
992
|
}
|
|
850
993
|
];
|
|
851
994
|
}
|
|
852
|
-
const mod = await loadModule(file);
|
|
995
|
+
const mod = await loadModule(file, logger);
|
|
853
996
|
const value = mod?.default ?? mod;
|
|
854
997
|
if (!value) {
|
|
855
998
|
return [];
|
|
@@ -867,19 +1010,52 @@ async function loadRules(file, logger) {
|
|
|
867
1010
|
return [value];
|
|
868
1011
|
}
|
|
869
1012
|
|
|
1013
|
+
const silentLogger = {
|
|
1014
|
+
info: () => {
|
|
1015
|
+
},
|
|
1016
|
+
warn: () => {
|
|
1017
|
+
},
|
|
1018
|
+
error: () => {
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
function resolveSkipRoute(params) {
|
|
1022
|
+
const derived = params.derived ?? deriveRouteFromFile(params.file, params.rootDir, silentLogger);
|
|
1023
|
+
if (!derived?.method) {
|
|
1024
|
+
return null;
|
|
1025
|
+
}
|
|
1026
|
+
const resolved = resolveRule({
|
|
1027
|
+
rule: { handler: null },
|
|
1028
|
+
derivedTemplate: derived.template,
|
|
1029
|
+
derivedMethod: derived.method,
|
|
1030
|
+
prefix: params.prefix,
|
|
1031
|
+
file: params.file,
|
|
1032
|
+
logger: silentLogger
|
|
1033
|
+
});
|
|
1034
|
+
if (!resolved) {
|
|
1035
|
+
return null;
|
|
1036
|
+
}
|
|
1037
|
+
return {
|
|
1038
|
+
method: resolved.method,
|
|
1039
|
+
url: resolved.template
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
function buildSkipInfo(file, reason, resolved) {
|
|
1043
|
+
const info = { file, reason };
|
|
1044
|
+
if (resolved) {
|
|
1045
|
+
info.method = resolved.method;
|
|
1046
|
+
info.url = resolved.url;
|
|
1047
|
+
}
|
|
1048
|
+
return info;
|
|
1049
|
+
}
|
|
870
1050
|
async function scanRoutes(params) {
|
|
871
1051
|
const routes = [];
|
|
872
1052
|
const seen = /* @__PURE__ */ new Set();
|
|
873
1053
|
const files = await collectFiles(params.dirs);
|
|
1054
|
+
const globalIgnorePrefix = normalizeIgnorePrefix(params.ignorePrefix);
|
|
874
1055
|
const configCache = /* @__PURE__ */ new Map();
|
|
875
1056
|
const fileCache = /* @__PURE__ */ new Map();
|
|
1057
|
+
const shouldCollectSkip = typeof params.onSkip === "function";
|
|
876
1058
|
for (const fileInfo of files) {
|
|
877
|
-
if (!isSupportedFile(fileInfo.file)) {
|
|
878
|
-
continue;
|
|
879
|
-
}
|
|
880
|
-
if (!matchesFilter(fileInfo.file, params.include, params.exclude)) {
|
|
881
|
-
continue;
|
|
882
|
-
}
|
|
883
1059
|
const config = await resolveDirectoryConfig({
|
|
884
1060
|
file: fileInfo.file,
|
|
885
1061
|
rootDir: fileInfo.rootDir,
|
|
@@ -888,6 +1064,43 @@ async function scanRoutes(params) {
|
|
|
888
1064
|
fileCache
|
|
889
1065
|
});
|
|
890
1066
|
if (config.enabled === false) {
|
|
1067
|
+
if (shouldCollectSkip && isSupportedFile(fileInfo.file)) {
|
|
1068
|
+
const resolved = resolveSkipRoute({
|
|
1069
|
+
file: fileInfo.file,
|
|
1070
|
+
rootDir: fileInfo.rootDir,
|
|
1071
|
+
prefix: params.prefix
|
|
1072
|
+
});
|
|
1073
|
+
params.onSkip?.(buildSkipInfo(fileInfo.file, "disabled-dir", resolved));
|
|
1074
|
+
}
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
const effectiveIgnorePrefix = typeof config.ignorePrefix !== "undefined" ? normalizeIgnorePrefix(config.ignorePrefix, []) : globalIgnorePrefix;
|
|
1078
|
+
if (hasIgnoredPrefix(fileInfo.file, fileInfo.rootDir, effectiveIgnorePrefix)) {
|
|
1079
|
+
if (shouldCollectSkip && isSupportedFile(fileInfo.file)) {
|
|
1080
|
+
const resolved = resolveSkipRoute({
|
|
1081
|
+
file: fileInfo.file,
|
|
1082
|
+
rootDir: fileInfo.rootDir,
|
|
1083
|
+
prefix: params.prefix
|
|
1084
|
+
});
|
|
1085
|
+
params.onSkip?.(buildSkipInfo(fileInfo.file, "ignore-prefix", resolved));
|
|
1086
|
+
}
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
if (!isSupportedFile(fileInfo.file)) {
|
|
1090
|
+
continue;
|
|
1091
|
+
}
|
|
1092
|
+
const effectiveInclude = typeof config.include !== "undefined" ? config.include : params.include;
|
|
1093
|
+
const effectiveExclude = typeof config.exclude !== "undefined" ? config.exclude : params.exclude;
|
|
1094
|
+
if (!matchesFilter(fileInfo.file, effectiveInclude, effectiveExclude)) {
|
|
1095
|
+
if (shouldCollectSkip) {
|
|
1096
|
+
const resolved = resolveSkipRoute({
|
|
1097
|
+
file: fileInfo.file,
|
|
1098
|
+
rootDir: fileInfo.rootDir,
|
|
1099
|
+
prefix: params.prefix
|
|
1100
|
+
});
|
|
1101
|
+
const reason = effectiveExclude && matchesFilter(fileInfo.file, void 0, effectiveExclude) ? "exclude" : "include";
|
|
1102
|
+
params.onSkip?.(buildSkipInfo(fileInfo.file, reason, resolved));
|
|
1103
|
+
}
|
|
891
1104
|
continue;
|
|
892
1105
|
}
|
|
893
1106
|
const derived = deriveRouteFromFile(fileInfo.file, fileInfo.rootDir, params.logger);
|
|
@@ -899,6 +1112,18 @@ async function scanRoutes(params) {
|
|
|
899
1112
|
if (!rule || typeof rule !== "object") {
|
|
900
1113
|
continue;
|
|
901
1114
|
}
|
|
1115
|
+
if (rule.enabled === false) {
|
|
1116
|
+
if (shouldCollectSkip) {
|
|
1117
|
+
const resolved2 = resolveSkipRoute({
|
|
1118
|
+
file: fileInfo.file,
|
|
1119
|
+
rootDir: fileInfo.rootDir,
|
|
1120
|
+
prefix: params.prefix,
|
|
1121
|
+
derived
|
|
1122
|
+
});
|
|
1123
|
+
params.onSkip?.(buildSkipInfo(fileInfo.file, "disabled", resolved2));
|
|
1124
|
+
}
|
|
1125
|
+
continue;
|
|
1126
|
+
}
|
|
902
1127
|
const ruleValue = rule;
|
|
903
1128
|
const unsupportedKeys = ["response", "url", "method"].filter(
|
|
904
1129
|
(key2) => key2 in ruleValue
|
|
@@ -978,7 +1203,7 @@ function resolveRoot(list) {
|
|
|
978
1203
|
if (deno?.cwd) {
|
|
979
1204
|
return deno.cwd();
|
|
980
1205
|
}
|
|
981
|
-
return
|
|
1206
|
+
return process.cwd();
|
|
982
1207
|
}
|
|
983
1208
|
function resolveAllDirs(list, root) {
|
|
984
1209
|
const dirs = [];
|
|
@@ -999,6 +1224,7 @@ function buildApp(params) {
|
|
|
999
1224
|
registerPlaygroundRoutes({
|
|
1000
1225
|
app,
|
|
1001
1226
|
routes: params.routes,
|
|
1227
|
+
disabledRoutes: params.disabledRoutes,
|
|
1002
1228
|
dirs: params.dirs,
|
|
1003
1229
|
logger: params.logger,
|
|
1004
1230
|
config: params.playground,
|
|
@@ -1008,7 +1234,8 @@ function buildApp(params) {
|
|
|
1008
1234
|
app.get(`${params.playground.path}/ws`, params.wsHandler);
|
|
1009
1235
|
}
|
|
1010
1236
|
if (params.routes.length > 0) {
|
|
1011
|
-
const
|
|
1237
|
+
const mockAppOptions = params.onResponse ? { onResponse: params.onResponse } : {};
|
|
1238
|
+
const mockApp = createHonoApp(params.routes, mockAppOptions);
|
|
1012
1239
|
app.route("/", mockApp);
|
|
1013
1240
|
}
|
|
1014
1241
|
return app;
|
|
@@ -1162,23 +1389,27 @@ async function createFetchServer(options = {}) {
|
|
|
1162
1389
|
}
|
|
1163
1390
|
}
|
|
1164
1391
|
let routes = [];
|
|
1392
|
+
let disabledRoutes = [];
|
|
1165
1393
|
let app = buildApp({
|
|
1166
1394
|
routes,
|
|
1395
|
+
disabledRoutes,
|
|
1167
1396
|
dirs,
|
|
1168
1397
|
playground: playgroundConfig,
|
|
1169
1398
|
root,
|
|
1170
1399
|
logger,
|
|
1171
1400
|
onResponse: handleRouteResponse,
|
|
1172
|
-
wsHandler
|
|
1401
|
+
...wsHandler ? { wsHandler } : {}
|
|
1173
1402
|
});
|
|
1174
1403
|
const refreshRoutes = async () => {
|
|
1175
1404
|
try {
|
|
1176
1405
|
const collected = [];
|
|
1406
|
+
const collectedDisabled = [];
|
|
1177
1407
|
for (const entry of optionList) {
|
|
1178
1408
|
const scanParams = {
|
|
1179
1409
|
dirs: resolveDirs(entry.dir, root),
|
|
1180
1410
|
prefix: entry.prefix ?? "",
|
|
1181
|
-
logger
|
|
1411
|
+
logger,
|
|
1412
|
+
onSkip: (info) => collectedDisabled.push(info)
|
|
1182
1413
|
};
|
|
1183
1414
|
if (entry.include) {
|
|
1184
1415
|
scanParams.include = entry.include;
|
|
@@ -1186,19 +1417,24 @@ async function createFetchServer(options = {}) {
|
|
|
1186
1417
|
if (entry.exclude) {
|
|
1187
1418
|
scanParams.exclude = entry.exclude;
|
|
1188
1419
|
}
|
|
1420
|
+
if (typeof entry.ignorePrefix !== "undefined") {
|
|
1421
|
+
scanParams.ignorePrefix = entry.ignorePrefix;
|
|
1422
|
+
}
|
|
1189
1423
|
const scanned = await scanRoutes(scanParams);
|
|
1190
1424
|
collected.push(...scanned);
|
|
1191
1425
|
}
|
|
1192
1426
|
const resolvedRoutes = sortRoutes(collected);
|
|
1193
1427
|
routes = resolvedRoutes;
|
|
1428
|
+
disabledRoutes = collectedDisabled;
|
|
1194
1429
|
app = buildApp({
|
|
1195
1430
|
routes,
|
|
1431
|
+
disabledRoutes,
|
|
1196
1432
|
dirs,
|
|
1197
1433
|
playground: playgroundConfig,
|
|
1198
1434
|
root,
|
|
1199
1435
|
logger,
|
|
1200
1436
|
onResponse: handleRouteResponse,
|
|
1201
|
-
wsHandler
|
|
1437
|
+
...wsHandler ? { wsHandler } : {}
|
|
1202
1438
|
});
|
|
1203
1439
|
logger.info(`Loaded ${routes.length} mock routes.`);
|
|
1204
1440
|
} catch (error) {
|
|
@@ -1224,7 +1460,7 @@ async function createFetchServer(options = {}) {
|
|
|
1224
1460
|
fetch,
|
|
1225
1461
|
refresh: refreshRoutes,
|
|
1226
1462
|
getRoutes: () => routes,
|
|
1227
|
-
injectWebSocket
|
|
1463
|
+
...injectWebSocket ? { injectWebSocket } : {}
|
|
1228
1464
|
};
|
|
1229
1465
|
if (watcher) {
|
|
1230
1466
|
server.close = async () => {
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createRuntime, compareRouteScore, parseRouteTemplate } from '@mokup/runtime';
|
|
2
2
|
import { t as toRuntimeOptions, a as toRuntimeRequestFromNode, b as applyRuntimeResultToNode, d as toBinaryBody, c as createFetchHandler } from './shared/server.Dje1y79O.mjs';
|
|
3
|
-
import { cwd } from 'node:process';
|
|
3
|
+
import process, { cwd } from 'node:process';
|
|
4
4
|
import { Hono, PatternRouter } from '@mokup/shared/hono';
|
|
5
|
-
import { resolve, isAbsolute, join, normalize, extname, dirname,
|
|
5
|
+
import { resolve, isAbsolute, relative, join, normalize, extname, dirname, basename } from '@mokup/shared/pathe';
|
|
6
6
|
import { promises } from 'node:fs';
|
|
7
7
|
import { createRequire } from 'node:module';
|
|
8
8
|
import { Buffer } from 'node:buffer';
|
|
@@ -138,6 +138,20 @@ function matchesFilter(file, include, exclude) {
|
|
|
138
138
|
}
|
|
139
139
|
return true;
|
|
140
140
|
}
|
|
141
|
+
function normalizeIgnorePrefix(value, fallback = ["."]) {
|
|
142
|
+
const list = typeof value === "undefined" ? fallback : Array.isArray(value) ? value : [value];
|
|
143
|
+
return list.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
144
|
+
}
|
|
145
|
+
function hasIgnoredPrefix(file, rootDir, prefixes) {
|
|
146
|
+
if (prefixes.length === 0) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
const relativePath = toPosix(relative(rootDir, file));
|
|
150
|
+
const segments = relativePath.split("/");
|
|
151
|
+
return segments.some(
|
|
152
|
+
(segment) => prefixes.some((prefix) => segment.startsWith(prefix))
|
|
153
|
+
);
|
|
154
|
+
}
|
|
141
155
|
function delay(ms) {
|
|
142
156
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
143
157
|
}
|
|
@@ -369,6 +383,20 @@ function resolveGroupRoot(dirs, serverRoot) {
|
|
|
369
383
|
}
|
|
370
384
|
return common;
|
|
371
385
|
}
|
|
386
|
+
const disabledReasonSet = /* @__PURE__ */ new Set([
|
|
387
|
+
"disabled",
|
|
388
|
+
"disabled-dir",
|
|
389
|
+
"exclude",
|
|
390
|
+
"ignore-prefix",
|
|
391
|
+
"include",
|
|
392
|
+
"unknown"
|
|
393
|
+
]);
|
|
394
|
+
function normalizeDisabledReason(reason) {
|
|
395
|
+
if (reason && disabledReasonSet.has(reason)) {
|
|
396
|
+
return reason;
|
|
397
|
+
}
|
|
398
|
+
return "unknown";
|
|
399
|
+
}
|
|
372
400
|
function formatRouteFile(file, root) {
|
|
373
401
|
if (!root) {
|
|
374
402
|
return toPosixPath(file);
|
|
@@ -429,6 +457,24 @@ function toPlaygroundRoute(route, root, groups) {
|
|
|
429
457
|
group: matchedGroup?.label
|
|
430
458
|
};
|
|
431
459
|
}
|
|
460
|
+
function toPlaygroundDisabledRoute(route, root, groups) {
|
|
461
|
+
const matchedGroup = resolveRouteGroup(route.file, groups);
|
|
462
|
+
const disabled = {
|
|
463
|
+
file: formatRouteFile(route.file, root),
|
|
464
|
+
reason: normalizeDisabledReason(route.reason)
|
|
465
|
+
};
|
|
466
|
+
if (typeof route.method !== "undefined") {
|
|
467
|
+
disabled.method = route.method;
|
|
468
|
+
}
|
|
469
|
+
if (typeof route.url !== "undefined") {
|
|
470
|
+
disabled.url = route.url;
|
|
471
|
+
}
|
|
472
|
+
if (matchedGroup) {
|
|
473
|
+
disabled.groupKey = matchedGroup.key;
|
|
474
|
+
disabled.group = matchedGroup.label;
|
|
475
|
+
}
|
|
476
|
+
return disabled;
|
|
477
|
+
}
|
|
432
478
|
function registerPlaygroundRoutes(params) {
|
|
433
479
|
if (!params.config.enabled) {
|
|
434
480
|
return;
|
|
@@ -466,7 +512,8 @@ function registerPlaygroundRoutes(params) {
|
|
|
466
512
|
root: baseRoot,
|
|
467
513
|
count: params.routes.length,
|
|
468
514
|
groups: groups.map((group) => ({ key: group.key, label: group.label })),
|
|
469
|
-
routes: params.routes.map((route) => toPlaygroundRoute(route, baseRoot, groups))
|
|
515
|
+
routes: params.routes.map((route) => toPlaygroundRoute(route, baseRoot, groups)),
|
|
516
|
+
disabled: (params.disabledRoutes ?? []).map((route) => toPlaygroundDisabledRoute(route, baseRoot, groups))
|
|
470
517
|
});
|
|
471
518
|
});
|
|
472
519
|
params.app.get(`${playgroundPath}/*`, async (c) => {
|
|
@@ -612,8 +659,79 @@ function sortRoutes(routes) {
|
|
|
612
659
|
});
|
|
613
660
|
}
|
|
614
661
|
|
|
662
|
+
let registerPromise = null;
|
|
663
|
+
let hasLoggedFailure = false;
|
|
664
|
+
async function ensureTsxRegister(logger) {
|
|
665
|
+
if (registerPromise) {
|
|
666
|
+
return registerPromise;
|
|
667
|
+
}
|
|
668
|
+
registerPromise = (async () => {
|
|
669
|
+
try {
|
|
670
|
+
const mod = await import('tsx/esm/api');
|
|
671
|
+
const setSourceMapsEnabled = process.setSourceMapsEnabled;
|
|
672
|
+
if (typeof setSourceMapsEnabled === "function") {
|
|
673
|
+
setSourceMapsEnabled(true);
|
|
674
|
+
}
|
|
675
|
+
if (typeof mod.register === "function") {
|
|
676
|
+
mod.register();
|
|
677
|
+
}
|
|
678
|
+
return true;
|
|
679
|
+
} catch (error) {
|
|
680
|
+
if (!hasLoggedFailure && logger) {
|
|
681
|
+
logger.warn(
|
|
682
|
+
"Failed to register tsx loader; falling back to bundled TS loader.",
|
|
683
|
+
error
|
|
684
|
+
);
|
|
685
|
+
hasLoggedFailure = true;
|
|
686
|
+
}
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
})();
|
|
690
|
+
return registerPromise;
|
|
691
|
+
}
|
|
692
|
+
|
|
615
693
|
const configExtensions = [".ts", ".js", ".mjs", ".cjs"];
|
|
616
|
-
|
|
694
|
+
function isUnknownFileExtensionError$1(error) {
|
|
695
|
+
if (!error || typeof error !== "object") {
|
|
696
|
+
return false;
|
|
697
|
+
}
|
|
698
|
+
const code = error.code;
|
|
699
|
+
if (code === "ERR_UNKNOWN_FILE_EXTENSION") {
|
|
700
|
+
return true;
|
|
701
|
+
}
|
|
702
|
+
const message = error.message;
|
|
703
|
+
return typeof message === "string" && message.includes("Unknown file extension");
|
|
704
|
+
}
|
|
705
|
+
async function loadTsModule$1(file, logger) {
|
|
706
|
+
const cacheBust = Date.now();
|
|
707
|
+
const fileUrl = `${pathToFileURL(file).href}?t=${cacheBust}`;
|
|
708
|
+
const registered = await ensureTsxRegister(logger);
|
|
709
|
+
if (registered) {
|
|
710
|
+
try {
|
|
711
|
+
return await import(fileUrl);
|
|
712
|
+
} catch (error) {
|
|
713
|
+
if (!isUnknownFileExtensionError$1(error)) {
|
|
714
|
+
throw error;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
const result = await build({
|
|
719
|
+
entryPoints: [file],
|
|
720
|
+
bundle: true,
|
|
721
|
+
format: "esm",
|
|
722
|
+
platform: "node",
|
|
723
|
+
sourcemap: "inline",
|
|
724
|
+
target: "es2020",
|
|
725
|
+
write: false
|
|
726
|
+
});
|
|
727
|
+
const output = result.outputFiles[0];
|
|
728
|
+
const code = output?.text ?? "";
|
|
729
|
+
const dataUrl = `data:text/javascript;base64,${Buffer.from(code).toString(
|
|
730
|
+
"base64"
|
|
731
|
+
)}`;
|
|
732
|
+
return import(`${dataUrl}#${cacheBust}`);
|
|
733
|
+
}
|
|
734
|
+
async function loadModule$1(file, logger) {
|
|
617
735
|
const ext = configExtensions.find((extension) => file.endsWith(extension));
|
|
618
736
|
if (ext === ".cjs") {
|
|
619
737
|
const require = createRequire(import.meta.url);
|
|
@@ -624,21 +742,7 @@ async function loadModule$1(file) {
|
|
|
624
742
|
return import(`${pathToFileURL(file).href}?t=${Date.now()}`);
|
|
625
743
|
}
|
|
626
744
|
if (ext === ".ts") {
|
|
627
|
-
|
|
628
|
-
entryPoints: [file],
|
|
629
|
-
bundle: true,
|
|
630
|
-
format: "esm",
|
|
631
|
-
platform: "node",
|
|
632
|
-
sourcemap: "inline",
|
|
633
|
-
target: "es2020",
|
|
634
|
-
write: false
|
|
635
|
-
});
|
|
636
|
-
const output = result.outputFiles[0];
|
|
637
|
-
const code = output?.text ?? "";
|
|
638
|
-
const dataUrl = `data:text/javascript;base64,${Buffer.from(code).toString(
|
|
639
|
-
"base64"
|
|
640
|
-
)}`;
|
|
641
|
-
return import(`${dataUrl}#${Date.now()}`);
|
|
745
|
+
return loadTsModule$1(file, logger);
|
|
642
746
|
}
|
|
643
747
|
return null;
|
|
644
748
|
}
|
|
@@ -662,8 +766,8 @@ async function findConfigFile(dir, cache) {
|
|
|
662
766
|
cache.set(dir, null);
|
|
663
767
|
return null;
|
|
664
768
|
}
|
|
665
|
-
async function loadConfig(file) {
|
|
666
|
-
const mod = await loadModule$1(file);
|
|
769
|
+
async function loadConfig(file, logger) {
|
|
770
|
+
const mod = await loadModule$1(file, logger);
|
|
667
771
|
if (!mod) {
|
|
668
772
|
return null;
|
|
669
773
|
}
|
|
@@ -714,7 +818,7 @@ async function resolveDirectoryConfig(params) {
|
|
|
714
818
|
}
|
|
715
819
|
let config = configCache.get(configPath);
|
|
716
820
|
if (config === void 0) {
|
|
717
|
-
config = await loadConfig(configPath);
|
|
821
|
+
config = await loadConfig(configPath, logger);
|
|
718
822
|
configCache.set(configPath, config);
|
|
719
823
|
}
|
|
720
824
|
if (!config) {
|
|
@@ -733,6 +837,15 @@ async function resolveDirectoryConfig(params) {
|
|
|
733
837
|
if (typeof config.enabled === "boolean") {
|
|
734
838
|
merged.enabled = config.enabled;
|
|
735
839
|
}
|
|
840
|
+
if (typeof config.ignorePrefix !== "undefined") {
|
|
841
|
+
merged.ignorePrefix = config.ignorePrefix;
|
|
842
|
+
}
|
|
843
|
+
if (typeof config.include !== "undefined") {
|
|
844
|
+
merged.include = config.include;
|
|
845
|
+
}
|
|
846
|
+
if (typeof config.exclude !== "undefined") {
|
|
847
|
+
merged.exclude = config.exclude;
|
|
848
|
+
}
|
|
736
849
|
const normalized = normalizeMiddlewares(config.middleware, configPath, logger);
|
|
737
850
|
if (normalized.length > 0) {
|
|
738
851
|
merged.middlewares.push(...normalized);
|
|
@@ -786,7 +899,47 @@ function isSupportedFile(file) {
|
|
|
786
899
|
return supportedExtensions.has(ext);
|
|
787
900
|
}
|
|
788
901
|
|
|
789
|
-
|
|
902
|
+
function isUnknownFileExtensionError(error) {
|
|
903
|
+
if (!error || typeof error !== "object") {
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
906
|
+
const code = error.code;
|
|
907
|
+
if (code === "ERR_UNKNOWN_FILE_EXTENSION") {
|
|
908
|
+
return true;
|
|
909
|
+
}
|
|
910
|
+
const message = error.message;
|
|
911
|
+
return typeof message === "string" && message.includes("Unknown file extension");
|
|
912
|
+
}
|
|
913
|
+
async function loadTsModule(file, logger) {
|
|
914
|
+
const cacheBust = Date.now();
|
|
915
|
+
const fileUrl = `${pathToFileURL(file).href}?t=${cacheBust}`;
|
|
916
|
+
const registered = await ensureTsxRegister(logger);
|
|
917
|
+
if (registered) {
|
|
918
|
+
try {
|
|
919
|
+
return await import(fileUrl);
|
|
920
|
+
} catch (error) {
|
|
921
|
+
if (!isUnknownFileExtensionError(error)) {
|
|
922
|
+
throw error;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
const result = await build({
|
|
927
|
+
entryPoints: [file],
|
|
928
|
+
bundle: true,
|
|
929
|
+
format: "esm",
|
|
930
|
+
platform: "node",
|
|
931
|
+
sourcemap: "inline",
|
|
932
|
+
target: "es2020",
|
|
933
|
+
write: false
|
|
934
|
+
});
|
|
935
|
+
const output = result.outputFiles[0];
|
|
936
|
+
const code = output?.text ?? "";
|
|
937
|
+
const dataUrl = `data:text/javascript;base64,${Buffer.from(code).toString(
|
|
938
|
+
"base64"
|
|
939
|
+
)}`;
|
|
940
|
+
return import(`${dataUrl}#${cacheBust}`);
|
|
941
|
+
}
|
|
942
|
+
async function loadModule(file, logger) {
|
|
790
943
|
const ext = extname(file).toLowerCase();
|
|
791
944
|
if (ext === ".cjs") {
|
|
792
945
|
const require = createRequire(import.meta.url);
|
|
@@ -797,21 +950,7 @@ async function loadModule(file) {
|
|
|
797
950
|
return import(`${pathToFileURL(file).href}?t=${Date.now()}`);
|
|
798
951
|
}
|
|
799
952
|
if (ext === ".ts") {
|
|
800
|
-
|
|
801
|
-
entryPoints: [file],
|
|
802
|
-
bundle: true,
|
|
803
|
-
format: "esm",
|
|
804
|
-
platform: "node",
|
|
805
|
-
sourcemap: "inline",
|
|
806
|
-
target: "es2020",
|
|
807
|
-
write: false
|
|
808
|
-
});
|
|
809
|
-
const output = result.outputFiles[0];
|
|
810
|
-
const code = output?.text ?? "";
|
|
811
|
-
const dataUrl = `data:text/javascript;base64,${Buffer.from(code).toString(
|
|
812
|
-
"base64"
|
|
813
|
-
)}`;
|
|
814
|
-
return import(`${dataUrl}#${Date.now()}`);
|
|
953
|
+
return loadTsModule(file, logger);
|
|
815
954
|
}
|
|
816
955
|
return null;
|
|
817
956
|
}
|
|
@@ -846,7 +985,7 @@ async function loadRules(file, logger) {
|
|
|
846
985
|
}
|
|
847
986
|
];
|
|
848
987
|
}
|
|
849
|
-
const mod = await loadModule(file);
|
|
988
|
+
const mod = await loadModule(file, logger);
|
|
850
989
|
const value = mod?.default ?? mod;
|
|
851
990
|
if (!value) {
|
|
852
991
|
return [];
|
|
@@ -864,19 +1003,52 @@ async function loadRules(file, logger) {
|
|
|
864
1003
|
return [value];
|
|
865
1004
|
}
|
|
866
1005
|
|
|
1006
|
+
const silentLogger = {
|
|
1007
|
+
info: () => {
|
|
1008
|
+
},
|
|
1009
|
+
warn: () => {
|
|
1010
|
+
},
|
|
1011
|
+
error: () => {
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
function resolveSkipRoute(params) {
|
|
1015
|
+
const derived = params.derived ?? deriveRouteFromFile(params.file, params.rootDir, silentLogger);
|
|
1016
|
+
if (!derived?.method) {
|
|
1017
|
+
return null;
|
|
1018
|
+
}
|
|
1019
|
+
const resolved = resolveRule({
|
|
1020
|
+
rule: { handler: null },
|
|
1021
|
+
derivedTemplate: derived.template,
|
|
1022
|
+
derivedMethod: derived.method,
|
|
1023
|
+
prefix: params.prefix,
|
|
1024
|
+
file: params.file,
|
|
1025
|
+
logger: silentLogger
|
|
1026
|
+
});
|
|
1027
|
+
if (!resolved) {
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
1030
|
+
return {
|
|
1031
|
+
method: resolved.method,
|
|
1032
|
+
url: resolved.template
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
function buildSkipInfo(file, reason, resolved) {
|
|
1036
|
+
const info = { file, reason };
|
|
1037
|
+
if (resolved) {
|
|
1038
|
+
info.method = resolved.method;
|
|
1039
|
+
info.url = resolved.url;
|
|
1040
|
+
}
|
|
1041
|
+
return info;
|
|
1042
|
+
}
|
|
867
1043
|
async function scanRoutes(params) {
|
|
868
1044
|
const routes = [];
|
|
869
1045
|
const seen = /* @__PURE__ */ new Set();
|
|
870
1046
|
const files = await collectFiles(params.dirs);
|
|
1047
|
+
const globalIgnorePrefix = normalizeIgnorePrefix(params.ignorePrefix);
|
|
871
1048
|
const configCache = /* @__PURE__ */ new Map();
|
|
872
1049
|
const fileCache = /* @__PURE__ */ new Map();
|
|
1050
|
+
const shouldCollectSkip = typeof params.onSkip === "function";
|
|
873
1051
|
for (const fileInfo of files) {
|
|
874
|
-
if (!isSupportedFile(fileInfo.file)) {
|
|
875
|
-
continue;
|
|
876
|
-
}
|
|
877
|
-
if (!matchesFilter(fileInfo.file, params.include, params.exclude)) {
|
|
878
|
-
continue;
|
|
879
|
-
}
|
|
880
1052
|
const config = await resolveDirectoryConfig({
|
|
881
1053
|
file: fileInfo.file,
|
|
882
1054
|
rootDir: fileInfo.rootDir,
|
|
@@ -885,6 +1057,43 @@ async function scanRoutes(params) {
|
|
|
885
1057
|
fileCache
|
|
886
1058
|
});
|
|
887
1059
|
if (config.enabled === false) {
|
|
1060
|
+
if (shouldCollectSkip && isSupportedFile(fileInfo.file)) {
|
|
1061
|
+
const resolved = resolveSkipRoute({
|
|
1062
|
+
file: fileInfo.file,
|
|
1063
|
+
rootDir: fileInfo.rootDir,
|
|
1064
|
+
prefix: params.prefix
|
|
1065
|
+
});
|
|
1066
|
+
params.onSkip?.(buildSkipInfo(fileInfo.file, "disabled-dir", resolved));
|
|
1067
|
+
}
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
const effectiveIgnorePrefix = typeof config.ignorePrefix !== "undefined" ? normalizeIgnorePrefix(config.ignorePrefix, []) : globalIgnorePrefix;
|
|
1071
|
+
if (hasIgnoredPrefix(fileInfo.file, fileInfo.rootDir, effectiveIgnorePrefix)) {
|
|
1072
|
+
if (shouldCollectSkip && isSupportedFile(fileInfo.file)) {
|
|
1073
|
+
const resolved = resolveSkipRoute({
|
|
1074
|
+
file: fileInfo.file,
|
|
1075
|
+
rootDir: fileInfo.rootDir,
|
|
1076
|
+
prefix: params.prefix
|
|
1077
|
+
});
|
|
1078
|
+
params.onSkip?.(buildSkipInfo(fileInfo.file, "ignore-prefix", resolved));
|
|
1079
|
+
}
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
if (!isSupportedFile(fileInfo.file)) {
|
|
1083
|
+
continue;
|
|
1084
|
+
}
|
|
1085
|
+
const effectiveInclude = typeof config.include !== "undefined" ? config.include : params.include;
|
|
1086
|
+
const effectiveExclude = typeof config.exclude !== "undefined" ? config.exclude : params.exclude;
|
|
1087
|
+
if (!matchesFilter(fileInfo.file, effectiveInclude, effectiveExclude)) {
|
|
1088
|
+
if (shouldCollectSkip) {
|
|
1089
|
+
const resolved = resolveSkipRoute({
|
|
1090
|
+
file: fileInfo.file,
|
|
1091
|
+
rootDir: fileInfo.rootDir,
|
|
1092
|
+
prefix: params.prefix
|
|
1093
|
+
});
|
|
1094
|
+
const reason = effectiveExclude && matchesFilter(fileInfo.file, void 0, effectiveExclude) ? "exclude" : "include";
|
|
1095
|
+
params.onSkip?.(buildSkipInfo(fileInfo.file, reason, resolved));
|
|
1096
|
+
}
|
|
888
1097
|
continue;
|
|
889
1098
|
}
|
|
890
1099
|
const derived = deriveRouteFromFile(fileInfo.file, fileInfo.rootDir, params.logger);
|
|
@@ -896,6 +1105,18 @@ async function scanRoutes(params) {
|
|
|
896
1105
|
if (!rule || typeof rule !== "object") {
|
|
897
1106
|
continue;
|
|
898
1107
|
}
|
|
1108
|
+
if (rule.enabled === false) {
|
|
1109
|
+
if (shouldCollectSkip) {
|
|
1110
|
+
const resolved2 = resolveSkipRoute({
|
|
1111
|
+
file: fileInfo.file,
|
|
1112
|
+
rootDir: fileInfo.rootDir,
|
|
1113
|
+
prefix: params.prefix,
|
|
1114
|
+
derived
|
|
1115
|
+
});
|
|
1116
|
+
params.onSkip?.(buildSkipInfo(fileInfo.file, "disabled", resolved2));
|
|
1117
|
+
}
|
|
1118
|
+
continue;
|
|
1119
|
+
}
|
|
899
1120
|
const ruleValue = rule;
|
|
900
1121
|
const unsupportedKeys = ["response", "url", "method"].filter(
|
|
901
1122
|
(key2) => key2 in ruleValue
|
|
@@ -996,6 +1217,7 @@ function buildApp(params) {
|
|
|
996
1217
|
registerPlaygroundRoutes({
|
|
997
1218
|
app,
|
|
998
1219
|
routes: params.routes,
|
|
1220
|
+
disabledRoutes: params.disabledRoutes,
|
|
999
1221
|
dirs: params.dirs,
|
|
1000
1222
|
logger: params.logger,
|
|
1001
1223
|
config: params.playground,
|
|
@@ -1005,7 +1227,8 @@ function buildApp(params) {
|
|
|
1005
1227
|
app.get(`${params.playground.path}/ws`, params.wsHandler);
|
|
1006
1228
|
}
|
|
1007
1229
|
if (params.routes.length > 0) {
|
|
1008
|
-
const
|
|
1230
|
+
const mockAppOptions = params.onResponse ? { onResponse: params.onResponse } : {};
|
|
1231
|
+
const mockApp = createHonoApp(params.routes, mockAppOptions);
|
|
1009
1232
|
app.route("/", mockApp);
|
|
1010
1233
|
}
|
|
1011
1234
|
return app;
|
|
@@ -1159,23 +1382,27 @@ async function createFetchServer(options = {}) {
|
|
|
1159
1382
|
}
|
|
1160
1383
|
}
|
|
1161
1384
|
let routes = [];
|
|
1385
|
+
let disabledRoutes = [];
|
|
1162
1386
|
let app = buildApp({
|
|
1163
1387
|
routes,
|
|
1388
|
+
disabledRoutes,
|
|
1164
1389
|
dirs,
|
|
1165
1390
|
playground: playgroundConfig,
|
|
1166
1391
|
root,
|
|
1167
1392
|
logger,
|
|
1168
1393
|
onResponse: handleRouteResponse,
|
|
1169
|
-
wsHandler
|
|
1394
|
+
...wsHandler ? { wsHandler } : {}
|
|
1170
1395
|
});
|
|
1171
1396
|
const refreshRoutes = async () => {
|
|
1172
1397
|
try {
|
|
1173
1398
|
const collected = [];
|
|
1399
|
+
const collectedDisabled = [];
|
|
1174
1400
|
for (const entry of optionList) {
|
|
1175
1401
|
const scanParams = {
|
|
1176
1402
|
dirs: resolveDirs(entry.dir, root),
|
|
1177
1403
|
prefix: entry.prefix ?? "",
|
|
1178
|
-
logger
|
|
1404
|
+
logger,
|
|
1405
|
+
onSkip: (info) => collectedDisabled.push(info)
|
|
1179
1406
|
};
|
|
1180
1407
|
if (entry.include) {
|
|
1181
1408
|
scanParams.include = entry.include;
|
|
@@ -1183,19 +1410,24 @@ async function createFetchServer(options = {}) {
|
|
|
1183
1410
|
if (entry.exclude) {
|
|
1184
1411
|
scanParams.exclude = entry.exclude;
|
|
1185
1412
|
}
|
|
1413
|
+
if (typeof entry.ignorePrefix !== "undefined") {
|
|
1414
|
+
scanParams.ignorePrefix = entry.ignorePrefix;
|
|
1415
|
+
}
|
|
1186
1416
|
const scanned = await scanRoutes(scanParams);
|
|
1187
1417
|
collected.push(...scanned);
|
|
1188
1418
|
}
|
|
1189
1419
|
const resolvedRoutes = sortRoutes(collected);
|
|
1190
1420
|
routes = resolvedRoutes;
|
|
1421
|
+
disabledRoutes = collectedDisabled;
|
|
1191
1422
|
app = buildApp({
|
|
1192
1423
|
routes,
|
|
1424
|
+
disabledRoutes,
|
|
1193
1425
|
dirs,
|
|
1194
1426
|
playground: playgroundConfig,
|
|
1195
1427
|
root,
|
|
1196
1428
|
logger,
|
|
1197
1429
|
onResponse: handleRouteResponse,
|
|
1198
|
-
wsHandler
|
|
1430
|
+
...wsHandler ? { wsHandler } : {}
|
|
1199
1431
|
});
|
|
1200
1432
|
logger.info(`Loaded ${routes.length} mock routes.`);
|
|
1201
1433
|
} catch (error) {
|
|
@@ -1221,7 +1453,7 @@ async function createFetchServer(options = {}) {
|
|
|
1221
1453
|
fetch,
|
|
1222
1454
|
refresh: refreshRoutes,
|
|
1223
1455
|
getRoutes: () => routes,
|
|
1224
|
-
injectWebSocket
|
|
1456
|
+
...injectWebSocket ? { injectWebSocket } : {}
|
|
1225
1457
|
};
|
|
1226
1458
|
if (watcher) {
|
|
1227
1459
|
server.close = async () => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mokup/server",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"description": "Server adapters for @mokup/runtime.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://mokup.icebreaker.top",
|
|
@@ -39,12 +39,13 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@hono/node-server": "^1.19.9",
|
|
41
41
|
"@hono/node-ws": "^1.1.1",
|
|
42
|
+
"tsx": "^4.21.0",
|
|
43
|
+
"@mokup/playground": "0.0.9",
|
|
42
44
|
"@mokup/runtime": "1.0.0",
|
|
43
|
-
"@mokup/playground": "0.0.7",
|
|
44
45
|
"@mokup/shared": "1.0.0"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
|
-
"@types/node": "^25.0.
|
|
48
|
+
"@types/node": "^25.0.10",
|
|
48
49
|
"typescript": "^5.9.3",
|
|
49
50
|
"unbuild": "^3.6.1"
|
|
50
51
|
},
|