@timber-js/app 0.2.0-alpha.90 → 0.2.0-alpha.92

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.
@@ -388,6 +388,213 @@ function findPageExtFile(dirPath, name, extSet) {
388
388
  }
389
389
  }
390
390
  //#endregion
391
+ //#region src/routing/codegen-shared.ts
392
+ /**
393
+ * Shared codegen helpers — import-path computation, codec chain type
394
+ * builder, and searchParams type formatter.
395
+ *
396
+ * Extracted from `codegen.ts` so `link-codegen.ts` can use the same
397
+ * helpers without a cyclic import.
398
+ */
399
+ /**
400
+ * Compute a relative import specifier for a codec/page file, stripping
401
+ * the .ts/.tsx extension and resolving against the codegen output dir.
402
+ */
403
+ function codecImportPath(codecFilePath, importBase) {
404
+ const absPath = codecFilePath.replace(/\.(ts|tsx)$/, "");
405
+ if (importBase) return "./" + relative(importBase, absPath).replace(/\\/g, "/");
406
+ return "./" + posix.basename(absPath);
407
+ }
408
+ /** Name of the shared helper type emitted at the top of the .d.ts. */
409
+ var RESOLVE_SEGMENT_FIELD_TYPE_NAME = "_TimberResolveSegmentField";
410
+ /**
411
+ * Helper type emitted once at the top of the generated `.d.ts` and
412
+ * referenced by every codec-chain conditional. Without this shared
413
+ * helper, the inline expansion would duplicate the fallback branch on
414
+ * every step and grow O(2^N) in chain depth (a single deep nested route
415
+ * could blow up the file size and TS performance). With the helper,
416
+ * each step reuses the named type and growth is O(N).
417
+ *
418
+ * The helper is a 2-arg conditional: given a typeof import expression
419
+ * (`Def`), a key (`K`), and a fallback (`F`), it returns `T[K]` if
420
+ * `Def extends ParamsDefinition<T>` and `K extends keyof T`, otherwise
421
+ * `F`. The codec chain composes calls to this helper.
422
+ */
423
+ function emitResolveSegmentFieldHelper() {
424
+ return [
425
+ `type ${RESOLVE_SEGMENT_FIELD_TYPE_NAME}<Def, K extends string, F> =`,
426
+ ` Def extends import('@timber-js/app/segment-params').ParamsDefinition<infer T>`,
427
+ ` ? K extends keyof T ? T[K] : F`,
428
+ ` : F;`
429
+ ].join("\n");
430
+ }
431
+ /**
432
+ * Build a TypeScript type expression that resolves a single param's
433
+ * codec by walking a chain of params.ts files in priority order.
434
+ *
435
+ * Each entry in the chain emits one application of the shared
436
+ * `_TimberResolveSegmentField` helper type. Composing N applications
437
+ * grows linearly with chain depth (O(N) characters), unlike an inline
438
+ * conditional that would duplicate the fallback in each branch and
439
+ * grow O(2^N).
440
+ *
441
+ * The closest match (position 0 in the chain) is checked first; if its
442
+ * `segmentParams` definition declares the key, its inferred type wins.
443
+ * Otherwise we fall through to the next entry, and finally to the
444
+ * provided fallback. See TIM-834.
445
+ */
446
+ function buildCodecChainType(p, importBase, fallback) {
447
+ const files = p.codecFilePaths;
448
+ if (!files || files.length === 0) return fallback;
449
+ const key = JSON.stringify(p.name);
450
+ let inner = fallback;
451
+ for (let i = files.length - 1; i >= 0; i--) inner = `${RESOLVE_SEGMENT_FIELD_TYPE_NAME}<(typeof import('${codecImportPath(files[i], importBase)}'))['segmentParams'], ${key}, ${inner}>`;
452
+ return inner;
453
+ }
454
+ /**
455
+ * Format the searchParams type for a route entry.
456
+ *
457
+ * When a page.tsx (or params.ts) exports searchParams, we reference its
458
+ * inferred type via an import type. The import path is relative to
459
+ * `importBase` (the directory where the .d.ts will be written). When
460
+ * importBase is undefined, falls back to a bare relative path.
461
+ */
462
+ function formatSearchParamsType(route, importBase) {
463
+ if (route.hasSearchParams && route.searchParamsPagePath) return `(typeof import('${codecImportPath(route.searchParamsPagePath, importBase)}'))['searchParams'] extends import('@timber-js/app/search-params').SearchParamsDefinition<infer T> ? T : never`;
464
+ return "{}";
465
+ }
466
+ //#endregion
467
+ //#region src/routing/link-codegen.ts
468
+ /** Shared Link base-props type literal used in every emitted call signature. */
469
+ var LINK_BASE_PROPS_TYPE = "Omit<import('react').AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> & { prefetch?: boolean; scroll?: boolean; preserveSearchParams?: true | string[]; onNavigate?: import('./client/link.js').OnNavigateHandler; children?: import('react').ReactNode }";
470
+ /**
471
+ * Build a TypeScript template literal pattern for a dynamic route.
472
+ * e.g. '/products/[id]' → '/products/${string}'
473
+ * '/blog/[...slug]' → '/blog/${string}'
474
+ * '/docs/[[...path]]' → '/docs/${string}' (also matches /docs)
475
+ * '/[org]/[repo]' → '/${string}/${string}'
476
+ */
477
+ function buildResolvedPattern(route) {
478
+ return route.urlPath.split("/").map((part) => {
479
+ if (part.startsWith("[[...") && part.endsWith("]]")) return "${string}";
480
+ if (part.startsWith("[...") && part.endsWith("]")) return "${string}";
481
+ if (part.startsWith("[") && part.endsWith("]")) return "${string}";
482
+ return part;
483
+ }).join("/");
484
+ }
485
+ /**
486
+ * Format the segmentParams type for Link overloads.
487
+ *
488
+ * Link params accept `string | number` for single dynamic segments
489
+ * (convenience — values are stringified at runtime). Catch-all and
490
+ * optional catch-all remain `string[]` / `string[] | undefined`.
491
+ *
492
+ * When the segment's params chain (TIM-834) declares a typed codec, the
493
+ * inferred type from the codec wins via a nested conditional.
494
+ */
495
+ function formatLinkParamsType(params, importBase) {
496
+ if (params.length === 0) return "{}";
497
+ return `{ ${params.map((p) => {
498
+ const codecType = buildCodecChainType(p, importBase, p.type === "string" ? "string | number" : p.type);
499
+ return `${p.name}: ${codecType}`;
500
+ }).join("; ")} }`;
501
+ }
502
+ /**
503
+ * Catch-all call signatures for `<Link>` — external hrefs and computed
504
+ * `string` variables. Emitted from codegen in a SEPARATE
505
+ * `declare module` block (declared AFTER the per-route block) so the TS
506
+ * "later overload set ordered first" rule places these catch-all
507
+ * signatures ahead of per-route in resolution order, leaving per-route
508
+ * as the final overload whose error message is reported on failure.
509
+ *
510
+ * The conditional `string extends H ? ... : never` protection preserves
511
+ * TIM-624's guarantee that unknown internal path literals don't match
512
+ * the catch-all — typos like `<Link href="/typo" />` still error.
513
+ */
514
+ function formatLinkCatchAllOverloads() {
515
+ const lines = [];
516
+ const baseProps = LINK_BASE_PROPS_TYPE;
517
+ const catchAllSearchParams = "{ definition: SearchParamsDefinition<Record<string, unknown>>; values: Record<string, unknown> } | Record<string, unknown>";
518
+ const externalHref = "`http://${string}` | `https://${string}` | `mailto:${string}` | `tel:${string}` | `ftp://${string}` | `//${string}` | `#${string}` | `?${string}`";
519
+ lines.push(" // Typed Link overloads — catch-all (block 2 / emitted second)");
520
+ lines.push(" interface LinkFunction {");
521
+ lines.push(` (props: ${baseProps} & {`);
522
+ lines.push(` href: ${externalHref}`);
523
+ lines.push(` segmentParams?: never`);
524
+ lines.push(` searchParams?: ${catchAllSearchParams}`);
525
+ lines.push(` }): import('react').JSX.Element`);
526
+ lines.push(` <H extends string>(`);
527
+ lines.push(` props: string extends H`);
528
+ lines.push(` ? ${baseProps} & {`);
529
+ lines.push(` href: H`);
530
+ lines.push(` segmentParams?: Record<string, string | number | string[]>`);
531
+ lines.push(` searchParams?: ${catchAllSearchParams}`);
532
+ lines.push(` }`);
533
+ lines.push(` : never`);
534
+ lines.push(` ): import('react').JSX.Element`);
535
+ lines.push(" }");
536
+ return lines;
537
+ }
538
+ /**
539
+ * Generate typed per-route Link call signatures via LinkFunction
540
+ * interface merging.
541
+ *
542
+ * TIM-835: This emits a SINGLE call signature whose props is a
543
+ * discriminated union keyed on `href`. TypeScript narrows the union by
544
+ * the literal `href` at the call site, then checks the rest of the
545
+ * props against the matched variant. When `segmentParams` or
546
+ * `searchParams` is wrong, TS reports the error against the matched
547
+ * variant — naming the user's actual `href` and the offending field.
548
+ *
549
+ * Before TIM-835, this function emitted N separate per-route overloads
550
+ * (one per route, sometimes two for dynamic routes). When ALL overloads
551
+ * failed, TS would pick an arbitrary failed overload (heuristically the
552
+ * "last tried") to render the diagnostic, often pointing at a route
553
+ * completely unrelated to the one the user wrote. The discriminated
554
+ * union sidesteps overload-resolution heuristics entirely.
555
+ *
556
+ * Open property shapes (`Record<string, unknown>` instead of `never`)
557
+ * for `segmentParams` on routes that don't declare them keep generic
558
+ * `LinkProps`-spreading wrappers compiling. (Aligned with TIM-833.)
559
+ *
560
+ * TIM-832: this function still emits the per-route block FIRST and the
561
+ * catch-all block follows, so per-route remains the LAST overload set in
562
+ * resolution order — the one TS reports against on failure. With a
563
+ * discriminated union there is only one call signature in this block,
564
+ * so the resolved-template-vs-pattern ordering reduces to placing the
565
+ * pattern variant before the resolved-template variant inside the union.
566
+ */
567
+ function formatTypedLinkOverloads(routes, importBase) {
568
+ const lines = [];
569
+ const baseProps = LINK_BASE_PROPS_TYPE;
570
+ const variants = [];
571
+ for (const route of routes) {
572
+ const hasDynamicParams = route.params.length > 0;
573
+ const paramsProp = hasDynamicParams ? `segmentParams: ${formatLinkParamsType(route.params, importBase)}` : "segmentParams?: Record<string, unknown>";
574
+ const searchParamsType = route.hasSearchParams ? formatSearchParamsType(route, importBase) : null;
575
+ const patternSearchParamsProp = searchParamsType ? `searchParams?: Partial<${searchParamsType}>` : "searchParams?: Record<string, never>";
576
+ variants.push(`${baseProps} & { href: '${route.urlPath}'; ${paramsProp}; ${patternSearchParamsProp} }`);
577
+ if (hasDynamicParams) {
578
+ const templatePattern = buildResolvedPattern(route);
579
+ if (templatePattern) {
580
+ const resolvedSearchParamsProp = searchParamsType ? `searchParams?: { definition: SearchParamsDefinition<${searchParamsType}>; values: Partial<${searchParamsType}> }` : "searchParams?: Record<string, never>";
581
+ variants.push(`${baseProps} & { href: \`${templatePattern}\`; segmentParams?: Record<string, never>; ${resolvedSearchParamsProp} }`);
582
+ }
583
+ }
584
+ }
585
+ lines.push(" interface LinkFunction {");
586
+ if (variants.length === 0) {
587
+ lines.push(" }");
588
+ return lines;
589
+ }
590
+ lines.push(" (");
591
+ lines.push(" props:");
592
+ for (const variant of variants) lines.push(` | (${variant})`);
593
+ lines.push(` ): import('react').JSX.Element`);
594
+ lines.push(" }");
595
+ return lines;
596
+ }
597
+ //#endregion
391
598
  //#region src/routing/codegen.ts
