UNPKG

@nimpl/path-parser

Version:

Helper for next.js-styled pathname parsing (f.e. /(site)/docs/[key]) to get dynamic params

102 lines (101 loc) 4.03 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parse = void 0; const normalizePathname = (pathname) => { const cleanPathname = pathname && new URL(pathname, "http://n").pathname; const pathnameWithoutTrailingSlash = cleanPathname?.replace(/^(\/.*)\/$/, "$1"); const pathnameWithoutFileType = pathnameWithoutTrailingSlash?.replace(/\/_not-found$/, ""); return pathnameWithoutFileType.endsWith("/") ? pathnameWithoutFileType : pathnameWithoutFileType + "/"; }; const normalizePagePath = (pagePath) => { const cleanPagePath = pagePath && new URL(pagePath, "http://n").pathname; const pagePathWithoutFileType = cleanPagePath?.replace(/(\/page|\/_not-found)$/, "/"); const pagePathWithoutGroups = pagePathWithoutFileType .split("/") .filter(Boolean) .filter((segment) => !segment.match(/^(\([^)]+\)$|^\@.+$)/g)); if (pagePathWithoutGroups.length === 0) return "/"; return "/" + pagePathWithoutGroups.join("/") + "/"; }; const parseSegments = (pagePathParts, pathnameParts) => { const query = pagePathParts.reduce((acc, cur, index) => { const optionalCatchAllSegment = cur.match(/^\[\[\.\.\.([^\]]+)\]\]$/); if (optionalCatchAllSegment) { const key = optionalCatchAllSegment[1]; const segmentParts = pathnameParts.slice(index); if (segmentParts.length) { acc[key] = segmentParts; } return acc; } const catchAllSegment = cur.match(/^\[\.\.\.([^\]]+)\]$/); if (catchAllSegment) { const key = catchAllSegment[1]; acc[key] = pathnameParts.slice(index); return acc; } const dynamicSegment = cur.match(/^\[([^\]]+)\]$/); if (dynamicSegment) { const key = dynamicSegment[1]; acc[key] = pathnameParts[index]; return acc; } return acc; }, {}); return query; }; const normalizeInterceptingRoutes = (pageParts) => { let skip = 0; const normilizedParts = []; for (const pagepart of [...pageParts].reverse()) { if (skip) { skip -= 1; continue; } if (pagepart.startsWith("(...)")) { normilizedParts.push(pagepart.replace(/^\(\.\.\.\)/, "")); break; } else if (pagepart.startsWith("(.)")) { normilizedParts.push(pagepart.replace(/^\(\.\)/, "")); } else if (pagepart.startsWith("(..)")) { const skipLeafs = pagepart.match(/\(\.\.\)/g); skip += skipLeafs?.length || 0; normilizedParts.push(pagepart.replace(/^(\(\.\.\))+/, "")); } else { normilizedParts.push(pagepart); } } return normilizedParts.reverse(); }; const isSamePaths = (urlPathnameParts, pagePathParts) => { for (let i = 0; i < pagePathParts.length; i++) { const urlPathnamePart = urlPathnameParts[i]; const pagePathPart = pagePathParts[i]; if (pagePathPart.match(/\[\.\.\.[^\]]+\]/)) return true; if (pagePathPart.match(/\[[^\]]+\]/)) continue; if (urlPathnamePart !== pagePathPart) return false; } return urlPathnameParts.length === pagePathParts.length; }; const parse = (pathname, rule) => { const cleanUrlPathname = normalizePathname(pathname); const cleanPagePath = normalizePagePath(rule); const pagePathParts = cleanPagePath.split("/").slice(0, -1); const pagePathInterceptedParts = normalizeInterceptingRoutes(pagePathParts); const pathnameParts = cleanUrlPathname.split("/").slice(0, -1); const isNotFoundPage = rule.match(/\/_not-found\/?$/); const isCorrectMatched = isNotFoundPage || isSamePaths(pathnameParts, pagePathInterceptedParts); if (!isCorrectMatched) { return null; } const query = parseSegments(pagePathInterceptedParts, pathnameParts); return query; }; exports.parse = parse;