UNPKG

next

Version:

The React Framework

105 lines (104 loc) 5.21 kB
/** * Route pattern normalization utilities for path-to-regexp compatibility. * * path-to-regexp 6.3.0+ introduced stricter validation that rejects certain * patterns commonly used in Next.js interception routes. This module provides * normalization functions to make Next.js route patterns compatible with the * updated library while preserving all functionality. */ /** * Internal separator used to normalize adjacent parameter patterns. * This unique marker is inserted between adjacent parameters and stripped out * during parameter extraction to avoid conflicts with real URL content. */ export const PARAM_SEPARATOR = '_NEXTSEP_'; /** * Detects if a route pattern needs normalization for path-to-regexp compatibility. */ export function hasAdjacentParameterIssues(route) { if (typeof route !== 'string') return false; // Check for interception route markers followed immediately by parameters // Pattern: /(.):param, /(..):param, /(...):param, /(.)(.):param etc. // These patterns cause "Must have text between two parameters" errors if (/\/\(\.{1,3}\):[^/\s]+/.test(route)) { return true; } // Check for basic adjacent parameters without separators // Pattern: :param1:param2 (but not :param* or other URL patterns) if (/:[a-zA-Z_][a-zA-Z0-9_]*:[a-zA-Z_][a-zA-Z0-9_]*/.test(route)) { return true; } return false; } /** * Normalizes route patterns that have adjacent parameters without text between them. * Inserts a unique separator that can be safely stripped out later. */ export function normalizeAdjacentParameters(route) { let normalized = route; // Handle interception route patterns: (.):param -> (.)_NEXTSEP_:param normalized = normalized.replace(/(\([^)]*\)):([^/\s]+)/g, `$1${PARAM_SEPARATOR}:$2`); // Handle other adjacent parameter patterns: :param1:param2 -> :param1_NEXTSEP_:param2 normalized = normalized.replace(/:([^:/\s)]+)(?=:)/g, `:$1${PARAM_SEPARATOR}`); return normalized; } /** * Normalizes tokens that have repeating modifiers (* or +) but empty prefix and suffix. * * path-to-regexp 6.3.0+ introduced validation that throws: * "Can not repeat without prefix/suffix" * * This occurs when a token has modifier: '*' or '+' with both prefix: '' and suffix: '' */ export function normalizeTokensForRegexp(tokens) { return tokens.map((token)=>{ // Token union type: Token = string | TokenObject // Literal path segments are strings, parameters/wildcards are objects if (typeof token === 'object' && token !== null && // Not all token objects have 'modifier' property (e.g., simple text tokens) 'modifier' in token && // Only repeating modifiers (* or +) cause the validation error // Other modifiers like '?' (optional) are fine (token.modifier === '*' || token.modifier === '+') && // Token objects can have different shapes depending on route pattern 'prefix' in token && 'suffix' in token && // Both prefix and suffix must be empty strings // This is what causes the validation error in path-to-regexp token.prefix === '' && token.suffix === '') { // Add minimal prefix to satisfy path-to-regexp validation // We use '/' as it's the most common path delimiter and won't break route matching // The prefix gets used in regex generation but doesn't affect parameter extraction return { ...token, prefix: '/' }; } return token; }); } /** * Strips normalization separators from compiled pathname. * This removes separators that were inserted by normalizeAdjacentParameters * to satisfy path-to-regexp validation. * * Only removes separators in the specific contexts where they were inserted: * - After interception route markers: (.)_NEXTSEP_ -> (.) * * This targeted approach ensures we don't accidentally remove the separator * from legitimate user content. */ export function stripNormalizedSeparators(pathname) { // Remove separator after interception route markers // Pattern: (.)_NEXTSEP_ -> (.), (..)_NEXTSEP_ -> (..), etc. // The separator appears after the closing paren of interception markers return pathname.replace(new RegExp(`\\)${PARAM_SEPARATOR}`, 'g'), ')'); } /** * Strips normalization separators from extracted route parameters. * Used by both server and client code to clean up parameters after route matching. */ export function stripParameterSeparators(params) { const cleaned = {}; for (const [key, value] of Object.entries(params)){ if (typeof value === 'string') { // Remove the separator if it appears at the start of parameter values cleaned[key] = value.replace(new RegExp(`^${PARAM_SEPARATOR}`), ''); } else if (Array.isArray(value)) { // Handle array parameters (from repeated route segments) cleaned[key] = value.map((item)=>typeof item === 'string' ? item.replace(new RegExp(`^${PARAM_SEPARATOR}`), '') : item); } else { cleaned[key] = value; } } return cleaned; } //# sourceMappingURL=route-pattern-normalizer.js.map