(
name: string,
options: { params?: NavParams; query?: NavQuery } = {}
)
| 63 | |
| 64 | // Fill `:param` placeholders and append a query string. |
| 65 | export function resolvePath( |
| 66 | name: string, |
| 67 | options: { params?: NavParams; query?: NavQuery } = {} |
| 68 | ): string { |
| 69 | const pattern = nameIndex.get(name); |
| 70 | if (!pattern) { |
| 71 | // Unknown name → root, so a misconfigured redirect can't throw mid-guard. |
| 72 | console.warn(`resolvePath: no route registered for name "${name}"`); |
| 73 | return "/"; |
| 74 | } |
| 75 | let path = pattern; |
| 76 | if (options.params) { |
| 77 | for (const [key, value] of Object.entries(options.params)) { |
| 78 | if (value === undefined) continue; |
| 79 | const single = Array.isArray(value) ? (value[0] ?? "") : value; |
| 80 | // Match `:key` only when NOT followed by another identifier char, |
| 81 | // so `:project` doesn't substring-match inside `:projectId` and |
| 82 | // turn `/projects/:projectId/...` into `/projects/<v>Id/...`. The |
| 83 | // collision shows up when callers merge in inherited route params |
| 84 | // (e.g. the SQL editor's `:project` clobbers the issue route's |
| 85 | // `:projectId`). React-router param names are restricted to |
| 86 | // `[A-Za-z0-9_]+`, so the negative lookahead is safe — no regex |
| 87 | // escape needed. |
| 88 | path = path.replace( |
| 89 | new RegExp(`:${key}(?![A-Za-z0-9_])`), |
| 90 | encodeURIComponent(single) |
| 91 | ); |
| 92 | } |
| 93 | } |
| 94 | if (options.query) { |
| 95 | const qs = buildSearchString(options.query); |
| 96 | if (qs) path = `${path}?${qs}`; |
| 97 | } |
| 98 | return path; |
| 99 | } |
| 100 | |
| 101 | // The createBrowserRouter instance, registered by the app root in Phase 4 so |
| 102 | // non-component code (the auth slice) can navigate without importing the root |
no test coverage detected