(ctx: ResolutionContext, onYield: MaybeYield)
| 47 | const MAX_LINKS_PER_COMPONENT = 24; |
| 48 | |
| 49 | export async function tanstackLinkEdges(ctx: ResolutionContext, onYield: MaybeYield): Promise<Edge[]> { |
| 50 | const table = tanstackTable(ctx); |
| 51 | if (table.byRoot.size === 0) return []; |
| 52 | const edges: Edge[] = []; |
| 53 | const seen = new Set<string>(); |
| 54 | const perComponent = new Map<string, number>(); |
| 55 | let scanned = 0; |
| 56 | for (const file of ctx.getAllFiles()) { |
| 57 | if (!JSX_FILE.test(file) || isTestPath(file)) continue; |
| 58 | const routes = routesForFile(table, file); |
| 59 | if (!routes || routes.exact.size === 0) continue; |
| 60 | if ((++scanned & 63) === 0) await onYield(); |
| 61 | const source = ctx.readFile(file); |
| 62 | if (!source || !HAS_LINK_TAG.test(source)) continue; |
| 63 | const safe = stripCommentsForRegex(source, 'typescript'); |
| 64 | const nodes = ctx.getNodesInFile(file); |
| 65 | const lineOf = makeLineAt(safe, 1); |
| 66 | LINK_TAG.lastIndex = 0; |
| 67 | let m: RegExpExecArray | null; |
| 68 | while ((m = LINK_TAG.exec(safe)) !== null) { |
| 69 | let literal: string | null = m[3] ?? m[4] ?? null; |
| 70 | if (literal === null) { |
| 71 | // `to={…}`: a string or a template. |
| 72 | const at = m.index + m[0].length; |
| 73 | const ch = safe[at]; |
| 74 | if (ch === '"' || ch === "'" || ch === '`') literal = readStringAt(safe, at); |
| 75 | } |
| 76 | if (literal === null) continue; |
| 77 | const href = tanstackDestination(JSON.stringify(literal)); |
| 78 | if (!href) continue; |
| 79 | const line = lineOf(m.index); |
| 80 | const component = enclosingFn(nodes, line); |
| 81 | if (!component) continue; |
| 82 | // A destination written as a choice names one route per arm, and the |
| 83 | // user reaches every one of them — each is drawn. |
| 84 | for (const { node: route, href: arm } of destinationsForHref(href, routes)) { |
| 85 | const key = `${component.id}>${route.id}`; |
| 86 | if (seen.has(key)) continue; |
| 87 | const count = (perComponent.get(component.id) ?? 0) + 1; |
| 88 | perComponent.set(component.id, count); |
| 89 | if (count > MAX_LINKS_PER_COMPONENT) continue; |
| 90 | seen.add(key); |
| 91 | edges.push({ |
| 92 | source: component.id, |
| 93 | target: route.id, |
| 94 | kind: 'navigates', |
| 95 | line, |
| 96 | provenance: 'heuristic', |
| 97 | metadata: { |
| 98 | synthesizedBy: 'tanstack-link', |
| 99 | href: arm.display, |
| 100 | navMethod: m[1] === 'Navigate' ? 'navigate' : 'link', |
| 101 | registeredAt: `${file}:${line}`, |
| 102 | }, |
| 103 | }); |
| 104 | } |
| 105 | } |
| 106 | } |
no test coverage detected