All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources.util.pathParser.js Maven / Gradle / Ivy

There is a newer version: 5.0.55
Show newest version
/**
 * 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;
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy