@marko/run 0.5.13 → 0.5.15

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.
@@ -10,19 +10,57 @@ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot
10
10
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
11
11
 
12
12
  // src/vite/plugin.ts
13
- import path4 from "path";
13
+ import markoVitePlugin from "@marko/vite";
14
+ import browserslist from "browserslist";
15
+ import { createHash } from "crypto";
16
+ import createDebug from "debug";
17
+ import { resolveToEsbuildTarget } from "esbuild-plugin-browserslist";
14
18
  import fs3 from "fs";
15
19
  import { glob } from "glob";
20
+ import path4 from "path";
16
21
  import { fileURLToPath } from "url";
17
- import browserslist from "browserslist";
18
- import { resolveToEsbuildTarget } from "esbuild-plugin-browserslist";
19
22
  import { buildErrorMessage, mergeConfig } from "vite";
20
- import markoVitePlugin from "@marko/vite";
23
+
24
+ // src/adapter/utils.ts
25
+ import kleur from "kleur";
26
+ import supporsColor from "supports-color";
27
+ function stripAnsi(string) {
28
+ return string.replace(
29
+ /([\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><])/g,
30
+ ""
31
+ );
32
+ }
33
+ function cleanStack(stack) {
34
+ return stack.split(/\n/).filter((l) => /^\s*at/.test(l)).join("\n");
35
+ }
36
+ function prepareError(err) {
37
+ var _a;
38
+ return {
39
+ message: stripAnsi(err.message),
40
+ stack: stripAnsi(cleanStack(err.stack || "")),
41
+ id: err.id,
42
+ frame: stripAnsi(err.frame || ""),
43
+ plugin: err.plugin,
44
+ pluginCode: (_a = err.pluginCode) == null ? void 0 : _a.toString(),
45
+ loc: err.loc
46
+ };
47
+ }
48
+
49
+ // src/vite/codegen/index.ts
50
+ import path from "path";
21
51
 
22
52
  // src/vite/constants.ts
23
53
  var markoRunFilePrefix = "__marko-run__";
24
54
  var virtualFilePrefix = "virtual:marko-run";
25
- var httpVerbs = ["get", "post", "put", "delete"];
55
+ var httpVerbs = [
56
+ "get",
57
+ "head",
58
+ "post",
59
+ "put",
60
+ "delete",
61
+ "patch",
62
+ "options"
63
+ ];
26
64
  var serverEntryQuery = "?marko-server-entry";
