META-INF.resources.util.pathParser.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.frontend.js.spa.web
Show all versions of com.liferay.frontend.js.spa.web
Liferay Frontend JS SPA Web
/**
* SPDX-FileCopyrightText: (c) 2000 Liferay, Inc. https://liferay.com
* SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
*/
const REGEX =
/([/])?(?:(?::(\w+)(?:\(((?:\\.|[^\\()])*)\))?|\(((?:\\.|[^\\()])+)\))([+*?])?)/g;
/**
* Converts the given array of regex matches to a more readable object format.
* @param {!Array} matches
* @return {!Object}
*/
function convertMatchesToObj(matches) {
return {
match: matches[0],
modifier: matches[5],
name: matches[2],
paramPattern: matches[3],
prefix: matches[1],
unnamedPattern: matches[4],
};
}
/**
* Converts the given tokens parsed from a route format string to a regex.
* @param {!Array} tokens
* @return {!RegExp}
*/
function convertTokensToRegex(tokens) {
let regex = '';
for (let i = 0; i < tokens.length; i++) {
if (typeof tokens[i] === 'string') {
regex += escape(tokens[i]);
}
else {
let capture = encloseNonCapturingGroup(tokens[i].pattern);
if (tokens[i].repeat) {
capture += encloseNonCapturingGroup('\\/' + capture) + '*';
}
capture = escape(tokens[i].prefix) + `(${capture})`;
if (tokens[i].optional) {
if (!tokens[i].partial) {
capture = encloseNonCapturingGroup(capture);
}
capture += '?';
}
regex += capture;
}
}
return new RegExp('^' + makeTrailingSlashOptional(regex) + '$');
}
/**
* Encloses the given regex pattern into a non capturing group.
* @param {string} pattern
* @return {string}
*/
function encloseNonCapturingGroup(pattern) {
return `(?:${pattern})`;
}
/**
* Escapes the given string to show up in the path regex.
* @param {string} str
* @return {string}
*/
function escape(str) {
return str.replace(/([.+*?=^!:${}()[]|\/\\])/g, '\\$1');
}
/**
* Makes trailing slash optional on paths.
* @param {string} regex
* @param {string}
*/
function makeTrailingSlashOptional(regex) {
if (regex.endsWith('/')) {
regex += '?';
}
else {
regex += '\\/?';
}
return regex;
}
/**
* Parses the given route format string into tokens representing its contents.
* @param {!Array|string} routeOrTokens Either a route format string or tokens
* previously parsed via the `parse` function.
* @return {!Array} An array of tokens that can be either plain
* strings (part of the route) or objects containing informations on params.
*/
export function parse(routeOrTokens) {
if (typeof routeOrTokens !== 'string') {
return routeOrTokens;
}
const route = routeOrTokens;
let unnamedCount = 0;
const tokens = [];
let currPath = '';
let index = 0;
let matches = REGEX.exec(route);
while (matches) {
const data = convertMatchesToObj(matches);
currPath = route.slice(index, matches.index);
index = matches.index + data.match.length;
tokens.push(currPath);
tokens.push({
name: data.name ? data.name : '' + unnamedCount++,
optional: data.modifier === '*' || data.modifier === '?',
partial: route[index] && route[index] !== data.prefix,
pattern: data.paramPattern || data.unnamedPattern || '[^\\/]+',
prefix: data.prefix || '',
repeat: data.modifier === '*' || data.modifier === '+',
});
matches = REGEX.exec(route);
}
if (index < route.length) {
tokens.push(route.substr(index));
}
return tokens;
}
/**
* Converts the given route format string to a regex that can extract param
* data from paths matching it.
* @param {!Array|string} routeOrTokens Either a route format string or tokens
* previously parsed via the `parse` function.
* @return {!RegExp}
*/
export function toRegex(routeOrTokens) {
return convertTokensToRegex(parse(routeOrTokens));
}
/**
* Extracts data from the given path according to the specified route format.
* @param {!Array|string} routeOrTokens Either a route format string or tokens
* previously parsed via the `parse` function.
* @param {string} The path to extract param data from.
* @return {Object} The data object, or null if the path doesn't
* match the given format.
*/
export function extractData(routeOrTokens, path) {
const data = {};
const tokens = parse(routeOrTokens);
const match = path.match(convertTokensToRegex(tokens));
if (!match) {
return null;
}
let paramIndex = 1;
for (let i = 0; i < tokens.length; i++) {
if (typeof tokens[i] !== 'string') {
let value = match[paramIndex++];
if (value !== undefined) {
if (tokens[i].repeat) {
value = value.split('/');
}
data[tokens[i].name] = value;
}
}
}
return data;
}