vinext 0.0.21 → 0.0.23

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 (70) hide show
  1. package/dist/build/static-export.d.ts.map +1 -1
  2. package/dist/build/static-export.js +9 -7
  3. package/dist/build/static-export.js.map +1 -1
  4. package/dist/config/next-config.d.ts +4 -1
  5. package/dist/config/next-config.d.ts.map +1 -1
  6. package/dist/config/next-config.js +10 -5
  7. package/dist/config/next-config.js.map +1 -1
  8. package/dist/deploy.d.ts.map +1 -1
  9. package/dist/deploy.js +17 -4
  10. package/dist/deploy.js.map +1 -1
  11. package/dist/index.d.ts +25 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +381 -38
  14. package/dist/index.js.map +1 -1
  15. package/dist/routing/app-router.d.ts +2 -1
  16. package/dist/routing/app-router.d.ts.map +1 -1
  17. package/dist/routing/app-router.js +74 -107
  18. package/dist/routing/app-router.js.map +1 -1
  19. package/dist/routing/file-matcher.d.ts +24 -0
  20. package/dist/routing/file-matcher.d.ts.map +1 -0
  21. package/dist/routing/file-matcher.js +75 -0
  22. package/dist/routing/file-matcher.js.map +1 -0
  23. package/dist/routing/pages-router.d.ts +3 -2
  24. package/dist/routing/pages-router.d.ts.map +1 -1
  25. package/dist/routing/pages-router.js +25 -44
  26. package/dist/routing/pages-router.js.map +1 -1
  27. package/dist/routing/utils.d.ts +25 -0
  28. package/dist/routing/utils.d.ts.map +1 -0
  29. package/dist/routing/utils.js +70 -0
  30. package/dist/routing/utils.js.map +1 -0
  31. package/dist/server/app-dev-server.d.ts.map +1 -1
  32. package/dist/server/app-dev-server.js +123 -47
  33. package/dist/server/app-dev-server.js.map +1 -1
  34. package/dist/server/dev-server.d.ts +2 -1
  35. package/dist/server/dev-server.d.ts.map +1 -1
  36. package/dist/server/dev-server.js +93 -18
  37. package/dist/server/dev-server.js.map +1 -1
  38. package/dist/server/prod-server.d.ts.map +1 -1
  39. package/dist/server/prod-server.js +33 -2
  40. package/dist/server/prod-server.js.map +1 -1
  41. package/dist/server/request-log.d.ts +34 -0
  42. package/dist/server/request-log.d.ts.map +1 -0
  43. package/dist/server/request-log.js +65 -0
  44. package/dist/server/request-log.js.map +1 -0
  45. package/dist/shims/cache-runtime.d.ts.map +1 -1
  46. package/dist/shims/cache-runtime.js +5 -1
  47. package/dist/shims/cache-runtime.js.map +1 -1
  48. package/dist/shims/cache.d.ts +7 -1
  49. package/dist/shims/cache.d.ts.map +1 -1
  50. package/dist/shims/cache.js +30 -5
  51. package/dist/shims/cache.js.map +1 -1
  52. package/dist/shims/head.d.ts +11 -0
  53. package/dist/shims/head.d.ts.map +1 -1
  54. package/dist/shims/head.js +21 -0
  55. package/dist/shims/head.js.map +1 -1
  56. package/dist/shims/headers.d.ts +8 -0
  57. package/dist/shims/headers.d.ts.map +1 -1
  58. package/dist/shims/headers.js +41 -0
  59. package/dist/shims/headers.js.map +1 -1
  60. package/dist/shims/metadata.d.ts +1 -0
  61. package/dist/shims/metadata.d.ts.map +1 -1
  62. package/dist/shims/metadata.js +5 -1
  63. package/dist/shims/metadata.js.map +1 -1
  64. package/dist/shims/script.d.ts.map +1 -1
  65. package/dist/shims/script.js +7 -1
  66. package/dist/shims/script.js.map +1 -1
  67. package/dist/shims/server.d.ts.map +1 -1
  68. package/dist/shims/server.js +2 -1
  69. package/dist/shims/server.js.map +1 -1
  70. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ import { type ValidFileMatcher } from "./file-matcher.js";
