Skip to content

Commit

Permalink
fix: support using view component name in urlFor
Browse files Browse the repository at this point in the history
  • Loading branch information
platosha committed Oct 1, 2024
1 parent f3310fb commit e074f67
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 23 deletions.
10 changes: 5 additions & 5 deletions src/resolver/generateUrls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import { parse, type ParseOptions, type Token, tokensToFunction, type TokensToFunctionOptions } from 'path-to-regexp';
import type { EmptyObject, Writable } from 'type-fest';
import Resolver from './resolver.js';
import type { AnyObject, ChildrenCallback, Route } from './types.js';
import type { AnyObject, ChildrenCallback, Route, Params } from './types.js';
import { getRoutePath, isString } from './utils.js';

export type UrlParams = Readonly<Record<string, ReadonlyArray<number | string> | number | string>>;
Expand All @@ -33,7 +33,7 @@ function cacheRoutes<T, R extends AnyObject = EmptyObject>(
if (Array.isArray<ReadonlyArray<Writable<Route<T, R>>>>(routes)) {
for (const childRoute of routes) {
childRoute.parent = route;
cacheRoutes(routesByName, childRoute, childRoute.__children ?? childRoute.children);
cacheRoutes(routesByName, childRoute, childRoute.__children ?? childRoute.children, cacheKeyProvider);
}
}
}
Expand Down Expand Up @@ -67,7 +67,7 @@ export type GenerateUrlOptions<T, R extends AnyObject> = ParseOptions &
* Generates a unique route name based on all parent routes with the specified separator.
*/
uniqueRouteNameSep?: string;
cacheKeyProvider?(route: Route<T, R>): string;
cacheKeyProvider?(route: Route<T, R>): string | undefined;
}> &
TokensToFunctionOptions;

Expand All @@ -80,7 +80,7 @@ export type UrlGenerator = (routeName: string, params?: Params) => string;

function generateUrls<T, R extends AnyObject = EmptyObject>(
resolver: Resolver<R>,
options: GenerateUrlOptions<T, R> = { encode: encodeURIComponent },
options: GenerateUrlOptions<T, R> = {},
): UrlGenerator {
if (!(resolver instanceof Resolver)) {
throw new TypeError('An instance of Resolver is expected');
Expand Down Expand Up @@ -129,7 +129,7 @@ function generateUrls<T, R extends AnyObject = EmptyObject>(
route.fullPath = fullPath;
}

const toPath = tokensToFunction(cached.tokens, options);
const toPath = tokensToFunction(cached.tokens, { encode: encodeURIComponent, ...options });
let url = toPath(params) || '/';

if (options.stringifyQueryParams && params) {
Expand Down
8 changes: 4 additions & 4 deletions src/resolver/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ export type ChildrenCallback<T, R extends AnyObject> = (
context: RouteChildrenContext<T, R>,
) => MaybePromise<ReadonlyArray<Route<T, R>>>;

export type ParamValue = readonly string[] | string;
export type PrimitiveParamValue = string | number | null;

export type IndexedParams = Readonly<{
[key in keyof any]?: ParamValue;
}>;
export type ParamValue = PrimitiveParamValue | readonly PrimitiveParamValue[];

export type IndexedParams = Readonly<Record<string, ParamValue>>;

export type Params = IndexedParams | ParamValue[];
8 changes: 7 additions & 1 deletion src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1057,7 +1057,13 @@ export class Router<R extends AnyObject = EmptyObject> extends Resolver<R> {
*/
urlForName(name: string, params?: Params | null): string {
if (!this.__urlForName) {
this.__urlForName = generateUrls(this);
this.__urlForName = generateUrls(this, {
cacheKeyProvider(route): string | undefined {
return 'component' in route && typeof route.component === 'string'
? (route as Readonly<{ component: string }>).component
: undefined;
},
});
}
return getPathnameForRouter(this.__urlForName(name, params ?? undefined), this);
}
Expand Down
12 changes: 2 additions & 10 deletions src/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { EmptyObject, RequireAtLeastOne } from 'type-fest';
import type { ResolutionError } from './resolver/resolver.js';
import type { ChildrenCallback } from './resolver/types.js';
import type { ChildrenCallback, IndexedParams, Params, ParamValue, PrimitiveParamValue } from './resolver/types.js';
import type { Router } from './router.js';

export { ResolutionError };
export type { ResolutionError, IndexedParams, Params, ParamValue, PrimitiveParamValue };

export type VaadinRouterLocationChangedEvent = CustomEvent<
Readonly<{
Expand Down Expand Up @@ -447,11 +447,3 @@ export type Route<R extends AnyObject = EmptyObject> = Readonly<
path?: string | readonly string[];
}
>;

export type PrimitiveParamValue = string | number | null;

export type ParamValue = PrimitiveParamValue | readonly PrimitiveParamValue[];

export type IndexedParams = Readonly<Record<string, ParamValue>>;

export type Params = IndexedParams | ParamValue[];
13 changes: 10 additions & 3 deletions test/router/url-for.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expect } from '@esm-bundle/chai';
import sinon from 'sinon';
import { Router } from '../../src/router.js';
import '../setup.js';
import { cleanup } from './test-utils.js';
Expand Down Expand Up @@ -215,6 +216,8 @@ describe('urlFor', () => {
expect(parse).to.be.calledWithMatch(path);
expect(result).to.equal('/app/users/42');
} finally {
// @ts-ignore not exposed anymore
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
Router.pathToRegexp.parse.restore();
}
});
Expand Down Expand Up @@ -246,19 +249,21 @@ describe('urlFor', () => {
});

it('should prepend baseUrl', () => {
router.baseUrl = '/base/';
(router as { baseUrl: string }).baseUrl = '/base/';
expect(router.urlForPath('foo')).to.equal('/base/foo');
expect(router.urlForPath('/bar')).to.equal('/base/bar');
});

// cannot mock the call to `compile()` from the 'pathToRegexp' package
xit('should use pathToRegexp', () => {
const compiledRegExp = sinon.stub().returns('/ok/url');
// @ts-ignore not exposed anymore
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
const compile = sinon.stub(Router.pathToRegexp, 'compile').returns(compiledRegExp);

try {
const path = '/users/:userId',
parameters = { userId: 42, foo: 'bar' };
const path = '/users/:userId';
const parameters = { userId: 42, foo: 'bar' };
const result = router.urlForPath(path, parameters);

expect(compile).to.be.calledOnce;
Expand All @@ -268,6 +273,8 @@ describe('urlFor', () => {
expect(compiledRegExp).to.have.returned('/ok/url');
expect(result).to.equal('/ok/url');
} finally {
// @ts-ignore not exposed anymore
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
Router.pathToRegexp.compile.restore();
}
});
Expand Down

0 comments on commit e074f67

Please sign in to comment.