Files
klz-cables.com/.pnpm-store/v10/files/f6/bb7139e0843ac4c869719b430dae569672720882d660e8f80b635cfab2422a2b5c5a6198162530e2dd9a88ab0916a183fb556c8b0708f1edff8d471a53d906
Marc Mintel 5397309103
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
fix(products): fix breadcrumbs and product filtering (backport from main)
2026-02-24 16:04:21 +01:00

195 lines
5.9 KiB
Plaintext

Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const core = require('@sentry/core');
const debugBuild = require('../debug-build.js');
/**
* Strip the basename from a pathname if exists.
*
* Vendored and modified from `react-router`
* https://github.com/remix-run/react-router/blob/462bb712156a3f739d6139a0f14810b76b002df6/packages/router/utils.ts#L1038
*/
function stripBasenameFromPathname(pathname, basename) {
if (!basename || basename === '/') {
return pathname;
}
if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) {
return pathname;
}
// We want to leave trailing slash behavior in the user's control, so if they
// specify a basename with a trailing slash, we should support it
const startIndex = basename.endsWith('/') ? basename.length - 1 : basename.length;
const nextChar = pathname.charAt(startIndex);
if (nextChar && nextChar !== '/') {
// pathname does not start with basename/
return pathname;
}
return pathname.slice(startIndex) || '/';
}
// Cache for sorted manifests - keyed by manifest array reference
const SORTED_MANIFEST_CACHE = new WeakMap();
/**
* Matches a pathname against a route manifest and returns the matching pattern.
* Optionally strips a basename prefix before matching.
*/
function matchRouteManifest(pathname, manifest, basename) {
if (!pathname || !manifest || !manifest.length) {
return null;
}
const normalizedPathname = basename ? stripBasenameFromPathname(pathname, basename) : pathname;
let sorted = SORTED_MANIFEST_CACHE.get(manifest);
if (!sorted) {
sorted = sortBySpecificity(manifest);
SORTED_MANIFEST_CACHE.set(manifest, sorted);
debugBuild.DEBUG_BUILD && core.debug.log('[React Router] Sorted route manifest by specificity:', sorted.length, 'patterns');
}
for (const pattern of sorted) {
if (matchesPattern(normalizedPathname, pattern)) {
debugBuild.DEBUG_BUILD && core.debug.log('[React Router] Matched pathname', normalizedPathname, 'to pattern', pattern);
return pattern;
}
}
debugBuild.DEBUG_BUILD && core.debug.log('[React Router] No manifest match found for pathname:', normalizedPathname);
return null;
}
/**
* Checks if a pathname matches a route pattern.
*/
function matchesPattern(pathname, pattern) {
// Handle root path special case
if (pattern === '/') {
return pathname === '/' || pathname === '';
}
const pathSegments = splitPath(pathname);
const patternSegments = splitPath(pattern);
// Handle wildcard at end
const hasWildcard = patternSegments.length > 0 && patternSegments[patternSegments.length - 1] === '*';
if (hasWildcard) {
// Pattern with wildcard: path must have at least as many segments as pattern (minus wildcard)
const patternSegmentsWithoutWildcard = patternSegments.slice(0, -1);
if (pathSegments.length < patternSegmentsWithoutWildcard.length) {
return false;
}
for (const [i, patternSegment] of patternSegmentsWithoutWildcard.entries()) {
if (!segmentMatches(pathSegments[i], patternSegment)) {
return false;
}
}
return true;
}
// Exact segment count match required
if (pathSegments.length !== patternSegments.length) {
return false;
}
for (const [i, patternSegment] of patternSegments.entries()) {
if (!segmentMatches(pathSegments[i], patternSegment)) {
return false;
}
}
return true;
}
/**
* Checks if a path segment matches a pattern segment.
*/
function segmentMatches(pathSegment, patternSegment) {
if (pathSegment === undefined || patternSegment === undefined) {
return false;
}
// Parameter matches anything
if (PARAM_RE.test(patternSegment)) {
return true;
}
// Literal must match exactly
return pathSegment === patternSegment;
}
/**
* Splits a path into segments, filtering out empty strings.
*/
function splitPath(path) {
return path.split('/').filter(Boolean);
}
/**
* React Router scoring weights and param detection.
* https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/router/utils.ts
*/
const PARAM_RE = /^:[\w-]+$/;
const STATIC_SEGMENT_SCORE = 10;
const DYNAMIC_SEGMENT_SCORE = 3;
const EMPTY_SEGMENT_SCORE = 1;
const SPLAT_PENALTY = -2;
/**
* Computes a specificity score for a route pattern.
* Matches React Router's computeScore() algorithm exactly.
*/
function computeScore(pattern) {
const segments = pattern.split('/');
// Base score is segment count (including empty segment from leading slash)
let score = segments.length;
// Apply splat penalty once if pattern contains wildcard
if (segments.includes('*')) {
score += SPLAT_PENALTY;
}
for (const segment of segments) {
if (segment === '*') {
// Splat penalty already applied globally above
continue;
} else if (PARAM_RE.test(segment)) {
score += DYNAMIC_SEGMENT_SCORE;
} else if (segment === '') {
score += EMPTY_SEGMENT_SCORE;
} else {
score += STATIC_SEGMENT_SCORE;
}
}
return score;
}
/**
* Sorts route patterns by specificity (most specific first).
* Implements React Router's ranking algorithm from computeScore():
* https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/router/utils.ts
*
* React Router scoring: base=segments.length, static=+10, dynamic=+3, empty=+1, splat=-2 (once)
* Higher score = more specific pattern.
* Equal scores preserve manifest order (same as React Router).
*
* Note: Users should order their manifest from most specific to least specific
* when patterns have equal specificity (e.g., `/users/:id/settings` and `/:type/123/settings`).
*/
function sortBySpecificity(manifest) {
return [...manifest].sort((a, b) => {
const aScore = computeScore(a);
const bScore = computeScore(b);
return bScore - aScore;
});
}
exports.matchRouteManifest = matchRouteManifest;
exports.stripBasenameFromPathname = stripBasenameFromPathname;
//# sourceMappingURL=route-manifest.js.map