vike
Version:
The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.
150 lines (149 loc) • 5.46 kB
JavaScript
export { resolveRouteString };
export { getUrlFromRouteString };
export { isStaticRouteString };
export { analyzeRouteString };
export { assertRouteString };
export { getRouteStringParameterList };
import { assertWarning, assert, assertUsage } from '../../utils/assert.js';
import { escapeRegex } from '../../utils/escapeRegex.js';
import { isBrowser } from '../../utils/isBrowser.js';
import pc from '@brillout/picocolors';
const PARAM_TOKEN_NEW = '@';
// TO-DO/next-major-release: remove
const PARAM_TOKEN_OLD = ':';
function assertRouteString(routeString, errPrefix = 'Invalid') {
let errPrefix2 = `${errPrefix} Route String ${highlight(routeString)}`;
assertUsage(routeString !== '', `${errPrefix2} (empty string): set it to ${highlight('/')} instead`);
assertUsage(['/', '*'].includes(routeString[0]), `${errPrefix2}: it should start with ${highlight('/')} or ${highlight('*')}`);
assertUsage(!routeString.includes('**'), `${errPrefix2}: set it to ${highlight(routeString.split('**').join('*'))} instead`);
}
function resolveRouteString(routeString, urlPathname) {
assertRouteString(routeString);
const segments = parseRouteString(routeString);
const routeRegexStrInner = segments
.map((segment) => {
if (segment.param) {
return '[^/]+';
}
if (segment.glob) {
return '.*';
}
// segment.static
return escapeRegex(segment.static);
})
.map((s) => `(${s})`)
.join('');
const routeRegex = new RegExp(`^${routeRegexStrInner}/?$`);
const routeRegexMatch = urlPathname.match(routeRegex);
/* DEBUG
console.log()
console.log('routeString', routeString)
console.log('urlPathname', urlPathname)
console.log('routeSegments', segments)
console.log('routeRegex', routeRegex)
console.log('routeRegexMatch', routeRegexMatch)
//*/
if (!routeRegexMatch)
return null;
const routeParams = {};
const [_, ...segmentsValue] = routeRegexMatch;
let globIdx = 0;
const hasMultipleGlobs = segments.filter((segment) => segment.glob).length > 1;
segments.forEach((segment, i) => {
let val = segmentsValue[i];
if (segment.param) {
routeParams[segment.param] = val;
}
if (segment.glob) {
const param = `*${hasMultipleGlobs ? ++globIdx : ''}`;
routeParams[param] = val;
}
});
return { routeParams };
}
function parseRouteString(routeString) {
const segments = [];
const pushStatic = (s) => {
const segmentLast = segments[segments.length - 1];
if (segmentLast?.static) {
segmentLast.static += s;
}
else {
segments.push({ static: s });
}
};
const parts = routeString.split('/');
parts.forEach((s, i) => {
if (i !== 0)
pushStatic('/');
if (isParam(s)) {
assertWarning(!s.startsWith(PARAM_TOKEN_OLD), `Outdated Route String ${highlight(routeString)}, use ${highlight(routeString.split(PARAM_TOKEN_OLD).join(PARAM_TOKEN_NEW))} instead`, { onlyOnce: true });
segments.push({ param: s.slice(1) });
}
else {
if (s === '*' && i === parts.length - 1 && routeString !== '*' && routeString !== '/*') {
segments.push({ glob: true });
}
else {
s.split('*').forEach((s, i) => {
if (i !== 0)
segments.push({ glob: true });
if (s !== '') {
pushStatic(s);
}
});
}
}
});
return segments;
}
function getRouteStringParameterList(routeString) {
const routeParameterList = [];
const segments = parseRouteString(routeString);
segments.forEach((segment) => {
if (segment.param)
routeParameterList.push(segment.param);
});
return routeParameterList;
}
function getUrlFromRouteString(routeString) {
if (isStaticRouteString(routeString)) {
const url = routeString;
return url;
}
return null;
}
function analyzeRouteString(routeString) {
const segments = parseRouteString(routeString);
const countStaticParts = (s) => s?.split('/').filter(Boolean).length || 0;
let numberOfStaticPartsBeginning = 0;
for (const segment of segments) {
if (!segment.static)
break;
numberOfStaticPartsBeginning += countStaticParts(segment.static);
}
const numberOfStaticParts = segments.map((s) => countStaticParts(s.static)).reduce((sum, a) => sum + a, 0);
const numberOfParams = segments.filter((s) => s.param).length;
const numberOfGlobs = segments.filter((s) => s.glob).length;
return { numberOfStaticPartsBeginning, numberOfStaticParts, numberOfParams, numberOfGlobs };
}
function isParam(routeSegment) {
return routeSegment.startsWith(PARAM_TOKEN_NEW) || routeSegment.startsWith(PARAM_TOKEN_OLD);
}
function isStaticRouteString(routeString) {
const url = routeString;
const match = resolveRouteString(routeString, url);
assert(match);
return Object.keys(match.routeParams).length === 0;
}
function highlight(routeString) {
if (isBrowser()) {
return `'${routeString}'`;
}
else {
if (routeString === '') {
routeString = "''";
}
return pc.cyan(routeString);
}
}