UNPKG

type-route

Version:

The flexible, type safe routing library.

1,307 lines (1,280 loc) 63.9 kB
import { createMemoryHistory, createHashHistory, createBrowserHistory } from 'history'; function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (it) return (it = it.call(o)).next.bind(it); if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function typeOf(value) { if (value === null) { return "null"; } if (Array.isArray(value)) { return "array"; } return typeof value; } function getBuildPathDefRouteNameMessage(routeName) { return "This problem occurred when building the route definition for the \"" + routeName + "\" route."; } function getBuildPathDefErrorMessage(context) { return [getBuildPathDefRouteNameMessage(context.routeName), "The path was constructed as `" + context.rawPath + "`"]; } var TypeRouteError = /*#__PURE__*/buildErrorCollection({ Path_may_not_be_an_empty_string: { errorCode: 1000, getDetails: getBuildPathDefErrorMessage }, Path_must_start_with_a_forward_slash: { errorCode: 1001, getDetails: getBuildPathDefErrorMessage }, Path_may_not_end_with_a_forward_slash: { errorCode: 1002, getDetails: getBuildPathDefErrorMessage }, Path_may_not_include_characters_that_must_be_URL_encoded: { errorCode: 1003, getDetails: function getDetails(context, segment) { var _segment$trailing, _segment$paramId; var leading = segment.leading; var trailing = (_segment$trailing = segment.trailing) != null ? _segment$trailing : ""; var paramId = (_segment$paramId = segment.paramId) != null ? _segment$paramId : ""; var invalidCharacters = (leading + trailing).split("").filter(function (character) { return character !== encodeURIComponent(character); }); return [].concat(getBuildPathDefErrorMessage(context), ["The path segment `" + (leading + paramId + trailing) + "` has the following invalid characters: " + invalidCharacters.join(", ")]); } }, Path_may_not_include_empty_segments: { errorCode: 1004, getDetails: function getDetails(context) { return [].concat(getBuildPathDefErrorMessage(context), ["Empty segments can be spotted by finding the place in the path with two consecutive forward slashes '//'."]); } }, Path_may_have_at_most_one_parameter_per_segment: { errorCode: 1005, getDetails: function getDetails(context, parameterNames) { return [].concat(getBuildPathDefErrorMessage(context), ["A single segment of the path included the following parameters: " + parameterNames, "Consider using ofType with a customer ValueSerializer for this scenario."]); } }, Path_parameters_may_not_be_used_more_than_once_when_building_a_path: { errorCode: 1005, getDetails: function getDetails(context, parameterName) { return [].concat(getBuildPathDefErrorMessage(context), ["The parameter \"" + parameterName + "\" was used more than once."]); } }, Optional_path_parameters_may_not_have_any_text_around_the_parameter: { errorCode: 1006, getDetails: function getDetails(context, parameterName, leadingText, trailingText) { var messages = getBuildPathDefErrorMessage(context); if (leadingText) { messages.push("The parameter \"" + parameterName + "\" cannot be preceded by \"" + leadingText + "\"."); } if (trailingText) { messages.push("The parameter \"" + parameterName + "\" cannot be followed by \"" + trailingText + "\"."); } return messages; } }, Path_may_have_at_most_one_optional_or_trailing_parameter: { errorCode: 1007, getDetails: function getDetails(context, numOptionalTrailingParameterNames) { return [].concat(getBuildPathDefErrorMessage(context), ["At most one optional/trailing parameter should be given but " + numOptionalTrailingParameterNames + " were provided."]); } }, Optional_or_trailing_path_parameters_may_only_appear_in_the_last_path_segment: { errorCode: 1008, getDetails: getBuildPathDefErrorMessage }, All_path_parameters_must_be_used_in_path_construction: { errorCode: 1009, getDetails: function getDetails(context, unusedParameters) { return [].concat(getBuildPathDefErrorMessage(context), ["The following parameters were not used: " + unusedParameters.join(", ")]); } }, Path_parameter_name_must_not_include_curly_brackets_dollar_signs_or_the_forward_slash_character: { errorCode: 1010, getDetails: function getDetails(routeName, paramName) { return [getBuildPathDefRouteNameMessage(routeName), "The $ { } or / character was used in this parameter name: " + paramName]; } }, Extension_route_definition_parameter_names_may_not_be_the_same_as_base_route_definition_parameter_names: { errorCode: 1011, getDetails: function getDetails(duplicateParameterNames) { return ["The following parameter names were used in both the base route definition and the extension: " + duplicateParameterNames.join(", ")]; } }, Expected_type_does_not_match_actual_type: { errorCode: 1012, getDetails: function getDetails(_ref) { var context = _ref.context, value = _ref.value, valueName = _ref.valueName, expectedType = _ref.expectedType, actualType = _ref.actualType; return ["Problem found with your usage of `" + context + "`", "`" + valueName + "` was expected to be of type `" + (Array.isArray(expectedType) ? expectedType.join(" | ") : expectedType) + "` but was of type `" + actualType + "`", "The actual value provided was: " + (typeOf(value) === "object" ? "\n" + JSON.stringify(value, null, 2).split("\n").map(function (line) { return " " + line; }).join("\n") : "`" + value + "`")]; } }, Expected_number_of_arguments_does_match_actual_number: { errorCode: 1013, getDetails: function getDetails(_ref2) { var context = _ref2.context, args = _ref2.args, min = _ref2.min, max = _ref2.max; return ["Problem found with your usage of `" + context + "`", "Expected " + min + (min === max ? "" : " - " + max) + " but received " + args.length + " argument" + (args.length === 1 ? "" : "s")]; } }, Query_string_array_format_and_custom_query_string_serializer_may_not_both_be_provided: { errorCode: 1014, getDetails: function getDetails() { return ["You may not provide both options.arrayFormat.queryString and options.queryStringSerializer. These options are not compatible."]; } }, Expected_length_of_array_does_match_actual_length: { errorCode: 1015, getDetails: function getDetails(_ref3) { var context = _ref3.context, array = _ref3.array, min = _ref3.min, max = _ref3.max; return ["Problem found with your usage of `" + context + "`", "Expected array to be of length " + min + (min === max ? "" : " - " + max) + " but actual length was " + array.length]; } }, Encountered_unexpected_parameter_when_building_route: { errorCode: 1016, getDetails: function getDetails(_ref4) { var routeName = _ref4.routeName, unexpectedParameterName = _ref4.unexpectedParameterName, allowedParameterNames = _ref4.allowedParameterNames; return ["Problem found with your usage of routes." + routeName + "( ... )", "Unexpected parameter passed to route builder named \"" + unexpectedParameterName + "\"", allowedParameterNames.length === 0 ? "The route does not take any parameters" : "This route takes the following parameters: " + allowedParameterNames.map(function (name) { return "\"" + name + "\""; }).join(", ")]; } }, Missing_required_parameter_when_building_route: { errorCode: 1017, getDetails: function getDetails(_ref5) { var routeName = _ref5.routeName, missingParameterName = _ref5.missingParameterName; return ["Problem found with your usage of routes." + routeName + "( ... )", "The parameter \"" + missingParameterName + "\" is required but was not provided."]; } }, Base_url_must_start_with_a_forward_slash: { errorCode: 1018, getDetails: function getDetails(baseUrl) { return ['Base URL must start with a forward slash "/"', "The value you provided \"" + baseUrl + "\" does not start with a forward slash."]; } }, Base_url_must_not_contain_any_characters_that_must_be_url_encoded: { errorCode: 1019, getDetails: function getDetails(baseUrl) { var invalidCharacters = baseUrl.replace(/\//g, "").split("").filter(function (character) { return character !== encodeURIComponent(character); }); return ["The following characters are invalid: " + invalidCharacters.join(", ") + "."]; } }, App_should_be_wrapped_in_a_RouteProvider_component: { errorCode: 1020, getDetails: function getDetails() { return ["Your application must be wrapped in the `RouteProvider` component returned by `createRouter` in order to use the `useRoute` hook."]; } }, Invalid_React_version: { errorCode: 1021, getDetails: function getDetails(version) { return ["React version must be 16.8 or greater.", "You have version " + version + " installed.", "If you cannot upgrade the React version try using `type-route/core`."]; } } }); function buildErrorCollection(definitions) { var errors = {}; Object.keys(definitions).forEach(function (key) { var name = key.replace(/_/g, " ") + "."; var _definitions$key = definitions[key], errorCode = _definitions$key.errorCode, getDetails = _definitions$key.getDetails; var messageTitle = "TR" + errorCode + " \xB7 " + name; errors[key] = { errorCode: errorCode, name: name, create: function create() { var _getDetails; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var message = ((_getDetails = getDetails == null ? void 0 : getDetails.apply(void 0, args)) != null ? _getDetails : []).map(function (detail) { return "- " + detail; }).join("\n"); var error = new Error(message ? "\n\n" + messageTitle + "\n\n" + message + "\n" : "\n\n" + messageTitle + "\n"); error.name = "(hopefully helpful \uD83D\uDE04) TypeRouteError"; return error; } }; }); return errors; } function asArray(value) { return Array.isArray(value) ? value : [value]; } function assert(context, assertions) { assertions.forEach(function (assert) { return assert(context); }); } assert.arrayLength = function (array, min, max) { if (max === void 0) { max = min; } return function (context) { if (array.length < min || array.length > max) { throw TypeRouteError.Expected_length_of_array_does_match_actual_length.create({ context: context, array: array, min: min, max: max }); } }; }; assert.numArgs = function (args, min, max) { if (max === void 0) { max = min; } return function (context) { if (args.length < min || args.length > max) { throw TypeRouteError.Expected_number_of_arguments_does_match_actual_number.create({ context: context, args: args, min: min, max: max }); } }; }; assert.collectionOfType = function (expectedType, valueName, value) { return function (context) { if (typeOf(value) === "object") { var valuePropertyNames = Object.keys(value); for (var _i = 0, _valuePropertyNames = valuePropertyNames; _i < _valuePropertyNames.length; _i++) { var propertyName = _valuePropertyNames[_i]; assert.type(expectedType, valueName + "." + propertyName, value[propertyName])(context); } return; } throw TypeRouteError.Expected_type_does_not_match_actual_type.create({ context: context, actualType: typeOf(value), expectedType: "Record<string, " + (Array.isArray(expectedType) ? expectedType.join(" | ") : expectedType) + ">", value: value, valueName: valueName }); }; }; assert.arrayOfType = function (expectedType, valueName, value) { return function (context) { if (Array.isArray(value)) { for (var index = 0; index < value.length; index++) { assert.type(expectedType, valueName + "[" + index + "]", value[index])(context); } return; } throw TypeRouteError.Expected_type_does_not_match_actual_type.create({ context: context, actualType: typeOf(value), expectedType: "Array<" + (Array.isArray(expectedType) ? expectedType.join(" | ") : expectedType) + ">", value: value, valueName: valueName }); }; }; assert.type = function (expectedType, valueName, value) { return function (context) { var expectedTypeList = typeof expectedType === "string" ? [expectedType] : expectedType; for (var _iterator = _createForOfIteratorHelperLoose(expectedTypeList), _step; !(_step = _iterator()).done;) { var _expectedType = _step.value; var expectsProperType = _expectedType[0].toUpperCase() === _expectedType[0]; if (expectsProperType && (typeOf(value) === "object" || typeOf(value) === "function") && typeOf(value["~internal"]) === "object" && value["~internal"].type === _expectedType || !expectsProperType && typeOf(value) === _expectedType) { return; } } throw TypeRouteError.Expected_type_does_not_match_actual_type.create({ context: context, actualType: typeOf(value), expectedType: expectedType, value: value, valueName: valueName }); }; }; function buildPathDefs(routeName, pathParamDefCollection, getRawPath) { var namedPathParamDefs = Object.keys(pathParamDefCollection).map(function (paramName) { var namedPathParameterDefinition = _extends({ paramName: paramName }, pathParamDefCollection[paramName]); return namedPathParameterDefinition; }); var paramIdCollection = {}; namedPathParamDefs.forEach(function (_ref) { var paramName = _ref.paramName; if (process.env.NODE_ENV !== "production") { if (paramName.indexOf("$") >= 0 || paramName.indexOf("{") >= 0 || paramName.indexOf("}") >= 0 || paramName.indexOf("/") >= 0) { throw TypeRouteError.Path_parameter_name_must_not_include_curly_brackets_dollar_signs_or_the_forward_slash_character.create(routeName, paramName); } } paramIdCollection[paramName] = getParamId(paramName); }); var rawPath = getRawPath(paramIdCollection); if (process.env.NODE_ENV !== "production") { if (Array.isArray(rawPath)) { assert("ReturnType<" + routeName + ".path>", [assert.arrayOfType("string", "path", rawPath), assert.arrayLength(rawPath, 1, Infinity)]); } else { assert("ReturnType<" + routeName + ".path>", [assert.type("string", "path", rawPath)]); } } return asArray(rawPath).map(function (rawPath) { var errorContext = { rawPath: rawPath, routeName: routeName }; if (process.env.NODE_ENV !== "production") { if (rawPath.length === 0) { throw TypeRouteError.Path_may_not_be_an_empty_string.create(errorContext); } if (rawPath[0] !== "/") { throw TypeRouteError.Path_must_start_with_a_forward_slash.create(errorContext); } } if (rawPath.length === 1) { return []; } if (process.env.NODE_ENV !== "production") { if (rawPath.length > 0 && rawPath[rawPath.length - 1] === "/") { throw TypeRouteError.Path_may_not_end_with_a_forward_slash.create(errorContext); } } var rawPathSegments = rawPath.split("/").slice(1); var usedPathParams = {}; var pathDef = []; for (var _iterator = _createForOfIteratorHelperLoose(rawPathSegments), _step; !(_step = _iterator()).done;) { var rawSegment = _step.value; if (process.env.NODE_ENV !== "production") { if (rawSegment.length === 0) { throw TypeRouteError.Path_may_not_include_empty_segments.create(errorContext); } } var includedParamDef = null; for (var _iterator2 = _createForOfIteratorHelperLoose(namedPathParamDefs), _step2; !(_step2 = _iterator2()).done;) { var paramDef = _step2.value; if (rawSegment.indexOf(getParamId(paramDef.paramName)) >= 0) { if (process.env.NODE_ENV !== "production") { if (includedParamDef !== null) { throw TypeRouteError.Path_may_have_at_most_one_parameter_per_segment.create(errorContext, [paramDef.paramName, includedParamDef.paramName]); } if (usedPathParams[paramDef.paramName]) { throw TypeRouteError.Path_parameters_may_not_be_used_more_than_once_when_building_a_path.create(errorContext, paramDef.paramName); } } includedParamDef = paramDef; usedPathParams[paramDef.paramName] = true; } } if (includedParamDef) { var _rawSegment$split = rawSegment.split(getParamId(includedParamDef.paramName)), leading = _rawSegment$split[0], trailing = _rawSegment$split[1]; if (process.env.NODE_ENV !== "production") { if (encodeURIComponent(leading) !== leading || encodeURIComponent(trailing) !== trailing) { throw TypeRouteError.Path_may_not_include_characters_that_must_be_URL_encoded.create(errorContext, { leading: leading, paramId: getParamId(includedParamDef.paramName), trailing: trailing }); } if (includedParamDef["~internal"].optional && (leading !== "" || trailing !== "")) { throw TypeRouteError.Optional_path_parameters_may_not_have_any_text_around_the_parameter.create(errorContext, includedParamDef.paramName, leading, trailing); } } pathDef.push({ leading: leading, trailing: trailing, namedParamDef: includedParamDef }); } else { if (process.env.NODE_ENV !== "production") { if (encodeURIComponent(rawSegment) !== rawSegment) { throw TypeRouteError.Path_may_not_include_characters_that_must_be_URL_encoded.create(errorContext, { leading: rawSegment }); } } pathDef.push({ leading: rawSegment, trailing: "", namedParamDef: null }); } } var numOptionalOrTrailingParams = pathDef.filter(function (part) { var _part$namedParamDef, _part$namedParamDef2; return ((_part$namedParamDef = part.namedParamDef) == null ? void 0 : _part$namedParamDef["~internal"].optional) || ((_part$namedParamDef2 = part.namedParamDef) == null ? void 0 : _part$namedParamDef2["~internal"].trailing); }).length; if (process.env.NODE_ENV !== "production") { if (numOptionalOrTrailingParams > 1) { throw TypeRouteError.Path_may_have_at_most_one_optional_or_trailing_parameter.create(errorContext, numOptionalOrTrailingParams); } } var lastPathSegmentParameterDefinition = pathDef[pathDef.length - 1].namedParamDef; if (process.env.NODE_ENV !== "production") { if (numOptionalOrTrailingParams === 1 && !(lastPathSegmentParameterDefinition != null && lastPathSegmentParameterDefinition["~internal"].optional) && !(lastPathSegmentParameterDefinition != null && lastPathSegmentParameterDefinition["~internal"].trailing)) { throw TypeRouteError.Optional_or_trailing_path_parameters_may_only_appear_in_the_last_path_segment.create(errorContext); } } var unusedPathParameterDefinitions = namedPathParamDefs.map(function (_ref2) { var name = _ref2.paramName; return name; }).filter(function (name) { return !usedPathParams[name]; }); if (process.env.NODE_ENV !== "production") { if (unusedPathParameterDefinitions.length > 0) { throw TypeRouteError.All_path_parameters_must_be_used_in_path_construction.create(errorContext, unusedPathParameterDefinitions); } } return pathDef; }); } function getParamId(parameterName) { return "${p." + parameterName + "}"; } function getParamDefsOfType(type, paramDefCollection) { var filteredParamDefCollection = {}; Object.keys(paramDefCollection).forEach(function (name) { var paramDef = paramDefCollection[name]; if (paramDef["~internal"].kind === type) { filteredParamDefCollection[name] = paramDef; } }); return filteredParamDefCollection; } function createLocation(_ref) { var paramCollection = _ref.paramCollection, paramDefCollection = _ref.paramDefCollection, arraySeparator = _ref.arraySeparator, queryStringSerializer = _ref.queryStringSerializer, pathDefs = _ref.pathDefs, baseUrl = _ref.baseUrl; var params = { path: {}, query: {}, state: {} }; var _loop = function _loop(paramName) { var _paramDef$Internal$v; var paramValue = paramCollection[paramName]; if (paramValue === undefined) { return "continue"; } var paramDef = paramDefCollection[paramName]; var urlEncodeDefault = paramDef["~internal"].kind !== "state" && !paramDef["~internal"].trailing; var urlEncode = (_paramDef$Internal$v = paramDef["~internal"].valueSerializer.urlEncode) != null ? _paramDef$Internal$v : urlEncodeDefault; var getValue = function getValue(paramValue) { var value; if (paramDef["~internal"].array) { if (!Array.isArray(paramValue)) { if (process.env.NODE_ENV !== "production") { throw TypeRouteError.Expected_type_does_not_match_actual_type.create({ context: "routes[routeName](...)", actualType: typeOf(paramValue), expectedType: "array", value: paramValue, valueName: paramName }); } } value = paramValue.length === 0 ? null : paramValue.map(function (part) { return stringify(paramDef, part, urlEncode); }).join(arraySeparator); } else { value = stringify(paramDef, paramValue, urlEncode); } return value; }; var value = getValue(paramValue); if (paramDef["~internal"].kind === "query" && paramDef["~internal"]["default"] !== undefined && getValue(paramDef["~internal"]["default"]) === value) { return "continue"; } params[paramDef["~internal"].kind][paramName] = { valueSerializerId: paramDef["~internal"].valueSerializer.id, array: paramDef["~internal"].array, value: value }; }; for (var paramName in paramDefCollection) { var _ret = _loop(paramName); if (_ret === "continue") continue; } var path = "/" + pathDefs[0].filter(function (_ref2) { var namedParamDef = _ref2.namedParamDef; return !(namedParamDef != null && namedParamDef["~internal"].optional && params.path[namedParamDef.paramName] === undefined); }).map(function (_ref3) { var namedParamDef = _ref3.namedParamDef, leading = _ref3.leading, trailing = _ref3.trailing; var rawParam = namedParamDef ? params.path[namedParamDef.paramName].value : ""; return leading + rawParam + trailing; }).join("/"); var hasQueryParams = Object.keys(params.query).length > 0; var query = hasQueryParams ? queryStringSerializer.stringify(params.query) : undefined; if (process.env.NODE_ENV !== "production") { if (hasQueryParams) { assert("query", [assert.type("string", "query", query)]); } } var state = Object.keys(params.state).length === 0 ? undefined : Object.keys(params.state).reduce(function (state, key) { var _extends2; return _extends({}, state, (_extends2 = {}, _extends2[key] = params.state[key].value, _extends2)); }, {}); return { fullPath: (baseUrl === "/" ? "" : baseUrl) + path, path: path, query: query, state: state }; } function stringify(paramDef, value, urlEncode) { var result = paramDef["~internal"].valueSerializer.stringify(value); if (process.env.NODE_ENV !== "production") { assert("[ValueSerializer].stringify", [assert.type("string", "result", result)]); } return urlEncode ? encodeURIComponent(result) : result; } var noMatch = { __noMatch: true }; var stringUtils = { startsWith: startsWith, endsWith: endsWith, splitFirst: splitFirst }; function startsWith(value, start) { for (var i = 0; i < start.length; i++) { if (start[i] !== value[i]) { return false; } } return true; } function endsWith(value, end) { for (var i = 1; i <= end.length; i++) { if (end[end.length - i] !== value[value.length - i]) { return false; } } return true; } function splitFirst(value, split) { var _value$split = value.split(split), first = _value$split[0], rest = _value$split.slice(1); return [first, rest.join(split)]; } var endsWith$1 = stringUtils.endsWith, startsWith$1 = stringUtils.startsWith; function getPathMatch(_ref) { var path = _ref.path, pathDefs = _ref.pathDefs, arraySeparator = _ref.arraySeparator; for (var index = 0; index < pathDefs.length; index++) { var result = match(pathDefs[index]); if (result !== false) { return _extends({}, result, { primaryPath: index === 0 }); } } return false; function match(pathDef) { var params = {}; if (path === "/" && pathDef.length === 0) { return { params: params, numExtraneousParams: 0 }; } var pathHasTrailingSlash = path.length > 1 && endsWith$1(path, "/"); if (pathHasTrailingSlash) { path = path.slice(0, path.length - 1); } var pathSegmentList = path.split("/").slice(1); var _loop = function _loop(segmentIndex) { var _pathSegmentDef$named2, _pathSegmentDef$named3; var pathSegmentDef = segmentIndex >= pathDef.length ? null : pathDef[segmentIndex]; var pathSegment = segmentIndex >= pathSegmentList.length ? null : pathSegmentList[segmentIndex]; if (pathSegmentDef === null) { return { v: false }; } var numRemainingPathSegmentDefs = pathDef.length - 1 - segmentIndex; if (pathSegment === null) { var _pathSegmentDef$named; if (numRemainingPathSegmentDefs !== 0 || !((_pathSegmentDef$named = pathSegmentDef.namedParamDef) != null && _pathSegmentDef$named["~internal"].optional)) { return { v: false }; } return "break"; } if ((_pathSegmentDef$named2 = pathSegmentDef.namedParamDef) != null && _pathSegmentDef$named2["~internal"].trailing) { pathSegment = pathSegmentList.slice(segmentIndex).join("/"); } if (!startsWith$1(pathSegment, pathSegmentDef.leading)) { return { v: false }; } var pathSegmentMinusLeading = pathSegment.slice(pathSegmentDef.leading.length); if (!endsWith$1(pathSegmentMinusLeading, pathSegmentDef.trailing)) { return { v: false }; } var pathSegmentMinusLeadingAndTrailing = pathSegmentMinusLeading.slice(0, pathSegmentMinusLeading.length - pathSegmentDef.trailing.length); if (!pathSegmentDef.namedParamDef) { if (pathSegmentMinusLeadingAndTrailing === "") { return "continue"; } return { v: false }; } if (pathSegmentMinusLeadingAndTrailing === "") { if (pathSegmentDef.namedParamDef["~internal"].optional) { return "continue"; } return { v: false }; } var urlEncode = (_pathSegmentDef$named3 = pathSegmentDef.namedParamDef["~internal"].valueSerializer.urlEncode) != null ? _pathSegmentDef$named3 : !pathSegmentDef.namedParamDef["~internal"].trailing; var value = void 0; if (pathSegmentDef.namedParamDef["~internal"].array) { value = pathSegmentMinusLeadingAndTrailing.split(arraySeparator).map(function (part) { var _pathSegmentDef$named4; return (_pathSegmentDef$named4 = pathSegmentDef.namedParamDef) == null ? void 0 : _pathSegmentDef$named4["~internal"].valueSerializer.parse(urlEncode ? decodeURIComponent(part) : part); }); if (value.some(function (part) { return part === noMatch; })) { return { v: false }; } } else { value = pathSegmentDef.namedParamDef["~internal"].valueSerializer.parse(urlEncode ? decodeURIComponent(pathSegmentMinusLeadingAndTrailing) : pathSegmentMinusLeadingAndTrailing); if (value === noMatch) { return { v: false }; } } if (pathSegmentDef.namedParamDef["~internal"].trailing && pathHasTrailingSlash && pathSegmentDef.trailing === "") { value = value + "/"; } params[pathSegmentDef.namedParamDef.paramName] = value; if (pathSegmentDef.namedParamDef["~internal"].trailing) { return "break"; } }; for (var segmentIndex = 0; segmentIndex < Math.max(pathDef.length, pathSegmentList.length); segmentIndex++) { var _ret = _loop(segmentIndex); if (_ret === "break") break; if (_ret === "continue") continue; if (typeof _ret === "object") return _ret.v; } return { params: params, numExtraneousParams: 0 }; } } function getObjectMatch(_ref) { var object = _ref.object, paramDefs = _ref.paramDefs, urlEncodeDefault = _ref.urlEncodeDefault, arraySeparator = _ref.arraySeparator; var params = {}; var namedParamDefs = Object.keys(paramDefs).map(function (name) { return _extends({ name: name }, paramDefs[name]); }); var extraneousParams = _extends({}, object); var _loop = function _loop() { var paramDef = _step.value; var raw = object[paramDef.name]; delete extraneousParams[paramDef.name]; if (raw === undefined) { if (paramDef["~internal"].optional) { return "continue"; } return { v: false }; } var value = void 0; if (raw === null) { if (paramDef["~internal"].array) { value = []; } else if (paramDef["~internal"].optional) { return "continue"; } else { return { v: false }; } } else if (paramDef["~internal"].array) { value = raw.split(arraySeparator).map(function (part) { var _paramDef$Internal$v; return paramDef["~internal"].valueSerializer.parse(((_paramDef$Internal$v = paramDef["~internal"].valueSerializer.urlEncode) != null ? _paramDef$Internal$v : urlEncodeDefault) ? decodeURIComponent(part) : part); }); if (value.some(function (part) { return part === noMatch; })) { if (paramDef["~internal"].optional) { return "continue"; } return { v: false }; } } else { var _paramDef$Internal$v2; value = paramDef["~internal"].valueSerializer.parse(((_paramDef$Internal$v2 = paramDef["~internal"].valueSerializer.urlEncode) != null ? _paramDef$Internal$v2 : urlEncodeDefault) ? decodeURIComponent(raw) : raw); if (value === noMatch) { if (paramDef["~internal"].optional) { return "continue"; } return { v: false }; } } params[paramDef.name] = value; }; for (var _iterator = _createForOfIteratorHelperLoose(namedParamDefs), _step; !(_step = _iterator()).done;) { var _ret = _loop(); if (_ret === "continue") continue; if (typeof _ret === "object") return _ret.v; } return { params: params, numExtraneousParams: Object.keys(extraneousParams).length }; } function getStateMatch(state, paramDefs, arraySeparator) { return getObjectMatch({ object: state != null ? state : {}, paramDefs: paramDefs, urlEncodeDefault: false, arraySeparator: arraySeparator }); } function getQueryMatch(query, paramDefs, queryStringSerializer, arraySeparator) { var object = {}; if (query) { object = queryStringSerializer.parse(query); if (process.env.NODE_ENV !== "production") { assert("[QueryStringSerializer].parse", [assert.collectionOfType(["string", "null"], "parsedQueryString", object)]); } } return getObjectMatch({ object: object, paramDefs: paramDefs, urlEncodeDefault: true, arraySeparator: arraySeparator }); } function createMatcher(_ref) { var pathDefs = _ref.pathDefs, params = _ref.params; var queryParamDefCollection = getParamDefsOfType("query", params); var stateParamDefCollection = getParamDefsOfType("state", params); var defaultParams = {}; Object.keys(params).forEach(function (paramName) { var param = params[paramName]; if (param["~internal"]["default"] === undefined) { return; } defaultParams[paramName] = param["~internal"]["default"]; }); return function (_ref2) { var routerLocation = _ref2.routerLocation, arraySeparator = _ref2.arraySeparator, queryStringSerializer = _ref2.queryStringSerializer; if (routerLocation.path === undefined) { return false; } var pathMatch = getPathMatch({ path: routerLocation.path, pathDefs: pathDefs, arraySeparator: arraySeparator }); if (pathMatch === false) { return false; } var queryMatch = getQueryMatch(routerLocation.query, queryParamDefCollection, queryStringSerializer, arraySeparator); if (queryMatch === false) { return false; } var stateMatch = getStateMatch(routerLocation.state, stateParamDefCollection, arraySeparator); if (stateMatch === false) { return false; } return { primaryPath: pathMatch.primaryPath, params: _extends({}, defaultParams, pathMatch.params, queryMatch.params, stateMatch.params), numExtraneousParams: pathMatch.numExtraneousParams + queryMatch.numExtraneousParams + stateMatch.numExtraneousParams }; }; } function preventDefaultLinkClickBehavior(event) { if (event === void 0) { event = {}; } var e = event; var isModifiedEvent = !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey); var isSelfTarget = !e.target || !e.target.target || e.target.target === "_self"; if (isSelfTarget && // Ignore everything but links with target self !e.defaultPrevented && // onClick prevented default e.button === 0 && // ignore everything but left clicks !isModifiedEvent // ignore clicks with modifier keys ) { if (e && e.preventDefault) { e.preventDefault(); } return true; } return false; } var startsWith$2 = stringUtils.startsWith; function buildRoute(_ref) { var routeName = _ref.routeName, params = _ref.params, location = _ref.location, routerContext = _ref.routerContext; var navigate = routerContext.navigate, history = routerContext.history; var href = history.createHref({ pathname: routeName === false ? location.fullPath : location.path, search: location.query ? "?" + location.query : "" }); if (startsWith$2(href, "#")) { href = "/" + href; } if (routeName !== false && routerContext.baseUrl !== "/") { href = routerContext.baseUrl + href; } var route = { name: routeName, params: params, href: href, link: { href: href, onClick: function onClick(event) { if (preventDefaultLinkClickBehavior(event)) { return route.push(); } } }, action: null, push: function push() { return navigate(_extends({}, route, { action: "push" }), true); }, replace: function replace() { return navigate(_extends({}, route, { action: "replace" }), true); } }; return route; } function createRouteBuilder(routeName, routeDef, getRouterContext) { var pathDefs = buildPathDefs(routeName, getParamDefsOfType("path", routeDef["~internal"].params), routeDef["~internal"].path); var build = function build(params) { if (params === void 0) { params = {}; } if (process.env.NODE_ENV !== "production") { assert("routes." + routeName, [assert.numArgs([].slice.call(arguments), 0, 1), assert.type("object", "params", params)]); for (var paramKey in params) { if (!(paramKey in routeDef["~internal"].params)) { throw TypeRouteError.Encountered_unexpected_parameter_when_building_route.create({ routeName: routeName, unexpectedParameterName: paramKey, allowedParameterNames: Object.keys(routeDef["~internal"].params) }); } } for (var _paramKey in routeDef["~internal"].params) { var value = params[_paramKey]; var paramDef = routeDef["~internal"].params[_paramKey]["~internal"]; if (value === undefined) { if (!paramDef.optional) { throw TypeRouteError.Missing_required_parameter_when_building_route.create({ routeName: routeName, missingParameterName: _paramKey }); } continue; } } } var routerContext = getRouterContext(); var arraySeparator = routerContext.arraySeparator, queryStringSerializer = routerContext.queryStringSerializer, baseUrl = routerContext.baseUrl; var paramsWithDefault = _extends({}, params); Object.keys(routeDef["~internal"].params).forEach(function (paramName) { var paramDef = routeDef["~internal"].params[paramName]; if (paramsWithDefault[paramName] === undefined && paramDef["~internal"]["default"] !== undefined) { paramsWithDefault[paramName] = paramDef["~internal"]["default"]; } }); var location = createLocation({ paramCollection: params, paramDefCollection: routeDef["~internal"].params, arraySeparator: arraySeparator, queryStringSerializer: queryStringSerializer, pathDefs: pathDefs, baseUrl: baseUrl }); return buildRoute({ routeName: routeName, params: paramsWithDefault, location: location, routerContext: routerContext }); }; Object.defineProperty(build, "name", { value: routeName }); build["~internal"] = { type: "RouteBuilder", match: createMatcher({ pathDefs: pathDefs, params: routeDef["~internal"].params }), pathDefs: pathDefs, Route: null }; return build; } function createQueryStringSerializer(args) { var _args$queryStringArra, _args$arraySeparator; if (args === void 0) { args = {}; } var queryStringArrayFormat = (_args$queryStringArra = args.queryStringArrayFormat) != null ? _args$queryStringArra : "singleKeyWithBracket"; var arraySeparator = (_args$arraySeparator = args.arraySeparator) != null ? _args$arraySeparator : ","; var multiKey = queryStringArrayFormat === "multiKey" || queryStringArrayFormat === "multiKeyWithBracket"; var arrayKeySuffix = queryStringArrayFormat === "multiKey" || queryStringArrayFormat === "singleKey" ? "" : "[]"; return { parse: function parse(raw) { var queryParams = {}; for (var _iterator = _createForOfIteratorHelperLoose(raw.split("&")), _step; !(_step = _iterator()).done;) { var part = _step.value; var _part$split = part.split("="), rawParamName = _part$split[0], rawParamValue = _part$split[1], rest = _part$split.slice(2); if (rawParamName === undefined || rest.length > 0) { continue; } var key = decodeURIComponent(stringUtils.endsWith(rawParamName, arrayKeySuffix) ? rawParamName.slice(0, rawParamName.length - arrayKeySuffix.length) : rawParamName); if (rawParamValue === undefined) { queryParams[key] = null; } else if (queryParams[key] && multiKey) { queryParams[key] += "" + arraySeparator + rawParamValue; } else { queryParams[key] = rawParamValue; } } return queryParams; }, stringify: function stringify(queryParams) { return Object.keys(queryParams).map(function (name) { var encodedName = encodeURIComponent(name); var key = queryParams[name].array ? "" + encodedName + arrayKeySuffix : encodedName; var value = queryParams[name].value; if (value === null) { return key; } if (queryParams[name].array && multiKey) { var valueParts = value.split(arraySeparator); return valueParts.map(function (part) { return key + "=" + part; }).join("&"); } return key + "=" + value; }).join("&"); } }; } function getMatchingRoute(location, routerContext) { var getRoutes = routerContext.getRoutes, queryStringSerializer = routerContext.queryStringSerializer, arraySeparator = routerContext.arraySeparator; var routes = getRoutes(); var nonExactMatch = false; for (var routeName in routes) { var match = routes[routeName]["~internal"].match({ routerLocation: location, queryStringSerializer: queryStringSerializer, arraySeparator: arraySeparator }); if (match === false) { continue; } if (match.numExtraneousParams === 0) { return { route: routes[routeName](match.params), primaryPath: match.primaryPath }; } if (nonExactMatch === false || match.numExtraneousParams < nonExactMatch.numExtraneousParams) { nonExactMatch = _extends({}, match, { routeName: routeName }); } } if (nonExactMatch) { return { route: routes[nonExactMatch.routeName](nonExactMatch.params), primaryPath: nonExactMatch.primaryPath }; } return { route: buildRoute({ routeName: false, params: {}, location: location, routerContext: routerContext }), primaryPath: true }; } var startsWith$3 = stringUtils.startsWith; function convertToRouterLocationFromHistoryLocation(rawLocation, baseUrl) { return { fullPath: rawLocation.pathname, path: startsWith$3(rawLocation.pathname, baseUrl) ? baseUrl !== "/" ? rawLocation.pathname.replace(baseUrl, "") : rawLocation.pathname : undefined, query: rawLocation.search ? startsWith$3(rawLocation.search, "?") ? rawLocation.search.slice(1) : rawLocation.search : undefined, state: typeof rawLocation.state === "object" && rawLocation.state !== null ? rawLocation.state.state : undefined }; } var splitFirst$1 = stringUtils.splitFirst; function getRouteByHref(href, state, routerContext) { var _splitFirst = splitFirst$1(href, "?"), pathname = _splitFirst[0], search = _splitFirst[1]; var location = convertToRouterLocationFromHistoryLocation({ pathname: pathname, search: search, state: state }, routerContext.baseUrl); return getMatchingRoute(location, routerContext); } function createNavigationHandlerManager(_ref) { var startListening = _ref.startListening, stopListening = _ref.stopListening; var handlerIdList = []; var idCounter = 0; return { add: add, getHandlers: getHandlers }; function getHandlers() { return handlerIdList.map(function (_ref2) { var handler = _ref2.handler; return handler; }); } function add(handler) { var id = idCounter++; handlerIdList.push({ id: id, handler: handler }); if (handlerIdList.length === 1) { startListening(); } return remove; function remove() { var index = handlerIdList.map(function (_ref3) { var id = _ref3.id; return id; }).indexOf(id); if (index >= 0) { handlerIdList.splice(index, 1); if (handlerIdList.length === 0) { stopListening(); } } } } } function attemptScrollToTop(route, scrollToTop) { if (route.action === "push" && typeof window === "object" && window !== null && typeof window.scroll === "function" && scrollToTop !== false && typeof navigator === "object" && navigator !== null && typeof navigator.userAgent === "string" && !(navigator.userAgent.indexOf("Node.js") > 0 || navigator.userAgent.indexOf("jsdom") > 0)) { try { window.scroll(0, 0); } catch (_unused) {} } } function serializeStateParams(route, routeDefs) { var state = {}; if (route.name) { var sortedParams = Object.keys(route.params).sort(); for (var _iterator = _createForOfIteratorHelperLoose(sortedParams), _step; !(_step = _iterator()).done;) { var paramName = _step.value; var paramDef = routeDefs[route.name]["~internal"].params[paramName]["~internal"]; if (paramDef.kind === "state") { var value = route.params[paramName]; state[paramName] = paramDef.valueSerializer.stringify(value); } } } return state; } var startsWith$4 = stringUtils.startsWith, splitFirst$2 = stringUtils.splitFirst; function createRouter() { var _opts$baseUrl, _opts$arrayFormat$sep, _opts$arrayFormat, _opts$queryStringSeri, _opts$arrayFormat2; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var _parseArgs = parseArgs(args), routeDefs = _parseArgs.routeDefs, opts = _parseArgs.opts; var navigationHandlerManager = createNavigationHandlerManager({ startListening: function startListening() { unlisten = history.listen(function (update) { if (skipNextEnvironmentTriggeredNavigation) { skipNextEnvironmentTriggeredNavigation = false; return; } var location = convertToRouterLocationFromHistoryLocation(update.location, baseUrl); var action = update.action.toLowerCase(); var _getMatchingRoute = getMatchingRoute(location, getRouterContext()), route = _getMatchingRoute.route, primaryPath = _getMatchingRoute.primaryPath; handleNavigation(_extends({}, route, { action: action }), primaryPath); }); }, stopListening: function stopListening() { return unlisten == null ? void 0 : unlisten(); } }); var baseUrl = (_opts$baseUrl = opts.baseUrl) != null ? _opts$baseUrl : "/"; var arraySeparator = (_opts$arrayFormat$sep = (_opts$arrayFormat = opts.arrayFormat) == null ? void 0 : _opts$arrayFormat.separator) != null ? _opts$arrayFormat$sep : ","; var queryStringSerializer = (_opts$queryStringSeri = opts.queryStringSerializer) != null ? _opts$queryStringSeri : createQueryStringSerializer({ queryStringArrayFormat: (_opts$arrayFormat2 = opts.arrayFormat) == null ? void 0 : _opts$arrayFormat2.queryString, arraySeparator: arraySeparator }); var history; var unlisten; var skipNextEnvironmentTriggeredNavigation = false; var skipHandlingNextApplicationTriggeredNavigation = false; var initialRoute = null; var previousRoute = null; var blockerCollection = []; applySessionOpts(opts.session); var routes = createRouteBuilderCollection(getRouterContext); var router = { routes: routes, session: { push: function push(href, state) { if (process.env.NODE_ENV !== "production") { assert("[RouterSessionHistory].push", [assert.numArgs([].slice.call(arguments), 1, 2), assert.type("string", "href", href), assert.type(["object", "undefined"], "state", state)]); } var _getRouteByHref = getRouteByHref(href, state, getRouterContext()), route = _getRouteByHref.route, primaryPath = _getRouteByHref.primaryPath; return navigate(_extends({}, route, { action: "push" }), primaryPath); }, replace: function replace(href, state) { if (process.env.NODE_ENV !== "production") { assert("[RouterSessionHistory].replace", [assert.numArgs([].slice.call(arguments), 1, 2), assert.type("string", "href", href), assert.type(["object", "undefined"], "state", state)]); } var _getRouteByHref2 = getRouteByHref(href, state, getRouterContext()), route = _getRouteByHref2.route, primaryPath = _getRouteByHref2.primaryPath; return navigate(_extends({}, route, { action: "replace" }), primaryPath); }, back: function back(amount) { if (amount === void 0) { amount = 1; } if (process.env.NODE_ENV !== "production") { assert("[RouterSessionHistory].back", [assert.numArgs([].slice.call(arguments), 0, 1), assert.type("number", "amount", amount)]); } history.go(-amount); }, forward: function forward(amount) { if (amount === void 0) { amount = 1;