@run0/jiki 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/browser-bundle.d.ts +40 -0
  2. package/dist/builtins.d.ts +22 -0
  3. package/dist/code-transform.d.ts +7 -0
  4. package/dist/config/cdn.d.ts +13 -0
  5. package/dist/container.d.ts +101 -0
  6. package/dist/dev-server.d.ts +69 -0
  7. package/dist/errors.d.ts +19 -0
  8. package/dist/frameworks/code-transforms.d.ts +32 -0
  9. package/dist/frameworks/next-api-handler.d.ts +72 -0
  10. package/dist/frameworks/next-dev-server.d.ts +141 -0
  11. package/dist/frameworks/next-html-generator.d.ts +36 -0
  12. package/dist/frameworks/next-route-resolver.d.ts +19 -0
  13. package/dist/frameworks/next-shims.d.ts +78 -0
  14. package/dist/frameworks/remix-dev-server.d.ts +47 -0
  15. package/dist/frameworks/sveltekit-dev-server.d.ts +43 -0
  16. package/dist/frameworks/vite-dev-server.d.ts +50 -0
  17. package/dist/fs-errors.d.ts +36 -0
  18. package/dist/index.cjs +14916 -0
  19. package/dist/index.cjs.map +1 -0
  20. package/dist/index.d.ts +61 -0
  21. package/dist/index.mjs +14898 -0
  22. package/dist/index.mjs.map +1 -0
  23. package/dist/kernel.d.ts +48 -0
  24. package/dist/memfs.d.ts +144 -0
  25. package/dist/metrics.d.ts +78 -0
  26. package/dist/module-resolver.d.ts +60 -0
  27. package/dist/network-interceptor.d.ts +71 -0
  28. package/dist/npm/cache.d.ts +76 -0
  29. package/dist/npm/index.d.ts +60 -0
  30. package/dist/npm/lockfile-reader.d.ts +32 -0
  31. package/dist/npm/pnpm.d.ts +18 -0
  32. package/dist/npm/registry.d.ts +45 -0
  33. package/dist/npm/resolver.d.ts +39 -0
  34. package/dist/npm/sync-installer.d.ts +18 -0
  35. package/dist/npm/tarball.d.ts +4 -0
  36. package/dist/npm/workspaces.d.ts +46 -0
  37. package/dist/persistence.d.ts +94 -0
  38. package/dist/plugin.d.ts +156 -0
  39. package/dist/polyfills/assert.d.ts +30 -0
  40. package/dist/polyfills/child_process.d.ts +116 -0
  41. package/dist/polyfills/chokidar.d.ts +18 -0
  42. package/dist/polyfills/crypto.d.ts +49 -0
  43. package/dist/polyfills/events.d.ts +28 -0
  44. package/dist/polyfills/fs.d.ts +82 -0
  45. package/dist/polyfills/http.d.ts +147 -0
  46. package/dist/polyfills/module.d.ts +29 -0
  47. package/dist/polyfills/net.d.ts +53 -0
  48. package/dist/polyfills/os.d.ts +91 -0
  49. package/dist/polyfills/path.d.ts +96 -0
  50. package/dist/polyfills/perf_hooks.d.ts +21 -0
  51. package/dist/polyfills/process.d.ts +99 -0
  52. package/dist/polyfills/querystring.d.ts +15 -0
  53. package/dist/polyfills/readdirp.d.ts +18 -0
  54. package/dist/polyfills/readline.d.ts +32 -0
  55. package/dist/polyfills/stream.d.ts +106 -0
  56. package/dist/polyfills/stubs.d.ts +737 -0
  57. package/dist/polyfills/tty.d.ts +25 -0
  58. package/dist/polyfills/url.d.ts +41 -0
  59. package/dist/polyfills/util.d.ts +61 -0
  60. package/dist/polyfills/v8.d.ts +43 -0
  61. package/dist/polyfills/vm.d.ts +76 -0
  62. package/dist/polyfills/worker-threads.d.ts +77 -0
  63. package/dist/polyfills/ws.d.ts +32 -0
  64. package/dist/polyfills/zlib.d.ts +87 -0
  65. package/dist/runtime-helpers.d.ts +4 -0
  66. package/dist/runtime-interface.d.ts +39 -0
  67. package/dist/sandbox.d.ts +69 -0
  68. package/dist/server-bridge.d.ts +55 -0
  69. package/dist/shell-commands.d.ts +2 -0
  70. package/dist/shell.d.ts +101 -0
  71. package/dist/transpiler.d.ts +47 -0
  72. package/dist/type-checker.d.ts +57 -0
  73. package/dist/types/package-json.d.ts +17 -0
  74. package/dist/utils/binary-encoding.d.ts +4 -0
  75. package/dist/utils/hash.d.ts +6 -0
  76. package/dist/utils/safe-path.d.ts +6 -0
  77. package/dist/worker-runtime.d.ts +34 -0
  78. package/package.json +59 -0
  79. package/src/browser-bundle.ts +498 -0
  80. package/src/builtins.ts +222 -0
  81. package/src/code-transform.ts +183 -0
  82. package/src/config/cdn.ts +17 -0
  83. package/src/container.ts +343 -0
  84. package/src/dev-server.ts +322 -0
  85. package/src/errors.ts +604 -0
  86. package/src/frameworks/code-transforms.ts +667 -0
  87. package/src/frameworks/next-api-handler.ts +366 -0
  88. package/src/frameworks/next-dev-server.ts +1252 -0
  89. package/src/frameworks/next-html-generator.ts +585 -0
  90. package/src/frameworks/next-route-resolver.ts +521 -0
  91. package/src/frameworks/next-shims.ts +1084 -0
  92. package/src/frameworks/remix-dev-server.ts +163 -0
  93. package/src/frameworks/sveltekit-dev-server.ts +197 -0
  94. package/src/frameworks/vite-dev-server.ts +370 -0
  95. package/src/fs-errors.ts +118 -0
  96. package/src/index.ts +188 -0
  97. package/src/kernel.ts +381 -0
  98. package/src/memfs.ts +1006 -0
  99. package/src/metrics.ts +140 -0
  100. package/src/module-resolver.ts +511 -0
  101. package/src/network-interceptor.ts +143 -0
  102. package/src/npm/cache.ts +172 -0
  103. package/src/npm/index.ts +377 -0
  104. package/src/npm/lockfile-reader.ts +105 -0
  105. package/src/npm/pnpm.ts +108 -0
  106. package/src/npm/registry.ts +120 -0
  107. package/src/npm/resolver.ts +339 -0
  108. package/src/npm/sync-installer.ts +217 -0
  109. package/src/npm/tarball.ts +136 -0
  110. package/src/npm/workspaces.ts +255 -0
  111. package/src/persistence.ts +235 -0
  112. package/src/plugin.ts +293 -0
  113. package/src/polyfills/assert.ts +164 -0
  114. package/src/polyfills/child_process.ts +535 -0
  115. package/src/polyfills/chokidar.ts +52 -0
  116. package/src/polyfills/crypto.ts +433 -0
  117. package/src/polyfills/events.ts +178 -0
  118. package/src/polyfills/fs.ts +297 -0
  119. package/src/polyfills/http.ts +478 -0
  120. package/src/polyfills/module.ts +97 -0
  121. package/src/polyfills/net.ts +123 -0
  122. package/src/polyfills/os.ts +108 -0
  123. package/src/polyfills/path.ts +169 -0
  124. package/src/polyfills/perf_hooks.ts +30 -0
  125. package/src/polyfills/process.ts +349 -0
  126. package/src/polyfills/querystring.ts +66 -0
  127. package/src/polyfills/readdirp.ts +72 -0
  128. package/src/polyfills/readline.ts +80 -0
  129. package/src/polyfills/stream.ts +610 -0
  130. package/src/polyfills/stubs.ts +600 -0
  131. package/src/polyfills/tty.ts +43 -0
  132. package/src/polyfills/url.ts +97 -0
  133. package/src/polyfills/util.ts +173 -0
  134. package/src/polyfills/v8.ts +62 -0
  135. package/src/polyfills/vm.ts +111 -0
  136. package/src/polyfills/worker-threads.ts +189 -0
  137. package/src/polyfills/ws.ts +73 -0
  138. package/src/polyfills/zlib.ts +244 -0
  139. package/src/runtime-helpers.ts +83 -0
  140. package/src/runtime-interface.ts +46 -0
  141. package/src/sandbox.ts +178 -0
  142. package/src/server-bridge.ts +473 -0
  143. package/src/service-worker.ts +153 -0
  144. package/src/shell-commands.ts +708 -0
  145. package/src/shell.ts +795 -0
  146. package/src/transpiler.ts +282 -0
  147. package/src/type-checker.ts +241 -0
  148. package/src/types/package-json.ts +17 -0
  149. package/src/utils/binary-encoding.ts +38 -0
  150. package/src/utils/hash.ts +24 -0
  151. package/src/utils/safe-path.ts +38 -0
  152. package/src/worker-runtime.ts +42 -0