27
65
  var RoutableFileTypes = {
28
66
  Page: "page",
@@ -34,1394 +72,1435 @@ var RoutableFileTypes = {
34
72
  Error: "500"
35
73
  };
36
74
 
37
- // src/vite/routes/vdir.ts
38
- var _dirs, _pathlessDirs;
39
- var _VDir = class _VDir {
40
- constructor(parent, segment, source) {
41
- __privateAdd(this, _dirs);
42
- __privateAdd(this, _pathlessDirs);
43
- __publicField(this, "parent");
44
- __publicField(this, "source");
45
- __publicField(this, "path");
46
- __publicField(this, "fullPath");
47
- __publicField(this, "segment");
48
- __publicField(this, "files");
49
- if (!parent || !segment) {
50
- this.parent = null;
51
- this.source = null;
52
- this.path = "/";
53
- this.fullPath = "/";
54
- this.segment = {
55
- raw: "",
56
- name: ""
57
- };
58
- } else {
59
- this.parent = parent;
60
- this.source = source;
61
- this.path = parent.path + (parent.path === "/" ? segment.name : `/${segment.name}`);
62
- this.fullPath = parent.fullPath + (parent.fullPath === "/" ? segment.name : `/${segment.name}`);
63
- if (segment.param) {
64
- this.fullPath += segment.param;
65
- }
66
- this.segment = segment;
67
- }
75
+ // src/vite/utils/route.ts
76
+ var httpVerbOrder = httpVerbs.reduce(
77
+ (order, verb, index) => {
78
+ order[verb] = index;
79
+ return order;
80
+ },
81
+ {}
82
+ );
83
+ function getVerbs(route, noAutoHead) {
84
+ var _a;
85
+ const verbs = new Set((_a = route.handler) == null ? void 0 : _a.verbs);
86
+ if (route.page) {
87
+ verbs.add("get");
68
88
  }
69
- get pathInfo() {
70
- const value = {
71
- id: "/",
72
- path: "/",
73
- segments: []
74
- };
75
- let sep = "";
76
- for (const { segment } of this) {
77
- const { type, name, param } = segment;
78
- if (name && type !== "_") {
79
- value.id += sep + (type || name);
80
- value.path += sep + name;
81
- value.isEnd = type === "$$";
82
- if (param) {
83
- value.path += param;
84
- let index = type === "$$" ? null : value.segments.length;
85
- if (!value.params) {
86
- value.params = { [param]: index };
87
- } else if (!(param in value.params)) {
88
- value.params[param] = index;
89
- }
90
- }
91
- value.segments.push(name);
92
- sep = "/";
93
- }
94
- }
95
- Object.defineProperty(this, "pathInfo", {
96
- value,
97
- enumerable: true
98
- });
99
- return value;
89
+ if (!noAutoHead && verbs.has("get")) {
90
+ verbs.add("head");
100
91
  }
101
- addDir(path5, segment) {
102
- const map = segment.type === "_" ? __privateGet(this, _pathlessDirs) ?? __privateSet(this, _pathlessDirs, /* @__PURE__ */ new Map()) : __privateGet(this, _dirs) ?? __privateSet(this, _dirs, /* @__PURE__ */ new Map());
103
- if (!map.has(segment.name)) {
104
- const dir = new _VDir(this, segment, path5);
105
- map.set(segment.name, dir);
106
- return dir;
92
+ return [...verbs].sort((a, b) => httpVerbOrder[a] - httpVerbOrder[b]);
93
+ }
94
+ function hasVerb(route, verb) {
95
+ var _a, _b;
96
+ return verb === "get" && !!route.page || ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.includes(verb)) || verb === "head" && hasVerb(route, "get");
97
+ }
98
+
99
+ // src/vite/codegen/writer.ts
100
+ function createWriter(sink, options) {
101
+ let buffer = "";
102
+ let indentLevel = 0;
103
+ let indentString = "";
104
+ let firstOpenIndex = 0;
105
+ const branches = [];
106
+ const openWriters = /* @__PURE__ */ new Map();
107
+ function write(data) {
108
+ if (!writer.__isActive) {
109
+ throw new Error("Cannot write to branch that has been joined");
107
110
  }
108
- return map.get(segment.name);
109
- }
110
- addFile(file) {
111
- if (!this.files) {
112
- this.files = /* @__PURE__ */ new Map();
113
- this.files.set(file.type, file);
114
- } else if (!this.files.has(file.type)) {
115
- this.files.set(file.type, file);
111
+ if (openWriters.size) {
112
+ buffer += data;
116
113
  } else {
117
- const existing = this.files.get(file.type);
118
- if (existing !== file) {
119
- throw new Error(
120
- `Duplicate file type '${file.type}' added at path '${this.path}'. File '${file.importPath}' collides with '${existing.importPath}'.`
121
- );
122
- } else if (file.type === RoutableFileTypes.Page || file.type === RoutableFileTypes.Handler) {
123
- throw new Error(
124
- `Ambiguous path definition: route '${this.path}' is defined multiple times by ${file.importPath}`
125
- );
126
- }
127
- throw new Error(
128
- `Ambiguous path definition: file '${this.path}' is included multiple times by ${file.importPath}`
129
- );
130
- }
131
- }
132
- *dirs() {
133
- if (__privateGet(this, _pathlessDirs)) {
134
- yield* __privateGet(this, _pathlessDirs).values();
135
- }
136
- if (__privateGet(this, _dirs)) {
137
- yield* __privateGet(this, _dirs).values();
138
- }
139
- }
140
- *[Symbol.iterator]() {
141
- if (this.parent) {
142
- yield* this.parent;
114
+ sink(data);
143
115
  }
144
- yield this;
116
+ return writer;
145
117
  }
146
- static addPaths(roots, paths) {
147
- const dirs = [];
148
- const unique = /* @__PURE__ */ new Set();
149
- for (const root of roots) {
150
- for (const path5 of paths) {
151
- let dir = root;
152
- for (const segment of path5.segments) {
153
- dir = dir.addDir(path5, segment);
118
+ const writer = {
119
+ __isActive: true,
120
+ get indent() {
121
+ return indentLevel;
122
+ },
123
+ set indent(value) {
124
+ if (options == null ? void 0 : options.indentWith) {
125
+ if (value < 0) {
126
+ value = 0;
154
127
  }
155
- if (unique.has(dir.path)) {
156
- const sources = /* @__PURE__ */ new Set();
157
- let sourcePath = "";
158
- for (const { source } of dir) {
159
- if (source && !sources.has(source.source)) {
160
- sources.add(source.source);
161
- sourcePath += source.source + "/";
162
- }
163
- }
164
- throw new Error(
165
- `Ambiguous directory structure: '${sourcePath}${path5.source}' defines '${dir.path}' multiple times.`
166
- );
167
- } else {
168
- unique.add(dir.path);
169
- dirs.push(dir);
128
+ if (value !== indentLevel) {
129
+ indentLevel = value;
130
+ indentString = options.indentWith.repeat(indentLevel);
170
131
  }
171
132
  }
172
- }
173
- return dirs;
174
- }
175
- };
176
- _dirs = new WeakMap();
177
- _pathlessDirs = new WeakMap();
178
- var VDir = _VDir;
179
-
180
- // src/vite/routes/parse.ts
181
- function parseFlatRoute(pattern) {
182
- if (!pattern) throw new Error("Empty pattern");
183
- const len = pattern.length;
184
- let i = 0;
185
- return parse2([
186
- {
187
- id: "/",
188
- segments: [],
189
- source: pattern
190
- }
191
- ]);
192
- function parse2(basePaths, group) {
193
- const pathMap = /* @__PURE__ */ new Map();
194
- const delimiters = group ? ").," : ".,";
195
- let charCode;
196
- let segmentStart = i;
197
- let type;
198
- let current;
199
- do {
200
- charCode = pattern.charCodeAt(i);
201
- if (charCode === 41 && group) {
202
- break;
203
- } else if (charCode === 44) {
204
- if (!current) {
205
- segmentEnd(
206
- basePaths.map((path5) => ({
207
- ...path5,
208
- segments: path5.segments.slice()
209
- })),
210
- "",
211
- "_",
212
- pathMap
213
- );
214
- } else {
215
- segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
133
+ },
134
+ write(data, indent = false) {
135
+ if (indent && indentString) {
136
+ write(indentString);
137
+ }
138
+ return write(data);
139
+ },
140
+ writeLines(...lines) {
141
+ for (const line of lines) {
142
+ if (line) {
143
+ writer.write(line, true);
216
144
  }
217
- current = void 0;
218
- type = void 0;
219
- segmentStart = ++i;
220
- } else if (charCode === 46) {
221
- if (current) {
222
- segmentEnd(current, pattern.slice(segmentStart, i), type);
223
- }
224
- type = void 0;
225
- segmentStart = ++i;
226
- } else if (charCode === 40) {
227
- const groupPaths = parse2(current || basePaths, ++i);
228
- if (groupPaths.length) {
229
- current = groupPaths;
230
- }
231
- segmentStart = ++i;
232
- } else {
233
- if (charCode === 95) {
234
- type = "_";
235
- } else if (charCode === 36) {
236
- type = pattern.charCodeAt(i + 1) === 36 ? "$$" : "$";
237
- }
238
- current ?? (current = basePaths.map((path5) => ({
239
- ...path5,
240
- segments: path5.segments.slice()
241
- })));
242
- i = len;
243
- for (const char of delimiters) {
244
- const index = pattern.indexOf(char, segmentStart);
245
- if (index >= 0 && index < i) {
246
- i = index;
145
+ writer.write("\n");
146
+ }
147
+ return writer;
148
+ },
149
+ writeBlockStart(data) {
150
+ writer.writeLines(data).indent++;
151
+ return writer;
152
+ },
153
+ writeBlockEnd(data = "}") {
154
+ writer.indent--;
155
+ writer.writeLines(data);
156
+ return writer;
157
+ },
158
+ writeBlock(start, lines, end) {
159
+ return writer.writeBlockStart(start).writeLines(...lines).writeBlockEnd(end);
160
+ },
161
+ branch(name) {
162
+ const existing = openWriters.get(name);
163
+ if (existing) {
164
+ return existing;
165
+ }
166
+ const branch = {
167
+ buffer,
168
+ writer: createWriter(
169
+ (data) => {
170
+ branch.buffer += data;
171
+ },
172
+ {
173
+ ...options,
174
+ onJoin() {
175
+ openWriters.delete(name);
176
+ for (let i = firstOpenIndex; i < branches.length; i++) {
177
+ const b = branches[i];
178
+ if (!b) {
179
+ continue;
180
+ } else if (b.writer.__isActive) {
181
+ break;
182
+ }
183
+ sink(b.buffer);
184
+ branches[i] = null;
185
+ firstOpenIndex++;
186
+ }
187
+ if (!openWriters.size) {
188
+ sink(buffer);
189
+ buffer = "";
190
+ }
191
+ }
192
+ }
193
+ )
194
+ };
195
+ branch.writer.indent = indentLevel;
196
+ openWriters.set(name, branch.writer);
197
+ branches.push(branch);
198
+ buffer = "";
199
+ return branch.writer;
200
+ },
201
+ join(recursive) {
202
+ var _a;
203
+ if (writer.__isActive) {
204
+ if (openWriters.size) {
205
+ if (recursive) {
206
+ for (const branch of openWriters.values()) {
207
+ branch.join(true);
208
+ }
209
+ } else {
210
+ throw new Error(
211
+ `Cannot join a Writer with un-joined branches - use the \`recursive\` argument to join all open branches`
212
+ );
247
213
  }
248
214
  }
215
+ buffer && sink(buffer);
216
+ writer.__isActive = false;
217
+ (_a = options == null ? void 0 : options.onJoin) == null ? void 0 : _a.call(options, writer);
249
218
  }
250
- } while (i < len);
251
- if (group && charCode !== 41) {
252
- throw new Error(
253
- `Invalid route pattern: group was not closed '${pattern.slice(
254
- group
255
- )}' in '${pattern}'`
256
- );
257
219
  }
258
- if (!current) {
259
- segmentEnd(
260
- basePaths.map((path5) => ({
261
- ...path5,
262
- segments: path5.segments.slice()
263
- })),
264
- "",
265
- "_",
266
- pathMap
267
- );
220
+ };
221
+ return writer;
222
+ }
223
+ function createStringWriter(opts) {
224
+ let code = "";
225
+ const writer = createWriter(
226
+ (data) => {
227
+ code += data;
228
+ },
229
+ { indentWith: " ", ...opts }
230
+ );
231
+ return Object.assign(writer, {
232
+ end() {
233
+ writer.join(true);
234
+ return code;
235
+ }
236
+ });
237
+ }
238
+
239
+ // src/vite/codegen/index.ts
240
+ function renderRouteTemplate(route, getRelativePath) {
241
+ if (!route.page) {
242
+ throw new Error(`Route ${route.key} has no page to render`);
243
+ }
244
+ return renderEntryTemplate(
245
+ route.entryName,
246
+ [...route.layouts, route.page].map(
247
+ (file) => getRelativePath(file.importPath)
248
+ ),
249
+ route.key === RoutableFileTypes.Error ? ["error"] : []
250
+ );
251
+ }
252
+ function renderEntryTemplate(name, files, pageInputs = []) {
253
+ if (!name) {
254
+ throw new Error(`Invalid argument - 'name' cannot be empty`);
255
+ }
256
+ if (!files.length) {
257
+ throw new Error(`Invalid argument - 'files' cannot be empty`);
258
+ }
259
+ const writer = createStringWriter();
260
+ writer.writeLines(`// ${name}.marko`);
261
+ writer.branch("imports");
262
+ writer.writeLines("");
263
+ writeEntryTemplateTag(writer, files, pageInputs);
264
+ return writer.end();
265
+ }
266
+ function writeEntryTemplateTag(writer, [file, ...rest], pageInputs, index = 1) {
267
+ if (file) {
268
+ const isLast = !rest.length;
269
+ const tag = isLast ? "Page" : `Layout${index}`;
270
+ writer.branch("imports").writeLines(`import ${tag} from '${file}';`);
271
+ if (isLast) {
272
+ const attributes = pageInputs.length ? " " + pageInputs.map((name) => `${name}=input.${name}`).join(" ") : "";
273
+ writer.writeLines(`<${tag}${attributes}/>`);
268
274
  } else {
269
- segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
275
+ writer.writeBlockStart(`<${tag}>`);
276
+ writeEntryTemplateTag(writer, rest, pageInputs, index + 1);
277
+ writer.writeBlockEnd(`</>`);
270
278
  }
271
- return [...pathMap.values()];
272
279
  }
273
- function segmentEnd(paths, raw, type, map) {
274
- let segment;
275
- if (raw) {
276
- segment = {
277
- raw,
278
- name: raw,
279
- type
280
- };
281
- if (type === "$" || type === "$$") {
282
- segment.name = type;
283
- segment.param = raw.slice(type.length);
284
- }
280
+ }
281
+ function renderRouteEntry(route, entriesDir) {
282
+ var _a;
283
+ const { key, index, handler, page, middleware, meta, entryName } = route;
284
+ const verbs = getVerbs(route);
285
+ if (!verbs) {
286
+ throw new Error(
287
+ `Route ${key} doesn't have a handler or page for any HTTP verbs`
288
+ );
289
+ }
290
+ const writer = createStringWriter();
291
+ writer.writeLines(`// ${virtualFilePrefix}/${entryName}.js`);
292
+ const imports = writer.branch("imports");
293
+ const runtimeImports = [];
294
+ if (handler) {
295
+ runtimeImports.push("normalize");
296
+ }
297
+ if (handler || middleware.length) {
298
+ runtimeImports.push("call");
299
+ }
300
+ if (!page || verbs.some((verb) => verb !== "get" && verb !== "head")) {
301
+ runtimeImports.push("noContent");
302
+ }
303
+ if (page) {
304
+ runtimeImports.push("pageResponse");
305
+ }
306
+ if (verbs.includes("head")) {
307
+ runtimeImports.push("stripResponseBody");
308
+ }
309
+ if (runtimeImports.length) {
310
+ imports.writeLines(
311
+ `import { ${runtimeImports.join(
312
+ ", "
313
+ )} } from '${virtualFilePrefix}/runtime/internal';`
314
+ );
315
+ }
316
+ if (middleware.length) {
317
+ const names = middleware.map((m) => `mware${m.id}`);
318
+ imports.writeLines(
319
+ `import { ${names.join(
320
+ ", "
321
+ )} } from '${virtualFilePrefix}/${markoRunFilePrefix}middleware.js';`
322
+ );
323
+ }
324
+ if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.length) {
325
+ writer.writeLines("");
326
+ const names = [];
327
+ for (const verb of handler.verbs) {
328
+ const importName = verb.toUpperCase();
329
+ names.push(importName);
330
+ writer.writeLines(`const ${verb}Handler = normalize(${importName});`);
285
331
  }
286
- for (const path5 of paths) {
287
- if (segment) {
288
- if (path5.isCatchall) {
289
- throw new Error(
290
- `Invalid route pattern: nested segments are not allowed after a catch-all parameter. Found '.' following '${pattern.slice(
291
- 0,
292
- i
293
- )}' in '${pattern}'.`
294
- );
295
- }
296
- path5.segments.push(segment);
297
- path5.id += path5.id === "/" ? segment.name : `/${segment.name}`;
298
- if (type === "$$") {
299
- path5.isCatchall = true;
300
- }
301
- }
302
- if (map) {
303
- if (map.has(path5.id)) {
304
- const existing = map.get(path5.id);
305
- const existingExpansion = existing.segments.map((s) => s.raw).join(".");
306
- const currentExpansion = path5.segments.map((s) => s.raw).join(".");
307
- throw new Error(
308
- `Invalid route pattern: route '${path5.id}' is ambiguous. Expansion '${currentExpansion}' collides with '${existingExpansion}' in '${pattern}'.`
332
+ imports.writeLines(
333
+ `import { ${names.join(", ")} } from './${handler.importPath}';`
334
+ );
335
+ }
336
+ if (page) {
337
+ const pageNameIndex = page.name.indexOf("+page");
338
+ const pageNamePrefix = pageNameIndex > 0 ? `${page.name.slice(0, pageNameIndex)}.` : "";
339
+ const importPath = route.layouts.length ? `./${path.posix.join(entriesDir, page.relativePath, "..", pageNamePrefix + "route.marko")}` : `./${page.importPath}`;
340
+ imports.writeLines(`import page from '${importPath}${serverEntryQuery}';`);
341
+ }
342
+ if (meta) {
343
+ imports.writeLines(
344
+ `export { default as meta${index} } from './${meta.importPath}';`
345
+ );
346
+ }
347
+ for (const verb of verbs) {
348
+ writeRouteEntryHandler(writer, route, verb);
349
+ }
350
+ return writer.end();
351
+ }
352
+ function writeRouteEntryHandler(writer, route, verb) {
353
+ var _a, _b, _c, _d;
354
+ const { key, index, page, handler, middleware } = route;
355
+ const len = middleware.length;
356
+ let nextName;
357
+ let currentName;
358
+ let hasBody = false;
359
+ writer.writeLines("");
360
+ if (page && (verb === "get" || verb === "head")) {
361
+ writer.writeBlockStart(
362
+ `export function ${verb}${index}(context, buildInput) {`
363
+ );
364
+ } else {
365
+ writer.writeBlockStart(`export function ${verb}${index}(context) {`);
366
+ }
367
+ const continuations = writer.branch("cont");
368
+ if (page && (verb === "get" || verb === "head")) {
369
+ currentName = "__page";
370
+ if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes(verb)) {
371
+ const name = `${verb}Handler`;
372
+ continuations.writeLines(
373
+ `const ${currentName} = () => pageResponse(page, buildInput());`
374
+ );
375
+ if (len) {
376
+ nextName = currentName;
377
+ currentName = `__${name}`;
378
+ continuations.writeLines(
379
+ `const ${currentName} = () => call(${name}, ${nextName}, context);`
380
+ );
381
+ } else {
382
+ if (verb === "head") {
383
+ writer.writeLines(
384
+ `return stripResponseBody(call(${name}, ${currentName}, context));`
309
385
  );
386
+ } else {
387
+ writer.writeLines(`return call(${name}, ${currentName}, context);`);
310
388
  }
311
- map.set(path5.id, path5);
389
+ hasBody = true;
312
390
  }
391
+ } else if (verb === "head") {
392
+ writer.writeLines(
393
+ `return stripResponseBody(get${index}(context, buildInput));`
394
+ );
395
+ hasBody = true;
396
+ } else if (len) {
397
+ continuations.writeLines(
398
+ `const ${currentName} = () => pageResponse(page, buildInput());`
399
+ );
400
+ nextName = currentName;
401
+ } else {
402
+ writer.writeLines(`return pageResponse(page, buildInput());`);
403
+ hasBody = true;
313
404
  }
314
- }
315
- }
316
-
317
- // src/vite/routes/builder.ts
318
- var markoFiles = `(${RoutableFileTypes.Layout}|${RoutableFileTypes.Page}|${RoutableFileTypes.NotFound}|${RoutableFileTypes.Error})\\.(?:.*\\.)?(marko)`;
319
- var nonMarkoFiles = `(${RoutableFileTypes.Middleware}|${RoutableFileTypes.Handler}|${RoutableFileTypes.Meta})\\.(?:.*\\.)?(.+)`;
320
- var routeableFileRegex = new RegExp(
321
- `[+](?:${markoFiles}|${nonMarkoFiles})$`,
322
- "i"
323
- );
324
- function matchRoutableFile(filename) {
325
- const match = filename.match(routeableFileRegex);
326
- return match && (match[1] || match[3]).toLowerCase();
327
- }
328
- function isSpecialType(type) {
329
- return type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error;
330
- }
331
- async function buildRoutes(sources) {
332
- const uniqueRoutes = /* @__PURE__ */ new Map();
333
- const routes = [];
334
- const special = {};
335
- const middlewares = /* @__PURE__ */ new Set();
336
- const unusedFiles = /* @__PURE__ */ new Set();
337
- const currentLayouts = /* @__PURE__ */ new Set();
338
- const currentMiddleware = /* @__PURE__ */ new Set();
339
- const root = new VDir();
340
- const dirStack = [];
341
- let basePath;
342
- let importPrefix;
343
- let activeDirs;
344
- let isBaseDir;
345
- let nextFileId = 1;
346
- let nextRouteIndex = 1;
347
- const walkOptions = {
348
- onEnter({ name }) {
349
- const prevDirStackLength = dirStack.length;
350
- if (isBaseDir) {
351
- isBaseDir = false;
352
- if (!basePath) {
353
- return;
354
- }
355
- name = basePath;
405
+ } else if ((_b = handler == null ? void 0 : handler.verbs) == null ? void 0 : _b.includes(verb)) {
406
+ const name = `${verb}Handler`;
407
+ currentName = `__${name}`;
408
+ nextName = "noContent";
409
+ if (len) {
410
+ continuations.writeLines(
411
+ `const ${currentName} = () => call(${name}, ${nextName}, context);`
412
+ );
413
+ } else {
414
+ if (verb === "head") {
415
+ writer.writeLines(
416
+ `return stripResponseBody(call(${name}, ${nextName}, context));`
417
+ );
356
418
  } else {
357
- dirStack.push(name);
358
- }
359
- const previousDirs = activeDirs;
360
- const paths = parseFlatRoute(name);
361
- activeDirs = VDir.addPaths(previousDirs, paths);
362
- return () => {
363
- activeDirs = previousDirs;
364
- dirStack.length = prevDirStackLength;
365
- };
366
- },
367
- onFile({ name, path: path5 }) {
368
- const match = name.match(routeableFileRegex);
369
- if (!match) {
370
- return;
419
+ writer.writeLines(`return call(${name}, ${nextName}, context);`);
371
420
  }
372
- const type = (match[1] || match[3]).toLowerCase();
373
- if (dirStack.length && isSpecialType(type)) {
374
- console.warn(
375
- `Special pages '${RoutableFileTypes.NotFound}' and '${RoutableFileTypes.Error}' are only considered in the root directory - ignoring ${path5}`
421
+ hasBody = true;
422
+ }
423
+ } else if (verb === "head" && ((_d = (_c = route.handler) == null ? void 0 : _c.verbs) == null ? void 0 : _d.includes("get"))) {
424
+ writer.writeLines(`return stripResponseBody(get${index}(context));`);
425
+ hasBody = true;
426
+ } else {
427
+ throw new Error(`Route ${key} has no handler for ${verb} requests`);
428
+ }
429
+ if (!hasBody) {
430
+ let i = len;
431
+ while (i--) {
432
+ const { id } = middleware[i];
433
+ const name = `mware${id}`;
434
+ nextName = currentName;
435
+ currentName = i ? `__${name}` : "";
436
+ if (currentName) {
437
+ continuations.writeLines(
438
+ `const ${currentName} = () => call(${name}, ${nextName}, context);`
376
439
  );
377
- return;
378
- }
379
- let dirs = activeDirs;
380
- if (match.index) {
381
- const paths = parseFlatRoute(name.slice(0, match.index));
382
- dirs = VDir.addPaths(activeDirs, paths);
383
- }
384
- const dirPath = dirStack.join("/");
385
- const relativePath = dirPath ? `${dirPath}/${name}` : name;
386
- const file = {
387
- id: String(nextFileId++),
388
- name,
389
- type,
390
- filePath: path5,
391
- relativePath,
392
- importPath: `${importPrefix}/${relativePath}`,
393
- verbs: type === RoutableFileTypes.Page ? ["get"] : void 0
394
- };
395
- for (const dir of dirs) {
396
- dir.addFile(file);
440
+ } else if (verb === "head") {
441
+ continuations.writeLines(
442
+ `return stripResponseBody(call(${name}, ${nextName}, context));`
443
+ );
444
+ } else {
445
+ continuations.writeLines(`return call(${name}, ${nextName}, context);`);
397
446
  }
398
447
  }
399
- };
400
- if (!Array.isArray(sources)) {
401
- sources = [sources];
402
448
  }
403
- for (const source of sources) {
404
- importPrefix = source.importPrefix ? source.importPrefix.replace(/^\/+|\/+$/g, "") : "";
405
- basePath = source.basePath || "";
406
- activeDirs = [root];
407
- isBaseDir = true;
408
- await source.walker(walkOptions);
449
+ continuations.join();
450
+ writer.writeBlockEnd("}");
451
+ }
452
+ function renderRouter(routes, entriesDir, options = {
453
+ trailingSlashes: "RedirectWithout"
454
+ }) {
455
+ const writer = createStringWriter();
456
+ writer.writeLines(`// @marko/run/router`);
457
+ const imports = writer.branch("imports");
458
+ imports.writeLines(
459
+ `import { NotHandled, NotMatched, createContext } from '${virtualFilePrefix}/runtime/internal';`
460
+ );
461
+ for (const route of routes.list) {
462
+ const verbs = getVerbs(route);
463
+ const names = verbs.map((verb) => `${verb}${route.index}`);
464
+ route.meta && names.push(`meta${route.index}`);
465
+ imports.writeLines(
466
+ `import { ${names.join(", ")} } from '${virtualFilePrefix}/${route.entryName}.js';`
467
+ );
409
468
  }
410
- traverse(root);
411
- return {
412
- list: routes,
413
- middleware: [...middlewares],
414
- special
415
- };
416
- function traverse(dir) {
417
- let middleware;
418
- let layout;
419
- if (dir.files) {
420
- middleware = dir.files.get(RoutableFileTypes.Middleware);
421
- layout = dir.files.get(RoutableFileTypes.Layout);
422
- const handler = dir.files.get(RoutableFileTypes.Handler);
423
- const page = dir.files.get(RoutableFileTypes.Page);
424
- let hasSpecial = false;
425
- if (middleware) {
426
- if (currentMiddleware.has(middleware)) {
427
- middleware = void 0;
428
- } else {
429
- currentMiddleware.add(middleware);
430
- unusedFiles.add(middleware);
431
- }
432
- }
433
- if (layout) {
434
- if (currentLayouts.has(layout)) {
435
- layout = void 0;
436
- } else {
437
- currentLayouts.add(layout);
438
- unusedFiles.add(layout);
439
- }
440
- }
441
- if (page || handler) {
442
- const path5 = dir.pathInfo;
443
- if (uniqueRoutes.has(path5.id)) {
444
- const existing = uniqueRoutes.get(path5.id);
445
- const route = routes[existing.index];
446
- const existingFiles = [route.handler, route.page].filter(Boolean).map((f) => f.filePath);
447
- const currentFiles = [handler, page].filter(Boolean).map((f) => f.filePath);
448
- throw new Error(`Duplicate routes for path '${path5.path}' were defined. A route established by:
449
- ${existingFiles.join(" and ")} via '${existing.dir.path}'
450
- collides with
451
- ${currentFiles.join(" and ")} via '${dir.path}'
452
- `);
453
- }
454
- uniqueRoutes.set(path5.id, { dir, index: routes.length });
455
- routes.push({
456
- index: nextRouteIndex++,
457
- key: dir.fullPath,
458
- paths: [path5],
459
- middleware: [...currentMiddleware],
460
- layouts: page ? [...currentLayouts] : [],
461
- meta: dir.files.get(RoutableFileTypes.Meta),
462
- page,
463
- handler,
464
- entryName: `${markoRunFilePrefix}route` + (dir.path !== "/" ? dir.fullPath.replace(/\//g, ".").replace(/(%[A-Fa-f0-9]{2})+/g, "_") : "")
465
- });
466
- }
467
- if (dir === root) {
468
- for (const [type, file] of dir.files) {
469
- if (isSpecialType(type)) {
470
- hasSpecial = true;
471
- special[type] = {
472
- index: 0,
473
- key: type,
474
- paths: [],
475
- middleware: [],
476
- layouts: [...currentLayouts],
477
- page: file,
478
- entryName: `${markoRunFilePrefix}special.${type}`
479
- };
480
- }
481
- }
482
- }
483
- if (handler || page) {
484
- for (const middleware2 of currentMiddleware) {
485
- middlewares.add(middleware2);
486
- unusedFiles.delete(middleware2);
487
- }
488
- }
489
- if (page || hasSpecial) {
490
- for (const layout2 of currentLayouts) {
491
- unusedFiles.delete(layout2);
492
- }
493
- }
494
- }
495
- if (dir.dirs) {
496
- for (const child of dir.dirs()) {
497
- traverse(child);
498
- }
499
- }
500
- if (middleware) {
501
- currentMiddleware.delete(middleware);
469
+ for (const route of Object.values(routes.special)) {
470
+ const importPath = route.layouts.length ? `./${path.posix.join(entriesDir, route.page.relativePath, "..", `route.${route.key}.marko`)}` : `./${route.page.importPath}`;
471
+ imports.writeLines(
472
+ `import page${route.key} from '${importPath}${serverEntryQuery}';`
473
+ );
474
+ }
475
+ writer.writeLines(
476
+ `
477
+ globalThis.__marko_run__ = { match, fetch, invoke };
478
+ `
479
+ ).writeBlockStart(`export function match(method, pathname) {`).writeLines(
480
+ `if (!pathname) {
481
+ pathname = '/';
482
+ } else if (pathname.charAt(0) !== '/') {
483
+ pathname = '/' + pathname;
484
+ }`
485
+ ).writeBlockStart(`switch (method) {`);
486
+ for (const verb of httpVerbs) {
487
+ const filteredRoutes = routes.list.filter((route) => hasVerb(route, verb));
488
+ if (filteredRoutes.length) {
489
+ const trie = createRouteTrie(filteredRoutes);
490
+ writer.writeLines(`case '${verb.toUpperCase()}':`);
491
+ writer.writeBlockStart(`case '${verb.toLowerCase()}': {`);
492
+ writeRouterVerb(writer, trie, verb);
493
+ writer.writeBlockEnd("}");
502
494
  }
503
- if (layout) {
504
- currentLayouts.delete(layout);
495
+ }
496
+ writer.writeBlockEnd("}").writeLines("return null;").writeBlockEnd("}");
497
+ writer.writeLines("").writeBlockStart(
498
+ "export async function invoke(route, request, platform, url) {"
499
+ ).writeLines(
500
+ "const [context, buildInput] = createContext(route, request, platform, url);"
501
+ );
502
+ const hasErrorPage = Boolean(routes.special[RoutableFileTypes.Error]);
503
+ if (hasErrorPage) {
504
+ writer.writeBlockStart("try {");
505
+ }
506
+ writer.writeBlockStart("if (route) {").writeBlockStart("try {").writeLines(
507
+ "const response = await route.handler(context, buildInput);",
508
+ "if (response) return response;"
509
+ ).indent--;
510
+ writer.writeBlockStart("} catch (error) {").writeLines(
511
+ "if (error === NotHandled) return;",
512
+ "if (error !== NotMatched) throw error;"
513
+ ).writeBlockEnd("}").writeBlockEnd("}");
514
+ if (routes.special[RoutableFileTypes.NotFound]) {
515
+ imports.writeLines(
516
+ `
517
+ const page404ResponseInit = {
518
+ status: 404,
519
+ headers: { "content-type": "text/html;charset=UTF-8" },
520
+ };`
521
+ );
522
+ writer.write(`
523
+ if (context.request.headers.get('Accept')?.includes('text/html')) {
524
+ return new Response(page404.stream(buildInput()), page404ResponseInit);
505
525
  }
526
+ `);
527
+ }
528
+ writer.indent--;
529
+ if (hasErrorPage) {
530
+ imports.writeLines(`
531
+ const page500ResponseInit = {
532
+ status: 500,
533
+ headers: { "content-type": "text/html;charset=UTF-8" },
534
+ };`);
535
+ writer.writeBlockStart(`} catch (error) {`).writeBlockStart(
536
+ `if (context.request.headers.get('Accept')?.includes('text/html')) {`
537
+ ).writeLines(
538
+ `return new Response(page500.stream(buildInput({ error })), page500ResponseInit);`
539
+ ).writeBlockEnd("}").writeLines("throw error;").writeBlockEnd("}");
506
540
  }
541
+ writer.writeBlockEnd("}");
542
+ renderFetch(writer, options);
543
+ return writer.end();
507
544
  }
545
+ function renderFetch(writer, options) {
546
+ writer.write(`
547
+ export async function fetch(request, platform) {
548
+ try {
549
+ const url = new URL(request.url);
550
+ let { pathname } = url;`);
551
+ switch (options.trailingSlashes) {
552
+ case "RedirectWithout":
553
+ writer.write(`
554
+ if (pathname !== '/' && pathname.endsWith('/')) {
555
+ url.pathname = pathname.slice(0, -1);
556
+ return Response.redirect(url);
557
+ }`);
558
+ break;
559
+ case "RedirectWith":
560
+ writer.write(`
561
+ if (pathname !== '/' && !pathname.endsWith('/')) {
562
+ url.pathname = pathname + '/';
563
+ return Response.redirect(url);
564
+ }`);
565
+ break;
566
+ case "RewriteWithout":
567
+ writer.write(`
568
+ if (pathname !== '/' && pathname.endsWith('/')) {
569
+ url.pathname = pathname = pathname.slice(0, -1);
570
+ }`);
571
+ break;
572
+ case "RewriteWith":
573
+ writer.write(`
574
+ if (pathname !== '/' && !pathname.endsWith('/')) {
575
+ url.pathname = pathname = pathname + '/';
576
+ }`);
577
+ break;
578
+ }
579
+ writer.write(`
508
580
 
509
- // src/vite/routes/walk.ts
510
- import fs from "fs";
511
- import path from "path";
512
- function createFSWalker(dir) {
513
- return async function walkFS({
514
- onEnter,
515
- onFile,
516
- onDir,
517
- maxDepth = 50
518
- }) {
519
- async function walk(dir2, depth) {
520
- const onExit = onEnter == null ? void 0 : onEnter(dir2);
521
- if (onExit !== false) {
522
- const dirs = [];
523
- const entries = await fs.promises.readdir(dir2.path, {
524
- withFileTypes: true
525
- });
526
- const prefix = dir2.path + path.sep;
527
- for (const entry of entries) {
528
- const walkEntry = {
529
- name: entry.name,
530
- path: prefix + entry.name
531
- };
532
- if (entry.isDirectory()) {
533
- dirs.push(walkEntry);
534
- } else {
535
- onFile == null ? void 0 : onFile(walkEntry);
536
- }
537
- }
538
- if ((onDir == null ? void 0 : onDir()) !== false && --depth > 0) {
539
- for (const entry of dirs) {
540
- await walk(entry, depth);
541
- }
542
- }
543
- onExit == null ? void 0 : onExit();
544
- }
581
+ const route = match(request.method, pathname);
582
+ return await invoke(route, request, platform, url);
583
+ } catch (error) {
584
+ if (import.meta.env.DEV) {
585
+ throw error;
545
586
  }
546
- await walk(
547
- {
548
- path: dir,
549
- name: path.basename(dir)
550
- },
551
- maxDepth
552
- );
553
- };
587
+ return new Response(null, {
588
+ status: 500
589
+ });
590
+ }
591
+ }`);
554
592
  }
555
-
556
- // src/vite/codegen/writer.ts
557
- function createWriter(sink, options) {
558
- let buffer = "";
559
- let indentLevel = 0;
560
- let indentString = "";
561
- let firstOpenIndex = 0;
562
- const branches = [];
563
- const openWriters = /* @__PURE__ */ new Map();
564
- function write(data) {
565
- if (!writer.__isActive) {
566
- throw new Error("Cannot write to branch that has been joined");
567
- }
568
- if (openWriters.size) {
569
- buffer += data;
570
- } else {
571
- sink(data);
593
+ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
594
+ const { route, dynamic, catchAll } = trie;
595
+ let closeCount = 0;
596
+ if (level === 0) {
597
+ writer.writeLines(`const len = pathname.length;`);
598
+ if (route) {
599
+ writer.writeLines(
600
+ `if (len === 1) return ${renderMatch(verb, route, trie.path)}; // ${trie.path.path}`
601
+ );
602
+ } else if (trie.static || dynamic) {
603
+ writer.writeBlockStart(`if (len > 1) {`);
604
+ closeCount++;
572
605
  }
573
- return writer;
574
606
  }
575
- const writer = {
576
- __isActive: true,
577
- get indent() {
578
- return indentLevel;
579
- },
580
- set indent(value) {
581
- if (options == null ? void 0 : options.indentWith) {
582
- if (value < 0) {
583
- value = 0;
607
+ if (trie.static || dynamic) {
608
+ const next = level + 1;
609
+ const index = `i${next}`;
610
+ let terminal;
611
+ let children;
612
+ writer.writeLines(`const ${index} = pathname.indexOf('/', ${offset}) + 1;`);
613
+ if (trie.static) {
614
+ for (const child of trie.static.values()) {
615
+ if (child.route) {
616
+ (terminal ?? (terminal = [])).push(child);
584
617
  }
585
- if (value !== indentLevel) {
586
- indentLevel = value;
587
- indentString = options.indentWith.repeat(indentLevel);
618
+ if (child.static || child.dynamic || child.catchAll) {
619
+ (children ?? (children = [])).push(child);
588
620
  }
589
621
  }
590
- },
591
- write(data, indent = false) {
592
- if (indent && indentString) {
593
- write(indentString);
622
+ }
623
+ if (terminal || (dynamic == null ? void 0 : dynamic.route)) {
624
+ closeCount++;
625
+ writer.writeBlockStart(`if (!${index} || ${index} === len) {`);
626
+ let value = `pathname.slice(${offset}, ${index} ? -1 : len)`;
627
+ if (dynamic == null ? void 0 : dynamic.route) {
628
+ const segment = `s${next}`;
629
+ writer.writeLines(`const ${segment} = decodeURIComponent(${value});`);
630
+ value = segment;
631
+ } else if (terminal == null ? void 0 : terminal.some(
632
+ (terminal2) => decodeURIComponent(terminal2.key) !== terminal2.key
633
+ )) {
634
+ value = `decodeURIComponent(${value})`;
594
635
  }
595
- return write(data);
596
- },
597
- writeLines(...lines) {
598
- for (const line of lines) {
599
- if (line) {
600
- writer.write(line, true);
636
+ if (terminal) {
637
+ const useSwitch = terminal.length > 1;
638
+ if (useSwitch) {
639
+ writer.writeBlockStart(`switch (${value}) {`);
601
640
  }
602
- writer.write("\n");
641
+ for (const { key, path: path5, route: route2 } of terminal) {
642
+ const decodedKey = decodeURIComponent(key);
643
+ if (useSwitch) {
644
+ writer.write(`case '${decodedKey}': `, true);
645
+ } else {
646
+ writer.write(`if (${value} === '${decodedKey}') `, true);
647
+ }
648
+ writer.write(
649
+ `return ${renderMatch(verb, route2, path5)}; // ${path5.path}
650
+ `
651
+ );
652
+ }
653
+ if (useSwitch) {
654
+ writer.writeBlockEnd("}");
655
+ }
656
+ }
657
+ if (dynamic == null ? void 0 : dynamic.route) {
658
+ writer.writeLines(
659
+ `if (${value}) return ${renderMatch(
660
+ verb,
661
+ dynamic.route,
662
+ dynamic.path
663
+ )}; // ${dynamic.path.path}`
664
+ );
665
+ }
666
+ }
667
+ if (children || (dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
668
+ if (terminal || (dynamic == null ? void 0 : dynamic.route)) {
669
+ writer.writeBlockEnd("} else {").indent++;
670
+ } else {
671
+ writer.writeBlockStart(`if (${index} && ${index} !== len) {`);
672
+ closeCount++;
603
673
  }
604
- return writer;
605
- },
606
- writeBlockStart(data) {
607
- writer.writeLines(data).indent++;
608
- return writer;
609
- },
610
- writeBlockEnd(data = "}") {
611
- writer.indent--;
612
- writer.writeLines(data);
613
- return writer;
614
- },
615
- writeBlock(start, lines, end) {
616
- return writer.writeBlockStart(start).writeLines(...lines).writeBlockEnd(end);
617
- },
618
- branch(name) {
619
- let existing = openWriters.get(name);
620
- if (existing) {
621
- return existing;
674
+ let value = `pathname.slice(${offset}, ${index} - 1)`;
675
+ if ((dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
676
+ const segment = `s${next}`;
677
+ writer.writeLines(`const ${segment} = decodeURIComponent(${value});`);
678
+ value = segment;
679
+ } else if (children == null ? void 0 : children.some((child) => decodeURIComponent(child.key) !== child.key)) {
680
+ value = `decodeURIComponent(${value})`;
622
681
  }
623
- const branch = {
624
- buffer,
625
- writer: createWriter(
626
- (data) => {
627
- branch.buffer += data;
628
- },
629
- {
630
- ...options,
631
- onJoin() {
632
- openWriters.delete(name);
633
- for (let i = firstOpenIndex; i < branches.length; i++) {
634
- const b = branches[i];
635
- if (!b) {
636
- continue;
637
- } else if (b.writer.__isActive) {
638
- break;
639
- }
640
- sink(b.buffer);
641
- branches[i] = null;
642
- firstOpenIndex++;
643
- }
644
- if (!openWriters.size) {
645
- sink(buffer);
646
- buffer = "";
647
- }
648
- }
682
+ if (children) {
683
+ const useSwitch = children.length > 1;
684
+ if (useSwitch) {
685
+ writer.writeBlockStart(`switch (${value}) {`);
686
+ }
687
+ for (const child of children) {
688
+ const decodedKey = decodeURIComponent(child.key);
689
+ if (useSwitch) {
690
+ writer.writeBlockStart(`case '${decodedKey}': {`);
691
+ } else {
692
+ writer.writeBlockStart(`if (${value} === '${decodedKey}') {`);
649
693
  }
650
- )
651
- };
652
- branch.writer.indent = indentLevel;
653
- openWriters.set(name, branch.writer);
654
- branches.push(branch);
655
- buffer = "";
656
- return branch.writer;
657
- },
658
- join(recursive) {
659
- var _a;
660
- if (writer.__isActive) {
661
- if (openWriters.size) {
662
- if (recursive) {
663
- for (const branch of openWriters.values()) {
664
- branch.join(true);
665
- }
694
+ const nextOffset = typeof offset === "string" ? index : offset + child.key.length + 1;
695
+ writeRouterVerb(writer, child, verb, next, nextOffset);
696
+ if (useSwitch) {
697
+ writer.writeBlockEnd("} break;");
666
698
  } else {
667
- throw new Error(
668
- `Cannot join a Writer with un-joined branches - use the \`recursive\` argument to join all open branches`
669
- );
699
+ writer.writeBlockEnd("}");
670
700
  }
671
701
  }
672
- buffer && sink(buffer);
673
- writer.__isActive = false;
674
- (_a = options == null ? void 0 : options.onJoin) == null ? void 0 : _a.call(options, writer);
702
+ if (useSwitch) {
703
+ writer.writeBlockEnd("}");
704
+ }
705
+ }
706
+ if ((dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
707
+ writer.writeBlockStart(`if (${value}) {`);
708
+ writeRouterVerb(writer, dynamic, verb, next, index);
709
+ writer.writeBlockEnd(`}`);
675
710
  }
676
711
  }
677
- };
678
- return writer;
679
- }
680
- function createStringWriter(opts) {
681
- let code = "";
682
- const writer = createWriter((data) => {
683
- code += data;
684
- }, { indentWith: " ", ...opts });
685
- return Object.assign(writer, {
686
- end() {
687
- writer.join(true);
688
- return code;
689
- }
690
- });
691
- }
692
-
693
- // src/vite/utils/route.ts
694
- function getVerbs(route) {
695
- var _a, _b;
696
- const verbs = ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.slice()) || [];
697
- if (route.page && !verbs.includes("get")) {
698
- verbs.unshift("get");
699
- }
700
- return verbs;
701
- }
702
- function hasVerb(route, verb) {
703
- var _a, _b;
704
- return verb === "get" && route.page || ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.includes(verb));
705
- }
706
-
707
- // src/vite/codegen/index.ts
708
- import path2 from "path";
709
- function renderRouteTemplate(route, getRelativePath) {
710
- if (!route.page) {
711
- throw new Error(`Route ${route.key} has no page to render`);
712
712
  }
713
- return renderEntryTemplate(
714
- route.entryName,
715
- [...route.layouts, route.page].map(
716
- (file) => getRelativePath(file.importPath)
717
- ),
718
- route.key === RoutableFileTypes.Error ? ["error"] : []
719
- );
720
- }
721
- function renderEntryTemplate(name, files, pageInputs = []) {
722
- if (!name) {
723
- throw new Error(`Invalid argument - 'name' cannot be empty`);
713
+ while (closeCount--) {
714
+ writer.writeBlockEnd("}");
724
715
  }
725
- if (!files.length) {
726
- throw new Error(`Invalid argument - 'files' cannot be empty`);
716
+ if (catchAll) {
717
+ writer.writeLines(
718
+ `return ${renderMatch(
719
+ verb,
720
+ catchAll.route,
721
+ catchAll.path,
722
+ String(offset)
723
+ )}; // ${catchAll.path.path}`
724
+ );
725
+ } else if (level === 0) {
726
+ writer.writeLines("return null;");
727
727
  }
728
- const writer = createStringWriter();
729
- writer.writeLines(`// ${name}.marko`);
730
- writer.branch("imports");
731
- writer.writeLines("");
732
- writeEntryTemplateTag(writer, files, pageInputs);
733
- return writer.end();
734
728
  }
735
- function writeEntryTemplateTag(writer, [file, ...rest], pageInputs, index = 1) {
736
- if (file) {
737
- const isLast = !rest.length;
738
- const tag = isLast ? "Page" : `Layout${index}`;
739
- writer.branch("imports").writeLines(`import ${tag} from '${file}';`);
740
- if (isLast) {
741
- const attributes = pageInputs.length ? " " + pageInputs.map((name) => `${name}=input.${name}`).join(" ") : "";
742
- writer.writeLines(`<${tag}${attributes}/>`);
743
- } else {
744
- writer.writeBlockStart(`<${tag}>`);
745
- writeEntryTemplateTag(writer, rest, pageInputs, index + 1);
746
- writer.writeBlockEnd(`</>`);
747
- }
748
- }
729
+ function wrapPropertyName(name) {
730
+ name = decodeURIComponent(name);
731
+ return /^[^A-Za-z_$]|[^A-Za-z0-9$_]/.test(name) ? `'${name}'` : name;
749
732
  }
750
- function renderRouteEntry(route, entriesDir) {
751
- var _a;
752
- const { key, index, handler, page, middleware, meta, entryName } = route;
753
- const verbs = getVerbs(route);
754
- if (!verbs) {
755
- throw new Error(
756
- `Route ${key} doesn't have a handler or page for any HTTP verbs`
757
- );
758
- }
759
- const writer = createStringWriter();
760
- writer.writeLines(`// ${virtualFilePrefix}/${entryName}.js`);
761
- const imports = writer.branch("imports");
762
- const runtimeImports = [];
763
- if (handler) {
764
- runtimeImports.push("normalize");
765
- }
766
- if (handler || middleware.length) {
767
- runtimeImports.push("call");
768
- }
769
- if (!page || verbs.length > 1) {
770
- runtimeImports.push("noContent");
771
- }
772
- if (page) {
773
- runtimeImports.push("pageResponse");
774
- }
775
- if (runtimeImports.length) {
776
- imports.writeLines(
777
- `import { ${runtimeImports.join(
778
- ", "
779
- )} } from '${virtualFilePrefix}/runtime/internal';`
780
- );
781
- }
782
- if (middleware.length) {
783
- const names = middleware.map((m) => `mware${m.id}`);
784
- imports.writeLines(
785
- `import { ${names.join(
786
- ", "
787
- )} } from '${virtualFilePrefix}/${markoRunFilePrefix}middleware.js';`
788
- );
789
- }
790
- if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.length) {
791
- writer.writeLines("");
792
- const names = [];
793
- for (const verb of handler.verbs) {
794
- const importName = verb.toUpperCase();
795
- names.push(importName);
796
- writer.writeLines(`const ${verb}Handler = normalize(${importName});`);
733
+ function renderParams(params, pathIndex) {
734
+ let result = "";
735
+ let catchAll = "";
736
+ let sep = "{";
737
+ for (const [name, index] of Object.entries(params)) {
738
+ if (typeof index === "number") {
739
+ result += `${sep} ${wrapPropertyName(name)}: s${index + 1}`;
740
+ sep = ",";
741
+ } else if (pathIndex) {
742
+ catchAll = name;
797
743
  }
798
- imports.writeLines(
799
- `import { ${names.join(", ")} } from './${handler.importPath}';`
800
- );
801
- }
802
- if (page) {
803
- const importPath = route.layouts.length ? `./${path2.posix.join(entriesDir, page.relativePath, "..", "route.marko")}` : `./${page.importPath}`;
804
- imports.writeLines(`import page from '${importPath}${serverEntryQuery}';`);
805
- }
806
- if (meta) {
807
- imports.writeLines(
808
- `export { default as meta${index} } from './${meta.importPath}';`
809
- );
810
744
  }
811
- for (const verb of verbs) {
812
- writeRouteEntryHandler(writer, route, verb);
745
+ if (catchAll) {
746
+ result += `${sep} ${wrapPropertyName(
747
+ catchAll
748
+ )}: pathname.slice(${pathIndex})`;
813
749
  }
814
- return writer.end();
750
+ return result ? result + " }" : "{}";
815
751
  }
816
- function writePageResponse(writer, wrapFn) {
752
+ function renderMatch(verb, route, path5, pathIndex) {
753
+ const handler = `${verb}${route.index}`;
754
+ const params = path5.params ? renderParams(path5.params, pathIndex) : "{}";
755
+ const meta = route.meta ? `meta${route.index}` : "{}";
756
+ const pathPattern = pathToURLPatternString(path5.path);
757
+ return `{ handler: ${handler}, params: ${params}, meta: ${meta}, path: '${pathPattern}' }`;
758
+ }
759
+ function renderMiddleware(middleware) {
760
+ const writer = createStringWriter();
817
761
  writer.writeLines(
818
- `${wrapFn ? `const ${wrapFn} = () =>` : `return`} pageResponse(page, buildInput());`
762
+ `// ${virtualFilePrefix}/${markoRunFilePrefix}middleware.js`
763
+ );
764
+ const imports = writer.branch("imports");
765
+ imports.writeLines(
766
+ `import { normalize } from '${virtualFilePrefix}/runtime/internal';`
819
767
  );
820
- }
821
- function writeMiddleware(writer, middleware, next, wrapFn) {
822
- if (wrapFn) {
823
- writer.writeLines(
824
- `const ${wrapFn} = () => call(${middleware}, ${next}, context);`
825
- );
826
- } else {
827
- writer.writeLines(`return call(${middleware}, ${next}, context);`);
828
- }
829
- }
830
- function writeRouteEntryHandler(writer, route, verb) {
831
- var _a;
832
- const { key, index, page, handler, middleware } = route;
833
- const len = middleware.length;
834
- let nextName;
835
- let currentName;
836
- let hasBody = false;
837
768
  writer.writeLines("");
838
- if (page) {
839
- writer.writeBlockStart(
840
- `export async function ${verb}${index}(context, buildInput) {`
841
- );
842
- } else {
843
- writer.writeBlockStart(`export async function ${verb}${index}(context) {`);
844
- }
845
- const continuations = writer.branch("cont");
846
- if (page && verb === "get") {
847
- currentName = "__page";
848
- if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes(verb)) {
849
- const name = `${verb}Handler`;
850
- writePageResponse(continuations, currentName);
851
- if (len) {
852
- nextName = currentName;
853
- currentName = `__${name}`;
854
- writeMiddleware(continuations, name, nextName, currentName);
855
- } else {
856
- writeMiddleware(writer, name, currentName);
857
- hasBody = true;
858
- }
859
- } else if (len) {
860
- writePageResponse(continuations, currentName);
861
- nextName = currentName;
862
- } else {
863
- writePageResponse(continuations);
864
- hasBody = true;
865
- }
866
- } else if (handler) {
867
- const name = `${verb}Handler`;
868
- currentName = `__${name}`;
869
- nextName = "noContent";
870
- if (len) {
871
- writeMiddleware(continuations, name, nextName, currentName);
872
- } else {
873
- writeMiddleware(writer, name, nextName);
874
- hasBody = true;
875
- }
876
- } else {
877
- throw new Error(`Route ${key} has no handler for ${verb} requests`);
769
+ for (const { id, importPath } of middleware) {
770
+ const importName = `middleware${id}`;
771
+ imports.writeLines(`import ${importName} from './${importPath}';`);
772
+ writer.writeLines(`export const mware${id} = normalize(${importName});`);
878
773
  }
879
- if (!hasBody) {
880
- let i = len;
881
- while (i--) {
882
- const { id } = middleware[i];
883
- const name = `mware${id}`;
884
- nextName = currentName;
885
- currentName = i ? `__${name}` : "";
886
- writeMiddleware(continuations, name, nextName, currentName);
774
+ imports.join();
775
+ return writer.end();
776
+ }
777
+ function stripTsExtension(path5) {
778
+ const index = path5.lastIndexOf(".");
779
+ if (index !== -1) {
780
+ const ext = path5.slice(index + 1);
781
+ if (ext.toLowerCase() === "ts") {
782
+ return path5.slice(0, index);
887
783
  }
888
784
  }
889
- continuations.join();
890
- writer.writeBlockEnd("}");
785
+ return path5;
891
786
  }
892
- function renderRouter(routes, entriesDir, options = {
893
- trailingSlashes: "RedirectWithout"
894
- }) {
787
+ async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
788
+ var _a, _b;
895
789
  const writer = createStringWriter();
896
- writer.writeLines(`// @marko/run/router`);
897
- const imports = writer.branch("imports");
898
- imports.writeLines(
899
- `import { NotHandled, NotMatched, createContext } from '${virtualFilePrefix}/runtime/internal';`
790
+ writer.writeLines(
791
+ `/*
792
+ WARNING: This file is automatically generated and any changes made to it will be overwritten without warning.
793
+ Do NOT manually edit this file or your changes will be lost.
794
+ */
795
+ `,
796
+ `import { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform } from "@marko/run/namespace";`,
797
+ `import type * as Run from "@marko/run";`
900
798
  );
901
- for (const route of routes.list) {
902
- const verbs = getVerbs(route);
903
- const names = verbs.map((verb) => `${verb}${route.index}`);
904
- route.meta && names.push(`meta${route.index}`);
905
- imports.writeLines(
906
- `import { ${names.join(", ")} } from '${virtualFilePrefix}/${route.entryName}.js';`
799
+ const headWriter = writer.branch("head");
800
+ writer.writeLines("\n").writeBlockStart(`declare module "@marko/run" {`);
801
+ if (adapter && adapter.typeInfo) {
802
+ const platformType = await adapter.typeInfo(
803
+ (data) => headWriter.write(data)
907
804
  );
805
+ if (platformType) {
806
+ writer.writeLines(`interface Platform extends ${platformType} {}
807
+ `);
808
+ }
908
809
  }
909
- for (const route of Object.values(routes.special)) {
910
- const importPath = route.layouts.length ? `./${path2.posix.join(entriesDir, route.page.relativePath, "..", `route.${route.key}.marko`)}` : `./${route.page.importPath}`;
911
- imports.writeLines(
912
- `import page${route.key} from '${importPath}${serverEntryQuery}';`
913
- );
810
+ headWriter.join();
811
+ writer.writeBlockStart(`interface AppData extends Run.DefineApp<{`).writeBlockStart("routes: {");
812
+ const routesWriter = writer.branch("routes");
813
+ writer.writeBlockEnd("}").writeBlockEnd(`}> {}`).writeBlockEnd(`}`);
814
+ const routeTypes = /* @__PURE__ */ new Map();
815
+ for (const route of routes.list) {
816
+ let routeType = "";
817
+ for (const path5 of route.paths) {
818
+ const pathType = `"${pathToURLPatternString(path5.path)}"`;
819
+ routeType += routeType ? " | " + pathType : pathType;
820
+ routesWriter.writeLines(`${pathType}: Routes["${route.key}"];`);
821
+ }
822
+ for (const file of [route.handler, route.page]) {
823
+ if (file) {
824
+ const existing = routeTypes.get(file);
825
+ if (!existing) {
826
+ routeTypes.set(file, [routeType]);
827
+ } else {
828
+ existing.push(routeType);
829
+ }
830
+ }
831
+ }
832
+ for (const files of [route.middleware, route.layouts]) {
833
+ if (files) {
834
+ for (const file of files) {
835
+ const existing = routeTypes.get(file);
836
+ if (!existing) {
837
+ routeTypes.set(file, [routeType]);
838
+ } else {
839
+ existing.push(routeType);
840
+ }
841
+ }
842
+ }
843
+ }
914
844
  }
915
- writer.writeLines(
916
- `
917
- globalThis.__marko_run__ = { match, fetch, invoke };
918
- `
919
- ).writeBlockStart(`export function match(method, pathname) {`).writeLines(
920
- `if (!pathname) {
921
- pathname = '/';
922
- } else if (pathname.charAt(0) !== '/') {
923
- pathname = '/' + pathname;
845
+ for (const special of Object.values(routes.special)) {
846
+ routeTypes.set(special.page, []);
847
+ }
848
+ routesWriter.join();
849
+ const handlerWriter = writer.branch("handler");
850
+ const middlewareWriter = writer.branch("middleware");
851
+ const pageWriter = writer.branch("page");
852
+ const layoutWriter = writer.branch("layout");
853
+ for (const [file, types] of routeTypes) {
854
+ const path5 = `${pathPrefix}/${file.relativePath}`;
855
+ const routeType = `Run.Routes[${types.join(" | ")}]`;
856
+ switch (file.type) {
857
+ case RoutableFileTypes.Handler:
858
+ writeModuleDeclaration(handlerWriter, path5, routeType);
859
+ break;
860
+ case RoutableFileTypes.Middleware:
861
+ writeModuleDeclaration(middlewareWriter, path5, routeType);
862
+ break;
863
+ case RoutableFileTypes.Page:
864
+ writeModuleDeclaration(pageWriter, path5, routeType);
865
+ break;
866
+ case RoutableFileTypes.Layout:
867
+ writeModuleDeclaration(
868
+ layoutWriter,
869
+ path5,
870
+ routeType,
871
+ `
872
+ export interface Input {
873
+ renderBody: Marko.Body;
924
874
  }`
925
- ).writeBlockStart(`switch (method) {`);
926
- for (const verb of httpVerbs) {
927
- const filteredRoutes = routes.list.filter((route) => hasVerb(route, verb));
928
- if (filteredRoutes.length) {
929
- const trie = createRouteTrie(filteredRoutes);
930
- writer.writeLines(`case '${verb.toUpperCase()}':`);
931
- writer.writeBlockStart(`case '${verb.toLowerCase()}': {`);
932
- writeRouterVerb(writer, trie, verb);
933
- writer.writeBlockEnd("}");
875
+ );
876
+ break;
877
+ case RoutableFileTypes.Error:
878
+ writeModuleDeclaration(
879
+ writer,
880
+ path5,
881
+ "globalThis.MarkoRun.Route",
882
+ `
883
+ export interface Input {
884
+ error: unknown;
885
+ }`
886
+ );
887
+ break;
888
+ case RoutableFileTypes.NotFound:
889
+ writeModuleDeclaration(writer, path5, "Run.Route");
890
+ break;
934
891
  }
935
892
  }
936
- writer.writeBlockEnd("}").writeLines("return null;").writeBlockEnd("}");
937
- writer.writeLines("").writeBlockStart(
938
- "export async function invoke(route, request, platform, url) {"
939
- ).writeLines(
940
- "const [context, buildInput] = createContext(route, request, platform, url);"
941
- );
942
- const hasErrorPage = Boolean(routes.special[RoutableFileTypes.Error]);
943
- if (hasErrorPage) {
944
- writer.writeBlockStart("try {");
945
- }
946
- writer.writeBlockStart("if (route) {").writeBlockStart("try {").writeLines(
947
- "const response = await route.handler(context, buildInput);",
948
- "if (response) return response;"
949
- ).indent--;
950
- writer.writeBlockStart("} catch (error) {").writeLines(
951
- "if (error === NotHandled) return;",
952
- "if (error !== NotMatched) throw error;"
953
- ).writeBlockEnd("}").writeBlockEnd("}");
954
- if (routes.special[RoutableFileTypes.NotFound]) {
955
- imports.writeLines(
956
- `
957
- const page404ResponseInit = {
958
- status: 404,
959
- headers: { "content-type": "text/html;charset=UTF-8" },
960
- };`
961
- );
962
- writer.write(`
963
- if (context.request.headers.get('Accept')?.includes('text/html')) {
964
- return new Response(page404.stream(buildInput()), page404ResponseInit);
893
+ handlerWriter.join();
894
+ middlewareWriter.join();
895
+ pageWriter.join();
896
+ layoutWriter.join();
897
+ writer.writeBlockStart(`
898
+ type Routes = {`);
899
+ for (const route of routes.list) {
900
+ const { meta, handler, page } = route;
901
+ if (page || handler) {
902
+ const verbs = [];
903
+ if (page || ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes("get"))) {
904
+ verbs.push(`"get"`);
905
+ }
906
+ if ((_b = handler == null ? void 0 : handler.verbs) == null ? void 0 : _b.includes("post")) {
907
+ verbs.push(`"post"`);
908
+ }
909
+ let routeType = `{ verb: ${verbs.join(" | ")};`;
910
+ if (meta) {
911
+ const metaPath = stripTsExtension(`${pathPrefix}/${meta.relativePath}`);
912
+ let metaType = `typeof import("${metaPath}")`;
913
+ if (/\.(ts|js|mjs)$/.test(meta.name)) {
914
+ metaType += `["default"]`;
915
+ }
916
+ routeType += ` meta: ${metaType};`;
917
+ }
918
+ writer.writeLines(`"${route.key}": ${routeType} };`);
965
919
  }
966
- `);
967
- }
968
- writer.indent--;
969
- if (hasErrorPage) {
970
- imports.writeLines(`
971
- const page500ResponseInit = {
972
- status: 500,
973
- headers: { "content-type": "text/html;charset=UTF-8" },
974
- };`);
975
- writer.writeBlockStart(`} catch (error) {`).writeBlockStart(
976
- `if (context.request.headers.get('Accept')?.includes('text/html')) {`
977
- ).writeLines(
978
- `return new Response(page500.stream(buildInput({ error })), page500ResponseInit);`
979
- ).writeBlockEnd("}").writeLines("throw error;").writeBlockEnd("}");
980
920
  }
981
921
  writer.writeBlockEnd("}");
982
- renderFetch(writer, options);
983
922
  return writer.end();
984
923
  }
985
- function renderFetch(writer, options) {
986
- writer.write(`
987
- export async function fetch(request, platform) {
988
- try {
989
- const url = new URL(request.url);
990
- let { pathname } = url;`);
991
- switch (options.trailingSlashes) {
992
- case "RedirectWithout":
993
- writer.write(`
994
- if (pathname !== '/' && pathname.endsWith('/')) {
995
- url.pathname = pathname.slice(0, -1);
996
- return Response.redirect(url);
997
- }`);
998
- break;
999
- case "RedirectWith":
1000
- writer.write(`
1001
- if (pathname !== '/' && !pathname.endsWith('/')) {
1002
- url.pathname = pathname + '/';
1003
- return Response.redirect(url);
1004
- }`);
1005
- break;
1006
- case "RewriteWithout":
1007
- writer.write(`
1008
- if (pathname !== '/' && pathname.endsWith('/')) {
1009
- url.pathname = pathname = pathname.slice(0, -1);
1010
- }`);
1011
- break;
1012
- case "RewriteWith":
1013
- writer.write(`
1014
- if (pathname !== '/' && !pathname.endsWith('/')) {
1015
- url.pathname = pathname = pathname + '/';
1016
- }`);
1017
- break;
924
+ function writeModuleDeclaration(writer, path5, routeType, moduleTypes) {
925
+ writer.writeLines("").write(`declare module "${stripTsExtension(path5)}" {`);
926
+ if (moduleTypes) {
927
+ writer.write(moduleTypes);
1018
928
  }
1019
- writer.write(`
1020
-
1021
- const route = match(request.method, pathname);
1022
- return await invoke(route, request, platform, url);
1023
- } catch (error) {
1024
- if (import.meta.env.DEV) {
1025
- throw error;
1026
- }
1027
- return new Response(null, {
1028
- status: 500
1029
- });
929
+ if (routeType) {
930
+ const isMarko = path5.endsWith(".marko");
931
+ writer.write(`
932
+ namespace MarkoRun {
933
+ export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
934
+ export type Route = ${routeType};
935
+ export type Context = Run.MultiRouteContext<Route>${isMarko ? " & Marko.Global" : ""};
936
+ export type Handler = Run.HandlerLike<Route>;
937
+ /** @deprecated use \`((context, next) => { ... }) satisfies MarkoRun.Handler\` instead */
938
+ export const route: Run.HandlerTypeFn<Route>;
939
+ }`);
1030
940
  }
941
+ writer.writeLines(`
1031
942
  }`);
1032
943
  }
1033
- function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
1034
- const { route, dynamic, catchAll } = trie;
1035
- let closeCount = 0;
1036
- if (level === 0) {
1037
- writer.writeLines(`const len = pathname.length;`);
1038
- if (route) {
1039
- writer.writeLines(
1040
- `if (len === 1) return ${renderMatch(verb, route, trie.path)}; // ${trie.path.path}`
1041
- );
1042
- } else if (trie.static || dynamic) {
1043
- writer.writeBlockStart(`if (len > 1) {`);
1044
- closeCount++;
944
+ function pathToURLPatternString(path5) {
945
+ return path5.replace(/\/\$(\$?)([^/]*)/g, (_, catchAll, name) => {
946
+ name = decodeURIComponent(name);
947
+ return catchAll ? `/:${name || "rest"}*` : `/:${name}`;
948
+ });
949
+ }
950
+ function createRouteTrie(routes) {
951
+ const root = {
952
+ key: ""
953
+ };
954
+ function insert(path5, route) {
955
+ let node = root;
956
+ for (const segment of path5.segments) {
957
+ if (segment === "$$") {
958
+ node.catchAll ?? (node.catchAll = { route, path: path5 });
959
+ return;
960
+ } else if (segment === "$") {
961
+ node = node.dynamic ?? (node.dynamic = {
962
+ key: ""
963
+ });
964
+ } else {
965
+ node.static ?? (node.static = /* @__PURE__ */ new Map());
966
+ let next = node.static.get(segment);
967
+ if (!next) {
968
+ next = {
969
+ key: segment
970
+ };
971
+ node.static.set(segment, next);
972
+ }
973
+ node = next;
974
+ }
1045
975
  }
976
+ node.path ?? (node.path = path5);
977
+ node.route ?? (node.route = route);
1046
978
  }
1047
- if (trie.static || dynamic) {
1048
- const next = level + 1;
1049
- const index = `i${next}`;
1050
- let terminal;
1051
- let children;
1052
- writer.writeLines(`const ${index} = pathname.indexOf('/', ${offset}) + 1;`);
1053
- if (trie.static) {
1054
- for (const child of trie.static.values()) {
1055
- if (child.route) {
1056
- (terminal ?? (terminal = [])).push(child);
979
+ for (const route of routes) {
980
+ for (const path5 of route.paths) {
981
+ insert(path5, route);
982
+ }
983
+ }
984
+ return root;
985
+ }
986
+
987
+ // src/vite/routes/parse.ts
988
+ function parseFlatRoute(pattern) {
989
+ if (!pattern) throw new Error("Empty pattern");
990
+ const len = pattern.length;
991
+ let i = 0;
992
+ return parse2([
993
+ {
994
+ id: "/",
995
+ segments: [],
996
+ source: pattern
997
+ }
998
+ ]);
999
+ function parse2(basePaths, group) {
1000
+ const pathMap = /* @__PURE__ */ new Map();
1001
+ const delimiters = group ? ").," : ".,";
1002
+ let charCode;
1003
+ let segmentStart = i;
1004
+ let type;
1005
+ let current;
1006
+ do {
1007
+ charCode = pattern.charCodeAt(i);
1008
+ if (charCode === 41 && group) {
1009
+ break;
1010
+ } else if (charCode === 44) {
1011
+ if (!current) {
1012
+ segmentEnd(
1013
+ basePaths.map((path5) => ({
1014
+ ...path5,
1015
+ segments: path5.segments.slice()
1016
+ })),
1017
+ "",
1018
+ "_",
1019
+ pathMap
1020
+ );
1021
+ } else {
1022
+ segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
1057
1023
  }
1058
- if (child.static || child.dynamic || child.catchAll) {
1059
- (children ?? (children = [])).push(child);
1024
+ current = void 0;
1025
+ type = void 0;
1026
+ segmentStart = ++i;
1027
+ } else if (charCode === 46) {
1028
+ if (current) {
1029
+ segmentEnd(current, pattern.slice(segmentStart, i), type);
1060
1030
  }
1061
- }
1062
- }
1063
- if (terminal || (dynamic == null ? void 0 : dynamic.route)) {
1064
- closeCount++;
1065
- writer.writeBlockStart(`if (!${index} || ${index} === len) {`);
1066
- let value = `pathname.slice(${offset}, ${index} ? -1 : len)`;
1067
- if (dynamic == null ? void 0 : dynamic.route) {
1068
- const segment = `s${next}`;
1069
- writer.writeLines(`const ${segment} = decodeURIComponent(${value});`);
1070
- value = segment;
1071
- } else if (terminal == null ? void 0 : terminal.some(
1072
- (terminal2) => decodeURIComponent(terminal2.key) !== terminal2.key
1073
- )) {
1074
- value = `decodeURIComponent(${value})`;
1075
- }
1076
- if (terminal) {
1077
- const useSwitch = terminal.length > 1;
1078
- if (useSwitch) {
1079
- writer.writeBlockStart(`switch (${value}) {`);
1031
+ type = void 0;
1032
+ segmentStart = ++i;
1033
+ } else if (charCode === 40) {
1034
+ const groupPaths = parse2(current || basePaths, ++i);
1035
+ if (groupPaths.length) {
1036
+ current = groupPaths;
1080
1037
  }
1081
- for (const { key, path: path5, route: route2 } of terminal) {
1082
- const decodedKey = decodeURIComponent(key);
1083
- if (useSwitch) {
1084
- writer.write(`case '${decodedKey}': `, true);
1085
- } else {
1086
- writer.write(`if (${value} === '${decodedKey}') `, true);
1087
- }
1088
- writer.write(
1089
- `return ${renderMatch(verb, route2, path5)}; // ${path5.path}
1090
- `
1091
- );
1038
+ segmentStart = ++i;
1039
+ } else {
1040
+ if (charCode === 95) {
1041
+ type = "_";
1042
+ } else if (charCode === 36) {
1043
+ type = pattern.charCodeAt(i + 1) === 36 ? "$$" : "$";
1092
1044
  }
1093
- if (useSwitch) {
1094
- writer.writeBlockEnd("}");
1045
+ current ?? (current = basePaths.map((path5) => ({
1046
+ ...path5,
1047
+ segments: path5.segments.slice()
1048
+ })));
1049
+ i = len;
1050
+ for (const char of delimiters) {
1051
+ const index = pattern.indexOf(char, segmentStart);
1052
+ if (index >= 0 && index < i) {
1053
+ i = index;
1054
+ }
1095
1055
  }
1096
1056
  }
1097
- if (dynamic == null ? void 0 : dynamic.route) {
1098
- writer.writeLines(
1099
- `if (${value}) return ${renderMatch(
1100
- verb,
1101
- dynamic.route,
1102
- dynamic.path
1103
- )}; // ${dynamic.path.path}`
1104
- );
1105
- }
1057
+ } while (i < len);
1058
+ if (group && charCode !== 41) {
1059
+ throw new Error(
1060
+ `Invalid route pattern: group was not closed '${pattern.slice(
1061
+ group
1062
+ )}' in '${pattern}'`
1063
+ );
1064
+ }
1065
+ if (!current) {
1066
+ segmentEnd(
1067
+ basePaths.map((path5) => ({
1068
+ ...path5,
1069
+ segments: path5.segments.slice()
1070
+ })),
1071
+ "",
1072
+ "_",
1073
+ pathMap
1074
+ );
1075
+ } else {
1076
+ segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
1106
1077
  }
1107
- if (children || (dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
1108
- if (terminal || (dynamic == null ? void 0 : dynamic.route)) {
1109
- writer.writeBlockEnd("} else {").indent++;
1110
- } else {
1111
- writer.writeBlockStart(`if (${index} && ${index} !== len) {`);
1112
- closeCount++;
1113
- }
1114
- let value = `pathname.slice(${offset}, ${index} - 1)`;
1115
- if ((dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
1116
- const segment = `s${next}`;
1117
- writer.writeLines(`const ${segment} = decodeURIComponent(${value});`);
1118
- value = segment;
1119
- } else if (children == null ? void 0 : children.some((child) => decodeURIComponent(child.key) !== child.key)) {
1120
- value = `decodeURIComponent(${value})`;
1078
+ return [...pathMap.values()];
1079
+ }
1080
+ function segmentEnd(paths, raw, type, map) {
1081
+ let segment;
1082
+ if (raw) {
1083
+ segment = {
1084
+ raw,
1085
+ name: raw,
1086
+ type
1087
+ };
1088
+ if (type === "$" || type === "$$") {
1089
+ segment.name = type;
1090
+ segment.param = raw.slice(type.length);
1121
1091
  }
1122
- if (children) {
1123
- const useSwitch = children.length > 1;
1124
- if (useSwitch) {
1125
- writer.writeBlockStart(`switch (${value}) {`);
1126
- }
1127
- for (const child of children) {
1128
- const decodedKey = decodeURIComponent(child.key);
1129
- if (useSwitch) {
1130
- writer.writeBlockStart(`case '${decodedKey}': {`);
1131
- } else {
1132
- writer.writeBlockStart(`if (${value} === '${decodedKey}') {`);
1133
- }
1134
- const nextOffset = typeof offset === "string" ? index : offset + child.key.length + 1;
1135
- writeRouterVerb(writer, child, verb, next, nextOffset);
1136
- if (useSwitch) {
1137
- writer.writeBlockEnd("} break;");
1138
- } else {
1139
- writer.writeBlockEnd("}");
1140
- }
1092
+ }
1093
+ for (const path5 of paths) {
1094
+ if (segment) {
1095
+ if (path5.isCatchall) {
1096
+ throw new Error(
1097
+ `Invalid route pattern: nested segments are not allowed after a catch-all parameter. Found '.' following '${pattern.slice(
1098
+ 0,
1099
+ i
1100
+ )}' in '${pattern}'.`
1101
+ );
1141
1102
  }
1142
- if (useSwitch) {
1143
- writer.writeBlockEnd("}");
1103
+ path5.segments.push(segment);
1104
+ path5.id += path5.id === "/" ? segment.name : `/${segment.name}`;
1105
+ if (type === "$$") {
1106
+ path5.isCatchall = true;
1144
1107
  }
1145
1108
  }
1146
- if ((dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
1147
- writer.writeBlockStart(`if (${value}) {`);
1148
- writeRouterVerb(writer, dynamic, verb, next, index);
1149
- writer.writeBlockEnd(`}`);
1109
+ if (map) {
1110
+ if (map.has(path5.id)) {
1111
+ const existing = map.get(path5.id);
1112
+ const existingExpansion = existing.segments.map((s) => s.raw).join(".");
1113
+ const currentExpansion = path5.segments.map((s) => s.raw).join(".");
1114
+ throw new Error(
1115
+ `Invalid route pattern: route '${path5.id}' is ambiguous. Expansion '${currentExpansion}' collides with '${existingExpansion}' in '${pattern}'.`
1116
+ );
1117
+ }
1118
+ map.set(path5.id, path5);
1150
1119
  }
1151
1120
  }
1152
1121
  }
1153
- while (closeCount--) {
1154
- writer.writeBlockEnd("}");
1155
- }
1156
- if (catchAll) {
1157
- writer.writeLines(
1158
- `return ${renderMatch(
1159
- verb,
1160
- catchAll.route,
1161
- catchAll.path,
1162
- String(offset)
1163
- )}; // ${catchAll.path.path}`
1164
- );
1165
- } else if (level === 0) {
1166
- writer.writeLines("return null;");
1167
- }
1168
- }
1169
- function wrapPropertyName(name) {
1170
- name = decodeURIComponent(name);
1171
- return /^[^A-Za-z_$]|[^A-Za-z0-9$_]/.test(name) ? `'${name}'` : name;
1172
1122
  }
1173
- function renderParams(params, pathIndex) {
1174
- let result = "";
1175
- let catchAll = "";
1176
- let sep = "{";
1177
- for (const [name, index] of Object.entries(params)) {
1178
- if (typeof index === "number") {
1179
- result += `${sep} ${wrapPropertyName(name)}: s${index + 1}`;
1180
- sep = ",";
1181
- } else if (pathIndex) {
1182
- catchAll = name;
1123
+
1124
+ // src/vite/routes/vdir.ts
1125
+ var _dirs, _pathlessDirs;
1126
+ var _VDir = class _VDir {
1127
+ constructor(parent, segment, source) {
1128
+ __privateAdd(this, _dirs);
1129
+ __privateAdd(this, _pathlessDirs);
1130
+ __publicField(this, "parent");
1131
+ __publicField(this, "source");
1132
+ __publicField(this, "path");
1133
+ __publicField(this, "fullPath");
1134
+ __publicField(this, "segment");
1135
+ __publicField(this, "files");
1136
+ if (!parent || !segment) {
1137
+ this.parent = null;
1138
+ this.source = null;
1139
+ this.path = "/";
1140
+ this.fullPath = "/";
1141
+ this.segment = {
1142
+ raw: "",
1143
+ name: ""
1144
+ };
1145
+ } else {
1146
+ this.parent = parent;
1147
+ this.source = source;
1148
+ this.path = parent.path + (parent.path === "/" ? segment.name : `/${segment.name}`);
1149
+ this.fullPath = parent.fullPath + (parent.fullPath === "/" ? segment.name : `/${segment.name}`);
1150
+ if (segment.param) {
1151
+ this.fullPath += segment.param;
1152
+ }
1153
+ this.segment = segment;
1183
1154
  }
1184
1155
  }
1185
- if (catchAll) {
1186
- result += `${sep} ${wrapPropertyName(
1187
- catchAll
1188
- )}: pathname.slice(${pathIndex})`;
1189
- }
1190
- return result ? result + " }" : "{}";
1191
- }
1192
- function renderMatch(verb, route, path5, pathIndex) {
1193
- const handler = `${verb}${route.index}`;
1194
- const params = path5.params ? renderParams(path5.params, pathIndex) : "{}";
1195
- const meta = route.meta ? `meta${route.index}` : "{}";
1196
- const pathPattern = pathToURLPatternString(path5.path);
1197
- return `{ handler: ${handler}, params: ${params}, meta: ${meta}, path: '${pathPattern}' }`;
1198
- }
1199
- function renderMiddleware(middleware) {
1200
- const writer = createStringWriter();
1201
- writer.writeLines(
1202
- `// ${virtualFilePrefix}/${markoRunFilePrefix}middleware.js`
1203
- );
1204
- const imports = writer.branch("imports");
1205
- imports.writeLines(
1206
- `import { normalize } from '${virtualFilePrefix}/runtime/internal';`
1207
- );
1208
- writer.writeLines("");
1209
- for (const { id, importPath } of middleware) {
1210
- const importName = `middleware${id}`;
1211
- imports.writeLines(`import ${importName} from './${importPath}';`);
1212
- writer.writeLines(`export const mware${id} = normalize(${importName});`);
1156
+ get pathInfo() {
1157
+ const value = {
1158
+ id: "/",
1159
+ path: "/",
1160
+ segments: []
1161
+ };
1162
+ let sep = "";
1163
+ for (const { segment } of this) {
1164
+ const { type, name, param } = segment;
1165
+ if (name && type !== "_") {
1166
+ value.id += sep + (type || name);
1167
+ value.path += sep + name;
1168
+ value.isEnd = type === "$$";
1169
+ if (param) {
1170
+ value.path += param;
1171
+ const index = type === "$$" ? null : value.segments.length;
1172
+ if (!value.params) {
1173
+ value.params = { [param]: index };
1174
+ } else if (!(param in value.params)) {
1175
+ value.params[param] = index;
1176
+ }
1177
+ }
1178
+ value.segments.push(name);
1179
+ sep = "/";
1180
+ }
1181
+ }
1182
+ Object.defineProperty(this, "pathInfo", {
1183
+ value,
1184
+ enumerable: true
1185
+ });
1186
+ return value;
1213
1187
  }
1214
- imports.join();
1215
- return writer.end();
1216
- }
1217
- function stripTsExtension(path5) {
1218
- const index = path5.lastIndexOf(".");
1219
- if (index !== -1) {
1220
- const ext = path5.slice(index + 1);
1221
- if (ext.toLowerCase() === "ts") {
1222
- return path5.slice(0, index);
1188
+ addDir(path5, segment) {
1189
+ const map = segment.type === "_" ? __privateGet(this, _pathlessDirs) ?? __privateSet(this, _pathlessDirs, /* @__PURE__ */ new Map()) : __privateGet(this, _dirs) ?? __privateSet(this, _dirs, /* @__PURE__ */ new Map());
1190
+ if (!map.has(segment.name)) {
1191
+ const dir = new _VDir(this, segment, path5);
1192
+ map.set(segment.name, dir);
1193
+ return dir;
1223
1194
  }
1195
+ return map.get(segment.name);
1224
1196
  }
1225
- return path5;
1226
- }
1227
- async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
1228
- var _a, _b;
1229
- const writer = createStringWriter();
1230
- writer.writeLines(
1231
- `/*
1232
- WARNING: This file is automatically generated and any changes made to it will be overwritten without warning.
1233
- Do NOT manually edit this file or your changes will be lost.
1234
- */
1235
- `,
1236
- `import { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform } from "@marko/run/namespace";`,
1237
- `import type * as Run from "@marko/run";`
1238
- );
1239
- const headWriter = writer.branch("head");
1240
- writer.writeLines("\n").writeBlockStart(`declare module "@marko/run" {`);
1241
- if (adapter && adapter.typeInfo) {
1242
- const platformType = await adapter.typeInfo(
1243
- (data) => headWriter.write(data)
1244
- );
1245
- if (platformType) {
1246
- writer.writeLines(`interface Platform extends ${platformType} {}
1247
- `);
1197
+ addFile(file) {
1198
+ if (!this.files) {
1199
+ this.files = /* @__PURE__ */ new Map();
1200
+ this.files.set(file.type, file);
1201
+ } else if (!this.files.has(file.type)) {
1202
+ this.files.set(file.type, file);
1203
+ } else {
1204
+ const existing = this.files.get(file.type);
1205
+ if (existing !== file) {
1206
+ throw new Error(
1207
+ `Duplicate file type '${file.type}' added at path '${this.path}'. File '${file.importPath}' collides with '${existing.importPath}'.`
1208
+ );
1209
+ } else if (file.type === RoutableFileTypes.Page || file.type === RoutableFileTypes.Handler) {
1210
+ throw new Error(
1211
+ `Ambiguous path definition: route '${this.path}' is defined multiple times by ${file.importPath}`
1212
+ );
1213
+ }
1214
+ throw new Error(
1215
+ `Ambiguous path definition: file '${this.path}' is included multiple times by ${file.importPath}`
1216
+ );
1248
1217
  }
1249
1218
  }
1250
- headWriter.join();
1251
- writer.writeBlockStart(`interface AppData extends Run.DefineApp<{`).writeBlockStart("routes: {");
1252
- const routesWriter = writer.branch("routes");
1253
- writer.writeBlockEnd("}").writeBlockEnd(`}> {}`).writeBlockEnd(`}`);
1254
- const routeTypes = /* @__PURE__ */ new Map();
1255
- for (const route of routes.list) {
1256
- let routeType = "";
1257
- for (const path5 of route.paths) {
1258
- const pathType = `"${pathToURLPatternString(path5.path)}"`;
1259
- routeType += routeType ? " | " + pathType : pathType;
1260
- routesWriter.writeLines(`${pathType}: Routes["${route.key}"];`);
1219
+ *dirs() {
1220
+ if (__privateGet(this, _pathlessDirs)) {
1221
+ yield* __privateGet(this, _pathlessDirs).values();
1261
1222
  }
1262
- for (const file of [route.handler, route.page]) {
1263
- if (file) {
1264
- const existing = routeTypes.get(file);
1265
- if (!existing) {
1266
- routeTypes.set(file, [routeType]);
1267
- } else {
1268
- existing.push(routeType);
1269
- }
1270
- }
1223
+ if (__privateGet(this, _dirs)) {
1224
+ yield* __privateGet(this, _dirs).values();
1271
1225
  }
1272
- for (const files of [route.middleware, route.layouts]) {
1273
- if (files) {
1274
- for (const file of files) {
1275
- const existing = routeTypes.get(file);
1276
- if (!existing) {
1277
- routeTypes.set(file, [routeType]);
1278
- } else {
1279
- existing.push(routeType);
1226
+ }
1227
+ *[Symbol.iterator]() {
1228
+ if (this.parent) {
1229
+ yield* this.parent;
1230
+ }
1231
+ yield this;
1232
+ }
1233
+ static addPaths(roots, paths) {
1234
+ const dirs = [];
1235
+ const unique = /* @__PURE__ */ new Set();
1236
+ for (const root of roots) {
1237
+ for (const path5 of paths) {
1238
+ let dir = root;
1239
+ for (const segment of path5.segments) {
1240
+ dir = dir.addDir(path5, segment);
1241
+ }
1242
+ if (unique.has(dir.path)) {
1243
+ const sources = /* @__PURE__ */ new Set();
1244
+ let sourcePath = "";
1245
+ for (const { source } of dir) {
1246
+ if (source && !sources.has(source.source)) {
1247
+ sources.add(source.source);
1248
+ sourcePath += source.source + "/";
1249
+ }
1280
1250
  }
1251
+ throw new Error(
1252
+ `Ambiguous directory structure: '${sourcePath}${path5.source}' defines '${dir.path}' multiple times.`
1253
+ );
1254
+ } else {
1255
+ unique.add(dir.path);
1256
+ dirs.push(dir);
1281
1257
  }
1282
1258
  }
1283
1259
  }
1260
+ return dirs;
1284
1261
  }
1285
- for (const special of Object.values(routes.special)) {
1286
- routeTypes.set(special.page, []);
1287
- }
1288
- routesWriter.join();
1289
- const handlerWriter = writer.branch("handler");
1290
- const middlewareWriter = writer.branch("middleware");
1291
- const pageWriter = writer.branch("page");
1292
- const layoutWriter = writer.branch("layout");
1293
- for (const [file, types] of routeTypes) {
1294
- const path5 = `${pathPrefix}/${file.relativePath}`;
1295
- const routeType = `Run.Routes[${types.join(" | ")}]`;
1296
- switch (file.type) {
1297
- case RoutableFileTypes.Handler:
1298
- writeModuleDeclaration(handlerWriter, path5, routeType);
1299
- break;
1300
- case RoutableFileTypes.Middleware:
1301
- writeModuleDeclaration(middlewareWriter, path5, routeType);
1302
- break;
1303
- case RoutableFileTypes.Page:
1304
- writeModuleDeclaration(pageWriter, path5, routeType);
1305
- break;
1306
- case RoutableFileTypes.Layout:
1307
- writeModuleDeclaration(
1308
- layoutWriter,
1309
- path5,
1310
- routeType,
1311
- `
1312
- export interface Input {
1313
- renderBody: Marko.Body;
1314
- }`
1315
- );
1316
- break;
1317
- case RoutableFileTypes.Error:
1318
- writeModuleDeclaration(
1319
- writer,
1320
- path5,
1321
- "globalThis.MarkoRun.Route",
1322
- `
1323
- export interface Input {
1324
- error: unknown;
1325
- }`
1262
+ };
1263
+ _dirs = new WeakMap();
1264
+ _pathlessDirs = new WeakMap();
1265
+ var VDir = _VDir;
1266
+
1267
+ // src/vite/routes/builder.ts
1268
+ var markoFiles = `(${RoutableFileTypes.Layout}|${RoutableFileTypes.Page}|${RoutableFileTypes.NotFound}|${RoutableFileTypes.Error})\\.(?:.*\\.)?(marko)`;
1269
+ var nonMarkoFiles = `(${RoutableFileTypes.Middleware}|${RoutableFileTypes.Handler}|${RoutableFileTypes.Meta})\\.(?:.*\\.)?(.+)`;
1270
+ var routeableFileRegex = new RegExp(
1271
+ `[+](?:${markoFiles}|${nonMarkoFiles})$`,
1272
+ "i"
1273
+ );
1274
+ function matchRoutableFile(filename) {
1275
+ const match = filename.match(routeableFileRegex);
1276
+ return match && (match[1] || match[3]).toLowerCase();
1277
+ }
1278
+ function isSpecialType(type) {
1279
+ return type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error;
1280
+ }
1281
+ async function buildRoutes(sources) {
1282
+ const uniqueRoutes = /* @__PURE__ */ new Map();
1283
+ const routes = [];
1284
+ const special = {};
1285
+ const middlewares = /* @__PURE__ */ new Set();
1286
+ const unusedFiles = /* @__PURE__ */ new Set();
1287
+ const currentLayouts = /* @__PURE__ */ new Set();
1288
+ const currentMiddleware = /* @__PURE__ */ new Set();
1289
+ const root = new VDir();
1290
+ const dirStack = [];
1291
+ let basePath;
1292
+ let importPrefix;
1293
+ let activeDirs;
1294
+ let isBaseDir;
1295
+ let nextFileId = 1;
1296
+ let nextRouteIndex = 1;
1297
+ const walkOptions = {
1298
+ onEnter({ name }) {
1299
+ const prevDirStackLength = dirStack.length;
1300
+ if (isBaseDir) {
1301
+ isBaseDir = false;
1302
+ if (!basePath) {
1303
+ return;
1304
+ }
1305
+ name = basePath;
1306
+ } else {
1307
+ dirStack.push(name);
1308
+ }
1309
+ const previousDirs = activeDirs;
1310
+ const paths = parseFlatRoute(name);
1311
+ activeDirs = VDir.addPaths(previousDirs, paths);
1312
+ return () => {
1313
+ activeDirs = previousDirs;
1314
+ dirStack.length = prevDirStackLength;
1315
+ };
1316
+ },
1317
+ onFile({ name, path: path5 }) {
1318
+ const match = name.match(routeableFileRegex);
1319
+ if (!match) {
1320
+ return;
1321
+ }
1322
+ const type = (match[1] || match[3]).toLowerCase();
1323
+ if (dirStack.length && isSpecialType(type)) {
1324
+ console.warn(
1325
+ `Special pages '${RoutableFileTypes.NotFound}' and '${RoutableFileTypes.Error}' are only considered in the root directory - ignoring ${path5}`
1326
1326
  );
1327
- break;
1328
- case RoutableFileTypes.NotFound:
1329
- writeModuleDeclaration(writer, path5, "Run.Route");
1330
- break;
1327
+ return;
1328
+ }
1329
+ let dirs = activeDirs;
1330
+ if (match.index) {
1331
+ const paths = parseFlatRoute(name.slice(0, match.index));
1332
+ dirs = VDir.addPaths(activeDirs, paths);
1333
+ }
1334
+ const dirPath = dirStack.join("/");
1335
+ const relativePath = dirPath ? `${dirPath}/${name}` : name;
1336
+ const file = {
1337
+ id: String(nextFileId++),
1338
+ name,
1339
+ type,
1340
+ filePath: path5,
1341
+ relativePath,
1342
+ importPath: `${importPrefix}/${relativePath}`,
1343
+ verbs: type === RoutableFileTypes.Page ? ["get", "head"] : void 0
1344
+ };
1345
+ for (const dir of dirs) {
1346
+ dir.addFile(file);
1347
+ }
1331
1348
  }
1349
+ };
1350
+ if (!Array.isArray(sources)) {
1351
+ sources = [sources];
1332
1352
  }
1333
- handlerWriter.join();
1334
- middlewareWriter.join();
1335
- pageWriter.join();
1336
- layoutWriter.join();
1337
- writer.writeBlockStart(`
1338
- type Routes = {`);
1339
- for (const route of routes.list) {
1340
- const { meta, handler, page } = route;
1341
- if (page || handler) {
1342
- const verbs = [];
1343
- if (page || ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes("get"))) {
1344
- verbs.push(`"get"`);
1353
+ for (const source of sources) {
1354
+ importPrefix = source.importPrefix ? source.importPrefix.replace(/^\/+|\/+$/g, "") : "";
1355
+ basePath = source.basePath || "";
1356
+ activeDirs = [root];
1357
+ isBaseDir = true;
1358
+ await source.walker(walkOptions);
1359
+ }
1360
+ traverse(root);
1361
+ return {
1362
+ list: routes,
1363
+ middleware: [...middlewares],
1364
+ special
1365
+ };
1366
+ function traverse(dir) {
1367
+ let middleware;
1368
+ let layout;
1369
+ if (dir.files) {
1370
+ middleware = dir.files.get(RoutableFileTypes.Middleware);
1371
+ layout = dir.files.get(RoutableFileTypes.Layout);
1372
+ const handler = dir.files.get(RoutableFileTypes.Handler);
1373
+ const page = dir.files.get(RoutableFileTypes.Page);
1374
+ let hasSpecial = false;
1375
+ if (middleware) {
1376
+ if (currentMiddleware.has(middleware)) {
1377
+ middleware = void 0;
1378
+ } else {
1379
+ currentMiddleware.add(middleware);
1380
+ unusedFiles.add(middleware);
1381
+ }
1382
+ }
1383
+ if (layout) {
1384
+ if (currentLayouts.has(layout)) {
1385
+ layout = void 0;
1386
+ } else {
1387
+ currentLayouts.add(layout);
1388
+ unusedFiles.add(layout);
1389
+ }
1390
+ }
1391
+ if (page || handler) {
1392
+ const path5 = dir.pathInfo;
1393
+ if (uniqueRoutes.has(path5.id)) {
1394
+ const existing = uniqueRoutes.get(path5.id);
1395
+ const route = routes[existing.index];
1396
+ const existingFiles = [route.handler, route.page].filter(Boolean).map((f) => f.filePath);
1397
+ const currentFiles = [handler, page].filter(Boolean).map((f) => f.filePath);
1398
+ throw new Error(`Duplicate routes for path '${path5.path}' were defined. A route established by:
1399
+ ${existingFiles.join(" and ")} via '${existing.dir.path}'
1400
+ collides with
1401
+ ${currentFiles.join(" and ")} via '${dir.path}'
1402
+ `);
1403
+ }
1404
+ uniqueRoutes.set(path5.id, { dir, index: routes.length });
1405
+ routes.push({
1406
+ index: nextRouteIndex++,
1407
+ key: dir.fullPath,
1408
+ paths: [path5],
1409
+ middleware: [...currentMiddleware],
1410
+ layouts: page ? [...currentLayouts] : [],
1411
+ meta: dir.files.get(RoutableFileTypes.Meta),
1412
+ page,
1413
+ handler,
1414
+ entryName: `${markoRunFilePrefix}route` + (dir.path !== "/" ? dir.fullPath.replace(/\//g, ".").replace(/(%[A-Fa-f0-9]{2})+/g, "_") : "")
1415
+ });
1345
1416
  }
1346
- if ((_b = handler == null ? void 0 : handler.verbs) == null ? void 0 : _b.includes("post")) {
1347
- verbs.push(`"post"`);
1417
+ if (dir === root) {
1418
+ for (const [type, file] of dir.files) {
1419
+ if (isSpecialType(type)) {
1420
+ hasSpecial = true;
1421
+ special[type] = {
1422
+ index: 0,
1423
+ key: type,
1424
+ paths: [],
1425
+ middleware: [],
1426
+ layouts: [...currentLayouts],
1427
+ page: file,
1428
+ entryName: `${markoRunFilePrefix}special.${type}`
1429
+ };
1430
+ }
1431
+ }
1348
1432
  }
1349
- let routeType = `{ verb: ${verbs.join(" | ")};`;
1350
- if (meta) {
1351
- const metaPath = stripTsExtension(`${pathPrefix}/${meta.relativePath}`);
1352
- let metaType = `typeof import("${metaPath}")`;
1353
- if (/\.(ts|js|mjs)$/.test(meta.name)) {
1354
- metaType += `["default"]`;
1433
+ if (handler || page) {
1434
+ for (const middleware2 of currentMiddleware) {
1435
+ middlewares.add(middleware2);
1436
+ unusedFiles.delete(middleware2);
1437
+ }
1438
+ }
1439
+ if (page || hasSpecial) {
1440
+ for (const layout2 of currentLayouts) {
1441
+ unusedFiles.delete(layout2);
1355
1442
  }
1356
- routeType += ` meta: ${metaType};`;
1357
1443
  }
1358
- writer.writeLines(`"${route.key}": ${routeType} };`);
1444
+ }
1445
+ if (dir.dirs) {
1446
+ for (const child of dir.dirs()) {
1447
+ traverse(child);
1448
+ }
1449
+ }
1450
+ if (middleware) {
1451
+ currentMiddleware.delete(middleware);
1452
+ }
1453
+ if (layout) {
1454
+ currentLayouts.delete(layout);
1359
1455
  }
1360
1456
  }
1361
- writer.writeBlockEnd("}");
1362
- return writer.end();
1363
- }
1364
- function writeModuleDeclaration(writer, path5, routeType, moduleTypes) {
1365
- writer.writeLines("").write(`declare module "${stripTsExtension(path5)}" {`);
1366
- if (moduleTypes) {
1367
- writer.write(moduleTypes);
1368
- }
1369
- if (routeType) {
1370
- const isMarko = path5.endsWith(".marko");
1371
- writer.write(`
1372
- namespace MarkoRun {
1373
- export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
1374
- export type Route = ${routeType};
1375
- export type Context = Run.MultiRouteContext<Route>${isMarko ? " & Marko.Global" : ""};
1376
- export type Handler = Run.HandlerLike<Route>;
1377
- /** @deprecated use \`((context, next) => { ... }) satisfies MarkoRun.Handler\` instead */
1378
- export const route: Run.HandlerTypeFn<Route>;
1379
- }`);
1380
- }
1381
- writer.writeLines(`
1382
- }`);
1383
- }
1384
- function pathToURLPatternString(path5) {
1385
- return path5.replace(/\/\$(\$?)([^\/]*)/g, (_, catchAll, name) => {
1386
- name = decodeURIComponent(name);
1387
- return catchAll ? `/:${name || "rest"}*` : `/:${name}`;
1388
- });
1389
1457
  }
1390
- function createRouteTrie(routes) {
1391
- const root = {
1392
- key: ""
1393
- };
1394
- function insert(path5, route) {
1395
- let node = root;
1396
- for (const segment of path5.segments) {
1397
- if (segment === "$$") {
1398
- node.catchAll ?? (node.catchAll = { route, path: path5 });
1399
- return;
1400
- } else if (segment === "$") {
1401
- node = node.dynamic ?? (node.dynamic = {
1402
- key: ""
1458
+
1459
+ // src/vite/routes/walk.ts
1460
+ import fs from "fs";
1461
+ import path2 from "path";
1462
+ function createFSWalker(dir) {
1463
+ return async function walkFS({
1464
+ onEnter,
1465
+ onFile,
1466
+ onDir,
1467
+ maxDepth = 50
1468
+ }) {
1469
+ async function walk(dir2, depth) {
1470
+ const onExit = onEnter == null ? void 0 : onEnter(dir2);
1471
+ if (onExit !== false) {
1472
+ const dirs = [];
1473
+ const entries = await fs.promises.readdir(dir2.path, {
1474
+ withFileTypes: true
1403
1475
  });
1404
- } else {
1405
- node.static ?? (node.static = /* @__PURE__ */ new Map());
1406
- let next = node.static.get(segment);
1407
- if (!next) {
1408
- next = {
1409
- key: segment
1476
+ const prefix = dir2.path + path2.sep;
1477
+ for (const entry of entries) {
1478
+ const walkEntry = {
1479
+ name: entry.name,
1480
+ path: prefix + entry.name
1410
1481
  };
1411
- node.static.set(segment, next);
1482
+ if (entry.isDirectory()) {
1483
+ dirs.push(walkEntry);
1484
+ } else {
1485
+ onFile == null ? void 0 : onFile(walkEntry);
1486
+ }
1412
1487
  }
1413
- node = next;
1488
+ if ((onDir == null ? void 0 : onDir()) !== false && --depth > 0) {
1489
+ for (const entry of dirs) {
1490
+ await walk(entry, depth);
1491
+ }
1492
+ }
1493
+ onExit == null ? void 0 : onExit();
1414
1494
  }
1415
1495
  }
1416
- node.path ?? (node.path = path5);
1417
- node.route ?? (node.route = route);
1418
- }
1419
- for (const route of routes) {
1420
- for (const path5 of route.paths) {
1421
- insert(path5, route);
1422
- }
1423
- }
1424
- return root;
1496
+ await walk(
1497
+ {
1498
+ path: dir,
1499
+ name: path2.basename(dir)
1500
+ },
1501
+ maxDepth
1502
+ );
1503
+ };
1425
1504
  }
1426
1505
 
1427
1506
  // src/vite/utils/ast.ts
@@ -1480,25 +1559,38 @@ function getViteSSRExportIdentifiers(astProgramNode, exportObjectName = "__vite_
1480
1559
  return result;
1481
1560
  }
1482
1561
 
1562
+ // src/vite/utils/config.ts
1563
+ var PluginConfigKey = "__MARKO_RUN_PLUGIN_CONFIG__";
1564
+ var AdapterConfigKey = "__MARKO_RUN_ADAPTER_CONFIG__";
1565
+ function getConfig(obj, key) {
1566
+ return obj[key];
1567
+ }
1568
+ function setConfig(obj, key, value) {
1569
+ obj[key] = value;
1570
+ return obj;
1571
+ }
1572
+ var getExternalPluginOptions = (viteConfig) => getConfig(viteConfig, PluginConfigKey);
1573
+ var setExternalPluginOptions = (viteConfig, value) => setConfig(viteConfig, PluginConfigKey, value);
1574
+ var getExternalAdapterOptions = (viteConfig) => getConfig(viteConfig, AdapterConfigKey);
1575
+
1483
1576
  // src/vite/utils/log.ts
1484
1577
  import zlib from "node:zlib";
1578
+ import { Blob } from "buffer";
1485
1579
  import Table from "cli-table3";
1486
- import kleur from "kleur";
1487
1580
  import format from "human-format";
1488
- import { Blob } from "buffer";
1581
+ import kleur2 from "kleur";
1489
1582
  var HttpVerbColors = {
1490
- get: kleur.green,
1491
- post: kleur.magenta,
1492
- put: kleur.cyan,
1493
- delete: kleur.red,
1494
- other: kleur.white
1495
- };
1496
- var HttpVerbOrder = {
1497
- get: 0,
1498
- post: 1,
1499
- put: 2,
1500
- delete: 3
1583
+ get: kleur2.green,
1584
+ head: kleur2.dim().green,
1585
+ post: kleur2.magenta,
1586
+ put: kleur2.cyan,
1587
+ delete: kleur2.red,
1588
+ patch: kleur2.yellow,
1589
+ options: kleur2.grey
1501
1590
  };
1591
+ function verbColor(verb) {
1592
+ return verb in HttpVerbColors ? HttpVerbColors[verb] : kleur2.gray;
1593
+ }
1502
1594
  function logRoutesTable(routes, bundle, options) {
1503
1595
  function getRouteChunkName(route) {
1504
1596
  return options.sanitizeFileName(`${route.entryName}.marko`);
@@ -1518,30 +1610,34 @@ function logRoutesTable(routes, bundle, options) {
1518
1610
  headings.push("Size/GZip");
1519
1611
  colAligns.push("right");
1520
1612
  const table = new Table({
1521
- head: headings.map((title) => kleur.bold(kleur.white(title.toUpperCase()))),
1613
+ head: headings.map((title) => kleur2.bold(kleur2.white(title.toUpperCase()))),
1522
1614
  wordWrap: true,
1523
1615
  colAligns,
1524
1616
  style: { compact: true }
1525
1617
  });
1526
1618
  for (const route of routes.list) {
1527
1619
  for (const path5 of route.paths) {
1528
- const verbs = getVerbs(route).sort(
1529
- (a, b) => HttpVerbOrder[a] - HttpVerbOrder[b]
1530
- );
1620
+ const verbs = getVerbs(route, true);
1531
1621
  let firstRow = true;
1532
1622
  for (const verb of verbs) {
1533
- let size = "";
1534
1623
  const entryType = [];
1624
+ let size = "";
1625
+ let verbCell = verbColor(verb)(verb.toUpperCase());
1626
+ if (verb === "get" && !verbs.includes("head")) {
1627
+ verbCell += kleur2.dim(`,${verbColor(verb)("HEAD")}`);
1628
+ }
1535
1629
  if (route.handler) {
1536
- entryType.push(kleur.blue("handler"));
1630
+ entryType.push(kleur2.blue("handler"));
1537
1631
  }
1538
- if (verb === "get" && route.page) {
1539
- entryType.push(kleur.yellow("page"));
1540
- size = prettySize(computeRouteSize(getRouteChunkName(route), bundle));
1632
+ if (route.page && (verb === "get" || verb === "head")) {
1633
+ entryType.push(kleur2.yellow("page"));
1634
+ if (verb === "get") {
1635
+ size = prettySize(
1636
+ computeRouteSize(getRouteChunkName(route), bundle)
1637
+ );
1638
+ }
1541
1639
  }
1542
- const row = [
1543
- kleur.bold(HttpVerbColors[verb](verb.toUpperCase()))
1544
- ];
1640
+ const row = [verbCell];
1545
1641
  if (verbs.length === 1 || firstRow) {
1546
1642
  row.push({ rowSpan: verbs.length, content: prettyPath(path5.path) });
1547
1643
  firstRow = false;
@@ -1555,7 +1651,7 @@ function logRoutesTable(routes, bundle, options) {
1555
1651
  }
1556
1652
  }
1557
1653
  for (const [key, route] of Object.entries(routes.special).sort()) {
1558
- const row = [kleur.bold(kleur.white("*")), key, kleur.yellow("page")];
1654
+ const row = [kleur2.bold(kleur2.white("*")), key, kleur2.yellow("page")];
1559
1655
  hasMiddleware && row.push("");
1560
1656
  hasMeta && row.push("");
1561
1657
  row.push(prettySize(computeRouteSize(getRouteChunkName(route), bundle)));
@@ -1594,41 +1690,24 @@ function computeChunkSize(chunk, bundle, seen = /* @__PURE__ */ new Set()) {
1594
1690
  }
1595
1691
  function prettySize([bytes, compBytes]) {
1596
1692
  if (bytes <= 0) {
1597
- return kleur.gray("0.0 kB");
1693
+ return kleur2.gray("0.0 kB");
1598
1694
  }
1599
1695
  const [size, prefix] = format(bytes, { decimals: 1 }).split(/\s+/);
1600
1696
  const compSize = format(compBytes, { decimals: 1, prefix, unit: "B" });
1601
- let str = kleur.white(size) + kleur.gray("/");
1602
- if (compBytes < 20 * 1e3) str += kleur.green(compSize);
1603
- else if (compBytes < 50 * 1e3) str += kleur.yellow(compSize);
1604
- else str += kleur.bold(kleur.red(compSize));
1697
+ let str = kleur2.white(size) + kleur2.gray("/");
1698
+ if (compBytes < 20 * 1e3) str += kleur2.green(compSize);
1699
+ else if (compBytes < 50 * 1e3) str += kleur2.yellow(compSize);
1700
+ else str += kleur2.bold(kleur2.red(compSize));
1605
1701
  return str;
1606
1702
  }
1607
1703
  function prettyPath(path5) {
1608
- return path5.replace(/\/\$\$(.*)$/, (_, p) => "/" + kleur.bold(kleur.dim(`*${p}`))).replace(/\/\$([^/]+)/g, (_, p) => "/" + kleur.bold(kleur.dim(`:${p}`)));
1609
- }
1610
-
1611
- // src/vite/utils/config.ts
1612
- var PluginConfigKey = "__MARKO_RUN_PLUGIN_CONFIG__";
1613
- var AdapterConfigKey = "__MARKO_RUN_ADAPTER_CONFIG__";
1614
- function getConfig(obj, key) {
1615
- return obj[key];
1616
- }
1617
- function setConfig(obj, key, value) {
1618
- obj[key] = value;
1619
- return obj;
1704
+ return path5.replace(/\/\$\$(.*)$/, (_, p) => "/" + kleur2.bold(kleur2.dim(`*${p}`))).replace(/\/\$([^/]+)/g, (_, p) => "/" + kleur2.bold(kleur2.dim(`:${p}`)));
1620
1705
  }
1621
- var getExternalPluginOptions = (viteConfig) => getConfig(viteConfig, PluginConfigKey);
1622
- var setExternalPluginOptions = (viteConfig, value) => setConfig(viteConfig, PluginConfigKey, value);
1623
- var getExternalAdapterOptions = (viteConfig) => getConfig(viteConfig, AdapterConfigKey);
1624
-
1625
- // src/vite/plugin.ts
1626
- import createDebug from "debug";
1627
1706
 
1628
1707
  // src/vite/utils/read-once-persisted-store.ts
1708
+ import { promises as fs2 } from "fs";
1629
1709
  import os from "os";
1630
1710
  import path3 from "path";
1631
- import { promises as fs2 } from "fs";
1632
1711
  var noop = () => {
1633
1712
  };
1634
1713
  var tmpFile = path3.join(os.tmpdir(), "marko-run-storage.json");
@@ -1671,33 +1750,7 @@ process.once("beforeExit", (code) => {
1671
1750
  }
1672
1751
  });
1673
1752
 
1674
- // src/adapter/utils.ts
1675
- import supporsColor from "supports-color";
1676
- import kleur2 from "kleur";
1677
- function stripAnsi(string) {
1678
- return string.replace(
1679
- /([\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><])/g,
1680
- ""
1681
- );
1682
- }
1683
- function cleanStack(stack) {
1684
- return stack.split(/\n/).filter((l) => /^\s*at/.test(l)).join("\n");
1685
- }
1686
- function prepareError(err) {
1687
- var _a;
1688
- return {
1689
- message: stripAnsi(err.message),
1690
- stack: stripAnsi(cleanStack(err.stack || "")),
1691
- id: err.id,
1692
- frame: stripAnsi(err.frame || ""),
1693
- plugin: err.plugin,
1694
- pluginCode: (_a = err.pluginCode) == null ? void 0 : _a.toString(),
1695
- loc: err.loc
1696
- };
1697
- }
1698
-
1699
1753
  // src/vite/plugin.ts
1700
- import { createHash } from "crypto";
1701
1754
  var debug = createDebug("@marko/run");
1702
1755
  var __dirname = path4.dirname(fileURLToPath(import.meta.url));
1703
1756
  var PLUGIN_NAME_PREFIX = "marko-run-vite";
@@ -1729,7 +1782,7 @@ function markoRun(opts = {}) {
1729
1782
  let getExportsFromFile;
1730
1783
  let resolvedConfig;
1731
1784
  let typesFile;
1732
- let seenErrors = /* @__PURE__ */ new Set();
1785
+ const seenErrors = /* @__PURE__ */ new Set();
1733
1786
  const virtualFiles = /* @__PURE__ */ new Map();
1734
1787
  let times = {
1735
1788
  routesBuild: 0,
@@ -1754,32 +1807,28 @@ function markoRun(opts = {}) {
1754
1807
  }
1755
1808
  let buildVirtualFilesResult;
1756
1809
  function buildVirtualFiles() {
1757
- return buildVirtualFilesResult ?? (buildVirtualFilesResult = new Promise(async (resolve, reject) => {
1758
- try {
1759
- virtualFiles.clear();
1760
- routes = await buildRoutes({
1761
- walker: createFSWalker(resolvedRoutesDir),
1762
- importPrefix: routesDir
1763
- });
1764
- if (!routes.list.length) {
1765
- throw new Error("No routes generated");
1766
- }
1767
- for (const route of routes.list) {
1768
- virtualFiles.set(path4.posix.join(root, `${route.entryName}.js`), "");
1769
- }
1770
- if (routes.middleware.length) {
1771
- virtualFiles.set(path4.posix.join(root, MIDDLEWARE_FILENAME), "");
1772
- }
1773
- virtualFiles.set(path4.posix.join(root, ROUTER_FILENAME), "");
1774
- resolve(routes);
1775
- } catch (err) {
1776
- reject(err);
1810
+ return buildVirtualFilesResult ?? (buildVirtualFilesResult = (async () => {
1811
+ virtualFiles.clear();
1812
+ routes = await buildRoutes({
1813
+ walker: createFSWalker(resolvedRoutesDir),
1814
+ importPrefix: routesDir
1815
+ });
1816
+ if (!routes.list.length) {
1817
+ throw new Error("No routes generated");
1818
+ }
1819
+ for (const route of routes.list) {
1820
+ virtualFiles.set(path4.posix.join(root, `${route.entryName}.js`), "");
1821
+ }
1822
+ if (routes.middleware.length) {
1823
+ virtualFiles.set(path4.posix.join(root, MIDDLEWARE_FILENAME), "");
1777
1824
  }
1778
- }));
1825
+ virtualFiles.set(path4.posix.join(root, ROUTER_FILENAME), "");
1826
+ return routes;
1827
+ })());
1779
1828
  }
1780
1829
  let renderVirtualFilesResult;
1781
1830
  function renderVirtualFiles(context) {
1782
- return renderVirtualFilesResult ?? (renderVirtualFilesResult = new Promise(async (resolve) => {
1831
+ return renderVirtualFilesResult ?? (renderVirtualFilesResult = (async () => {
1783
1832
  var _a;
1784
1833
  try {
1785
1834
  const routes2 = await buildVirtualFiles();
@@ -1787,32 +1836,40 @@ function markoRun(opts = {}) {
1787
1836
  fs3.rmSync(entryFilesDir, { recursive: true });
1788
1837
  }
1789
1838
  for (const route of routes2.list) {
1790
- if (route.handler) {
1791
- const exports = await getExportsFromFile(
1792
- context,
1793
- route.handler.filePath
1794
- );
1795
- route.handler.verbs = [];
1839
+ const { handler, page, layouts } = route;
1840
+ if (handler) {
1841
+ const exports = await getExportsFromFile(context, handler.filePath);
1842
+ handler.verbs = [];
1796
1843
  for (const name of exports) {
1797
1844
  const verb = name.toLowerCase();
1798
1845
  if (name === verb.toUpperCase() && httpVerbs.includes(verb)) {
1799
- route.handler.verbs.push(verb);
1846
+ handler.verbs.push(verb);
1800
1847
  }
1801
1848
  }
1802
- if (!route.handler.verbs.length) {
1849
+ if (!handler.verbs.length) {
1803
1850
  context.warn(
1804
- `Did not find any http verb exports in handler '${path4.relative(root, route.handler.filePath)}' - expected ${httpVerbs.map((v) => v.toUpperCase()).join(", ")}`
1851
+ `Did not find any http verb exports in handler '${path4.relative(root, handler.filePath)}' - expected ${httpVerbs.map((v) => v.toUpperCase()).join(", ")}`
1805
1852
  );
1806
1853
  }
1807
1854
  }
1808
- if (route.page && route.layouts.length) {
1809
- const relativePath = path4.relative(resolvedRoutesDir, route.page.filePath);
1855
+ if (page && layouts.length) {
1856
+ const relativePath = path4.relative(
1857
+ resolvedRoutesDir,
1858
+ page.filePath
1859
+ );
1810
1860
  const routeFileDir = path4.join(entryFilesDir, relativePath, "..");
1811
- const routeFileRelativePathPosix = normalizePath(path4.relative(routeFileDir, root));
1861
+ const routeFileRelativePathPosix = normalizePath(
1862
+ path4.relative(routeFileDir, root)
1863
+ );
1812
1864
  fs3.mkdirSync(routeFileDir, { recursive: true });
1865
+ const pageNameIndex = page.name.indexOf("+page");
1866
+ const pageNamePrefix = pageNameIndex > 0 ? `${page.name.slice(0, pageNameIndex)}.` : "";
1813
1867
  fs3.writeFileSync(
1814
- path4.join(routeFileDir, "route.marko"),
1815
- renderRouteTemplate(route, (to) => path4.posix.join(routeFileRelativePathPosix, to))
1868
+ path4.join(routeFileDir, pageNamePrefix + "route.marko"),
1869
+ renderRouteTemplate(
1870
+ route,
1871
+ (to) => path4.posix.join(routeFileRelativePathPosix, to)
1872
+ )
1816
1873
  );
1817
1874
  }
1818
1875
  virtualFiles.set(
@@ -1821,14 +1878,23 @@ function markoRun(opts = {}) {
1821
1878
  );
1822
1879
  }
1823
1880
  for (const route of Object.values(routes2.special)) {
1824
- if (route.page && route.layouts.length) {
1825
- const relativePath = path4.relative(resolvedRoutesDir, route.page.filePath);
1881
+ const { page, layouts, key } = route;
1882
+ if (page && layouts.length) {
1883
+ const relativePath = path4.relative(
1884
+ resolvedRoutesDir,
1885
+ page.filePath
1886
+ );
1826
1887
  const routeFileDir = path4.join(entryFilesDir, relativePath, "..");
1827
- const routeFileRelativePathPosix = normalizePath(path4.relative(routeFileDir, root));
1888
+ const routeFileRelativePathPosix = normalizePath(
1889
+ path4.relative(routeFileDir, root)
1890
+ );
1828
1891
  fs3.mkdirSync(routeFileDir, { recursive: true });
1829
1892
  fs3.writeFileSync(
1830
- path4.join(routeFileDir, `route.${route.key}.marko`),
1831
- renderRouteTemplate(route, (to) => path4.posix.join(routeFileRelativePathPosix, to))
1893
+ path4.join(routeFileDir, `route.${key}.marko`),
1894
+ renderRouteTemplate(
1895
+ route,
1896
+ (to) => path4.posix.join(routeFileRelativePathPosix, to)
1897
+ )
1832
1898
  );
1833
1899
  }
1834
1900
  }
@@ -1874,8 +1940,7 @@ function markoRun(opts = {}) {
1874
1940
  `throw ${JSON.stringify(prepareError(err))}`
1875
1941
  );
1876
1942
  }
1877
- resolve();
1878
- }));
1943
+ })());
1879
1944
  }
1880
1945
  return [
1881
1946
  defaultConfigPlugin,
@@ -1920,7 +1985,9 @@ function markoRun(opts = {}) {
1920
1985
  createHash("shake256", { outputLength: 4 }).update(root).digest("hex")
1921
1986
  );
1922
1987
  entryFilesDirPosix = normalizePath(entryFilesDir);
1923
- relativeEntryFilesDirPosix = normalizePath(path4.relative(root, entryFilesDir));
1988
+ relativeEntryFilesDirPosix = normalizePath(
1989
+ path4.relative(root, entryFilesDir)
1990
+ );
1924
1991
  typesDir = path4.join(root, ".marko-run");
1925
1992
  devEntryFile = path4.join(root, "index.html");
1926
1993
  devEntryFilePosix = normalizePath(devEntryFile);
@@ -1941,7 +2008,7 @@ function markoRun(opts = {}) {
1941
2008
  entryFileNames(info) {
1942
2009
  let name = getEntryFileName(info.facadeModuleId);
1943
2010
  if (!name) {
1944
- for (let id of info.moduleIds) {
2011
+ for (const id of info.moduleIds) {
1945
2012
  name = getEntryFileName(id);
1946
2013
  if (name) {
1947
2014
  break;
@@ -2246,7 +2313,10 @@ async function resolveAdapter(root, options, log) {
2246
2313
  for (const name of dependecies) {
2247
2314
  if (name.startsWith("@marko/run-adapter") || name.indexOf("marko-run-adapter") !== -1) {
2248
2315
  try {
2249
- const module2 = await import(name);
2316
+ const module2 = await import(
2317
+ /* @vite-ignore */
2318
+ name
2319
+ );
2250
2320
  log && debug(
2251
2321
  `Using adapter ${name} listed in your package.json dependecies`
2252
2322
  );
@@ -2258,7 +2328,10 @@ async function resolveAdapter(root, options, log) {
2258
2328
  }
2259
2329
  }
2260
2330
  const defaultAdapter = "@marko/run/adapter";
2261
- const module = await import(defaultAdapter);
2331
+ const module = await import(
2332
+ /* @vite-ignore */
2333
+ defaultAdapter
2334
+ );
2262
2335
  log && debug("Using default adapter");
2263
2336
  return module.default();
2264
2337
  }
@@ -2302,11 +2375,11 @@ var defaultConfigPlugin = {
2302
2375
  };
2303
2376
 
2304
2377
  // src/vite/utils/server.ts
2305
- import net from "net";
2306
2378
  import cp from "child_process";
2307
- import { parse, config } from "dotenv";
2308
- import fs4 from "fs";
2309
2379
  import cluster from "cluster";
2380
+ import { config, parse } from "dotenv";
2381
+ import fs4 from "fs";
2382
+ import net from "net";
2310
2383
  async function parseEnv(envFile) {
2311
2384
  if (fs4.existsSync(envFile)) {
2312
2385
  const content = await fs4.promises.readFile(envFile, "utf8");