392
599
  /**
393
600
  * Route map codegen.
@@ -406,7 +613,7 @@ function findPageExtFile(dirPath, name, extSet) {
406
613
  */
407
614
  function generateRouteMap(tree, options = {}) {
408
615
  const routes = [];
409
- collectRoutes(tree.root, [], routes);
616
+ collectRoutes(tree.root, [], [], routes);
410
617
  routes.sort((a, b) => a.urlPath.localeCompare(b.urlPath));
411
618
  return formatDeclarationFile(routes, options.outputDir ?? options.appDir);
412
619
  }
@@ -416,22 +623,33 @@ function generateRouteMap(tree, options = {}) {
416
623
  * A route entry is created for any segment that has a `page` or `route` file.
417
624
  * Params accumulate from ancestor dynamic segments.
418
625
  */
419
- function collectRoutes(node, ancestorParams, routes) {
626
+ function collectRoutes(node, ancestorParams, ancestorParamsFiles, routes) {
627
+ const ownParamsFile = node.params && fileHasExport(node.params.filePath, "segmentParams") ? node.params.filePath : void 0;
420
628
  const params = [...ancestorParams];
421
629
  if (node.paramName) {
422
- const codecFile = findParamsExport(node);
630
+ const legacyFallback = ownParamsFile ? void 0 : findLegacyParamsExport(node);
423
631
  params.push({
424
632
  name: node.paramName,
425
633
  type: paramTypeForSegment(node.segmentType),
426
- codecFilePath: codecFile
634
+ legacyCodecFilePath: legacyFallback
427
635
  });
428
636
  }
637
+ const nextAncestorFiles = ownParamsFile ? [...ancestorParamsFiles, ownParamsFile] : ancestorParamsFiles;
429
638
  const isPage = !!node.page;
430
639
  const isApiRoute = !!node.route;
431
640
  if (isPage || isApiRoute) {
641
+ const leafFirstChain = nextAncestorFiles.length > 0 ? [...nextAncestorFiles].reverse() : [];
642
+ const resolvedParams = params.map((p) => {
643
+ const codecFilePaths = leafFirstChain.length > 0 ? leafFirstChain : p.legacyCodecFilePath ? [p.legacyCodecFilePath] : void 0;
644
+ return {
645
+ name: p.name,
646
+ type: p.type,
647
+ codecFilePaths
648
+ };
649
+ });
432
650
  const entry = {
433
651
  urlPath: node.urlPath,
434
- params: [...params],
652
+ params: resolvedParams,
435
653
  hasSearchParams: false,
436
654
  isApiRoute
437
655
  };
@@ -446,8 +664,8 @@ function collectRoutes(node, ancestorParams, routes) {
446
664
  }
447
665
  routes.push(entry);
448
666
  }
449
- for (const child of node.children) collectRoutes(child, params, routes);
450
- for (const [, slot] of node.slots) collectRoutes(slot, params, routes);
667
+ for (const child of node.children) collectRoutes(child, params, nextAncestorFiles, routes);
668
+ for (const [, slot] of node.slots) collectRoutes(slot, params, nextAncestorFiles, routes);
451
669
  }
452
670
  /**
453
671
  * Determine the TypeScript type for a segment's param.
@@ -476,14 +694,13 @@ function fileHasExport(filePath, exportName) {
476
694
  }
477
695
  }
478
696
  /**
479
- * Find which file exports `segmentParams` for a dynamic segment.
697
+ * Find a legacy `segmentParams` export on layout.tsx or page.tsx.
480
698
  *
481
- * Priority: params.ts (convention file) > layout > page.
482
- * params.ts is the canonical location (TIM-508). Layout/page fallback
483
- * exists for backward compat during migration.
699
+ * Backward-compat shim: TIM-508 made params.ts the canonical location
700
+ * for `segmentParams`. Layout/page exports are still accepted for the
701
+ * OWN segment only (not inherited by descendants — see TIM-834).
484
702
  */
485
- function findParamsExport(node) {
486
- if (node.params && fileHasExport(node.params.filePath, "segmentParams")) return node.params.filePath;
703
+ function findLegacyParamsExport(node) {
487
704
  if (node.layout && fileHasExport(node.layout.filePath, "segmentParams")) return node.layout.filePath;
488
705
  if (node.page && fileHasExport(node.page.filePath, "segmentParams")) return node.page.filePath;
489
706
  }
@@ -497,6 +714,8 @@ function formatDeclarationFile(routes, importBase) {
497
714
  lines.push("");
498
715
  lines.push("export {};");
499
716
  lines.push("");
717
+ lines.push(emitResolveSegmentFieldHelper());
718
+ lines.push("");
500
719
  lines.push("declare module '@timber-js/app' {");
501
720
  lines.push(" interface Routes {");
502
721
  for (const route of routes) {
@@ -550,59 +769,11 @@ function formatDeclarationFile(routes, importBase) {
550
769
  function formatParamsType(params, importBase) {
551
770
  if (params.length === 0) return "{}";
552
771
  return `{ ${params.map((p) => {
553
- if (p.codecFilePath) {
554
- const absPath = p.codecFilePath.replace(/\.(ts|tsx)$/, "");
555
- let importPath;
556
- if (importBase) importPath = "./" + relative(importBase, absPath).replace(/\\/g, "/");
557
- else importPath = "./" + posix.basename(absPath);
558
- const codecType = `(typeof import('${importPath}'))['segmentParams'] extends import('@timber-js/app/segment-params').ParamsDefinition<infer T> ? T[${JSON.stringify(p.name)}] : ${p.type}`;
559
- return `${p.name}: ${codecType}`;
560
- }
561
- return `${p.name}: ${p.type}`;
562
- }).join("; ")} }`;
563
- }
564
- /**
565
- * Format the params type for Link overloads.
566
- *
567
- * Link params accept `string | number` for single dynamic segments
568
- * (convenience — values are stringified at runtime). Catch-all and
569
- * optional catch-all remain `string[]` / `string[] | undefined`.
570
- *
571
- * Uses DIRECT types (not conditional) to preserve excess property checking.
572
- */
573
- function formatLinkParamsType(params, importBase) {
574
- if (params.length === 0) return "{}";
575
- return `{ ${params.map((p) => {
576
- if (p.codecFilePath) {
577
- const absPath = p.codecFilePath.replace(/\.(ts|tsx)$/, "");
578
- let importPath;
579
- if (importBase) importPath = "./" + relative(importBase, absPath).replace(/\\/g, "/");
580
- else importPath = "./" + posix.basename(absPath);
581
- const codecType = `(typeof import('${importPath}'))['segmentParams'] extends import('@timber-js/app/segment-params').ParamsDefinition<infer T> ? T[${JSON.stringify(p.name)}] : ${p.type}`;
582
- return `${p.name}: ${codecType}`;
583
- }
584
- const type = p.type === "string" ? "string | number" : p.type;
585
- return `${p.name}: ${type}`;
772
+ const codecType = buildCodecChainType(p, importBase, p.type);
773
+ return `${p.name}: ${codecType}`;
586
774
  }).join("; ")} }`;
587
775
  }
588
776
  /**
589
- * Format the searchParams type for a route entry.
590
- *
591
- * When a page.tsx exports searchParams, we reference its inferred type via an import type.
592
- * The import path is relative to `importBase` (the directory where the .d.ts will be
593
- * written). When importBase is undefined, falls back to a bare relative path.
594
- */
595
- function formatSearchParamsType(route, importBase) {
596
- if (route.hasSearchParams && route.searchParamsPagePath) {
597
- const absPath = route.searchParamsPagePath.replace(/\.(ts|tsx)$/, "");
598
- let importPath;
599
- if (importBase) importPath = "./" + relative(importBase, absPath).replace(/\\/g, "/");
600
- else importPath = "./" + posix.basename(absPath);
601
- return `(typeof import('${importPath}'))['searchParams'] extends import('@timber-js/app/search-params').SearchParamsDefinition<infer T> ? T : never`;
602
- }
603
- return "{}";
604
- }
605
- /**
606
777
  * Generate useQueryStates overloads.
607
778
  *
608
779
  * For each page route:
@@ -620,102 +791,6 @@ function formatUseQueryStatesOverloads(routes, importBase) {
620
791
  lines.push(" export function useQueryStates<T extends Record<string, unknown>>(codecs: { [K in keyof T]: SearchParamCodec<T[K]> }, options?: QueryStatesOptions): [T, SetParams<T>]");
621
792
  return lines;
622
793
  }
623
- /**
624
- * Build a TypeScript template literal pattern for a dynamic route.
625
- * e.g. '/products/[id]' → '/products/${string}'
626
- * '/blog/[...slug]' → '/blog/${string}'
627
- * '/docs/[[...path]]' → '/docs/${string}' (also matches /docs)
628
- * '/[org]/[repo]' → '/${string}/${string}'
629
- */
630
- function buildResolvedPattern(route) {
631
- return route.urlPath.split("/").map((part) => {
632
- if (part.startsWith("[[...") && part.endsWith("]]")) return "${string}";
633
- if (part.startsWith("[...") && part.endsWith("]")) return "${string}";
634
- if (part.startsWith("[") && part.endsWith("]")) return "${string}";
635
- return part;
636
- }).join("/");
637
- }
638
- /** Shared Link base-props type literal used in every emitted call signature. */
639
- var LINK_BASE_PROPS_TYPE = "Omit<import('react').AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> & { prefetch?: boolean; scroll?: boolean; preserveSearchParams?: true | string[]; onNavigate?: import('./client/link.js').OnNavigateHandler; children?: import('react').ReactNode }";
640
- /**
641
- * TIM-832: Catch-all call signatures for `<Link>` — external hrefs and
642
- * computed `string` variables. Emitted from codegen in a SEPARATE
643
- * `declare module` block (declared AFTER the per-route block) so the TS
644
- * "later overload set ordered first" rule places these catch-all
645
- * signatures ahead of per-route in resolution order, leaving per-route
646
- * as the final overload whose error message is reported on failure.
647
- *
648
- * The conditional `string extends H ? ... : never` protection preserves
649
- * TIM-624's guarantee that unknown internal path literals don't match
650
- * the catch-all — typos like `<Link href="/typo" />` still error.
651
- */
652
- function formatLinkCatchAllOverloads() {
653
- const lines = [];
654
- const baseProps = LINK_BASE_PROPS_TYPE;
655
- const catchAllSearchParams = "{ definition: SearchParamsDefinition<Record<string, unknown>>; values: Record<string, unknown> } | Record<string, unknown>";
656
- const externalHref = "`http://${string}` | `https://${string}` | `mailto:${string}` | `tel:${string}` | `ftp://${string}` | `//${string}` | `#${string}` | `?${string}`";
657
- lines.push(" // Typed Link overloads — catch-all (block 2 / emitted second)");
658
- lines.push(" interface LinkFunction {");
659
- lines.push(` (props: ${baseProps} & {`);
660
- lines.push(` href: ${externalHref}`);
661
- lines.push(` segmentParams?: never`);
662
- lines.push(` searchParams?: ${catchAllSearchParams}`);
663
- lines.push(` }): import('react').JSX.Element`);
664
- lines.push(` <H extends string>(`);
665
- lines.push(` props: string extends H`);
666
- lines.push(` ? ${baseProps} & {`);
667
- lines.push(` href: H`);
668
- lines.push(` segmentParams?: Record<string, string | number | string[]>`);
669
- lines.push(` searchParams?: ${catchAllSearchParams}`);
670
- lines.push(` }`);
671
- lines.push(` : never`);
672
- lines.push(` ): import('react').JSX.Element`);
673
- lines.push(" }");
674
- return lines;
675
- }
676
- /**
677
- * Generate typed per-route Link call signatures via LinkFunction
678
- * interface merging.
679
- *
680
- * Each call signature uses DIRECT types (not conditional types) for
681
- * segmentParams, preserving TypeScript's excess property checking.
682
- * Interface merging is the only reliable way to add "overloads" via
683
- * module augmentation — function overloads can't be augmented.
684
- *
685
- * TIM-832: this function emits ONLY the per-route block. The catch-all
686
- * block is emitted separately by `formatLinkCatchAllOverloads` inside a
687
- * second `declare module` so TS overload ordering reports per-route
688
- * errors (see `formatDeclarationFile`).
689
- */
690
- function formatTypedLinkOverloads(routes, importBase) {
691
- const lines = [];
692
- const baseProps = LINK_BASE_PROPS_TYPE;
693
- lines.push(" interface LinkFunction {");
694
- for (const route of routes) {
695
- const hasDynamicParams = route.params.length > 0;
696
- const paramsProp = hasDynamicParams ? `segmentParams: ${formatLinkParamsType(route.params, importBase)}` : "segmentParams?: never";
697
- const searchParamsType = route.hasSearchParams ? formatSearchParamsType(route, importBase) : null;
698
- const patternSearchParamsProp = searchParamsType ? `searchParams?: Partial<${searchParamsType}>` : "searchParams?: never";
699
- if (hasDynamicParams) {
700
- const templatePattern = buildResolvedPattern(route);
701
- if (templatePattern) {
702
- const resolvedSearchParamsProp = searchParamsType ? `searchParams?: { definition: SearchParamsDefinition<${searchParamsType}>; values: Partial<${searchParamsType}> }` : "searchParams?: never";
703
- lines.push(` (props: ${baseProps} & {`);
704
- lines.push(` href: \`${templatePattern}\``);
705
- lines.push(` segmentParams?: never`);
706
- lines.push(` ${resolvedSearchParamsProp}`);
707
- lines.push(` }): import('react').JSX.Element`);
708
- }
709
- }
710
- lines.push(` (props: ${baseProps} & {`);
711
- lines.push(` href: '${route.urlPath}'`);
712
- lines.push(` ${paramsProp}`);
713
- lines.push(` ${patternSearchParamsProp}`);
714
- lines.push(` }): import('react').JSX.Element`);
715
- }
716
- lines.push(" }");
717
- return lines;
718
- }
719
794
  //#endregion
720
795
  //#region src/routing/interception.ts
721
796
  /**
@@ -795,4 +870,4 @@ function computeInterceptedBase(parentUrlPath, marker) {
795
870
  //#endregion
796
871
  export { DEFAULT_PAGE_EXTENSIONS as a, scanRoutes as i, generateRouteMap as n, INTERCEPTION_MARKERS as o, classifySegment as r, collectInterceptionRewrites as t };
797
872
 
798
- //# sourceMappingURL=interception-DRlhJWbu.js.map
873
+ //# sourceMappingURL=interception-DSv3A2Zn.js.map