diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 341af3d..3fcdf74 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,10 +11,10 @@ jobs: - uses: actions/checkout@v3 - uses: pnpm/action-setup@v2 with: - version: 7 + version: 8 - uses: actions/setup-node@v3 with: - node-version: 16.x + node-version: 18.x cache: "pnpm" - run: pnpm install --frozen-lockfile diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index dbca311..d5cc90a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,10 +18,10 @@ jobs: - uses: actions/checkout@v3 - uses: pnpm/action-setup@v2 with: - version: 7 + version: 8 - uses: actions/setup-node@v3 with: - node-version: 16.x + node-version: 18.x cache: "pnpm" - run: pnpm install --frozen-lockfile diff --git a/package.json b/package.json index eab4acc..1f09db7 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ }, "scripts": { "demo": "pnpm run --filter @favy/router-demo dev", + "lint": "pnpm run -r lint", + "build": "pnpm run -r build", "pub": "tsx ./bin/publish.mts" }, "devDependencies": { diff --git a/packages/demo/package.json b/packages/demo/package.json index 86535b0..f29bd57 100644 --- a/packages/demo/package.json +++ b/packages/demo/package.json @@ -5,13 +5,13 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc && vite build", + "build": "vite build", "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, "dependencies": { - "@favy/wayfind": "0.0.2", - "@favy/wayfind-react": "0.0.1", + "@favy/wayfind": "workspace:*", + "@favy/wayfind-react": "workspace:*", "scheduler": "^0.23.0", "type-fest": "^4.4.0" }, diff --git a/packages/demo/src/main.tsx b/packages/demo/src/main.tsx index c6ead65..87f6fc7 100644 --- a/packages/demo/src/main.tsx +++ b/packages/demo/src/main.tsx @@ -5,6 +5,7 @@ import { createRoot } from "react-dom/client"; import { useIsActive, useNavigate, Link } from "./routerComponent"; import { router } from "./router"; +// eslint-disable-next-line react-refresh/only-export-components const Head = () => { const isActiveRoot = useIsActive("/"); const go = useNavigate(); @@ -30,6 +31,7 @@ const Head = () => { ); }; +// eslint-disable-next-line react-refresh/only-export-components function App() { return ( diff --git a/packages/demo/src/router.tsx b/packages/demo/src/router.tsx index e0bce73..cc45773 100644 --- a/packages/demo/src/router.tsx +++ b/packages/demo/src/router.tsx @@ -33,7 +33,8 @@ const root = route({ /> ), - "/logs/{page}": (ctx) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + "/logs/{page}": (ctx: any) => { throw `random error ${ctx.page} ` + Date.now(); }, error: ({ error }) =>
Throw error: {`${error}`}
, @@ -43,4 +44,5 @@ const root = route({ 404: () =>
Not found
, }); +// eslint-disable-next-line react-refresh/only-export-components export const router = createRouter(root); diff --git a/packages/wayfind-react/src/components/Link.tsx b/packages/wayfind-react/src/components/Link.tsx index 8eec6a1..b9ca049 100644 --- a/packages/wayfind-react/src/components/Link.tsx +++ b/packages/wayfind-react/src/components/Link.tsx @@ -27,9 +27,11 @@ export const makeLink = (config: ConfProps["Link"] = { active: {}, default: {} } const isActive = ctx.router?.isActive(url); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { to: _to, vars: _vars, ...restProps } = props as any; const linkProps: any = { ...(config.default ?? {}), - ...props, + ...restProps, }; if (isActive && config.active) { diff --git a/packages/wayfind-react/src/components/Redirect.tsx b/packages/wayfind-react/src/components/Redirect.tsx index 0b4c530..f1001be 100644 --- a/packages/wayfind-react/src/components/Redirect.tsx +++ b/packages/wayfind-react/src/components/Redirect.tsx @@ -11,7 +11,7 @@ export const Redirect = ({ to, vars }: ToRouteProps) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error ctx?.go(to, vars); - }); + }, [to, vars]); return <>; }; diff --git a/packages/wayfind-react/src/components/getComponents.tsx b/packages/wayfind-react/src/components/getComponents.tsx index 4892051..2b19125 100644 --- a/packages/wayfind-react/src/components/getComponents.tsx +++ b/packages/wayfind-react/src/components/getComponents.tsx @@ -14,7 +14,7 @@ export interface ConfProps { } export const getComponents = , C extends RouteType>( - router: T, + _router: T, config: ConfProps ) => { type RedirectProps = { [k in T["paths"]]: ToRouteProps> }[T["paths"]]; diff --git a/packages/wayfind-react/src/hooks/useNavigate.ts b/packages/wayfind-react/src/hooks/useNavigate.ts index b64497f..d37511b 100644 --- a/packages/wayfind-react/src/hooks/useNavigate.ts +++ b/packages/wayfind-react/src/hooks/useNavigate.ts @@ -7,7 +7,7 @@ export const makeNavigateHook =

() => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (props: P) => ctx?.go(props.url, props.vars); + return (props: P) => ctx?.go((props as any).to, (props as any).vars); }; return useNavigate; diff --git a/packages/wayfind/src/Parsers.ts b/packages/wayfind/src/Parsers.ts index e9e8e5d..9f5b473 100644 --- a/packages/wayfind/src/Parsers.ts +++ b/packages/wayfind/src/Parsers.ts @@ -24,7 +24,7 @@ export const parsers = { number: (value: string) => Number(value), bigint: (value: string) => BigInt(value), string: (value: string) => value, - boolean: (value: string) => Boolean(value), + boolean: (value: string) => value === "true", }; export type Parsers = { diff --git a/packages/wayfind/src/createRouter.ts b/packages/wayfind/src/createRouter.ts index 1d176b3..287638a 100644 --- a/packages/wayfind/src/createRouter.ts +++ b/packages/wayfind/src/createRouter.ts @@ -32,8 +32,7 @@ export const createRouter = >(config: C) => window.addEventListener("popstate", handleChange); handleChange(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - type RemoveType = S extends `${infer L}{${infer N}:${infer T}}${infer R}` + type RemoveType = S extends `${infer L}{${infer N}:${infer _T}}${infer R}` ? `${L}{${N}}${RemoveType}` : S; @@ -59,6 +58,9 @@ export const createRouter = >(config: C) => isActive(path: string) { return findRoute(routes, path)?.route === currentRoute?.route; }, + destroy() { + window.removeEventListener("popstate", handleChange); + }, } as any as RouterInstance>>>; }; @@ -78,27 +80,24 @@ export type RouterInstance, P extends string, V> = match(path: string): unknown; isActive(path: string): boolean; paths: P; + destroy(): void; }; export const compileRoute = >(route: C) => { const routeMap: any = flatRoute(route, ""); const urls = Object.keys(routeMap); - let errorRender: any; return urls.map((k) => { const varTypes = new Map(); - const regString = k.replace(/[\s!#$()+,.:<=?[\\\]^|]/g, "\\$&").replace(/\{(.+?)\}/g, (_, name) => { + const errorRender = routeMap[k].error ?? findErrorRender(route, k); + const regString = k.replace(/[\s!#$()*+,.\-:<=?[\\\]^|]/g, "\\$&").replace(/\\\{(.+?)\\\}/g, (_, name) => { const [varName, varType] = name.split("\\:"); varTypes.set(varName, varType); - return `(?<${varName}>.+)`; + return `(?<${varName}>[^/?#]+)`; }); const regEx = new RegExp("^" + regString + "$", "gi"); - if (routeMap[k].error) { - errorRender = routeMap[k].error; - } - return { parse: (path: string) => { const res = regEx.exec(path); @@ -125,20 +124,26 @@ export const compileRoute = >(route: C) => { }); }; -const findRoute = (routes: R, path: string) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - for (const r of routes) { - const res = r.parse(path); - if (res) return res; +const findErrorRender = (route: RouteType, path: string): any => { + const segments = path.split("/").filter(Boolean); + let current: any = route; + + for (const segment of segments) { + const key = "/" + segment; + if (current[key] && typeof current[key] === "object") { + if (current[key].error) return current[key].error; + current = current[key]; + } } + + return (route as any).error; }; -export const isActive = (routes: R, path: string) => { +const findRoute = (routes: R, path: string) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error for (const r of routes) { - const res = r.isActive(path); + const res = r.parse(path); if (res) return res; } }; @@ -149,10 +154,15 @@ const flatRoute = (route: RouteType, prefix = "", res: Record { if (!vars) return path; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - return path.replace(/\{(.+?)\}/g, (_, name) => vars[name]); + return path.replace(/\{(.+?)\}/g, (_, name) => vars[name.split(":")[0]]); }; diff --git a/packages/wayfind/src/index.ts b/packages/wayfind/src/index.ts index acaa86b..fcdaab6 100644 --- a/packages/wayfind/src/index.ts +++ b/packages/wayfind/src/index.ts @@ -1,5 +1,5 @@ export * from "./createRouter"; export * from "./ExtractVars"; export * from "./RouteType"; -export * from "./ExtractVars"; +export * from "./Path"; export * from "./route"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be8a4b3..f474276 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,10 +17,10 @@ importers: packages/demo: dependencies: '@favy/wayfind': - specifier: 0.0.2 + specifier: workspace:* version: link:../wayfind '@favy/wayfind-react': - specifier: 0.0.1 + specifier: workspace:* version: link:../wayfind-react react: specifier: ^18.2.0 diff --git a/tsconfig.json b/tsconfig.json index bfb42d1..081f855 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2016", + "target": "es2020", "module": "commonjs", "esModuleInterop": true, "forceConsistentCasingInFileNames": true,