@devp0nt/route0 1.0.0-next.17 → 1.0.0-next.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +5 -5
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +5 -5
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.test.ts +2 -0
- package/src/index.ts +18 -7
package/dist/cjs/index.cjs
CHANGED
|
@@ -172,9 +172,10 @@ class Route0 {
|
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
174
|
static getMatch(route0, location) {
|
|
175
|
+
const locationCloned = { ...location, params: { ...location.params }, query: { ...location.query } };
|
|
175
176
|
const escapeRegex = (s) => s.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
176
177
|
const def = route0.pathDefinition.length > 1 && route0.pathDefinition.endsWith("/") ? route0.pathDefinition.slice(0, -1) : route0.pathDefinition;
|
|
177
|
-
const pathname =
|
|
178
|
+
const pathname = locationCloned.pathname.length > 1 && locationCloned.pathname.endsWith("/") ? locationCloned.pathname.slice(0, -1) : locationCloned.pathname;
|
|
178
179
|
const paramNames = [];
|
|
179
180
|
const pattern = def.replace(/:([A-Za-z0-9_]+)/g, (_m, name) => {
|
|
180
181
|
paramNames.push(String(name));
|
|
@@ -186,10 +187,9 @@ class Route0 {
|
|
|
186
187
|
if (exactMatch) {
|
|
187
188
|
const values = exactMatch.slice(1);
|
|
188
189
|
const params = Object.fromEntries(paramNames.map((n, i) => [n, decodeURIComponent(values[i] ?? "")]));
|
|
189
|
-
|
|
190
|
+
locationCloned.params = params;
|
|
190
191
|
} else {
|
|
191
|
-
;
|
|
192
|
-
location.params = {};
|
|
192
|
+
locationCloned.params = {};
|
|
193
193
|
}
|
|
194
194
|
const exact = !!exactMatch;
|
|
195
195
|
const parent = !exact && parentRe.test(pathname);
|
|
@@ -198,7 +198,7 @@ class Route0 {
|
|
|
198
198
|
exact,
|
|
199
199
|
parent,
|
|
200
200
|
children,
|
|
201
|
-
location
|
|
201
|
+
location: locationCloned
|
|
202
202
|
};
|
|
203
203
|
}
|
|
204
204
|
match(url) {
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts"],"sourcesContent":["// TODO: query input can be boolean, or even object with qs\n// TODO: Роут0 три пусть три тоже сам генерится вероятно\n// TODO: Роут0 три мод, тогда там все ноуты кончаются на .селф\n// TODO: use splats in param definition \"*\"\n// TODO: ? check extend for query only .extend('&x&z')\n// TODO: .create(route, {useQuery, useParams})\n// TODO: Из пас экзакт, из пасвизквери экзает, из чилдрен, из парент, из экзактОр\n// TODO: isEqual, isChildren, isParent\n// TODO: extractParams, extractQuery\n// TODO: getPathDefinition respecting definitionParamPrefix, definitionQueryPrefix\n// TODO: prepend\n// TODO: Route0.createTree({base:{self: x, children: ...})\n// TODO: overrideTree\n// TODO: .create(route, {baseUrl, useLocation})\n// TODO: ? optional path params as @\n// TODO: prependMany, extendMany, overrideMany, with types\n\n// page0\n// TODO: Сделать чисто фронтовую штуку, которая сама вызывает лоадер, сама вызывает нужные мета и title, и отдаёт в компонент нужные штуки\n\n// ssr0\n// TODO: ССР работает просто поверх любого роутера, который поддерживает асинхронную загрузку страниц\n\nexport class Route0<\n TPathOriginalDefinition extends string,\n TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,\n TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,\n TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,\n> {\n pathOriginalDefinition: TPathOriginalDefinition\n private readonly pathDefinition: TPathDefinition\n paramsDefinition: TParamsDefinition\n queryDefinition: TQueryDefinition\n baseUrl: string\n\n private constructor(definition: TPathOriginalDefinition, config: Route0.RouteConfigInput = {}) {\n this.pathOriginalDefinition = definition\n this.pathDefinition = Route0._getPathDefinitionByOriginalDefinition(definition) as TPathDefinition\n this.paramsDefinition = Route0._getParamsDefinitionByRouteDefinition(definition) as TParamsDefinition\n this.queryDefinition = Route0._getQueryDefinitionByRouteDefinition(definition) as TQueryDefinition\n\n const { baseUrl } = config\n if (baseUrl && typeof baseUrl === 'string' && baseUrl.length) {\n this.baseUrl = baseUrl\n } else {\n const g = globalThis as unknown as { location?: { origin?: string } }\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (g?.location?.origin) {\n this.baseUrl = g.location.origin\n } else {\n this.baseUrl = 'https://example.com'\n }\n }\n }\n\n static create<\n TPathOriginalDefinition extends string,\n TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,\n TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,\n TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,\n >(\n definition: TPathOriginalDefinition,\n config?: Route0.RouteConfigInput,\n ): Route0.Callable<Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>> {\n const original = new Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>(\n definition,\n config,\n )\n const callable = original.get.bind(original)\n const proxy = new Proxy(callable, {\n get(_target, prop, receiver) {\n const value = (original as any)[prop]\n if (typeof value === 'function') {\n return value.bind(original)\n }\n return value\n },\n set(_target, prop, value, receiver) {\n ;(original as any)[prop] = value\n return true\n },\n has(_target, prop) {\n return prop in original\n },\n })\n Object.setPrototypeOf(proxy, Route0.prototype)\n return proxy as never\n }\n\n private static _splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition: string) {\n const i = pathOriginalDefinition.indexOf('&')\n if (i === -1) return { pathDefinition: pathOriginalDefinition, queryTailDefinition: '' }\n return {\n pathDefinition: pathOriginalDefinition.slice(0, i),\n queryTailDefinition: pathOriginalDefinition.slice(i),\n }\n }\n\n private static _getAbsPath(baseUrl: string, pathWithQuery: string) {\n return new URL(pathWithQuery, baseUrl).toString().replace(/\\/$/, '')\n }\n\n private static _getPathDefinitionByOriginalDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n return pathDefinition as Route0._PathDefinition<TPathOriginalDefinition>\n }\n\n private static _getParamsDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n const matches = Array.from(pathDefinition.matchAll(/:([A-Za-z0-9_]+)/g))\n const paramsDefinition = Object.fromEntries(matches.map((m) => [m[1], true]))\n return paramsDefinition as Route0._ParamsDefinition<TPathOriginalDefinition>\n }\n\n private static _getQueryDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { queryTailDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n if (!queryTailDefinition) {\n return {} as Route0._QueryDefinition<TPathOriginalDefinition>\n }\n const keys = queryTailDefinition.split('&').map(Boolean)\n const queryDefinition = Object.fromEntries(keys.map((k) => [k, true]))\n return queryDefinition as Route0._QueryDefinition<TPathOriginalDefinition>\n }\n\n static overrideMany<T extends Record<string, Route0.AnyRoute>>(routes: T, config: Route0.RouteConfigInput): T {\n const result = {} as T\n for (const [key, value] of Object.entries(routes)) {\n ;(result as any)[key] = value.clone(config)\n }\n return result\n }\n\n extend<TSuffixDefinition extends string>(\n suffixDefinition: TSuffixDefinition,\n ): Route0.Callable<\n Route0<\n Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,\n Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>\n >\n > {\n const { pathDefinition: parentPathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(\n this.pathOriginalDefinition,\n )\n const { pathDefinition: suffixPathDefinition, queryTailDefinition: suffixQueryTailDefinition } =\n Route0._splitPathDefinitionAndQueryTailDefinition(suffixDefinition)\n const pathDefinition = `${parentPathDefinition}/${suffixPathDefinition}`.replace(/\\/{2,}/g, '/')\n const pathOriginalDefinition =\n `${pathDefinition}${suffixQueryTailDefinition}` as Route0._RoutePathOriginalDefinitionExtended<\n TPathOriginalDefinition,\n TSuffixDefinition\n >\n return Route0.create<\n Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,\n Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>\n >(pathOriginalDefinition, { baseUrl: this.baseUrl })\n }\n\n // has params\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs?: false }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs: true }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>\n\n // no params\n get(\n ...args: Route0._OnlyIfNoParams<TParamsDefinition, [], [never]>\n ): Route0._PathOnlyRouteValue<TPathOriginalDefinition>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs?: false }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs: true }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>\n\n // implementation\n get(...args: any[]): string {\n const { queryInput, paramsInput, absInput } = ((): {\n queryInput: Record<string, string | number>\n paramsInput: Record<string, string | number>\n absInput: boolean\n } => {\n if (args.length === 0) {\n return { queryInput: {}, paramsInput: {}, absInput: false }\n }\n const input = args[0]\n if (typeof input !== 'object' || input === null) {\n // throw new Error(\"Invalid get route input: expected object\")\n return { queryInput: {}, paramsInput: {}, absInput: false }\n }\n const { query, abs, ...params } = input\n return { queryInput: query || {}, paramsInput: params, absInput: abs ?? false }\n })()\n\n // validate params\n const neededParamsKeys = Object.keys(this.paramsDefinition)\n const providedParamsKeys = Object.keys(paramsInput)\n const notProvidedKeys = neededParamsKeys.filter((k) => !providedParamsKeys.includes(k))\n if (notProvidedKeys.length) {\n // throw new Error(`Missing params: not defined keys ${notProvidedKeys.map((k) => `\"${k}\"`).join(\", \")}.`)\n Object.assign(paramsInput, Object.fromEntries(notProvidedKeys.map((k) => [k, 'undefined'])))\n }\n\n // create url\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion\n let url = String(this.pathDefinition)\n // replace params\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n url = url.replace(/:([A-Za-z0-9_]+)/g, (_m, k) => encodeURIComponent(String(paramsInput?.[k] ?? '')))\n // query params\n const queryInputStringified = Object.fromEntries(Object.entries(queryInput).map(([k, v]) => [k, String(v)]))\n url = [url, new URLSearchParams(queryInputStringified).toString()].filter(Boolean).join('?')\n // dedupe slashes\n url = url.replace(/\\/{2,}/g, '/')\n // absolute\n url = absInput ? Route0._getAbsPath(this.baseUrl, url) : url\n\n return url\n }\n\n getDefinition() {\n return this.pathDefinition\n }\n\n clone(config?: Route0.RouteConfigInput) {\n return new Route0(this.pathOriginalDefinition, config)\n }\n\n static getLocation(url: `${string}://${string}`): Route0.Location\n static getLocation(path: `/${string}`): Route0.Location\n static getLocation(urlOrPath: string): Route0.Location\n static getLocation(urlOrPath: string): Route0.Location {\n // Allow both relative and absolute inputs\n const abs = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:\\/\\//.test(urlOrPath)\n const base = abs ? undefined : 'http://example.com' // dummy base for relative URLs\n\n const url = new URL(urlOrPath, base)\n\n // Extract query and params (params left empty for now — parsed separately)\n const query = Object.fromEntries(url.searchParams.entries())\n\n // Normalize pathname (remove trailing slash except for root)\n let pathname = url.pathname\n if (pathname.length > 1 && pathname.endsWith('/')) {\n pathname = pathname.slice(0, -1)\n }\n\n // Build the location object similar to browser location\n return {\n query,\n params: {}, // filled later by .parse()\n pathname,\n search: url.search,\n hash: url.hash,\n origin: abs ? url.origin : undefined,\n href: abs ? url.href : pathname + url.search + url.hash,\n abs,\n }\n }\n\n static getMatch<TRoute0 extends Route0.AnyRoute>(\n route0: TRoute0,\n location: Route0.Location,\n ): Route0.MatchResult<TRoute0> {\n const escapeRegex = (s: string) => s.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&')\n\n // Normalize both sides (no trailing slash except root)\n const def =\n route0.pathDefinition.length > 1 && route0.pathDefinition.endsWith('/')\n ? route0.pathDefinition.slice(0, -1)\n : route0.pathDefinition\n const pathname =\n location.pathname.length > 1 && location.pathname.endsWith('/')\n ? location.pathname.slice(0, -1)\n : location.pathname\n\n // Build a regex from the route definition: /a/:x/b/:y -> ^/a/([^/]+)/b/([^/]+)$\n const paramNames: string[] = []\n const pattern = def.replace(/:([A-Za-z0-9_]+)/g, (_m: string, name: string) => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion\n paramNames.push(String(name))\n return '([^/]+)'\n })\n\n const exactRe = new RegExp(`^${pattern}$`)\n const parentRe = new RegExp(`^${pattern}(?:/.*)?$`) // route matches the beginning of the URL (may have more)\n const exactMatch = pathname.match(exactRe)\n\n // Fill params only for exact match (keeps behavior predictable)\n if (exactMatch) {\n const values = exactMatch.slice(1)\n const params = Object.fromEntries(paramNames.map((n, i) => [n, decodeURIComponent(values[i] ?? '')]))\n ;(location as any).params = params\n } else {\n ;(location as any).params = {}\n }\n\n const exact = !!exactMatch\n const parent = !exact && parentRe.test(pathname)\n\n // \"children\": the URL is a prefix of the route definition (ignoring params’ concrete values)\n // We check if the definition starts with the URL path boundary-wise.\n const children = !exact && new RegExp(`^${escapeRegex(pathname)}(?:/|$)`).test(def)\n\n return {\n exact,\n parent,\n children,\n location,\n } as never\n }\n match(url: string): Route0.MatchResult<this> {\n const location = Route0.getLocation(url)\n return Route0.getMatch(this, location)\n }\n}\n\nexport namespace Route0 {\n export type AnyRoute = Route0<any, any, any, any>\n export type Callable<T extends AnyRoute> = T & T['get']\n export type RouteConfigInput = {\n baseUrl?: string\n }\n export type Params<TRoute0 extends AnyRoute> = {\n [K in keyof TRoute0['paramsDefinition']]: string\n }\n export type Query<TRoute0 extends AnyRoute> = Partial<\n {\n [K in keyof TRoute0['queryDefinition']]: string | undefined\n } & Record<string, string | undefined>\n >\n\n export type _TrimQueryTailDefinition<S extends string> = S extends `${infer P}&${string}` ? P : S\n export type _QueryTailDefinitionWithoutFirstAmp<S extends string> = S extends `${string}&${infer T}` ? T : ''\n export type _QueryTailDefinitionWithFirstAmp<S extends string> = S extends `${string}&${infer T}` ? `&${T}` : ''\n export type _AmpSplit<S extends string> = S extends `${infer A}&${infer B}` ? A | _AmpSplit<B> : S\n // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\n export type _NonEmpty<T> = [T] extends ['' | never] ? never : T\n export type _ExtractPathParams<S extends string> = S extends `${string}:${infer After}`\n ? After extends `${infer Name}/${infer Rest}`\n ? Name | _ExtractPathParams<`/${Rest}`>\n : After\n : never\n export type _ReplacePathParams<S extends string> = S extends `${infer Head}:${infer Tail}`\n ? // eslint-disable-next-line @typescript-eslint/no-unused-vars\n Tail extends `${infer _Param}/${infer Rest}`\n ? _ReplacePathParams<`${Head}${string}/${Rest}`>\n : `${Head}${string}`\n : S\n export type _DedupeSlashes<S extends string> = S extends `${infer A}//${infer B}` ? _DedupeSlashes<`${A}/${B}`> : S\n export type _EmptyRecord = Record<never, never>\n export type _JoinPath<Parent extends string, Suffix extends string> = _DedupeSlashes<\n _PathDefinition<Parent> extends infer A extends string\n ? _PathDefinition<Suffix> extends infer B extends string\n ? A extends ''\n ? B extends ''\n ? ''\n : B extends `/${string}`\n ? B\n : `/${B}`\n : B extends ''\n ? A\n : A extends `${string}/`\n ? `${A}${B}`\n : B extends `/${string}`\n ? `${A}${B}`\n : `${A}/${B}`\n : never\n : never\n >\n\n export type _OnlyIfNoParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? Yes : No\n export type _OnlyIfHasParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? No : Yes\n\n export type _PathDefinition<TPathOriginalDefinition extends string> =\n _TrimQueryTailDefinition<TPathOriginalDefinition>\n export type _ParamsDefinition<TPathOriginalDefinition extends string> =\n _ExtractPathParams<_PathDefinition<TPathOriginalDefinition>> extends infer U\n ? [U] extends [never]\n ? _EmptyRecord\n : { [K in Extract<U, string>]: true }\n : _EmptyRecord\n export type _QueryDefinition<TPathOriginalDefinition extends string> =\n _NonEmpty<_QueryTailDefinitionWithoutFirstAmp<TPathOriginalDefinition>> extends infer Tail extends string\n ? _AmpSplit<Tail> extends infer U\n ? [U] extends [never]\n ? _EmptyRecord\n : { [K in Extract<U, string>]: true }\n : _EmptyRecord\n : _EmptyRecord\n export type _RoutePathOriginalDefinitionExtended<\n TSourcePathOriginalDefinition extends string,\n TSuffixPathOriginalDefinition extends string,\n > = `${_JoinPath<TSourcePathOriginalDefinition, TSuffixPathOriginalDefinition>}${_QueryTailDefinitionWithFirstAmp<TSuffixPathOriginalDefinition>}`\n\n export type _ParamsInput<TParamsDefinition extends object> = {\n [K in keyof TParamsDefinition]: string | number\n }\n export type _QueryInput<TQueryDefinition extends object> = Partial<{\n [K in keyof TQueryDefinition]: string | number\n }> &\n Record<string, string | number>\n export type _WithParamsInput<\n TParamsDefinition extends object,\n T extends {\n query?: _QueryInput<any>\n abs?: boolean\n },\n > = _ParamsInput<TParamsDefinition> & T\n\n export type _PathOnlyRouteValue<TPathOriginalDefinition extends string> =\n `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}`\n export type _WithQueryRouteValue<TPathOriginalDefinition extends string> =\n `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}?${string}`\n export type _AbsolutePathOnlyRouteValue<TPathOriginalDefinition extends string> =\n `${string}${_PathOnlyRouteValue<TPathOriginalDefinition>}`\n export type _AbsoluteWithQueryRouteValue<TPathOriginalDefinition extends string> =\n `${string}${_WithQueryRouteValue<TPathOriginalDefinition>}`\n\n export type _LocationParams<TParamsDefinition extends object> = {\n [K in keyof TParamsDefinition]: string\n }\n export type _LocationQuery<TQueryDefinition extends object> = {\n [K in keyof TQueryDefinition]: string | undefined\n } & Record<string, string | undefined>\n export type Location<TRoute0 extends AnyRoute = AnyRoute> = {\n query: _LocationQuery<TRoute0['queryDefinition']>\n params: _LocationParams<TRoute0['paramsDefinition']>\n pathname: string\n search: string\n hash: string\n origin?: string\n href: string\n abs: boolean\n }\n export type MatchResult<TRoute0 extends AnyRoute, TExact extends boolean = boolean> = TExact extends true\n ? {\n exact: true\n parent: false\n children: false\n location: Location<TRoute0>\n }\n : TExact extends false\n ? {\n exact: false\n parent: boolean\n children: boolean\n location: Location\n }\n : never\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts"],"sourcesContent":["// TODO: getLocation by windows location\n// TODO: избавиться от жутких эни в роуте\n// TODO: .extension('.json') to not add additional / but just add some extension\n// TODO: query input can be boolean, or even object with qs\n// TODO: Роут0 три пусть три тоже сам генерится вероятно\n// TODO: Роут0 три мод, тогда там все ноуты кончаются на .селф\n// TODO: use splats in param definition \"*\"\n// TODO: ? check extend for query only .extend('&x&z')\n// TODO: .create(route, {useQuery, useParams})\n// TODO: Из пас экзакт, из пасвизквери экзает, из чилдрен, из парент, из экзактОр\n// TODO: isEqual, isChildren, isParent\n// TODO: extractParams, extractQuery\n// TODO: getPathDefinition respecting definitionParamPrefix, definitionQueryPrefix\n// TODO: prepend\n// TODO: Route0.createTree({base:{self: x, children: ...})\n// TODO: overrideTree\n// TODO: .create(route, {baseUrl, useLocation})\n// TODO: ? optional path params as @\n// TODO: prependMany, extendMany, overrideMany, with types\n// TODO: optional route params /x/:id?\n\n// point0\n// TODO: Сделать чисто фронтовую штуку, которая сама вызывает лоадер, сама вызывает нужные мета и title, и отдаёт в компонент нужные штуки\n\n// ssr0\n// TODO: ССР работает просто поверх любого роутера, который поддерживает асинхронную загрузку страниц\n\nexport class Route0<\n TPathOriginalDefinition extends string,\n TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,\n TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,\n TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,\n> {\n pathOriginalDefinition: TPathOriginalDefinition\n private readonly pathDefinition: TPathDefinition\n paramsDefinition: TParamsDefinition\n queryDefinition: TQueryDefinition\n baseUrl: string\n\n private constructor(definition: TPathOriginalDefinition, config: Route0.RouteConfigInput = {}) {\n this.pathOriginalDefinition = definition\n this.pathDefinition = Route0._getPathDefinitionByOriginalDefinition(definition) as TPathDefinition\n this.paramsDefinition = Route0._getParamsDefinitionByRouteDefinition(definition) as TParamsDefinition\n this.queryDefinition = Route0._getQueryDefinitionByRouteDefinition(definition) as TQueryDefinition\n\n const { baseUrl } = config\n if (baseUrl && typeof baseUrl === 'string' && baseUrl.length) {\n this.baseUrl = baseUrl\n } else {\n const g = globalThis as unknown as { location?: { origin?: string } }\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (g?.location?.origin) {\n this.baseUrl = g.location.origin\n } else {\n this.baseUrl = 'https://example.com'\n }\n }\n }\n\n static create<\n TPathOriginalDefinition extends string,\n TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,\n TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,\n TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,\n >(\n definition: TPathOriginalDefinition,\n config?: Route0.RouteConfigInput,\n ): Route0.Callable<Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>> {\n const original = new Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>(\n definition,\n config,\n )\n const callable = original.get.bind(original)\n const proxy = new Proxy(callable, {\n get(_target, prop, receiver) {\n const value = (original as any)[prop]\n if (typeof value === 'function') {\n return value.bind(original)\n }\n return value\n },\n set(_target, prop, value, receiver) {\n ;(original as any)[prop] = value\n return true\n },\n has(_target, prop) {\n return prop in original\n },\n })\n Object.setPrototypeOf(proxy, Route0.prototype)\n return proxy as never\n }\n\n private static _splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition: string) {\n const i = pathOriginalDefinition.indexOf('&')\n if (i === -1) return { pathDefinition: pathOriginalDefinition, queryTailDefinition: '' }\n return {\n pathDefinition: pathOriginalDefinition.slice(0, i),\n queryTailDefinition: pathOriginalDefinition.slice(i),\n }\n }\n\n private static _getAbsPath(baseUrl: string, pathWithQuery: string) {\n return new URL(pathWithQuery, baseUrl).toString().replace(/\\/$/, '')\n }\n\n private static _getPathDefinitionByOriginalDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n return pathDefinition as Route0._PathDefinition<TPathOriginalDefinition>\n }\n\n private static _getParamsDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n const matches = Array.from(pathDefinition.matchAll(/:([A-Za-z0-9_]+)/g))\n const paramsDefinition = Object.fromEntries(matches.map((m) => [m[1], true]))\n return paramsDefinition as Route0._ParamsDefinition<TPathOriginalDefinition>\n }\n\n private static _getQueryDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { queryTailDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n if (!queryTailDefinition) {\n return {} as Route0._QueryDefinition<TPathOriginalDefinition>\n }\n const keys = queryTailDefinition.split('&').map(Boolean)\n const queryDefinition = Object.fromEntries(keys.map((k) => [k, true]))\n return queryDefinition as Route0._QueryDefinition<TPathOriginalDefinition>\n }\n\n static overrideMany<T extends Record<string, Route0.AnyRoute>>(routes: T, config: Route0.RouteConfigInput): T {\n const result = {} as T\n for (const [key, value] of Object.entries(routes)) {\n ;(result as any)[key] = value.clone(config)\n }\n return result\n }\n\n extend<TSuffixDefinition extends string>(\n suffixDefinition: TSuffixDefinition,\n ): Route0.Callable<\n Route0<\n Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,\n Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>\n >\n > {\n const { pathDefinition: parentPathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(\n this.pathOriginalDefinition,\n )\n const { pathDefinition: suffixPathDefinition, queryTailDefinition: suffixQueryTailDefinition } =\n Route0._splitPathDefinitionAndQueryTailDefinition(suffixDefinition)\n const pathDefinition = `${parentPathDefinition}/${suffixPathDefinition}`.replace(/\\/{2,}/g, '/')\n const pathOriginalDefinition =\n `${pathDefinition}${suffixQueryTailDefinition}` as Route0._RoutePathOriginalDefinitionExtended<\n TPathOriginalDefinition,\n TSuffixDefinition\n >\n return Route0.create<\n Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,\n Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>\n >(pathOriginalDefinition, { baseUrl: this.baseUrl })\n }\n\n // has params\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs?: false }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs: true }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>\n\n // no params\n get(\n ...args: Route0._OnlyIfNoParams<TParamsDefinition, [], [never]>\n ): Route0._PathOnlyRouteValue<TPathOriginalDefinition>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs?: false }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs: true }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>\n\n // implementation\n get(...args: any[]): string {\n const { queryInput, paramsInput, absInput } = ((): {\n queryInput: Record<string, string | number>\n paramsInput: Record<string, string | number>\n absInput: boolean\n } => {\n if (args.length === 0) {\n return { queryInput: {}, paramsInput: {}, absInput: false }\n }\n const input = args[0]\n if (typeof input !== 'object' || input === null) {\n // throw new Error(\"Invalid get route input: expected object\")\n return { queryInput: {}, paramsInput: {}, absInput: false }\n }\n const { query, abs, ...params } = input\n return { queryInput: query || {}, paramsInput: params, absInput: abs ?? false }\n })()\n\n // validate params\n const neededParamsKeys = Object.keys(this.paramsDefinition)\n const providedParamsKeys = Object.keys(paramsInput)\n const notProvidedKeys = neededParamsKeys.filter((k) => !providedParamsKeys.includes(k))\n if (notProvidedKeys.length) {\n // throw new Error(`Missing params: not defined keys ${notProvidedKeys.map((k) => `\"${k}\"`).join(\", \")}.`)\n Object.assign(paramsInput, Object.fromEntries(notProvidedKeys.map((k) => [k, 'undefined'])))\n }\n\n // create url\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion\n let url = String(this.pathDefinition)\n // replace params\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n url = url.replace(/:([A-Za-z0-9_]+)/g, (_m, k) => encodeURIComponent(String(paramsInput?.[k] ?? '')))\n // query params\n const queryInputStringified = Object.fromEntries(Object.entries(queryInput).map(([k, v]) => [k, String(v)]))\n url = [url, new URLSearchParams(queryInputStringified).toString()].filter(Boolean).join('?')\n // dedupe slashes\n url = url.replace(/\\/{2,}/g, '/')\n // absolute\n url = absInput ? Route0._getAbsPath(this.baseUrl, url) : url\n\n return url\n }\n\n getDefinition() {\n return this.pathDefinition\n }\n\n clone(config?: Route0.RouteConfigInput) {\n return new Route0(this.pathOriginalDefinition, config)\n }\n\n static getLocation(url: `${string}://${string}`): Route0.Location\n static getLocation(path: `/${string}`): Route0.Location\n static getLocation(urlOrPath: string): Route0.Location\n static getLocation(urlOrPath: string): Route0.Location {\n // Allow both relative and absolute inputs\n const abs = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:\\/\\//.test(urlOrPath)\n const base = abs ? undefined : 'http://example.com' // dummy base for relative URLs\n\n const url = new URL(urlOrPath, base)\n\n // Extract query and params (params left empty for now — parsed separately)\n const query = Object.fromEntries(url.searchParams.entries())\n\n // Normalize pathname (remove trailing slash except for root)\n let pathname = url.pathname\n if (pathname.length > 1 && pathname.endsWith('/')) {\n pathname = pathname.slice(0, -1)\n }\n\n // Build the location object similar to browser location\n return {\n query,\n params: {}, // filled later by .parse()\n pathname,\n search: url.search,\n hash: url.hash,\n origin: abs ? url.origin : undefined,\n href: abs ? url.href : pathname + url.search + url.hash,\n abs,\n }\n }\n\n static getMatch<TRoute0 extends Route0.AnyRoute>(\n route0: TRoute0,\n location: Route0.Location,\n ): Route0.MatchResult<TRoute0> {\n const locationCloned = { ...location, params: { ...location.params }, query: { ...location.query } }\n const escapeRegex = (s: string) => s.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&')\n\n // Normalize both sides (no trailing slash except root)\n const def =\n route0.pathDefinition.length > 1 && route0.pathDefinition.endsWith('/')\n ? route0.pathDefinition.slice(0, -1)\n : route0.pathDefinition\n const pathname =\n locationCloned.pathname.length > 1 && locationCloned.pathname.endsWith('/')\n ? locationCloned.pathname.slice(0, -1)\n : locationCloned.pathname\n\n // Build a regex from the route definition: /a/:x/b/:y -> ^/a/([^/]+)/b/([^/]+)$\n const paramNames: string[] = []\n const pattern = def.replace(/:([A-Za-z0-9_]+)/g, (_m: string, name: string) => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion\n paramNames.push(String(name))\n return '([^/]+)'\n })\n\n const exactRe = new RegExp(`^${pattern}$`)\n const parentRe = new RegExp(`^${pattern}(?:/.*)?$`) // route matches the beginning of the URL (may have more)\n const exactMatch = pathname.match(exactRe)\n\n // Fill params only for exact match (keeps behavior predictable)\n if (exactMatch) {\n const values = exactMatch.slice(1)\n const params = Object.fromEntries(paramNames.map((n, i) => [n, decodeURIComponent(values[i] ?? '')]))\n locationCloned.params = params\n } else {\n locationCloned.params = {}\n }\n\n const exact = !!exactMatch\n const parent = !exact && parentRe.test(pathname)\n\n // \"children\": the URL is a prefix of the route definition (ignoring params’ concrete values)\n // We check if the definition starts with the URL path boundary-wise.\n const children = !exact && new RegExp(`^${escapeRegex(pathname)}(?:/|$)`).test(def)\n\n return {\n exact,\n parent,\n children,\n location: locationCloned,\n } as never\n }\n match(url: string): Route0.MatchResult<this> {\n const location = Route0.getLocation(url)\n return Route0.getMatch(this, location)\n }\n}\n\nexport namespace Route0 {\n export type AnyRoute = Route0<any, any, any, any>\n export type Callable<T extends AnyRoute> = T & T['get']\n export type RouteConfigInput = {\n baseUrl?: string\n }\n export type Params<TRoute0 extends AnyRoute> = {\n [K in keyof TRoute0['paramsDefinition']]: string\n }\n export type Query<TRoute0 extends AnyRoute> = Partial<\n {\n [K in keyof TRoute0['queryDefinition']]: string | undefined\n } & Record<string, string | undefined>\n >\n\n export type _TrimQueryTailDefinition<S extends string> = S extends `${infer P}&${string}` ? P : S\n export type _QueryTailDefinitionWithoutFirstAmp<S extends string> = S extends `${string}&${infer T}` ? T : ''\n export type _QueryTailDefinitionWithFirstAmp<S extends string> = S extends `${string}&${infer T}` ? `&${T}` : ''\n export type _AmpSplit<S extends string> = S extends `${infer A}&${infer B}` ? A | _AmpSplit<B> : S\n // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\n export type _NonEmpty<T> = [T] extends ['' | never] ? never : T\n export type _ExtractPathParams<S extends string> = S extends `${string}:${infer After}`\n ? After extends `${infer Name}/${infer Rest}`\n ? Name | _ExtractPathParams<`/${Rest}`>\n : After\n : never\n export type _ReplacePathParams<S extends string> = S extends `${infer Head}:${infer Tail}`\n ? // eslint-disable-next-line @typescript-eslint/no-unused-vars\n Tail extends `${infer _Param}/${infer Rest}`\n ? _ReplacePathParams<`${Head}${string}/${Rest}`>\n : `${Head}${string}`\n : S\n export type _DedupeSlashes<S extends string> = S extends `${infer A}//${infer B}` ? _DedupeSlashes<`${A}/${B}`> : S\n export type _EmptyRecord = Record<never, never>\n export type _JoinPath<Parent extends string, Suffix extends string> = _DedupeSlashes<\n _PathDefinition<Parent> extends infer A extends string\n ? _PathDefinition<Suffix> extends infer B extends string\n ? A extends ''\n ? B extends ''\n ? ''\n : B extends `/${string}`\n ? B\n : `/${B}`\n : B extends ''\n ? A\n : A extends `${string}/`\n ? `${A}${B}`\n : B extends `/${string}`\n ? `${A}${B}`\n : `${A}/${B}`\n : never\n : never\n >\n\n export type _OnlyIfNoParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? Yes : No\n export type _OnlyIfHasParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? No : Yes\n\n export type _PathDefinition<TPathOriginalDefinition extends string> =\n _TrimQueryTailDefinition<TPathOriginalDefinition>\n export type _ParamsDefinition<TPathOriginalDefinition extends string> =\n _ExtractPathParams<_PathDefinition<TPathOriginalDefinition>> extends infer U\n ? [U] extends [never]\n ? _EmptyRecord\n : { [K in Extract<U, string>]: true }\n : _EmptyRecord\n export type _QueryDefinition<TPathOriginalDefinition extends string> =\n _NonEmpty<_QueryTailDefinitionWithoutFirstAmp<TPathOriginalDefinition>> extends infer Tail extends string\n ? _AmpSplit<Tail> extends infer U\n ? [U] extends [never]\n ? _EmptyRecord\n : { [K in Extract<U, string>]: true }\n : _EmptyRecord\n : _EmptyRecord\n export type _RoutePathOriginalDefinitionExtended<\n TSourcePathOriginalDefinition extends string,\n TSuffixPathOriginalDefinition extends string,\n > = `${_JoinPath<TSourcePathOriginalDefinition, TSuffixPathOriginalDefinition>}${_QueryTailDefinitionWithFirstAmp<TSuffixPathOriginalDefinition>}`\n\n export type _ParamsInput<TParamsDefinition extends object> = {\n [K in keyof TParamsDefinition]: string | number\n }\n export type _QueryInput<TQueryDefinition extends object> = Partial<{\n [K in keyof TQueryDefinition]: string | number\n }> &\n Record<string, string | number>\n export type _WithParamsInput<\n TParamsDefinition extends object,\n T extends {\n query?: _QueryInput<any>\n abs?: boolean\n },\n > = _ParamsInput<TParamsDefinition> & T\n\n export type _PathOnlyRouteValue<TPathOriginalDefinition extends string> =\n `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}`\n export type _WithQueryRouteValue<TPathOriginalDefinition extends string> =\n `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}?${string}`\n export type _AbsolutePathOnlyRouteValue<TPathOriginalDefinition extends string> =\n `${string}${_PathOnlyRouteValue<TPathOriginalDefinition>}`\n export type _AbsoluteWithQueryRouteValue<TPathOriginalDefinition extends string> =\n `${string}${_WithQueryRouteValue<TPathOriginalDefinition>}`\n\n export type _LocationParams<TParamsDefinition extends object> = {\n [K in keyof TParamsDefinition]: string\n }\n export type _LocationQuery<TQueryDefinition extends object> = {\n [K in keyof TQueryDefinition]: string | undefined\n } & Record<string, string | undefined>\n export type Location<TRoute0 extends AnyRoute = AnyRoute> = {\n query: _LocationQuery<TRoute0['queryDefinition']>\n params: _LocationParams<TRoute0['paramsDefinition']>\n pathname: string\n search: string\n hash: string\n origin?: string\n href: string\n abs: boolean\n }\n export type MatchResult<TRoute0 extends AnyRoute, TExact extends boolean = boolean> = TExact extends true\n ? {\n exact: true\n parent: false\n children: false\n location: Location<TRoute0>\n }\n : TExact extends false\n ? {\n exact: false\n parent: boolean\n children: boolean\n location: Location\n }\n : never\n export type HasParams<TRoute0 extends AnyRoute> =\n _ExtractPathParams<_PathDefinition<TRoute0['pathOriginalDefinition']>> extends infer U\n ? [U] extends [never]\n ? false\n : true\n : false\n}\n"],"mappings":"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","names":[]}
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -127,6 +127,7 @@ declare namespace Route0 {
|
|
|
127
127
|
children: boolean;
|
|
128
128
|
location: Location;
|
|
129
129
|
} : never;
|
|
130
|
+
type HasParams<TRoute0 extends AnyRoute> = _ExtractPathParams<_PathDefinition<TRoute0['pathOriginalDefinition']>> extends infer U ? [U] extends [never] ? false : true : false;
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
export { Route0 };
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -127,6 +127,7 @@ declare namespace Route0 {
|
|
|
127
127
|
children: boolean;
|
|
128
128
|
location: Location;
|
|
129
129
|
} : never;
|
|
130
|
+
type HasParams<TRoute0 extends AnyRoute> = _ExtractPathParams<_PathDefinition<TRoute0['pathOriginalDefinition']>> extends infer U ? [U] extends [never] ? false : true : false;
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
export { Route0 };
|
package/dist/esm/index.js
CHANGED
|
@@ -149,9 +149,10 @@ class Route0 {
|
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
151
|
static getMatch(route0, location) {
|
|
152
|
+
const locationCloned = { ...location, params: { ...location.params }, query: { ...location.query } };
|
|
152
153
|
const escapeRegex = (s) => s.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
153
154
|
const def = route0.pathDefinition.length > 1 && route0.pathDefinition.endsWith("/") ? route0.pathDefinition.slice(0, -1) : route0.pathDefinition;
|
|
154
|
-
const pathname =
|
|
155
|
+
const pathname = locationCloned.pathname.length > 1 && locationCloned.pathname.endsWith("/") ? locationCloned.pathname.slice(0, -1) : locationCloned.pathname;
|
|
155
156
|
const paramNames = [];
|
|
156
157
|
const pattern = def.replace(/:([A-Za-z0-9_]+)/g, (_m, name) => {
|
|
157
158
|
paramNames.push(String(name));
|
|
@@ -163,10 +164,9 @@ class Route0 {
|
|
|
163
164
|
if (exactMatch) {
|
|
164
165
|
const values = exactMatch.slice(1);
|
|
165
166
|
const params = Object.fromEntries(paramNames.map((n, i) => [n, decodeURIComponent(values[i] ?? "")]));
|
|
166
|
-
|
|
167
|
+
locationCloned.params = params;
|
|
167
168
|
} else {
|
|
168
|
-
;
|
|
169
|
-
location.params = {};
|
|
169
|
+
locationCloned.params = {};
|
|
170
170
|
}
|
|
171
171
|
const exact = !!exactMatch;
|
|
172
172
|
const parent = !exact && parentRe.test(pathname);
|
|
@@ -175,7 +175,7 @@ class Route0 {
|
|
|
175
175
|
exact,
|
|
176
176
|
parent,
|
|
177
177
|
children,
|
|
178
|
-
location
|
|
178
|
+
location: locationCloned
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
181
|
match(url) {
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts"],"sourcesContent":["// TODO: query input can be boolean, or even object with qs\n// TODO: Роут0 три пусть три тоже сам генерится вероятно\n// TODO: Роут0 три мод, тогда там все ноуты кончаются на .селф\n// TODO: use splats in param definition \"*\"\n// TODO: ? check extend for query only .extend('&x&z')\n// TODO: .create(route, {useQuery, useParams})\n// TODO: Из пас экзакт, из пасвизквери экзает, из чилдрен, из парент, из экзактОр\n// TODO: isEqual, isChildren, isParent\n// TODO: extractParams, extractQuery\n// TODO: getPathDefinition respecting definitionParamPrefix, definitionQueryPrefix\n// TODO: prepend\n// TODO: Route0.createTree({base:{self: x, children: ...})\n// TODO: overrideTree\n// TODO: .create(route, {baseUrl, useLocation})\n// TODO: ? optional path params as @\n// TODO: prependMany, extendMany, overrideMany, with types\n\n// page0\n// TODO: Сделать чисто фронтовую штуку, которая сама вызывает лоадер, сама вызывает нужные мета и title, и отдаёт в компонент нужные штуки\n\n// ssr0\n// TODO: ССР работает просто поверх любого роутера, который поддерживает асинхронную загрузку страниц\n\nexport class Route0<\n TPathOriginalDefinition extends string,\n TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,\n TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,\n TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,\n> {\n pathOriginalDefinition: TPathOriginalDefinition\n private readonly pathDefinition: TPathDefinition\n paramsDefinition: TParamsDefinition\n queryDefinition: TQueryDefinition\n baseUrl: string\n\n private constructor(definition: TPathOriginalDefinition, config: Route0.RouteConfigInput = {}) {\n this.pathOriginalDefinition = definition\n this.pathDefinition = Route0._getPathDefinitionByOriginalDefinition(definition) as TPathDefinition\n this.paramsDefinition = Route0._getParamsDefinitionByRouteDefinition(definition) as TParamsDefinition\n this.queryDefinition = Route0._getQueryDefinitionByRouteDefinition(definition) as TQueryDefinition\n\n const { baseUrl } = config\n if (baseUrl && typeof baseUrl === 'string' && baseUrl.length) {\n this.baseUrl = baseUrl\n } else {\n const g = globalThis as unknown as { location?: { origin?: string } }\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (g?.location?.origin) {\n this.baseUrl = g.location.origin\n } else {\n this.baseUrl = 'https://example.com'\n }\n }\n }\n\n static create<\n TPathOriginalDefinition extends string,\n TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,\n TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,\n TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,\n >(\n definition: TPathOriginalDefinition,\n config?: Route0.RouteConfigInput,\n ): Route0.Callable<Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>> {\n const original = new Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>(\n definition,\n config,\n )\n const callable = original.get.bind(original)\n const proxy = new Proxy(callable, {\n get(_target, prop, receiver) {\n const value = (original as any)[prop]\n if (typeof value === 'function') {\n return value.bind(original)\n }\n return value\n },\n set(_target, prop, value, receiver) {\n ;(original as any)[prop] = value\n return true\n },\n has(_target, prop) {\n return prop in original\n },\n })\n Object.setPrototypeOf(proxy, Route0.prototype)\n return proxy as never\n }\n\n private static _splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition: string) {\n const i = pathOriginalDefinition.indexOf('&')\n if (i === -1) return { pathDefinition: pathOriginalDefinition, queryTailDefinition: '' }\n return {\n pathDefinition: pathOriginalDefinition.slice(0, i),\n queryTailDefinition: pathOriginalDefinition.slice(i),\n }\n }\n\n private static _getAbsPath(baseUrl: string, pathWithQuery: string) {\n return new URL(pathWithQuery, baseUrl).toString().replace(/\\/$/, '')\n }\n\n private static _getPathDefinitionByOriginalDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n return pathDefinition as Route0._PathDefinition<TPathOriginalDefinition>\n }\n\n private static _getParamsDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n const matches = Array.from(pathDefinition.matchAll(/:([A-Za-z0-9_]+)/g))\n const paramsDefinition = Object.fromEntries(matches.map((m) => [m[1], true]))\n return paramsDefinition as Route0._ParamsDefinition<TPathOriginalDefinition>\n }\n\n private static _getQueryDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { queryTailDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n if (!queryTailDefinition) {\n return {} as Route0._QueryDefinition<TPathOriginalDefinition>\n }\n const keys = queryTailDefinition.split('&').map(Boolean)\n const queryDefinition = Object.fromEntries(keys.map((k) => [k, true]))\n return queryDefinition as Route0._QueryDefinition<TPathOriginalDefinition>\n }\n\n static overrideMany<T extends Record<string, Route0.AnyRoute>>(routes: T, config: Route0.RouteConfigInput): T {\n const result = {} as T\n for (const [key, value] of Object.entries(routes)) {\n ;(result as any)[key] = value.clone(config)\n }\n return result\n }\n\n extend<TSuffixDefinition extends string>(\n suffixDefinition: TSuffixDefinition,\n ): Route0.Callable<\n Route0<\n Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,\n Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>\n >\n > {\n const { pathDefinition: parentPathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(\n this.pathOriginalDefinition,\n )\n const { pathDefinition: suffixPathDefinition, queryTailDefinition: suffixQueryTailDefinition } =\n Route0._splitPathDefinitionAndQueryTailDefinition(suffixDefinition)\n const pathDefinition = `${parentPathDefinition}/${suffixPathDefinition}`.replace(/\\/{2,}/g, '/')\n const pathOriginalDefinition =\n `${pathDefinition}${suffixQueryTailDefinition}` as Route0._RoutePathOriginalDefinitionExtended<\n TPathOriginalDefinition,\n TSuffixDefinition\n >\n return Route0.create<\n Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,\n Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>\n >(pathOriginalDefinition, { baseUrl: this.baseUrl })\n }\n\n // has params\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs?: false }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs: true }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>\n\n // no params\n get(\n ...args: Route0._OnlyIfNoParams<TParamsDefinition, [], [never]>\n ): Route0._PathOnlyRouteValue<TPathOriginalDefinition>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs?: false }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs: true }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>\n\n // implementation\n get(...args: any[]): string {\n const { queryInput, paramsInput, absInput } = ((): {\n queryInput: Record<string, string | number>\n paramsInput: Record<string, string | number>\n absInput: boolean\n } => {\n if (args.length === 0) {\n return { queryInput: {}, paramsInput: {}, absInput: false }\n }\n const input = args[0]\n if (typeof input !== 'object' || input === null) {\n // throw new Error(\"Invalid get route input: expected object\")\n return { queryInput: {}, paramsInput: {}, absInput: false }\n }\n const { query, abs, ...params } = input\n return { queryInput: query || {}, paramsInput: params, absInput: abs ?? false }\n })()\n\n // validate params\n const neededParamsKeys = Object.keys(this.paramsDefinition)\n const providedParamsKeys = Object.keys(paramsInput)\n const notProvidedKeys = neededParamsKeys.filter((k) => !providedParamsKeys.includes(k))\n if (notProvidedKeys.length) {\n // throw new Error(`Missing params: not defined keys ${notProvidedKeys.map((k) => `\"${k}\"`).join(\", \")}.`)\n Object.assign(paramsInput, Object.fromEntries(notProvidedKeys.map((k) => [k, 'undefined'])))\n }\n\n // create url\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion\n let url = String(this.pathDefinition)\n // replace params\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n url = url.replace(/:([A-Za-z0-9_]+)/g, (_m, k) => encodeURIComponent(String(paramsInput?.[k] ?? '')))\n // query params\n const queryInputStringified = Object.fromEntries(Object.entries(queryInput).map(([k, v]) => [k, String(v)]))\n url = [url, new URLSearchParams(queryInputStringified).toString()].filter(Boolean).join('?')\n // dedupe slashes\n url = url.replace(/\\/{2,}/g, '/')\n // absolute\n url = absInput ? Route0._getAbsPath(this.baseUrl, url) : url\n\n return url\n }\n\n getDefinition() {\n return this.pathDefinition\n }\n\n clone(config?: Route0.RouteConfigInput) {\n return new Route0(this.pathOriginalDefinition, config)\n }\n\n static getLocation(url: `${string}://${string}`): Route0.Location\n static getLocation(path: `/${string}`): Route0.Location\n static getLocation(urlOrPath: string): Route0.Location\n static getLocation(urlOrPath: string): Route0.Location {\n // Allow both relative and absolute inputs\n const abs = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:\\/\\//.test(urlOrPath)\n const base = abs ? undefined : 'http://example.com' // dummy base for relative URLs\n\n const url = new URL(urlOrPath, base)\n\n // Extract query and params (params left empty for now — parsed separately)\n const query = Object.fromEntries(url.searchParams.entries())\n\n // Normalize pathname (remove trailing slash except for root)\n let pathname = url.pathname\n if (pathname.length > 1 && pathname.endsWith('/')) {\n pathname = pathname.slice(0, -1)\n }\n\n // Build the location object similar to browser location\n return {\n query,\n params: {}, // filled later by .parse()\n pathname,\n search: url.search,\n hash: url.hash,\n origin: abs ? url.origin : undefined,\n href: abs ? url.href : pathname + url.search + url.hash,\n abs,\n }\n }\n\n static getMatch<TRoute0 extends Route0.AnyRoute>(\n route0: TRoute0,\n location: Route0.Location,\n ): Route0.MatchResult<TRoute0> {\n const escapeRegex = (s: string) => s.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&')\n\n // Normalize both sides (no trailing slash except root)\n const def =\n route0.pathDefinition.length > 1 && route0.pathDefinition.endsWith('/')\n ? route0.pathDefinition.slice(0, -1)\n : route0.pathDefinition\n const pathname =\n location.pathname.length > 1 && location.pathname.endsWith('/')\n ? location.pathname.slice(0, -1)\n : location.pathname\n\n // Build a regex from the route definition: /a/:x/b/:y -> ^/a/([^/]+)/b/([^/]+)$\n const paramNames: string[] = []\n const pattern = def.replace(/:([A-Za-z0-9_]+)/g, (_m: string, name: string) => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion\n paramNames.push(String(name))\n return '([^/]+)'\n })\n\n const exactRe = new RegExp(`^${pattern}$`)\n const parentRe = new RegExp(`^${pattern}(?:/.*)?$`) // route matches the beginning of the URL (may have more)\n const exactMatch = pathname.match(exactRe)\n\n // Fill params only for exact match (keeps behavior predictable)\n if (exactMatch) {\n const values = exactMatch.slice(1)\n const params = Object.fromEntries(paramNames.map((n, i) => [n, decodeURIComponent(values[i] ?? '')]))\n ;(location as any).params = params\n } else {\n ;(location as any).params = {}\n }\n\n const exact = !!exactMatch\n const parent = !exact && parentRe.test(pathname)\n\n // \"children\": the URL is a prefix of the route definition (ignoring params’ concrete values)\n // We check if the definition starts with the URL path boundary-wise.\n const children = !exact && new RegExp(`^${escapeRegex(pathname)}(?:/|$)`).test(def)\n\n return {\n exact,\n parent,\n children,\n location,\n } as never\n }\n match(url: string): Route0.MatchResult<this> {\n const location = Route0.getLocation(url)\n return Route0.getMatch(this, location)\n }\n}\n\nexport namespace Route0 {\n export type AnyRoute = Route0<any, any, any, any>\n export type Callable<T extends AnyRoute> = T & T['get']\n export type RouteConfigInput = {\n baseUrl?: string\n }\n export type Params<TRoute0 extends AnyRoute> = {\n [K in keyof TRoute0['paramsDefinition']]: string\n }\n export type Query<TRoute0 extends AnyRoute> = Partial<\n {\n [K in keyof TRoute0['queryDefinition']]: string | undefined\n } & Record<string, string | undefined>\n >\n\n export type _TrimQueryTailDefinition<S extends string> = S extends `${infer P}&${string}` ? P : S\n export type _QueryTailDefinitionWithoutFirstAmp<S extends string> = S extends `${string}&${infer T}` ? T : ''\n export type _QueryTailDefinitionWithFirstAmp<S extends string> = S extends `${string}&${infer T}` ? `&${T}` : ''\n export type _AmpSplit<S extends string> = S extends `${infer A}&${infer B}` ? A | _AmpSplit<B> : S\n // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\n export type _NonEmpty<T> = [T] extends ['' | never] ? never : T\n export type _ExtractPathParams<S extends string> = S extends `${string}:${infer After}`\n ? After extends `${infer Name}/${infer Rest}`\n ? Name | _ExtractPathParams<`/${Rest}`>\n : After\n : never\n export type _ReplacePathParams<S extends string> = S extends `${infer Head}:${infer Tail}`\n ? // eslint-disable-next-line @typescript-eslint/no-unused-vars\n Tail extends `${infer _Param}/${infer Rest}`\n ? _ReplacePathParams<`${Head}${string}/${Rest}`>\n : `${Head}${string}`\n : S\n export type _DedupeSlashes<S extends string> = S extends `${infer A}//${infer B}` ? _DedupeSlashes<`${A}/${B}`> : S\n export type _EmptyRecord = Record<never, never>\n export type _JoinPath<Parent extends string, Suffix extends string> = _DedupeSlashes<\n _PathDefinition<Parent> extends infer A extends string\n ? _PathDefinition<Suffix> extends infer B extends string\n ? A extends ''\n ? B extends ''\n ? ''\n : B extends `/${string}`\n ? B\n : `/${B}`\n : B extends ''\n ? A\n : A extends `${string}/`\n ? `${A}${B}`\n : B extends `/${string}`\n ? `${A}${B}`\n : `${A}/${B}`\n : never\n : never\n >\n\n export type _OnlyIfNoParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? Yes : No\n export type _OnlyIfHasParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? No : Yes\n\n export type _PathDefinition<TPathOriginalDefinition extends string> =\n _TrimQueryTailDefinition<TPathOriginalDefinition>\n export type _ParamsDefinition<TPathOriginalDefinition extends string> =\n _ExtractPathParams<_PathDefinition<TPathOriginalDefinition>> extends infer U\n ? [U] extends [never]\n ? _EmptyRecord\n : { [K in Extract<U, string>]: true }\n : _EmptyRecord\n export type _QueryDefinition<TPathOriginalDefinition extends string> =\n _NonEmpty<_QueryTailDefinitionWithoutFirstAmp<TPathOriginalDefinition>> extends infer Tail extends string\n ? _AmpSplit<Tail> extends infer U\n ? [U] extends [never]\n ? _EmptyRecord\n : { [K in Extract<U, string>]: true }\n : _EmptyRecord\n : _EmptyRecord\n export type _RoutePathOriginalDefinitionExtended<\n TSourcePathOriginalDefinition extends string,\n TSuffixPathOriginalDefinition extends string,\n > = `${_JoinPath<TSourcePathOriginalDefinition, TSuffixPathOriginalDefinition>}${_QueryTailDefinitionWithFirstAmp<TSuffixPathOriginalDefinition>}`\n\n export type _ParamsInput<TParamsDefinition extends object> = {\n [K in keyof TParamsDefinition]: string | number\n }\n export type _QueryInput<TQueryDefinition extends object> = Partial<{\n [K in keyof TQueryDefinition]: string | number\n }> &\n Record<string, string | number>\n export type _WithParamsInput<\n TParamsDefinition extends object,\n T extends {\n query?: _QueryInput<any>\n abs?: boolean\n },\n > = _ParamsInput<TParamsDefinition> & T\n\n export type _PathOnlyRouteValue<TPathOriginalDefinition extends string> =\n `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}`\n export type _WithQueryRouteValue<TPathOriginalDefinition extends string> =\n `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}?${string}`\n export type _AbsolutePathOnlyRouteValue<TPathOriginalDefinition extends string> =\n `${string}${_PathOnlyRouteValue<TPathOriginalDefinition>}`\n export type _AbsoluteWithQueryRouteValue<TPathOriginalDefinition extends string> =\n `${string}${_WithQueryRouteValue<TPathOriginalDefinition>}`\n\n export type _LocationParams<TParamsDefinition extends object> = {\n [K in keyof TParamsDefinition]: string\n }\n export type _LocationQuery<TQueryDefinition extends object> = {\n [K in keyof TQueryDefinition]: string | undefined\n } & Record<string, string | undefined>\n export type Location<TRoute0 extends AnyRoute = AnyRoute> = {\n query: _LocationQuery<TRoute0['queryDefinition']>\n params: _LocationParams<TRoute0['paramsDefinition']>\n pathname: string\n search: string\n hash: string\n origin?: string\n href: string\n abs: boolean\n }\n export type MatchResult<TRoute0 extends AnyRoute, TExact extends boolean = boolean> = TExact extends true\n ? {\n exact: true\n parent: false\n children: false\n location: Location<TRoute0>\n }\n : TExact extends false\n ? {\n exact: false\n parent: boolean\n children: boolean\n location: Location\n }\n : never\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts"],"sourcesContent":["// TODO: getLocation by windows location\n// TODO: избавиться от жутких эни в роуте\n// TODO: .extension('.json') to not add additional / but just add some extension\n// TODO: query input can be boolean, or even object with qs\n// TODO: Роут0 три пусть три тоже сам генерится вероятно\n// TODO: Роут0 три мод, тогда там все ноуты кончаются на .селф\n// TODO: use splats in param definition \"*\"\n// TODO: ? check extend for query only .extend('&x&z')\n// TODO: .create(route, {useQuery, useParams})\n// TODO: Из пас экзакт, из пасвизквери экзает, из чилдрен, из парент, из экзактОр\n// TODO: isEqual, isChildren, isParent\n// TODO: extractParams, extractQuery\n// TODO: getPathDefinition respecting definitionParamPrefix, definitionQueryPrefix\n// TODO: prepend\n// TODO: Route0.createTree({base:{self: x, children: ...})\n// TODO: overrideTree\n// TODO: .create(route, {baseUrl, useLocation})\n// TODO: ? optional path params as @\n// TODO: prependMany, extendMany, overrideMany, with types\n// TODO: optional route params /x/:id?\n\n// point0\n// TODO: Сделать чисто фронтовую штуку, которая сама вызывает лоадер, сама вызывает нужные мета и title, и отдаёт в компонент нужные штуки\n\n// ssr0\n// TODO: ССР работает просто поверх любого роутера, который поддерживает асинхронную загрузку страниц\n\nexport class Route0<\n TPathOriginalDefinition extends string,\n TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,\n TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,\n TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,\n> {\n pathOriginalDefinition: TPathOriginalDefinition\n private readonly pathDefinition: TPathDefinition\n paramsDefinition: TParamsDefinition\n queryDefinition: TQueryDefinition\n baseUrl: string\n\n private constructor(definition: TPathOriginalDefinition, config: Route0.RouteConfigInput = {}) {\n this.pathOriginalDefinition = definition\n this.pathDefinition = Route0._getPathDefinitionByOriginalDefinition(definition) as TPathDefinition\n this.paramsDefinition = Route0._getParamsDefinitionByRouteDefinition(definition) as TParamsDefinition\n this.queryDefinition = Route0._getQueryDefinitionByRouteDefinition(definition) as TQueryDefinition\n\n const { baseUrl } = config\n if (baseUrl && typeof baseUrl === 'string' && baseUrl.length) {\n this.baseUrl = baseUrl\n } else {\n const g = globalThis as unknown as { location?: { origin?: string } }\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (g?.location?.origin) {\n this.baseUrl = g.location.origin\n } else {\n this.baseUrl = 'https://example.com'\n }\n }\n }\n\n static create<\n TPathOriginalDefinition extends string,\n TPathDefinition extends Route0._PathDefinition<TPathOriginalDefinition>,\n TParamsDefinition extends Route0._ParamsDefinition<TPathOriginalDefinition>,\n TQueryDefinition extends Route0._QueryDefinition<TPathOriginalDefinition>,\n >(\n definition: TPathOriginalDefinition,\n config?: Route0.RouteConfigInput,\n ): Route0.Callable<Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>> {\n const original = new Route0<TPathOriginalDefinition, TPathDefinition, TParamsDefinition, TQueryDefinition>(\n definition,\n config,\n )\n const callable = original.get.bind(original)\n const proxy = new Proxy(callable, {\n get(_target, prop, receiver) {\n const value = (original as any)[prop]\n if (typeof value === 'function') {\n return value.bind(original)\n }\n return value\n },\n set(_target, prop, value, receiver) {\n ;(original as any)[prop] = value\n return true\n },\n has(_target, prop) {\n return prop in original\n },\n })\n Object.setPrototypeOf(proxy, Route0.prototype)\n return proxy as never\n }\n\n private static _splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition: string) {\n const i = pathOriginalDefinition.indexOf('&')\n if (i === -1) return { pathDefinition: pathOriginalDefinition, queryTailDefinition: '' }\n return {\n pathDefinition: pathOriginalDefinition.slice(0, i),\n queryTailDefinition: pathOriginalDefinition.slice(i),\n }\n }\n\n private static _getAbsPath(baseUrl: string, pathWithQuery: string) {\n return new URL(pathWithQuery, baseUrl).toString().replace(/\\/$/, '')\n }\n\n private static _getPathDefinitionByOriginalDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n return pathDefinition as Route0._PathDefinition<TPathOriginalDefinition>\n }\n\n private static _getParamsDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { pathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n const matches = Array.from(pathDefinition.matchAll(/:([A-Za-z0-9_]+)/g))\n const paramsDefinition = Object.fromEntries(matches.map((m) => [m[1], true]))\n return paramsDefinition as Route0._ParamsDefinition<TPathOriginalDefinition>\n }\n\n private static _getQueryDefinitionByRouteDefinition<TPathOriginalDefinition extends string>(\n pathOriginalDefinition: TPathOriginalDefinition,\n ) {\n const { queryTailDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(pathOriginalDefinition)\n if (!queryTailDefinition) {\n return {} as Route0._QueryDefinition<TPathOriginalDefinition>\n }\n const keys = queryTailDefinition.split('&').map(Boolean)\n const queryDefinition = Object.fromEntries(keys.map((k) => [k, true]))\n return queryDefinition as Route0._QueryDefinition<TPathOriginalDefinition>\n }\n\n static overrideMany<T extends Record<string, Route0.AnyRoute>>(routes: T, config: Route0.RouteConfigInput): T {\n const result = {} as T\n for (const [key, value] of Object.entries(routes)) {\n ;(result as any)[key] = value.clone(config)\n }\n return result\n }\n\n extend<TSuffixDefinition extends string>(\n suffixDefinition: TSuffixDefinition,\n ): Route0.Callable<\n Route0<\n Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,\n Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>\n >\n > {\n const { pathDefinition: parentPathDefinition } = Route0._splitPathDefinitionAndQueryTailDefinition(\n this.pathOriginalDefinition,\n )\n const { pathDefinition: suffixPathDefinition, queryTailDefinition: suffixQueryTailDefinition } =\n Route0._splitPathDefinitionAndQueryTailDefinition(suffixDefinition)\n const pathDefinition = `${parentPathDefinition}/${suffixPathDefinition}`.replace(/\\/{2,}/g, '/')\n const pathOriginalDefinition =\n `${pathDefinition}${suffixQueryTailDefinition}` as Route0._RoutePathOriginalDefinitionExtended<\n TPathOriginalDefinition,\n TSuffixDefinition\n >\n return Route0.create<\n Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>,\n Route0._PathDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._ParamsDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>,\n Route0._QueryDefinition<Route0._RoutePathOriginalDefinitionExtended<TPathOriginalDefinition, TSuffixDefinition>>\n >(pathOriginalDefinition, { baseUrl: this.baseUrl })\n }\n\n // has params\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs?: false }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query?: undefined; abs: true }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfHasParams<\n TParamsDefinition,\n Route0._WithParamsInput<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>\n >,\n ): Route0._OnlyIfHasParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>\n\n // no params\n get(\n ...args: Route0._OnlyIfNoParams<TParamsDefinition, [], [never]>\n ): Route0._PathOnlyRouteValue<TPathOriginalDefinition>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs?: false }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._PathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs?: false }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._WithQueryRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query?: undefined; abs: true }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsolutePathOnlyRouteValue<TPathOriginalDefinition>>\n get(\n input: Route0._OnlyIfNoParams<TParamsDefinition, { query: Route0._QueryInput<TQueryDefinition>; abs: true }>,\n ): Route0._OnlyIfNoParams<TParamsDefinition, Route0._AbsoluteWithQueryRouteValue<TPathOriginalDefinition>>\n\n // implementation\n get(...args: any[]): string {\n const { queryInput, paramsInput, absInput } = ((): {\n queryInput: Record<string, string | number>\n paramsInput: Record<string, string | number>\n absInput: boolean\n } => {\n if (args.length === 0) {\n return { queryInput: {}, paramsInput: {}, absInput: false }\n }\n const input = args[0]\n if (typeof input !== 'object' || input === null) {\n // throw new Error(\"Invalid get route input: expected object\")\n return { queryInput: {}, paramsInput: {}, absInput: false }\n }\n const { query, abs, ...params } = input\n return { queryInput: query || {}, paramsInput: params, absInput: abs ?? false }\n })()\n\n // validate params\n const neededParamsKeys = Object.keys(this.paramsDefinition)\n const providedParamsKeys = Object.keys(paramsInput)\n const notProvidedKeys = neededParamsKeys.filter((k) => !providedParamsKeys.includes(k))\n if (notProvidedKeys.length) {\n // throw new Error(`Missing params: not defined keys ${notProvidedKeys.map((k) => `\"${k}\"`).join(\", \")}.`)\n Object.assign(paramsInput, Object.fromEntries(notProvidedKeys.map((k) => [k, 'undefined'])))\n }\n\n // create url\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion\n let url = String(this.pathDefinition)\n // replace params\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n url = url.replace(/:([A-Za-z0-9_]+)/g, (_m, k) => encodeURIComponent(String(paramsInput?.[k] ?? '')))\n // query params\n const queryInputStringified = Object.fromEntries(Object.entries(queryInput).map(([k, v]) => [k, String(v)]))\n url = [url, new URLSearchParams(queryInputStringified).toString()].filter(Boolean).join('?')\n // dedupe slashes\n url = url.replace(/\\/{2,}/g, '/')\n // absolute\n url = absInput ? Route0._getAbsPath(this.baseUrl, url) : url\n\n return url\n }\n\n getDefinition() {\n return this.pathDefinition\n }\n\n clone(config?: Route0.RouteConfigInput) {\n return new Route0(this.pathOriginalDefinition, config)\n }\n\n static getLocation(url: `${string}://${string}`): Route0.Location\n static getLocation(path: `/${string}`): Route0.Location\n static getLocation(urlOrPath: string): Route0.Location\n static getLocation(urlOrPath: string): Route0.Location {\n // Allow both relative and absolute inputs\n const abs = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:\\/\\//.test(urlOrPath)\n const base = abs ? undefined : 'http://example.com' // dummy base for relative URLs\n\n const url = new URL(urlOrPath, base)\n\n // Extract query and params (params left empty for now — parsed separately)\n const query = Object.fromEntries(url.searchParams.entries())\n\n // Normalize pathname (remove trailing slash except for root)\n let pathname = url.pathname\n if (pathname.length > 1 && pathname.endsWith('/')) {\n pathname = pathname.slice(0, -1)\n }\n\n // Build the location object similar to browser location\n return {\n query,\n params: {}, // filled later by .parse()\n pathname,\n search: url.search,\n hash: url.hash,\n origin: abs ? url.origin : undefined,\n href: abs ? url.href : pathname + url.search + url.hash,\n abs,\n }\n }\n\n static getMatch<TRoute0 extends Route0.AnyRoute>(\n route0: TRoute0,\n location: Route0.Location,\n ): Route0.MatchResult<TRoute0> {\n const locationCloned = { ...location, params: { ...location.params }, query: { ...location.query } }\n const escapeRegex = (s: string) => s.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&')\n\n // Normalize both sides (no trailing slash except root)\n const def =\n route0.pathDefinition.length > 1 && route0.pathDefinition.endsWith('/')\n ? route0.pathDefinition.slice(0, -1)\n : route0.pathDefinition\n const pathname =\n locationCloned.pathname.length > 1 && locationCloned.pathname.endsWith('/')\n ? locationCloned.pathname.slice(0, -1)\n : locationCloned.pathname\n\n // Build a regex from the route definition: /a/:x/b/:y -> ^/a/([^/]+)/b/([^/]+)$\n const paramNames: string[] = []\n const pattern = def.replace(/:([A-Za-z0-9_]+)/g, (_m: string, name: string) => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion\n paramNames.push(String(name))\n return '([^/]+)'\n })\n\n const exactRe = new RegExp(`^${pattern}$`)\n const parentRe = new RegExp(`^${pattern}(?:/.*)?$`) // route matches the beginning of the URL (may have more)\n const exactMatch = pathname.match(exactRe)\n\n // Fill params only for exact match (keeps behavior predictable)\n if (exactMatch) {\n const values = exactMatch.slice(1)\n const params = Object.fromEntries(paramNames.map((n, i) => [n, decodeURIComponent(values[i] ?? '')]))\n locationCloned.params = params\n } else {\n locationCloned.params = {}\n }\n\n const exact = !!exactMatch\n const parent = !exact && parentRe.test(pathname)\n\n // \"children\": the URL is a prefix of the route definition (ignoring params’ concrete values)\n // We check if the definition starts with the URL path boundary-wise.\n const children = !exact && new RegExp(`^${escapeRegex(pathname)}(?:/|$)`).test(def)\n\n return {\n exact,\n parent,\n children,\n location: locationCloned,\n } as never\n }\n match(url: string): Route0.MatchResult<this> {\n const location = Route0.getLocation(url)\n return Route0.getMatch(this, location)\n }\n}\n\nexport namespace Route0 {\n export type AnyRoute = Route0<any, any, any, any>\n export type Callable<T extends AnyRoute> = T & T['get']\n export type RouteConfigInput = {\n baseUrl?: string\n }\n export type Params<TRoute0 extends AnyRoute> = {\n [K in keyof TRoute0['paramsDefinition']]: string\n }\n export type Query<TRoute0 extends AnyRoute> = Partial<\n {\n [K in keyof TRoute0['queryDefinition']]: string | undefined\n } & Record<string, string | undefined>\n >\n\n export type _TrimQueryTailDefinition<S extends string> = S extends `${infer P}&${string}` ? P : S\n export type _QueryTailDefinitionWithoutFirstAmp<S extends string> = S extends `${string}&${infer T}` ? T : ''\n export type _QueryTailDefinitionWithFirstAmp<S extends string> = S extends `${string}&${infer T}` ? `&${T}` : ''\n export type _AmpSplit<S extends string> = S extends `${infer A}&${infer B}` ? A | _AmpSplit<B> : S\n // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\n export type _NonEmpty<T> = [T] extends ['' | never] ? never : T\n export type _ExtractPathParams<S extends string> = S extends `${string}:${infer After}`\n ? After extends `${infer Name}/${infer Rest}`\n ? Name | _ExtractPathParams<`/${Rest}`>\n : After\n : never\n export type _ReplacePathParams<S extends string> = S extends `${infer Head}:${infer Tail}`\n ? // eslint-disable-next-line @typescript-eslint/no-unused-vars\n Tail extends `${infer _Param}/${infer Rest}`\n ? _ReplacePathParams<`${Head}${string}/${Rest}`>\n : `${Head}${string}`\n : S\n export type _DedupeSlashes<S extends string> = S extends `${infer A}//${infer B}` ? _DedupeSlashes<`${A}/${B}`> : S\n export type _EmptyRecord = Record<never, never>\n export type _JoinPath<Parent extends string, Suffix extends string> = _DedupeSlashes<\n _PathDefinition<Parent> extends infer A extends string\n ? _PathDefinition<Suffix> extends infer B extends string\n ? A extends ''\n ? B extends ''\n ? ''\n : B extends `/${string}`\n ? B\n : `/${B}`\n : B extends ''\n ? A\n : A extends `${string}/`\n ? `${A}${B}`\n : B extends `/${string}`\n ? `${A}${B}`\n : `${A}/${B}`\n : never\n : never\n >\n\n export type _OnlyIfNoParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? Yes : No\n export type _OnlyIfHasParams<TParams extends object, Yes, No = never> = keyof TParams extends never ? No : Yes\n\n export type _PathDefinition<TPathOriginalDefinition extends string> =\n _TrimQueryTailDefinition<TPathOriginalDefinition>\n export type _ParamsDefinition<TPathOriginalDefinition extends string> =\n _ExtractPathParams<_PathDefinition<TPathOriginalDefinition>> extends infer U\n ? [U] extends [never]\n ? _EmptyRecord\n : { [K in Extract<U, string>]: true }\n : _EmptyRecord\n export type _QueryDefinition<TPathOriginalDefinition extends string> =\n _NonEmpty<_QueryTailDefinitionWithoutFirstAmp<TPathOriginalDefinition>> extends infer Tail extends string\n ? _AmpSplit<Tail> extends infer U\n ? [U] extends [never]\n ? _EmptyRecord\n : { [K in Extract<U, string>]: true }\n : _EmptyRecord\n : _EmptyRecord\n export type _RoutePathOriginalDefinitionExtended<\n TSourcePathOriginalDefinition extends string,\n TSuffixPathOriginalDefinition extends string,\n > = `${_JoinPath<TSourcePathOriginalDefinition, TSuffixPathOriginalDefinition>}${_QueryTailDefinitionWithFirstAmp<TSuffixPathOriginalDefinition>}`\n\n export type _ParamsInput<TParamsDefinition extends object> = {\n [K in keyof TParamsDefinition]: string | number\n }\n export type _QueryInput<TQueryDefinition extends object> = Partial<{\n [K in keyof TQueryDefinition]: string | number\n }> &\n Record<string, string | number>\n export type _WithParamsInput<\n TParamsDefinition extends object,\n T extends {\n query?: _QueryInput<any>\n abs?: boolean\n },\n > = _ParamsInput<TParamsDefinition> & T\n\n export type _PathOnlyRouteValue<TPathOriginalDefinition extends string> =\n `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}`\n export type _WithQueryRouteValue<TPathOriginalDefinition extends string> =\n `${_ReplacePathParams<_PathDefinition<TPathOriginalDefinition>>}?${string}`\n export type _AbsolutePathOnlyRouteValue<TPathOriginalDefinition extends string> =\n `${string}${_PathOnlyRouteValue<TPathOriginalDefinition>}`\n export type _AbsoluteWithQueryRouteValue<TPathOriginalDefinition extends string> =\n `${string}${_WithQueryRouteValue<TPathOriginalDefinition>}`\n\n export type _LocationParams<TParamsDefinition extends object> = {\n [K in keyof TParamsDefinition]: string\n }\n export type _LocationQuery<TQueryDefinition extends object> = {\n [K in keyof TQueryDefinition]: string | undefined\n } & Record<string, string | undefined>\n export type Location<TRoute0 extends AnyRoute = AnyRoute> = {\n query: _LocationQuery<TRoute0['queryDefinition']>\n params: _LocationParams<TRoute0['paramsDefinition']>\n pathname: string\n search: string\n hash: string\n origin?: string\n href: string\n abs: boolean\n }\n export type MatchResult<TRoute0 extends AnyRoute, TExact extends boolean = boolean> = TExact extends true\n ? {\n exact: true\n parent: false\n children: false\n location: Location<TRoute0>\n }\n : TExact extends false\n ? {\n exact: false\n parent: boolean\n children: boolean\n location: Location\n }\n : never\n export type HasParams<TRoute0 extends AnyRoute> =\n _ExtractPathParams<_PathDefinition<TRoute0['pathOriginalDefinition']>> extends infer U\n ? [U] extends [never]\n ? false\n : true\n : false\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
package/src/index.test.ts
CHANGED
|
@@ -8,6 +8,7 @@ describe('route0', () => {
|
|
|
8
8
|
expect(route0).toBeInstanceOf(Route0)
|
|
9
9
|
expectTypeOf<typeof path>().toEqualTypeOf<'/'>()
|
|
10
10
|
expect(path).toBe('/')
|
|
11
|
+
expectTypeOf<Route0.HasParams<typeof route0>>().toEqualTypeOf<false>()
|
|
11
12
|
})
|
|
12
13
|
|
|
13
14
|
it('simple, callable', () => {
|
|
@@ -30,6 +31,7 @@ describe('route0', () => {
|
|
|
30
31
|
const path = route0.get({ x: '1', y: 2, z: '3' })
|
|
31
32
|
expectTypeOf<typeof path>().toEqualTypeOf<`/prefix/${string}/some/${string}/${string}`>()
|
|
32
33
|
expect(path).toBe('/prefix/1/some/2/3')
|
|
34
|
+
expectTypeOf<Route0.HasParams<typeof route0>>().toEqualTypeOf<true>()
|
|
33
35
|
})
|
|
34
36
|
|
|
35
37
|
it('params and any query', () => {
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// TODO: getLocation by windows location
|
|
2
|
+
// TODO: избавиться от жутких эни в роуте
|
|
3
|
+
// TODO: .extension('.json') to not add additional / but just add some extension
|
|
1
4
|
// TODO: query input can be boolean, or even object with qs
|
|
2
5
|
// TODO: Роут0 три пусть три тоже сам генерится вероятно
|
|
3
6
|
// TODO: Роут0 три мод, тогда там все ноуты кончаются на .селф
|
|
@@ -14,8 +17,9 @@
|
|
|
14
17
|
// TODO: .create(route, {baseUrl, useLocation})
|
|
15
18
|
// TODO: ? optional path params as @
|
|
16
19
|
// TODO: prependMany, extendMany, overrideMany, with types
|
|
20
|
+
// TODO: optional route params /x/:id?
|
|
17
21
|
|
|
18
|
-
//
|
|
22
|
+
// point0
|
|
19
23
|
// TODO: Сделать чисто фронтовую штуку, которая сама вызывает лоадер, сама вызывает нужные мета и title, и отдаёт в компонент нужные штуки
|
|
20
24
|
|
|
21
25
|
// ssr0
|
|
@@ -297,6 +301,7 @@ export class Route0<
|
|
|
297
301
|
route0: TRoute0,
|
|
298
302
|
location: Route0.Location,
|
|
299
303
|
): Route0.MatchResult<TRoute0> {
|
|
304
|
+
const locationCloned = { ...location, params: { ...location.params }, query: { ...location.query } }
|
|
300
305
|
const escapeRegex = (s: string) => s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
|
|
301
306
|
|
|
302
307
|
// Normalize both sides (no trailing slash except root)
|
|
@@ -305,9 +310,9 @@ export class Route0<
|
|
|
305
310
|
? route0.pathDefinition.slice(0, -1)
|
|
306
311
|
: route0.pathDefinition
|
|
307
312
|
const pathname =
|
|
308
|
-
|
|
309
|
-
?
|
|
310
|
-
:
|
|
313
|
+
locationCloned.pathname.length > 1 && locationCloned.pathname.endsWith('/')
|
|
314
|
+
? locationCloned.pathname.slice(0, -1)
|
|
315
|
+
: locationCloned.pathname
|
|
311
316
|
|
|
312
317
|
// Build a regex from the route definition: /a/:x/b/:y -> ^/a/([^/]+)/b/([^/]+)$
|
|
313
318
|
const paramNames: string[] = []
|
|
@@ -325,9 +330,9 @@ export class Route0<
|
|
|
325
330
|
if (exactMatch) {
|
|
326
331
|
const values = exactMatch.slice(1)
|
|
327
332
|
const params = Object.fromEntries(paramNames.map((n, i) => [n, decodeURIComponent(values[i] ?? '')]))
|
|
328
|
-
|
|
333
|
+
locationCloned.params = params
|
|
329
334
|
} else {
|
|
330
|
-
|
|
335
|
+
locationCloned.params = {}
|
|
331
336
|
}
|
|
332
337
|
|
|
333
338
|
const exact = !!exactMatch
|
|
@@ -341,7 +346,7 @@ export class Route0<
|
|
|
341
346
|
exact,
|
|
342
347
|
parent,
|
|
343
348
|
children,
|
|
344
|
-
location,
|
|
349
|
+
location: locationCloned,
|
|
345
350
|
} as never
|
|
346
351
|
}
|
|
347
352
|
match(url: string): Route0.MatchResult<this> {
|
|
@@ -483,4 +488,10 @@ export namespace Route0 {
|
|
|
483
488
|
location: Location
|
|
484
489
|
}
|
|
485
490
|
: never
|
|
491
|
+
export type HasParams<TRoute0 extends AnyRoute> =
|
|
492
|
+
_ExtractPathParams<_PathDefinition<TRoute0['pathOriginalDefinition']>> extends infer U
|
|
493
|
+
? [U] extends [never]
|
|
494
|
+
? false
|
|
495
|
+
: true
|
|
496
|
+
: false
|
|
486
497
|
}
|