1
2
  export interface InterceptingRoute {
2
3
  /** The interception convention: "." | ".." | "../.." | "..." */
3
4
  convention: string;
@@ -85,7 +86,7 @@ export declare function invalidateAppRouteCache(): void;
85
86
  /**
86
87
  * Scan the app/ directory and return a list of routes.
87
88
  */
88
- export declare function appRouter(appDir: string): Promise<AppRoute[]>;
89
+ export declare function appRouter(appDir: string, pageExtensions?: readonly string[], matcher?: ValidFileMatcher): Promise<AppRoute[]>;
89
90
  /**
90
91
  * Match a URL against App Router routes.
91
92
  */
@@ -1 +1 @@
1
- {"version":3,"file":"app-router.d.ts","sourceRoot":"","sources":["../../src/routing/app-router.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,uDAAuD;IACvD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,wEAAwE;IACxE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oDAAoD;IACpD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kDAAkD;IAClD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,2CAA2C;IAC3C,kBAAkB,EAAE,iBAAiB,EAAE,CAAC;IACxC;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,yDAAyD;IACzD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,qDAAqD;IACrD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,6EAA6E;IAC7E,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,mFAAmF;IACnF,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,6BAA6B;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iDAAiD;IACjD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;;;OAMG;IACH,gBAAgB,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IACpC,mEAAmE;IACnE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,aAAa,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IACjC,qCAAqC;IACrC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,wCAAwC;IACxC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;;;;OAMG;IACH,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,sCAAsC;IACtC,SAAS,EAAE,OAAO,CAAC;IACnB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAMD,wBAAgB,uBAAuB,IAAI,IAAI,CAG9C;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAoCnE;AA+yBD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,QAAQ,EAAE,GACjB;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAA;CAAE,GAAG,IAAI,CAiBvE"}
1
+ {"version":3,"file":"app-router.d.ts","sourceRoot":"","sources":["../../src/routing/app-router.ts"],"names":[],"mappings":"AAkBA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,uDAAuD;IACvD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,wEAAwE;IACxE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oDAAoD;IACpD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kDAAkD;IAClD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,2CAA2C;IAC3C,kBAAkB,EAAE,iBAAiB,EAAE,CAAC;IACxC;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,yDAAyD;IACzD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,qDAAqD;IACrD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,6EAA6E;IAC7E,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,mFAAmF;IACnF,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,6BAA6B;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iDAAiD;IACjD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;;;OAMG;IACH,gBAAgB,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IACpC,mEAAmE;IACnE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,aAAa,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IACjC,qCAAqC;IACrC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,wCAAwC;IACxC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;;;;OAMG;IACH,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,sCAAsC;IACtC,SAAS,EAAE,OAAO,CAAC;IACnB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAOD,wBAAgB,uBAAuB,IAAI,IAAI,CAI9C;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,EAClC,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAuDrB;AAk2BD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,QAAQ,EAAE,GACjB;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAA;CAAE,GAAG,IAAI,CAiBvE"}
@@ -15,33 +15,41 @@
15
15
  */
16
16
  import path from "node:path";
17
17
  import fs from "node:fs";
18
- import { glob } from "node:fs/promises";
18
+ import { routePrecedence } from "./utils.js";
19
+ import { createValidFileMatcher, scanWithExtensions, } from "./file-matcher.js";
19
20
  // Cache for app routes
20
21
  let cachedRoutes = null;
21
22
  let cachedAppDir = null;
23
+ let cachedPageExtensionsKey = null;
22
24
  export function invalidateAppRouteCache() {
23
25
  cachedRoutes = null;
24
26
  cachedAppDir = null;
27
+ cachedPageExtensionsKey = null;
25
28
  }
26
29
  /**
27
30
  * Scan the app/ directory and return a list of routes.
28
31
  */
29
- export async function appRouter(appDir) {
30
- if (cachedRoutes && cachedAppDir === appDir)
32
+ export async function appRouter(appDir, pageExtensions, matcher) {
33
+ matcher ??= createValidFileMatcher(pageExtensions);
34
+ const pageExtensionsKey = JSON.stringify(matcher.extensions);
35
+ if (cachedRoutes &&
36
+ cachedAppDir === appDir &&
37
+ cachedPageExtensionsKey === pageExtensionsKey) {
31
38
  return cachedRoutes;
39
+ }
32
40
  // Find all page.tsx and route.ts files, excluding @slot directories
33
41
  // (slot pages are not standalone routes — they're rendered as props of their parent layout)
34
42
  const routes = [];
35
43
  // Process page files in a single pass
36
44
  // Use function form of exclude for Node < 22.14 compatibility (string arrays require >= 22.14)
37
- for await (const file of glob("**/page.{tsx,ts,jsx,js}", { cwd: appDir, exclude: (name) => name.startsWith("@") })) {
38
- const route = fileToAppRoute(file, appDir, "page");
45
+ for await (const file of scanWithExtensions("**/page", appDir, matcher.extensions, (name) => name.startsWith("@"))) {
46
+ const route = fileToAppRoute(file, appDir, "page", matcher);
39
47
  if (route)
40
48
  routes.push(route);
41
49
  }
42
50
  // Process route handler files (API routes) in a single pass
43
- for await (const file of glob("**/route.{tsx,ts,jsx,js}", { cwd: appDir, exclude: (name) => name.startsWith("@") })) {
44
- const route = fileToAppRoute(file, appDir, "route");
51
+ for await (const file of scanWithExtensions("**/route", appDir, matcher.extensions, (name) => name.startsWith("@"))) {
52
+ const route = fileToAppRoute(file, appDir, "route", matcher);
45
53
  if (route)
46
54
  routes.push(route);
47
55
  }
@@ -49,7 +57,7 @@ export async function appRouter(appDir) {
49
57
  // In Next.js, pages nested inside @slot directories create additional URL routes.
50
58
  // For example, @audience/demographics/page.tsx at app/parallel-routes/ creates
51
59
  // a route at /parallel-routes/demographics.
52
- const slotSubRoutes = discoverSlotSubRoutes(routes, appDir);
60
+ const slotSubRoutes = discoverSlotSubRoutes(routes, appDir, matcher);
53
61
  routes.push(...slotSubRoutes);
54
62
  // Sort: static routes first, then dynamic, then catch-all
55
63
  routes.sort((a, b) => {
@@ -58,6 +66,7 @@ export async function appRouter(appDir) {
58
66
  });
59
67
  cachedRoutes = routes;
60
68
  cachedAppDir = appDir;
69
+ cachedPageExtensionsKey = pageExtensionsKey;
61
70
  return routes;
62
71
  }
63
72
  /**
@@ -71,7 +80,7 @@ export async function appRouter(appDir) {
71
80
  * - @audience slot → @audience/demographics/page.tsx (matched)
72
81
  * - other slots → their default.tsx (fallback)
73
82
  */
74
- function discoverSlotSubRoutes(routes, _appDir) {
83
+ function discoverSlotSubRoutes(routes, _appDir, matcher) {
75
84
  const syntheticRoutes = [];
76
85
  const existingPatterns = new Set(routes.map((r) => r.pattern));
77
86
  for (const parentRoute of routes) {
@@ -87,7 +96,7 @@ function discoverSlotSubRoutes(routes, _appDir) {
87
96
  const slotDir = path.join(parentPageDir, `@${slot.name}`);
88
97
  if (!fs.existsSync(slotDir))
89
98
  continue;
90
- const subPages = findSlotSubPages(slotDir);
99
+ const subPages = findSlotSubPages(slotDir, matcher);
91
100
  for (const { relativePath, pagePath } of subPages) {
92
101
  if (!subPathMap.has(relativePath)) {
93
102
  subPathMap.set(relativePath, new Map());
@@ -98,7 +107,7 @@ function discoverSlotSubRoutes(routes, _appDir) {
98
107
  if (subPathMap.size === 0)
99
108
  continue;
100
109
  // Find the default.tsx for the children slot at the parent directory
101
- const childrenDefault = findFile(parentPageDir, "default");
110
+ const childrenDefault = findFile(parentPageDir, "default", matcher);
102
111
  for (const [subPath, slotPages] of subPathMap) {
103
112
  // Convert sub-path segments to URL pattern parts
104
113
  const subSegments = subPath.split(path.sep);
@@ -175,7 +184,7 @@ function discoverSlotSubRoutes(routes, _appDir) {
175
184
  * Skips the root page.tsx (already handled as the slot's main page)
176
185
  * and intercepting route directories.
177
186
  */
178
- function findSlotSubPages(slotDir) {
187
+ function findSlotSubPages(slotDir, matcher) {
179
188
  const results = [];
180
189
  function scan(dir) {
181
190
  if (!fs.existsSync(dir))
@@ -191,7 +200,7 @@ function findSlotSubPages(slotDir) {
191
200
  if (entry.name.startsWith("_"))
192
201
  continue;
193
202
  const subDir = path.join(dir, entry.name);
194
- const page = findFile(subDir, "page");
203
+ const page = findFile(subDir, "page", matcher);
195
204
  if (page) {
196
205
  const relativePath = path.relative(slotDir, subDir);
197
206
  results.push({ relativePath, pagePath: page });
@@ -206,7 +215,7 @@ function findSlotSubPages(slotDir) {
206
215
  /**
207
216
  * Convert a file path relative to app/ into an AppRoute.
208
217
  */
209
- function fileToAppRoute(file, appDir, type) {
218
+ function fileToAppRoute(file, appDir, type, matcher) {
210
219
  // Remove the filename (page.tsx or route.ts)
211
220
  const dir = path.dirname(file);
212
221
  const segments = dir === "." ? [] : dir.split(path.sep);
@@ -256,33 +265,33 @@ function fileToAppRoute(file, appDir, type) {
256
265
  }
257
266
  const pattern = "/" + urlSegments.join("/");
258
267
  // Discover layouts and templates from root to leaf
259
- const layouts = discoverLayouts(segments, appDir);
260
- const templates = discoverTemplates(segments, appDir);
268
+ const layouts = discoverLayouts(segments, appDir, matcher);
269
+ const templates = discoverTemplates(segments, appDir, matcher);
261
270
  // Compute the URL segment depth for each layout.
262
271
  // Each layout corresponds to a directory level. We need to count how many
263
272
  // of the filesystem segments up to that layout's level contribute URL segments
264
273
  // (i.e., are not route groups or parallel slots).
265
- const layoutSegmentDepths = computeLayoutSegmentDepths(segments, appDir, layouts);
274
+ const layoutSegmentDepths = computeLayoutSegmentDepths(segments, appDir, layouts, matcher);
266
275
  // Discover per-layout error boundaries (aligned with layouts array).
267
276
  // In Next.js, each segment independently wraps its children with an ErrorBoundary.
268
277
  // This array enables interleaving error boundaries with layouts in the rendering.
269
- const layoutErrorPaths = discoverLayoutAlignedErrors(segments, appDir);
278
+ const layoutErrorPaths = discoverLayoutAlignedErrors(segments, appDir, matcher);
270
279
  // Discover loading, error in the route's directory
271
280
  const routeDir = dir === "." ? appDir : path.join(appDir, dir);
272
- const loadingPath = findFile(routeDir, "loading");
273
- const errorPath = findFile(routeDir, "error");
281
+ const loadingPath = findFile(routeDir, "loading", matcher);
282
+ const errorPath = findFile(routeDir, "error", matcher);
274
283
  // Discover not-found/forbidden/unauthorized: walk from route directory up to root (nearest wins).
275
- const notFoundPath = discoverBoundaryFile(segments, appDir, "not-found");
276
- const forbiddenPath = discoverBoundaryFile(segments, appDir, "forbidden");
277
- const unauthorizedPath = discoverBoundaryFile(segments, appDir, "unauthorized");
284
+ const notFoundPath = discoverBoundaryFile(segments, appDir, "not-found", matcher);
285
+ const forbiddenPath = discoverBoundaryFile(segments, appDir, "forbidden", matcher);
286
+ const unauthorizedPath = discoverBoundaryFile(segments, appDir, "unauthorized", matcher);
278
287
  // Discover per-layout not-found files (one per layout directory).
279
288
  // These are used for per-layout NotFoundBoundary to match Next.js behavior where
280
289
  // notFound() thrown from a layout is caught by the parent layout's boundary.
281
- const notFoundPaths = discoverBoundaryFilePerLayout(layouts, "not-found");
290
+ const notFoundPaths = discoverBoundaryFilePerLayout(layouts, "not-found", matcher);
282
291
  // Discover parallel slots (@team, @analytics, etc.).
283
292
  // Slots at the route's own directory use page.tsx; slots at ancestor directories
284
293
  // (inherited from parent layouts) use default.tsx as fallback.
285
- const parallelSlots = discoverInheritedParallelSlots(segments, appDir, routeDir);
294
+ const parallelSlots = discoverInheritedParallelSlots(segments, appDir, routeDir, matcher);
286
295
  return {
287
296
  pattern: pattern === "/" ? "/" : pattern,
288
297
  pagePath: type === "page" ? path.join(appDir, file) : null,
@@ -307,12 +316,12 @@ function fileToAppRoute(file, appDir, type) {
307
316
  * Root layout = 0, then each directory level that contributes a URL segment
308
317
  * increments the depth. Route groups and parallel slots don't contribute.
309
318
  */
310
- function computeLayoutSegmentDepths(segments, appDir, layouts) {
319
+ function computeLayoutSegmentDepths(segments, appDir, layouts, matcher) {
311
320
  // Build a map: layout file path → depth in URL segments
312
321
  // Walk the segments directory-by-directory, tracking cumulative URL depth
313
322
  const depthMap = new Map();
314
323
  // Root layout (at appDir) always has depth 0
315
- const rootLayout = findFile(appDir, "layout");
324
+ const rootLayout = findFile(appDir, "layout", matcher);
316
325
  if (rootLayout)
317
326
  depthMap.set(rootLayout, 0);
318
327
  let urlDepth = 0;
@@ -325,7 +334,7 @@ function computeLayoutSegmentDepths(segments, appDir, layouts) {
325
334
  if (!isRouteGroup && !isParallelSlot) {
326
335
  urlDepth++;
327
336
  }
328
- const layout = findFile(currentDir, "layout");
337
+ const layout = findFile(currentDir, "layout", matcher);
329
338
  if (layout) {
330
339
  depthMap.set(layout, urlDepth);
331
340
  }
@@ -337,17 +346,17 @@ function computeLayoutSegmentDepths(segments, appDir, layouts) {
337
346
  * Discover all layout files from root to the given directory.
338
347
  * Each level of the directory tree may have a layout.tsx.
339
348
  */
340
- function discoverLayouts(segments, appDir) {
349
+ function discoverLayouts(segments, appDir, matcher) {
341
350
  const layouts = [];
342
351
  // Check root layout
343
- const rootLayout = findFile(appDir, "layout");
352
+ const rootLayout = findFile(appDir, "layout", matcher);
344
353
  if (rootLayout)
345
354
  layouts.push(rootLayout);
346
355
  // Check each directory level
347
356
  let currentDir = appDir;
348
357
  for (const segment of segments) {
349
358
  currentDir = path.join(currentDir, segment);
350
- const layout = findFile(currentDir, "layout");
359
+ const layout = findFile(currentDir, "layout", matcher);
351
360
  if (layout)
352
361
  layouts.push(layout);
353
362
  }
@@ -358,17 +367,17 @@ function discoverLayouts(segments, appDir) {
358
367
  * Each level of the directory tree may have a template.tsx.
359
368
  * Templates are like layouts but re-mount on navigation.
360
369
  */
361
- function discoverTemplates(segments, appDir) {
370
+ function discoverTemplates(segments, appDir, matcher) {
362
371
  const templates = [];
363
372
  // Check root template
364
- const rootTemplate = findFile(appDir, "template");
373
+ const rootTemplate = findFile(appDir, "template", matcher);
365
374
  if (rootTemplate)
366
375
  templates.push(rootTemplate);
367
376
  // Check each directory level
368
377
  let currentDir = appDir;
369
378
  for (const segment of segments) {
370
379
  currentDir = path.join(currentDir, segment);
371
- const template = findFile(currentDir, "template");
380
+ const template = findFile(currentDir, "template", matcher);
372
381
  if (template)
373
382
  templates.push(template);
374
383
  }
@@ -385,20 +394,20 @@ function discoverTemplates(segments, appDir) {
385
394
  * rendering tree, matching Next.js behavior where each segment independently
386
395
  * wraps its children with an error boundary.
387
396
  */
388
- function discoverLayoutAlignedErrors(segments, appDir) {
397
+ function discoverLayoutAlignedErrors(segments, appDir, matcher) {
389
398
  const errors = [];
390
399
  // Root level (only if root has a layout — matching discoverLayouts logic)
391
- const rootLayout = findFile(appDir, "layout");
400
+ const rootLayout = findFile(appDir, "layout", matcher);
392
401
  if (rootLayout) {
393
- errors.push(findFile(appDir, "error"));
402
+ errors.push(findFile(appDir, "error", matcher));
394
403
  }
395
404
  // Check each directory level
396
405
  let currentDir = appDir;
397
406
  for (const segment of segments) {
398
407
  currentDir = path.join(currentDir, segment);
399
- const layout = findFile(currentDir, "layout");
408
+ const layout = findFile(currentDir, "layout", matcher);
400
409
  if (layout) {
401
- errors.push(findFile(currentDir, "error"));
410
+ errors.push(findFile(currentDir, "error", matcher));
402
411
  }
403
412
  }
404
413
  return errors;
@@ -408,7 +417,7 @@ function discoverLayoutAlignedErrors(segments, appDir) {
408
417
  * by walking from the route's directory up to the app root.
409
418
  * Returns the first (closest) file found, or null.
410
419
  */
411
- function discoverBoundaryFile(segments, appDir, fileName) {
420
+ function discoverBoundaryFile(segments, appDir, fileName, matcher) {
412
421
  // Build all directory paths from leaf to root
413
422
  const dirs = [];
414
423
  let dir = appDir;
@@ -419,7 +428,7 @@ function discoverBoundaryFile(segments, appDir, fileName) {
419
428
  }
420
429
  // Walk from leaf (last) to root (first)
421
430
  for (let i = dirs.length - 1; i >= 0; i--) {
422
- const f = findFile(dirs[i], fileName);
431
+ const f = findFile(dirs[i], fileName, matcher);
423
432
  if (f)
424
433
  return f;
425
434
  }
@@ -434,10 +443,10 @@ function discoverBoundaryFile(segments, appDir, fileName) {
434
443
  * has its own boundary that wraps the layout's children. When notFound() is thrown
435
444
  * from a layout, it propagates up to the parent layout's boundary.
436
445
  */
437
- function discoverBoundaryFilePerLayout(layouts, fileName) {
446
+ function discoverBoundaryFilePerLayout(layouts, fileName, matcher) {
438
447
  return layouts.map((layoutPath) => {
439
448
  const layoutDir = path.dirname(layoutPath);
440
- return findFile(layoutDir, fileName);
449
+ return findFile(layoutDir, fileName, matcher);
441
450
  });
442
451
  }
443
452
  /**
@@ -452,25 +461,25 @@ function discoverBoundaryFilePerLayout(layouts, fileName) {
452
461
  * that has @slot dirs, collect them. Slots at the route's own directory level
453
462
  * use page.tsx; slots at ancestor levels use default.tsx only.
454
463
  */
455
- function discoverInheritedParallelSlots(segments, appDir, routeDir) {
464
+ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher) {
456
465
  const slotMap = new Map();
457
466
  // Walk from appDir through each segment, tracking layout indices.
458
467
  // layoutIndex tracks which position in the route's layouts[] array corresponds
459
468
  // to a given directory. Only directories with a layout.tsx file increment.
460
469
  let currentDir = appDir;
461
470
  const dirsToCheck = [];
462
- let layoutIdx = findFile(appDir, "layout") ? 0 : -1;
471
+ let layoutIdx = findFile(appDir, "layout", matcher) ? 0 : -1;
463
472
  dirsToCheck.push({ dir: appDir, layoutIdx: Math.max(layoutIdx, 0) });
464
473
  for (const segment of segments) {
465
474
  currentDir = path.join(currentDir, segment);
466
- if (findFile(currentDir, "layout")) {
475
+ if (findFile(currentDir, "layout", matcher)) {
467
476
  layoutIdx++;
468
477
  }
469
478
  dirsToCheck.push({ dir: currentDir, layoutIdx: Math.max(layoutIdx, 0) });
470
479
  }
471
480
  for (const { dir, layoutIdx: lvlLayoutIdx } of dirsToCheck) {
472
481
  const isOwnDir = dir === routeDir;
473
- const slotsAtLevel = discoverParallelSlots(dir, appDir);
482
+ const slotsAtLevel = discoverParallelSlots(dir, appDir, matcher);
474
483
  for (const slot of slotsAtLevel) {
475
484
  if (isOwnDir) {
476
485
  // At the route's own directory: use page.tsx (normal behavior)
@@ -499,7 +508,7 @@ function discoverInheritedParallelSlots(segments, appDir, routeDir) {
499
508
  * Discover parallel route slots (@team, @analytics, etc.) in a directory.
500
509
  * Returns a ParallelSlot for each @-prefixed subdirectory that has a page or default component.
501
510
  */
502
- function discoverParallelSlots(dir, appDir) {
511
+ function discoverParallelSlots(dir, appDir, matcher) {
503
512
  if (!fs.existsSync(dir))
504
513
  return [];
505
514
  const entries = fs.readdirSync(dir, { withFileTypes: true });
@@ -509,9 +518,9 @@ function discoverParallelSlots(dir, appDir) {
509
518
  continue;
510
519
  const slotName = entry.name.slice(1); // "@team" -> "team"
511
520
  const slotDir = path.join(dir, entry.name);
512
- const pagePath = findFile(slotDir, "page");
513
- const defaultPath = findFile(slotDir, "default");
514
- const interceptingRoutes = discoverInterceptingRoutes(slotDir, dir, appDir);
521
+ const pagePath = findFile(slotDir, "page", matcher);
522
+ const defaultPath = findFile(slotDir, "default", matcher);
523
+ const interceptingRoutes = discoverInterceptingRoutes(slotDir, dir, appDir, matcher);
515
524
  // Only include slots that have at least a page, default, or intercepting route
516
525
  if (!pagePath && !defaultPath && interceptingRoutes.length === 0)
517
526
  continue;
@@ -519,9 +528,9 @@ function discoverParallelSlots(dir, appDir) {
519
528
  name: slotName,
520
529
  pagePath,
521
530
  defaultPath,
522
- layoutPath: findFile(slotDir, "layout"),
523
- loadingPath: findFile(slotDir, "loading"),
524
- errorPath: findFile(slotDir, "error"),
531
+ layoutPath: findFile(slotDir, "layout", matcher),
532
+ loadingPath: findFile(slotDir, "loading", matcher),
533
+ errorPath: findFile(slotDir, "error", matcher),
525
534
  interceptingRoutes,
526
535
  layoutIndex: -1, // Will be set by discoverInheritedParallelSlots
527
536
  });
@@ -548,19 +557,19 @@ const INTERCEPT_PATTERNS = [
548
557
  * @param routeDir - The directory of the route that owns this slot (e.g. app/feed)
549
558
  * @param appDir - The root app directory
550
559
  */
551
- function discoverInterceptingRoutes(slotDir, routeDir, appDir) {
560
+ function discoverInterceptingRoutes(slotDir, routeDir, appDir, matcher) {
552
561
  if (!fs.existsSync(slotDir))
553
562
  return [];
554
563
  const results = [];
555
564
  // Recursively scan for page files inside intercepting directories
556
- scanForInterceptingPages(slotDir, routeDir, appDir, results);
565
+ scanForInterceptingPages(slotDir, routeDir, appDir, results, matcher);
557
566
  return results;
558
567
  }
559
568
  /**
560
569
  * Recursively scan a directory tree for page.tsx files that are inside
561
570
  * intercepting route directories.
562
571
  */
563
- function scanForInterceptingPages(currentDir, routeDir, appDir, results) {
572
+ function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher) {
564
573
  if (!fs.existsSync(currentDir))
565
574
  return;
566
575
  const entries = fs.readdirSync(currentDir, { withFileTypes: true });
@@ -575,11 +584,11 @@ function scanForInterceptingPages(currentDir, routeDir, appDir, results) {
575
584
  const restOfName = entry.name.slice(interceptMatch.prefix.length);
576
585
  const interceptDir = path.join(currentDir, entry.name);
577
586
  // Find page files within this intercepting directory tree
578
- collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, results);
587
+ collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, results, matcher);
579
588
  }
580
589
  else {
581
590
  // Regular subdirectory — keep scanning for intercepting dirs
582
- scanForInterceptingPages(path.join(currentDir, entry.name), routeDir, appDir, results);
591
+ scanForInterceptingPages(path.join(currentDir, entry.name), routeDir, appDir, results, matcher);
583
592
  }
584
593
  }
585
594
  }
@@ -598,9 +607,9 @@ function matchInterceptConvention(name) {
598
607
  * Collect page.tsx files inside an intercepting route directory tree
599
608
  * and compute their target URL patterns.
600
609
  */
601
- function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, results) {
610
+ function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, results, matcher) {
602
611
  // Check for page.tsx in current directory
603
- const page = findFile(currentDir, "page");
612
+ const page = findFile(currentDir, "page", matcher);
604
613
  if (page) {
605
614
  const targetPattern = computeInterceptTarget(convention, interceptSegment, currentDir, interceptRoot, routeDir, appDir);
606
615
  if (targetPattern) {
@@ -619,7 +628,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
619
628
  for (const entry of entries) {
620
629
  if (!entry.isDirectory())
621
630
  continue;
622
- collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, results);
631
+ collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, results, matcher);
623
632
  }
624
633
  }
625
634
  /**
@@ -703,11 +712,10 @@ function computeInterceptTarget(convention, interceptSegment, currentDir, interc
703
712
  }
704
713
  /**
705
714
  * Find a file by name (without extension) in a directory.
706
- * Checks .tsx, .ts, .jsx, .js extensions.
715
+ * Checks configured pageExtensions.
707
716
  */
708
- function findFile(dir, name) {
709
- const extensions = [".tsx", ".ts", ".jsx", ".js"];
710
- for (const ext of extensions) {
717
+ function findFile(dir, name, matcher) {
718
+ for (const ext of matcher.dottedExtensions) {
711
719
  const filePath = path.join(dir, name + ext);
712
720
  if (fs.existsSync(filePath))
713
721
  return filePath;
@@ -768,45 +776,4 @@ function matchPattern(url, pattern) {
768
776
  return null;
769
777
  return params;
770
778
  }
771
- /**
772
- * Route precedence — lower score is higher priority.
773
- * Matches Next.js specificity rules:
774
- * 1. Static routes first (scored by segment count, more = more specific)
775
- * 2. Dynamic segments penalized by position
776
- * 3. Catch-all comes after dynamic
777
- * 4. Optional catch-all last
778
- * 5. Lexicographic tiebreaker for determinism
779
- *
780
- * Key insight: routes with static prefix segments should have higher priority
781
- * than catch-all routes without them. E.g., /_sites/:subdomain/:slug* should
782
- * match before /:slug* because "_sites" must match exactly.
783
- */
784
- function routePrecedence(pattern) {
785
- const parts = pattern.split("/").filter(Boolean);
786
- let score = 0;
787
- let staticPrefixCount = 0;
788
- // Count static prefix segments (before first dynamic/catch-all)
789
- for (const p of parts) {
790
- if (p.startsWith(":") || p.endsWith("+") || p.endsWith("*"))
791
- break;
792
- staticPrefixCount++;
793
- }
794
- // Static prefix segments dramatically reduce score (increase priority).
795
- // Each static prefix segment gives -10000 priority boost.
796
- score -= staticPrefixCount * 10000;
797
- for (let i = 0; i < parts.length; i++) {
798
- const p = parts[i];
799
- if (p.endsWith("+")) {
800
- score += 1000 + i; // catch-all: moderate penalty
801
- }
802
- else if (p.endsWith("*")) {
803
- score += 2000 + i; // optional catch-all: high penalty
804
- }
805
- else if (p.startsWith(":")) {
806
- score += 100 + i; // dynamic: small penalty by position
807
- }
808
- // static segments after first dynamic don't contribute extra
809
- }
810
- return score;
811
- }
812
779
  //# sourceMappingURL=app-router.js.map