105 lines
5.1 KiB
JavaScript
105 lines
5.1 KiB
JavaScript
/**
|
|
* 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
|