mokup 2.0.2 → 2.1.1
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/cli-bin.cjs +3 -1
- package/dist/cli-bin.mjs +3 -1
- package/dist/index.cjs +39 -0
- package/dist/index.d.cts +62 -44
- package/dist/index.d.mts +62 -44
- package/dist/index.d.ts +64 -44
- package/dist/index.mjs +37 -0
- package/dist/server/worker.d.ts +2 -0
- package/dist/shared/mokup.CWQ8woZc.d.cts +302 -0
- package/dist/shared/mokup.CWQ8woZc.d.mts +302 -0
- package/dist/shared/mokup.CWQ8woZc.d.ts +302 -0
- package/dist/shared/{mokup.B-yfMz5B.cjs → mokup.To7knvSq.cjs} +1631 -1220
- package/dist/shared/{mokup.jeVwRMia.mjs → mokup.ghra3mzH.mjs} +1631 -1220
- package/dist/sw.cjs +5 -2
- package/dist/sw.d.cts +63 -0
- package/dist/sw.d.mts +63 -0
- package/dist/sw.d.ts +63 -0
- package/dist/sw.mjs +5 -2
- package/dist/types/virtual.d.ts +9 -0
- package/dist/vite.cjs +694 -346
- package/dist/vite.d.cts +20 -2
- package/dist/vite.d.mts +20 -2
- package/dist/vite.d.ts +22 -2
- package/dist/vite.mjs +687 -340
- package/dist/webpack.cjs +276 -204
- package/dist/webpack.d.cts +36 -4
- package/dist/webpack.d.mts +37 -3
- package/dist/webpack.d.ts +36 -4
- package/dist/webpack.mjs +272 -197
- package/package.json +7 -6
|
@@ -1,37 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const node_buffer = require('node:buffer');
|
|
4
|
-
const hono = require('@mokup/shared/hono');
|
|
5
3
|
const node_fs = require('node:fs');
|
|
4
|
+
const pathe = require('@mokup/shared/pathe');
|
|
6
5
|
const node_module = require('node:module');
|
|
7
6
|
const process = require('node:process');
|
|
8
|
-
const
|
|
7
|
+
const node_buffer = require('node:buffer');
|
|
8
|
+
const hono = require('@mokup/shared/hono');
|
|
9
9
|
const node_url = require('node:url');
|
|
10
10
|
const esbuild = require('@mokup/shared/esbuild');
|
|
11
11
|
const jsoncParser = require('@mokup/shared/jsonc-parser');
|
|
12
12
|
const runtime = require('@mokup/runtime');
|
|
13
13
|
|
|
14
14
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
15
|
-
function createLogger(enabled) {
|
|
16
|
-
return {
|
|
17
|
-
info: (...args) => {
|
|
18
|
-
if (enabled) {
|
|
19
|
-
console.info("[mokup]", ...args);
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
warn: (...args) => {
|
|
23
|
-
if (enabled) {
|
|
24
|
-
console.warn("[mokup]", ...args);
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
error: (...args) => {
|
|
28
|
-
if (enabled) {
|
|
29
|
-
console.error("[mokup]", ...args);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
15
|
const methodSet = /* @__PURE__ */ new Set([
|
|
36
16
|
"GET",
|
|
37
17
|
"POST",
|
|
@@ -101,20 +81,6 @@ function isInDirs(file, dirs) {
|
|
|
101
81
|
return normalized === normalizedDir || normalized.startsWith(`${normalizedDir}/`);
|
|
102
82
|
});
|
|
103
83
|
}
|
|
104
|
-
function testPatterns(patterns, value) {
|
|
105
|
-
const list = Array.isArray(patterns) ? patterns : [patterns];
|
|
106
|
-
return list.some((pattern) => pattern.test(value));
|
|
107
|
-
}
|
|
108
|
-
function matchesFilter(file, include, exclude) {
|
|
109
|
-
const normalized = toPosix(file);
|
|
110
|
-
if (exclude && testPatterns(exclude, normalized)) {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
if (include) {
|
|
114
|
-
return testPatterns(include, normalized);
|
|
115
|
-
}
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
84
|
function normalizeIgnorePrefix(value, fallback = ["."]) {
|
|
119
85
|
const list = typeof value === "undefined" ? fallback : Array.isArray(value) ? value : [value];
|
|
120
86
|
return list.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
@@ -133,225 +99,191 @@ function delay(ms) {
|
|
|
133
99
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
134
100
|
}
|
|
135
101
|
|
|
136
|
-
function
|
|
137
|
-
|
|
138
|
-
|
|
102
|
+
function toViteImportPath(file, root) {
|
|
103
|
+
const absolute = pathe.isAbsolute(file) ? file : pathe.resolve(root, file);
|
|
104
|
+
const rel = pathe.relative(root, absolute);
|
|
105
|
+
if (!rel.startsWith("..") && !pathe.isAbsolute(rel)) {
|
|
106
|
+
return `/${toPosix(rel)}`;
|
|
139
107
|
}
|
|
140
|
-
|
|
141
|
-
if (token.type === "static") {
|
|
142
|
-
return token.value;
|
|
143
|
-
}
|
|
144
|
-
if (token.type === "param") {
|
|
145
|
-
return `:${token.name}`;
|
|
146
|
-
}
|
|
147
|
-
if (token.type === "catchall") {
|
|
148
|
-
return `:${token.name}{.+}`;
|
|
149
|
-
}
|
|
150
|
-
return `:${token.name}{.+}?`;
|
|
151
|
-
});
|
|
152
|
-
return `/${segments.join("/")}`;
|
|
153
|
-
}
|
|
154
|
-
function isValidStatus(status) {
|
|
155
|
-
return typeof status === "number" && Number.isFinite(status) && status >= 200 && status <= 599;
|
|
108
|
+
return `/@fs/${toPosix(absolute)}`;
|
|
156
109
|
}
|
|
157
|
-
function
|
|
158
|
-
if (
|
|
159
|
-
return
|
|
110
|
+
function shouldModuleize(handler) {
|
|
111
|
+
if (typeof handler === "function") {
|
|
112
|
+
return true;
|
|
160
113
|
}
|
|
161
|
-
if (
|
|
162
|
-
return
|
|
114
|
+
if (typeof Response !== "undefined" && handler instanceof Response) {
|
|
115
|
+
return true;
|
|
163
116
|
}
|
|
164
|
-
return
|
|
117
|
+
return false;
|
|
165
118
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
for (const [key, value] of Object.entries(route.headers)) {
|
|
171
|
-
headers.set(key, value);
|
|
172
|
-
}
|
|
119
|
+
const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
120
|
+
function getNodeBuffer() {
|
|
121
|
+
if (typeof globalThis === "undefined") {
|
|
122
|
+
return null;
|
|
173
123
|
}
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
124
|
+
const buffer = globalThis.Buffer;
|
|
125
|
+
return buffer ?? null;
|
|
126
|
+
}
|
|
127
|
+
function getBtoa() {
|
|
128
|
+
if (typeof globalThis === "undefined") {
|
|
129
|
+
return null;
|
|
177
130
|
}
|
|
178
|
-
|
|
131
|
+
const btoaFn = globalThis.btoa;
|
|
132
|
+
return typeof btoaFn === "function" ? btoaFn : null;
|
|
133
|
+
}
|
|
134
|
+
function encodeBase64(bytes) {
|
|
135
|
+
const buffer = getNodeBuffer();
|
|
136
|
+
if (buffer) {
|
|
137
|
+
return buffer.from(bytes).toString("base64");
|
|
138
|
+
}
|
|
139
|
+
const btoaFn = getBtoa();
|
|
140
|
+
if (btoaFn) {
|
|
141
|
+
let binary = "";
|
|
142
|
+
const chunkSize = 32768;
|
|
143
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
144
|
+
const chunk = bytes.subarray(i, i + chunkSize);
|
|
145
|
+
binary += String.fromCharCode(...chunk);
|
|
146
|
+
}
|
|
147
|
+
return btoaFn(binary);
|
|
148
|
+
}
|
|
149
|
+
let output = "";
|
|
150
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
151
|
+
const a = bytes[i] ?? 0;
|
|
152
|
+
const b = i + 1 < bytes.length ? bytes[i + 1] ?? 0 : 0;
|
|
153
|
+
const c = i + 2 < bytes.length ? bytes[i + 2] ?? 0 : 0;
|
|
154
|
+
const triple = a << 16 | b << 8 | c;
|
|
155
|
+
output += BASE64_ALPHABET[triple >> 18 & 63];
|
|
156
|
+
output += BASE64_ALPHABET[triple >> 12 & 63];
|
|
157
|
+
output += i + 1 < bytes.length ? BASE64_ALPHABET[triple >> 6 & 63] : "=";
|
|
158
|
+
output += i + 2 < bytes.length ? BASE64_ALPHABET[triple & 63] : "=";
|
|
159
|
+
}
|
|
160
|
+
return output;
|
|
179
161
|
}
|
|
180
|
-
function
|
|
181
|
-
if (
|
|
182
|
-
return
|
|
162
|
+
function toBinaryBody(handler) {
|
|
163
|
+
if (handler instanceof ArrayBuffer) {
|
|
164
|
+
return encodeBase64(new Uint8Array(handler));
|
|
183
165
|
}
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
if (resolved instanceof Response) {
|
|
187
|
-
return resolved;
|
|
188
|
-
}
|
|
166
|
+
if (handler instanceof Uint8Array) {
|
|
167
|
+
return encodeBase64(handler);
|
|
189
168
|
}
|
|
190
|
-
return
|
|
169
|
+
return null;
|
|
191
170
|
}
|
|
192
|
-
function
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (
|
|
199
|
-
|
|
200
|
-
status: 204,
|
|
201
|
-
headers: response.headers
|
|
202
|
-
});
|
|
171
|
+
function buildManifestResponse(route, moduleId) {
|
|
172
|
+
if (moduleId) {
|
|
173
|
+
const response = {
|
|
174
|
+
type: "module",
|
|
175
|
+
module: moduleId
|
|
176
|
+
};
|
|
177
|
+
if (typeof route.ruleIndex === "number") {
|
|
178
|
+
response.ruleIndex = route.ruleIndex;
|
|
203
179
|
}
|
|
204
180
|
return response;
|
|
205
181
|
}
|
|
206
|
-
|
|
207
|
-
|
|
182
|
+
const handler = route.handler;
|
|
183
|
+
if (typeof handler === "string") {
|
|
184
|
+
return {
|
|
185
|
+
type: "text",
|
|
186
|
+
body: handler
|
|
187
|
+
};
|
|
208
188
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
189
|
+
const binary = toBinaryBody(handler);
|
|
190
|
+
if (binary) {
|
|
191
|
+
return {
|
|
192
|
+
type: "binary",
|
|
193
|
+
body: binary,
|
|
194
|
+
encoding: "base64"
|
|
195
|
+
};
|
|
215
196
|
}
|
|
216
|
-
return
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
return async (c) => {
|
|
220
|
-
const value = typeof route.handler === "function" ? await route.handler(c) : route.handler;
|
|
221
|
-
return normalizeHandlerValue(c, value);
|
|
197
|
+
return {
|
|
198
|
+
type: "json",
|
|
199
|
+
body: handler
|
|
222
200
|
};
|
|
223
201
|
}
|
|
224
|
-
function
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
202
|
+
function buildManifestData(params) {
|
|
203
|
+
const { routes, root } = params;
|
|
204
|
+
const resolveModulePath = params.resolveModulePath ?? toViteImportPath;
|
|
205
|
+
const ruleModules = /* @__PURE__ */ new Map();
|
|
206
|
+
const middlewareModules = /* @__PURE__ */ new Map();
|
|
207
|
+
const manifestRoutes = routes.map((route) => {
|
|
208
|
+
const moduleId = shouldModuleize(route.handler) ? resolveModulePath(route.file, root) : null;
|
|
209
|
+
if (moduleId && !ruleModules.has(moduleId)) {
|
|
210
|
+
ruleModules.set(moduleId, { id: moduleId, kind: "rule" });
|
|
230
211
|
}
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
212
|
+
const middleware = route.middlewares?.map((entry) => {
|
|
213
|
+
const modulePath = resolveModulePath(entry.source, root);
|
|
214
|
+
if (!middlewareModules.has(modulePath)) {
|
|
215
|
+
middlewareModules.set(modulePath, { id: modulePath, kind: "middleware" });
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
module: modulePath,
|
|
219
|
+
ruleIndex: entry.index
|
|
220
|
+
};
|
|
221
|
+
});
|
|
222
|
+
const response = buildManifestResponse(route, moduleId);
|
|
223
|
+
const manifestRoute = {
|
|
224
|
+
method: route.method,
|
|
225
|
+
url: route.template,
|
|
226
|
+
...route.tokens ? { tokens: route.tokens } : {},
|
|
227
|
+
...route.score ? { score: route.score } : {},
|
|
228
|
+
...route.status ? { status: route.status } : {},
|
|
229
|
+
...route.headers ? { headers: route.headers } : {},
|
|
230
|
+
...route.delay ? { delay: route.delay } : {},
|
|
231
|
+
...middleware && middleware.length > 0 ? { middleware } : {},
|
|
232
|
+
response
|
|
233
|
+
};
|
|
234
|
+
return manifestRoute;
|
|
235
|
+
});
|
|
236
|
+
const manifest = {
|
|
237
|
+
version: 1,
|
|
238
|
+
routes: manifestRoutes
|
|
234
239
|
};
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
+
return {
|
|
241
|
+
manifest,
|
|
242
|
+
modules: [
|
|
243
|
+
...ruleModules.values(),
|
|
244
|
+
...middlewareModules.values()
|
|
245
|
+
]
|
|
240
246
|
};
|
|
241
247
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
app.on(
|
|
247
|
-
route.method,
|
|
248
|
-
toHonoPath(route),
|
|
249
|
-
createFinalizeMiddleware(route),
|
|
250
|
-
...middlewares,
|
|
251
|
-
createRouteHandler(route)
|
|
252
|
-
);
|
|
248
|
+
|
|
249
|
+
function normalizePlaygroundPath(value) {
|
|
250
|
+
if (!value) {
|
|
251
|
+
return "/__mokup";
|
|
253
252
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
async function readRawBody(req) {
|
|
257
|
-
return await new Promise((resolve, reject) => {
|
|
258
|
-
const chunks = [];
|
|
259
|
-
req.on("data", (chunk) => {
|
|
260
|
-
if (typeof chunk === "string") {
|
|
261
|
-
chunks.push(node_buffer.Buffer.from(chunk));
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
if (chunk instanceof Uint8Array) {
|
|
265
|
-
chunks.push(chunk);
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
chunks.push(node_buffer.Buffer.from(String(chunk)));
|
|
269
|
-
});
|
|
270
|
-
req.on("end", () => {
|
|
271
|
-
if (chunks.length === 0) {
|
|
272
|
-
resolve(null);
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
resolve(node_buffer.Buffer.concat(chunks));
|
|
276
|
-
});
|
|
277
|
-
req.on("error", reject);
|
|
278
|
-
});
|
|
253
|
+
const normalized = value.startsWith("/") ? value : `/${value}`;
|
|
254
|
+
return normalized.length > 1 && normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
|
|
279
255
|
}
|
|
280
|
-
function
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (typeof value === "undefined") {
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
if (Array.isArray(value)) {
|
|
287
|
-
result.set(key, value.join(","));
|
|
288
|
-
} else {
|
|
289
|
-
result.set(key, value);
|
|
290
|
-
}
|
|
256
|
+
function normalizeBase(base) {
|
|
257
|
+
if (!base || base === "/") {
|
|
258
|
+
return "";
|
|
291
259
|
}
|
|
292
|
-
return
|
|
260
|
+
return base.endsWith("/") ? base.slice(0, -1) : base;
|
|
293
261
|
}
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const rawBody = await readRawBody(req);
|
|
300
|
-
if (rawBody && method !== "GET" && method !== "HEAD") {
|
|
301
|
-
init.body = rawBody;
|
|
262
|
+
function resolvePlaygroundRequestPath(base, playgroundPath) {
|
|
263
|
+
const normalizedBase = normalizeBase(base);
|
|
264
|
+
const normalizedPath = normalizePlaygroundPath(playgroundPath);
|
|
265
|
+
if (!normalizedBase) {
|
|
266
|
+
return normalizedPath;
|
|
302
267
|
}
|
|
303
|
-
|
|
268
|
+
if (normalizedPath.startsWith(normalizedBase)) {
|
|
269
|
+
return normalizedPath;
|
|
270
|
+
}
|
|
271
|
+
return `${normalizedBase}${normalizedPath}`;
|
|
304
272
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
res.setHeader(key, value);
|
|
309
|
-
});
|
|
310
|
-
if (!response.body) {
|
|
311
|
-
res.end();
|
|
312
|
-
return;
|
|
273
|
+
function resolvePlaygroundOptions(playground) {
|
|
274
|
+
if (playground === false) {
|
|
275
|
+
return { enabled: false, path: "/__mokup" };
|
|
313
276
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
return
|
|
321
|
-
}
|
|
322
|
-
function createMiddleware(getApp, logger) {
|
|
323
|
-
return async (req, res, next) => {
|
|
324
|
-
const app = getApp();
|
|
325
|
-
if (!app) {
|
|
326
|
-
return next();
|
|
327
|
-
}
|
|
328
|
-
const url = req.url ?? "/";
|
|
329
|
-
const parsedUrl = new URL(url, "http://mokup.local");
|
|
330
|
-
const pathname = parsedUrl.pathname;
|
|
331
|
-
const method = normalizeMethod(req.method) ?? "GET";
|
|
332
|
-
if (!hasMatch(app, method, pathname)) {
|
|
333
|
-
return next();
|
|
334
|
-
}
|
|
335
|
-
const startedAt = Date.now();
|
|
336
|
-
try {
|
|
337
|
-
const response = await app.fetch(await toRequest(req));
|
|
338
|
-
if (res.writableEnded) {
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
await sendResponse(res, response);
|
|
342
|
-
logger.info(`${method} ${pathname} ${Date.now() - startedAt}ms`);
|
|
343
|
-
} catch (error) {
|
|
344
|
-
if (!res.headersSent) {
|
|
345
|
-
res.statusCode = 500;
|
|
346
|
-
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
347
|
-
}
|
|
348
|
-
res.end("Mock handler error");
|
|
349
|
-
logger.error("Mock handler failed:", error);
|
|
350
|
-
}
|
|
351
|
-
};
|
|
277
|
+
if (playground && typeof playground === "object") {
|
|
278
|
+
return {
|
|
279
|
+
enabled: playground.enabled !== false,
|
|
280
|
+
path: normalizePlaygroundPath(playground.path)
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
return { enabled: true, path: "/__mokup" };
|
|
352
284
|
}
|
|
353
285
|
|
|
354
|
-
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/mokup.
|
|
286
|
+
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/mokup.To7knvSq.cjs', document.baseURI).href)));
|
|
355
287
|
const mimeTypes = {
|
|
356
288
|
".html": "text/html; charset=utf-8",
|
|
357
289
|
".css": "text/css; charset=utf-8",
|
|
@@ -364,96 +296,6 @@ const mimeTypes = {
|
|
|
364
296
|
".jpeg": "image/jpeg",
|
|
365
297
|
".ico": "image/x-icon"
|
|
366
298
|
};
|
|
367
|
-
function normalizePlaygroundPath(value) {
|
|
368
|
-
if (!value) {
|
|
369
|
-
return "/_mokup";
|
|
370
|
-
}
|
|
371
|
-
const normalized = value.startsWith("/") ? value : `/${value}`;
|
|
372
|
-
return normalized.length > 1 && normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
|
|
373
|
-
}
|
|
374
|
-
function normalizeBase(base) {
|
|
375
|
-
if (!base || base === "/") {
|
|
376
|
-
return "";
|
|
377
|
-
}
|
|
378
|
-
return base.endsWith("/") ? base.slice(0, -1) : base;
|
|
379
|
-
}
|
|
380
|
-
function resolvePlaygroundRequestPath(base, playgroundPath) {
|
|
381
|
-
const normalizedBase = normalizeBase(base);
|
|
382
|
-
const normalizedPath = normalizePlaygroundPath(playgroundPath);
|
|
383
|
-
if (!normalizedBase) {
|
|
384
|
-
return normalizedPath;
|
|
385
|
-
}
|
|
386
|
-
if (normalizedPath.startsWith(normalizedBase)) {
|
|
387
|
-
return normalizedPath;
|
|
388
|
-
}
|
|
389
|
-
return `${normalizedBase}${normalizedPath}`;
|
|
390
|
-
}
|
|
391
|
-
function injectPlaygroundHmr(html, base) {
|
|
392
|
-
if (html.includes("mokup-playground-hmr")) {
|
|
393
|
-
return html;
|
|
394
|
-
}
|
|
395
|
-
const normalizedBase = normalizeBase(base);
|
|
396
|
-
const clientPath = `${normalizedBase}/@vite/client`;
|
|
397
|
-
const snippet = [
|
|
398
|
-
'<script type="module" id="mokup-playground-hmr">',
|
|
399
|
-
`import('${clientPath}').then(({ createHotContext }) => {`,
|
|
400
|
-
" const hot = createHotContext('/@mokup/playground')",
|
|
401
|
-
" hot.on('mokup:routes-changed', () => {",
|
|
402
|
-
" const api = window.__MOKUP_PLAYGROUND__",
|
|
403
|
-
" if (api && typeof api.reloadRoutes === 'function') {",
|
|
404
|
-
" api.reloadRoutes()",
|
|
405
|
-
" return",
|
|
406
|
-
" }",
|
|
407
|
-
" window.location.reload()",
|
|
408
|
-
" })",
|
|
409
|
-
"}).catch(() => {})",
|
|
410
|
-
"<\/script>"
|
|
411
|
-
].join("\n");
|
|
412
|
-
if (html.includes("</body>")) {
|
|
413
|
-
return html.replace("</body>", `${snippet}
|
|
414
|
-
</body>`);
|
|
415
|
-
}
|
|
416
|
-
return `${html}
|
|
417
|
-
${snippet}`;
|
|
418
|
-
}
|
|
419
|
-
function injectPlaygroundSw(html, script) {
|
|
420
|
-
if (!script) {
|
|
421
|
-
return html;
|
|
422
|
-
}
|
|
423
|
-
if (html.includes("mokup-playground-sw")) {
|
|
424
|
-
return html;
|
|
425
|
-
}
|
|
426
|
-
const snippet = [
|
|
427
|
-
'<script type="module" id="mokup-playground-sw">',
|
|
428
|
-
script,
|
|
429
|
-
"<\/script>"
|
|
430
|
-
].join("\n");
|
|
431
|
-
if (html.includes("</head>")) {
|
|
432
|
-
return html.replace("</head>", `${snippet}
|
|
433
|
-
</head>`);
|
|
434
|
-
}
|
|
435
|
-
if (html.includes("</body>")) {
|
|
436
|
-
return html.replace("</body>", `${snippet}
|
|
437
|
-
</body>`);
|
|
438
|
-
}
|
|
439
|
-
return `${html}
|
|
440
|
-
${snippet}`;
|
|
441
|
-
}
|
|
442
|
-
function isViteDevServer(server) {
|
|
443
|
-
return !!server && "ws" in server;
|
|
444
|
-
}
|
|
445
|
-
function resolvePlaygroundOptions(playground) {
|
|
446
|
-
if (playground === false) {
|
|
447
|
-
return { enabled: false, path: "/_mokup" };
|
|
448
|
-
}
|
|
449
|
-
if (playground && typeof playground === "object") {
|
|
450
|
-
return {
|
|
451
|
-
enabled: playground.enabled !== false,
|
|
452
|
-
path: normalizePlaygroundPath(playground.path)
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
return { enabled: true, path: "/_mokup" };
|
|
456
|
-
}
|
|
457
299
|
function resolvePlaygroundDist() {
|
|
458
300
|
const pkgPath = require$1.resolve("@mokup/playground/package.json");
|
|
459
301
|
return pathe.join(pkgPath, "..", "dist");
|
|
@@ -468,6 +310,7 @@ function sendFile(res, content, contentType) {
|
|
|
468
310
|
res.setHeader("Content-Type", contentType);
|
|
469
311
|
res.end(content);
|
|
470
312
|
}
|
|
313
|
+
|
|
471
314
|
function toPosixPath(value) {
|
|
472
315
|
return value.replace(/\\/g, "/");
|
|
473
316
|
}
|
|
@@ -509,41 +352,6 @@ function resolveGroupRoot(dirs, serverRoot) {
|
|
|
509
352
|
}
|
|
510
353
|
return common;
|
|
511
354
|
}
|
|
512
|
-
const disabledReasonSet = /* @__PURE__ */ new Set([
|
|
513
|
-
"disabled",
|
|
514
|
-
"disabled-dir",
|
|
515
|
-
"exclude",
|
|
516
|
-
"ignore-prefix",
|
|
517
|
-
"include",
|
|
518
|
-
"unknown"
|
|
519
|
-
]);
|
|
520
|
-
const ignoredReasonSet = /* @__PURE__ */ new Set([
|
|
521
|
-
"unsupported",
|
|
522
|
-
"invalid-route",
|
|
523
|
-
"unknown"
|
|
524
|
-
]);
|
|
525
|
-
function normalizeDisabledReason(reason) {
|
|
526
|
-
if (reason && disabledReasonSet.has(reason)) {
|
|
527
|
-
return reason;
|
|
528
|
-
}
|
|
529
|
-
return "unknown";
|
|
530
|
-
}
|
|
531
|
-
function normalizeIgnoredReason(reason) {
|
|
532
|
-
if (reason && ignoredReasonSet.has(reason)) {
|
|
533
|
-
return reason;
|
|
534
|
-
}
|
|
535
|
-
return "unknown";
|
|
536
|
-
}
|
|
537
|
-
function formatRouteFile(file, root) {
|
|
538
|
-
if (!root) {
|
|
539
|
-
return toPosixPath(file);
|
|
540
|
-
}
|
|
541
|
-
const rel = toPosixPath(pathe.relative(root, file));
|
|
542
|
-
if (!rel || rel.startsWith("..")) {
|
|
543
|
-
return toPosixPath(file);
|
|
544
|
-
}
|
|
545
|
-
return rel;
|
|
546
|
-
}
|
|
547
355
|
function resolveGroups(dirs, root) {
|
|
548
356
|
const groups = [];
|
|
549
357
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -578,63 +386,204 @@ function resolveRouteGroup(routeFile, groups) {
|
|
|
578
386
|
}
|
|
579
387
|
return matched;
|
|
580
388
|
}
|
|
581
|
-
function
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
return {
|
|
585
|
-
method: route.method,
|
|
586
|
-
url: route.template,
|
|
587
|
-
file: formatRouteFile(route.file, root),
|
|
588
|
-
type: typeof route.handler === "function" ? "handler" : "static",
|
|
589
|
-
status: route.status,
|
|
590
|
-
delay: route.delay,
|
|
591
|
-
middlewareCount: middlewareSources?.length ?? 0,
|
|
592
|
-
middlewares: middlewareSources,
|
|
593
|
-
groupKey: matchedGroup?.key,
|
|
594
|
-
group: matchedGroup?.label
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
function toPlaygroundDisabledRoute(route, root, groups) {
|
|
598
|
-
const matchedGroup = resolveRouteGroup(route.file, groups);
|
|
599
|
-
const disabled = {
|
|
600
|
-
file: formatRouteFile(route.file, root),
|
|
601
|
-
reason: normalizeDisabledReason(route.reason)
|
|
602
|
-
};
|
|
603
|
-
if (typeof route.method !== "undefined") {
|
|
604
|
-
disabled.method = route.method;
|
|
605
|
-
}
|
|
606
|
-
if (typeof route.url !== "undefined") {
|
|
607
|
-
disabled.url = route.url;
|
|
389
|
+
function formatRouteFile(file, root) {
|
|
390
|
+
if (!root) {
|
|
391
|
+
return toPosixPath(file);
|
|
608
392
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
393
|
+
const rel = toPosixPath(pathe.relative(root, file));
|
|
394
|
+
if (!rel || rel.startsWith("..")) {
|
|
395
|
+
return toPosixPath(file);
|
|
612
396
|
}
|
|
613
|
-
return
|
|
397
|
+
return rel;
|
|
614
398
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
reason: normalizeIgnoredReason(route.reason)
|
|
620
|
-
};
|
|
621
|
-
if (matchedGroup) {
|
|
622
|
-
ignored.groupKey = matchedGroup.key;
|
|
623
|
-
ignored.group = matchedGroup.label;
|
|
399
|
+
|
|
400
|
+
function injectPlaygroundHmr(html, base) {
|
|
401
|
+
if (html.includes("mokup-playground-hmr")) {
|
|
402
|
+
return html;
|
|
624
403
|
}
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
404
|
+
const normalizedBase = normalizeBase(base);
|
|
405
|
+
const clientPath = `${normalizedBase}/@vite/client`;
|
|
406
|
+
const snippet = [
|
|
407
|
+
'<script type="module" id="mokup-playground-hmr">',
|
|
408
|
+
`import('${clientPath}').then(({ createHotContext }) => {`,
|
|
409
|
+
" const hot = createHotContext('/@mokup/playground')",
|
|
410
|
+
" hot.on('mokup:routes-changed', () => {",
|
|
411
|
+
" const api = window.__MOKUP_PLAYGROUND__",
|
|
412
|
+
" if (api && typeof api.reloadRoutes === 'function') {",
|
|
413
|
+
" api.reloadRoutes()",
|
|
414
|
+
" return",
|
|
415
|
+
" }",
|
|
416
|
+
" window.location.reload()",
|
|
417
|
+
" })",
|
|
418
|
+
"}).catch(() => {})",
|
|
419
|
+
"<\/script>"
|
|
420
|
+
].join("\n");
|
|
421
|
+
if (html.includes("</body>")) {
|
|
422
|
+
return html.replace("</body>", `${snippet}
|
|
423
|
+
</body>`);
|
|
635
424
|
}
|
|
636
|
-
return
|
|
637
|
-
}
|
|
425
|
+
return `${html}
|
|
426
|
+
${snippet}`;
|
|
427
|
+
}
|
|
428
|
+
function injectPlaygroundSw(html, script) {
|
|
429
|
+
if (!script) {
|
|
430
|
+
return html;
|
|
431
|
+
}
|
|
432
|
+
if (html.includes("mokup-playground-sw")) {
|
|
433
|
+
return html;
|
|
434
|
+
}
|
|
435
|
+
const snippet = [
|
|
436
|
+
'<script type="module" id="mokup-playground-sw">',
|
|
437
|
+
script,
|
|
438
|
+
"<\/script>"
|
|
439
|
+
].join("\n");
|
|
440
|
+
if (html.includes("</head>")) {
|
|
441
|
+
return html.replace("</head>", `${snippet}
|
|
442
|
+
</head>`);
|
|
443
|
+
}
|
|
444
|
+
if (html.includes("</body>")) {
|
|
445
|
+
return html.replace("</body>", `${snippet}
|
|
446
|
+
</body>`);
|
|
447
|
+
}
|
|
448
|
+
return `${html}
|
|
449
|
+
${snippet}`;
|
|
450
|
+
}
|
|
451
|
+
function isViteDevServer(server) {
|
|
452
|
+
return !!server && "ws" in server;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const disabledReasonSet = /* @__PURE__ */ new Set([
|
|
456
|
+
"disabled",
|
|
457
|
+
"disabled-dir",
|
|
458
|
+
"exclude",
|
|
459
|
+
"ignore-prefix",
|
|
460
|
+
"include",
|
|
461
|
+
"unknown"
|
|
462
|
+
]);
|
|
463
|
+
const ignoredReasonSet = /* @__PURE__ */ new Set([
|
|
464
|
+
"unsupported",
|
|
465
|
+
"invalid-route",
|
|
466
|
+
"unknown"
|
|
467
|
+
]);
|
|
468
|
+
function normalizeDisabledReason(reason) {
|
|
469
|
+
if (reason && disabledReasonSet.has(reason)) {
|
|
470
|
+
return reason;
|
|
471
|
+
}
|
|
472
|
+
return "unknown";
|
|
473
|
+
}
|
|
474
|
+
function normalizeIgnoredReason(reason) {
|
|
475
|
+
if (reason && ignoredReasonSet.has(reason)) {
|
|
476
|
+
return reason;
|
|
477
|
+
}
|
|
478
|
+
return "unknown";
|
|
479
|
+
}
|
|
480
|
+
function toPlaygroundRoute(route, root, groups) {
|
|
481
|
+
const matchedGroup = resolveRouteGroup(route.file, groups);
|
|
482
|
+
const preSources = route.middlewares?.filter((entry) => entry.position === "pre").map((entry) => formatRouteFile(entry.source, root)) ?? [];
|
|
483
|
+
const postSources = route.middlewares?.filter((entry) => entry.position === "post").map((entry) => formatRouteFile(entry.source, root)) ?? [];
|
|
484
|
+
const normalSources = route.middlewares?.filter((entry) => entry.position !== "pre" && entry.position !== "post").map((entry) => formatRouteFile(entry.source, root)) ?? [];
|
|
485
|
+
const combinedSources = [
|
|
486
|
+
...preSources,
|
|
487
|
+
...normalSources,
|
|
488
|
+
...postSources
|
|
489
|
+
];
|
|
490
|
+
const configChain = route.configChain?.map((entry) => formatRouteFile(entry, root)) ?? [];
|
|
491
|
+
return {
|
|
492
|
+
method: route.method,
|
|
493
|
+
url: route.template,
|
|
494
|
+
file: formatRouteFile(route.file, root),
|
|
495
|
+
type: typeof route.handler === "function" ? "handler" : "static",
|
|
496
|
+
status: route.status,
|
|
497
|
+
delay: route.delay,
|
|
498
|
+
middlewareCount: combinedSources.length,
|
|
499
|
+
middlewares: combinedSources,
|
|
500
|
+
preMiddlewareCount: preSources.length,
|
|
501
|
+
normalMiddlewareCount: normalSources.length,
|
|
502
|
+
postMiddlewareCount: postSources.length,
|
|
503
|
+
preMiddlewares: preSources,
|
|
504
|
+
normalMiddlewares: normalSources,
|
|
505
|
+
postMiddlewares: postSources,
|
|
506
|
+
configChain: configChain.length > 0 ? configChain : void 0,
|
|
507
|
+
groupKey: matchedGroup?.key,
|
|
508
|
+
group: matchedGroup?.label
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
function toPlaygroundDisabledRoute(route, root, groups) {
|
|
512
|
+
const matchedGroup = resolveRouteGroup(route.file, groups);
|
|
513
|
+
const disabled = {
|
|
514
|
+
file: formatRouteFile(route.file, root),
|
|
515
|
+
reason: normalizeDisabledReason(route.reason)
|
|
516
|
+
};
|
|
517
|
+
if (typeof route.method !== "undefined") {
|
|
518
|
+
disabled.method = route.method;
|
|
519
|
+
}
|
|
520
|
+
if (typeof route.url !== "undefined") {
|
|
521
|
+
disabled.url = route.url;
|
|
522
|
+
}
|
|
523
|
+
if (route.configChain && route.configChain.length > 0) {
|
|
524
|
+
disabled.configChain = route.configChain.map((entry) => formatRouteFile(entry, root));
|
|
525
|
+
}
|
|
526
|
+
if (route.decisionChain && route.decisionChain.length > 0) {
|
|
527
|
+
disabled.decisionChain = formatDecisionChain(route.decisionChain, root);
|
|
528
|
+
}
|
|
529
|
+
if (route.effectiveConfig && Object.keys(route.effectiveConfig).length > 0) {
|
|
530
|
+
disabled.effectiveConfig = route.effectiveConfig;
|
|
531
|
+
}
|
|
532
|
+
if (matchedGroup) {
|
|
533
|
+
disabled.groupKey = matchedGroup.key;
|
|
534
|
+
disabled.group = matchedGroup.label;
|
|
535
|
+
}
|
|
536
|
+
return disabled;
|
|
537
|
+
}
|
|
538
|
+
function toPlaygroundIgnoredRoute(route, root, groups) {
|
|
539
|
+
const matchedGroup = resolveRouteGroup(route.file, groups);
|
|
540
|
+
const ignored = {
|
|
541
|
+
file: formatRouteFile(route.file, root),
|
|
542
|
+
reason: normalizeIgnoredReason(route.reason)
|
|
543
|
+
};
|
|
544
|
+
if (matchedGroup) {
|
|
545
|
+
ignored.groupKey = matchedGroup.key;
|
|
546
|
+
ignored.group = matchedGroup.label;
|
|
547
|
+
}
|
|
548
|
+
if (route.configChain && route.configChain.length > 0) {
|
|
549
|
+
ignored.configChain = route.configChain.map((entry) => formatRouteFile(entry, root));
|
|
550
|
+
}
|
|
551
|
+
if (route.decisionChain && route.decisionChain.length > 0) {
|
|
552
|
+
ignored.decisionChain = formatDecisionChain(route.decisionChain, root);
|
|
553
|
+
}
|
|
554
|
+
if (route.effectiveConfig && Object.keys(route.effectiveConfig).length > 0) {
|
|
555
|
+
ignored.effectiveConfig = route.effectiveConfig;
|
|
556
|
+
}
|
|
557
|
+
return ignored;
|
|
558
|
+
}
|
|
559
|
+
function toPlaygroundConfigFile(entry, root, groups) {
|
|
560
|
+
const matchedGroup = resolveRouteGroup(entry.file, groups);
|
|
561
|
+
const configFile = {
|
|
562
|
+
file: formatRouteFile(entry.file, root)
|
|
563
|
+
};
|
|
564
|
+
if (matchedGroup) {
|
|
565
|
+
configFile.groupKey = matchedGroup.key;
|
|
566
|
+
configFile.group = matchedGroup.label;
|
|
567
|
+
}
|
|
568
|
+
return configFile;
|
|
569
|
+
}
|
|
570
|
+
function formatDecisionChain(chain, root) {
|
|
571
|
+
return chain.map((entry) => {
|
|
572
|
+
const formatted = {
|
|
573
|
+
step: entry.step,
|
|
574
|
+
result: entry.result
|
|
575
|
+
};
|
|
576
|
+
if (typeof entry.detail !== "undefined") {
|
|
577
|
+
formatted.detail = entry.detail;
|
|
578
|
+
}
|
|
579
|
+
if (typeof entry.source !== "undefined") {
|
|
580
|
+
const source = entry.source;
|
|
581
|
+
formatted.source = source && pathe.isAbsolute(source) ? formatRouteFile(source, root) : source;
|
|
582
|
+
}
|
|
583
|
+
return formatted;
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
|
|
638
587
|
function createPlaygroundMiddleware(params) {
|
|
639
588
|
const distDir = resolvePlaygroundDist();
|
|
640
589
|
const playgroundPath = params.config.path;
|
|
@@ -718,938 +667,1400 @@ function createPlaygroundMiddleware(params) {
|
|
|
718
667
|
};
|
|
719
668
|
}
|
|
720
669
|
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
if (!
|
|
725
|
-
return
|
|
670
|
+
const defaultSwPath = "/mokup-sw.js";
|
|
671
|
+
const defaultSwScope = "/";
|
|
672
|
+
function normalizeSwPath(path) {
|
|
673
|
+
if (!path) {
|
|
674
|
+
return defaultSwPath;
|
|
726
675
|
}
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
676
|
+
return path.startsWith("/") ? path : `/${path}`;
|
|
677
|
+
}
|
|
678
|
+
function normalizeSwScope(scope) {
|
|
679
|
+
if (!scope) {
|
|
680
|
+
return defaultSwScope;
|
|
730
681
|
}
|
|
731
|
-
|
|
732
|
-
|
|
682
|
+
return scope.startsWith("/") ? scope : `/${scope}`;
|
|
683
|
+
}
|
|
684
|
+
function normalizeBasePath(value) {
|
|
685
|
+
if (!value) {
|
|
686
|
+
return "/";
|
|
733
687
|
}
|
|
734
|
-
|
|
735
|
-
|
|
688
|
+
const normalized = value.startsWith("/") ? value : `/${value}`;
|
|
689
|
+
if (normalized.length > 1 && normalized.endsWith("/")) {
|
|
690
|
+
return normalized.slice(0, -1);
|
|
736
691
|
}
|
|
737
|
-
return
|
|
692
|
+
return normalized;
|
|
738
693
|
}
|
|
739
|
-
function
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
694
|
+
function resolveSwConfigFromEntries(entries, logger) {
|
|
695
|
+
let path = defaultSwPath;
|
|
696
|
+
let scope = defaultSwScope;
|
|
697
|
+
let register = true;
|
|
698
|
+
let unregister = false;
|
|
699
|
+
const basePaths = [];
|
|
700
|
+
let hasPath = false;
|
|
701
|
+
let hasScope = false;
|
|
702
|
+
let hasRegister = false;
|
|
703
|
+
let hasUnregister = false;
|
|
704
|
+
for (const entry of entries) {
|
|
705
|
+
const config = entry.sw;
|
|
706
|
+
if (config?.path) {
|
|
707
|
+
const next = normalizeSwPath(config.path);
|
|
708
|
+
if (!hasPath) {
|
|
709
|
+
path = next;
|
|
710
|
+
hasPath = true;
|
|
711
|
+
} else if (path !== next) {
|
|
712
|
+
logger.warn(`SW path "${next}" ignored; using "${path}".`);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (config?.scope) {
|
|
716
|
+
const next = normalizeSwScope(config.scope);
|
|
717
|
+
if (!hasScope) {
|
|
718
|
+
scope = next;
|
|
719
|
+
hasScope = true;
|
|
720
|
+
} else if (scope !== next) {
|
|
721
|
+
logger.warn(`SW scope "${next}" ignored; using "${scope}".`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (typeof config?.register === "boolean") {
|
|
725
|
+
if (!hasRegister) {
|
|
726
|
+
register = config.register;
|
|
727
|
+
hasRegister = true;
|
|
728
|
+
} else if (register !== config.register) {
|
|
729
|
+
logger.warn(
|
|
730
|
+
`SW register="${String(config.register)}" ignored; using "${String(register)}".`
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
if (typeof config?.unregister === "boolean") {
|
|
735
|
+
if (!hasUnregister) {
|
|
736
|
+
unregister = config.unregister;
|
|
737
|
+
hasUnregister = true;
|
|
738
|
+
} else if (unregister !== config.unregister) {
|
|
739
|
+
logger.warn(
|
|
740
|
+
`SW unregister="${String(config.unregister)}" ignored; using "${String(unregister)}".`
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (typeof config?.basePath !== "undefined") {
|
|
745
|
+
const values = Array.isArray(config.basePath) ? config.basePath : [config.basePath];
|
|
746
|
+
for (const value of values) {
|
|
747
|
+
basePaths.push(normalizeBasePath(value));
|
|
748
|
+
}
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
const normalizedPrefix = normalizePrefix(entry.prefix ?? "");
|
|
752
|
+
if (normalizedPrefix) {
|
|
753
|
+
basePaths.push(normalizedPrefix);
|
|
754
|
+
}
|
|
748
755
|
}
|
|
749
756
|
return {
|
|
750
|
-
|
|
751
|
-
|
|
757
|
+
path,
|
|
758
|
+
scope,
|
|
759
|
+
register,
|
|
760
|
+
unregister,
|
|
761
|
+
basePaths: Array.from(new Set(basePaths))
|
|
752
762
|
};
|
|
753
763
|
}
|
|
754
|
-
function
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
const withoutExt = rel.slice(0, rel.length - ext.length);
|
|
758
|
-
const dir = pathe.dirname(withoutExt);
|
|
759
|
-
const base = pathe.basename(withoutExt);
|
|
760
|
-
const { name, method } = stripMethodSuffix(base);
|
|
761
|
-
const resolvedMethod = method ?? (jsonExtensions.has(ext) ? "GET" : void 0);
|
|
762
|
-
if (!resolvedMethod) {
|
|
763
|
-
logger.warn(`Skip mock without method suffix: ${file}`);
|
|
764
|
-
return null;
|
|
765
|
-
}
|
|
766
|
-
if (!name) {
|
|
767
|
-
logger.warn(`Skip mock with empty route name: ${file}`);
|
|
764
|
+
function resolveSwConfig(options, logger) {
|
|
765
|
+
const swEntries = options.filter((entry) => entry.mode === "sw");
|
|
766
|
+
if (swEntries.length === 0) {
|
|
768
767
|
return null;
|
|
769
768
|
}
|
|
770
|
-
|
|
771
|
-
const segments = toPosix(joined).split("/");
|
|
772
|
-
if (segments.at(-1) === "index") {
|
|
773
|
-
segments.pop();
|
|
774
|
-
}
|
|
775
|
-
const template = segments.length === 0 ? "/" : `/${segments.join("/")}`;
|
|
776
|
-
const parsed = runtime.parseRouteTemplate(template);
|
|
777
|
-
if (parsed.errors.length > 0) {
|
|
778
|
-
for (const error of parsed.errors) {
|
|
779
|
-
logger.warn(`${error} in ${file}`);
|
|
780
|
-
}
|
|
781
|
-
return null;
|
|
782
|
-
}
|
|
783
|
-
for (const warning of parsed.warnings) {
|
|
784
|
-
logger.warn(`${warning} in ${file}`);
|
|
785
|
-
}
|
|
786
|
-
return {
|
|
787
|
-
template: parsed.template,
|
|
788
|
-
method: resolvedMethod,
|
|
789
|
-
tokens: parsed.tokens,
|
|
790
|
-
score: parsed.score
|
|
791
|
-
};
|
|
769
|
+
return resolveSwConfigFromEntries(swEntries, logger);
|
|
792
770
|
}
|
|
793
|
-
function
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
771
|
+
function resolveSwUnregisterConfig(options, logger) {
|
|
772
|
+
return resolveSwConfigFromEntries(options, logger);
|
|
773
|
+
}
|
|
774
|
+
function buildSwScript(params) {
|
|
775
|
+
const { routes, root } = params;
|
|
776
|
+
const runtimeImportPath = params.runtimeImportPath ?? "mokup/runtime";
|
|
777
|
+
const loggerImportPath = params.loggerImportPath ?? "@mokup/shared/logger";
|
|
778
|
+
const basePaths = params.basePaths ?? [];
|
|
779
|
+
const resolveModulePath = params.resolveModulePath ?? toViteImportPath;
|
|
780
|
+
const { manifest, modules } = buildManifestData({
|
|
781
|
+
routes,
|
|
782
|
+
root,
|
|
783
|
+
resolveModulePath
|
|
784
|
+
});
|
|
785
|
+
const imports = [
|
|
786
|
+
`import { createLogger } from ${JSON.stringify(loggerImportPath)}`,
|
|
787
|
+
`import { createRuntimeApp, handle } from ${JSON.stringify(runtimeImportPath)}`
|
|
788
|
+
];
|
|
789
|
+
const moduleEntries = [];
|
|
790
|
+
let moduleIndex = 0;
|
|
791
|
+
for (const entry of modules) {
|
|
792
|
+
const name = `module${moduleIndex++}`;
|
|
793
|
+
imports.push(`import * as ${name} from '${entry.id}'`);
|
|
794
|
+
moduleEntries.push({ id: entry.id, name, kind: entry.kind });
|
|
798
795
|
}
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
796
|
+
const lines = [];
|
|
797
|
+
lines.push(...imports, "");
|
|
798
|
+
lines.push(
|
|
799
|
+
"const logger = createLogger()",
|
|
800
|
+
"",
|
|
801
|
+
"const resolveModuleExport = (mod) => mod?.default ?? mod",
|
|
802
|
+
"",
|
|
803
|
+
"const toRuntimeRule = (value) => {",
|
|
804
|
+
" if (typeof value === 'undefined') {",
|
|
805
|
+
" return null",
|
|
806
|
+
" }",
|
|
807
|
+
" if (typeof value === 'function') {",
|
|
808
|
+
" return { response: value }",
|
|
809
|
+
" }",
|
|
810
|
+
" if (value === null) {",
|
|
811
|
+
" return { response: null }",
|
|
812
|
+
" }",
|
|
813
|
+
" if (typeof value === 'object') {",
|
|
814
|
+
" if ('response' in value) {",
|
|
815
|
+
" return value",
|
|
816
|
+
" }",
|
|
817
|
+
" if ('handler' in value) {",
|
|
818
|
+
" const handlerRule = value",
|
|
819
|
+
" return {",
|
|
820
|
+
" response: handlerRule.handler,",
|
|
821
|
+
" ...(typeof handlerRule.status === 'number' ? { status: handlerRule.status } : {}),",
|
|
822
|
+
" ...(handlerRule.headers ? { headers: handlerRule.headers } : {}),",
|
|
823
|
+
" ...(typeof handlerRule.delay === 'number' ? { delay: handlerRule.delay } : {}),",
|
|
824
|
+
" }",
|
|
825
|
+
" }",
|
|
826
|
+
" return { response: value }",
|
|
827
|
+
" }",
|
|
828
|
+
" return { response: value }",
|
|
829
|
+
"}",
|
|
830
|
+
"",
|
|
831
|
+
"const toRuntimeRules = (value) => {",
|
|
832
|
+
" if (typeof value === 'undefined') {",
|
|
833
|
+
" return []",
|
|
834
|
+
" }",
|
|
835
|
+
" if (Array.isArray(value)) {",
|
|
836
|
+
" return value.map(toRuntimeRule).filter(Boolean)",
|
|
837
|
+
" }",
|
|
838
|
+
" const rule = toRuntimeRule(value)",
|
|
839
|
+
" return rule ? [rule] : []",
|
|
840
|
+
"}",
|
|
841
|
+
""
|
|
842
|
+
);
|
|
843
|
+
lines.push(
|
|
844
|
+
`const manifest = ${JSON.stringify(manifest, null, 2)}`,
|
|
845
|
+
""
|
|
846
|
+
);
|
|
847
|
+
if (moduleEntries.length > 0) {
|
|
848
|
+
lines.push("const moduleMap = {");
|
|
849
|
+
for (const entry of moduleEntries) {
|
|
850
|
+
if (entry.kind === "rule") {
|
|
851
|
+
lines.push(
|
|
852
|
+
` ${JSON.stringify(entry.id)}: { default: toRuntimeRules(resolveModuleExport(${entry.name})) },`
|
|
853
|
+
);
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
lines.push(
|
|
857
|
+
` ${JSON.stringify(entry.id)}: ${entry.name},`
|
|
858
|
+
);
|
|
804
859
|
}
|
|
805
|
-
|
|
806
|
-
}
|
|
807
|
-
for (const warning of parsed.warnings) {
|
|
808
|
-
params.logger.warn(`${warning} in ${params.file}`);
|
|
809
|
-
}
|
|
810
|
-
const route = {
|
|
811
|
-
file: params.file,
|
|
812
|
-
template: parsed.template,
|
|
813
|
-
method,
|
|
814
|
-
tokens: parsed.tokens,
|
|
815
|
-
score: parsed.score,
|
|
816
|
-
handler: params.rule.handler
|
|
817
|
-
};
|
|
818
|
-
if (typeof params.rule.status === "number") {
|
|
819
|
-
route.status = params.rule.status;
|
|
820
|
-
}
|
|
821
|
-
if (params.rule.headers) {
|
|
822
|
-
route.headers = params.rule.headers;
|
|
823
|
-
}
|
|
824
|
-
if (typeof params.rule.delay === "number") {
|
|
825
|
-
route.delay = params.rule.delay;
|
|
860
|
+
lines.push("}", "");
|
|
826
861
|
}
|
|
827
|
-
|
|
862
|
+
const runtimeOptions = moduleEntries.length > 0 ? "{ manifest, moduleMap }" : "{ manifest }";
|
|
863
|
+
lines.push(
|
|
864
|
+
`const basePaths = ${JSON.stringify(basePaths)}`,
|
|
865
|
+
"",
|
|
866
|
+
"self.addEventListener('install', () => {",
|
|
867
|
+
" self.skipWaiting()",
|
|
868
|
+
"})",
|
|
869
|
+
"",
|
|
870
|
+
"self.addEventListener('activate', (event) => {",
|
|
871
|
+
" event.waitUntil(self.clients.claim())",
|
|
872
|
+
"})",
|
|
873
|
+
"",
|
|
874
|
+
"const shouldHandle = (request) => {",
|
|
875
|
+
" if (!basePaths || basePaths.length === 0) {",
|
|
876
|
+
" return true",
|
|
877
|
+
" }",
|
|
878
|
+
" const pathname = new URL(request.url).pathname",
|
|
879
|
+
" return basePaths.some((basePath) => {",
|
|
880
|
+
" if (basePath === '/') {",
|
|
881
|
+
" return true",
|
|
882
|
+
" }",
|
|
883
|
+
" return pathname === basePath || pathname.startsWith(basePath + '/')",
|
|
884
|
+
" })",
|
|
885
|
+
"}",
|
|
886
|
+
"",
|
|
887
|
+
"const registerHandler = async () => {",
|
|
888
|
+
` const app = await createRuntimeApp(${runtimeOptions})`,
|
|
889
|
+
" const handler = handle(app)",
|
|
890
|
+
" self.addEventListener('fetch', (event) => {",
|
|
891
|
+
" if (!shouldHandle(event.request)) {",
|
|
892
|
+
" return",
|
|
893
|
+
" }",
|
|
894
|
+
" handler(event)",
|
|
895
|
+
" })",
|
|
896
|
+
"}",
|
|
897
|
+
"",
|
|
898
|
+
"registerHandler().catch((error) => {",
|
|
899
|
+
" logger.error('Failed to build service worker app:', error)",
|
|
900
|
+
"})",
|
|
901
|
+
""
|
|
902
|
+
);
|
|
903
|
+
return lines.join("\n");
|
|
828
904
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
905
|
+
|
|
906
|
+
function toHonoPath(route) {
|
|
907
|
+
if (!route.tokens || route.tokens.length === 0) {
|
|
908
|
+
return "/";
|
|
909
|
+
}
|
|
910
|
+
const segments = route.tokens.map((token) => {
|
|
911
|
+
if (token.type === "static") {
|
|
912
|
+
return token.value;
|
|
833
913
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
return scoreCompare;
|
|
914
|
+
if (token.type === "param") {
|
|
915
|
+
return `:${token.name}`;
|
|
837
916
|
}
|
|
838
|
-
|
|
917
|
+
if (token.type === "catchall") {
|
|
918
|
+
return `:${token.name}{.+}`;
|
|
919
|
+
}
|
|
920
|
+
return `:${token.name}{.+}?`;
|
|
839
921
|
});
|
|
922
|
+
return `/${segments.join("/")}`;
|
|
840
923
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
return require$1(file);
|
|
848
|
-
}
|
|
849
|
-
if (ext === ".js" || ext === ".mjs") {
|
|
850
|
-
return import(`${node_url.pathToFileURL(file).href}?t=${Date.now()}`);
|
|
924
|
+
function isValidStatus(status) {
|
|
925
|
+
return typeof status === "number" && Number.isFinite(status) && status >= 200 && status <= 599;
|
|
926
|
+
}
|
|
927
|
+
function resolveStatus(routeStatus, responseStatus) {
|
|
928
|
+
if (isValidStatus(routeStatus)) {
|
|
929
|
+
return routeStatus;
|
|
851
930
|
}
|
|
852
|
-
if (
|
|
853
|
-
|
|
854
|
-
entryPoints: [file],
|
|
855
|
-
bundle: true,
|
|
856
|
-
format: "esm",
|
|
857
|
-
platform: "node",
|
|
858
|
-
sourcemap: "inline",
|
|
859
|
-
target: "es2020",
|
|
860
|
-
write: false
|
|
861
|
-
});
|
|
862
|
-
const output = result.outputFiles[0];
|
|
863
|
-
const code = output?.text ?? "";
|
|
864
|
-
const dataUrl = `data:text/javascript;base64,${node_buffer.Buffer.from(code).toString(
|
|
865
|
-
"base64"
|
|
866
|
-
)}`;
|
|
867
|
-
return import(`${dataUrl}#${Date.now()}`);
|
|
931
|
+
if (isValidStatus(responseStatus)) {
|
|
932
|
+
return responseStatus;
|
|
868
933
|
}
|
|
869
|
-
return
|
|
934
|
+
return 200;
|
|
870
935
|
}
|
|
871
|
-
|
|
872
|
-
const
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
936
|
+
function applyRouteOverrides(response, route) {
|
|
937
|
+
const headers = new Headers(response.headers);
|
|
938
|
+
const hasHeaders = !!route.headers && Object.keys(route.headers).length > 0;
|
|
939
|
+
if (route.headers) {
|
|
940
|
+
for (const [key, value] of Object.entries(route.headers)) {
|
|
941
|
+
headers.set(key, value);
|
|
877
942
|
}
|
|
878
|
-
return asDevServer.ssrLoadModule(file);
|
|
879
|
-
}
|
|
880
|
-
return loadModule$1(file);
|
|
881
|
-
}
|
|
882
|
-
function getConfigFileCandidates(dir) {
|
|
883
|
-
return configExtensions.map((extension) => pathe.join(dir, `index.config${extension}`));
|
|
884
|
-
}
|
|
885
|
-
async function findConfigFile(dir, cache) {
|
|
886
|
-
const cached = cache.get(dir);
|
|
887
|
-
if (cached !== void 0) {
|
|
888
|
-
return cached;
|
|
889
943
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
cache.set(dir, candidate);
|
|
894
|
-
return candidate;
|
|
895
|
-
} catch {
|
|
896
|
-
continue;
|
|
897
|
-
}
|
|
944
|
+
const status = resolveStatus(route.status, response.status);
|
|
945
|
+
if (status === response.status && !hasHeaders) {
|
|
946
|
+
return response;
|
|
898
947
|
}
|
|
899
|
-
|
|
900
|
-
return null;
|
|
948
|
+
return new Response(response.body, { status, headers });
|
|
901
949
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
return null;
|
|
950
|
+
function resolveResponse(value, fallback) {
|
|
951
|
+
if (value instanceof Response) {
|
|
952
|
+
return value;
|
|
906
953
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
954
|
+
if (value && typeof value === "object" && "res" in value) {
|
|
955
|
+
const resolved = value.res;
|
|
956
|
+
if (resolved instanceof Response) {
|
|
957
|
+
return resolved;
|
|
958
|
+
}
|
|
910
959
|
}
|
|
911
|
-
return
|
|
960
|
+
return fallback;
|
|
912
961
|
}
|
|
913
|
-
function
|
|
914
|
-
if (
|
|
915
|
-
return
|
|
962
|
+
function normalizeHandlerValue(c, value) {
|
|
963
|
+
if (value instanceof Response) {
|
|
964
|
+
return value;
|
|
916
965
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
966
|
+
if (typeof value === "undefined") {
|
|
967
|
+
const response = c.body(null);
|
|
968
|
+
if (response.status === 200) {
|
|
969
|
+
return new Response(response.body, {
|
|
970
|
+
status: 204,
|
|
971
|
+
headers: response.headers
|
|
972
|
+
});
|
|
923
973
|
}
|
|
924
|
-
|
|
974
|
+
return response;
|
|
925
975
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
async function resolveDirectoryConfig(params) {
|
|
929
|
-
const { file, rootDir, server, logger, configCache, fileCache } = params;
|
|
930
|
-
const resolvedRoot = pathe.normalize(rootDir);
|
|
931
|
-
const resolvedFileDir = pathe.normalize(pathe.dirname(file));
|
|
932
|
-
const chain = [];
|
|
933
|
-
let current = resolvedFileDir;
|
|
934
|
-
while (true) {
|
|
935
|
-
chain.push(current);
|
|
936
|
-
if (current === resolvedRoot) {
|
|
937
|
-
break;
|
|
938
|
-
}
|
|
939
|
-
const parent = pathe.dirname(current);
|
|
940
|
-
if (parent === current) {
|
|
941
|
-
break;
|
|
942
|
-
}
|
|
943
|
-
current = parent;
|
|
976
|
+
if (typeof value === "string") {
|
|
977
|
+
return c.text(value);
|
|
944
978
|
}
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
const configPath = await findConfigFile(dir, fileCache);
|
|
949
|
-
if (!configPath) {
|
|
950
|
-
continue;
|
|
951
|
-
}
|
|
952
|
-
let config = configCache.get(configPath);
|
|
953
|
-
if (config === void 0) {
|
|
954
|
-
config = await loadConfig(configPath, server);
|
|
955
|
-
configCache.set(configPath, config);
|
|
956
|
-
}
|
|
957
|
-
if (!config) {
|
|
958
|
-
logger.warn(`Invalid config in ${configPath}`);
|
|
959
|
-
continue;
|
|
960
|
-
}
|
|
961
|
-
if (config.headers) {
|
|
962
|
-
merged.headers = { ...merged.headers ?? {}, ...config.headers };
|
|
963
|
-
}
|
|
964
|
-
if (typeof config.status === "number") {
|
|
965
|
-
merged.status = config.status;
|
|
966
|
-
}
|
|
967
|
-
if (typeof config.delay === "number") {
|
|
968
|
-
merged.delay = config.delay;
|
|
969
|
-
}
|
|
970
|
-
if (typeof config.enabled === "boolean") {
|
|
971
|
-
merged.enabled = config.enabled;
|
|
972
|
-
}
|
|
973
|
-
if (typeof config.ignorePrefix !== "undefined") {
|
|
974
|
-
merged.ignorePrefix = config.ignorePrefix;
|
|
975
|
-
}
|
|
976
|
-
if (typeof config.include !== "undefined") {
|
|
977
|
-
merged.include = config.include;
|
|
978
|
-
}
|
|
979
|
-
if (typeof config.exclude !== "undefined") {
|
|
980
|
-
merged.exclude = config.exclude;
|
|
981
|
-
}
|
|
982
|
-
const normalized = normalizeMiddlewares(config.middleware, configPath, logger);
|
|
983
|
-
if (normalized.length > 0) {
|
|
984
|
-
merged.middlewares.push(...normalized);
|
|
979
|
+
if (value instanceof Uint8Array || value instanceof ArrayBuffer) {
|
|
980
|
+
if (!c.res.headers.get("content-type")) {
|
|
981
|
+
c.header("content-type", "application/octet-stream");
|
|
985
982
|
}
|
|
983
|
+
const data = value instanceof ArrayBuffer ? new Uint8Array(value) : new Uint8Array(value);
|
|
984
|
+
return c.body(data);
|
|
986
985
|
}
|
|
987
|
-
return
|
|
986
|
+
return c.json(value);
|
|
988
987
|
}
|
|
989
|
-
|
|
990
|
-
async
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
988
|
+
function createRouteHandler(route) {
|
|
989
|
+
return async (c) => {
|
|
990
|
+
const value = typeof route.handler === "function" ? await route.handler(c) : route.handler;
|
|
991
|
+
return normalizeHandlerValue(c, value);
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
function createFinalizeMiddleware(route) {
|
|
995
|
+
return async (c, next) => {
|
|
996
|
+
const response = await next();
|
|
997
|
+
const resolved = resolveResponse(response, c.res);
|
|
998
|
+
if (route.delay && route.delay > 0) {
|
|
999
|
+
await delay(route.delay);
|
|
1000
1000
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1001
|
+
const overridden = applyRouteOverrides(resolved, route);
|
|
1002
|
+
c.res = overridden;
|
|
1003
|
+
return overridden;
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
function wrapMiddleware(handler) {
|
|
1007
|
+
return async (c, next) => {
|
|
1008
|
+
const response = await handler(c, next);
|
|
1009
|
+
return resolveResponse(response, c.res);
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
function splitRouteMiddlewares(route) {
|
|
1013
|
+
const before = [];
|
|
1014
|
+
const normal = [];
|
|
1015
|
+
const after = [];
|
|
1016
|
+
for (const entry of route.middlewares ?? []) {
|
|
1017
|
+
const wrapped = wrapMiddleware(entry.handle);
|
|
1018
|
+
if (entry.position === "post") {
|
|
1019
|
+
after.push(wrapped);
|
|
1020
|
+
} else if (entry.position === "pre") {
|
|
1021
|
+
before.push(wrapped);
|
|
1022
|
+
} else {
|
|
1023
|
+
normal.push(wrapped);
|
|
1003
1024
|
}
|
|
1004
1025
|
}
|
|
1026
|
+
return { before, normal, after };
|
|
1005
1027
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1028
|
+
function createHonoApp(routes) {
|
|
1029
|
+
const app = new hono.Hono({ router: new hono.PatternRouter(), strict: false });
|
|
1030
|
+
for (const route of routes) {
|
|
1031
|
+
const { before, normal, after } = splitRouteMiddlewares(route);
|
|
1032
|
+
app.on(
|
|
1033
|
+
route.method,
|
|
1034
|
+
toHonoPath(route),
|
|
1035
|
+
createFinalizeMiddleware(route),
|
|
1036
|
+
...before,
|
|
1037
|
+
...normal,
|
|
1038
|
+
...after,
|
|
1039
|
+
createRouteHandler(route)
|
|
1040
|
+
);
|
|
1012
1041
|
}
|
|
1042
|
+
return app;
|
|
1013
1043
|
}
|
|
1014
|
-
async function
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1044
|
+
async function readRawBody(req) {
|
|
1045
|
+
return await new Promise((resolve, reject) => {
|
|
1046
|
+
const chunks = [];
|
|
1047
|
+
req.on("data", (chunk) => {
|
|
1048
|
+
if (typeof chunk === "string") {
|
|
1049
|
+
chunks.push(node_buffer.Buffer.from(chunk));
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
if (chunk instanceof Uint8Array) {
|
|
1053
|
+
chunks.push(chunk);
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
chunks.push(node_buffer.Buffer.from(String(chunk)));
|
|
1057
|
+
});
|
|
1058
|
+
req.on("end", () => {
|
|
1059
|
+
if (chunks.length === 0) {
|
|
1060
|
+
resolve(null);
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
resolve(node_buffer.Buffer.concat(chunks));
|
|
1064
|
+
});
|
|
1065
|
+
req.on("error", reject);
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
function buildHeaders(headers) {
|
|
1069
|
+
const result = new Headers();
|
|
1070
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
1071
|
+
if (typeof value === "undefined") {
|
|
1018
1072
|
continue;
|
|
1019
1073
|
}
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
if (file.endsWith(".d.ts")) {
|
|
1026
|
-
return false;
|
|
1027
|
-
}
|
|
1028
|
-
if (isConfigFile(file)) {
|
|
1029
|
-
return false;
|
|
1074
|
+
if (Array.isArray(value)) {
|
|
1075
|
+
result.set(key, value.join(","));
|
|
1076
|
+
} else {
|
|
1077
|
+
result.set(key, value);
|
|
1078
|
+
}
|
|
1030
1079
|
}
|
|
1031
|
-
|
|
1032
|
-
return supportedExtensions.has(ext);
|
|
1080
|
+
return result;
|
|
1033
1081
|
}
|
|
1034
|
-
function
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
const
|
|
1039
|
-
|
|
1040
|
-
|
|
1082
|
+
async function toRequest(req) {
|
|
1083
|
+
const url = new URL(req.url ?? "/", "http://mokup.local");
|
|
1084
|
+
const method = req.method ?? "GET";
|
|
1085
|
+
const headers = buildHeaders(req.headers);
|
|
1086
|
+
const init = { method, headers };
|
|
1087
|
+
const rawBody = await readRawBody(req);
|
|
1088
|
+
if (rawBody && method !== "GET" && method !== "HEAD") {
|
|
1089
|
+
init.body = rawBody;
|
|
1041
1090
|
}
|
|
1042
|
-
|
|
1043
|
-
return configExtensions.includes(ext);
|
|
1091
|
+
return new Request(url.toString(), init);
|
|
1044
1092
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
if (ext === ".js" || ext === ".mjs") {
|
|
1054
|
-
return import(`${node_url.pathToFileURL(file).href}?t=${Date.now()}`);
|
|
1055
|
-
}
|
|
1056
|
-
if (ext === ".ts") {
|
|
1057
|
-
const result = await esbuild.build({
|
|
1058
|
-
entryPoints: [file],
|
|
1059
|
-
bundle: true,
|
|
1060
|
-
format: "esm",
|
|
1061
|
-
platform: "node",
|
|
1062
|
-
sourcemap: "inline",
|
|
1063
|
-
target: "es2020",
|
|
1064
|
-
write: false
|
|
1065
|
-
});
|
|
1066
|
-
const output = result.outputFiles[0];
|
|
1067
|
-
const code = output?.text ?? "";
|
|
1068
|
-
const dataUrl = `data:text/javascript;base64,${node_buffer.Buffer.from(code).toString(
|
|
1069
|
-
"base64"
|
|
1070
|
-
)}`;
|
|
1071
|
-
return import(`${dataUrl}#${Date.now()}`);
|
|
1093
|
+
async function sendResponse(res, response) {
|
|
1094
|
+
res.statusCode = response.status;
|
|
1095
|
+
response.headers.forEach((value, key) => {
|
|
1096
|
+
res.setHeader(key, value);
|
|
1097
|
+
});
|
|
1098
|
+
if (!response.body) {
|
|
1099
|
+
res.end();
|
|
1100
|
+
return;
|
|
1072
1101
|
}
|
|
1073
|
-
|
|
1102
|
+
const buffer = new Uint8Array(await response.arrayBuffer());
|
|
1103
|
+
res.end(buffer);
|
|
1074
1104
|
}
|
|
1075
|
-
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
if (moduleNode) {
|
|
1080
|
-
asDevServer.moduleGraph.invalidateModule(moduleNode);
|
|
1081
|
-
}
|
|
1082
|
-
return asDevServer.ssrLoadModule(file);
|
|
1083
|
-
}
|
|
1084
|
-
return loadModule(file);
|
|
1105
|
+
function hasMatch(app, method, pathname) {
|
|
1106
|
+
const matchMethod = method === "HEAD" ? "GET" : method;
|
|
1107
|
+
const match = app.router.match(matchMethod, pathname);
|
|
1108
|
+
return !!match && match[0].length > 0;
|
|
1085
1109
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
allowTrailingComma: true,
|
|
1092
|
-
disallowComments: false
|
|
1093
|
-
});
|
|
1094
|
-
if (errors.length > 0) {
|
|
1095
|
-
logger.warn(`Invalid JSONC in ${file}`);
|
|
1096
|
-
return void 0;
|
|
1110
|
+
function createMiddleware(getApp, logger) {
|
|
1111
|
+
return async (req, res, next) => {
|
|
1112
|
+
const app = getApp();
|
|
1113
|
+
if (!app) {
|
|
1114
|
+
return next();
|
|
1097
1115
|
}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
async function loadRules(file, server, logger) {
|
|
1105
|
-
const ext = pathe.extname(file).toLowerCase();
|
|
1106
|
-
if (ext === ".json" || ext === ".jsonc") {
|
|
1107
|
-
const json = await readJsonFile(file, logger);
|
|
1108
|
-
if (typeof json === "undefined") {
|
|
1109
|
-
return [];
|
|
1116
|
+
const url = req.url ?? "/";
|
|
1117
|
+
const parsedUrl = new URL(url, "http://mokup.local");
|
|
1118
|
+
const pathname = parsedUrl.pathname;
|
|
1119
|
+
const method = normalizeMethod(req.method) ?? "GET";
|
|
1120
|
+
if (!hasMatch(app, method, pathname)) {
|
|
1121
|
+
return next();
|
|
1110
1122
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1123
|
+
const startedAt = Date.now();
|
|
1124
|
+
try {
|
|
1125
|
+
const response = await app.fetch(await toRequest(req));
|
|
1126
|
+
if (res.writableEnded) {
|
|
1127
|
+
return;
|
|
1114
1128
|
}
|
|
1115
|
-
|
|
1129
|
+
await sendResponse(res, response);
|
|
1130
|
+
logger.info(`${method} ${pathname} ${Date.now() - startedAt}ms`);
|
|
1131
|
+
} catch (error) {
|
|
1132
|
+
if (!res.headersSent) {
|
|
1133
|
+
res.statusCode = 500;
|
|
1134
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
1135
|
+
}
|
|
1136
|
+
res.end("Mock handler error");
|
|
1137
|
+
logger.error("Mock handler failed:", error);
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
const jsonExtensions = /* @__PURE__ */ new Set([".json", ".jsonc"]);
|
|
1143
|
+
function resolveTemplate(template, prefix) {
|
|
1144
|
+
const normalized = template.startsWith("/") ? template : `/${template}`;
|
|
1145
|
+
if (!prefix) {
|
|
1146
|
+
return normalized;
|
|
1116
1147
|
}
|
|
1117
|
-
const
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
return [];
|
|
1148
|
+
const normalizedPrefix = normalizePrefix(prefix);
|
|
1149
|
+
if (!normalizedPrefix) {
|
|
1150
|
+
return normalized;
|
|
1121
1151
|
}
|
|
1122
|
-
if (
|
|
1123
|
-
return
|
|
1152
|
+
if (normalized === normalizedPrefix || normalized.startsWith(`${normalizedPrefix}/`)) {
|
|
1153
|
+
return normalized;
|
|
1124
1154
|
}
|
|
1125
|
-
if (
|
|
1126
|
-
return
|
|
1127
|
-
{
|
|
1128
|
-
handler: value
|
|
1129
|
-
}
|
|
1130
|
-
];
|
|
1155
|
+
if (normalized === "/") {
|
|
1156
|
+
return `${normalizedPrefix}/`;
|
|
1131
1157
|
}
|
|
1132
|
-
return
|
|
1158
|
+
return `${normalizedPrefix}${normalized}`;
|
|
1133
1159
|
}
|
|
1134
|
-
|
|
1135
|
-
const
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1160
|
+
function stripMethodSuffix(base) {
|
|
1161
|
+
const segments = base.split(".");
|
|
1162
|
+
const last = segments.at(-1);
|
|
1163
|
+
if (last && methodSuffixSet.has(last.toLowerCase())) {
|
|
1164
|
+
segments.pop();
|
|
1165
|
+
return {
|
|
1166
|
+
name: segments.join("."),
|
|
1167
|
+
method: last.toUpperCase()
|
|
1168
|
+
};
|
|
1141
1169
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1170
|
+
return {
|
|
1171
|
+
name: base,
|
|
1172
|
+
method: void 0
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
function deriveRouteFromFile(file, rootDir, logger) {
|
|
1176
|
+
const rel = toPosix(pathe.relative(rootDir, file));
|
|
1177
|
+
const ext = pathe.extname(rel);
|
|
1178
|
+
const withoutExt = rel.slice(0, rel.length - ext.length);
|
|
1179
|
+
const dir = pathe.dirname(withoutExt);
|
|
1180
|
+
const base = pathe.basename(withoutExt);
|
|
1181
|
+
const { name, method } = stripMethodSuffix(base);
|
|
1182
|
+
const resolvedMethod = method ?? (jsonExtensions.has(ext) ? "GET" : void 0);
|
|
1183
|
+
if (!resolvedMethod) {
|
|
1184
|
+
logger.warn(`Skip mock without method suffix: ${file}`);
|
|
1146
1185
|
return null;
|
|
1147
1186
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1187
|
+
if (!name) {
|
|
1188
|
+
logger.warn(`Skip mock with empty route name: ${file}`);
|
|
1189
|
+
return null;
|
|
1190
|
+
}
|
|
1191
|
+
const joined = dir === "." ? name : pathe.join(dir, name);
|
|
1192
|
+
const segments = toPosix(joined).split("/");
|
|
1193
|
+
if (segments.at(-1) === "index") {
|
|
1194
|
+
segments.pop();
|
|
1195
|
+
}
|
|
1196
|
+
const template = segments.length === 0 ? "/" : `/${segments.join("/")}`;
|
|
1197
|
+
const parsed = runtime.parseRouteTemplate(template);
|
|
1198
|
+
if (parsed.errors.length > 0) {
|
|
1199
|
+
for (const error of parsed.errors) {
|
|
1200
|
+
logger.warn(`${error} in ${file}`);
|
|
1201
|
+
}
|
|
1157
1202
|
return null;
|
|
1158
1203
|
}
|
|
1204
|
+
for (const warning of parsed.warnings) {
|
|
1205
|
+
logger.warn(`${warning} in ${file}`);
|
|
1206
|
+
}
|
|
1159
1207
|
return {
|
|
1160
|
-
|
|
1161
|
-
|
|
1208
|
+
template: parsed.template,
|
|
1209
|
+
method: resolvedMethod,
|
|
1210
|
+
tokens: parsed.tokens,
|
|
1211
|
+
score: parsed.score
|
|
1162
1212
|
};
|
|
1163
1213
|
}
|
|
1164
|
-
function
|
|
1165
|
-
const
|
|
1166
|
-
if (
|
|
1167
|
-
|
|
1168
|
-
|
|
1214
|
+
function resolveRule(params) {
|
|
1215
|
+
const method = params.derivedMethod;
|
|
1216
|
+
if (!method) {
|
|
1217
|
+
params.logger.warn(`Skip mock without method suffix: ${params.file}`);
|
|
1218
|
+
return null;
|
|
1169
1219
|
}
|
|
1170
|
-
|
|
1220
|
+
const template = resolveTemplate(params.derivedTemplate, params.prefix);
|
|
1221
|
+
const parsed = runtime.parseRouteTemplate(template);
|
|
1222
|
+
if (parsed.errors.length > 0) {
|
|
1223
|
+
for (const error of parsed.errors) {
|
|
1224
|
+
params.logger.warn(`${error} in ${params.file}`);
|
|
1225
|
+
}
|
|
1226
|
+
return null;
|
|
1227
|
+
}
|
|
1228
|
+
for (const warning of parsed.warnings) {
|
|
1229
|
+
params.logger.warn(`${warning} in ${params.file}`);
|
|
1230
|
+
}
|
|
1231
|
+
const route = {
|
|
1232
|
+
file: params.file,
|
|
1233
|
+
template: parsed.template,
|
|
1234
|
+
method,
|
|
1235
|
+
tokens: parsed.tokens,
|
|
1236
|
+
score: parsed.score,
|
|
1237
|
+
handler: params.rule.handler
|
|
1238
|
+
};
|
|
1239
|
+
if (typeof params.rule.status === "number") {
|
|
1240
|
+
route.status = params.rule.status;
|
|
1241
|
+
}
|
|
1242
|
+
if (params.rule.headers) {
|
|
1243
|
+
route.headers = params.rule.headers;
|
|
1244
|
+
}
|
|
1245
|
+
if (typeof params.rule.delay === "number") {
|
|
1246
|
+
route.delay = params.rule.delay;
|
|
1247
|
+
}
|
|
1248
|
+
return route;
|
|
1171
1249
|
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
const globalIgnorePrefix = normalizeIgnorePrefix(params.ignorePrefix);
|
|
1177
|
-
const configCache = /* @__PURE__ */ new Map();
|
|
1178
|
-
const fileCache = /* @__PURE__ */ new Map();
|
|
1179
|
-
const shouldCollectSkip = typeof params.onSkip === "function";
|
|
1180
|
-
const shouldCollectIgnore = typeof params.onIgnore === "function";
|
|
1181
|
-
const shouldCollectConfig = typeof params.onConfig === "function";
|
|
1182
|
-
for (const fileInfo of files) {
|
|
1183
|
-
if (isConfigFile(fileInfo.file)) {
|
|
1184
|
-
if (shouldCollectConfig) {
|
|
1185
|
-
const config2 = await resolveDirectoryConfig({
|
|
1186
|
-
file: fileInfo.file,
|
|
1187
|
-
rootDir: fileInfo.rootDir,
|
|
1188
|
-
server: params.server,
|
|
1189
|
-
logger: params.logger,
|
|
1190
|
-
configCache,
|
|
1191
|
-
fileCache
|
|
1192
|
-
});
|
|
1193
|
-
params.onConfig?.({ file: fileInfo.file, enabled: config2.enabled !== false });
|
|
1194
|
-
}
|
|
1195
|
-
continue;
|
|
1250
|
+
function sortRoutes(routes) {
|
|
1251
|
+
return routes.sort((a, b) => {
|
|
1252
|
+
if (a.method !== b.method) {
|
|
1253
|
+
return a.method.localeCompare(b.method);
|
|
1196
1254
|
}
|
|
1197
|
-
const
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
logger: params.logger,
|
|
1201
|
-
configCache,
|
|
1202
|
-
fileCache
|
|
1203
|
-
};
|
|
1204
|
-
if (params.server) {
|
|
1205
|
-
configParams.server = params.server;
|
|
1255
|
+
const scoreCompare = runtime.compareRouteScore(a.score, b.score);
|
|
1256
|
+
if (scoreCompare !== 0) {
|
|
1257
|
+
return scoreCompare;
|
|
1206
1258
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
params.onSkip?.(buildSkipInfo(fileInfo.file, "disabled-dir", resolved));
|
|
1216
|
-
}
|
|
1259
|
+
return a.template.localeCompare(b.template);
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
async function walkDir(dir, rootDir, files) {
|
|
1264
|
+
const entries = await node_fs.promises.readdir(dir, { withFileTypes: true });
|
|
1265
|
+
for (const entry of entries) {
|
|
1266
|
+
if (entry.name === "node_modules" || entry.name === ".git") {
|
|
1217
1267
|
continue;
|
|
1218
1268
|
}
|
|
1219
|
-
const
|
|
1220
|
-
if (
|
|
1221
|
-
|
|
1222
|
-
const resolved = resolveSkipRoute({
|
|
1223
|
-
file: fileInfo.file,
|
|
1224
|
-
rootDir: fileInfo.rootDir,
|
|
1225
|
-
prefix: params.prefix
|
|
1226
|
-
});
|
|
1227
|
-
params.onSkip?.(buildSkipInfo(fileInfo.file, "ignore-prefix", resolved));
|
|
1228
|
-
}
|
|
1229
|
-
continue;
|
|
1230
|
-
}
|
|
1231
|
-
if (!isSupportedFile(fileInfo.file)) {
|
|
1232
|
-
if (shouldCollectIgnore) {
|
|
1233
|
-
params.onIgnore?.({ file: fileInfo.file, reason: "unsupported" });
|
|
1234
|
-
}
|
|
1269
|
+
const fullPath = pathe.join(dir, entry.name);
|
|
1270
|
+
if (entry.isDirectory()) {
|
|
1271
|
+
await walkDir(fullPath, rootDir, files);
|
|
1235
1272
|
continue;
|
|
1236
1273
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
if (!matchesFilter(fileInfo.file, effectiveInclude, effectiveExclude)) {
|
|
1240
|
-
if (shouldCollectSkip) {
|
|
1241
|
-
const resolved = resolveSkipRoute({
|
|
1242
|
-
file: fileInfo.file,
|
|
1243
|
-
rootDir: fileInfo.rootDir,
|
|
1244
|
-
prefix: params.prefix
|
|
1245
|
-
});
|
|
1246
|
-
const reason = effectiveExclude && matchesFilter(fileInfo.file, void 0, effectiveExclude) ? "exclude" : "include";
|
|
1247
|
-
params.onSkip?.(buildSkipInfo(fileInfo.file, reason, resolved));
|
|
1248
|
-
}
|
|
1249
|
-
continue;
|
|
1274
|
+
if (entry.isFile()) {
|
|
1275
|
+
files.push({ file: fullPath, rootDir });
|
|
1250
1276
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
async function exists(path) {
|
|
1280
|
+
try {
|
|
1281
|
+
await node_fs.promises.stat(path);
|
|
1282
|
+
return true;
|
|
1283
|
+
} catch {
|
|
1284
|
+
return false;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
async function collectFiles(dirs) {
|
|
1288
|
+
const files = [];
|
|
1289
|
+
for (const dir of dirs) {
|
|
1290
|
+
if (!await exists(dir)) {
|
|
1256
1291
|
continue;
|
|
1257
1292
|
}
|
|
1258
|
-
|
|
1259
|
-
for (const [index, rule] of rules.entries()) {
|
|
1260
|
-
if (!rule || typeof rule !== "object") {
|
|
1261
|
-
continue;
|
|
1262
|
-
}
|
|
1263
|
-
if (rule.enabled === false) {
|
|
1264
|
-
if (shouldCollectSkip) {
|
|
1265
|
-
const resolved2 = resolveSkipRoute({
|
|
1266
|
-
file: fileInfo.file,
|
|
1267
|
-
rootDir: fileInfo.rootDir,
|
|
1268
|
-
prefix: params.prefix,
|
|
1269
|
-
derived
|
|
1270
|
-
});
|
|
1271
|
-
params.onSkip?.(buildSkipInfo(fileInfo.file, "disabled", resolved2));
|
|
1272
|
-
}
|
|
1273
|
-
continue;
|
|
1274
|
-
}
|
|
1275
|
-
const ruleValue = rule;
|
|
1276
|
-
const unsupportedKeys = ["response", "url", "method"].filter(
|
|
1277
|
-
(key2) => key2 in ruleValue
|
|
1278
|
-
);
|
|
1279
|
-
if (unsupportedKeys.length > 0) {
|
|
1280
|
-
params.logger.warn(
|
|
1281
|
-
`Skip mock with unsupported fields (${unsupportedKeys.join(", ")}): ${fileInfo.file}`
|
|
1282
|
-
);
|
|
1283
|
-
continue;
|
|
1284
|
-
}
|
|
1285
|
-
if (typeof rule.handler === "undefined") {
|
|
1286
|
-
params.logger.warn(`Skip mock without handler: ${fileInfo.file}`);
|
|
1287
|
-
continue;
|
|
1288
|
-
}
|
|
1289
|
-
const resolved = resolveRule({
|
|
1290
|
-
rule,
|
|
1291
|
-
derivedTemplate: derived.template,
|
|
1292
|
-
derivedMethod: derived.method,
|
|
1293
|
-
prefix: params.prefix,
|
|
1294
|
-
file: fileInfo.file,
|
|
1295
|
-
logger: params.logger
|
|
1296
|
-
});
|
|
1297
|
-
if (!resolved) {
|
|
1298
|
-
continue;
|
|
1299
|
-
}
|
|
1300
|
-
resolved.ruleIndex = index;
|
|
1301
|
-
if (config.headers) {
|
|
1302
|
-
resolved.headers = { ...config.headers, ...resolved.headers ?? {} };
|
|
1303
|
-
}
|
|
1304
|
-
if (typeof resolved.status === "undefined" && typeof config.status === "number") {
|
|
1305
|
-
resolved.status = config.status;
|
|
1306
|
-
}
|
|
1307
|
-
if (typeof resolved.delay === "undefined" && typeof config.delay === "number") {
|
|
1308
|
-
resolved.delay = config.delay;
|
|
1309
|
-
}
|
|
1310
|
-
if (config.middlewares.length > 0) {
|
|
1311
|
-
resolved.middlewares = config.middlewares;
|
|
1312
|
-
}
|
|
1313
|
-
const key = `${resolved.method} ${resolved.template}`;
|
|
1314
|
-
if (seen.has(key)) {
|
|
1315
|
-
params.logger.warn(`Duplicate mock route ${key} from ${fileInfo.file}`);
|
|
1316
|
-
}
|
|
1317
|
-
seen.add(key);
|
|
1318
|
-
routes.push(resolved);
|
|
1319
|
-
}
|
|
1293
|
+
await walkDir(dir, dir, files);
|
|
1320
1294
|
}
|
|
1321
|
-
return
|
|
1295
|
+
return files;
|
|
1322
1296
|
}
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
function normalizeSwPath(path) {
|
|
1327
|
-
if (!path) {
|
|
1328
|
-
return defaultSwPath;
|
|
1297
|
+
function isSupportedFile(file) {
|
|
1298
|
+
if (file.endsWith(".d.ts")) {
|
|
1299
|
+
return false;
|
|
1329
1300
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
function normalizeSwScope(scope) {
|
|
1333
|
-
if (!scope) {
|
|
1334
|
-
return defaultSwScope;
|
|
1301
|
+
if (isConfigFile(file)) {
|
|
1302
|
+
return false;
|
|
1335
1303
|
}
|
|
1336
|
-
|
|
1304
|
+
const ext = pathe.extname(file).toLowerCase();
|
|
1305
|
+
return supportedExtensions.has(ext);
|
|
1337
1306
|
}
|
|
1338
|
-
function
|
|
1339
|
-
if (
|
|
1340
|
-
return
|
|
1307
|
+
function isConfigFile(file) {
|
|
1308
|
+
if (file.endsWith(".d.ts")) {
|
|
1309
|
+
return false;
|
|
1341
1310
|
}
|
|
1342
|
-
const
|
|
1343
|
-
if (
|
|
1344
|
-
return
|
|
1311
|
+
const base = pathe.basename(file);
|
|
1312
|
+
if (!base.startsWith("index.config.")) {
|
|
1313
|
+
return false;
|
|
1345
1314
|
}
|
|
1346
|
-
|
|
1315
|
+
const ext = pathe.extname(file).toLowerCase();
|
|
1316
|
+
return configExtensions.includes(ext);
|
|
1347
1317
|
}
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
path = next;
|
|
1364
|
-
hasPath = true;
|
|
1365
|
-
} else if (path !== next) {
|
|
1366
|
-
logger.warn(`SW path "${next}" ignored; using "${path}".`);
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
if (config?.scope) {
|
|
1370
|
-
const next = normalizeSwScope(config.scope);
|
|
1371
|
-
if (!hasScope) {
|
|
1372
|
-
scope = next;
|
|
1373
|
-
hasScope = true;
|
|
1374
|
-
} else if (scope !== next) {
|
|
1375
|
-
logger.warn(`SW scope "${next}" ignored; using "${scope}".`);
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
if (typeof config?.register === "boolean") {
|
|
1379
|
-
if (!hasRegister) {
|
|
1380
|
-
register = config.register;
|
|
1381
|
-
hasRegister = true;
|
|
1382
|
-
} else if (register !== config.register) {
|
|
1383
|
-
logger.warn(
|
|
1384
|
-
`SW register="${String(config.register)}" ignored; using "${String(register)}".`
|
|
1385
|
-
);
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
if (typeof config?.unregister === "boolean") {
|
|
1389
|
-
if (!hasUnregister) {
|
|
1390
|
-
unregister = config.unregister;
|
|
1391
|
-
hasUnregister = true;
|
|
1392
|
-
} else if (unregister !== config.unregister) {
|
|
1393
|
-
logger.warn(
|
|
1394
|
-
`SW unregister="${String(config.unregister)}" ignored; using "${String(unregister)}".`
|
|
1395
|
-
);
|
|
1318
|
+
|
|
1319
|
+
const sourceRoot = pathe.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/mokup.To7knvSq.cjs', document.baseURI).href))));
|
|
1320
|
+
const mokupSourceEntry = pathe.resolve(sourceRoot, "../index.ts");
|
|
1321
|
+
const mokupViteSourceEntry = pathe.resolve(sourceRoot, "../vite.ts");
|
|
1322
|
+
const hasMokupSourceEntry = node_fs.existsSync(mokupSourceEntry);
|
|
1323
|
+
const hasMokupViteSourceEntry = node_fs.existsSync(mokupViteSourceEntry);
|
|
1324
|
+
function createWorkspaceResolvePlugin() {
|
|
1325
|
+
if (!hasMokupSourceEntry && !hasMokupViteSourceEntry) {
|
|
1326
|
+
return null;
|
|
1327
|
+
}
|
|
1328
|
+
return {
|
|
1329
|
+
name: "mokup:resolve-workspace",
|
|
1330
|
+
setup(build) {
|
|
1331
|
+
if (hasMokupSourceEntry) {
|
|
1332
|
+
build.onResolve({ filter: /^mokup$/ }, () => ({ path: mokupSourceEntry }));
|
|
1396
1333
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
const values = Array.isArray(config.basePath) ? config.basePath : [config.basePath];
|
|
1400
|
-
for (const value of values) {
|
|
1401
|
-
basePaths.push(normalizeBasePath(value));
|
|
1334
|
+
if (hasMokupViteSourceEntry) {
|
|
1335
|
+
build.onResolve({ filter: /^mokup\/vite$/ }, () => ({ path: mokupViteSourceEntry }));
|
|
1402
1336
|
}
|
|
1403
|
-
continue;
|
|
1404
|
-
}
|
|
1405
|
-
const normalizedPrefix = normalizePrefix(entry.prefix ?? "");
|
|
1406
|
-
if (normalizedPrefix) {
|
|
1407
|
-
basePaths.push(normalizedPrefix);
|
|
1408
1337
|
}
|
|
1409
|
-
}
|
|
1410
|
-
return {
|
|
1411
|
-
path,
|
|
1412
|
-
scope,
|
|
1413
|
-
register,
|
|
1414
|
-
unregister,
|
|
1415
|
-
basePaths: Array.from(new Set(basePaths))
|
|
1416
1338
|
};
|
|
1417
1339
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
function resolveSwUnregisterConfig(options, logger) {
|
|
1426
|
-
return resolveSwConfigFromEntries(options, logger);
|
|
1427
|
-
}
|
|
1428
|
-
function toViteImportPath(file, root) {
|
|
1429
|
-
const absolute = pathe.isAbsolute(file) ? file : pathe.resolve(root, file);
|
|
1430
|
-
const rel = pathe.relative(root, absolute);
|
|
1431
|
-
if (!rel.startsWith("..") && !pathe.isAbsolute(rel)) {
|
|
1432
|
-
return `/${toPosix(rel)}`;
|
|
1340
|
+
const workspaceResolvePlugin = createWorkspaceResolvePlugin();
|
|
1341
|
+
async function loadModule(file) {
|
|
1342
|
+
const ext = pathe.extname(file).toLowerCase();
|
|
1343
|
+
if (ext === ".cjs") {
|
|
1344
|
+
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/mokup.To7knvSq.cjs', document.baseURI).href)));
|
|
1345
|
+
delete require$1.cache[file];
|
|
1346
|
+
return require$1(file);
|
|
1433
1347
|
}
|
|
1434
|
-
|
|
1435
|
-
}
|
|
1436
|
-
function shouldModuleize(handler) {
|
|
1437
|
-
if (typeof handler === "function") {
|
|
1438
|
-
return true;
|
|
1348
|
+
if (ext === ".js" || ext === ".mjs") {
|
|
1349
|
+
return import(`${node_url.pathToFileURL(file).href}?t=${Date.now()}`);
|
|
1439
1350
|
}
|
|
1440
|
-
if (
|
|
1441
|
-
|
|
1351
|
+
if (ext === ".ts") {
|
|
1352
|
+
const result = await esbuild.build({
|
|
1353
|
+
entryPoints: [file],
|
|
1354
|
+
bundle: true,
|
|
1355
|
+
format: "esm",
|
|
1356
|
+
platform: "node",
|
|
1357
|
+
sourcemap: "inline",
|
|
1358
|
+
target: "es2020",
|
|
1359
|
+
write: false,
|
|
1360
|
+
...workspaceResolvePlugin ? { plugins: [workspaceResolvePlugin] } : {}
|
|
1361
|
+
});
|
|
1362
|
+
const output = result.outputFiles[0];
|
|
1363
|
+
const code = output?.text ?? "";
|
|
1364
|
+
const dataUrl = `data:text/javascript;base64,${node_buffer.Buffer.from(code).toString(
|
|
1365
|
+
"base64"
|
|
1366
|
+
)}`;
|
|
1367
|
+
return import(`${dataUrl}#${Date.now()}`);
|
|
1442
1368
|
}
|
|
1443
|
-
return
|
|
1369
|
+
return null;
|
|
1444
1370
|
}
|
|
1445
|
-
function
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
return handler.toString("base64");
|
|
1371
|
+
async function loadModuleWithVite(server, file) {
|
|
1372
|
+
const asDevServer = server;
|
|
1373
|
+
if ("ssrLoadModule" in asDevServer) {
|
|
1374
|
+
const moduleNode = asDevServer.moduleGraph.getModuleById(file);
|
|
1375
|
+
if (moduleNode) {
|
|
1376
|
+
asDevServer.moduleGraph.invalidateModule(moduleNode);
|
|
1377
|
+
}
|
|
1378
|
+
return asDevServer.ssrLoadModule(file);
|
|
1454
1379
|
}
|
|
1455
|
-
return
|
|
1380
|
+
return loadModule(file);
|
|
1456
1381
|
}
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1382
|
+
|
|
1383
|
+
const middlewareSymbol = Symbol.for("mokup.config.middlewares");
|
|
1384
|
+
function getConfigFileCandidates(dir) {
|
|
1385
|
+
return configExtensions.map((extension) => pathe.join(dir, `index.config${extension}`));
|
|
1386
|
+
}
|
|
1387
|
+
async function findConfigFile(dir, cache) {
|
|
1388
|
+
const cached = cache.get(dir);
|
|
1389
|
+
if (cached !== void 0) {
|
|
1390
|
+
return cached;
|
|
1391
|
+
}
|
|
1392
|
+
for (const candidate of getConfigFileCandidates(dir)) {
|
|
1393
|
+
try {
|
|
1394
|
+
await node_fs.promises.stat(candidate);
|
|
1395
|
+
cache.set(dir, candidate);
|
|
1396
|
+
return candidate;
|
|
1397
|
+
} catch {
|
|
1398
|
+
continue;
|
|
1465
1399
|
}
|
|
1466
|
-
return response;
|
|
1467
1400
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1401
|
+
cache.set(dir, null);
|
|
1402
|
+
return null;
|
|
1403
|
+
}
|
|
1404
|
+
async function loadConfig(file, server) {
|
|
1405
|
+
const mod = server ? await loadModuleWithVite(server, file) : await loadModule(file);
|
|
1406
|
+
if (!mod) {
|
|
1407
|
+
return null;
|
|
1474
1408
|
}
|
|
1475
|
-
const
|
|
1476
|
-
if (
|
|
1477
|
-
return
|
|
1478
|
-
type: "binary",
|
|
1479
|
-
body: binary,
|
|
1480
|
-
encoding: "base64"
|
|
1481
|
-
};
|
|
1409
|
+
const value = mod?.default ?? mod;
|
|
1410
|
+
if (!value || typeof value !== "object") {
|
|
1411
|
+
return null;
|
|
1482
1412
|
}
|
|
1483
|
-
return
|
|
1484
|
-
type: "json",
|
|
1485
|
-
body: handler
|
|
1486
|
-
};
|
|
1413
|
+
return value;
|
|
1487
1414
|
}
|
|
1488
|
-
function
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
const
|
|
1493
|
-
const
|
|
1494
|
-
const
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
ruleModules.set(moduleId, moduleId);
|
|
1415
|
+
function normalizeMiddlewares(value, source, logger, position) {
|
|
1416
|
+
if (!value) {
|
|
1417
|
+
return [];
|
|
1418
|
+
}
|
|
1419
|
+
const list = Array.isArray(value) ? value : [value];
|
|
1420
|
+
const middlewares = [];
|
|
1421
|
+
for (const [index, entry] of list.entries()) {
|
|
1422
|
+
if (typeof entry !== "function") {
|
|
1423
|
+
logger.warn(`Invalid middleware in ${source}`);
|
|
1424
|
+
continue;
|
|
1499
1425
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
ruleIndex: entry.index
|
|
1506
|
-
};
|
|
1426
|
+
middlewares.push({
|
|
1427
|
+
handle: entry,
|
|
1428
|
+
source,
|
|
1429
|
+
index,
|
|
1430
|
+
position
|
|
1507
1431
|
});
|
|
1508
|
-
const response = buildManifestResponse(route, moduleId);
|
|
1509
|
-
const manifestRoute = {
|
|
1510
|
-
method: route.method,
|
|
1511
|
-
url: route.template,
|
|
1512
|
-
...route.tokens ? { tokens: route.tokens } : {},
|
|
1513
|
-
...route.score ? { score: route.score } : {},
|
|
1514
|
-
...route.status ? { status: route.status } : {},
|
|
1515
|
-
...route.headers ? { headers: route.headers } : {},
|
|
1516
|
-
...route.delay ? { delay: route.delay } : {},
|
|
1517
|
-
...middleware && middleware.length > 0 ? { middleware } : {},
|
|
1518
|
-
response
|
|
1519
|
-
};
|
|
1520
|
-
return manifestRoute;
|
|
1521
|
-
});
|
|
1522
|
-
const manifest = {
|
|
1523
|
-
version: 1,
|
|
1524
|
-
routes: manifestRoutes
|
|
1525
|
-
};
|
|
1526
|
-
const imports = [
|
|
1527
|
-
`import { createRuntimeApp, handle } from ${JSON.stringify(runtimeImportPath)}`
|
|
1528
|
-
];
|
|
1529
|
-
const moduleEntries = [];
|
|
1530
|
-
let moduleIndex = 0;
|
|
1531
|
-
for (const id of ruleModules.keys()) {
|
|
1532
|
-
const name = `module${moduleIndex++}`;
|
|
1533
|
-
imports.push(`import * as ${name} from '${id}'`);
|
|
1534
|
-
moduleEntries.push({ id, name, kind: "rule" });
|
|
1535
1432
|
}
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1433
|
+
return middlewares;
|
|
1434
|
+
}
|
|
1435
|
+
function readMiddlewareMeta(config) {
|
|
1436
|
+
const value = config[middlewareSymbol];
|
|
1437
|
+
if (!value || typeof value !== "object") {
|
|
1438
|
+
return null;
|
|
1540
1439
|
}
|
|
1541
|
-
const
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
" return value",
|
|
1559
|
-
" }",
|
|
1560
|
-
" if ('handler' in value) {",
|
|
1561
|
-
" const handlerRule = value",
|
|
1562
|
-
" return {",
|
|
1563
|
-
" response: handlerRule.handler,",
|
|
1564
|
-
" ...(typeof handlerRule.status === 'number' ? { status: handlerRule.status } : {}),",
|
|
1565
|
-
" ...(handlerRule.headers ? { headers: handlerRule.headers } : {}),",
|
|
1566
|
-
" ...(typeof handlerRule.delay === 'number' ? { delay: handlerRule.delay } : {}),",
|
|
1567
|
-
" }",
|
|
1568
|
-
" }",
|
|
1569
|
-
" return { response: value }",
|
|
1570
|
-
" }",
|
|
1571
|
-
" return { response: value }",
|
|
1572
|
-
"}",
|
|
1573
|
-
"",
|
|
1574
|
-
"const toRuntimeRules = (value) => {",
|
|
1575
|
-
" if (typeof value === 'undefined') {",
|
|
1576
|
-
" return []",
|
|
1577
|
-
" }",
|
|
1578
|
-
" if (Array.isArray(value)) {",
|
|
1579
|
-
" return value.map(toRuntimeRule).filter(Boolean)",
|
|
1580
|
-
" }",
|
|
1581
|
-
" const rule = toRuntimeRule(value)",
|
|
1582
|
-
" return rule ? [rule] : []",
|
|
1583
|
-
"}",
|
|
1584
|
-
""
|
|
1585
|
-
);
|
|
1586
|
-
lines.push(
|
|
1587
|
-
`const manifest = ${JSON.stringify(manifest, null, 2)}`,
|
|
1588
|
-
""
|
|
1589
|
-
);
|
|
1590
|
-
if (moduleEntries.length > 0) {
|
|
1591
|
-
lines.push("const moduleMap = {");
|
|
1592
|
-
for (const entry of moduleEntries) {
|
|
1593
|
-
if (entry.kind === "rule") {
|
|
1594
|
-
lines.push(
|
|
1595
|
-
` ${JSON.stringify(entry.id)}: { default: toRuntimeRules(resolveModuleExport(${entry.name})) },`
|
|
1596
|
-
);
|
|
1597
|
-
continue;
|
|
1598
|
-
}
|
|
1599
|
-
lines.push(
|
|
1600
|
-
` ${JSON.stringify(entry.id)}: ${entry.name},`
|
|
1601
|
-
);
|
|
1440
|
+
const meta = value;
|
|
1441
|
+
return {
|
|
1442
|
+
pre: Array.isArray(meta.pre) ? meta.pre : [],
|
|
1443
|
+
normal: Array.isArray(meta.normal) ? meta.normal : [],
|
|
1444
|
+
post: Array.isArray(meta.post) ? meta.post : []
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
async function resolveDirectoryConfig(params) {
|
|
1448
|
+
const { file, rootDir, server, logger, configCache, fileCache } = params;
|
|
1449
|
+
const resolvedRoot = pathe.normalize(rootDir);
|
|
1450
|
+
const resolvedFileDir = pathe.normalize(pathe.dirname(file));
|
|
1451
|
+
const chain = [];
|
|
1452
|
+
let current = resolvedFileDir;
|
|
1453
|
+
while (true) {
|
|
1454
|
+
chain.push(current);
|
|
1455
|
+
if (current === resolvedRoot) {
|
|
1456
|
+
break;
|
|
1602
1457
|
}
|
|
1603
|
-
|
|
1458
|
+
const parent = pathe.dirname(current);
|
|
1459
|
+
if (parent === current) {
|
|
1460
|
+
break;
|
|
1461
|
+
}
|
|
1462
|
+
current = parent;
|
|
1604
1463
|
}
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
"
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
"
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
"
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1464
|
+
chain.reverse();
|
|
1465
|
+
const merged = {};
|
|
1466
|
+
const preMiddlewares = [];
|
|
1467
|
+
const normalMiddlewares = [];
|
|
1468
|
+
const postMiddlewares = [];
|
|
1469
|
+
const configChain = [];
|
|
1470
|
+
const configSources = {};
|
|
1471
|
+
for (const dir of chain) {
|
|
1472
|
+
const configPath = await findConfigFile(dir, fileCache);
|
|
1473
|
+
if (!configPath) {
|
|
1474
|
+
continue;
|
|
1475
|
+
}
|
|
1476
|
+
let config = configCache.get(configPath);
|
|
1477
|
+
if (config === void 0) {
|
|
1478
|
+
config = await loadConfig(configPath, server);
|
|
1479
|
+
configCache.set(configPath, config);
|
|
1480
|
+
}
|
|
1481
|
+
if (!config) {
|
|
1482
|
+
logger.warn(`Invalid config in ${configPath}`);
|
|
1483
|
+
continue;
|
|
1484
|
+
}
|
|
1485
|
+
configChain.push(configPath);
|
|
1486
|
+
if (config.headers) {
|
|
1487
|
+
merged.headers = { ...merged.headers ?? {}, ...config.headers };
|
|
1488
|
+
configSources.headers = configPath;
|
|
1489
|
+
}
|
|
1490
|
+
if (typeof config.status === "number") {
|
|
1491
|
+
merged.status = config.status;
|
|
1492
|
+
configSources.status = configPath;
|
|
1493
|
+
}
|
|
1494
|
+
if (typeof config.delay === "number") {
|
|
1495
|
+
merged.delay = config.delay;
|
|
1496
|
+
configSources.delay = configPath;
|
|
1497
|
+
}
|
|
1498
|
+
if (typeof config.enabled === "boolean") {
|
|
1499
|
+
merged.enabled = config.enabled;
|
|
1500
|
+
configSources.enabled = configPath;
|
|
1501
|
+
}
|
|
1502
|
+
if (typeof config.ignorePrefix !== "undefined") {
|
|
1503
|
+
merged.ignorePrefix = config.ignorePrefix;
|
|
1504
|
+
configSources.ignorePrefix = configPath;
|
|
1505
|
+
}
|
|
1506
|
+
if (typeof config.include !== "undefined") {
|
|
1507
|
+
merged.include = config.include;
|
|
1508
|
+
configSources.include = configPath;
|
|
1509
|
+
}
|
|
1510
|
+
if (typeof config.exclude !== "undefined") {
|
|
1511
|
+
merged.exclude = config.exclude;
|
|
1512
|
+
configSources.exclude = configPath;
|
|
1513
|
+
}
|
|
1514
|
+
const meta = readMiddlewareMeta(config);
|
|
1515
|
+
const normalizedPre = normalizeMiddlewares(
|
|
1516
|
+
meta?.pre,
|
|
1517
|
+
configPath,
|
|
1518
|
+
logger,
|
|
1519
|
+
"pre"
|
|
1520
|
+
);
|
|
1521
|
+
const normalizedNormal = normalizeMiddlewares(
|
|
1522
|
+
meta?.normal,
|
|
1523
|
+
configPath,
|
|
1524
|
+
logger,
|
|
1525
|
+
"normal"
|
|
1526
|
+
);
|
|
1527
|
+
const normalizedLegacy = normalizeMiddlewares(
|
|
1528
|
+
config.middleware,
|
|
1529
|
+
configPath,
|
|
1530
|
+
logger,
|
|
1531
|
+
"normal"
|
|
1532
|
+
);
|
|
1533
|
+
const normalizedPost = normalizeMiddlewares(
|
|
1534
|
+
meta?.post,
|
|
1535
|
+
configPath,
|
|
1536
|
+
logger,
|
|
1537
|
+
"post"
|
|
1538
|
+
);
|
|
1539
|
+
if (normalizedPre.length > 0) {
|
|
1540
|
+
preMiddlewares.push(...normalizedPre);
|
|
1541
|
+
}
|
|
1542
|
+
if (normalizedNormal.length > 0) {
|
|
1543
|
+
normalMiddlewares.push(...normalizedNormal);
|
|
1544
|
+
}
|
|
1545
|
+
if (normalizedLegacy.length > 0) {
|
|
1546
|
+
normalMiddlewares.push(...normalizedLegacy);
|
|
1547
|
+
}
|
|
1548
|
+
if (normalizedPost.length > 0) {
|
|
1549
|
+
postMiddlewares.push(...normalizedPost);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
return {
|
|
1553
|
+
...merged,
|
|
1554
|
+
middlewares: [...preMiddlewares, ...normalMiddlewares, ...postMiddlewares],
|
|
1555
|
+
configChain,
|
|
1556
|
+
configSources
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
async function readJsonFile(file, logger) {
|
|
1561
|
+
try {
|
|
1562
|
+
const content = await node_fs.promises.readFile(file, "utf8");
|
|
1563
|
+
const errors = [];
|
|
1564
|
+
const data = jsoncParser.parse(content, errors, {
|
|
1565
|
+
allowTrailingComma: true,
|
|
1566
|
+
disallowComments: false
|
|
1567
|
+
});
|
|
1568
|
+
if (errors.length > 0) {
|
|
1569
|
+
logger.warn(`Invalid JSONC in ${file}`);
|
|
1570
|
+
return void 0;
|
|
1571
|
+
}
|
|
1572
|
+
return data;
|
|
1573
|
+
} catch (error) {
|
|
1574
|
+
logger.warn(`Failed to read ${file}: ${String(error)}`);
|
|
1575
|
+
return void 0;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
async function loadRules(file, server, logger) {
|
|
1579
|
+
const ext = pathe.extname(file).toLowerCase();
|
|
1580
|
+
if (ext === ".json" || ext === ".jsonc") {
|
|
1581
|
+
const json = await readJsonFile(file, logger);
|
|
1582
|
+
if (typeof json === "undefined") {
|
|
1583
|
+
return [];
|
|
1584
|
+
}
|
|
1585
|
+
return [
|
|
1586
|
+
{
|
|
1587
|
+
handler: json
|
|
1588
|
+
}
|
|
1589
|
+
];
|
|
1590
|
+
}
|
|
1591
|
+
const mod = server ? await loadModuleWithVite(server, file) : await loadModule(file);
|
|
1592
|
+
const value = mod?.default ?? mod;
|
|
1593
|
+
if (!value) {
|
|
1594
|
+
return [];
|
|
1595
|
+
}
|
|
1596
|
+
if (Array.isArray(value)) {
|
|
1597
|
+
return value;
|
|
1598
|
+
}
|
|
1599
|
+
if (typeof value === "function") {
|
|
1600
|
+
return [
|
|
1601
|
+
{
|
|
1602
|
+
handler: value
|
|
1603
|
+
}
|
|
1604
|
+
];
|
|
1605
|
+
}
|
|
1606
|
+
return [value];
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
const silentLogger = {
|
|
1610
|
+
info: () => {
|
|
1611
|
+
},
|
|
1612
|
+
warn: () => {
|
|
1613
|
+
},
|
|
1614
|
+
error: () => {
|
|
1615
|
+
},
|
|
1616
|
+
log: () => {
|
|
1617
|
+
}
|
|
1618
|
+
};
|
|
1619
|
+
function resolveSkipRoute(params) {
|
|
1620
|
+
const derived = params.derived ?? deriveRouteFromFile(params.file, params.rootDir, silentLogger);
|
|
1621
|
+
if (!derived?.method) {
|
|
1622
|
+
return null;
|
|
1623
|
+
}
|
|
1624
|
+
const resolved = resolveRule({
|
|
1625
|
+
rule: { handler: null },
|
|
1626
|
+
derivedTemplate: derived.template,
|
|
1627
|
+
derivedMethod: derived.method,
|
|
1628
|
+
prefix: params.prefix,
|
|
1629
|
+
file: params.file,
|
|
1630
|
+
logger: silentLogger
|
|
1631
|
+
});
|
|
1632
|
+
if (!resolved) {
|
|
1633
|
+
return null;
|
|
1634
|
+
}
|
|
1635
|
+
return {
|
|
1636
|
+
method: resolved.method,
|
|
1637
|
+
url: resolved.template
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
function buildSkipInfo(file, reason, resolved, configChain, decisionChain, effectiveConfig) {
|
|
1641
|
+
const info = { file, reason };
|
|
1642
|
+
if (resolved) {
|
|
1643
|
+
info.method = resolved.method;
|
|
1644
|
+
info.url = resolved.url;
|
|
1645
|
+
}
|
|
1646
|
+
if (configChain && configChain.length > 0) {
|
|
1647
|
+
info.configChain = configChain;
|
|
1648
|
+
}
|
|
1649
|
+
if (decisionChain && decisionChain.length > 0) {
|
|
1650
|
+
info.decisionChain = decisionChain;
|
|
1651
|
+
}
|
|
1652
|
+
if (effectiveConfig && Object.keys(effectiveConfig).length > 0) {
|
|
1653
|
+
info.effectiveConfig = effectiveConfig;
|
|
1654
|
+
}
|
|
1655
|
+
return info;
|
|
1656
|
+
}
|
|
1657
|
+
function toFilterStrings(value) {
|
|
1658
|
+
if (!value) {
|
|
1659
|
+
return [];
|
|
1660
|
+
}
|
|
1661
|
+
const list = Array.isArray(value) ? value : [value];
|
|
1662
|
+
return list.filter((entry) => entry instanceof RegExp).map((entry) => entry.toString());
|
|
1663
|
+
}
|
|
1664
|
+
function toStringList(value) {
|
|
1665
|
+
return value.length === 1 ? value[0] : [...value];
|
|
1666
|
+
}
|
|
1667
|
+
function formatList(value) {
|
|
1668
|
+
return value.join(", ");
|
|
1669
|
+
}
|
|
1670
|
+
function testPatterns(patterns, value) {
|
|
1671
|
+
const list = Array.isArray(patterns) ? patterns : [patterns];
|
|
1672
|
+
return list.some((pattern) => pattern.test(value));
|
|
1673
|
+
}
|
|
1674
|
+
function buildEffectiveConfig(params) {
|
|
1675
|
+
const { config, effectiveInclude, effectiveExclude, effectiveIgnorePrefix } = params;
|
|
1676
|
+
const includeList = toFilterStrings(effectiveInclude);
|
|
1677
|
+
const excludeList = toFilterStrings(effectiveExclude);
|
|
1678
|
+
const effectiveConfig = {};
|
|
1679
|
+
if (config.headers && Object.keys(config.headers).length > 0) {
|
|
1680
|
+
effectiveConfig.headers = config.headers;
|
|
1681
|
+
}
|
|
1682
|
+
if (typeof config.status === "number") {
|
|
1683
|
+
effectiveConfig.status = config.status;
|
|
1684
|
+
}
|
|
1685
|
+
if (typeof config.delay === "number") {
|
|
1686
|
+
effectiveConfig.delay = config.delay;
|
|
1687
|
+
}
|
|
1688
|
+
if (typeof config.enabled !== "undefined") {
|
|
1689
|
+
effectiveConfig.enabled = config.enabled;
|
|
1690
|
+
}
|
|
1691
|
+
if (effectiveIgnorePrefix.length > 0) {
|
|
1692
|
+
effectiveConfig.ignorePrefix = toStringList(effectiveIgnorePrefix);
|
|
1693
|
+
}
|
|
1694
|
+
if (includeList.length > 0) {
|
|
1695
|
+
effectiveConfig.include = toStringList(includeList);
|
|
1696
|
+
}
|
|
1697
|
+
if (excludeList.length > 0) {
|
|
1698
|
+
effectiveConfig.exclude = toStringList(excludeList);
|
|
1699
|
+
}
|
|
1700
|
+
return effectiveConfig;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
function pushDecisionStep(chain, entry) {
|
|
1704
|
+
const step = {
|
|
1705
|
+
step: entry.step,
|
|
1706
|
+
result: entry.result
|
|
1707
|
+
};
|
|
1708
|
+
if (typeof entry.source !== "undefined") {
|
|
1709
|
+
step.source = entry.source;
|
|
1710
|
+
}
|
|
1711
|
+
if (typeof entry.detail !== "undefined") {
|
|
1712
|
+
step.detail = entry.detail;
|
|
1713
|
+
}
|
|
1714
|
+
chain.push(step);
|
|
1715
|
+
}
|
|
1716
|
+
function runRoutePrechecks(params) {
|
|
1717
|
+
const {
|
|
1718
|
+
fileInfo,
|
|
1719
|
+
prefix,
|
|
1720
|
+
config,
|
|
1721
|
+
configChain,
|
|
1722
|
+
globalIgnorePrefix,
|
|
1723
|
+
include,
|
|
1724
|
+
exclude,
|
|
1725
|
+
shouldCollectSkip,
|
|
1726
|
+
shouldCollectIgnore,
|
|
1727
|
+
onSkip,
|
|
1728
|
+
onIgnore
|
|
1729
|
+
} = params;
|
|
1730
|
+
const configSources = config.configSources ?? {};
|
|
1731
|
+
const decisionChain = [];
|
|
1732
|
+
const isConfigEnabled = config.enabled !== false;
|
|
1733
|
+
pushDecisionStep(decisionChain, {
|
|
1734
|
+
step: "config.enabled",
|
|
1735
|
+
result: isConfigEnabled ? "pass" : "fail",
|
|
1736
|
+
source: configSources.enabled,
|
|
1737
|
+
detail: config.enabled === false ? "enabled=false" : typeof config.enabled === "boolean" ? "enabled=true" : "enabled=true (default)"
|
|
1738
|
+
});
|
|
1739
|
+
const effectiveIgnorePrefix = typeof config.ignorePrefix !== "undefined" ? normalizeIgnorePrefix(config.ignorePrefix, []) : globalIgnorePrefix;
|
|
1740
|
+
const effectiveInclude = typeof config.include !== "undefined" ? config.include : include;
|
|
1741
|
+
const effectiveExclude = typeof config.exclude !== "undefined" ? config.exclude : exclude;
|
|
1742
|
+
const effectiveConfigParams = {
|
|
1743
|
+
config,
|
|
1744
|
+
effectiveIgnorePrefix
|
|
1745
|
+
};
|
|
1746
|
+
if (typeof effectiveInclude !== "undefined") {
|
|
1747
|
+
effectiveConfigParams.effectiveInclude = effectiveInclude;
|
|
1748
|
+
}
|
|
1749
|
+
if (typeof effectiveExclude !== "undefined") {
|
|
1750
|
+
effectiveConfigParams.effectiveExclude = effectiveExclude;
|
|
1751
|
+
}
|
|
1752
|
+
const effectiveConfig = buildEffectiveConfig(effectiveConfigParams);
|
|
1753
|
+
const effectiveConfigValue = Object.keys(effectiveConfig).length > 0 ? effectiveConfig : void 0;
|
|
1754
|
+
if (!isConfigEnabled) {
|
|
1755
|
+
if (shouldCollectSkip && isSupportedFile(fileInfo.file)) {
|
|
1756
|
+
const resolved = resolveSkipRoute({
|
|
1757
|
+
file: fileInfo.file,
|
|
1758
|
+
rootDir: fileInfo.rootDir,
|
|
1759
|
+
prefix
|
|
1760
|
+
});
|
|
1761
|
+
onSkip?.(buildSkipInfo(
|
|
1762
|
+
fileInfo.file,
|
|
1763
|
+
"disabled-dir",
|
|
1764
|
+
resolved,
|
|
1765
|
+
configChain,
|
|
1766
|
+
decisionChain,
|
|
1767
|
+
effectiveConfigValue
|
|
1768
|
+
));
|
|
1769
|
+
}
|
|
1770
|
+
return null;
|
|
1771
|
+
}
|
|
1772
|
+
if (effectiveIgnorePrefix.length > 0) {
|
|
1773
|
+
const ignoredByPrefix = hasIgnoredPrefix(fileInfo.file, fileInfo.rootDir, effectiveIgnorePrefix);
|
|
1774
|
+
pushDecisionStep(decisionChain, {
|
|
1775
|
+
step: "ignore-prefix",
|
|
1776
|
+
result: ignoredByPrefix ? "fail" : "pass",
|
|
1777
|
+
source: configSources.ignorePrefix,
|
|
1778
|
+
detail: `prefixes: ${formatList(effectiveIgnorePrefix)}`
|
|
1779
|
+
});
|
|
1780
|
+
if (ignoredByPrefix) {
|
|
1781
|
+
if (shouldCollectSkip && isSupportedFile(fileInfo.file)) {
|
|
1782
|
+
const resolved = resolveSkipRoute({
|
|
1783
|
+
file: fileInfo.file,
|
|
1784
|
+
rootDir: fileInfo.rootDir,
|
|
1785
|
+
prefix
|
|
1786
|
+
});
|
|
1787
|
+
onSkip?.(buildSkipInfo(
|
|
1788
|
+
fileInfo.file,
|
|
1789
|
+
"ignore-prefix",
|
|
1790
|
+
resolved,
|
|
1791
|
+
configChain,
|
|
1792
|
+
decisionChain,
|
|
1793
|
+
effectiveConfigValue
|
|
1794
|
+
));
|
|
1795
|
+
}
|
|
1796
|
+
return null;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
const supportedFile = isSupportedFile(fileInfo.file);
|
|
1800
|
+
pushDecisionStep(decisionChain, {
|
|
1801
|
+
step: "file.supported",
|
|
1802
|
+
result: supportedFile ? "pass" : "fail",
|
|
1803
|
+
detail: supportedFile ? void 0 : "unsupported file type"
|
|
1804
|
+
});
|
|
1805
|
+
if (!supportedFile) {
|
|
1806
|
+
if (shouldCollectIgnore) {
|
|
1807
|
+
const ignoreInfo = {
|
|
1808
|
+
file: fileInfo.file,
|
|
1809
|
+
reason: "unsupported",
|
|
1810
|
+
configChain,
|
|
1811
|
+
decisionChain
|
|
1812
|
+
};
|
|
1813
|
+
if (effectiveConfigValue) {
|
|
1814
|
+
ignoreInfo.effectiveConfig = effectiveConfigValue;
|
|
1815
|
+
}
|
|
1816
|
+
onIgnore?.(ignoreInfo);
|
|
1817
|
+
}
|
|
1818
|
+
return null;
|
|
1819
|
+
}
|
|
1820
|
+
const normalizedFile = toPosix(fileInfo.file);
|
|
1821
|
+
if (typeof effectiveExclude !== "undefined") {
|
|
1822
|
+
const excluded = testPatterns(effectiveExclude, normalizedFile);
|
|
1823
|
+
const patterns = toFilterStrings(effectiveExclude);
|
|
1824
|
+
pushDecisionStep(decisionChain, {
|
|
1825
|
+
step: "filter.exclude",
|
|
1826
|
+
result: excluded ? "fail" : "pass",
|
|
1827
|
+
source: configSources.exclude,
|
|
1828
|
+
detail: patterns.length > 0 ? `${excluded ? "matched" : "no match"}: ${patterns.join(", ")}` : void 0
|
|
1829
|
+
});
|
|
1830
|
+
if (excluded) {
|
|
1831
|
+
if (shouldCollectSkip) {
|
|
1832
|
+
const resolved = resolveSkipRoute({
|
|
1833
|
+
file: fileInfo.file,
|
|
1834
|
+
rootDir: fileInfo.rootDir,
|
|
1835
|
+
prefix
|
|
1836
|
+
});
|
|
1837
|
+
onSkip?.(buildSkipInfo(
|
|
1838
|
+
fileInfo.file,
|
|
1839
|
+
"exclude",
|
|
1840
|
+
resolved,
|
|
1841
|
+
configChain,
|
|
1842
|
+
decisionChain,
|
|
1843
|
+
effectiveConfigValue
|
|
1844
|
+
));
|
|
1845
|
+
}
|
|
1846
|
+
return null;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
if (typeof effectiveInclude !== "undefined") {
|
|
1850
|
+
const included = testPatterns(effectiveInclude, normalizedFile);
|
|
1851
|
+
const patterns = toFilterStrings(effectiveInclude);
|
|
1852
|
+
pushDecisionStep(decisionChain, {
|
|
1853
|
+
step: "filter.include",
|
|
1854
|
+
result: included ? "pass" : "fail",
|
|
1855
|
+
source: configSources.include,
|
|
1856
|
+
detail: patterns.length > 0 ? `${included ? "matched" : "no match"}: ${patterns.join(", ")}` : void 0
|
|
1857
|
+
});
|
|
1858
|
+
if (!included) {
|
|
1859
|
+
if (shouldCollectSkip) {
|
|
1860
|
+
const resolved = resolveSkipRoute({
|
|
1861
|
+
file: fileInfo.file,
|
|
1862
|
+
rootDir: fileInfo.rootDir,
|
|
1863
|
+
prefix
|
|
1864
|
+
});
|
|
1865
|
+
onSkip?.(buildSkipInfo(
|
|
1866
|
+
fileInfo.file,
|
|
1867
|
+
"include",
|
|
1868
|
+
resolved,
|
|
1869
|
+
configChain,
|
|
1870
|
+
decisionChain,
|
|
1871
|
+
effectiveConfigValue
|
|
1872
|
+
));
|
|
1873
|
+
}
|
|
1874
|
+
return null;
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
const result = { decisionChain };
|
|
1878
|
+
if (effectiveConfigValue) {
|
|
1879
|
+
result.effectiveConfigValue = effectiveConfigValue;
|
|
1880
|
+
}
|
|
1881
|
+
return result;
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
async function scanRoutes(params) {
|
|
1885
|
+
const routes = [];
|
|
1886
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1887
|
+
const files = await collectFiles(params.dirs);
|
|
1888
|
+
const globalIgnorePrefix = normalizeIgnorePrefix(params.ignorePrefix);
|
|
1889
|
+
const configCache = /* @__PURE__ */ new Map();
|
|
1890
|
+
const fileCache = /* @__PURE__ */ new Map();
|
|
1891
|
+
const shouldCollectSkip = typeof params.onSkip === "function";
|
|
1892
|
+
const shouldCollectIgnore = typeof params.onIgnore === "function";
|
|
1893
|
+
const shouldCollectConfig = typeof params.onConfig === "function";
|
|
1894
|
+
for (const fileInfo of files) {
|
|
1895
|
+
if (isConfigFile(fileInfo.file)) {
|
|
1896
|
+
if (shouldCollectConfig) {
|
|
1897
|
+
const configParams2 = {
|
|
1898
|
+
file: fileInfo.file,
|
|
1899
|
+
rootDir: fileInfo.rootDir,
|
|
1900
|
+
logger: params.logger,
|
|
1901
|
+
configCache,
|
|
1902
|
+
fileCache
|
|
1903
|
+
};
|
|
1904
|
+
if (params.server) {
|
|
1905
|
+
configParams2.server = params.server;
|
|
1906
|
+
}
|
|
1907
|
+
const config2 = await resolveDirectoryConfig(configParams2);
|
|
1908
|
+
params.onConfig?.({ file: fileInfo.file, enabled: config2.enabled !== false });
|
|
1909
|
+
}
|
|
1910
|
+
continue;
|
|
1911
|
+
}
|
|
1912
|
+
const configParams = {
|
|
1913
|
+
file: fileInfo.file,
|
|
1914
|
+
rootDir: fileInfo.rootDir,
|
|
1915
|
+
logger: params.logger,
|
|
1916
|
+
configCache,
|
|
1917
|
+
fileCache
|
|
1918
|
+
};
|
|
1919
|
+
if (params.server) {
|
|
1920
|
+
configParams.server = params.server;
|
|
1921
|
+
}
|
|
1922
|
+
const config = await resolveDirectoryConfig(configParams);
|
|
1923
|
+
const configChain = config.configChain ?? [];
|
|
1924
|
+
const precheckParams = {
|
|
1925
|
+
fileInfo,
|
|
1926
|
+
prefix: params.prefix,
|
|
1927
|
+
config,
|
|
1928
|
+
configChain,
|
|
1929
|
+
globalIgnorePrefix,
|
|
1930
|
+
shouldCollectSkip,
|
|
1931
|
+
shouldCollectIgnore
|
|
1932
|
+
};
|
|
1933
|
+
if (params.onSkip) {
|
|
1934
|
+
precheckParams.onSkip = params.onSkip;
|
|
1935
|
+
}
|
|
1936
|
+
if (params.onIgnore) {
|
|
1937
|
+
precheckParams.onIgnore = params.onIgnore;
|
|
1938
|
+
}
|
|
1939
|
+
if (params.include) {
|
|
1940
|
+
precheckParams.include = params.include;
|
|
1941
|
+
}
|
|
1942
|
+
if (params.exclude) {
|
|
1943
|
+
precheckParams.exclude = params.exclude;
|
|
1944
|
+
}
|
|
1945
|
+
const precheck = runRoutePrechecks(precheckParams);
|
|
1946
|
+
if (!precheck) {
|
|
1947
|
+
continue;
|
|
1948
|
+
}
|
|
1949
|
+
const { decisionChain, effectiveConfigValue } = precheck;
|
|
1950
|
+
const derived = deriveRouteFromFile(fileInfo.file, fileInfo.rootDir, params.logger);
|
|
1951
|
+
if (!derived) {
|
|
1952
|
+
if (shouldCollectIgnore) {
|
|
1953
|
+
decisionChain.push({
|
|
1954
|
+
step: "route.derived",
|
|
1955
|
+
result: "fail",
|
|
1956
|
+
source: fileInfo.file,
|
|
1957
|
+
detail: "invalid route name"
|
|
1958
|
+
});
|
|
1959
|
+
const ignoreInfo = {
|
|
1960
|
+
file: fileInfo.file,
|
|
1961
|
+
reason: "invalid-route",
|
|
1962
|
+
configChain,
|
|
1963
|
+
decisionChain
|
|
1964
|
+
};
|
|
1965
|
+
if (effectiveConfigValue) {
|
|
1966
|
+
ignoreInfo.effectiveConfig = effectiveConfigValue;
|
|
1967
|
+
}
|
|
1968
|
+
params.onIgnore?.(ignoreInfo);
|
|
1969
|
+
}
|
|
1970
|
+
continue;
|
|
1971
|
+
}
|
|
1972
|
+
decisionChain.push({
|
|
1973
|
+
step: "route.derived",
|
|
1974
|
+
result: "pass",
|
|
1975
|
+
source: fileInfo.file
|
|
1976
|
+
});
|
|
1977
|
+
const rules = await loadRules(fileInfo.file, params.server, params.logger);
|
|
1978
|
+
for (const [index, rule] of rules.entries()) {
|
|
1979
|
+
if (!rule || typeof rule !== "object") {
|
|
1980
|
+
continue;
|
|
1981
|
+
}
|
|
1982
|
+
if (rule.enabled === false) {
|
|
1983
|
+
if (shouldCollectSkip) {
|
|
1984
|
+
const resolved2 = resolveSkipRoute({
|
|
1985
|
+
file: fileInfo.file,
|
|
1986
|
+
rootDir: fileInfo.rootDir,
|
|
1987
|
+
prefix: params.prefix,
|
|
1988
|
+
derived
|
|
1989
|
+
});
|
|
1990
|
+
const ruleDecisionStep = {
|
|
1991
|
+
step: "rule.enabled",
|
|
1992
|
+
result: "fail",
|
|
1993
|
+
source: fileInfo.file,
|
|
1994
|
+
detail: "enabled=false"
|
|
1995
|
+
};
|
|
1996
|
+
const ruleDecisionChain = [...decisionChain, ruleDecisionStep];
|
|
1997
|
+
params.onSkip?.(buildSkipInfo(
|
|
1998
|
+
fileInfo.file,
|
|
1999
|
+
"disabled",
|
|
2000
|
+
resolved2,
|
|
2001
|
+
configChain,
|
|
2002
|
+
ruleDecisionChain,
|
|
2003
|
+
effectiveConfigValue
|
|
2004
|
+
));
|
|
2005
|
+
}
|
|
2006
|
+
continue;
|
|
2007
|
+
}
|
|
2008
|
+
const ruleValue = rule;
|
|
2009
|
+
const unsupportedKeys = ["response", "url", "method"].filter(
|
|
2010
|
+
(key2) => key2 in ruleValue
|
|
2011
|
+
);
|
|
2012
|
+
if (unsupportedKeys.length > 0) {
|
|
2013
|
+
params.logger.warn(
|
|
2014
|
+
`Skip mock with unsupported fields (${unsupportedKeys.join(", ")}): ${fileInfo.file}`
|
|
2015
|
+
);
|
|
2016
|
+
continue;
|
|
2017
|
+
}
|
|
2018
|
+
if (typeof rule.handler === "undefined") {
|
|
2019
|
+
params.logger.warn(`Skip mock without handler: ${fileInfo.file}`);
|
|
2020
|
+
continue;
|
|
2021
|
+
}
|
|
2022
|
+
const resolved = resolveRule({
|
|
2023
|
+
rule,
|
|
2024
|
+
derivedTemplate: derived.template,
|
|
2025
|
+
derivedMethod: derived.method,
|
|
2026
|
+
prefix: params.prefix,
|
|
2027
|
+
file: fileInfo.file,
|
|
2028
|
+
logger: params.logger
|
|
2029
|
+
});
|
|
2030
|
+
if (!resolved) {
|
|
2031
|
+
continue;
|
|
2032
|
+
}
|
|
2033
|
+
resolved.ruleIndex = index;
|
|
2034
|
+
if (configChain.length > 0) {
|
|
2035
|
+
resolved.configChain = configChain;
|
|
2036
|
+
}
|
|
2037
|
+
if (config.headers) {
|
|
2038
|
+
resolved.headers = { ...config.headers, ...resolved.headers ?? {} };
|
|
2039
|
+
}
|
|
2040
|
+
if (typeof resolved.status === "undefined" && typeof config.status === "number") {
|
|
2041
|
+
resolved.status = config.status;
|
|
2042
|
+
}
|
|
2043
|
+
if (typeof resolved.delay === "undefined" && typeof config.delay === "number") {
|
|
2044
|
+
resolved.delay = config.delay;
|
|
2045
|
+
}
|
|
2046
|
+
if (config.middlewares.length > 0) {
|
|
2047
|
+
resolved.middlewares = config.middlewares;
|
|
2048
|
+
}
|
|
2049
|
+
const key = `${resolved.method} ${resolved.template}`;
|
|
2050
|
+
if (seen.has(key)) {
|
|
2051
|
+
params.logger.warn(`Duplicate mock route ${key} from ${fileInfo.file}`);
|
|
2052
|
+
}
|
|
2053
|
+
seen.add(key);
|
|
2054
|
+
routes.push(resolved);
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
return sortRoutes(routes);
|
|
1647
2058
|
}
|
|
1648
2059
|
|
|
2060
|
+
exports.buildManifestData = buildManifestData;
|
|
1649
2061
|
exports.buildSwScript = buildSwScript;
|
|
1650
2062
|
exports.createDebouncer = createDebouncer;
|
|
1651
2063
|
exports.createHonoApp = createHonoApp;
|
|
1652
|
-
exports.createLogger = createLogger;
|
|
1653
2064
|
exports.createMiddleware = createMiddleware;
|
|
1654
2065
|
exports.createPlaygroundMiddleware = createPlaygroundMiddleware;
|
|
1655
2066
|
exports.isInDirs = isInDirs;
|