Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(history-sync): add encode interface #535

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/orange-kangaroos-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stackflow/plugin-history-sync": minor
---

Add `encode` interface
5 changes: 5 additions & 0 deletions .changeset/sweet-hotels-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stackflow/plugin-history-sync": minor
---

Enable usage of `encode` and `decode` with Future API as `encodePath` and `decodePath`
1 change: 0 additions & 1 deletion docs/middleware.js
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { locales as middleware } from "nextra/locales";

2 changes: 1 addition & 1 deletion docs/postcss.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ module.exports = {
tailwindcss: {},
autoprefixer: {},
},
}
};
9 changes: 6 additions & 3 deletions extensions/plugin-history-sync/src/RouteLike.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import type { ActivityComponentType } from "@stackflow/react";

type Params<K> = K extends ActivityComponentType<infer U>
? U
: Record<string, unknown>;

export type Route<K> = {
path: string;
decode?: (
params: Record<string, string>,
) => K extends ActivityComponentType<infer U> ? U : {};
decode?: (params: Record<string, string>) => Params<K> | null;
encode?: (params: Params<K>) => Record<string, string>;
};

export type RouteLike<T> = string | string[] | Route<T> | Route<T>[];
8 changes: 7 additions & 1 deletion extensions/plugin-history-sync/src/historySyncPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ type ConfigHistorySync = {
declare module "@stackflow/config" {
interface ActivityDefinition<ActivityName extends string> {
path: string;
decodePath?: (params: Record<string, string>) => Record<string, unknown> | null;
encodePath?: (params: Record<string, unknown>) => Record<string, string>;
}

interface Config<T extends ActivityDefinition<string>> {
Expand Down Expand Up @@ -74,7 +76,11 @@ export function historySyncPlugin<
: options.config.activities.reduce(
(acc, a) => ({
...acc,
[a.name]: a.path,
[a.name]: {
path: a.path,
decode: a.decodePath,
encode: a.encodePath,
},
}),
{},
);
Expand Down
34 changes: 34 additions & 0 deletions extensions/plugin-history-sync/src/makeTemplate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,37 @@ test("makeTemplate - parse with given decode function", () => {
articleId: 1234,
});
});

test("makeTemplate - fill with given encode function", () => {
const template = makeTemplate({
path: "/articles/:articleId",
encode: ({ id }) => ({
articleId: String(id),
}),
});

expect(template.fill({ id: 1234 as unknown as string })).toEqual(
"/articles/1234/",
);
});

test("makeTemplate - roundtrip with given encode and decode function", () => {
const template = makeTemplate({
path: "/articles/:articleId",
encode: ({ id }) => ({
articleId: String(id),
}),
decode: ({ articleId }) => ({
id: Number(articleId),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty surprised that the decode function can be used to make non-string activity params, which clashes with the assumption of ActivityBaseParams that expects Record<string, string | unknown>. Is it okay?

}),
});

expect(template.fill(template.parse("/articles/1234/") ?? {})).toStrictEqual(
"/articles/1234/",
);
expect(
template.parse(template.fill({ id: 1234 as unknown as string })),
).toStrictEqual({
id: 1234,
});
});
7 changes: 4 additions & 3 deletions extensions/plugin-history-sync/src/makeTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export interface UrlPatternOptions {
}

export function makeTemplate<T>(
{ path, decode }: Route<T>,
{ path, decode, encode }: Route<T>,
urlPatternOptions?: UrlPatternOptions,
) {
const pattern = new UrlPattern(`${path}(/)`, urlPatternOptions);
Expand All @@ -59,10 +59,11 @@ export function makeTemplate<T>(

return {
fill(params: { [key: string]: string | undefined }) {
const pathname = pattern.stringify(params);
const encodedParams = encode ? encode(params as any) : params;
const pathname = pattern.stringify(encodedParams);
const pathParams = pattern.match(pathname);

const searchParamsMap = { ...params };
const searchParamsMap = { ...encodedParams };

Object.keys(pathParams).forEach((key) => {
delete searchParamsMap[key];
Expand Down
Loading