forked from off-topic/apps.apple.com
init commit
This commit is contained in:
295
node_modules/@jet/environment/routing/routing-components.js
generated
vendored
Normal file
295
node_modules/@jet/environment/routing/routing-components.js
generated
vendored
Normal file
@@ -0,0 +1,295 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.UrlRouter = exports.UrlRule = void 0;
|
||||
const optional_1 = require("../types/optional");
|
||||
const urls = require("../util/urls");
|
||||
// endregion
|
||||
// region private URLRule helpers.
|
||||
/**
|
||||
* Checks whether or not a given pathComponents component contains a parameter.
|
||||
* @param pathComponent - The pathComponents component to check.
|
||||
* @returns true if the pathComponents component is surrounded by curly braces; false otherwise.
|
||||
*/
|
||||
function isPathComponentParameter(pathComponent) {
|
||||
return pathComponent.startsWith("{") && pathComponent.endsWith("}");
|
||||
}
|
||||
/**
|
||||
* Extracts the parameter contained in a pathComponents component.
|
||||
* @param pathComponent - A pathComponents component surrounded by curly braces.
|
||||
* @returns The parameter contained in the component.
|
||||
*/
|
||||
function getPathComponentParameter(pathComponent) {
|
||||
return pathComponent.replace("{", "").replace("}", "");
|
||||
}
|
||||
/**
|
||||
* Creates a mapping from key to pathComponents component index
|
||||
* for efficiently extracting parameters from a pathComponents.
|
||||
* @param rulePath - The pathComponents to create a mapping for.
|
||||
* @returns A map of keys to pathComponents component indexes.
|
||||
*/
|
||||
function makePathParameterMapping(rulePath) {
|
||||
const mapping = {};
|
||||
rulePath.forEach((ruleComponent, index) => {
|
||||
if (isPathComponentParameter(ruleComponent)) {
|
||||
mapping[ruleComponent] = index;
|
||||
}
|
||||
});
|
||||
return mapping;
|
||||
}
|
||||
/**
|
||||
* Creates `UrlRouteQuery` objects from substring of url.
|
||||
* @param parameters - strings of form `<key>[?]=<value>`.
|
||||
* @returns Array of `UrlRouteQuery` objects.
|
||||
*/
|
||||
function parseQuery(parameters) {
|
||||
const parsedQuery = [];
|
||||
if ((0, optional_1.isNothing)(parameters)) {
|
||||
return parsedQuery;
|
||||
}
|
||||
for (const param of parameters) {
|
||||
const parts = param.split("=");
|
||||
let key = parts[0];
|
||||
const optional = key.includes("?");
|
||||
key = key.replace("?", "");
|
||||
let value = null;
|
||||
if (parts.length > 1) {
|
||||
value = decodeURIComponent(parts[1]);
|
||||
}
|
||||
parsedQuery.push({
|
||||
key,
|
||||
value,
|
||||
optional,
|
||||
});
|
||||
}
|
||||
return parsedQuery;
|
||||
}
|
||||
/**
|
||||
* The `UrlRule` class extracts the pattern format from `UrlRuleDefinition`s, and encapsulates
|
||||
* the information needed to match against a candidate URL and extract parameters from it.
|
||||
*
|
||||
* The terminology here is:
|
||||
* - rule: A specific url pattern.
|
||||
* - route: A group of rules that together form a single route, i.e. UrlRule[].
|
||||
*/
|
||||
class UrlRule {
|
||||
/**
|
||||
* Construct the route with all required properties.
|
||||
* @param rule - The rule to match.
|
||||
*/
|
||||
constructor(rule) {
|
||||
this.identifier = rule.identifier;
|
||||
this.protocol = rule.protocol;
|
||||
this.hostName = rule.hostName;
|
||||
if ((0, optional_1.isSome)(rule.path)) {
|
||||
this.pathComponents = rule.path.split("/").filter((component) => component.length > 0);
|
||||
this.pathParameterMap = makePathParameterMapping(this.pathComponents);
|
||||
}
|
||||
else {
|
||||
this.pathComponents = undefined;
|
||||
this.pathParameterMap = undefined;
|
||||
}
|
||||
this.pathExtension = rule.pathExtension;
|
||||
this.query = parseQuery(rule.query);
|
||||
this.hash = rule.hash;
|
||||
this.regex = rule.regex;
|
||||
if ((0, optional_1.isSome)(rule.exclusions)) {
|
||||
this.exclusions = rule.exclusions.map(function (ex) {
|
||||
return new UrlRule(ex);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.exclusions = undefined;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Checks whether or not the route matches a given URL.
|
||||
* @param url - The URL to check against.
|
||||
* @returns true if the route matches `urls`; false otherwise.
|
||||
*
|
||||
* @deprecated prefer `match` to have access to regex match groups
|
||||
*/
|
||||
matches(url) {
|
||||
return (0, optional_1.isSome)(this.match(url));
|
||||
}
|
||||
/**
|
||||
* Extract information from a matching url.
|
||||
* @param matchingUrl - The url to extract parameters from.
|
||||
* @returns `Parameters` extracted from `matchingUrl`
|
||||
* @remarks This function is only valid when `this.matches(matchingUrl) === true`.
|
||||
*/
|
||||
extractParameters(matchingUrl) {
|
||||
var _a;
|
||||
const parameters = {};
|
||||
if ((0, optional_1.isSome)(this.pathComponents) && (0, optional_1.isSome)(this.pathParameterMap)) {
|
||||
const urlPathComponents = matchingUrl.pathComponents();
|
||||
for (const internalKey of Object.keys(this.pathParameterMap)) {
|
||||
const externalKey = getPathComponentParameter(internalKey);
|
||||
const index = this.pathParameterMap[internalKey];
|
||||
parameters[externalKey] = decodeURIComponent(urlPathComponents[index]);
|
||||
}
|
||||
}
|
||||
if ((0, optional_1.isSome)(this.query)) {
|
||||
for (const param of this.query) {
|
||||
const queryParam = (_a = matchingUrl.query) === null || _a === void 0 ? void 0 : _a[param.key];
|
||||
if ((0, optional_1.isSome)(queryParam)) {
|
||||
parameters[param.key] = queryParam;
|
||||
}
|
||||
}
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
/**
|
||||
* Checks whether or not the route matches a given URL.
|
||||
* @param url - The URL to check against.
|
||||
* @returns an optional `UrlRuleMatchResult` if the route matches `url`.
|
||||
*/
|
||||
match(url) {
|
||||
var _a, _b;
|
||||
let matchGroups = null;
|
||||
if ((0, optional_1.isSome)(this.regex)) {
|
||||
if (this.regex.length === 0) {
|
||||
// If the rule specifies regex but does not supply patterns, we need to return false. Otherwise, we will
|
||||
// risk matching against everything. This is because an empty regex with no other rule parameters will
|
||||
// cause us to fallthrough to the end and match against all URLs.
|
||||
return null;
|
||||
}
|
||||
let didMatchRegex = false;
|
||||
for (const regexPattern of this.regex) {
|
||||
const execResult = regexPattern.exec(url.toString());
|
||||
if (execResult !== null) {
|
||||
// If we match against any of regex patterns, then we should proceed.
|
||||
// If no matches are found, then this rule is not matched.
|
||||
didMatchRegex = true;
|
||||
matchGroups = (_a = execResult.groups) !== null && _a !== void 0 ? _a : null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!didMatchRegex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if ((0, optional_1.isSome)(this.protocol) && url.protocol !== this.protocol) {
|
||||
return null;
|
||||
}
|
||||
if ((0, optional_1.isSome)(this.hostName) && url.host !== this.hostName) {
|
||||
return null;
|
||||
}
|
||||
if ((0, optional_1.isSome)(this.pathComponents)) {
|
||||
const rulePathComponents = this.pathComponents;
|
||||
const urlPathComponents = url.pathComponents();
|
||||
if (rulePathComponents.length !== urlPathComponents.length) {
|
||||
return null;
|
||||
}
|
||||
// We're iterating two arrays here, an old style for-loop is appropriate
|
||||
const length = rulePathComponents.length;
|
||||
for (let i = 0; i < length; i += 1) {
|
||||
const ruleComponent = rulePathComponents[i];
|
||||
if (isPathComponentParameter(ruleComponent)) {
|
||||
// component parameters always match
|
||||
continue;
|
||||
}
|
||||
const urlComponent = urlPathComponents[i];
|
||||
if (ruleComponent !== urlComponent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((0, optional_1.isSome)(this.pathExtension)) {
|
||||
if (url.pathExtension() !== this.pathExtension) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if ((0, optional_1.isSome)(this.query)) {
|
||||
for (const param of this.query) {
|
||||
const value = (_b = url.query) === null || _b === void 0 ? void 0 : _b[param.key];
|
||||
if ((0, optional_1.isNothing)(value) && !param.optional) {
|
||||
return null;
|
||||
}
|
||||
if ((0, optional_1.isSome)(param.value) && param.value !== value) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((0, optional_1.isSome)(this.hash) && url.hash !== this.hash) {
|
||||
return null;
|
||||
}
|
||||
if ((0, optional_1.isSome)(this.exclusions)) {
|
||||
for (const exclusionRule of this.exclusions) {
|
||||
if ((0, optional_1.isSome)(exclusionRule.exclusions)) {
|
||||
throw Error("Matching exclusion rules with further exclusion rules may introduce significant code-complexity and/or reduce the ease with which developers are able to reason about your desired goals. Are there any simpler options?");
|
||||
}
|
||||
if ((0, optional_1.isSome)(exclusionRule.match(url))) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
const parameters = this.extractParameters(url);
|
||||
return {
|
||||
parameters,
|
||||
matchGroups,
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.UrlRule = UrlRule;
|
||||
/**
|
||||
* `UrlRouter` manages a set of url rule templates to allow `urls` to serve as keys for different associated objects (like Builders).
|
||||
*
|
||||
* @remarks This is replaces old `UrlRouter` as a synchronous way match route URLs to handlers. In contrast to the previous implementation,
|
||||
* it maps entire objects (containing related async handlers and properties) to urls.
|
||||
*/
|
||||
class UrlRouter {
|
||||
/**
|
||||
* Constructs an empty URL router object.
|
||||
*/
|
||||
constructor() {
|
||||
this.routeMappings = [];
|
||||
}
|
||||
/**
|
||||
* Register a new route defined by a set of definitions and object on the router.
|
||||
* @param routeDefinitions - The definitions of rules to register.
|
||||
* @param object - The object for the rule.
|
||||
*/
|
||||
associate(routeDefinitions, object) {
|
||||
const route = [];
|
||||
for (const definition of routeDefinitions) {
|
||||
route.push(new UrlRule(definition));
|
||||
}
|
||||
this.routeMappings.push({ route: route, object: object });
|
||||
}
|
||||
/**
|
||||
* Resolve given url to associated object, if any exist. Rules will be evaluated
|
||||
* in the order they are added using the `associate` function. Evaluation will stop
|
||||
* after any rule matches.
|
||||
* @param urlOrString - URL or string representation of url to resolve objects for.
|
||||
* @returns `UrlRouterResult` containing url, extracted parameters, and associated object, or `null` if no match was found.
|
||||
*/
|
||||
routedObjectForUrl(urlOrString) {
|
||||
var _a;
|
||||
const url = typeof urlOrString === "string" ? new urls.URL(urlOrString) : urlOrString;
|
||||
for (const mapping of this.routeMappings) {
|
||||
for (const rule of mapping.route) {
|
||||
const matchResult = rule.match(url);
|
||||
if ((0, optional_1.isSome)(matchResult)) {
|
||||
return {
|
||||
normalizedUrl: url,
|
||||
parameters: matchResult.parameters,
|
||||
object: mapping.object,
|
||||
matchedRuleIdentifier: (_a = rule.identifier) !== null && _a !== void 0 ? _a : null,
|
||||
regexMatchGroups: matchResult.matchGroups,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
// No match. Still return a result with normalized url.
|
||||
return {
|
||||
normalizedUrl: url,
|
||||
parameters: null,
|
||||
object: null,
|
||||
matchedRuleIdentifier: null,
|
||||
regexMatchGroups: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.UrlRouter = UrlRouter;
|
||||
// endregion
|
||||
//# sourceMappingURL=routing-components.js.map
|
||||
Reference in New Issue
Block a user