UNPKG

@koordinates/xstate-tree

Version:

Build UIs with Actors using xstate and React

186 lines (185 loc) 8.28 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildCreateRoute = void 0; const path_to_regexp_1 = require("path-to-regexp"); const query_string_1 = require("query-string"); const joinRoutes_1 = require("../joinRoutes"); /** * @public * * Creates a route factory * * @param history - the history object to use for this route factory, this needs to be the same one used in the trees root component * @param basePath - the base path for this route factory */ function buildCreateRoute(history, basePath) { function navigate({ history, url, meta, }) { const method = meta?.replace ? history.replace : history.push; method(url, { meta, previousUrl: `${window.location.pathname}${window.location.search}`, }); } return { simpleRoute(baseRoute) { return ({ url, paramsSchema, querySchema, ...args }) => { const matcher = (0, path_to_regexp_1.match)(url, { end: false }); const reverser = (0, path_to_regexp_1.compile)(url); return this.route(baseRoute)({ ...args, paramsSchema, querySchema, // @ts-ignore :cry: matcher: (url, query) => { const match = matcher(url); if (match === false) { return false; } const params = match.params; if (params && paramsSchema) { paramsSchema.parse(params); } if (query && querySchema) { querySchema.parse(query); } return { matchLength: match.path.length, params, query, }; }, // @ts-ignore :cry: reverser: (args) => { const url = reverser(args.params); if (args.query) { return `${url}?${(0, query_string_1.stringify)(args.query)}`; } return url; }, }); }; }, route(baseRoute) { function getParentArray() { const parentRoutes = []; let currentParent = baseRoute; while (currentParent) { parentRoutes.unshift(currentParent); currentParent = currentParent.parent; } return parentRoutes; } return ({ event, matcher, reverser, paramsSchema, querySchema, redirect, preload, canMatch, }) => { let fullParamsSchema = paramsSchema; let parentRoute = baseRoute; while (fullParamsSchema && parentRoute) { if (parentRoute.paramsSchema) { fullParamsSchema = fullParamsSchema.merge(parentRoute.paramsSchema); } parentRoute = parentRoute.parent; } return { basePath, event, history, paramsSchema, querySchema, parent: baseRoute, redirect, canMatch, matcher: matcher, reverser: reverser, // @ts-ignore :cry: getEvent(args) { const { params, query, meta } = args ?? {}; return { type: event, params, query, meta }; }, // @ts-ignore :cry: matches(suppliedUrl, search) { const fullUrl = suppliedUrl.endsWith("/") ? suppliedUrl : suppliedUrl + "/"; let url = fullUrl; const parentRoutes = getParentArray(); let params = {}; while (parentRoutes.length) { const parentRoute = parentRoutes.shift(); const parentMatch = parentRoute.matcher(url, undefined); if (parentMatch === false) { return false; } url = url.slice(parentMatch.matchLength); // All routes assume the url starts with a / // so if the parent route matches the / in the url, which consumes it // need to re-add it for the next route to match against if (!url.startsWith("/")) { url = "/" + url; } params = { ...params, ...(parentMatch.params ?? {}) }; } const matches = matcher(url, (0, query_string_1.parse)(search)); // if there is any URL left after matching this route, the last to match // that means the match isn't actually a match if (matches === false || matches.matchLength !== url.length) { return false; } const fullParams = { ...params, ...(matches.params ?? {}), }; if (fullParamsSchema) { fullParamsSchema.parse(fullParams); } if (querySchema) { querySchema.parse(matches.query); } // Check canMatch predicate if provided if (canMatch) { const canMatchResult = canMatch({ params: fullParams, query: matches.query ?? {}, }); if (!canMatchResult) { return false; } } return { originalUrl: `${fullUrl}${search}`, type: event, params: fullParams, query: matches.query ?? {}, }; }, // @ts-ignore :cry: reverse(args) { const { params, query } = args ?? {}; const parentRoutes = getParentArray(); const baseUrl = parentRoutes .map((route) => route.reverser({ params })) .reduce((fullUrl, urlPartial) => (0, joinRoutes_1.joinRoutes)(fullUrl, urlPartial), ""); return `${(0, joinRoutes_1.joinRoutes)(baseUrl, reverser({ params, query }))}`; }, // @ts-ignore :cry: navigate(args) { const { params, query, meta } = args ?? {}; const url = this.reverse({ params, query }); navigate({ url: (0, joinRoutes_1.joinRoutes)(this.basePath, url), meta, history: this.history(), }); }, // @ts-ignore :cry: preload(args) { const parentRoutes = getParentArray(); parentRoutes.forEach((route) => { route?.preload(args); }); preload?.(args); }, }; }; }, }; } exports.buildCreateRoute = buildCreateRoute;