diff --git a/.changeset/orange-kangaroos-camp.md b/.changeset/orange-kangaroos-camp.md new file mode 100644 index 00000000..a86501ac --- /dev/null +++ b/.changeset/orange-kangaroos-camp.md @@ -0,0 +1,5 @@ +--- +"@stackflow/plugin-history-sync": minor +--- + +Add `encode` interface diff --git a/.changeset/sweet-hotels-sleep.md b/.changeset/sweet-hotels-sleep.md new file mode 100644 index 00000000..6dfa6e9f --- /dev/null +++ b/.changeset/sweet-hotels-sleep.md @@ -0,0 +1,5 @@ +--- +"@stackflow/plugin-history-sync": minor +--- + +Enable usage of `encode` and `decode` with Future API as `encodePath` and `decodePath` diff --git a/docs/middleware.js b/docs/middleware.js index 6629716c..38011cf7 100644 --- a/docs/middleware.js +++ b/docs/middleware.js @@ -1,2 +1 @@ export { locales as middleware } from "nextra/locales"; - diff --git a/docs/postcss.config.js b/docs/postcss.config.js index 33ad091d..12a703d9 100644 --- a/docs/postcss.config.js +++ b/docs/postcss.config.js @@ -3,4 +3,4 @@ module.exports = { tailwindcss: {}, autoprefixer: {}, }, -} +}; diff --git a/extensions/plugin-history-sync/src/RouteLike.ts b/extensions/plugin-history-sync/src/RouteLike.ts index aa6083fe..5708b260 100644 --- a/extensions/plugin-history-sync/src/RouteLike.ts +++ b/extensions/plugin-history-sync/src/RouteLike.ts @@ -1,10 +1,13 @@ import type { ActivityComponentType } from "@stackflow/react"; +type Params = K extends ActivityComponentType + ? U + : Record; + export type Route = { path: string; - decode?: ( - params: Record, - ) => K extends ActivityComponentType ? U : {}; + decode?: (params: Record) => Params | null; + encode?: (params: Params) => Record; }; export type RouteLike = string | string[] | Route | Route[]; diff --git a/extensions/plugin-history-sync/src/historySyncPlugin.tsx b/extensions/plugin-history-sync/src/historySyncPlugin.tsx index e9a821a5..e1e2f379 100644 --- a/extensions/plugin-history-sync/src/historySyncPlugin.tsx +++ b/extensions/plugin-history-sync/src/historySyncPlugin.tsx @@ -25,6 +25,8 @@ type ConfigHistorySync = { declare module "@stackflow/config" { interface ActivityDefinition { path: string; + decodePath?: (params: Record) => Record | null; + encodePath?: (params: Record) => Record; } interface Config> { @@ -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, + }, }), {}, ); diff --git a/extensions/plugin-history-sync/src/makeTemplate.spec.ts b/extensions/plugin-history-sync/src/makeTemplate.spec.ts index 1c6c23cf..5e6bc17b 100644 --- a/extensions/plugin-history-sync/src/makeTemplate.spec.ts +++ b/extensions/plugin-history-sync/src/makeTemplate.spec.ts @@ -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), + }), + }); + + expect(template.fill(template.parse("/articles/1234/") ?? {})).toStrictEqual( + "/articles/1234/", + ); + expect( + template.parse(template.fill({ id: 1234 as unknown as string })), + ).toStrictEqual({ + id: 1234, + }); +}); diff --git a/extensions/plugin-history-sync/src/makeTemplate.ts b/extensions/plugin-history-sync/src/makeTemplate.ts index 0d57769f..6f44f9b6 100644 --- a/extensions/plugin-history-sync/src/makeTemplate.ts +++ b/extensions/plugin-history-sync/src/makeTemplate.ts @@ -46,7 +46,7 @@ export interface UrlPatternOptions { } export function makeTemplate( - { path, decode }: Route, + { path, decode, encode }: Route, urlPatternOptions?: UrlPatternOptions, ) { const pattern = new UrlPattern(`${path}(/)`, urlPatternOptions); @@ -59,10 +59,11 @@ export function makeTemplate( 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];