UNPKG

next

Version:

The React Framework

218 lines (217 loc) • 8.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 0 && (module.exports = { getNamedMiddlewareRegex: null, getNamedRouteRegex: null, getRouteRegex: null, parseParameter: null }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { getNamedMiddlewareRegex: function() { return getNamedMiddlewareRegex; }, getNamedRouteRegex: function() { return getNamedRouteRegex; }, getRouteRegex: function() { return getRouteRegex; }, parseParameter: function() { return parseParameter; } }); const _constants = require("../../../../lib/constants"); const _interceptionroutes = require("../../../../server/lib/interception-routes"); const _escaperegexp = require("../../escape-regexp"); const _removetrailingslash = require("./remove-trailing-slash"); /** * Regular expression pattern used to match route parameters. * Matches both single parameters and parameter groups. * Examples: * - `[[...slug]]` matches parameter group with key 'slug', repeat: true, optional: true * - `[...slug]` matches parameter group with key 'slug', repeat: true, optional: false * - `[[foo]]` matches parameter with key 'foo', repeat: false, optional: true * - `[bar]` matches parameter with key 'bar', repeat: false, optional: false */ const PARAMETER_PATTERN = /\[((?:\[.*\])|.+)\]/; function parseParameter(param) { const match = param.match(PARAMETER_PATTERN); if (!match) { return parseMatchedParameter(param); } return parseMatchedParameter(match[1]); } /** * Parses a matched parameter from the PARAMETER_PATTERN regex to a data structure that can be used * to generate the parametrized route. * Examples: * - `[...slug]` -> `{ key: 'slug', repeat: true, optional: true }` * - `...slug` -> `{ key: 'slug', repeat: true, optional: false }` * - `[foo]` -> `{ key: 'foo', repeat: false, optional: true }` * - `bar` -> `{ key: 'bar', repeat: false, optional: false }` * @param param - The matched parameter to parse. * @returns The parsed parameter as a data structure. */ function parseMatchedParameter(param) { const optional = param.startsWith('[') && param.endsWith(']'); if (optional) { param = param.slice(1, -1); } const repeat = param.startsWith('...'); if (repeat) { param = param.slice(3); } return { key: param, repeat, optional }; } function getParametrizedRoute(route) { const segments = (0, _removetrailingslash.removeTrailingSlash)(route).slice(1).split('/'); const groups = {}; let groupIndex = 1; return { parameterizedRoute: segments.map((segment)=>{ const markerMatch = _interceptionroutes.INTERCEPTION_ROUTE_MARKERS.find((m)=>segment.startsWith(m)); const paramMatches = segment.match(PARAMETER_PATTERN) // Check for parameters ; if (markerMatch && paramMatches) { const { key, optional, repeat } = parseMatchedParameter(paramMatches[1]); groups[key] = { pos: groupIndex++, repeat, optional }; return "/" + (0, _escaperegexp.escapeStringRegexp)(markerMatch) + "([^/]+?)"; } else if (paramMatches) { const { key, repeat, optional } = parseMatchedParameter(paramMatches[1]); groups[key] = { pos: groupIndex++, repeat, optional }; return repeat ? optional ? '(?:/(.+?))?' : '/(.+?)' : '/([^/]+?)'; } else { return "/" + (0, _escaperegexp.escapeStringRegexp)(segment); } }).join(''), groups }; } function getRouteRegex(normalizedRoute) { const { parameterizedRoute, groups } = getParametrizedRoute(normalizedRoute); return { re: new RegExp("^" + parameterizedRoute + "(?:/)?$"), groups: groups }; } /** * Builds a function to generate a minimal routeKey using only a-z and minimal * number of characters. */ function buildGetSafeRouteKey() { let i = 0; return ()=>{ let routeKey = ''; let j = ++i; while(j > 0){ routeKey += String.fromCharCode(97 + (j - 1) % 26); j = Math.floor((j - 1) / 26); } return routeKey; }; } function getSafeKeyFromSegment(param) { let { interceptionMarker, getSafeRouteKey, segment, routeKeys, keyPrefix } = param; const { key, optional, repeat } = parseMatchedParameter(segment); // replace any non-word characters since they can break // the named regex let cleanedKey = key.replace(/\W/g, ''); if (keyPrefix) { cleanedKey = "" + keyPrefix + cleanedKey; } let invalidKey = false; // check if the key is still invalid and fallback to using a known // safe key if (cleanedKey.length === 0 || cleanedKey.length > 30) { invalidKey = true; } if (!isNaN(parseInt(cleanedKey.slice(0, 1)))) { invalidKey = true; } if (invalidKey) { cleanedKey = getSafeRouteKey(); } if (keyPrefix) { routeKeys[cleanedKey] = "" + keyPrefix + key; } else { routeKeys[cleanedKey] = key; } // if the segment has an interception marker, make sure that's part of the regex pattern // this is to ensure that the route with the interception marker doesn't incorrectly match // the non-intercepted route (ie /app/(.)[username] should not match /app/[username]) const interceptionPrefix = interceptionMarker ? (0, _escaperegexp.escapeStringRegexp)(interceptionMarker) : ''; return repeat ? optional ? "(?:/" + interceptionPrefix + "(?<" + cleanedKey + ">.+?))?" : "/" + interceptionPrefix + "(?<" + cleanedKey + ">.+?)" : "/" + interceptionPrefix + "(?<" + cleanedKey + ">[^/]+?)"; } function getNamedParametrizedRoute(route, prefixRouteKeys) { const segments = (0, _removetrailingslash.removeTrailingSlash)(route).slice(1).split('/'); const getSafeRouteKey = buildGetSafeRouteKey(); const routeKeys = {}; return { namedParameterizedRoute: segments.map((segment)=>{ const hasInterceptionMarker = _interceptionroutes.INTERCEPTION_ROUTE_MARKERS.some((m)=>segment.startsWith(m)); const paramMatches = segment.match(/\[((?:\[.*\])|.+)\]/) // Check for parameters ; if (hasInterceptionMarker && paramMatches) { const [usedMarker] = segment.split(paramMatches[0]); return getSafeKeyFromSegment({ getSafeRouteKey, interceptionMarker: usedMarker, segment: paramMatches[1], routeKeys, keyPrefix: prefixRouteKeys ? _constants.NEXT_INTERCEPTION_MARKER_PREFIX : undefined }); } else if (paramMatches) { return getSafeKeyFromSegment({ getSafeRouteKey, segment: paramMatches[1], routeKeys, keyPrefix: prefixRouteKeys ? _constants.NEXT_QUERY_PARAM_PREFIX : undefined }); } else { return "/" + (0, _escaperegexp.escapeStringRegexp)(segment); } }).join(''), routeKeys }; } function getNamedRouteRegex(normalizedRoute, prefixRouteKey) { const result = getNamedParametrizedRoute(normalizedRoute, prefixRouteKey); return { ...getRouteRegex(normalizedRoute), namedRegex: "^" + result.namedParameterizedRoute + "(?:/)?$", routeKeys: result.routeKeys }; } function getNamedMiddlewareRegex(normalizedRoute, options) { const { parameterizedRoute } = getParametrizedRoute(normalizedRoute); const { catchAll = true } = options; if (parameterizedRoute === '/') { let catchAllRegex = catchAll ? '.*' : ''; return { namedRegex: "^/" + catchAllRegex + "$" }; } const { namedParameterizedRoute } = getNamedParametrizedRoute(normalizedRoute, false); let catchAllGroupedRegex = catchAll ? '(?:(/.*)?)' : ''; return { namedRegex: "^" + namedParameterizedRoute + catchAllGroupedRegex + "$" }; } //# sourceMappingURL=route-regex.js.map