@marko/run 0.2.4 → 0.2.6
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/.tsbuildinfo +1 -1
- package/dist/adapter/index.cjs +9 -3
- package/dist/adapter/index.js +9 -3
- package/dist/cli/index.mjs +1878 -37
- package/dist/vite/index.cjs +11 -8
- package/dist/vite/index.js +11 -8
- package/dist/vite/plugin.d.ts +3 -2
- package/dist/vite/utils/server.d.ts +1 -1
- package/package.json +2 -2
package/dist/cli/index.mjs
CHANGED
|
@@ -29,7 +29,7 @@ import sade from "sade";
|
|
|
29
29
|
|
|
30
30
|
// src/cli/commands.ts
|
|
31
31
|
import path3 from "path";
|
|
32
|
-
import
|
|
32
|
+
import fs4 from "fs";
|
|
33
33
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
34
34
|
import { build as viteBuild, resolveConfig } from "vite";
|
|
35
35
|
|
|
@@ -45,23 +45,58 @@ function setConfig(obj, key, value) {
|
|
|
45
45
|
}
|
|
46
46
|
var getExternalPluginOptions = (viteConfig) => getConfig(viteConfig, PluginConfigKey);
|
|
47
47
|
var setExternalPluginOptions = (viteConfig, value) => setConfig(viteConfig, PluginConfigKey, value);
|
|
48
|
+
var getExternalAdapterOptions = (viteConfig) => getConfig(viteConfig, AdapterConfigKey);
|
|
48
49
|
var setExternalAdapterOptions = (viteConfig, value) => setConfig(viteConfig, AdapterConfigKey, value);
|
|
49
50
|
|
|
50
51
|
// src/cli/commands.ts
|
|
51
52
|
import { MemoryStore } from "@marko/vite";
|
|
52
53
|
|
|
54
|
+
// src/vite/utils/server.ts
|
|
55
|
+
import net from "net";
|
|
56
|
+
import cp from "child_process";
|
|
57
|
+
import { parse, config } from "dotenv";
|
|
58
|
+
import fs from "fs";
|
|
59
|
+
import cluster from "cluster";
|
|
60
|
+
async function getConnection(port) {
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
const connection = net.connect(port).setNoDelay(true).setKeepAlive(false).on("error", () => {
|
|
63
|
+
connection.end();
|
|
64
|
+
resolve(null);
|
|
65
|
+
}).on("connect", () => {
|
|
66
|
+
resolve(connection);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async function isPortInUse(port) {
|
|
71
|
+
return Boolean(await getConnection(port));
|
|
72
|
+
}
|
|
73
|
+
async function getAvailablePort(port) {
|
|
74
|
+
if (port && !await isPortInUse(port)) {
|
|
75
|
+
return port;
|
|
76
|
+
}
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
const server = net.createServer().listen(0, () => {
|
|
79
|
+
const { port: port2 } = server.address();
|
|
80
|
+
server.close(() => resolve(port2));
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
53
85
|
// src/vite/plugin.ts
|
|
54
86
|
import path2 from "path";
|
|
55
87
|
import crypto from "crypto";
|
|
56
|
-
import
|
|
88
|
+
import fs3 from "fs";
|
|
57
89
|
import glob from "glob";
|
|
58
90
|
import { mergeConfig } from "vite";
|
|
59
91
|
import markoVitePlugin, { FileStore } from "@marko/vite";
|
|
60
92
|
|
|
61
93
|
// src/vite/constants.ts
|
|
94
|
+
var markoRunFilePrefix = "__marko-run__";
|
|
62
95
|
var virtualFilePrefix = "virtual:marko-run";
|
|
63
96
|
var virtualRoutesPrefix = `${virtualFilePrefix}/routes`;
|
|
64
97
|
var virtualRuntimePrefix = `${virtualFilePrefix}/internal`;
|
|
98
|
+
var httpVerbs = ["get", "post", "put", "delete"];
|
|
99
|
+
var serverEntryQuery = "?marko-server-entry";
|
|
65
100
|
var RoutableFileTypes = {
|
|
66
101
|
Page: "page",
|
|
67
102
|
Layout: "layout",
|
|
@@ -215,6 +250,144 @@ var VDir = _VDir;
|
|
|
215
250
|
_dirs = new WeakMap();
|
|
216
251
|
_pathlessDirs = new WeakMap();
|
|
217
252
|
|
|
253
|
+
// src/vite/routes/parse.ts
|
|
254
|
+
function parseFlatRoute(pattern) {
|
|
255
|
+
if (!pattern)
|
|
256
|
+
throw new Error("Empty pattern");
|
|
257
|
+
const len = pattern.length;
|
|
258
|
+
let i = 0;
|
|
259
|
+
return parse2([
|
|
260
|
+
{
|
|
261
|
+
id: "/",
|
|
262
|
+
segments: [],
|
|
263
|
+
source: pattern
|
|
264
|
+
}
|
|
265
|
+
]);
|
|
266
|
+
function parse2(basePaths, group) {
|
|
267
|
+
const pathMap = /* @__PURE__ */ new Map();
|
|
268
|
+
const delimiters = group ? ").," : ".,";
|
|
269
|
+
let charCode;
|
|
270
|
+
let segmentStart = i;
|
|
271
|
+
let type;
|
|
272
|
+
let current;
|
|
273
|
+
do {
|
|
274
|
+
charCode = pattern.charCodeAt(i);
|
|
275
|
+
if (charCode === 41 && group) {
|
|
276
|
+
break;
|
|
277
|
+
} else if (charCode === 44) {
|
|
278
|
+
if (!current) {
|
|
279
|
+
segmentEnd(
|
|
280
|
+
basePaths.map((path4) => ({
|
|
281
|
+
...path4,
|
|
282
|
+
segments: path4.segments.slice()
|
|
283
|
+
})),
|
|
284
|
+
"",
|
|
285
|
+
"_",
|
|
286
|
+
pathMap
|
|
287
|
+
);
|
|
288
|
+
} else {
|
|
289
|
+
segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
|
|
290
|
+
}
|
|
291
|
+
current = void 0;
|
|
292
|
+
type = void 0;
|
|
293
|
+
segmentStart = ++i;
|
|
294
|
+
} else if (charCode === 46) {
|
|
295
|
+
if (current) {
|
|
296
|
+
segmentEnd(current, pattern.slice(segmentStart, i), type);
|
|
297
|
+
}
|
|
298
|
+
type = void 0;
|
|
299
|
+
segmentStart = ++i;
|
|
300
|
+
} else if (charCode === 40) {
|
|
301
|
+
const groupPaths = parse2(current || basePaths, ++i);
|
|
302
|
+
if (groupPaths.length) {
|
|
303
|
+
current = groupPaths;
|
|
304
|
+
}
|
|
305
|
+
segmentStart = ++i;
|
|
306
|
+
} else {
|
|
307
|
+
if (charCode === 95) {
|
|
308
|
+
type = "_";
|
|
309
|
+
} else if (charCode === 36) {
|
|
310
|
+
type = pattern.charCodeAt(i + 1) === 36 ? "$$" : "$";
|
|
311
|
+
}
|
|
312
|
+
current ?? (current = basePaths.map((path4) => ({
|
|
313
|
+
...path4,
|
|
314
|
+
segments: path4.segments.slice()
|
|
315
|
+
})));
|
|
316
|
+
i = len;
|
|
317
|
+
for (const char of delimiters) {
|
|
318
|
+
const index = pattern.indexOf(char, segmentStart);
|
|
319
|
+
if (index >= 0 && index < i) {
|
|
320
|
+
i = index;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
} while (i < len);
|
|
325
|
+
if (group && charCode !== 41) {
|
|
326
|
+
throw new Error(
|
|
327
|
+
`Invalid route pattern: group was not closed '${pattern.slice(
|
|
328
|
+
group
|
|
329
|
+
)}' in '${pattern}'`
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
if (!current) {
|
|
333
|
+
segmentEnd(
|
|
334
|
+
basePaths.map((path4) => ({
|
|
335
|
+
...path4,
|
|
336
|
+
segments: path4.segments.slice()
|
|
337
|
+
})),
|
|
338
|
+
"",
|
|
339
|
+
"_",
|
|
340
|
+
pathMap
|
|
341
|
+
);
|
|
342
|
+
} else {
|
|
343
|
+
segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
|
|
344
|
+
}
|
|
345
|
+
return [...pathMap.values()];
|
|
346
|
+
}
|
|
347
|
+
function segmentEnd(paths, raw, type, map) {
|
|
348
|
+
let segment;
|
|
349
|
+
if (raw) {
|
|
350
|
+
segment = {
|
|
351
|
+
raw,
|
|
352
|
+
name: raw,
|
|
353
|
+
type
|
|
354
|
+
};
|
|
355
|
+
if (type === "$" || type === "$$") {
|
|
356
|
+
segment.name = type;
|
|
357
|
+
segment.param = raw.slice(type.length);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
for (const path4 of paths) {
|
|
361
|
+
if (segment) {
|
|
362
|
+
if (path4.isCatchall) {
|
|
363
|
+
throw new Error(
|
|
364
|
+
`Invalid route pattern: nested segments are not allowed after a catch-all parameter. Found '.' following '${pattern.slice(
|
|
365
|
+
0,
|
|
366
|
+
i
|
|
367
|
+
)}' in '${pattern}'.`
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
path4.segments.push(segment);
|
|
371
|
+
path4.id += path4.id === "/" ? segment.name : `/${segment.name}`;
|
|
372
|
+
if (type === "$$") {
|
|
373
|
+
path4.isCatchall = true;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (map) {
|
|
377
|
+
if (map.has(path4.id)) {
|
|
378
|
+
const existing = map.get(path4.id);
|
|
379
|
+
const existingExpansion = existing.segments.map((s) => s.raw).join(".");
|
|
380
|
+
const currentExpansion = path4.segments.map((s) => s.raw).join(".");
|
|
381
|
+
throw new Error(
|
|
382
|
+
`Invalid route pattern: route '${path4.id}' is ambiguous. Expansion '${currentExpansion}' collides with '${existingExpansion}' in '${pattern}'.`
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
map.set(path4.id, path4);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
218
391
|
// src/vite/routes/builder.ts
|
|
219
392
|
var markoFiles = `(${RoutableFileTypes.Layout}|${RoutableFileTypes.Page}|${RoutableFileTypes.NotFound}|${RoutableFileTypes.Error})\\.(?:.*\\.)?(marko)`;
|
|
220
393
|
var nonMarkoFiles = `(${RoutableFileTypes.Middleware}|${RoutableFileTypes.Handler}|${RoutableFileTypes.Meta})\\.(?:.*\\.)?(.+)`;
|
|
@@ -222,13 +395,1104 @@ var routeableFileRegex = new RegExp(
|
|
|
222
395
|
`[+](?:${markoFiles}|${nonMarkoFiles})$`,
|
|
223
396
|
"i"
|
|
224
397
|
);
|
|
398
|
+
function matchRoutableFile(filename) {
|
|
399
|
+
const match = filename.match(routeableFileRegex);
|
|
400
|
+
return match && (match[1] || match[3]).toLowerCase();
|
|
401
|
+
}
|
|
402
|
+
function isSpecialType(type) {
|
|
403
|
+
return type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error;
|
|
404
|
+
}
|
|
405
|
+
async function buildRoutes(walk, basePath = "") {
|
|
406
|
+
if (basePath) {
|
|
407
|
+
basePath = basePath.replace(/^\/+|\/+$/g, "");
|
|
408
|
+
}
|
|
409
|
+
const uniqueRoutes = /* @__PURE__ */ new Map();
|
|
410
|
+
const routes = [];
|
|
411
|
+
const special = {};
|
|
412
|
+
const middlewares = /* @__PURE__ */ new Set();
|
|
413
|
+
const unusedFiles = /* @__PURE__ */ new Set();
|
|
414
|
+
const currentLayouts = /* @__PURE__ */ new Set();
|
|
415
|
+
const currentMiddleware = /* @__PURE__ */ new Set();
|
|
416
|
+
const root = new VDir();
|
|
417
|
+
const dirStack = [];
|
|
418
|
+
let activeDirs = [root];
|
|
419
|
+
let nextFileId = 1;
|
|
420
|
+
let nextRouteIndex = 1;
|
|
421
|
+
let isBaseDir = true;
|
|
422
|
+
await walk({
|
|
423
|
+
onEnter({ name }) {
|
|
424
|
+
if (!name || isBaseDir) {
|
|
425
|
+
isBaseDir = false;
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
dirStack.push(name);
|
|
429
|
+
const previousDirs = activeDirs;
|
|
430
|
+
const paths = parseFlatRoute(name);
|
|
431
|
+
activeDirs = VDir.addPaths(previousDirs, paths);
|
|
432
|
+
return () => {
|
|
433
|
+
activeDirs = previousDirs;
|
|
434
|
+
dirStack.pop();
|
|
435
|
+
};
|
|
436
|
+
},
|
|
437
|
+
onFile({ name, path: path4 }) {
|
|
438
|
+
const match = name.match(routeableFileRegex);
|
|
439
|
+
if (!match) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const type = (match[1] || match[3]).toLowerCase();
|
|
443
|
+
if (dirStack.length && isSpecialType(type)) {
|
|
444
|
+
console.warn(
|
|
445
|
+
`Special pages '${RoutableFileTypes.NotFound}' and '${RoutableFileTypes.Error}' are only considered in the root directory - ignoring ${path4}`
|
|
446
|
+
);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
let dirs = activeDirs;
|
|
450
|
+
if (match.index) {
|
|
451
|
+
const paths = parseFlatRoute(name.slice(0, match.index));
|
|
452
|
+
dirs = VDir.addPaths(activeDirs, paths);
|
|
453
|
+
}
|
|
454
|
+
const dirPath = dirStack.join("/");
|
|
455
|
+
const relativePath = dirPath ? `${dirPath}/${name}` : name;
|
|
456
|
+
const file = {
|
|
457
|
+
id: String(nextFileId++),
|
|
458
|
+
name,
|
|
459
|
+
type,
|
|
460
|
+
filePath: path4,
|
|
461
|
+
relativePath,
|
|
462
|
+
importPath: `${basePath}/${relativePath}`,
|
|
463
|
+
verbs: type === RoutableFileTypes.Page ? ["get"] : void 0
|
|
464
|
+
};
|
|
465
|
+
for (const dir of dirs) {
|
|
466
|
+
dir.addFile(file);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
traverse(root);
|
|
471
|
+
return {
|
|
472
|
+
list: routes,
|
|
473
|
+
middleware: [...middlewares],
|
|
474
|
+
special
|
|
475
|
+
};
|
|
476
|
+
function traverse(dir) {
|
|
477
|
+
let middleware;
|
|
478
|
+
let layout;
|
|
479
|
+
if (dir.files) {
|
|
480
|
+
middleware = dir.files.get(RoutableFileTypes.Middleware);
|
|
481
|
+
layout = dir.files.get(RoutableFileTypes.Layout);
|
|
482
|
+
const handler = dir.files.get(RoutableFileTypes.Handler);
|
|
483
|
+
const page = dir.files.get(RoutableFileTypes.Page);
|
|
484
|
+
let hasSpecial = false;
|
|
485
|
+
if (middleware) {
|
|
486
|
+
if (currentMiddleware.has(middleware)) {
|
|
487
|
+
middleware = void 0;
|
|
488
|
+
} else {
|
|
489
|
+
currentMiddleware.add(middleware);
|
|
490
|
+
unusedFiles.add(middleware);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (layout) {
|
|
494
|
+
if (currentLayouts.has(layout)) {
|
|
495
|
+
layout = void 0;
|
|
496
|
+
} else {
|
|
497
|
+
currentLayouts.add(layout);
|
|
498
|
+
unusedFiles.add(layout);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
if (page || handler) {
|
|
502
|
+
const path4 = dir.pathInfo;
|
|
503
|
+
if (uniqueRoutes.has(path4.id)) {
|
|
504
|
+
const existing = uniqueRoutes.get(path4.id);
|
|
505
|
+
const route = routes[existing.index];
|
|
506
|
+
const existingFiles = [route.handler, route.page].filter(Boolean).map((f) => f.filePath);
|
|
507
|
+
const currentFiles = [handler, page].filter(Boolean).map((f) => f.filePath);
|
|
508
|
+
throw new Error(`Duplicate routes for path '${path4.path}' were defined. A route established by:
|
|
509
|
+
${existingFiles.join(" and ")} via '${existing.dir.path}'
|
|
510
|
+
collides with
|
|
511
|
+
${currentFiles.join(" and ")} via '${dir.path}'
|
|
512
|
+
`);
|
|
513
|
+
}
|
|
514
|
+
uniqueRoutes.set(path4.id, { dir, index: routes.length });
|
|
515
|
+
routes.push({
|
|
516
|
+
index: nextRouteIndex++,
|
|
517
|
+
key: dir.fullPath,
|
|
518
|
+
paths: [path4],
|
|
519
|
+
middleware: [...currentMiddleware],
|
|
520
|
+
layouts: page ? [...currentLayouts] : [],
|
|
521
|
+
meta: dir.files.get(RoutableFileTypes.Meta),
|
|
522
|
+
page,
|
|
523
|
+
handler,
|
|
524
|
+
entryName: `${markoRunFilePrefix}route` + (dir.path !== "/" ? dir.fullPath.replace(/\//g, ".").replace(/(%[A-Fa-f0-9]{2})+/g, "_") : "")
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
if (dir === root) {
|
|
528
|
+
for (const [type, file] of dir.files) {
|
|
529
|
+
if (isSpecialType(type)) {
|
|
530
|
+
hasSpecial = true;
|
|
531
|
+
special[type] = {
|
|
532
|
+
index: 0,
|
|
533
|
+
key: type,
|
|
534
|
+
paths: [],
|
|
535
|
+
middleware: [],
|
|
536
|
+
layouts: [...currentLayouts],
|
|
537
|
+
page: file,
|
|
538
|
+
entryName: `${markoRunFilePrefix}special.${type}`
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
if (handler || page) {
|
|
544
|
+
for (const middleware2 of currentMiddleware) {
|
|
545
|
+
middlewares.add(middleware2);
|
|
546
|
+
unusedFiles.delete(middleware2);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (page || hasSpecial) {
|
|
550
|
+
for (const layout2 of currentLayouts) {
|
|
551
|
+
unusedFiles.delete(layout2);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
if (dir.dirs) {
|
|
556
|
+
for (const child of dir.dirs()) {
|
|
557
|
+
traverse(child);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (middleware) {
|
|
561
|
+
currentMiddleware.delete(middleware);
|
|
562
|
+
}
|
|
563
|
+
if (layout) {
|
|
564
|
+
currentLayouts.delete(layout);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
225
568
|
|
|
226
569
|
// src/vite/routes/walk.ts
|
|
227
|
-
import
|
|
570
|
+
import fs2 from "fs";
|
|
228
571
|
import path from "path";
|
|
572
|
+
function createFSWalker(dir) {
|
|
573
|
+
return async function walkFS({
|
|
574
|
+
onEnter,
|
|
575
|
+
onFile,
|
|
576
|
+
onDir,
|
|
577
|
+
maxDepth = 50
|
|
578
|
+
}) {
|
|
579
|
+
async function walk(dir2, depth) {
|
|
580
|
+
const onExit = onEnter == null ? void 0 : onEnter(dir2);
|
|
581
|
+
if (onExit !== false) {
|
|
582
|
+
const dirs = [];
|
|
583
|
+
const entries = await fs2.promises.readdir(dir2.path, {
|
|
584
|
+
withFileTypes: true
|
|
585
|
+
});
|
|
586
|
+
const prefix = dir2.path + path.sep;
|
|
587
|
+
for (const entry of entries) {
|
|
588
|
+
const walkEntry = {
|
|
589
|
+
name: entry.name,
|
|
590
|
+
path: prefix + entry.name
|
|
591
|
+
};
|
|
592
|
+
if (entry.isDirectory()) {
|
|
593
|
+
dirs.push(walkEntry);
|
|
594
|
+
} else {
|
|
595
|
+
onFile == null ? void 0 : onFile(walkEntry);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
if ((onDir == null ? void 0 : onDir()) !== false && --depth > 0) {
|
|
599
|
+
for (const entry of dirs) {
|
|
600
|
+
await walk(entry, depth);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
onExit == null ? void 0 : onExit();
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
await walk(
|
|
607
|
+
{
|
|
608
|
+
path: dir,
|
|
609
|
+
name: path.basename(dir)
|
|
610
|
+
},
|
|
611
|
+
maxDepth
|
|
612
|
+
);
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// src/vite/codegen/writer.ts
|
|
617
|
+
function createWriter(sink, options) {
|
|
618
|
+
let buffer = "";
|
|
619
|
+
let indentLevel = 0;
|
|
620
|
+
let indentString = "";
|
|
621
|
+
let firstOpenIndex = 0;
|
|
622
|
+
const branches = [];
|
|
623
|
+
const openWriters = /* @__PURE__ */ new Map();
|
|
624
|
+
function write(data) {
|
|
625
|
+
if (!writer.__isActive) {
|
|
626
|
+
throw new Error("Cannot write to branch that has been joined");
|
|
627
|
+
}
|
|
628
|
+
if (openWriters.size) {
|
|
629
|
+
buffer += data;
|
|
630
|
+
} else {
|
|
631
|
+
sink(data);
|
|
632
|
+
}
|
|
633
|
+
return writer;
|
|
634
|
+
}
|
|
635
|
+
const writer = {
|
|
636
|
+
__isActive: true,
|
|
637
|
+
get indent() {
|
|
638
|
+
return indentLevel;
|
|
639
|
+
},
|
|
640
|
+
set indent(value) {
|
|
641
|
+
if (options == null ? void 0 : options.indentWith) {
|
|
642
|
+
if (value < 0) {
|
|
643
|
+
value = 0;
|
|
644
|
+
}
|
|
645
|
+
if (value !== indentLevel) {
|
|
646
|
+
indentLevel = value;
|
|
647
|
+
indentString = options.indentWith.repeat(indentLevel);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
},
|
|
651
|
+
write(data, indent = false) {
|
|
652
|
+
if (indent && indentString) {
|
|
653
|
+
write(indentString);
|
|
654
|
+
}
|
|
655
|
+
return write(data);
|
|
656
|
+
},
|
|
657
|
+
writeLines(...lines) {
|
|
658
|
+
for (const line of lines) {
|
|
659
|
+
if (line) {
|
|
660
|
+
writer.write(line, true);
|
|
661
|
+
}
|
|
662
|
+
writer.write("\n");
|
|
663
|
+
}
|
|
664
|
+
return writer;
|
|
665
|
+
},
|
|
666
|
+
writeBlockStart(data) {
|
|
667
|
+
writer.writeLines(data).indent++;
|
|
668
|
+
return writer;
|
|
669
|
+
},
|
|
670
|
+
writeBlockEnd(data = "}") {
|
|
671
|
+
writer.indent--;
|
|
672
|
+
writer.writeLines(data);
|
|
673
|
+
return writer;
|
|
674
|
+
},
|
|
675
|
+
writeBlock(start, lines, end) {
|
|
676
|
+
return writer.writeBlockStart(start).writeLines(...lines).writeBlockEnd(end);
|
|
677
|
+
},
|
|
678
|
+
branch(name) {
|
|
679
|
+
let existing = openWriters.get(name);
|
|
680
|
+
if (existing) {
|
|
681
|
+
return existing;
|
|
682
|
+
}
|
|
683
|
+
const branch = {
|
|
684
|
+
buffer,
|
|
685
|
+
writer: createWriter(
|
|
686
|
+
(data) => {
|
|
687
|
+
branch.buffer += data;
|
|
688
|
+
},
|
|
689
|
+
{
|
|
690
|
+
...options,
|
|
691
|
+
onJoin() {
|
|
692
|
+
openWriters.delete(name);
|
|
693
|
+
for (let i = firstOpenIndex; i < branches.length; i++) {
|
|
694
|
+
const b = branches[i];
|
|
695
|
+
if (!b) {
|
|
696
|
+
continue;
|
|
697
|
+
} else if (b.writer.__isActive) {
|
|
698
|
+
break;
|
|
699
|
+
}
|
|
700
|
+
sink(b.buffer);
|
|
701
|
+
branches[i] = null;
|
|
702
|
+
firstOpenIndex++;
|
|
703
|
+
}
|
|
704
|
+
if (!openWriters.size) {
|
|
705
|
+
sink(buffer);
|
|
706
|
+
buffer = "";
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
)
|
|
711
|
+
};
|
|
712
|
+
branch.writer.indent = indentLevel;
|
|
713
|
+
openWriters.set(name, branch.writer);
|
|
714
|
+
branches.push(branch);
|
|
715
|
+
buffer = "";
|
|
716
|
+
return branch.writer;
|
|
717
|
+
},
|
|
718
|
+
join(recursive) {
|
|
719
|
+
var _a;
|
|
720
|
+
if (writer.__isActive) {
|
|
721
|
+
if (openWriters.size) {
|
|
722
|
+
if (recursive) {
|
|
723
|
+
for (const branch of openWriters.values()) {
|
|
724
|
+
branch.join(true);
|
|
725
|
+
}
|
|
726
|
+
} else {
|
|
727
|
+
throw new Error(
|
|
728
|
+
`Cannot join a Writer with un-joined branches - use the \`recursive\` argument to join all open branches`
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
buffer && sink(buffer);
|
|
733
|
+
writer.__isActive = false;
|
|
734
|
+
(_a = options == null ? void 0 : options.onJoin) == null ? void 0 : _a.call(options, writer);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
return writer;
|
|
739
|
+
}
|
|
740
|
+
function createStringWriter(opts) {
|
|
741
|
+
let code = "";
|
|
742
|
+
const writer = createWriter((data) => {
|
|
743
|
+
code += data;
|
|
744
|
+
}, { indentWith: " ", ...opts });
|
|
745
|
+
return Object.assign(writer, {
|
|
746
|
+
end() {
|
|
747
|
+
writer.join(true);
|
|
748
|
+
return code;
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// src/vite/utils/route.ts
|
|
754
|
+
function getVerbs(route) {
|
|
755
|
+
var _a, _b;
|
|
756
|
+
const verbs = ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.slice()) || [];
|
|
757
|
+
if (route.page && !verbs.includes("get")) {
|
|
758
|
+
verbs.unshift("get");
|
|
759
|
+
}
|
|
760
|
+
return verbs;
|
|
761
|
+
}
|
|
762
|
+
function hasVerb(route, verb) {
|
|
763
|
+
var _a, _b;
|
|
764
|
+
return verb === "get" && route.page || ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.includes(verb));
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// src/vite/codegen/index.ts
|
|
768
|
+
function renderRouteTemplate(route) {
|
|
769
|
+
if (!route.page) {
|
|
770
|
+
throw new Error(`Route ${route.key} has no page to render`);
|
|
771
|
+
}
|
|
772
|
+
const writer = createStringWriter();
|
|
773
|
+
writer.writeLines(`// ${virtualFilePrefix}/${route.entryName}.marko`);
|
|
774
|
+
writer.branch("imports");
|
|
775
|
+
writer.writeLines("");
|
|
776
|
+
writeRouteTemplateTag(writer, [...route.layouts, route.page]);
|
|
777
|
+
return writer.end();
|
|
778
|
+
}
|
|
779
|
+
function writeRouteTemplateTag(writer, [file, ...rest], index = 1) {
|
|
780
|
+
if (file) {
|
|
781
|
+
const isLast = !rest.length;
|
|
782
|
+
const tag = isLast ? "page" : `layout${index}`;
|
|
783
|
+
writer.branch("imports").writeLines(`import ${tag} from './${file.importPath}';`);
|
|
784
|
+
if (isLast) {
|
|
785
|
+
writer.writeLines(`<${tag} ...input />`);
|
|
786
|
+
} else {
|
|
787
|
+
writer.writeBlockStart(`<${tag} ...input>`);
|
|
788
|
+
writeRouteTemplateTag(writer, rest, index + 1);
|
|
789
|
+
writer.writeBlockEnd(`</>`);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
function renderRouteEntry(route) {
|
|
794
|
+
var _a;
|
|
795
|
+
const { key, index, handler, page, middleware, meta, entryName } = route;
|
|
796
|
+
const verbs = getVerbs(route);
|
|
797
|
+
if (!verbs) {
|
|
798
|
+
throw new Error(
|
|
799
|
+
`Route ${key} doesn't have a handler or page for any HTTP verbs`
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
const writer = createStringWriter();
|
|
803
|
+
writer.writeLines(`// ${virtualFilePrefix}/${entryName}.js`);
|
|
804
|
+
const imports = writer.branch("imports");
|
|
805
|
+
const runtimeImports = [];
|
|
806
|
+
if (handler) {
|
|
807
|
+
runtimeImports.push("normalize");
|
|
808
|
+
}
|
|
809
|
+
if (handler || middleware.length) {
|
|
810
|
+
runtimeImports.push("call");
|
|
811
|
+
}
|
|
812
|
+
if (!page || verbs.length > 1) {
|
|
813
|
+
runtimeImports.push("noContent");
|
|
814
|
+
}
|
|
815
|
+
if (page) {
|
|
816
|
+
runtimeImports.push("pageResponse");
|
|
817
|
+
}
|
|
818
|
+
if (runtimeImports.length) {
|
|
819
|
+
imports.writeLines(
|
|
820
|
+
`import { ${runtimeImports.join(", ")} } from '${virtualRuntimePrefix}';`
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
if (middleware.length) {
|
|
824
|
+
const names = middleware.map((m) => `mware${m.id}`);
|
|
825
|
+
imports.writeLines(
|
|
826
|
+
`import { ${names.join(
|
|
827
|
+
", "
|
|
828
|
+
)} } from '${virtualFilePrefix}/${markoRunFilePrefix}middleware.js';`
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.length) {
|
|
832
|
+
writer.writeLines("");
|
|
833
|
+
const names = [];
|
|
834
|
+
for (const verb of handler.verbs) {
|
|
835
|
+
const importName = verb.toUpperCase();
|
|
836
|
+
names.push(importName);
|
|
837
|
+
writer.writeLines(`const ${verb}Handler = normalize(${importName});`);
|
|
838
|
+
}
|
|
839
|
+
imports.writeLines(
|
|
840
|
+
`import { ${names.join(", ")} } from './${handler.importPath}';`
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
if (page) {
|
|
844
|
+
imports.writeLines(
|
|
845
|
+
`import page from '${virtualFilePrefix}/${entryName}.marko${serverEntryQuery}';`
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
if (meta) {
|
|
849
|
+
imports.writeLines(
|
|
850
|
+
`export { default as meta${index} } from './${meta.importPath}';`
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
for (const verb of verbs) {
|
|
854
|
+
writeRouteEntryHandler(writer, route, verb);
|
|
855
|
+
}
|
|
856
|
+
return writer.end();
|
|
857
|
+
}
|
|
858
|
+
function writePageResponse(writer, wrapFn) {
|
|
859
|
+
writer.writeLines(
|
|
860
|
+
`${wrapFn ? `const ${wrapFn} = () =>` : `return`} pageResponse(page, buildInput());`
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
function writeMiddleware(writer, middleware, next, wrapFn) {
|
|
864
|
+
if (wrapFn) {
|
|
865
|
+
writer.writeLines(
|
|
866
|
+
`const ${wrapFn} = () => call(${middleware}, ${next}, context);`
|
|
867
|
+
);
|
|
868
|
+
} else {
|
|
869
|
+
writer.writeLines(`return call(${middleware}, ${next}, context);`);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
function writeRouteEntryHandler(writer, route, verb) {
|
|
873
|
+
var _a;
|
|
874
|
+
const { key, index, page, handler, middleware } = route;
|
|
875
|
+
const len = middleware.length;
|
|
876
|
+
let nextName;
|
|
877
|
+
let currentName;
|
|
878
|
+
let hasBody = false;
|
|
879
|
+
writer.writeLines("");
|
|
880
|
+
if (page) {
|
|
881
|
+
writer.writeBlockStart(
|
|
882
|
+
`export async function ${verb}${index}(context, buildInput) {`
|
|
883
|
+
);
|
|
884
|
+
} else {
|
|
885
|
+
writer.writeBlockStart(`export async function ${verb}${index}(context) {`);
|
|
886
|
+
}
|
|
887
|
+
const continuations = writer.branch("cont");
|
|
888
|
+
if (page && verb === "get") {
|
|
889
|
+
currentName = "__page";
|
|
890
|
+
if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes(verb)) {
|
|
891
|
+
const name = `${verb}Handler`;
|
|
892
|
+
writePageResponse(continuations, currentName);
|
|
893
|
+
if (len) {
|
|
894
|
+
nextName = currentName;
|
|
895
|
+
currentName = `__${name}`;
|
|
896
|
+
writeMiddleware(continuations, name, nextName, currentName);
|
|
897
|
+
} else {
|
|
898
|
+
writeMiddleware(writer, name, currentName);
|
|
899
|
+
hasBody = true;
|
|
900
|
+
}
|
|
901
|
+
} else if (len) {
|
|
902
|
+
writePageResponse(continuations, currentName);
|
|
903
|
+
nextName = currentName;
|
|
904
|
+
} else {
|
|
905
|
+
writePageResponse(continuations);
|
|
906
|
+
hasBody = true;
|
|
907
|
+
}
|
|
908
|
+
} else if (handler) {
|
|
909
|
+
const name = `${verb}Handler`;
|
|
910
|
+
currentName = `__${name}`;
|
|
911
|
+
nextName = "noContent";
|
|
912
|
+
if (len) {
|
|
913
|
+
writeMiddleware(continuations, name, nextName, currentName);
|
|
914
|
+
} else {
|
|
915
|
+
writeMiddleware(writer, name, nextName);
|
|
916
|
+
hasBody = true;
|
|
917
|
+
}
|
|
918
|
+
} else {
|
|
919
|
+
throw new Error(`Route ${key} has no handler for ${verb} requests`);
|
|
920
|
+
}
|
|
921
|
+
if (!hasBody) {
|
|
922
|
+
let i = len;
|
|
923
|
+
while (i--) {
|
|
924
|
+
const { id } = middleware[i];
|
|
925
|
+
const name = `mware${id}`;
|
|
926
|
+
nextName = currentName;
|
|
927
|
+
currentName = i ? `__${name}` : "";
|
|
928
|
+
writeMiddleware(continuations, name, nextName, currentName);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
continuations.join();
|
|
932
|
+
writer.writeBlockEnd("}");
|
|
933
|
+
}
|
|
934
|
+
function renderRouter(routes, options = {
|
|
935
|
+
trailingSlashes: "RedirectWithout"
|
|
936
|
+
}) {
|
|
937
|
+
const writer = createStringWriter();
|
|
938
|
+
writer.writeLines(`// @marko/run/router`);
|
|
939
|
+
const imports = writer.branch("imports");
|
|
940
|
+
imports.writeLines(
|
|
941
|
+
`import { NotHandled, NotMatched, createContext } from 'virtual:marko-run/internal';`
|
|
942
|
+
);
|
|
943
|
+
for (const route of routes.list) {
|
|
944
|
+
const verbs = getVerbs(route);
|
|
945
|
+
const names = verbs.map((verb) => `${verb}${route.index}`);
|
|
946
|
+
route.meta && names.push(`meta${route.index}`);
|
|
947
|
+
imports.writeLines(
|
|
948
|
+
`import { ${names.join(", ")} } from '${virtualFilePrefix}/${route.entryName}.js';`
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
for (const { key, entryName } of Object.values(routes.special)) {
|
|
952
|
+
imports.writeLines(
|
|
953
|
+
`import page${key} from '${virtualFilePrefix}/${entryName}.marko${serverEntryQuery}';`
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
writer.writeLines(
|
|
957
|
+
`
|
|
958
|
+
globalThis.__marko_run__ = { match, fetch, invoke };
|
|
959
|
+
`
|
|
960
|
+
).writeBlockStart(`export function match(method, pathname) {`).writeLines(
|
|
961
|
+
`if (!pathname) {
|
|
962
|
+
pathname = '/';
|
|
963
|
+
} else if (pathname.charAt(0) !== '/') {
|
|
964
|
+
pathname = '/' + pathname;
|
|
965
|
+
}`
|
|
966
|
+
).writeBlockStart(`switch (method.toLowerCase()) {`);
|
|
967
|
+
for (const verb of httpVerbs) {
|
|
968
|
+
const filteredRoutes = routes.list.filter((route) => hasVerb(route, verb));
|
|
969
|
+
if (filteredRoutes.length) {
|
|
970
|
+
const trie = createRouteTrie(filteredRoutes);
|
|
971
|
+
writer.writeBlockStart(`case '${verb}': {`);
|
|
972
|
+
writeRouterVerb(writer, trie, verb);
|
|
973
|
+
writer.writeBlockEnd("}");
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
writer.writeBlockEnd("}").writeLines("return null;").writeBlockEnd("}");
|
|
977
|
+
writer.write(`
|
|
978
|
+
export async function invoke(route, request, platform, url) {
|
|
979
|
+
const [context, buildInput] = createContext(route, request, platform, url);
|
|
980
|
+
try {
|
|
981
|
+
if (route) {
|
|
982
|
+
try {
|
|
983
|
+
const response = await route.handler(context, buildInput);
|
|
984
|
+
if (response) return response;
|
|
985
|
+
} catch (error) {
|
|
986
|
+
if (error === NotHandled) {
|
|
987
|
+
return;
|
|
988
|
+
} else if (error !== NotMatched) {
|
|
989
|
+
throw error;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
`);
|
|
993
|
+
if (routes.special[RoutableFileTypes.NotFound]) {
|
|
994
|
+
writer.indent = 2;
|
|
995
|
+
imports.writeLines(
|
|
996
|
+
`
|
|
997
|
+
const page404ResponseInit = {
|
|
998
|
+
status: 404,
|
|
999
|
+
headers: { "content-type": "text/html;charset=UTF-8" },
|
|
1000
|
+
};`
|
|
1001
|
+
);
|
|
1002
|
+
writer.write(
|
|
1003
|
+
` } else {
|
|
1004
|
+
}
|
|
1005
|
+
if (context.request.headers.get('Accept')?.includes('text/html')) {
|
|
1006
|
+
return new Response(page404.stream(buildInput()), page404ResponseInit);
|
|
1007
|
+
}
|
|
1008
|
+
`
|
|
1009
|
+
);
|
|
1010
|
+
} else {
|
|
1011
|
+
writer.indent = 3;
|
|
1012
|
+
writer.writeBlockEnd("}");
|
|
1013
|
+
}
|
|
1014
|
+
writer.indent--;
|
|
1015
|
+
writer.writeBlockStart(`} catch (error) {`);
|
|
1016
|
+
if (routes.special[RoutableFileTypes.Error]) {
|
|
1017
|
+
imports.writeLines(`
|
|
1018
|
+
const page500ResponseInit = {
|
|
1019
|
+
status: 500,
|
|
1020
|
+
headers: { "content-type": "text/html;charset=UTF-8" },
|
|
1021
|
+
};`);
|
|
1022
|
+
writer.writeBlockStart(
|
|
1023
|
+
`if (context.request.headers.get('Accept')?.includes('text/html')) {`
|
|
1024
|
+
).writeLines(
|
|
1025
|
+
`return new Response(page500.stream(buildInput({ error })), page500ResponseInit);`
|
|
1026
|
+
).writeBlockEnd("}");
|
|
1027
|
+
}
|
|
1028
|
+
writer.writeLines(`throw error;`).writeBlockEnd("}").writeBlockEnd("}").write(`
|
|
1029
|
+
export async function fetch(request, platform) {
|
|
1030
|
+
try {
|
|
1031
|
+
const url = new URL(request.url);
|
|
1032
|
+
let { pathname } = url;`);
|
|
1033
|
+
switch (options.trailingSlashes) {
|
|
1034
|
+
case "RedirectWithout":
|
|
1035
|
+
writer.write(`
|
|
1036
|
+
if (pathname !== '/' && pathname.endsWith('/')) {
|
|
1037
|
+
url.pathname = pathname.slice(0, -1);
|
|
1038
|
+
return Response.redirect(url);
|
|
1039
|
+
}`);
|
|
1040
|
+
break;
|
|
1041
|
+
case "RedirectWith":
|
|
1042
|
+
writer.write(`
|
|
1043
|
+
if (pathname !== '/' && !pathname.endsWith('/')) {
|
|
1044
|
+
url.pathname = pathname + '/';
|
|
1045
|
+
return Response.redirect(url);
|
|
1046
|
+
}`);
|
|
1047
|
+
break;
|
|
1048
|
+
case "RewriteWithout":
|
|
1049
|
+
writer.write(`
|
|
1050
|
+
if (pathname !== '/' && pathname.endsWith('/')) {
|
|
1051
|
+
url.pathname = pathname = pathname.slice(0, -1);
|
|
1052
|
+
}`);
|
|
1053
|
+
break;
|
|
1054
|
+
case "RewriteWith":
|
|
1055
|
+
writer.write(`
|
|
1056
|
+
if (pathname !== '/' && !pathname.endsWith('/')) {
|
|
1057
|
+
url.pathname = pathname = pathname + '/';
|
|
1058
|
+
}`);
|
|
1059
|
+
break;
|
|
1060
|
+
}
|
|
1061
|
+
writer.write(`
|
|
1062
|
+
|
|
1063
|
+
const route = match(request.method, pathname);
|
|
1064
|
+
return await invoke(route, request, platform, url);
|
|
1065
|
+
} catch (error) {
|
|
1066
|
+
const body = import.meta.env.DEV
|
|
1067
|
+
? error.stack || error.message || "Internal Server Error"
|
|
1068
|
+
: null;
|
|
1069
|
+
return new Response(body, {
|
|
1070
|
+
status: 500
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
}`);
|
|
1074
|
+
return writer.end();
|
|
1075
|
+
}
|
|
1076
|
+
function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
|
|
1077
|
+
const { route, dynamic, catchAll } = trie;
|
|
1078
|
+
let closeCount = 0;
|
|
1079
|
+
if (level === 0) {
|
|
1080
|
+
writer.writeLines(`const len = pathname.length;`);
|
|
1081
|
+
if (route) {
|
|
1082
|
+
writer.writeLines(
|
|
1083
|
+
`if (len === 1) return ${renderMatch(verb, route, trie.path)}; // ${trie.path.path}`
|
|
1084
|
+
);
|
|
1085
|
+
} else if (trie.static || dynamic) {
|
|
1086
|
+
writer.writeBlockStart(`if (len > 1) {`);
|
|
1087
|
+
closeCount++;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
if (trie.static || dynamic) {
|
|
1091
|
+
const next = level + 1;
|
|
1092
|
+
const index = `i${next}`;
|
|
1093
|
+
let terminal;
|
|
1094
|
+
let children;
|
|
1095
|
+
writer.writeLines(`const ${index} = pathname.indexOf('/', ${offset}) + 1;`);
|
|
1096
|
+
if (trie.static) {
|
|
1097
|
+
for (const child of trie.static.values()) {
|
|
1098
|
+
if (child.route) {
|
|
1099
|
+
(terminal ?? (terminal = [])).push(child);
|
|
1100
|
+
}
|
|
1101
|
+
if (child.static || child.dynamic || child.catchAll) {
|
|
1102
|
+
(children ?? (children = [])).push(child);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
if (terminal || (dynamic == null ? void 0 : dynamic.route)) {
|
|
1107
|
+
closeCount++;
|
|
1108
|
+
writer.writeBlockStart(`if (!${index} || ${index} === len) {`);
|
|
1109
|
+
let value = `decodeURIComponent(pathname.slice(${offset}, ${index} ? -1 : len))`;
|
|
1110
|
+
if (dynamic == null ? void 0 : dynamic.route) {
|
|
1111
|
+
const segment = `s${next}`;
|
|
1112
|
+
writer.writeLines(`const ${segment} = ${value};`);
|
|
1113
|
+
value = segment;
|
|
1114
|
+
}
|
|
1115
|
+
if (terminal) {
|
|
1116
|
+
const useSwitch = terminal.length > 1;
|
|
1117
|
+
if (useSwitch) {
|
|
1118
|
+
writer.writeBlockStart(`switch (${value}.toLowerCase()) {`);
|
|
1119
|
+
}
|
|
1120
|
+
for (const { key, path: path4, route: route2 } of terminal) {
|
|
1121
|
+
if (useSwitch) {
|
|
1122
|
+
writer.write(`case '${key}': `, true);
|
|
1123
|
+
} else {
|
|
1124
|
+
writer.write(`if (${value}.toLowerCase() === '${key}') `, true);
|
|
1125
|
+
}
|
|
1126
|
+
writer.write(
|
|
1127
|
+
`return ${renderMatch(verb, route2, path4)}; // ${path4.path}
|
|
1128
|
+
`
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
if (useSwitch) {
|
|
1132
|
+
writer.writeBlockEnd("}");
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
if (dynamic == null ? void 0 : dynamic.route) {
|
|
1136
|
+
writer.writeLines(
|
|
1137
|
+
`if (${value}) return ${renderMatch(
|
|
1138
|
+
verb,
|
|
1139
|
+
dynamic.route,
|
|
1140
|
+
dynamic.path
|
|
1141
|
+
)}; // ${dynamic.path.path}`
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
if (children || (dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
|
|
1146
|
+
if (terminal || (dynamic == null ? void 0 : dynamic.route)) {
|
|
1147
|
+
writer.writeBlockEnd("} else {").indent++;
|
|
1148
|
+
} else {
|
|
1149
|
+
writer.writeBlockStart(`if (${index} && ${index} !== len) {`);
|
|
1150
|
+
closeCount++;
|
|
1151
|
+
}
|
|
1152
|
+
let value = `decodeURIComponent(pathname.slice(${offset}, ${index} - 1))`;
|
|
1153
|
+
if ((dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic)) {
|
|
1154
|
+
const segment = `s${next}`;
|
|
1155
|
+
writer.writeLines(`const ${segment} = ${value};`);
|
|
1156
|
+
value = segment;
|
|
1157
|
+
}
|
|
1158
|
+
if (children) {
|
|
1159
|
+
const useSwitch = children.length > 1;
|
|
1160
|
+
if (useSwitch) {
|
|
1161
|
+
writer.writeBlockStart(`switch (${value}.toLowerCase()) {`);
|
|
1162
|
+
}
|
|
1163
|
+
for (const child of children) {
|
|
1164
|
+
const decodedKey = decodeURIComponent(child.key);
|
|
1165
|
+
if (useSwitch) {
|
|
1166
|
+
writer.writeBlockStart(`case '${decodedKey}': {`);
|
|
1167
|
+
} else {
|
|
1168
|
+
writer.writeBlockStart(
|
|
1169
|
+
`if (${value}.toLowerCase() === '${decodedKey}') {`
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
const nextOffset = typeof offset === "string" ? index : offset + child.key.length + 1;
|
|
1173
|
+
writeRouterVerb(writer, child, verb, next, nextOffset);
|
|
1174
|
+
if (useSwitch) {
|
|
1175
|
+
writer.writeBlockEnd("} break;");
|
|
1176
|
+
} else {
|
|
1177
|
+
writer.writeBlockEnd("}");
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
if (useSwitch) {
|
|
1181
|
+
writer.writeBlockEnd("}");
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
if ((dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
|
|
1185
|
+
writer.writeBlockStart(`if (${value}) {`);
|
|
1186
|
+
writeRouterVerb(writer, dynamic, verb, next, index);
|
|
1187
|
+
writer.writeBlockEnd(`}`);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
while (closeCount--) {
|
|
1192
|
+
writer.writeBlockEnd("}");
|
|
1193
|
+
}
|
|
1194
|
+
if (catchAll) {
|
|
1195
|
+
writer.writeLines(
|
|
1196
|
+
`return ${renderMatch(verb, catchAll.route, catchAll.path, String(offset))}; // ${catchAll.path.path}`
|
|
1197
|
+
);
|
|
1198
|
+
} else if (level === 0) {
|
|
1199
|
+
writer.writeLines("return null;");
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
function wrapPropertyName(name) {
|
|
1203
|
+
name = decodeURIComponent(name);
|
|
1204
|
+
return /^[^A-Za-z_$]|[^A-Za-z0-9$_]/.test(name) ? `'${name}'` : name;
|
|
1205
|
+
}
|
|
1206
|
+
function renderParams(params, pathIndex) {
|
|
1207
|
+
let result = "";
|
|
1208
|
+
let catchAll = "";
|
|
1209
|
+
let sep = "{";
|
|
1210
|
+
for (const [name, index] of Object.entries(params)) {
|
|
1211
|
+
if (typeof index === "number") {
|
|
1212
|
+
result += `${sep} ${wrapPropertyName(name)}: s${index + 1}`;
|
|
1213
|
+
sep = ",";
|
|
1214
|
+
} else if (pathIndex) {
|
|
1215
|
+
catchAll = name;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
if (catchAll) {
|
|
1219
|
+
result += `${sep} ${wrapPropertyName(
|
|
1220
|
+
catchAll
|
|
1221
|
+
)}: pathname.slice(${pathIndex})`;
|
|
1222
|
+
}
|
|
1223
|
+
return result ? result + " }" : "{}";
|
|
1224
|
+
}
|
|
1225
|
+
function renderMatch(verb, route, path4, pathIndex) {
|
|
1226
|
+
const handler = `${verb}${route.index}`;
|
|
1227
|
+
const params = path4.params ? renderParams(path4.params, pathIndex) : "{}";
|
|
1228
|
+
const meta = route.meta ? `meta${route.index}` : "{}";
|
|
1229
|
+
const pathPattern = pathToURLPatternString(path4.path);
|
|
1230
|
+
return `{ handler: ${handler}, params: ${params}, meta: ${meta}, path: '${pathPattern}' }`;
|
|
1231
|
+
}
|
|
1232
|
+
function renderMiddleware(middleware) {
|
|
1233
|
+
const writer = createStringWriter();
|
|
1234
|
+
writer.writeLines(
|
|
1235
|
+
`// ${virtualFilePrefix}/${markoRunFilePrefix}middleware.js`
|
|
1236
|
+
);
|
|
1237
|
+
const imports = writer.branch("imports");
|
|
1238
|
+
imports.writeLines(`import { normalize } from 'virtual:marko-run/internal';`);
|
|
1239
|
+
writer.writeLines("");
|
|
1240
|
+
for (const { id, importPath } of middleware) {
|
|
1241
|
+
const importName = `middleware${id}`;
|
|
1242
|
+
imports.writeLines(`import ${importName} from './${importPath}';`);
|
|
1243
|
+
writer.writeLines(`export const mware${id} = normalize(${importName});`);
|
|
1244
|
+
}
|
|
1245
|
+
imports.join();
|
|
1246
|
+
return writer.end();
|
|
1247
|
+
}
|
|
1248
|
+
function stripTsExtension(path4) {
|
|
1249
|
+
const index = path4.lastIndexOf(".");
|
|
1250
|
+
if (index !== -1) {
|
|
1251
|
+
const ext = path4.slice(index + 1);
|
|
1252
|
+
if (ext.toLowerCase() === "ts") {
|
|
1253
|
+
return path4.slice(0, index);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
return path4;
|
|
1257
|
+
}
|
|
1258
|
+
async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
|
|
1259
|
+
var _a, _b;
|
|
1260
|
+
const writer = createStringWriter();
|
|
1261
|
+
writer.writeLines(
|
|
1262
|
+
`/*
|
|
1263
|
+
WARNING: This file is automatically generated and any changes made to it will be overwritten without warning.
|
|
1264
|
+
Do NOT manually edit this file or your changes will be lost.
|
|
1265
|
+
*/
|
|
1266
|
+
`,
|
|
1267
|
+
`import { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform } from "@marko/run/namespace";`,
|
|
1268
|
+
`import type Run from "@marko/run";`
|
|
1269
|
+
);
|
|
1270
|
+
const headWriter = writer.branch("head");
|
|
1271
|
+
writer.writeLines("\n").writeBlockStart(`declare module "@marko/run" {`);
|
|
1272
|
+
if (adapter && adapter.typeInfo) {
|
|
1273
|
+
const platformType = await adapter.typeInfo(
|
|
1274
|
+
(data) => headWriter.write(data)
|
|
1275
|
+
);
|
|
1276
|
+
if (platformType) {
|
|
1277
|
+
writer.writeLines(`interface Platform extends ${platformType} {}
|
|
1278
|
+
`);
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
headWriter.join();
|
|
1282
|
+
writer.writeBlockStart(`interface AppData extends Run.DefineApp<{`).writeBlockStart("routes: {");
|
|
1283
|
+
const routesWriter = writer.branch("routes");
|
|
1284
|
+
writer.writeBlockEnd("}").writeBlockEnd(`}> {}`).writeBlockEnd(`}`);
|
|
1285
|
+
const routeTypes = /* @__PURE__ */ new Map();
|
|
1286
|
+
for (const route of routes.list) {
|
|
1287
|
+
let routeType = "";
|
|
1288
|
+
for (const path4 of route.paths) {
|
|
1289
|
+
const pathType = `"${pathToURLPatternString(path4.path)}"`;
|
|
1290
|
+
routeType += routeType ? " | " + pathType : pathType;
|
|
1291
|
+
routesWriter.writeLines(`${pathType}: Routes["${route.key}"];`);
|
|
1292
|
+
}
|
|
1293
|
+
for (const file of [route.handler, route.page]) {
|
|
1294
|
+
if (file) {
|
|
1295
|
+
const existing = routeTypes.get(file);
|
|
1296
|
+
if (!existing) {
|
|
1297
|
+
routeTypes.set(file, [routeType]);
|
|
1298
|
+
} else {
|
|
1299
|
+
existing.push(routeType);
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
for (const files of [route.middleware, route.layouts]) {
|
|
1304
|
+
if (files) {
|
|
1305
|
+
for (const file of files) {
|
|
1306
|
+
const existing = routeTypes.get(file);
|
|
1307
|
+
if (!existing) {
|
|
1308
|
+
routeTypes.set(file, [routeType]);
|
|
1309
|
+
} else {
|
|
1310
|
+
existing.push(routeType);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
for (const special of Object.values(routes.special)) {
|
|
1317
|
+
routeTypes.set(special.page, []);
|
|
1318
|
+
}
|
|
1319
|
+
routesWriter.join();
|
|
1320
|
+
const handlerWriter = writer.branch("handler");
|
|
1321
|
+
const middlewareWriter = writer.branch("middleware");
|
|
1322
|
+
const pageWriter = writer.branch("page");
|
|
1323
|
+
const layoutWriter = writer.branch("layout");
|
|
1324
|
+
for (const [file, types] of routeTypes) {
|
|
1325
|
+
const path4 = `${pathPrefix}/${file.relativePath}`;
|
|
1326
|
+
const routeType = `Run.Routes[${types.join(" | ")}]`;
|
|
1327
|
+
switch (file.type) {
|
|
1328
|
+
case RoutableFileTypes.Handler:
|
|
1329
|
+
writeModuleDeclaration(handlerWriter, path4, routeType);
|
|
1330
|
+
break;
|
|
1331
|
+
case RoutableFileTypes.Middleware:
|
|
1332
|
+
writeModuleDeclaration(middlewareWriter, path4, routeType);
|
|
1333
|
+
break;
|
|
1334
|
+
case RoutableFileTypes.Page:
|
|
1335
|
+
writeModuleDeclaration(pageWriter, path4, routeType);
|
|
1336
|
+
break;
|
|
1337
|
+
case RoutableFileTypes.Layout:
|
|
1338
|
+
writeModuleDeclaration(
|
|
1339
|
+
layoutWriter,
|
|
1340
|
+
path4,
|
|
1341
|
+
routeType,
|
|
1342
|
+
`
|
|
1343
|
+
export interface Input {
|
|
1344
|
+
renderBody: Marko.Body;
|
|
1345
|
+
}`
|
|
1346
|
+
);
|
|
1347
|
+
break;
|
|
1348
|
+
case RoutableFileTypes.Error:
|
|
1349
|
+
writeModuleDeclaration(
|
|
1350
|
+
writer,
|
|
1351
|
+
path4,
|
|
1352
|
+
"globalThis.MarkoRun.Route",
|
|
1353
|
+
`
|
|
1354
|
+
export interface Input {
|
|
1355
|
+
error: unknown;
|
|
1356
|
+
}`
|
|
1357
|
+
);
|
|
1358
|
+
break;
|
|
1359
|
+
case RoutableFileTypes.NotFound:
|
|
1360
|
+
writeModuleDeclaration(writer, path4, "Run.Route");
|
|
1361
|
+
break;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
handlerWriter.join();
|
|
1365
|
+
middlewareWriter.join();
|
|
1366
|
+
pageWriter.join();
|
|
1367
|
+
layoutWriter.join();
|
|
1368
|
+
writer.writeBlockStart(`
|
|
1369
|
+
type Routes = {`);
|
|
1370
|
+
for (const route of routes.list) {
|
|
1371
|
+
const { meta, handler, page } = route;
|
|
1372
|
+
if (page || handler) {
|
|
1373
|
+
const verbs = [];
|
|
1374
|
+
if (page || ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes("get"))) {
|
|
1375
|
+
verbs.push(`"get"`);
|
|
1376
|
+
}
|
|
1377
|
+
if ((_b = handler == null ? void 0 : handler.verbs) == null ? void 0 : _b.includes("post")) {
|
|
1378
|
+
verbs.push(`"post"`);
|
|
1379
|
+
}
|
|
1380
|
+
let routeType = `{ verb: ${verbs.join(" | ")};`;
|
|
1381
|
+
if (meta) {
|
|
1382
|
+
const metaPath = stripTsExtension(`${pathPrefix}/${meta.relativePath}`);
|
|
1383
|
+
let metaType = `typeof import("${metaPath}")`;
|
|
1384
|
+
if (/\.(ts|js|mjs)$/.test(meta.relativePath)) {
|
|
1385
|
+
metaType += `["default"]`;
|
|
1386
|
+
}
|
|
1387
|
+
routeType += ` meta: ${metaType};`;
|
|
1388
|
+
}
|
|
1389
|
+
writer.writeLines(`"${route.key}": ${routeType} };`);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
writer.writeBlockEnd("}");
|
|
1393
|
+
return writer.end();
|
|
1394
|
+
}
|
|
1395
|
+
function writeModuleDeclaration(writer, path4, routeType, moduleTypes) {
|
|
1396
|
+
writer.writeLines("").write(`declare module "${stripTsExtension(path4)}" {`);
|
|
1397
|
+
if (moduleTypes) {
|
|
1398
|
+
writer.write(moduleTypes);
|
|
1399
|
+
}
|
|
1400
|
+
if (routeType) {
|
|
1401
|
+
const isMarko = path4.endsWith(".marko");
|
|
1402
|
+
writer.write(`
|
|
1403
|
+
namespace MarkoRun {
|
|
1404
|
+
export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
|
|
1405
|
+
export type Route = ${routeType};
|
|
1406
|
+
export type Context = Run.MultiRouteContext<Route>${isMarko ? " & Marko.Global" : ""};
|
|
1407
|
+
export type Handler = Run.HandlerLike<Route>;
|
|
1408
|
+
export const route: Run.HandlerTypeFn<Route>;
|
|
1409
|
+
}`);
|
|
1410
|
+
}
|
|
1411
|
+
writer.writeLines(`
|
|
1412
|
+
}`);
|
|
1413
|
+
}
|
|
1414
|
+
function pathToURLPatternString(path4) {
|
|
1415
|
+
return path4.replace(/\/\$(\$?)([^\/]*)/g, (_, catchAll, name) => {
|
|
1416
|
+
name = decodeURIComponent(name);
|
|
1417
|
+
return catchAll ? `/:${name || "rest"}*` : `/:${name}`;
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
function createRouteTrie(routes) {
|
|
1421
|
+
const root = {
|
|
1422
|
+
key: ""
|
|
1423
|
+
};
|
|
1424
|
+
function insert(path4, route) {
|
|
1425
|
+
let node = root;
|
|
1426
|
+
for (const segment of path4.segments) {
|
|
1427
|
+
if (segment === "$$") {
|
|
1428
|
+
node.catchAll ?? (node.catchAll = { route, path: path4 });
|
|
1429
|
+
return;
|
|
1430
|
+
} else if (segment === "$") {
|
|
1431
|
+
node = node.dynamic ?? (node.dynamic = {
|
|
1432
|
+
key: ""
|
|
1433
|
+
});
|
|
1434
|
+
} else {
|
|
1435
|
+
node.static ?? (node.static = /* @__PURE__ */ new Map());
|
|
1436
|
+
let next = node.static.get(segment);
|
|
1437
|
+
if (!next) {
|
|
1438
|
+
next = {
|
|
1439
|
+
key: segment
|
|
1440
|
+
};
|
|
1441
|
+
node.static.set(segment, next);
|
|
1442
|
+
}
|
|
1443
|
+
node = next;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
node.path ?? (node.path = path4);
|
|
1447
|
+
node.route ?? (node.route = route);
|
|
1448
|
+
}
|
|
1449
|
+
for (const route of routes) {
|
|
1450
|
+
for (const path4 of route.paths) {
|
|
1451
|
+
insert(path4, route);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
return root;
|
|
1455
|
+
}
|
|
229
1456
|
|
|
230
1457
|
// src/vite/utils/ast.ts
|
|
231
1458
|
import * as t from "@babel/types";
|
|
1459
|
+
function getExportIdentifiers(astProgramNode) {
|
|
1460
|
+
const result = [];
|
|
1461
|
+
if (t.isProgram(astProgramNode)) {
|
|
1462
|
+
for (const node of astProgramNode.body) {
|
|
1463
|
+
if (t.isExportNamedDeclaration(node)) {
|
|
1464
|
+
const { declaration, specifiers } = node;
|
|
1465
|
+
if (declaration) {
|
|
1466
|
+
if (t.isFunctionDeclaration(declaration) && declaration.id) {
|
|
1467
|
+
result.push(declaration.id.name);
|
|
1468
|
+
} else if (t.isVariableDeclaration(declaration)) {
|
|
1469
|
+
for (const declarator of declaration.declarations) {
|
|
1470
|
+
if (t.isIdentifier(declarator.id)) {
|
|
1471
|
+
result.push(declarator.id.name);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
} else if (specifiers) {
|
|
1476
|
+
for (const specifier of specifiers) {
|
|
1477
|
+
if (t.isExportSpecifier(specifier) && t.isIdentifier(specifier.exported)) {
|
|
1478
|
+
result.push(specifier.exported.name);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
} else if (t.isExportDefaultDeclaration(node)) {
|
|
1483
|
+
const { declaration } = node;
|
|
1484
|
+
if (t.isObjectExpression(declaration)) {
|
|
1485
|
+
for (const property of declaration.properties) {
|
|
1486
|
+
if (t.isObjectMember(property) && t.isIdentifier(property.key)) {
|
|
1487
|
+
result.push(property.key.name);
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
return result;
|
|
1495
|
+
}
|
|
232
1496
|
|
|
233
1497
|
// src/vite/utils/log.ts
|
|
234
1498
|
import zlib from "node:zlib";
|
|
@@ -243,26 +1507,585 @@ var HttpVerbColors = {
|
|
|
243
1507
|
delete: kleur.red,
|
|
244
1508
|
other: kleur.white
|
|
245
1509
|
};
|
|
1510
|
+
var HttpVerbOrder = {
|
|
1511
|
+
get: 0,
|
|
1512
|
+
post: 1,
|
|
1513
|
+
put: 2,
|
|
1514
|
+
delete: 3
|
|
1515
|
+
};
|
|
1516
|
+
function logRoutesTable(routes, bundle, options) {
|
|
1517
|
+
function getRouteChunkName(route) {
|
|
1518
|
+
return options.sanitizeFileName(`${route.entryName}.marko`);
|
|
1519
|
+
}
|
|
1520
|
+
const hasMiddleware = routes.list.some((route) => route.middleware.length);
|
|
1521
|
+
const hasMeta = routes.list.some((route) => route.meta);
|
|
1522
|
+
const headings = ["Method", "Path", "Entry"];
|
|
1523
|
+
const colAligns = ["left", "left", "left"];
|
|
1524
|
+
if (hasMiddleware) {
|
|
1525
|
+
headings.push("MW");
|
|
1526
|
+
colAligns.push("right");
|
|
1527
|
+
}
|
|
1528
|
+
if (hasMeta) {
|
|
1529
|
+
headings.push("Meta");
|
|
1530
|
+
colAligns.push("center");
|
|
1531
|
+
}
|
|
1532
|
+
headings.push("Size/GZip");
|
|
1533
|
+
colAligns.push("right");
|
|
1534
|
+
const table = new Table({
|
|
1535
|
+
head: headings.map((title) => kleur.bold(kleur.white(title.toUpperCase()))),
|
|
1536
|
+
wordWrap: true,
|
|
1537
|
+
colAligns,
|
|
1538
|
+
style: { compact: true }
|
|
1539
|
+
});
|
|
1540
|
+
for (const route of routes.list) {
|
|
1541
|
+
for (const path4 of route.paths) {
|
|
1542
|
+
const verbs = getVerbs(route).sort(
|
|
1543
|
+
(a, b) => HttpVerbOrder[a] - HttpVerbOrder[b]
|
|
1544
|
+
);
|
|
1545
|
+
let firstRow = true;
|
|
1546
|
+
for (const verb of verbs) {
|
|
1547
|
+
let size = "";
|
|
1548
|
+
const entryType = [];
|
|
1549
|
+
if (route.handler) {
|
|
1550
|
+
entryType.push(kleur.blue("handler"));
|
|
1551
|
+
}
|
|
1552
|
+
if (verb === "get" && route.page) {
|
|
1553
|
+
entryType.push(kleur.yellow("page"));
|
|
1554
|
+
size = prettySize(computeRouteSize(getRouteChunkName(route), bundle));
|
|
1555
|
+
}
|
|
1556
|
+
const row = [kleur.bold(HttpVerbColors[verb](verb.toUpperCase()))];
|
|
1557
|
+
if (verbs.length === 1 || firstRow) {
|
|
1558
|
+
row.push({ rowSpan: verbs.length, content: prettyPath(path4.path) });
|
|
1559
|
+
firstRow = false;
|
|
1560
|
+
}
|
|
1561
|
+
row.push(entryType.join(" -> "));
|
|
1562
|
+
hasMiddleware && row.push(route.middleware.length || "");
|
|
1563
|
+
hasMeta && row.push(route.meta ? "\u2713" : "");
|
|
1564
|
+
row.push(size || "");
|
|
1565
|
+
table.push(row);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
for (const [key, route] of Object.entries(routes.special).sort()) {
|
|
1570
|
+
const row = [kleur.bold(kleur.white("*")), key, kleur.yellow("page")];
|
|
1571
|
+
hasMiddleware && row.push("");
|
|
1572
|
+
hasMeta && row.push("");
|
|
1573
|
+
row.push(prettySize(computeRouteSize(getRouteChunkName(route), bundle)));
|
|
1574
|
+
table.push(row);
|
|
1575
|
+
}
|
|
1576
|
+
console.log(table.toString());
|
|
1577
|
+
}
|
|
1578
|
+
function computeRouteSize(entryName, bundle) {
|
|
1579
|
+
for (const chunk of Object.values(bundle)) {
|
|
1580
|
+
if (chunk.type === "chunk" && chunk.isEntry && chunk.name === entryName) {
|
|
1581
|
+
return computeChunkSize(chunk, bundle);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
return [0, 0];
|
|
1585
|
+
}
|
|
1586
|
+
function gzipSize(source) {
|
|
1587
|
+
return zlib.gzipSync(source, { level: 9 }).length;
|
|
1588
|
+
}
|
|
1589
|
+
function byteSize(source) {
|
|
1590
|
+
return new Blob([source]).size;
|
|
1591
|
+
}
|
|
1592
|
+
function computeChunkSize(chunk, bundle, seen = /* @__PURE__ */ new Set()) {
|
|
1593
|
+
if (chunk.type === "asset") {
|
|
1594
|
+
return [
|
|
1595
|
+
byteSize(chunk.source),
|
|
1596
|
+
gzipSize(chunk.source)
|
|
1597
|
+
];
|
|
1598
|
+
}
|
|
1599
|
+
const size = [byteSize(chunk.code), gzipSize(chunk.code)];
|
|
1600
|
+
for (const id of chunk.imports) {
|
|
1601
|
+
if (!seen.has(id)) {
|
|
1602
|
+
const [bytes, compBytes] = computeChunkSize(bundle[id], bundle, seen);
|
|
1603
|
+
size[0] += bytes;
|
|
1604
|
+
size[1] += compBytes;
|
|
1605
|
+
seen.add(id);
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
return size;
|
|
1609
|
+
}
|
|
1610
|
+
function prettySize([bytes, compBytes]) {
|
|
1611
|
+
if (bytes <= 0) {
|
|
1612
|
+
return kleur.gray("0.0 kB");
|
|
1613
|
+
}
|
|
1614
|
+
const [size, prefix] = format(bytes, { decimals: 1 }).split(/\s+/);
|
|
1615
|
+
const compSize = format(compBytes, { decimals: 1, prefix, unit: "B" });
|
|
1616
|
+
let str = kleur.white(size) + kleur.gray("/");
|
|
1617
|
+
if (compBytes < 20 * 1e3)
|
|
1618
|
+
str += kleur.green(compSize);
|
|
1619
|
+
else if (compBytes < 50 * 1e3)
|
|
1620
|
+
str += kleur.yellow(compSize);
|
|
1621
|
+
else
|
|
1622
|
+
str += kleur.bold(kleur.red(compSize));
|
|
1623
|
+
return str;
|
|
1624
|
+
}
|
|
1625
|
+
function prettyPath(path4) {
|
|
1626
|
+
return path4.replace(/\/\$\$(.*)$/, (_, p) => "/" + kleur.bold(kleur.dim(`*${p}`))).replace(/\/\$([^/]+)/g, (_, p) => "/" + kleur.bold(kleur.dim(`:${p}`)));
|
|
1627
|
+
}
|
|
246
1628
|
|
|
247
1629
|
// src/vite/plugin.ts
|
|
248
1630
|
import { fileURLToPath } from "url";
|
|
249
1631
|
var __dirname = path2.dirname(fileURLToPath(import.meta.url));
|
|
1632
|
+
var PLUGIN_NAME_PREFIX = "marko-run-vite";
|
|
250
1633
|
var POSIX_SEP = "/";
|
|
251
1634
|
var WINDOWS_SEP = "\\";
|
|
252
1635
|
var normalizePath = path2.sep === WINDOWS_SEP ? (id) => id.replace(/\\/g, POSIX_SEP) : (id) => id;
|
|
1636
|
+
function markoRun(opts = {}) {
|
|
1637
|
+
let { routesDir, adapter, ...markoVitePluginOptions } = opts;
|
|
1638
|
+
let compiler;
|
|
1639
|
+
let store;
|
|
1640
|
+
let root;
|
|
1641
|
+
let resolvedRoutesDir;
|
|
1642
|
+
let typesDir;
|
|
1643
|
+
let isBuild = false;
|
|
1644
|
+
let isSSRBuild = false;
|
|
1645
|
+
let tsConfigExists;
|
|
1646
|
+
let ssrEntryFiles;
|
|
1647
|
+
let devEntryFile;
|
|
1648
|
+
let devEntryFilePosix;
|
|
1649
|
+
let devServer;
|
|
1650
|
+
let routes;
|
|
1651
|
+
let routeData;
|
|
1652
|
+
let routeDataFilename = "routes.json";
|
|
1653
|
+
let extractVerbs;
|
|
1654
|
+
let resolvedConfig;
|
|
1655
|
+
let typesFile;
|
|
1656
|
+
let isStale = true;
|
|
1657
|
+
let isRendered = false;
|
|
1658
|
+
const virtualFiles = /* @__PURE__ */ new Map();
|
|
1659
|
+
let times = {
|
|
1660
|
+
routesBuild: 0,
|
|
1661
|
+
routesRender: 0
|
|
1662
|
+
};
|
|
1663
|
+
async function writeTypesFile() {
|
|
1664
|
+
if (tsConfigExists ?? (tsConfigExists = await globFileExists(
|
|
1665
|
+
root,
|
|
1666
|
+
"{.tsconfig*,tsconfig*.json}"
|
|
1667
|
+
))) {
|
|
1668
|
+
const filepath = path2.join(typesDir, "routes.d.ts");
|
|
1669
|
+
const data = await renderRouteTypeInfo(
|
|
1670
|
+
routes,
|
|
1671
|
+
normalizePath(path2.relative(typesDir, resolvedRoutesDir)),
|
|
1672
|
+
adapter
|
|
1673
|
+
);
|
|
1674
|
+
if (data !== typesFile || !fs3.existsSync(filepath)) {
|
|
1675
|
+
await ensureDir(typesDir);
|
|
1676
|
+
await fs3.promises.writeFile(filepath, typesFile = data);
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
async function setVirtualFiles(render = false) {
|
|
1681
|
+
for (const route of routes.list) {
|
|
1682
|
+
if (render && route.handler) {
|
|
1683
|
+
route.handler.verbs = await extractVerbs(route.handler.filePath);
|
|
1684
|
+
if (!route.handler.verbs.length) {
|
|
1685
|
+
console.warn(
|
|
1686
|
+
`Did not find any valid exports in middleware entry file:'${route.handler.filePath}' - expected to find any of 'GET', 'POST', 'PUT' or 'DELETE'`
|
|
1687
|
+
);
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
if (route.page) {
|
|
1691
|
+
virtualFiles.set(
|
|
1692
|
+
path2.posix.join(root, `${route.entryName}.marko`),
|
|
1693
|
+
render ? renderRouteTemplate(route) : ""
|
|
1694
|
+
);
|
|
1695
|
+
}
|
|
1696
|
+
virtualFiles.set(
|
|
1697
|
+
path2.posix.join(root, `${route.entryName}.js`),
|
|
1698
|
+
render ? renderRouteEntry(route) : ""
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1701
|
+
for (const route of Object.values(routes.special)) {
|
|
1702
|
+
virtualFiles.set(
|
|
1703
|
+
path2.posix.join(root, `${route.entryName}.marko`),
|
|
1704
|
+
render ? renderRouteTemplate(route) : ""
|
|
1705
|
+
);
|
|
1706
|
+
}
|
|
1707
|
+
virtualFiles.set(
|
|
1708
|
+
"@marko/run/router",
|
|
1709
|
+
render ? renderRouter(routes, {
|
|
1710
|
+
trailingSlashes: opts.trailingSlashes || "RedirectWithout"
|
|
1711
|
+
}) : ""
|
|
1712
|
+
);
|
|
1713
|
+
virtualFiles.set(
|
|
1714
|
+
path2.posix.join(root, `${markoRunFilePrefix}middleware.js`),
|
|
1715
|
+
render ? renderMiddleware(routes.middleware) : ""
|
|
1716
|
+
);
|
|
1717
|
+
}
|
|
1718
|
+
const buildVirtualFiles = single(async () => {
|
|
1719
|
+
const startTime = performance.now();
|
|
1720
|
+
routes = await buildRoutes(createFSWalker(resolvedRoutesDir), routesDir);
|
|
1721
|
+
times.routesBuild = performance.now() - startTime;
|
|
1722
|
+
await setVirtualFiles(false);
|
|
1723
|
+
isStale = false;
|
|
1724
|
+
isRendered = false;
|
|
1725
|
+
});
|
|
1726
|
+
const renderVirtualFiles = single(async () => {
|
|
1727
|
+
const startTime = performance.now();
|
|
1728
|
+
await setVirtualFiles(true);
|
|
1729
|
+
await writeTypesFile();
|
|
1730
|
+
times.routesRender = performance.now() - startTime;
|
|
1731
|
+
isRendered = true;
|
|
1732
|
+
});
|
|
1733
|
+
return [
|
|
1734
|
+
{
|
|
1735
|
+
name: `${PLUGIN_NAME_PREFIX}:pre`,
|
|
1736
|
+
enforce: "pre",
|
|
1737
|
+
async config(config2, env) {
|
|
1738
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1739
|
+
const externalPluginOptions = getExternalPluginOptions(config2);
|
|
1740
|
+
if (externalPluginOptions) {
|
|
1741
|
+
opts = mergeConfig(opts, externalPluginOptions);
|
|
1742
|
+
}
|
|
1743
|
+
root = normalizePath(config2.root || process.cwd());
|
|
1744
|
+
isBuild = env.command === "build";
|
|
1745
|
+
isSSRBuild = isBuild && Boolean((_a = config2.build) == null ? void 0 : _a.ssr);
|
|
1746
|
+
adapter = await resolveAdapter(
|
|
1747
|
+
root,
|
|
1748
|
+
opts,
|
|
1749
|
+
config2.logLevel !== "silent" && !isBuild || isSSRBuild
|
|
1750
|
+
);
|
|
1751
|
+
if (adapter) {
|
|
1752
|
+
(_b = adapter.configure) == null ? void 0 : _b.call(adapter, {
|
|
1753
|
+
...getExternalAdapterOptions(config2),
|
|
1754
|
+
root,
|
|
1755
|
+
isBuild
|
|
1756
|
+
});
|
|
1757
|
+
const adapterOptions = await ((_c = adapter.pluginOptions) == null ? void 0 : _c.call(adapter, opts));
|
|
1758
|
+
if (adapterOptions) {
|
|
1759
|
+
opts = mergeConfig(opts, adapterOptions);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
compiler ?? (compiler = await import(opts.compiler || "@marko/compiler"));
|
|
1763
|
+
compiler.taglib.register("@marko/run", {
|
|
1764
|
+
"<*>": {
|
|
1765
|
+
transform: path2.resolve(
|
|
1766
|
+
__dirname,
|
|
1767
|
+
"../components/src-attributes-transformer.cjs"
|
|
1768
|
+
)
|
|
1769
|
+
}
|
|
1770
|
+
});
|
|
1771
|
+
routesDir = opts.routesDir || "src/routes";
|
|
1772
|
+
markoVitePluginOptions.store = store = opts.store || new FileStore(
|
|
1773
|
+
`marko-serve-vite-${crypto.createHash("SHA1").update(root).digest("hex")}`
|
|
1774
|
+
);
|
|
1775
|
+
markoVitePluginOptions.runtimeId = opts.runtimeId;
|
|
1776
|
+
markoVitePluginOptions.basePathVar = opts.basePathVar;
|
|
1777
|
+
resolvedRoutesDir = path2.resolve(root, routesDir);
|
|
1778
|
+
typesDir = path2.join(root, ".marko-run");
|
|
1779
|
+
devEntryFile = path2.join(root, "index.html");
|
|
1780
|
+
devEntryFilePosix = normalizePath(devEntryFile);
|
|
1781
|
+
const assetsDir = ((_d = config2.build) == null ? void 0 : _d.assetsDir) || "assets";
|
|
1782
|
+
let rollupOutputOptions = (_f = (_e = config2.build) == null ? void 0 : _e.rollupOptions) == null ? void 0 : _f.output;
|
|
1783
|
+
if (isBuild) {
|
|
1784
|
+
const defaultRollupOutputOptions = {
|
|
1785
|
+
assetFileNames({ name }) {
|
|
1786
|
+
if (name && name.indexOf("_marko-virtual_id_") < 0) {
|
|
1787
|
+
return `${assetsDir}/${getEntryFileName(name) || "[name]"}-[hash].[ext]`;
|
|
1788
|
+
}
|
|
1789
|
+
return `${assetsDir}/_[hash].[ext]`;
|
|
1790
|
+
},
|
|
1791
|
+
entryFileNames(info) {
|
|
1792
|
+
let name = getEntryFileName(info.facadeModuleId);
|
|
1793
|
+
if (!name) {
|
|
1794
|
+
for (let id of info.moduleIds) {
|
|
1795
|
+
name = getEntryFileName(id);
|
|
1796
|
+
if (name) {
|
|
1797
|
+
break;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
return `${assetsDir}/${name || "[name]"}-[hash].js`;
|
|
1802
|
+
},
|
|
1803
|
+
chunkFileNames: isSSRBuild ? `_[hash].js` : `${assetsDir}/_[hash].js`
|
|
1804
|
+
};
|
|
1805
|
+
if (!rollupOutputOptions) {
|
|
1806
|
+
rollupOutputOptions = defaultRollupOutputOptions;
|
|
1807
|
+
} else if (!Array.isArray(rollupOutputOptions)) {
|
|
1808
|
+
rollupOutputOptions = {
|
|
1809
|
+
...defaultRollupOutputOptions,
|
|
1810
|
+
...rollupOutputOptions
|
|
1811
|
+
};
|
|
1812
|
+
} else {
|
|
1813
|
+
rollupOutputOptions = rollupOutputOptions.map((options) => ({
|
|
1814
|
+
...defaultRollupOutputOptions,
|
|
1815
|
+
...options
|
|
1816
|
+
}));
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
let pluginConfig = {
|
|
1820
|
+
logLevel: isBuild ? "warn" : void 0,
|
|
1821
|
+
define: isBuild ? {
|
|
1822
|
+
"process.env.NODE_ENV": "'production'"
|
|
1823
|
+
} : void 0,
|
|
1824
|
+
ssr: {
|
|
1825
|
+
noExternal: /@marko\/run/
|
|
1826
|
+
},
|
|
1827
|
+
build: {
|
|
1828
|
+
emptyOutDir: isSSRBuild,
|
|
1829
|
+
rollupOptions: {
|
|
1830
|
+
output: rollupOutputOptions
|
|
1831
|
+
}
|
|
1832
|
+
},
|
|
1833
|
+
resolve: isBuild ? {
|
|
1834
|
+
browserField: isSSRBuild ? false : void 0,
|
|
1835
|
+
conditions: [
|
|
1836
|
+
isSSRBuild ? "node" : "browser",
|
|
1837
|
+
"import",
|
|
1838
|
+
"require",
|
|
1839
|
+
"production",
|
|
1840
|
+
"default"
|
|
1841
|
+
]
|
|
1842
|
+
} : void 0
|
|
1843
|
+
};
|
|
1844
|
+
if (adapter == null ? void 0 : adapter.viteConfig) {
|
|
1845
|
+
const adapterConfig = await adapter.viteConfig(config2);
|
|
1846
|
+
if (adapterConfig) {
|
|
1847
|
+
pluginConfig = mergeConfig(pluginConfig, adapterConfig);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
return setExternalPluginOptions(pluginConfig, opts);
|
|
1851
|
+
},
|
|
1852
|
+
configResolved(config2) {
|
|
1853
|
+
resolvedConfig = config2;
|
|
1854
|
+
const {
|
|
1855
|
+
ssr,
|
|
1856
|
+
rollupOptions: { input }
|
|
1857
|
+
} = config2.build;
|
|
1858
|
+
if (typeof ssr === "string") {
|
|
1859
|
+
ssrEntryFiles = [ssr];
|
|
1860
|
+
} else if (typeof input === "string") {
|
|
1861
|
+
ssrEntryFiles = [input];
|
|
1862
|
+
} else if (Array.isArray(input)) {
|
|
1863
|
+
ssrEntryFiles = input;
|
|
1864
|
+
} else if (input) {
|
|
1865
|
+
ssrEntryFiles = Object.values(input);
|
|
1866
|
+
} else {
|
|
1867
|
+
ssrEntryFiles = [];
|
|
1868
|
+
}
|
|
1869
|
+
},
|
|
1870
|
+
configureServer(_server) {
|
|
1871
|
+
devServer = _server;
|
|
1872
|
+
devServer.watcher.on("all", async (type, filename) => {
|
|
1873
|
+
const routableFileType = matchRoutableFile(path2.parse(filename).base);
|
|
1874
|
+
if (filename.startsWith(resolvedRoutesDir) && routableFileType) {
|
|
1875
|
+
if (type === "add") {
|
|
1876
|
+
isStale = true;
|
|
1877
|
+
} else if (type === "unlink") {
|
|
1878
|
+
isStale = true;
|
|
1879
|
+
} else if (type === "change") {
|
|
1880
|
+
if (routableFileType === RoutableFileTypes.Handler) {
|
|
1881
|
+
isStale = true;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
if (isStale) {
|
|
1885
|
+
for (const id of virtualFiles.keys()) {
|
|
1886
|
+
devServer.watcher.emit("change", id);
|
|
1887
|
+
break;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
});
|
|
1892
|
+
},
|
|
1893
|
+
async buildStart(_options) {
|
|
1894
|
+
if (isBuild && !isSSRBuild) {
|
|
1895
|
+
try {
|
|
1896
|
+
routeData = JSON.parse(
|
|
1897
|
+
await store.get(routeDataFilename)
|
|
1898
|
+
);
|
|
1899
|
+
} catch {
|
|
1900
|
+
this.error(
|
|
1901
|
+
`You must run the "ssr" build before the "browser" build.`
|
|
1902
|
+
);
|
|
1903
|
+
}
|
|
1904
|
+
routes = routeData.routes;
|
|
1905
|
+
times = routeData.times;
|
|
1906
|
+
for (const { key, code } of routeData.files) {
|
|
1907
|
+
virtualFiles.set(key, code);
|
|
1908
|
+
}
|
|
1909
|
+
isStale = false;
|
|
1910
|
+
isRendered = true;
|
|
1911
|
+
} else {
|
|
1912
|
+
extractVerbs = isBuild ? getVerbsFromFileBuild.bind(null, this) : getVerbsFromFileDev.bind(null, devServer);
|
|
1913
|
+
}
|
|
1914
|
+
},
|
|
1915
|
+
async resolveId(importee, importer) {
|
|
1916
|
+
let resolved;
|
|
1917
|
+
if (importee.startsWith(virtualRuntimePrefix)) {
|
|
1918
|
+
return this.resolve(
|
|
1919
|
+
path2.resolve(__dirname, "../runtime/internal"),
|
|
1920
|
+
importer,
|
|
1921
|
+
{ skipSelf: true }
|
|
1922
|
+
);
|
|
1923
|
+
} else if (importee.startsWith(virtualFilePrefix)) {
|
|
1924
|
+
importee = path2.resolve(
|
|
1925
|
+
root,
|
|
1926
|
+
importee.slice(virtualFilePrefix.length + 1)
|
|
1927
|
+
);
|
|
1928
|
+
} else if (!isBuild && importer && (importer === devEntryFile || normalizePath(importer) === devEntryFilePosix) && importee.startsWith(`/${markoRunFilePrefix}`)) {
|
|
1929
|
+
importee = path2.resolve(root, "." + importee);
|
|
1930
|
+
}
|
|
1931
|
+
importee = normalizePath(importee);
|
|
1932
|
+
if (isStale) {
|
|
1933
|
+
await buildVirtualFiles();
|
|
1934
|
+
}
|
|
1935
|
+
if (virtualFiles.has(importee)) {
|
|
1936
|
+
resolved = importee;
|
|
1937
|
+
}
|
|
1938
|
+
return resolved || null;
|
|
1939
|
+
},
|
|
1940
|
+
async load(id) {
|
|
1941
|
+
var _a;
|
|
1942
|
+
if (id.endsWith(serverEntryQuery)) {
|
|
1943
|
+
id = id.slice(0, -serverEntryQuery.length);
|
|
1944
|
+
}
|
|
1945
|
+
if (virtualFiles.has(id)) {
|
|
1946
|
+
if (!isRendered) {
|
|
1947
|
+
await renderVirtualFiles();
|
|
1948
|
+
if (!isBuild) {
|
|
1949
|
+
await ((_a = opts == null ? void 0 : opts.emitRoutes) == null ? void 0 : _a.call(opts, routes.list));
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
return virtualFiles.get(id);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
},
|
|
1956
|
+
...markoVitePlugin(markoVitePluginOptions),
|
|
1957
|
+
{
|
|
1958
|
+
name: `${PLUGIN_NAME_PREFIX}:post`,
|
|
1959
|
+
enforce: "post",
|
|
1960
|
+
generateBundle(options, bundle) {
|
|
1961
|
+
if (options.sourcemap && options.sourcemap !== "inline") {
|
|
1962
|
+
for (const key of Object.keys(bundle)) {
|
|
1963
|
+
if (key.endsWith(".map") && !bundle[key.slice(0, -4)]) {
|
|
1964
|
+
delete bundle[key];
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
},
|
|
1969
|
+
async writeBundle(options, bundle) {
|
|
1970
|
+
var _a;
|
|
1971
|
+
if (isSSRBuild) {
|
|
1972
|
+
const builtEntries = Object.values(bundle).reduce(
|
|
1973
|
+
(acc, item) => {
|
|
1974
|
+
if (item.type === "chunk" && item.isEntry) {
|
|
1975
|
+
acc.push(path2.join(options.dir, item.fileName));
|
|
1976
|
+
}
|
|
1977
|
+
return acc;
|
|
1978
|
+
},
|
|
1979
|
+
[]
|
|
1980
|
+
);
|
|
1981
|
+
routeData = {
|
|
1982
|
+
routes,
|
|
1983
|
+
files: [],
|
|
1984
|
+
times,
|
|
1985
|
+
builtEntries,
|
|
1986
|
+
sourceEntries: ssrEntryFiles
|
|
1987
|
+
};
|
|
1988
|
+
for (const [key, code] of virtualFiles) {
|
|
1989
|
+
routeData.files.push({ key, code });
|
|
1990
|
+
}
|
|
1991
|
+
await store.set(routeDataFilename, JSON.stringify(routeData));
|
|
1992
|
+
await ((_a = opts == null ? void 0 : opts.emitRoutes) == null ? void 0 : _a.call(opts, routes.list));
|
|
1993
|
+
} else if (isBuild) {
|
|
1994
|
+
logRoutesTable(routes, bundle, options);
|
|
1995
|
+
}
|
|
1996
|
+
},
|
|
1997
|
+
async closeBundle() {
|
|
1998
|
+
if (isBuild && !isSSRBuild && (adapter == null ? void 0 : adapter.buildEnd)) {
|
|
1999
|
+
await adapter.buildEnd(
|
|
2000
|
+
resolvedConfig,
|
|
2001
|
+
routes.list,
|
|
2002
|
+
routeData.builtEntries,
|
|
2003
|
+
routeData.sourceEntries
|
|
2004
|
+
);
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
];
|
|
2009
|
+
}
|
|
2010
|
+
async function getVerbsFromFileBuild(context, filePath) {
|
|
2011
|
+
const verbs = [];
|
|
2012
|
+
const result = await context.load({
|
|
2013
|
+
id: filePath,
|
|
2014
|
+
resolveDependencies: false
|
|
2015
|
+
});
|
|
2016
|
+
if (result) {
|
|
2017
|
+
const exportIds = getExportIdentifiers(result.ast);
|
|
2018
|
+
for (const id of exportIds) {
|
|
2019
|
+
const verb = id.toLowerCase();
|
|
2020
|
+
if (id === verb.toUpperCase() && httpVerbs.includes(verb)) {
|
|
2021
|
+
verbs.push(verb);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
return verbs;
|
|
2026
|
+
}
|
|
2027
|
+
async function getVerbsFromFileDev(devServer, filePath) {
|
|
2028
|
+
const verbs = [];
|
|
2029
|
+
const result = await devServer.transformRequest(filePath, { ssr: true });
|
|
2030
|
+
if (result && result.code) {
|
|
2031
|
+
const verbMatchReg = /__vite_ssr_exports__,\s+["'](GET|POST|PUT|DELETE)["']/gi;
|
|
2032
|
+
let match = verbMatchReg.exec(result.code);
|
|
2033
|
+
while (match) {
|
|
2034
|
+
const id = match[1];
|
|
2035
|
+
const verb = id.toLowerCase();
|
|
2036
|
+
if (httpVerbs.includes(verb)) {
|
|
2037
|
+
if (id === verb.toUpperCase()) {
|
|
2038
|
+
verbs.push(verb);
|
|
2039
|
+
} else {
|
|
2040
|
+
console.warn(
|
|
2041
|
+
`Found export '${id}' in handler ${filePath} which is close to '${verb.toUpperCase()}'. Exported handlers need to be uppercase: GET, POST, PUT or DELETE.`
|
|
2042
|
+
);
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
match = verbMatchReg.exec(result.code);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
return verbs;
|
|
2049
|
+
}
|
|
2050
|
+
function single(fn) {
|
|
2051
|
+
let promise;
|
|
2052
|
+
return async (...args) => {
|
|
2053
|
+
if (promise) {
|
|
2054
|
+
return promise;
|
|
2055
|
+
}
|
|
2056
|
+
promise = fn(...args);
|
|
2057
|
+
const result = await promise;
|
|
2058
|
+
promise = void 0;
|
|
2059
|
+
return result;
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2062
|
+
async function globFileExists(root, pattern) {
|
|
2063
|
+
return new Promise((resolve, reject) => {
|
|
2064
|
+
glob(pattern, { root }, (err, matches) => {
|
|
2065
|
+
if (err) {
|
|
2066
|
+
reject(err);
|
|
2067
|
+
}
|
|
2068
|
+
resolve(matches.length > 0);
|
|
2069
|
+
});
|
|
2070
|
+
});
|
|
2071
|
+
}
|
|
2072
|
+
async function ensureDir(dir) {
|
|
2073
|
+
if (!fs3.existsSync(dir)) {
|
|
2074
|
+
await fs3.promises.mkdir(dir, { recursive: true });
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
253
2077
|
async function getPackageData(dir) {
|
|
254
2078
|
do {
|
|
255
2079
|
const pkgPath = path2.join(dir, "package.json");
|
|
256
|
-
if (
|
|
257
|
-
return JSON.parse(await
|
|
2080
|
+
if (fs3.existsSync(pkgPath)) {
|
|
2081
|
+
return JSON.parse(await fs3.promises.readFile(pkgPath, "utf-8"));
|
|
258
2082
|
}
|
|
259
2083
|
} while (dir !== (dir = path2.dirname(dir)));
|
|
260
2084
|
return null;
|
|
261
2085
|
}
|
|
262
2086
|
async function resolveAdapter(root, options, log) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
return adapter;
|
|
2087
|
+
if (options && options.adapter !== void 0) {
|
|
2088
|
+
return options.adapter;
|
|
266
2089
|
}
|
|
267
2090
|
const pkg = await getPackageData(root);
|
|
268
2091
|
if (pkg) {
|
|
@@ -289,10 +2112,20 @@ async function resolveAdapter(root, options, log) {
|
|
|
289
2112
|
log && console.log("Using default adapter");
|
|
290
2113
|
return module.default();
|
|
291
2114
|
}
|
|
2115
|
+
var markoEntryFileRegex = /__marko-run__([^.]+)\.(.+)\.marko\.([^.]+)$/;
|
|
2116
|
+
function getEntryFileName(file) {
|
|
2117
|
+
const match = file && markoEntryFileRegex.exec(file);
|
|
2118
|
+
return match ? match[2] : void 0;
|
|
2119
|
+
}
|
|
2120
|
+
function isPluginIncluded(config2) {
|
|
2121
|
+
return config2.plugins.some((plugin) => {
|
|
2122
|
+
return plugin.name.startsWith(PLUGIN_NAME_PREFIX);
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
292
2125
|
|
|
293
2126
|
// src/cli/commands.ts
|
|
294
2127
|
var __dirname2 = path3.dirname(fileURLToPath2(import.meta.url));
|
|
295
|
-
var defaultPort =
|
|
2128
|
+
var defaultPort = Number(process.env.PORT || 3e3);
|
|
296
2129
|
var defaultConfigFileBases = ["serve.config", "vite.config"];
|
|
297
2130
|
var defaultConfigFileExts = [".js", ".cjs", ".mjs", ".ts", ".mts"];
|
|
298
2131
|
async function preview(entry, cwd, configFile, port, outDir, envFile, args = []) {
|
|
@@ -300,10 +2133,10 @@ async function preview(entry, cwd, configFile, port, outDir, envFile, args = [])
|
|
|
300
2133
|
{ root: cwd, configFile, logLevel: "silent", build: { outDir } },
|
|
301
2134
|
"serve"
|
|
302
2135
|
);
|
|
303
|
-
|
|
304
|
-
port
|
|
305
|
-
|
|
306
|
-
|
|
2136
|
+
const [availablePort, adapter] = await Promise.all([
|
|
2137
|
+
getAvailablePort(port ?? resolvedConfig.preview.port ?? resolvedConfig.server.port ?? defaultPort),
|
|
2138
|
+
resolveAdapter2(resolvedConfig)
|
|
2139
|
+
]);
|
|
307
2140
|
if (!adapter) {
|
|
308
2141
|
throw new Error("No adapter specified for 'serve' command");
|
|
309
2142
|
} else if (!adapter.startPreview) {
|
|
@@ -318,7 +2151,7 @@ async function preview(entry, cwd, configFile, port, outDir, envFile, args = [])
|
|
|
318
2151
|
cwd,
|
|
319
2152
|
dir,
|
|
320
2153
|
args,
|
|
321
|
-
port,
|
|
2154
|
+
port: availablePort,
|
|
322
2155
|
envFile
|
|
323
2156
|
};
|
|
324
2157
|
return await adapter.startPreview(entryFile, options);
|
|
@@ -328,13 +2161,13 @@ async function dev(entry, cwd, configFile, port, envFile, args = []) {
|
|
|
328
2161
|
{ root: cwd, configFile, logLevel: "silent" },
|
|
329
2162
|
"build"
|
|
330
2163
|
);
|
|
331
|
-
if (port === void 0) {
|
|
332
|
-
port = resolvedConfig.preview.port ?? defaultPort;
|
|
333
|
-
}
|
|
334
2164
|
if (envFile) {
|
|
335
2165
|
envFile = path3.resolve(cwd, envFile);
|
|
336
2166
|
}
|
|
337
|
-
const adapter = await
|
|
2167
|
+
const [availablePort, adapter] = await Promise.all([
|
|
2168
|
+
getAvailablePort(port ?? resolvedConfig.server.port ?? resolvedConfig.preview.port ?? defaultPort),
|
|
2169
|
+
resolveAdapter2(resolvedConfig)
|
|
2170
|
+
]);
|
|
338
2171
|
if (!adapter) {
|
|
339
2172
|
throw new Error(
|
|
340
2173
|
"No adapter specified for 'dev' command without custom target"
|
|
@@ -344,18 +2177,24 @@ async function dev(entry, cwd, configFile, port, envFile, args = []) {
|
|
|
344
2177
|
`Adapter '${adapter.name}' does not support 'serve' command`
|
|
345
2178
|
);
|
|
346
2179
|
}
|
|
2180
|
+
const config2 = {
|
|
2181
|
+
root: cwd,
|
|
2182
|
+
configFile,
|
|
2183
|
+
plugins: isPluginIncluded(resolvedConfig) ? void 0 : [markoRun()]
|
|
2184
|
+
};
|
|
347
2185
|
const options = {
|
|
348
2186
|
cwd,
|
|
349
2187
|
args,
|
|
350
|
-
port,
|
|
2188
|
+
port: availablePort,
|
|
351
2189
|
envFile
|
|
352
2190
|
};
|
|
353
|
-
return await adapter.startDev(entry,
|
|
2191
|
+
return await adapter.startDev(entry, config2, options);
|
|
354
2192
|
}
|
|
355
2193
|
async function build(entry, cwd, configFile, outDir, envFile) {
|
|
356
2194
|
var _a;
|
|
2195
|
+
const root = cwd;
|
|
357
2196
|
const resolvedConfig = await resolveConfig(
|
|
358
|
-
{ root
|
|
2197
|
+
{ root, configFile, logLevel: "silent" },
|
|
359
2198
|
"build"
|
|
360
2199
|
);
|
|
361
2200
|
const adapter = await resolveAdapter2(resolvedConfig);
|
|
@@ -374,7 +2213,7 @@ async function build(entry, cwd, configFile, outDir, envFile) {
|
|
|
374
2213
|
envFile = path3.resolve(cwd, envFile);
|
|
375
2214
|
}
|
|
376
2215
|
let buildConfig = {
|
|
377
|
-
root
|
|
2216
|
+
root,
|
|
378
2217
|
configFile,
|
|
379
2218
|
build: {
|
|
380
2219
|
ssr: false,
|
|
@@ -385,10 +2224,14 @@ async function build(entry, cwd, configFile, outDir, envFile) {
|
|
|
385
2224
|
store: new MemoryStore()
|
|
386
2225
|
});
|
|
387
2226
|
buildConfig = setExternalAdapterOptions(buildConfig, {
|
|
2227
|
+
root,
|
|
2228
|
+
isBuild: true,
|
|
388
2229
|
envFile
|
|
389
2230
|
});
|
|
2231
|
+
const hasPlugin = isPluginIncluded(resolvedConfig);
|
|
390
2232
|
await viteBuild({
|
|
391
2233
|
...buildConfig,
|
|
2234
|
+
plugins: hasPlugin ? void 0 : [markoRun()],
|
|
392
2235
|
build: {
|
|
393
2236
|
target: "esnext",
|
|
394
2237
|
...buildConfig.build,
|
|
@@ -402,6 +2245,7 @@ async function build(entry, cwd, configFile, outDir, envFile) {
|
|
|
402
2245
|
});
|
|
403
2246
|
await viteBuild({
|
|
404
2247
|
...buildConfig,
|
|
2248
|
+
plugins: hasPlugin ? void 0 : [markoRun()],
|
|
405
2249
|
build: {
|
|
406
2250
|
...buildConfig.build,
|
|
407
2251
|
sourcemap: true
|
|
@@ -411,7 +2255,7 @@ async function build(entry, cwd, configFile, outDir, envFile) {
|
|
|
411
2255
|
function findFileWithExt(dir, base, extensions = defaultConfigFileExts) {
|
|
412
2256
|
for (const ext of extensions) {
|
|
413
2257
|
const filePath = path3.join(dir, base + ext);
|
|
414
|
-
if (
|
|
2258
|
+
if (fs4.existsSync(filePath)) {
|
|
415
2259
|
return filePath;
|
|
416
2260
|
}
|
|
417
2261
|
}
|
|
@@ -420,7 +2264,7 @@ function findFileWithExt(dir, base, extensions = defaultConfigFileExts) {
|
|
|
420
2264
|
async function getViteConfig(dir, configFile, bases = defaultConfigFileBases) {
|
|
421
2265
|
if (configFile) {
|
|
422
2266
|
const configFilePath = path3.join(dir, configFile);
|
|
423
|
-
if (!
|
|
2267
|
+
if (!fs4.existsSync(configFilePath)) {
|
|
424
2268
|
throw new Error(`No config file found at '${configFilePath}'`);
|
|
425
2269
|
}
|
|
426
2270
|
return configFile;
|
|
@@ -433,12 +2277,9 @@ async function getViteConfig(dir, configFile, bases = defaultConfigFileBases) {
|
|
|
433
2277
|
}
|
|
434
2278
|
return path3.join(__dirname2, "default.config.mjs");
|
|
435
2279
|
}
|
|
436
|
-
async function resolveAdapter2(
|
|
437
|
-
const options = getExternalPluginOptions(
|
|
438
|
-
|
|
439
|
-
throw new Error("Unable to resolve @marko/run options");
|
|
440
|
-
}
|
|
441
|
-
return resolveAdapter(config.root, options);
|
|
2280
|
+
async function resolveAdapter2(config2) {
|
|
2281
|
+
const options = getExternalPluginOptions(config2);
|
|
2282
|
+
return resolveAdapter(config2.root, options);
|
|
442
2283
|
}
|
|
443
2284
|
|
|
444
2285
|
// src/cli/index.ts
|
|
@@ -458,12 +2299,12 @@ prog.command("preview [entry]").describe("Start a production-like server for alr
|
|
|
458
2299
|
process.env.NODE_ENV = "production";
|
|
459
2300
|
const cwd = process.cwd();
|
|
460
2301
|
const args = process.argv.slice(entry ? 4 : 3);
|
|
461
|
-
const
|
|
462
|
-
await build(entry, cwd,
|
|
2302
|
+
const config2 = await getViteConfig(cwd, opts.config);
|
|
2303
|
+
await build(entry, cwd, config2, opts.output, opts.env);
|
|
463
2304
|
await preview(
|
|
464
2305
|
opts.file,
|
|
465
2306
|
cwd,
|
|
466
|
-
|
|
2307
|
+
config2,
|
|
467
2308
|
opts.port,
|
|
468
2309
|
opts.output,
|
|
469
2310
|
opts.env,
|
|
@@ -476,15 +2317,15 @@ prog.command("dev [entry]", "", { default: true }).describe("Start development s
|
|
|
476
2317
|
).example("dev --config vite.config.js").action(async (entry, opts) => {
|
|
477
2318
|
const cwd = process.cwd();
|
|
478
2319
|
const args = process.argv.slice(entry ? 4 : 3);
|
|
479
|
-
const
|
|
480
|
-
await dev(entry, cwd,
|
|
2320
|
+
const config2 = await getViteConfig(cwd, opts.config);
|
|
2321
|
+
await dev(entry, cwd, config2, opts.port, opts.env, args);
|
|
481
2322
|
});
|
|
482
2323
|
prog.command("build [entry]").describe("Build the application (without serving it)").option(
|
|
483
2324
|
"-o, --output",
|
|
484
2325
|
"Directory to write built files (default: 'build.outDir' in Vite config)"
|
|
485
2326
|
).example("build --config vite.config.js").action(async (entry, opts) => {
|
|
486
2327
|
const cwd = process.cwd();
|
|
487
|
-
const
|
|
488
|
-
await build(entry, cwd,
|
|
2328
|
+
const config2 = await getViteConfig(cwd, opts.config);
|
|
2329
|
+
await build(entry, cwd, config2, opts.ouput, opts.env);
|
|
489
2330
|
});
|
|
490
2331
|
prog.parse(process.argv);
|