@@ -0,0 +1,521 @@
1
+ /**
2
+ * Next.js route resolution
3
+ * Standalone functions for resolving App Router routes, Pages Router routes,
4
+ * API routes, and file extensions.
5
+ */
6
+
7
+ import { type AppRoute } from "./next-html-generator";
8
+
9
+ /** Context needed by route resolution functions */
10
+ export interface RouteResolverContext {
11
+ exists: (path: string) => boolean;
12
+ isDirectory: (path: string) => boolean;
13
+ readdir: (path: string) => string[];
14
+ }
15
+
16
+ const PAGE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
17
+ const API_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
18
+
19
+ export function hasAppRouter(
20
+ appDir: string,
21
+ ctx: RouteResolverContext,
22
+ ): boolean {
23
+ try {
24
+ if (!ctx.exists(appDir)) return false;
25
+
26
+ for (const ext of PAGE_EXTENSIONS) {
27
+ if (ctx.exists(`${appDir}/page${ext}`)) return true;
28
+ }
29
+
30
+ try {
31
+ const entries = ctx.readdir(appDir);
32
+ for (const entry of entries) {
33
+ if (
34
+ /^\([^)]+\)$/.test(entry) &&
35
+ ctx.isDirectory(`${appDir}/${entry}`)
36
+ ) {
37
+ for (const ext of PAGE_EXTENSIONS) {
38
+ if (ctx.exists(`${appDir}/${entry}/page${ext}`)) return true;
39
+ }
40
+ }
41
+ }
42
+ } catch {
43
+ /* ignore */
44
+ }
45
+
46
+ for (const ext of PAGE_EXTENSIONS) {
47
+ if (ctx.exists(`${appDir}/layout${ext}`)) return true;
48
+ }
49
+
50
+ return false;
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
55
+
56
+ export function resolveAppRoute(
57
+ appDir: string,
58
+ pathname: string,
59
+ ctx: RouteResolverContext,
60
+ ): AppRoute | null {
61
+ const segments = pathname === "/" ? [] : pathname.split("/").filter(Boolean);
62
+ return resolveAppDynamicRoute(appDir, segments, ctx);
63
+ }
64
+
65
+ function resolveAppDynamicRoute(
66
+ appDir: string,
67
+ segments: string[],
68
+ ctx: RouteResolverContext,
69
+ ): AppRoute | null {
70
+ const collectLayout = (dirPath: string, layouts: string[]): string[] => {
71
+ for (const ext of PAGE_EXTENSIONS) {
72
+ const layoutPath = `${dirPath}/layout${ext}`;
73
+ if (ctx.exists(layoutPath) && !layouts.includes(layoutPath)) {
74
+ return [...layouts, layoutPath];
75
+ }
76
+ }
77
+ return layouts;
78
+ };
79
+
80
+ const findPage = (dirPath: string): string | null => {
81
+ for (const ext of PAGE_EXTENSIONS) {
82
+ const pagePath = `${dirPath}/page${ext}`;
83
+ if (ctx.exists(pagePath)) return pagePath;
84
+ }
85
+ return null;
86
+ };
87
+
88
+ const findConventionFile = (dirPath: string, name: string): string | null => {
89
+ for (const ext of PAGE_EXTENSIONS) {
90
+ const filePath = `${dirPath}/${name}${ext}`;
91
+ if (ctx.exists(filePath)) return filePath;
92
+ }
93
+ return null;
94
+ };
95
+
96
+ const findNearestConventionFile = (
97
+ dirPath: string,
98
+ name: string,
99
+ ): string | null => {
100
+ let current = dirPath;
101
+ while (current.startsWith(appDir)) {
102
+ const file = findConventionFile(current, name);
103
+ if (file) return file;
104
+ const parent = current.replace(/\/[^/]+$/, "");
105
+ if (parent === current) break;
106
+ current = parent;
107
+ }
108
+ return null;
109
+ };
110
+
111
+ const getRouteGroups = (dirPath: string): string[] => {
112
+ try {
113
+ const entries = ctx.readdir(dirPath);
114
+ return entries.filter(
115
+ e => /^\([^)]+\)$/.test(e) && ctx.isDirectory(`${dirPath}/${e}`),
116
+ );
117
+ } catch {
118
+ return [];
119
+ }
120
+ };
121
+
122
+ const tryPath = (
123
+ dirPath: string,
124
+ remainingSegments: string[],
125
+ layouts: string[],
126
+ params: Record<string, string | string[]>,
127
+ ): AppRoute | null => {
128
+ layouts = collectLayout(dirPath, layouts);
129
+
130
+ if (remainingSegments.length === 0) {
131
+ const page = findPage(dirPath);
132
+ if (page) {
133
+ return {
134
+ page,
135
+ layouts,
136
+ params,
137
+ loading: findNearestConventionFile(dirPath, "loading") || undefined,
138
+ error: findNearestConventionFile(dirPath, "error") || undefined,
139
+ notFound:
140
+ findNearestConventionFile(dirPath, "not-found") || undefined,
141
+ };
142
+ }
143
+
144
+ const groups = getRouteGroups(dirPath);
145
+ for (const group of groups) {
146
+ const groupPath = `${dirPath}/${group}`;
147
+ const groupLayouts = collectLayout(groupPath, layouts);
148
+ const page = findPage(groupPath);
149
+ if (page) {
150
+ return {
151
+ page,
152
+ layouts: groupLayouts,
153
+ params,
154
+ loading:
155
+ findNearestConventionFile(groupPath, "loading") || undefined,
156
+ error: findNearestConventionFile(groupPath, "error") || undefined,
157
+ notFound:
158
+ findNearestConventionFile(groupPath, "not-found") || undefined,
159
+ };
160
+ }
161
+ }
162
+
163
+ return null;
164
+ }
165
+
166
+ const [current, ...rest] = remainingSegments;
167
+
168
+ const exactPath = `${dirPath}/${current}`;
169
+ if (ctx.isDirectory(exactPath)) {
170
+ const result = tryPath(exactPath, rest, layouts, params);
171
+ if (result) return result;
172
+ }
173
+
174
+ const groups = getRouteGroups(dirPath);
175
+ for (const group of groups) {
176
+ const groupPath = `${dirPath}/${group}`;
177
+ const groupLayouts = collectLayout(groupPath, layouts);
178
+
179
+ const groupExactPath = `${groupPath}/${current}`;
180
+ if (ctx.isDirectory(groupExactPath)) {
181
+ const result = tryPath(groupExactPath, rest, groupLayouts, params);
182
+ if (result) return result;
183
+ }
184
+
185
+ try {
186
+ const groupEntries = ctx.readdir(groupPath);
187
+ for (const entry of groupEntries) {
188
+ if (entry.startsWith("[...") && entry.endsWith("]")) {
189
+ const dynamicPath = `${groupPath}/${entry}`;
190
+ if (ctx.isDirectory(dynamicPath)) {
191
+ const paramName = entry.slice(4, -1);
192
+ const newParams = { ...params, [paramName]: [current, ...rest] };
193
+ const result = tryPath(dynamicPath, [], groupLayouts, newParams);
194
+ if (result) return result;
195
+ }
196
+ } else if (entry.startsWith("[[...") && entry.endsWith("]]")) {
197
+ const dynamicPath = `${groupPath}/${entry}`;
198
+ if (ctx.isDirectory(dynamicPath)) {
199
+ const paramName = entry.slice(5, -2);
200
+ const newParams = { ...params, [paramName]: [current, ...rest] };
201
+ const result = tryPath(dynamicPath, [], groupLayouts, newParams);
202
+ if (result) return result;
203
+ }
204
+ } else if (
205
+ entry.startsWith("[") &&
206
+ entry.endsWith("]") &&
207
+ !entry.includes(".")
208
+ ) {
209
+ const dynamicPath = `${groupPath}/${entry}`;
210
+ if (ctx.isDirectory(dynamicPath)) {
211
+ const paramName = entry.slice(1, -1);
212
+ const newParams = { ...params, [paramName]: current };
213
+ const result = tryPath(
214
+ dynamicPath,
215
+ rest,
216
+ groupLayouts,
217
+ newParams,
218
+ );
219
+ if (result) return result;
220
+ }
221
+ }
222
+ }
223
+ } catch {
224
+ /* ignore */
225
+ }
226
+ }
227
+
228
+ try {
229
+ const entries = ctx.readdir(dirPath);
230
+ for (const entry of entries) {
231
+ if (entry.startsWith("[...") && entry.endsWith("]")) {
232
+ const dynamicPath = `${dirPath}/${entry}`;
233
+ if (ctx.isDirectory(dynamicPath)) {
234
+ const paramName = entry.slice(4, -1);
235
+ const newParams = { ...params, [paramName]: [current, ...rest] };
236
+ const result = tryPath(dynamicPath, [], layouts, newParams);
237
+ if (result) return result;
238
+ }
239
+ } else if (entry.startsWith("[[...") && entry.endsWith("]]")) {
240
+ const dynamicPath = `${dirPath}/${entry}`;
241
+ if (ctx.isDirectory(dynamicPath)) {
242
+ const paramName = entry.slice(5, -2);
243
+ const newParams = { ...params, [paramName]: [current, ...rest] };
244
+ const result = tryPath(dynamicPath, [], layouts, newParams);
245
+ if (result) return result;
246
+ }
247
+ } else if (
248
+ entry.startsWith("[") &&
249
+ entry.endsWith("]") &&
250
+ !entry.includes(".")
251
+ ) {
252
+ const dynamicPath = `${dirPath}/${entry}`;
253
+ if (ctx.isDirectory(dynamicPath)) {
254
+ const paramName = entry.slice(1, -1);
255
+ const newParams = { ...params, [paramName]: current };
256
+ const result = tryPath(dynamicPath, rest, layouts, newParams);
257
+ if (result) return result;
258
+ }
259
+ }
260
+ }
261
+ } catch {
262
+ /* ignore */
263
+ }
264
+
265
+ return null;
266
+ };
267
+
268
+ const layouts: string[] = [];
269
+ for (const ext of PAGE_EXTENSIONS) {
270
+ const rootLayout = `${appDir}/layout${ext}`;
271
+ if (ctx.exists(rootLayout)) {
272
+ layouts.push(rootLayout);
273
+ break;
274
+ }
275
+ }
276
+
277
+ return tryPath(appDir, segments, layouts, {});
278
+ }
279
+
280
+ export function resolveAppRouteHandler(
281
+ appDir: string,
282
+ pathname: string,
283
+ ctx: RouteResolverContext,
284
+ ): string | null {
285
+ const segments = pathname === "/" ? [] : pathname.split("/").filter(Boolean);
286
+ let dirPath = appDir;
287
+ for (const segment of segments) dirPath = `${dirPath}/${segment}`;
288
+
289
+ for (const ext of API_EXTENSIONS) {
290
+ const routePath = `${dirPath}/route${ext}`;
291
+ if (ctx.exists(routePath)) return routePath;
292
+ }
293
+
294
+ return resolveAppRouteHandlerDynamic(appDir, segments, ctx);
295
+ }
296
+
297
+ function resolveAppRouteHandlerDynamic(
298
+ appDir: string,
299
+ segments: string[],
300
+ ctx: RouteResolverContext,
301
+ ): string | null {
302
+ const tryPath = (
303
+ dirPath: string,
304
+ remainingSegments: string[],
305
+ ): string | null => {
306
+ if (remainingSegments.length === 0) {
307
+ for (const ext of API_EXTENSIONS) {
308
+ const routePath = `${dirPath}/route${ext}`;
309
+ if (ctx.exists(routePath)) return routePath;
310
+ }
311
+
312
+ try {
313
+ const entries = ctx.readdir(dirPath);
314
+ for (const entry of entries) {
315
+ if (
316
+ /^\([^)]+\)$/.test(entry) &&
317
+ ctx.isDirectory(`${dirPath}/${entry}`)
318
+ ) {
319
+ for (const ext of API_EXTENSIONS) {
320
+ const routePath = `${dirPath}/${entry}/route${ext}`;
321
+ if (ctx.exists(routePath)) return routePath;
322
+ }
323
+ }
324
+ }
325
+ } catch {
326
+ /* ignore */
327
+ }
328
+
329
+ return null;
330
+ }
331
+
332
+ const [current, ...rest] = remainingSegments;
333
+
334
+ const exactPath = `${dirPath}/${current}`;
335
+ if (ctx.isDirectory(exactPath)) {
336
+ const result = tryPath(exactPath, rest);
337
+ if (result) return result;
338
+ }
339
+
340
+ try {
341
+ const entries = ctx.readdir(dirPath);
342
+ for (const entry of entries) {
343
+ if (
344
+ /^\([^)]+\)$/.test(entry) &&
345
+ ctx.isDirectory(`${dirPath}/${entry}`)
346
+ ) {
347
+ const groupExact = `${dirPath}/${entry}/${current}`;
348
+ if (ctx.isDirectory(groupExact)) {
349
+ const result = tryPath(groupExact, rest);
350
+ if (result) return result;
351
+ }
352
+ }
353
+ if (
354
+ entry.startsWith("[") &&
355
+ entry.endsWith("]") &&
356
+ !entry.includes(".")
357
+ ) {
358
+ const dynamicPath = `${dirPath}/${entry}`;
359
+ if (ctx.isDirectory(dynamicPath)) {
360
+ const result = tryPath(dynamicPath, rest);
361
+ if (result) return result;
362
+ }
363
+ }
364
+ if (entry.startsWith("[...") && entry.endsWith("]")) {
365
+ const dynamicPath = `${dirPath}/${entry}`;
366
+ if (ctx.isDirectory(dynamicPath)) {
367
+ const result = tryPath(dynamicPath, []);
368
+ if (result) return result;
369
+ }
370
+ }
371
+ }
372
+ } catch {
373
+ /* ignore */
374
+ }
375
+
376
+ return null;
377
+ };
378
+
379
+ return tryPath(appDir, segments);
380
+ }
381
+
382
+ export function resolvePageFile(
383
+ pagesDir: string,
384
+ pathname: string,
385
+ ctx: RouteResolverContext,
386
+ ): string | null {
387
+ if (pathname === "/") pathname = "/index";
388
+
389
+ for (const ext of PAGE_EXTENSIONS) {
390
+ const filePath = `${pagesDir}${pathname}${ext}`;
391
+ if (ctx.exists(filePath)) return filePath;
392
+ }
393
+
394
+ for (const ext of PAGE_EXTENSIONS) {
395
+ const filePath = `${pagesDir}${pathname}/index${ext}`;
396
+ if (ctx.exists(filePath)) return filePath;
397
+ }
398
+
399
+ return resolveDynamicRoute(pagesDir, pathname, ctx);
400
+ }
401
+
402
+ function resolveDynamicRoute(
403
+ pagesDir: string,
404
+ pathname: string,
405
+ ctx: RouteResolverContext,
406
+ ): string | null {
407
+ const segments = pathname.split("/").filter(Boolean);
408
+ if (segments.length === 0) return null;
409
+
410
+ const tryPath = (
411
+ dirPath: string,
412
+ remainingSegments: string[],
413
+ ): string | null => {
414
+ if (remainingSegments.length === 0) {
415
+ for (const ext of PAGE_EXTENSIONS) {
416
+ const indexPath = `${dirPath}/index${ext}`;
417
+ if (ctx.exists(indexPath)) return indexPath;
418
+ }
419
+ return null;
420
+ }
421
+
422
+ const [current, ...rest] = remainingSegments;
423
+ const exactPath = `${dirPath}/${current}`;
424
+
425
+ for (const ext of PAGE_EXTENSIONS) {
426
+ if (rest.length === 0 && ctx.exists(exactPath + ext))
427
+ return exactPath + ext;
428
+ }
429
+
430
+ if (ctx.isDirectory(exactPath)) {
431
+ const exactResult = tryPath(exactPath, rest);
432
+ if (exactResult) return exactResult;
433
+ }
434
+
435
+ try {
436
+ const entries = ctx.readdir(dirPath);
437
+ for (const entry of entries) {
438
+ for (const ext of PAGE_EXTENSIONS) {
439
+ const dynamicFilePattern = /^\[([^\]]+)\]$/;
440
+ const nameWithoutExt = entry.replace(ext, "");
441
+ if (entry.endsWith(ext) && dynamicFilePattern.test(nameWithoutExt)) {
442
+ if (rest.length === 0) {
443
+ const filePath = `${dirPath}/${entry}`;
444
+ if (ctx.exists(filePath)) return filePath;
445
+ }
446
+ }
447
+ }
448
+
449
+ if (
450
+ entry.startsWith("[") &&
451
+ entry.endsWith("]") &&
452
+ !entry.includes(".")
453
+ ) {
454
+ const dynamicPath = `${dirPath}/${entry}`;
455
+ if (ctx.isDirectory(dynamicPath)) {
456
+ const dynamicResult = tryPath(dynamicPath, rest);
457
+ if (dynamicResult) return dynamicResult;
458
+ }
459
+ }
460
+
461
+ for (const ext of PAGE_EXTENSIONS) {
462
+ if (entry.startsWith("[...") && entry.endsWith("]" + ext)) {
463
+ const filePath = `${dirPath}/${entry}`;
464
+ if (ctx.exists(filePath)) return filePath;
465
+ }
466
+ }
467
+ }
468
+ } catch {
469
+ /* ignore */
470
+ }
471
+
472
+ return null;
473
+ };
474
+
475
+ return tryPath(pagesDir, segments);
476
+ }
477
+
478
+ export function resolveApiFile(
479
+ pagesDir: string,
480
+ pathname: string,
481
+ ctx: RouteResolverContext,
482
+ ): string | null {
483
+ const apiPath = pathname.replace(/^\/api/, `${pagesDir}/api`);
484
+
485
+ for (const ext of API_EXTENSIONS) {
486
+ const filePath = apiPath + ext;
487
+ if (ctx.exists(filePath)) return filePath;
488
+ }
489
+
490
+ for (const ext of API_EXTENSIONS) {
491
+ const filePath = `${apiPath}/index${ext}`;
492
+ if (ctx.exists(filePath)) return filePath;
493
+ }
494
+
495
+ return null;
496
+ }
497
+
498
+ export function resolveFileWithExtension(
499
+ pathname: string,
500
+ ctx: RouteResolverContext,
501
+ ): string | null {
502
+ if (/\.\w+$/.test(pathname) && ctx.exists(pathname)) return pathname;
503
+
504
+ const extensions = [".tsx", ".ts", ".jsx", ".js"];
505
+
506
+ for (const ext of extensions) {
507
+ const withExt = pathname + ext;
508
+ if (ctx.exists(withExt)) return withExt;
509
+ }
510
+
511
+ for (const ext of extensions) {
512
+ const indexPath = pathname + "/index" + ext;
513
+ if (ctx.exists(indexPath)) return indexPath;
514
+ }
515
+
516
+ return null;
517
+ }
518
+
519
+ export function needsTransform(path: string): boolean {
520
+ return /\.(jsx|tsx|ts)$/.test(path);
521
+ }