@stackflow/plugin-history-sync
Advanced tools
Comparing version 1.3.18 to 1.4.0-canary.0
import type { StackflowReactPlugin } from "@stackflow/react"; | ||
import type { History } from "history"; | ||
import type { UrlPatternOptions } from "./makeTemplate"; | ||
declare type HistorySyncPluginOptions<K extends string> = { | ||
import type { RouteLike } from "./RouteLike"; | ||
declare type HistorySyncPluginOptions<T, K extends Extract<keyof T, string>> = { | ||
routes: { | ||
[key in K]: string | string[]; | ||
[key in keyof T]: RouteLike<T[key]>; | ||
}; | ||
@@ -17,3 +18,3 @@ fallbackActivity: (args: { | ||
[activityName: string]: unknown; | ||
}>(options: HistorySyncPluginOptions<Extract<keyof T, string>>): StackflowReactPlugin<T>; | ||
}, K extends Extract<keyof T, string>>(options: HistorySyncPluginOptions<T, K>): StackflowReactPlugin<T>; | ||
export {}; |
export { useHistoryTick } from "./HistoryQueueContext"; | ||
export * from "./historySyncPlugin"; | ||
export { makeTemplate, UrlPatternOptions } from "./makeTemplate"; | ||
export { normalizeRoute } from "./normalizeRoute"; | ||
export { useRoutes } from "./RoutesContext"; |
@@ -52,3 +52,2 @@ "use strict"; | ||
makeTemplate: () => makeTemplate, | ||
normalizeRoute: () => normalizeRoute, | ||
useHistoryTick: () => useHistoryTick, | ||
@@ -143,2 +142,22 @@ useRoutes: () => useRoutes | ||
// src/makeHistoryTaskQueue.ts | ||
var makeHistoryTaskQueue = (history) => { | ||
let previousTask = Promise.resolve(); | ||
const requestHistoryTick = (cb) => { | ||
previousTask = previousTask.then( | ||
() => new Promise((resolve) => { | ||
const clean = history.listen(() => { | ||
clean(); | ||
resolve(); | ||
}); | ||
cb(() => { | ||
clean(); | ||
resolve(); | ||
}); | ||
}) | ||
); | ||
}; | ||
return { requestHistoryTick }; | ||
}; | ||
// src/makeTemplate.ts | ||
@@ -169,4 +188,4 @@ var import_url_pattern = __toESM(require("url-pattern")); | ||
} | ||
function makeTemplate(templateStr, urlPatternOptions) { | ||
const pattern = new import_url_pattern.default(`${templateStr}(/)`, urlPatternOptions); | ||
function makeTemplate({ path, decode }, urlPatternOptions) { | ||
const pattern = new import_url_pattern.default(`${path}(/)`, urlPatternOptions); | ||
return { | ||
@@ -190,4 +209,4 @@ fill(params) { | ||
}, | ||
parse(path) { | ||
const url = pathToUrl(path); | ||
parse(path2) { | ||
const url = pathToUrl(path2); | ||
const pathParams = pattern.match(url.pathname); | ||
@@ -198,31 +217,29 @@ const searchParams = urlSearchParamsToMap(url.searchParams); | ||
} | ||
return __spreadValues(__spreadValues({}, searchParams), pathParams); | ||
} | ||
return __spreadValues(__spreadValues({}, searchParams), decode ? decode(pathParams) : pathParams); | ||
}, | ||
variableCount: pattern.names.length | ||
}; | ||
} | ||
// src/normalizeRoute.ts | ||
function normalizeRoute(route) { | ||
return typeof route === "string" ? [route] : route; | ||
// src/normalizeRouteInput.ts | ||
function makeRoute(path) { | ||
return typeof path === "string" ? { path } : path; | ||
} | ||
function normalizeRouteInput(route) { | ||
return (Array.isArray(route) ? route : [route]).map(makeRoute); | ||
} | ||
// src/queue.ts | ||
var makeHistoryTaskQueue = (history) => { | ||
let previousTask = Promise.resolve(); | ||
const requestHistoryTick = (cb) => { | ||
previousTask = previousTask.then( | ||
() => new Promise((resolve) => { | ||
const clean = history.listen(() => { | ||
clean(); | ||
resolve(); | ||
}); | ||
cb(() => { | ||
clean(); | ||
resolve(); | ||
}); | ||
}) | ||
); | ||
}; | ||
return { requestHistoryTick }; | ||
}; | ||
// src/normalizeActivityRouteMap.ts | ||
function normalizeActivityRouteMap(activityRouteMap) { | ||
const routes = Object.keys(activityRouteMap).flatMap((activityName) => { | ||
const routeInput = activityRouteMap[activityName]; | ||
if (!routeInput) { | ||
return []; | ||
} | ||
return normalizeRouteInput(routeInput).map((route) => __spreadValues({ | ||
activityName | ||
}, route)); | ||
}); | ||
return routes; | ||
} | ||
@@ -232,3 +249,3 @@ // src/RoutesContext.tsx | ||
var import_jsx_runtime2 = require("react/jsx-runtime"); | ||
var RoutesContext = (0, import_react2.createContext)({}); | ||
var RoutesContext = (0, import_react2.createContext)([]); | ||
var RoutesProvider = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoutesContext.Provider, { value: props.routes, children: props.children }); | ||
@@ -240,2 +257,9 @@ RoutesProvider.displayName = "RoutesProvider"; | ||
// src/sortActivityRoutes.ts | ||
function sortActivityRoutes(routes) { | ||
return [...routes].sort( | ||
(a, b) => makeTemplate(a).variableCount - makeTemplate(b).variableCount | ||
); | ||
} | ||
// src/historySyncPlugin.tsx | ||
@@ -251,2 +275,5 @@ var import_jsx_runtime3 = require("react/jsx-runtime"); | ||
const { location } = history; | ||
const activityRoutes = sortActivityRoutes( | ||
normalizeActivityRouteMap(options.routes) | ||
); | ||
return () => { | ||
@@ -259,3 +286,3 @@ let pushFlag = 0; | ||
wrapStack({ stack }) { | ||
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(HistoryQueueProvider, { requestHistoryTick, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RoutesProvider, { routes: options.routes, children: stack.render() }) }); | ||
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(HistoryQueueProvider, { requestHistoryTick, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RoutesProvider, { routes: activityRoutes, children: stack.render() }) }); | ||
}, | ||
@@ -279,3 +306,3 @@ overrideInitialEvents({ initialContext }) { | ||
} | ||
function resolvePath() { | ||
function resolveCurrentPath() { | ||
var _a3, _b2; | ||
@@ -290,24 +317,23 @@ if (((_a3 = initialContext == null ? void 0 : initialContext.req) == null ? void 0 : _a3.path) && typeof initialContext.req.path === "string") { | ||
} | ||
const path = resolvePath(); | ||
const activityNames = Object.keys(options.routes); | ||
if (path) { | ||
for (const activityName of activityNames) { | ||
const routes = normalizeRoute(options.routes[activityName]); | ||
for (const route of routes) { | ||
const template = makeTemplate(route, options.urlPatternOptions); | ||
const activityParams = template.parse(path); | ||
if (activityParams) { | ||
const activityId = (0, import_core.id)(); | ||
return [ | ||
(0, import_core.makeEvent)("Pushed", { | ||
activityId, | ||
activityName, | ||
activityParams: __spreadValues({}, activityParams), | ||
eventDate: new Date().getTime() - MINUTE, | ||
activityContext: { | ||
path | ||
} | ||
}) | ||
]; | ||
} | ||
const currentPath = resolveCurrentPath(); | ||
if (currentPath) { | ||
for (const activityRoute of activityRoutes) { | ||
const template = makeTemplate( | ||
activityRoute, | ||
options.urlPatternOptions | ||
); | ||
const activityParams = template.parse(currentPath); | ||
if (activityParams) { | ||
const activityId = (0, import_core.id)(); | ||
return [ | ||
(0, import_core.makeEvent)("Pushed", { | ||
activityId, | ||
activityName: activityRoute.activityName, | ||
activityParams: __spreadValues({}, activityParams), | ||
eventDate: new Date().getTime() - MINUTE, | ||
activityContext: { | ||
path: currentPath | ||
} | ||
}) | ||
]; | ||
} | ||
@@ -320,6 +346,6 @@ } | ||
}); | ||
const fallbackActivityRoutes = normalizeRoute( | ||
options.routes[fallbackActivityName] | ||
const fallbackActivityRoute = activityRoutes.find( | ||
(r) => r.activityName === fallbackActivityName | ||
); | ||
const fallbackActivityPath = fallbackActivityRoutes[0]; | ||
const fallbackActivityPath = fallbackActivityRoute == null ? void 0 : fallbackActivityRoute.path; | ||
return [ | ||
@@ -339,6 +365,6 @@ (0, import_core.makeEvent)("Pushed", { | ||
const rootActivity = getStack().activities[0]; | ||
const template = makeTemplate( | ||
normalizeRoute(options.routes[rootActivity.name])[0], | ||
options.urlPatternOptions | ||
const match = activityRoutes.find( | ||
(r) => r.activityName === rootActivity.name | ||
); | ||
const template = makeTemplate(match, options.urlPatternOptions); | ||
const lastStep = last(rootActivity.steps); | ||
@@ -455,6 +481,6 @@ requestHistoryTick( | ||
} | ||
const template = makeTemplate( | ||
normalizeRoute(options.routes[activity.name])[0], | ||
options.urlPatternOptions | ||
const match = activityRoutes.find( | ||
(r) => r.activityName === activity.name | ||
); | ||
const template = makeTemplate(match, options.urlPatternOptions); | ||
requestHistoryTick(() => { | ||
@@ -477,6 +503,6 @@ silentFlag = true; | ||
} | ||
const template = makeTemplate( | ||
normalizeRoute(options.routes[activity.name])[0], | ||
options.urlPatternOptions | ||
const match = activityRoutes.find( | ||
(r) => r.activityName === activity.name | ||
); | ||
const template = makeTemplate(match, options.urlPatternOptions); | ||
requestHistoryTick(() => { | ||
@@ -499,6 +525,6 @@ silentFlag = true; | ||
} | ||
const template = makeTemplate( | ||
normalizeRoute(options.routes[activity.name])[0], | ||
options.urlPatternOptions | ||
const match = activityRoutes.find( | ||
(r) => r.activityName === activity.name | ||
); | ||
const template = makeTemplate(match, options.urlPatternOptions); | ||
requestHistoryTick(() => { | ||
@@ -520,6 +546,6 @@ silentFlag = true; | ||
} | ||
const template = makeTemplate( | ||
normalizeRoute(options.routes[activity.name])[0], | ||
options.urlPatternOptions | ||
const match = activityRoutes.find( | ||
(r) => r.activityName === activity.name | ||
); | ||
const template = makeTemplate(match, options.urlPatternOptions); | ||
requestHistoryTick(() => { | ||
@@ -539,6 +565,6 @@ silentFlag = true; | ||
onBeforePush({ actionParams, actions: { overrideActionParams } }) { | ||
const template = makeTemplate( | ||
normalizeRoute(options.routes[actionParams.activityName])[0], | ||
options.urlPatternOptions | ||
const match = activityRoutes.find( | ||
(r) => r.activityName === actionParams.activityName | ||
); | ||
const template = makeTemplate(match, options.urlPatternOptions); | ||
const path = template.fill(actionParams.activityParams); | ||
@@ -555,6 +581,6 @@ overrideActionParams(__spreadProps(__spreadValues({}, actionParams), { | ||
}) { | ||
const template = makeTemplate( | ||
normalizeRoute(options.routes[actionParams.activityName])[0], | ||
options.urlPatternOptions | ||
const match = activityRoutes.find( | ||
(r) => r.activityName === actionParams.activityName | ||
); | ||
const template = makeTemplate(match, options.urlPatternOptions); | ||
const path = template.fill(actionParams.activityParams); | ||
@@ -561,0 +587,0 @@ overrideActionParams(__spreadProps(__spreadValues({}, actionParams), { |
@@ -0,1 +1,2 @@ | ||
import type { Route } from "./RouteLike"; | ||
/** | ||
@@ -13,9 +14,10 @@ * import { UrlPatternOptions } from "url-pattern" | ||
} | ||
export declare function makeTemplate(templateStr: string, urlPatternOptions?: UrlPatternOptions): { | ||
export declare function makeTemplate<T>({ path, decode }: Route<T>, urlPatternOptions?: UrlPatternOptions): { | ||
fill(params: { | ||
[key: string]: string | undefined; | ||
}): string; | ||
parse<T extends { | ||
parse<T_1 extends { | ||
[key: string]: string | undefined; | ||
}>(path: string): T | null; | ||
}>(path: string): T_1 | null; | ||
variableCount: any; | ||
}; |
/// <reference types="react" /> | ||
export declare type RoutesMap = { | ||
[activityName in string]?: string | string[]; | ||
}; | ||
export declare const RoutesContext: import("react").Context<RoutesMap>; | ||
interface RoutesProviderProps { | ||
routes: RoutesMap; | ||
import type { ActivityRoute } from "./ActivityRoute"; | ||
export declare const RoutesContext: import("react").Context<ActivityRoute<unknown>[]>; | ||
interface RoutesProviderProps<T> { | ||
routes: ActivityRoute<T>[]; | ||
children: React.ReactNode; | ||
} | ||
export declare const RoutesProvider: React.FC<RoutesProviderProps>; | ||
export declare function useRoutes(): RoutesMap; | ||
export declare const RoutesProvider: { | ||
<T>(props: RoutesProviderProps<T>): JSX.Element; | ||
displayName: string; | ||
}; | ||
export declare function useRoutes(): ActivityRoute<unknown>[]; | ||
export {}; |
{ | ||
"name": "@stackflow/plugin-history-sync", | ||
"version": "1.3.18", | ||
"version": "1.4.0-canary.0", | ||
"license": "MIT", | ||
@@ -46,3 +46,3 @@ "exports": { | ||
"@stackflow/eslint-config": "^1.0.2", | ||
"@stackflow/react": "^1.1.7", | ||
"@stackflow/react": "^1.1.8-canary.0", | ||
"@swc/core": "^1.3.35", | ||
@@ -72,3 +72,3 @@ "@swc/jest": "^0.2.24", | ||
"@stackflow/core": "^1.0.10", | ||
"@stackflow/react": "^1.1.7", | ||
"@stackflow/react": "^1.1.8-canary.0", | ||
"@types/react": ">=16.8.0", | ||
@@ -75,0 +75,0 @@ "react": ">=16.8.0" |
export { useHistoryTick } from "./HistoryQueueContext"; | ||
export * from "./historySyncPlugin"; | ||
export { makeTemplate, UrlPatternOptions } from "./makeTemplate"; | ||
export { normalizeRoute } from "./normalizeRoute"; | ||
export { useRoutes } from "./RoutesContext"; |
import { makeTemplate } from "./makeTemplate"; | ||
test("makeTemplate - 패스 파라미터만 있을 때는 패스 파라미터로 붙입니다", () => { | ||
const template = makeTemplate("/articles/:articleId"); | ||
const template = makeTemplate({ path: "/articles/:articleId" }); | ||
@@ -14,3 +14,3 @@ expect( | ||
test("makeTemplate - 패스 파라미터에 추가 파라미터가 주어질 때는 쿼리 파라미터로 붙입니다", () => { | ||
const template = makeTemplate("/articles/:articleId"); | ||
const template = makeTemplate({ path: "/articles/:articleId" }); | ||
@@ -26,3 +26,3 @@ expect( | ||
test("makeTemplate - 추가 파라미터만 있을 때는 모두 쿼리 파라미터로 붙입니다", () => { | ||
const template = makeTemplate("/home/"); | ||
const template = makeTemplate({ path: "/home/" }); | ||
@@ -38,3 +38,3 @@ expect( | ||
test("makeTemplate - 패스가 같으면 빈 객체를 내려줍니다", () => { | ||
const template = makeTemplate("/articles/"); | ||
const template = makeTemplate({ path: "/articles/" }); | ||
@@ -45,3 +45,3 @@ expect(template.parse("/articles/")).toStrictEqual({}); | ||
test("makeTemplate - 패스가 다르면 null을 내려줍니다", () => { | ||
const template = makeTemplate("/articles/"); | ||
const template = makeTemplate({ path: "/articles/" }); | ||
@@ -52,3 +52,3 @@ expect(template.parse("/not-articles/")).toEqual(null); | ||
test("makeTemplate - 패스 파라미터와 쿼리 파라미터를 적절하게 파싱합니다", () => { | ||
const template = makeTemplate("/articles/:articleId"); | ||
const template = makeTemplate({ path: "/articles/:articleId" }); | ||
@@ -62,3 +62,3 @@ expect(template.parse("/articles/1234/?title=hello")).toStrictEqual({ | ||
test("makeTemplate - 패스 파라미터에 `undefined` 값이 포함된 경우 삭제합니다", () => { | ||
const template = makeTemplate("/articles"); | ||
const template = makeTemplate({ path: "/articles" }); | ||
@@ -72,1 +72,14 @@ expect( | ||
}); | ||
test("makeTemplate - parse with given decode function", () => { | ||
const template = makeTemplate({ | ||
path: "/articles/:articleId", | ||
decode: ({ articleId }) => ({ | ||
articleId: Number(articleId), | ||
}), | ||
}); | ||
expect(template.parse("/articles/1234")).toStrictEqual({ | ||
articleId: 1234, | ||
}); | ||
}); |
import UrlPattern from "url-pattern"; | ||
import type { Route } from "./RouteLike"; | ||
function pathToUrl(path: string) { | ||
@@ -46,7 +48,7 @@ return new URL(path, "file://"); | ||
export function makeTemplate( | ||
templateStr: string, | ||
export function makeTemplate<T>( | ||
{ path, decode }: Route<T>, | ||
urlPatternOptions?: UrlPatternOptions, | ||
) { | ||
const pattern = new UrlPattern(`${templateStr}(/)`, urlPatternOptions); | ||
const pattern = new UrlPattern(`${path}(/)`, urlPatternOptions); | ||
@@ -96,6 +98,7 @@ return { | ||
...searchParams, | ||
...pathParams, | ||
...(decode ? decode(pathParams) : pathParams), | ||
}; | ||
}, | ||
variableCount: (pattern as any).names.length, | ||
}; | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
179672
46
3355
2