diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..36af2198 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx lint-staged diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 27355324..00000000 --- a/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "printWidth": 90, - "semi": true, -} diff --git a/README.md b/README.md index 0f7c0767..3bcac9ff 100644 --- a/README.md +++ b/README.md @@ -76,9 +76,9 @@ $ npm i @cher-ami/router -s ## Simple usage ```jsx -import React from "react"; -import { Router, Link, Stack } from "@cher-ami/router"; -import { createBrowserHistory } from "history"; +import React from "react" +import { Router, Link, Stack } from "@cher-ami/router" +import { createBrowserHistory } from "history" const routesList = [ { @@ -89,9 +89,9 @@ const routesList = [ path: "/foo", component: FooPage, }, -]; +] -const history = createBrowserHistory(); +const history = createBrowserHistory() function App() { return ( @@ -102,7 +102,7 @@ function App() { - ); + ) } ``` @@ -110,34 +110,34 @@ Page component need to be wrapped by `React.forwardRef`. The `handleRef` lets hold transitions, and ref used by `` component. ```jsx -import React from "react"; -import { useStack } from "@cher-ami/router"; +import React from "react" +import { useStack } from "@cher-ami/router" const FooPage = forwardRef((props, handleRef) => { - const componentName = "FooPage"; - const rootRef = useRef(null); + const componentName = "FooPage" + const rootRef = useRef(null) // create custom page transitions (example-client with GSAP) const playIn = () => { return new Promise((resolve) => { - gsap.from(rootRef.current, { autoAlpha: 0, onComplete: resolve }); - }); - }; + gsap.from(rootRef.current, { autoAlpha: 0, onComplete: resolve }) + }) + } const playOut = () => { return new Promise((resolve) => { - gsap.to(rootRef.current, { autoAlpha: 0, onComplete: resolve }); - }); - }; + gsap.to(rootRef.current, { autoAlpha: 0, onComplete: resolve }) + }) + } // register page transition properties used by Stack component - useStack({ componentName, handleRef, rootRef, playIn, playOut }); + useStack({ componentName, handleRef, rootRef, playIn, playOut }) return (
{componentName}
- ); -}); + ) +}) ``` ## Dynamic routes @@ -153,28 +153,28 @@ const routesList = [ path: "/blog/:id", component: ArticlePage, }, -]; +] ``` You can access route parameters by page component props or by `useRouter()` hook. ```jsx -import React, { useEffect, forwardRef } from "react"; -import { useRoute } from "@cher-ami/router"; +import React, { useEffect, forwardRef } from "react" +import { useRoute } from "@cher-ami/router" const ArticlePage = forwardRef((props, handleRef) => { useEffect(() => { - console.log(props.params); // { id: "my-article" } - }, [props]); + console.log(props.params) // { id: "my-article" } + }, [props]) // or from any nested components - const { currentRoute } = useRouter(); + const { currentRoute } = useRouter() useEffect(() => { - console.log(currentRoute.props.params); // { id: "my-article" } - }, [currentRoute]); + console.log(currentRoute.props.params) // { id: "my-article" } + }, [currentRoute]) // ... -}); +}) ``` Also, it is possible to match a specific route by a simple dynamic route @@ -197,7 +197,7 @@ const routesList = [ path: "/:rest", component: NotFoundPage, }, -]; +] ``` ## Sub-router @@ -229,7 +229,7 @@ const routesList = [ }, ], }, -]; +] ``` 2. Children were defined within the route that render `FooPage` component, so @@ -238,7 +238,7 @@ const routesList = [ 3. The new subRouter needs his own base and routes list, `getSubRouterBase` and `getSubRouterRoutes` functions are available to get them. ```jsx -import React from "react"; +import React from "react" import { Router, useStack, @@ -247,24 +247,24 @@ import { getPathByRouteName, getSubRouterBase, getSubRouterRoutes, -} from "@cher-ami/router"; +} from "@cher-ami/router" const FooPage = forwardRef((props, handleRef) => { - const router = useRouter(); + const router = useRouter() // Parsed routes list and get path by route name -> "/foo" - const path = getPathByRouteName(router.routes, "FooPage"); + const path = getPathByRouteName(router.routes, "FooPage") // (if last param is false, '/:lang' will be not added) -> "/base/:lang/foo" - const subBase = getSubRouterBase(path, router.base, true); + const subBase = getSubRouterBase(path, router.base, true) // get subRoutes - const subRoutes = getSubRouterRoutes(path, router.routes); + const subRoutes = getSubRouterRoutes(path, router.routes) return (
- ); -}); + ) +}) ``` ## Manage transitions @@ -280,29 +280,29 @@ previous page play out performs, then the new page play in. ```js const sequencialTransition = ({ previousPage, currentPage, unmountPreviousPage }) => { return new Promise(async (resolve) => { - const $current = currentPage?.$element; + const $current = currentPage?.$element // hide new page - if ($current) $current.style.visibility = "hidden"; + if ($current) $current.style.visibility = "hidden" // play out and unmount previous page if (previousPage) { - await previousPage.playOut(); - unmountPreviousPage(); + await previousPage.playOut() + unmountPreviousPage() } // wait page isReady promise - await currentPage?.isReadyPromise?.(); + await currentPage?.isReadyPromise?.() // show and play in new page if (currentPage) { - if ($current) $current.style.visibility = "visible"; - await currentPage?.playIn(); + if ($current) $current.style.visibility = "visible" + await currentPage?.playIn() } - resolve(); - }); -}; + resolve() + }) +} ``` ### Custom transitions @@ -317,18 +317,18 @@ const App = (props, handleRef) => { const customSenario = ({ previousPage, currentPage, unmountPreviousPage }) => { return new Promise(async (resolve) => { // write a custom "crossed" senario... - if (previousPage) previousPage?.playOut(); - if (currentPage) await currentPage?.playIn(); + if (previousPage) previousPage?.playOut() + if (currentPage) await currentPage?.playIn() - resolve(); - }); - }; + resolve() + }) + } return ( // ... - ); -}; + ) +} ``` ## SSR Support @@ -370,7 +370,7 @@ Then, get the response data populated in page component props: ```tsx function HomePage({ api }) { - return
{api.title}
; + return
{api.title}
} ``` @@ -464,18 +464,18 @@ Render previous and current page component. ```ts type TManageTransitions = { - previousPage: IRouteStack; - currentPage: IRouteStack; - unmountPreviousPage: () => void; -}; + previousPage: IRouteStack + currentPage: IRouteStack + unmountPreviousPage: () => void +} interface IRouteStack { - componentName: string; - playIn: () => Promise; - playOut: () => Promise; - isReady: boolean; - $element: HTMLElement; - isReadyPromise: () => Promise; + componentName: string + playIn: () => Promise + playOut: () => Promise + isReady: boolean + $element: HTMLElement + isReadyPromise: () => Promise } ``` @@ -484,7 +484,7 @@ interface IRouteStack { Get current router informations: ```jsx -const router = useRouter(); +const router = useRouter() ``` **Returns:** @@ -501,23 +501,23 @@ const router = useRouter(); ```ts // previousRoute and currentRoute type TRoute = Partial<{ - path: string | { [x: string]: string }; - component: React.ComponentType; - base: string; - name: string; - parser: Match; - props: TRouteProps; - children: TRoute[]; - url: string; - params?: TParams; - queryParams?: TQueryParams; - hash?: string; - getStaticProps: (props: TRouteProps, currentLang: TLanguage) => Promise; - _fullUrl: string; // full URL who not depends on current instance - _fullPath: string; // full Path /base/:lang/foo/second-foo - _langPath: { [x: string]: string } | null; - _context: TRoute; -}>; + path: string | { [x: string]: string } + component: React.ComponentType + base: string + name: string + parser: Match + props: TRouteProps + children: TRoute[] + url: string + params?: TParams + queryParams?: TQueryParams + hash?: string + getStaticProps: (props: TRouteProps, currentLang: TLanguage) => Promise + _fullUrl: string // full URL who not depends on current instance + _fullPath: string // full Path /base/:lang/foo/second-foo + _langPath: { [x: string]: string } | null + _context: TRoute +}> ``` ### useLocation @@ -525,11 +525,11 @@ type TRoute = Partial<{ Allow the router to change location. ```jsx -const [location, setLocation] = useLocation(); +const [location, setLocation] = useLocation() // give URL -setLocation("/bar"); +setLocation("/bar") // or an object -setLocation({ name: "FooPage", params: { id: "2" } }); +setLocation({ name: "FooPage", params: { id: "2" } }) ``` **Returns:** @@ -541,11 +541,11 @@ An array with these properties: ```ts type TOpenRouteParams = { - name: string; - params?: TParams; - queryParams?: TQueryParams; - hash?: string; -}; + name: string + params?: TParams + queryParams?: TQueryParams + hash?: string +} ``` ### useStack @@ -589,14 +589,14 @@ is executed. ```jsx // ... -const [pageIsReady, setPageIsReady] = useState(false); +const [pageIsReady, setPageIsReady] = useState(false) useEffect(() => { // simulate data fetching or whatever for 2 seconds setTimeout(() => { - setPageIsReady(true); - }, 2000); -}, []); + setPageIsReady(true) + }, 2000) +}, []) useStack({ componentName, @@ -607,7 +607,7 @@ useStack({ // add the state to useStack // playIn function wait for isReady to change to true isReady: pageIsReady, -}); +}) // ... ``` @@ -621,11 +621,11 @@ const customManageTransitions = ({ previousPage, currentPage, unmountPreviousPag return new Promise(async (resolve) => { // ... // waiting for page "isReady" state to change to continue... - await currentPage?.isReadyPromise?.(); + await currentPage?.isReadyPromise?.() // ... - resolve(); - }); -}; + resolve() + }) +} ``` **[Demo codesandbox: wait-is-ready](https://codesandbox.io/s/wait-isready-6irps?file=/src/pages/AboutPage.tsx)** @@ -650,7 +650,7 @@ nothing Returns route counter ```js -const { routeCounter, isFirstRoute, resetCounter } = useRouteCounter(); +const { routeCounter, isFirstRoute, resetCounter } = useRouteCounter() ``` **Parameters:** @@ -673,7 +673,7 @@ change. ```js const history = useHistory((e) => { // do something -}); +}) ``` **Parameters:** @@ -690,16 +690,16 @@ const history = useHistory((e) => { Get and update langService current language object. ```tsx -const [lang, setLang] = useLang(); +const [lang, setLang] = useLang() useEffect(() => { // when current lang change // it's usefull only if setLang method do not refresh the page. -}, [lang]); +}, [lang]) // set new lang with lang object "key" property value only -setLang("en"); +setLang("en") // set new lang with the lang object -setLang({ key: "en" }); +setLang({ key: "en" }) ``` **Returns:** @@ -714,13 +714,13 @@ Array of : Manage `:lang` params from anywhere inside Router scope. ```jsx -import { LangService } from "@cher-ami/router"; -import { Stack } from "./Stack"; +import { LangService } from "@cher-ami/router" +import { Stack } from "./Stack" -const base = "/"; +const base = "/" // first lang object is default lang -const languages = [{ key: "en" }, { key: "fr" }, { key: "de" }]; +const languages = [{ key: "en" }, { key: "fr" }, { key: "de" }] // optionally, default lang can be defined explicitly // const languages = [{ key: "en" }, { key: "fr", default: true }, { key: "de" }]; @@ -729,11 +729,11 @@ const langService = new LangService({ languages, showDefaultLangInUrl: true, base, -}); +}) - +; -; + ``` Inside the App @@ -741,7 +741,7 @@ Inside the App ```jsx function App() { // get langService instance from router context - const { langService } = useRouter(); + const { langService } = useRouter() return (
@@ -756,7 +756,7 @@ function App() {
- ); + ) } ``` @@ -777,16 +777,16 @@ const langService = new LangService({ languages: [{ key: "en" }, { key: "fr" }], showDefaultLangInUrl: true, base: "/", -}); +}) ``` `langService` instance is available in Router scope from `useRouter()` hook. ```tsx const Page = () => { - const { langService } = useRouter(); + const { langService } = useRouter() // langService.setLang() ... -}; +} ``` #### languages `Tlanguage[]` @@ -794,7 +794,7 @@ const Page = () => { Return languages list ```jsx -const langages = langService.languages; +const langages = langService.languages ``` #### currentLang `TLanguage` @@ -802,7 +802,7 @@ const langages = langService.languages; Return current Language object. ```jsx -const lang = langService.currentLang; +const lang = langService.currentLang // { key: "..." } ``` @@ -811,7 +811,7 @@ const lang = langService.currentLang; Return default language object ```jsx -const defaultLang = langService.defaultLang; +const defaultLang = langService.defaultLang // { key: "..." } ``` @@ -820,7 +820,7 @@ const defaultLang = langService.defaultLang; Return langService init state ```jsx -const isInit = langService.isInit; +const isInit = langService.isInit ``` #### setLang(toLang: TLanguage, forcePageReload = true) `void` @@ -832,7 +832,7 @@ component only. internal router stack to change the language ```jsx -langService.setLang({ key: "de" }); +langService.setLang({ key: "de" }) ``` #### redirectToDefaultLang(forcePageReload = true) `void` @@ -844,7 +844,7 @@ it will redirect to `/en`. internal router stack to change the language ```js -langService.redirectToDefaultLang(); +langService.redirectToDefaultLang() ``` #### redirectToBrowserLang(forcePageReload = true) `void` @@ -853,7 +853,7 @@ Same than `redirectToDefaultLang` method but redirect to the user `navigator.lan If the browser language doesn't exist in Languages array, we redirect to the default lang. ```js -langService.redirectToBrowserLang(); +langService.redirectToBrowserLang() ``` ### Translate Path diff --git a/examples/example-client/index.html b/examples/example-client/index.html index e394cd18..c3baacb8 100644 --- a/examples/example-client/index.html +++ b/examples/example-client/index.html @@ -1,4 +1,4 @@ - + diff --git a/examples/example-client/src/App.tsx b/examples/example-client/src/App.tsx index c312957a..0427425f 100644 --- a/examples/example-client/src/App.tsx +++ b/examples/example-client/src/App.tsx @@ -1,13 +1,13 @@ -import React from "react"; -import { Link, Stack, TManageTransitions, useLang, useLocation }from "@cher-ami/router"; -const componentName = "App"; +import React from "react" +import { Link, Stack, TManageTransitions, useLang, useLocation } from "@cher-ami/router" +const componentName = "App" /** * @name App */ export default function App() { - const [lang, setLang] = useLang(); - const [location, setLocation] = useLocation(); + const [lang, setLang] = useLang() + const [location, setLocation] = useLocation() const customSenario = ({ previousPage, @@ -15,19 +15,19 @@ export default function App() { unmountPreviousPage, }: TManageTransitions): Promise => { return new Promise(async (resolve) => { - const $currentPageElement = currentPage?.$element; + const $currentPageElement = currentPage?.$element if ($currentPageElement) { - $currentPageElement.style.visibility = "hidden"; + $currentPageElement.style.visibility = "hidden" } - if (previousPage) previousPage.playOut(); - await currentPage?.isReadyPromise(); + if (previousPage) previousPage.playOut() + await currentPage?.isReadyPromise() if ($currentPageElement) { - $currentPageElement.style.visibility = "visible"; + $currentPageElement.style.visibility = "visible" } - await currentPage.playIn(); - resolve(); - }); - }; + await currentPage.playIn() + resolve() + }) + } return (
@@ -36,7 +36,7 @@ export default function App() { key={i} children={el} onClick={() => { - setLang(el, true); + setLang(el, true) }} /> ))} @@ -46,7 +46,9 @@ export default function App() { Home
  • - About + + About +
  • @@ -57,5 +59,5 @@ export default function App() {
  • - ); + ) } diff --git a/examples/example-client/src/helper/transitionsHelper.ts b/examples/example-client/src/helper/transitionsHelper.ts index 48a43d92..4d9d8fae 100644 --- a/examples/example-client/src/helper/transitionsHelper.ts +++ b/examples/example-client/src/helper/transitionsHelper.ts @@ -1,16 +1,16 @@ -import { gsap } from "gsap"; -import debug from "@wbe/debug"; -const log = debug(`router:transitionsHelper`); +import { gsap } from "gsap" +import debug from "@wbe/debug" +const log = debug(`router:transitionsHelper`) export const transitionsHelper = ( el, show: boolean, from: any = {}, - to: any = {} + to: any = {}, ): Promise => { return new Promise((resolve) => { if (!el) { - log("el doesnt exist", el); + log("el doesnt exist", el) } gsap.fromTo( @@ -23,7 +23,7 @@ export const transitionsHelper = ( autoAlpha: show ? 1 : 0, ease: "power1.out", onComplete: resolve, - } - ); - }); -}; + }, + ) + }) +} diff --git a/examples/example-client/src/index.css b/examples/example-client/src/index.css index f5678e81..b77a96ff 100644 --- a/examples/example-client/src/index.css +++ b/examples/example-client/src/index.css @@ -42,5 +42,5 @@ button:hover { padding-left: 2rem; position: absolute; width: 100%; - top:0; + top: 0; } diff --git a/examples/example-client/src/index.tsx b/examples/example-client/src/index.tsx index eff53f46..fcb302ed 100644 --- a/examples/example-client/src/index.tsx +++ b/examples/example-client/src/index.tsx @@ -1,24 +1,24 @@ -import { createRoot } from "react-dom/client"; -import React from "react"; -import "./index.css"; -import App from "./App"; -import { Router, LangService } from "@cher-ami/router"; -import { routesList } from "./routes"; -import { createBrowserHistory } from "history"; +import { createRoot } from "react-dom/client" +import React from "react" +import "./index.css" +import App from "./App" +import { Router, LangService } from "@cher-ami/router" +import { routesList } from "./routes" +import { createBrowserHistory } from "history" -const base = "/base/"; -type TLang = "en" | "fr" | "de"; +const base = "/base/" +type TLang = "en" | "fr" | "de" const langService = new LangService({ languages: [{ key: "en" }, { key: "fr" }, { key: "de" }], showDefaultLangInUrl: false, base, -}); +}) /** * Init Application */ -const root = createRoot(document.getElementById("root")); +const root = createRoot(document.getElementById("root")) root.render( - -); + , +) diff --git a/examples/example-client/src/pages/AboutPage.tsx b/examples/example-client/src/pages/AboutPage.tsx index 94e01c34..f7196d72 100644 --- a/examples/example-client/src/pages/AboutPage.tsx +++ b/examples/example-client/src/pages/AboutPage.tsx @@ -1,4 +1,4 @@ -import React, { ForwardedRef, forwardRef, useRef } from "react"; +import React, { ForwardedRef, forwardRef, useRef } from "react" import { getPathByRouteName, getSubRouterBase, @@ -8,14 +8,14 @@ import { Router, useRouter, useStack, -} from "@cher-ami/router"; -import { transitionsHelper } from "../helper/transitionsHelper"; -import { routesList } from "../routes"; +} from "@cher-ami/router" +import { transitionsHelper } from "../helper/transitionsHelper" +import { routesList } from "../routes" -const componentName: string = "AboutPage"; +const componentName: string = "AboutPage" const AboutPage = forwardRef((props, handleRef: ForwardedRef) => { - const rootRef = useRef(null); + const rootRef = useRef(null) useStack({ componentName, @@ -23,11 +23,11 @@ const AboutPage = forwardRef((props, handleRef: ForwardedRef) => { rootRef, playIn: () => transitionsHelper(rootRef.current, true, { x: -50 }, { x: 0 }), playOut: () => transitionsHelper(rootRef.current, false, { x: -0 }, { x: 50 }), - }); + }) // prepare routes & base for subRouter - const router = useRouter(); - const path = getPathByRouteName(routesList, "AboutPage"); + const router = useRouter() + const path = getPathByRouteName(routesList, "AboutPage") return (
    @@ -54,8 +54,8 @@ const AboutPage = forwardRef((props, handleRef: ForwardedRef) => {
    - ); -}); + ) +}) -AboutPage.displayName = componentName; -export default AboutPage; +AboutPage.displayName = componentName +export default AboutPage diff --git a/examples/example-client/src/pages/ArticlePage.tsx b/examples/example-client/src/pages/ArticlePage.tsx index 964cf56c..3ec3a875 100644 --- a/examples/example-client/src/pages/ArticlePage.tsx +++ b/examples/example-client/src/pages/ArticlePage.tsx @@ -1,27 +1,27 @@ -import React, {ForwardedRef, forwardRef, useEffect, useRef} from "react" -import { useLocation } from "@cher-ami/router"; -import { useStack } from "@cher-ami/router"; -import { transitionsHelper } from "../helper/transitionsHelper"; -import debug from "@wbe/debug"; +import React, { ForwardedRef, forwardRef, useEffect, useRef } from "react" +import { useLocation } from "@cher-ami/router" +import { useStack } from "@cher-ami/router" +import { transitionsHelper } from "../helper/transitionsHelper" +import debug from "@wbe/debug" interface IProps { params?: { - id: string; - }; + id: string + } time: { datetime: string - }; + } } -const componentName = "ArticlePage"; -const log = debug(`router:${componentName}`); +const componentName = "ArticlePage" +const log = debug(`router:${componentName}`) /** * @name ArticlePage */ export const ArticlePage = forwardRef((props: IProps, handleRef: ForwardedRef) => { - const rootRef = useRef(null); - const [location, setLocation] = useLocation(); + const rootRef = useRef(null) + const [location, setLocation] = useLocation() useStack({ componentName, @@ -29,12 +29,11 @@ export const ArticlePage = forwardRef((props: IProps, handleRef: ForwardedRef transitionsHelper(rootRef.current, true, { x: -50 }, { x: 0 }), playOut: () => transitionsHelper(rootRef.current, false, { x: -0 }, { x: 50 }), - }); + }) - useEffect(()=> - { - log("props.time",props.time) - },[props.time]) + useEffect(() => { + log("props.time", props.time) + }, [props.time]) return (
    fetch props datetime: {props.time?.datetime}
    @@ -44,7 +43,7 @@ export const ArticlePage = forwardRef((props: IProps, handleRef: ForwardedRef
    - ); -}); + ) +}) -ArticlePage.displayName = componentName; -export default ArticlePage; +ArticlePage.displayName = componentName +export default ArticlePage diff --git a/examples/example-client/src/pages/BarPage.tsx b/examples/example-client/src/pages/BarPage.tsx index 838758cd..08e542ca 100644 --- a/examples/example-client/src/pages/BarPage.tsx +++ b/examples/example-client/src/pages/BarPage.tsx @@ -1,4 +1,4 @@ -import React, { ForwardedRef, forwardRef, useRef } from "react"; +import React, { ForwardedRef, forwardRef, useRef } from "react" import { getPathByRouteName, getSubRouterBase, @@ -8,18 +8,18 @@ import { Router, Stack, useRouter, -} from "@cher-ami/router"; -import { transitionsHelper } from "../helper/transitionsHelper"; -import { routesList } from "../routes"; -import debug from "@wbe/debug"; +} from "@cher-ami/router" +import { transitionsHelper } from "../helper/transitionsHelper" +import { routesList } from "../routes" +import debug from "@wbe/debug" -const componentName: string = "BarPage"; -const log = debug(`router:${componentName}`); +const componentName: string = "BarPage" +const log = debug(`router:${componentName}`) interface IProps {} export const BarPage = forwardRef((props: IProps, handleRef: ForwardedRef) => { - const rootRef = useRef(null); + const rootRef = useRef(null) useStack({ componentName, @@ -27,12 +27,12 @@ export const BarPage = forwardRef((props: IProps, handleRef: ForwardedRef) rootRef, playIn: () => transitionsHelper(rootRef.current, true, { y: -20 }, { y: 0 }), playOut: () => transitionsHelper(rootRef.current, false, { y: -0 }, { y: 20 }), - }); + }) - const router = useRouter(); - const path = getPathByRouteName(router.routes, "BarPage"); - const subBase = getSubRouterBase(path, router.base); - const subRoutes = getSubRouterRoutes(path, router.routes); + const router = useRouter() + const path = getPathByRouteName(router.routes, "BarPage") + const subBase = getSubRouterBase(path, router.base) + const subRoutes = getSubRouterRoutes(path, router.routes) return (
    @@ -53,8 +53,8 @@ export const BarPage = forwardRef((props: IProps, handleRef: ForwardedRef)
    - ); -}); + ) +}) -BarPage.displayName = componentName; -export default BarPage; +BarPage.displayName = componentName +export default BarPage diff --git a/examples/example-client/src/pages/FooPage.tsx b/examples/example-client/src/pages/FooPage.tsx index 1dc61189..ca1f3b96 100644 --- a/examples/example-client/src/pages/FooPage.tsx +++ b/examples/example-client/src/pages/FooPage.tsx @@ -1,12 +1,12 @@ -import React, { ForwardedRef, forwardRef, useRef } from "react"; -import { Link, useStack } from "@cher-ami/router"; -import { transitionsHelper } from "../helper/transitionsHelper"; -const componentName: string = "FooPage"; +import React, { ForwardedRef, forwardRef, useRef } from "react" +import { Link, useStack } from "@cher-ami/router" +import { transitionsHelper } from "../helper/transitionsHelper" +const componentName: string = "FooPage" interface IProps {} const FooPage = forwardRef((props: IProps, handleRef: ForwardedRef) => { - const rootRef = useRef(null); + const rootRef = useRef(null) useStack({ componentName, @@ -16,7 +16,7 @@ const FooPage = forwardRef((props: IProps, handleRef: ForwardedRef) => { transitionsHelper(rootRef.current, true, { y: -50, autoAlpha: 1 }, { y: 0 }), playOut: () => transitionsHelper(rootRef.current, false, { y: -0 }, { y: 50, autoAlpha: 0 }), - }); + }) return (
    @@ -26,8 +26,8 @@ const FooPage = forwardRef((props: IProps, handleRef: ForwardedRef) => {
    About
    - ); -}); + ) +}) -FooPage.displayName = componentName; -export default FooPage; +FooPage.displayName = componentName +export default FooPage diff --git a/examples/example-client/src/pages/HelloPage.tsx b/examples/example-client/src/pages/HelloPage.tsx index b2840135..4d5d115b 100644 --- a/examples/example-client/src/pages/HelloPage.tsx +++ b/examples/example-client/src/pages/HelloPage.tsx @@ -1,13 +1,13 @@ -import React, { ForwardedRef, forwardRef, useRef } from "react"; -import { useStack } from "@cher-ami/router"; -import { transitionsHelper } from "../helper/transitionsHelper"; +import React, { ForwardedRef, forwardRef, useRef } from "react" +import { useStack } from "@cher-ami/router" +import { transitionsHelper } from "../helper/transitionsHelper" -const componentName: string = "HelloPage"; +const componentName: string = "HelloPage" interface IProps {} export const HelloPage = forwardRef((props: IProps, handleRef: ForwardedRef) => { - const rootRef = useRef(null); + const rootRef = useRef(null) useStack({ componentName, @@ -15,14 +15,14 @@ export const HelloPage = forwardRef((props: IProps, handleRef: ForwardedRef rootRef, playIn: () => transitionsHelper(rootRef.current, true), playOut: () => transitionsHelper(rootRef.current, false), - }); + }) return (
    {componentName}
    - ); -}); + ) +}) -HelloPage.displayName = componentName; -export default HelloPage; +HelloPage.displayName = componentName +export default HelloPage diff --git a/examples/example-client/src/pages/HomePage.tsx b/examples/example-client/src/pages/HomePage.tsx index d56dfb7a..680020d4 100644 --- a/examples/example-client/src/pages/HomePage.tsx +++ b/examples/example-client/src/pages/HomePage.tsx @@ -1,4 +1,4 @@ -import React, { ForwardedRef, forwardRef, useRef } from "react"; +import React, { ForwardedRef, forwardRef, useRef } from "react" import { getPathByRouteName, getSubRouterBase, @@ -8,21 +8,21 @@ import { Stack, useRouter, useStack, -} from "@cher-ami/router"; -import { transitionsHelper } from "../helper/transitionsHelper"; -import debug from "@wbe/debug"; +} from "@cher-ami/router" +import { transitionsHelper } from "../helper/transitionsHelper" +import debug from "@wbe/debug" -const componentName: string = "HomePage"; -const log = debug(`router:${componentName}`); +const componentName: string = "HomePage" +const log = debug(`router:${componentName}`) interface IProps { params: { - lang: string; - }; + lang: string + } } const HomePage = forwardRef((props: IProps, handleRef: ForwardedRef) => { - const rootRef = useRef(null); + const rootRef = useRef(null) useStack({ componentName, @@ -30,12 +30,12 @@ const HomePage = forwardRef((props: IProps, handleRef: ForwardedRef) => { rootRef, playIn: () => transitionsHelper(rootRef.current, true, { x: -50 }, { x: 0 }), playOut: () => transitionsHelper(rootRef.current, false, { x: -0 }, { x: 50 }), - }); + }) - const router = useRouter(); - const path = getPathByRouteName(router.routes, "HomePage"); - const subBase = getSubRouterBase(path, router.base); - const subRoutes = getSubRouterRoutes(path, router.routes); + const router = useRouter() + const path = getPathByRouteName(router.routes, "HomePage") + const subBase = getSubRouterBase(path, router.base) + const subRoutes = getSubRouterRoutes(path, router.routes) return (
    @@ -56,8 +56,8 @@ const HomePage = forwardRef((props: IProps, handleRef: ForwardedRef) => {
    - ); -}); + ) +}) -HomePage.displayName = componentName; -export default HomePage; +HomePage.displayName = componentName +export default HomePage diff --git a/examples/example-client/src/pages/LaPage.tsx b/examples/example-client/src/pages/LaPage.tsx index 329d8ac8..44920111 100644 --- a/examples/example-client/src/pages/LaPage.tsx +++ b/examples/example-client/src/pages/LaPage.tsx @@ -1,12 +1,12 @@ -import React, { ForwardedRef, forwardRef, useRef } from "react"; -import { useStack } from "@cher-ami/router"; -import { transitionsHelper } from "../helper/transitionsHelper"; -const componentName: string = "LaPage"; +import React, { ForwardedRef, forwardRef, useRef } from "react" +import { useStack } from "@cher-ami/router" +import { transitionsHelper } from "../helper/transitionsHelper" +const componentName: string = "LaPage" interface IProps {} const LaPage = forwardRef((props: IProps, handleRef: ForwardedRef) => { - const rootRef = useRef(null); + const rootRef = useRef(null) useStack({ componentName, @@ -14,14 +14,14 @@ const LaPage = forwardRef((props: IProps, handleRef: ForwardedRef) => { rootRef, playIn: () => transitionsHelper(rootRef.current, true), playOut: () => transitionsHelper(rootRef.current, false), - }); + }) return (
    {componentName}
    - ); -}); + ) +}) -LaPage.displayName = componentName; -export default LaPage; +LaPage.displayName = componentName +export default LaPage diff --git a/examples/example-client/src/pages/NotFoundPage.tsx b/examples/example-client/src/pages/NotFoundPage.tsx index a197103c..08b78bfa 100644 --- a/examples/example-client/src/pages/NotFoundPage.tsx +++ b/examples/example-client/src/pages/NotFoundPage.tsx @@ -1,12 +1,12 @@ -import React, { ForwardedRef, forwardRef, useRef } from "react"; -import { useStack } from "@cher-ami/router"; -import { transitionsHelper } from "../helper/transitionsHelper"; -const componentName: string = "NotFoundPage"; +import React, { ForwardedRef, forwardRef, useRef } from "react" +import { useStack } from "@cher-ami/router" +import { transitionsHelper } from "../helper/transitionsHelper" +const componentName: string = "NotFoundPage" interface IProps {} const NotFoundPage = forwardRef((props: IProps, handleRef: ForwardedRef) => { - const rootRef = useRef(null); + const rootRef = useRef(null) useStack({ componentName, @@ -14,14 +14,14 @@ const NotFoundPage = forwardRef((props: IProps, handleRef: ForwardedRef) => rootRef, playIn: () => transitionsHelper(rootRef.current, true), playOut: () => transitionsHelper(rootRef.current, false), - }); + }) return (
    {componentName}
    - ); -}); + ) +}) -NotFoundPage.displayName = componentName; -export default NotFoundPage; +NotFoundPage.displayName = componentName +export default NotFoundPage diff --git a/examples/example-client/src/pages/OurPage.tsx b/examples/example-client/src/pages/OurPage.tsx index f327329c..c785455f 100644 --- a/examples/example-client/src/pages/OurPage.tsx +++ b/examples/example-client/src/pages/OurPage.tsx @@ -1,14 +1,14 @@ -import React, { ForwardedRef, forwardRef, useRef } from "react"; -import { Link, useLocation, useStack } from "@cher-ami/router"; -import { transitionsHelper } from "../helper/transitionsHelper"; -const componentName: string = "OurPage"; +import React, { ForwardedRef, forwardRef, useRef } from "react" +import { Link, useLocation, useStack } from "@cher-ami/router" +import { transitionsHelper } from "../helper/transitionsHelper" +const componentName: string = "OurPage" interface IProps {} const OurPage = forwardRef((props: IProps, handleRef: ForwardedRef) => { - const rootRef = useRef(null); + const rootRef = useRef(null) - const [location, setLocation] = useLocation(); + const [location, setLocation] = useLocation() useStack({ componentName, @@ -16,7 +16,7 @@ const OurPage = forwardRef((props: IProps, handleRef: ForwardedRef) => { rootRef, playIn: () => transitionsHelper(rootRef.current, true), playOut: () => transitionsHelper(rootRef.current, false), - }); + }) return (
    @@ -29,8 +29,8 @@ const OurPage = forwardRef((props: IProps, handleRef: ForwardedRef) => {
    - ); -}); + ) +}) -OurPage.displayName = componentName; -export default OurPage; +OurPage.displayName = componentName +export default OurPage diff --git a/examples/example-client/src/pages/YoloPage.tsx b/examples/example-client/src/pages/YoloPage.tsx index 71afccad..b06b1adc 100644 --- a/examples/example-client/src/pages/YoloPage.tsx +++ b/examples/example-client/src/pages/YoloPage.tsx @@ -1,16 +1,16 @@ -import React, { ForwardedRef, forwardRef, useRef } from "react"; -import { useLocation, useStack } from "@cher-ami/router"; -import { transitionsHelper } from "../helper/transitionsHelper"; -const componentName: string = "YoloPage"; +import React, { ForwardedRef, forwardRef, useRef } from "react" +import { useLocation, useStack } from "@cher-ami/router" +import { transitionsHelper } from "../helper/transitionsHelper" +const componentName: string = "YoloPage" interface IProps { time: { - datetime: string; - }; + datetime: string + } } const YoloPage = forwardRef((props: IProps, handleRef: ForwardedRef) => { - const rootRef = useRef(null); + const rootRef = useRef(null) useStack({ componentName, @@ -18,9 +18,9 @@ const YoloPage = forwardRef((props: IProps, handleRef: ForwardedRef) => { rootRef, playIn: () => transitionsHelper(rootRef.current, true), playOut: () => transitionsHelper(rootRef.current, false), - }); + }) - const [location, setLocation] = useLocation(); + const [location, setLocation] = useLocation() return (
    @@ -31,15 +31,15 @@ const YoloPage = forwardRef((props: IProps, handleRef: ForwardedRef) => {
    {` setLocation({ name: "ArticlePage", params: { id: "form-sub-router" } })`}
    - ); -}); + ) +}) -YoloPage.displayName = componentName; -export default YoloPage; +YoloPage.displayName = componentName +export default YoloPage diff --git a/examples/example-client/src/routes.ts b/examples/example-client/src/routes.ts index 27c7d33a..d5aed96e 100644 --- a/examples/example-client/src/routes.ts +++ b/examples/example-client/src/routes.ts @@ -1,15 +1,15 @@ -import { TRoute } from "@cher-ami/router"; +import { TRoute } from "@cher-ami/router" -import HomePage from "./pages/HomePage"; -import AboutPage from "./pages/AboutPage"; -import ArticlePage from "./pages/ArticlePage"; -import FooPage from "./pages/FooPage"; -import BarPage from "./pages/BarPage"; -import NotFoundPage from "./pages/NotFoundPage"; -import YoloPage from "./pages/YoloPage"; -import HelloPage from "./pages/HelloPage"; -import LaPage from "./pages/LaPage"; -import OurPage from "./pages/OurPage"; +import HomePage from "./pages/HomePage" +import AboutPage from "./pages/AboutPage" +import ArticlePage from "./pages/ArticlePage" +import FooPage from "./pages/FooPage" +import BarPage from "./pages/BarPage" +import NotFoundPage from "./pages/NotFoundPage" +import YoloPage from "./pages/YoloPage" +import HelloPage from "./pages/HelloPage" +import LaPage from "./pages/LaPage" +import OurPage from "./pages/OurPage" /** * Define routes list @@ -31,9 +31,9 @@ export const routesList: TRoute[] = [ path: "/yolo", component: YoloPage, getStaticProps: async (props, currentLang) => { - const res = await fetch("https://worldtimeapi.org/api/ip"); - const time = await res.json(); - return { time }; + const res = await fetch("https://worldtimeapi.org/api/ip") + const time = await res.json() + return { time } }, }, { @@ -67,13 +67,13 @@ export const routesList: TRoute[] = [ color: "red", }, getStaticProps: async (props, currentLang) => { - const res = await fetch("https://worldtimeapi.org/api/ip"); - const time = await res.json(); - return { time }; + const res = await fetch("https://worldtimeapi.org/api/ip") + const time = await res.json() + return { time } }, }, { path: "/:rest", component: NotFoundPage, }, -]; +] diff --git a/examples/example-client/vite.config.ts b/examples/example-client/vite.config.ts index 4b698efe..ebec7c2f 100644 --- a/examples/example-client/vite.config.ts +++ b/examples/example-client/vite.config.ts @@ -1,8 +1,8 @@ -import { defineConfig } from "vite"; +import { defineConfig } from "vite" import react from "@vitejs/plugin-react" // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], - server: { host: true } -}); + server: { host: true }, +}) diff --git a/examples/example-ssr/index.html b/examples/example-ssr/index.html index 086036b4..496aa523 100644 --- a/examples/example-ssr/index.html +++ b/examples/example-ssr/index.html @@ -1,4 +1,4 @@ - + @@ -12,7 +12,7 @@
    diff --git a/examples/example-ssr/prerender/helpers/ManifestParser.ts b/examples/example-ssr/prerender/helpers/ManifestParser.ts index 39797026..792d1352 100644 --- a/examples/example-ssr/prerender/helpers/ManifestParser.ts +++ b/examples/example-ssr/prerender/helpers/ManifestParser.ts @@ -160,7 +160,7 @@ export class ManifestParser { ...(jsonManifest[b]?.css || []), ] : a, - [] + [], ) .filter((e) => e) diff --git a/examples/example-ssr/prerender/prerender.ts b/examples/example-ssr/prerender/prerender.ts index 68a76a23..a9138233 100644 --- a/examples/example-ssr/prerender/prerender.ts +++ b/examples/example-ssr/prerender/prerender.ts @@ -1,14 +1,14 @@ // @ts-ignore -import { render } from "~/server/index-server"; -import * as mfs from "@wbe/mfs"; -import path, { resolve } from "path"; -import chalk from "chalk"; -import { loadEnv } from "vite"; -import { isRouteIndex } from "./helpers/isRouteIndex"; -import { ManifestParser } from "./helpers/ManifestParser"; -import { renderToPipeableStream, renderToString } from "react-dom/server"; -import { ReactElement } from "react"; -import { htmlReplacement } from "~/server/helpers/htmlReplacement"; +import { render } from "~/server/index-server" +import * as mfs from "@wbe/mfs" +import path, { resolve } from "path" +import chalk from "chalk" +import { loadEnv } from "vite" +import { isRouteIndex } from "./helpers/isRouteIndex" +import { ManifestParser } from "./helpers/ManifestParser" +import { renderToPipeableStream, renderToString } from "react-dom/server" +import { ReactElement } from "react" +import { htmlReplacement } from "~/server/helpers/htmlReplacement" /** * Prerender @@ -20,39 +20,39 @@ export const prerender = async ( urls: string[], outDirStatic = resolve("dist/static"), ) => { - const indexTemplateSrc = `${outDirStatic}/index-template.html`; + const indexTemplateSrc = `${outDirStatic}/index-template.html` // copy index as template to avoid the override with the generated static index.html bellow if (!(await mfs.fileExists(indexTemplateSrc))) { - await mfs.copyFile(`${outDirStatic}/index.html`, indexTemplateSrc); + await mfs.copyFile(`${outDirStatic}/index.html`, indexTemplateSrc) } // get script tags to inject in render - const base = process.env.VITE_APP_BASE || loadEnv("", process.cwd(), "").VITE_APP_BASE; - const manifest = (await mfs.readFile(`${outDirStatic}/manifest.json`)) as string; - const scriptTags = ManifestParser.getScriptTagFromManifest(manifest, base); + const base = process.env.VITE_APP_BASE || loadEnv("", process.cwd(), "").VITE_APP_BASE + const manifest = (await mfs.readFile(`${outDirStatic}/manifest.json`)) as string + const scriptTags = ManifestParser.getScriptTagFromManifest(manifest, base) // pre-render each route for (let url of urls) { - url = url.startsWith("/") ? url : `/${url}`; + url = url.startsWith("/") ? url : `/${url}` try { // Request DOM - const dom = await render(url, scriptTags, true); + const dom = await render(url, scriptTags, true) // create stream and generate current file when all DOM is ready renderToPipeableStream(dom, { onAllReady() { - createHtmlFile(urls, url, outDirStatic, dom); + createHtmlFile(urls, url, outDirStatic, dom) }, onError(x) { - console.error(x); + console.error(x) }, - }); + }) } catch (e) { - console.log(e); + console.log(e) } } -}; +} /** * Create a single HTML file @@ -68,10 +68,10 @@ const createHtmlFile = async ( dom: ReactElement, ): Promise => { // Prepare file - if (isRouteIndex(url, urls)) url = `${url}/index`; - const routePath = path.resolve(`${outDir}/${url}`); - const htmlFilePath = `${routePath}.html`; + if (isRouteIndex(url, urls)) url = `${url}/index` + const routePath = path.resolve(`${outDir}/${url}`) + const htmlFilePath = `${routePath}.html` // Create file - await mfs.createFile(htmlFilePath, htmlReplacement(renderToString(dom))); - console.log(chalk.green(` → ${htmlFilePath.split("static")[1]}`)); -}; + await mfs.createFile(htmlFilePath, htmlReplacement(renderToString(dom))) + console.log(chalk.green(` → ${htmlFilePath.split("static")[1]}`)) +} diff --git a/examples/example-ssr/prerender/urls.ts b/examples/example-ssr/prerender/urls.ts index 046a57d7..eae483a3 100644 --- a/examples/example-ssr/prerender/urls.ts +++ b/examples/example-ssr/prerender/urls.ts @@ -1,4 +1,4 @@ -import fetch from "cross-fetch"; +import fetch from "cross-fetch" export const fetchAvailableUrls = async (): Promise => { /** @@ -44,4 +44,4 @@ export const fetchAvailableUrls = async (): Promise => { "/404" ]) }) -}; +} diff --git a/examples/example-ssr/server.js b/examples/example-ssr/server.js index 5e4c2a89..23258258 100644 --- a/examples/example-ssr/server.js +++ b/examples/example-ssr/server.js @@ -1,23 +1,23 @@ -import express from "express"; -import portFinderSync from "portfinder-sync"; -import { renderToPipeableStream } from "react-dom/server"; -import { createServer } from "vite"; +import express from "express" +import portFinderSync from "portfinder-sync" +import { renderToPipeableStream } from "react-dom/server" +import { createServer } from "vite" -const port = portFinderSync.getPort(3000); +const port = portFinderSync.getPort(3000) -(async () => { +;(async () => { /** * Dev server * * */ async function createDevServer() { - const app = express(); + const app = express() // dev script to inject const devScripts = { js: [{ tag: "script", attr: { type: "module", src: "/src/index.tsx" } }], - }; + } // Create Vite server in middleware mode. // This disables Vite's own HTML serving logic and let the parent server take control. @@ -28,36 +28,36 @@ const port = portFinderSync.getPort(3000); middlewareMode: true, }, appType: "custom", - }); + }) // use vite's connect instance as middleware - app.use(vite.middlewares); + app.use(vite.middlewares) app.use("*", async (req, res, next) => { try { // Transforms the ESM source code to be usable in Node.js - const { render } = await vite.ssrLoadModule(`src/server/index-server.tsx`); + const { render } = await vite.ssrLoadModule(`src/server/index-server.tsx`) // Get react-dom from the render method - const dom = await render(req.originalUrl, devScripts, false); + const dom = await render(req.originalUrl, devScripts, false) // Create stream to support Suspense API const stream = renderToPipeableStream(dom, { onShellReady() { - res.statusCode = 200; - res.setHeader("Content-type", "text/html"); - stream.pipe(res); + res.statusCode = 200 + res.setHeader("Content-type", "text/html") + stream.pipe(res) }, onError(e) { - res.statusCode = 500; - console.error(e); + res.statusCode = 500 + console.error(e) }, - }); + }) } catch (e) { - vite.ssrFixStacktrace(e); - next(e); + vite.ssrFixStacktrace(e) + next(e) } - }); + }) // return vite, app and server - return { vite, app }; + return { vite, app } } /** @@ -65,7 +65,7 @@ const port = portFinderSync.getPort(3000); */ createDevServer().then(({ app }) => app.listen(port, () => { - console.log(`http://localhost:${port}`); + console.log(`http://localhost:${port}`) }), - ); -})(); + ) +})() diff --git a/examples/example-ssr/src/components/App.tsx b/examples/example-ssr/src/components/App.tsx index f2cd821d..c4301b2f 100644 --- a/examples/example-ssr/src/components/App.tsx +++ b/examples/example-ssr/src/components/App.tsx @@ -1,12 +1,12 @@ -import React, { useEffect } from "react"; -import { Link, Stack, useLang } from "@cher-ami/router"; -import { languages } from "~/languages"; -import { EPages } from "~/routes"; -import { useRouter } from "@cher-ami/router"; +import React, { useEffect } from "react" +import { Link, Stack, useLang } from "@cher-ami/router" +import { languages } from "~/languages" +import { EPages } from "~/routes" +import { useRouter } from "@cher-ami/router" export function App() { - const { langService } = useRouter(); - const [lang, setLang] = useLang(); + const { langService } = useRouter() + const [lang, setLang] = useLang() const crossedTransitions = ({ previousPage, @@ -14,20 +14,20 @@ export function App() { unmountPreviousPage, }): Promise => { return new Promise(async (resolve) => { - const $current = currentPage?.$element; - if ($current) $current.style.visibility = "hidden"; + const $current = currentPage?.$element + if ($current) $current.style.visibility = "hidden" if (previousPage) { - previousPage.playOut(); + previousPage.playOut() } if (currentPage) { - await currentPage.isReadyPromise(); - if ($current) $current.style.visibility = "visible"; - await currentPage.playIn(); - unmountPreviousPage(); + await currentPage.isReadyPromise() + if ($current) $current.style.visibility = "visible" + await currentPage.playIn() + unmountPreviousPage() } - resolve(); - }); - }; + resolve() + }) + } // prettier-ignore return ( diff --git a/examples/example-ssr/src/helpers/transitionsHelper.ts b/examples/example-ssr/src/helpers/transitionsHelper.ts index 48a43d92..4d9d8fae 100644 --- a/examples/example-ssr/src/helpers/transitionsHelper.ts +++ b/examples/example-ssr/src/helpers/transitionsHelper.ts @@ -1,16 +1,16 @@ -import { gsap } from "gsap"; -import debug from "@wbe/debug"; -const log = debug(`router:transitionsHelper`); +import { gsap } from "gsap" +import debug from "@wbe/debug" +const log = debug(`router:transitionsHelper`) export const transitionsHelper = ( el, show: boolean, from: any = {}, - to: any = {} + to: any = {}, ): Promise => { return new Promise((resolve) => { if (!el) { - log("el doesnt exist", el); + log("el doesnt exist", el) } gsap.fromTo( @@ -23,7 +23,7 @@ export const transitionsHelper = ( autoAlpha: show ? 1 : 0, ease: "power1.out", onComplete: resolve, - } - ); - }); -}; + }, + ) + }) +} diff --git a/examples/example-ssr/src/index.tsx b/examples/example-ssr/src/index.tsx index 435f63c2..05eefbcd 100644 --- a/examples/example-ssr/src/index.tsx +++ b/examples/example-ssr/src/index.tsx @@ -1,12 +1,12 @@ -import React from "react"; -import { hydrateRoot } from "react-dom/client"; -import { App } from "./components/App"; -import { routes } from "./routes"; -import { createBrowserHistory } from "history"; -import "./index.css"; -import { Router } from "@cher-ami/router"; -import { langServiceInstance } from "./langServiceInstance"; -import { GlobalDataContext } from "~/store/GlobalDataContext"; +import React from "react" +import { hydrateRoot } from "react-dom/client" +import { App } from "./components/App" +import { routes } from "./routes" +import { createBrowserHistory } from "history" +import "./index.css" +import { Router } from "@cher-ami/router" +import { langServiceInstance } from "./langServiceInstance" +import { GlobalDataContext } from "~/store/GlobalDataContext" /** * Client side @@ -23,4 +23,4 @@ hydrateRoot( , -); +) diff --git a/examples/example-ssr/src/langServiceInstance.ts b/examples/example-ssr/src/langServiceInstance.ts index 0405f15d..88913d8a 100644 --- a/examples/example-ssr/src/langServiceInstance.ts +++ b/examples/example-ssr/src/langServiceInstance.ts @@ -1,5 +1,5 @@ -import { languages, showDefaultLangInUrl } from "./languages"; -import { LangService } from "@cher-ami/router"; +import { languages, showDefaultLangInUrl } from "./languages" +import { LangService } from "@cher-ami/router" export const langServiceInstance = (base = "/", url = window.location.pathname) => new LangService({ @@ -7,4 +7,4 @@ export const langServiceInstance = (base = "/", url = window.location.pathname) staticLocation: url, languages, base, - }); + }) diff --git a/examples/example-ssr/src/languages.ts b/examples/example-ssr/src/languages.ts index 7542907c..69acaf62 100644 --- a/examples/example-ssr/src/languages.ts +++ b/examples/example-ssr/src/languages.ts @@ -1,4 +1,4 @@ -import { TLanguage } from "@cher-ami/router"; +import { TLanguage } from "@cher-ami/router" /** * Available languages @@ -12,10 +12,10 @@ export const languages: TLanguage[] = [ { key: "en", }, -]; +] /** * Show default lang in URL * Common configuration between server and client */ -export const showDefaultLangInUrl = false; +export const showDefaultLangInUrl = false diff --git a/examples/example-ssr/src/pages/AboutPage.tsx b/examples/example-ssr/src/pages/AboutPage.tsx index 5b5cb519..ddc09e57 100644 --- a/examples/example-ssr/src/pages/AboutPage.tsx +++ b/examples/example-ssr/src/pages/AboutPage.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from "react"; +import React, { useRef } from "react" import { useStack, getSubRouterBase, @@ -7,19 +7,19 @@ import { Router, Stack, useRouter, -} from "@cher-ami/router"; -import { transitionsHelper } from "../helpers/transitionsHelper"; -import { getPathByRouteName } from "@cher-ami/router"; -import debug from "@wbe/debug"; -import { useLang } from "@cher-ami/router"; -import { EPages } from "../routes"; +} from "@cher-ami/router" +import { transitionsHelper } from "../helpers/transitionsHelper" +import { getPathByRouteName } from "@cher-ami/router" +import debug from "@wbe/debug" +import { useLang } from "@cher-ami/router" +import { EPages } from "../routes" -const componentName = "AboutPage"; -const log = debug(`front:${componentName}`); +const componentName = "AboutPage" +const log = debug(`front:${componentName}`) function AboutPage(props, handleRef) { - const rootRef = useRef(null); - const [lang] = useLang(); + const rootRef = useRef(null) + const [lang] = useLang() useStack({ componentName, @@ -27,13 +27,13 @@ function AboutPage(props, handleRef) { rootRef, playIn: () => transitionsHelper(rootRef.current, true, { x: -50 }, { x: 0 }), playOut: () => transitionsHelper(rootRef.current, false, { x: -0 }, { x: 50 }), - }); + }) // prepare routes & base for subRouter - const router = useRouter(); - const path = getPathByRouteName(router.routes, EPages.ABOUT); - const subRouterBase = getSubRouterBase(path, router.base, true); - const surRouterRoutes = getSubRouterRoutes(path, router.routes); + const router = useRouter() + const path = getPathByRouteName(router.routes, EPages.ABOUT) + const subRouterBase = getSubRouterBase(path, router.base, true) + const surRouterRoutes = getSubRouterRoutes(path, router.routes) return (
    @@ -41,13 +41,15 @@ function AboutPage(props, handleRef) {
    Foo
    - Bar + + Bar +
    - ); + ) } -export default React.forwardRef(AboutPage); +export default React.forwardRef(AboutPage) diff --git a/examples/example-ssr/src/pages/ArticlePage.tsx b/examples/example-ssr/src/pages/ArticlePage.tsx index a83c68a5..3e3d6368 100644 --- a/examples/example-ssr/src/pages/ArticlePage.tsx +++ b/examples/example-ssr/src/pages/ArticlePage.tsx @@ -1,5 +1,5 @@ -import React, {useEffect, useRef} from "react" -import { useStack } from "@cher-ami/router"; +import React, { useEffect, useRef } from "react" +import { useStack } from "@cher-ami/router" import { transitionsHelper } from "../helpers/transitionsHelper" const componentName = "ArticlePage" @@ -17,9 +17,7 @@ function ArticlePage(props, handleRef) { return (
    e).join(" ")} ref={rootRef}> {componentName} -

    - article slug: {props.params.slug} -

    +

    article slug: {props.params.slug}

    {/*{props.todo?.map((e, i) =>
    {e.title}
    )}*/}
    diff --git a/examples/example-ssr/src/pages/BarPage.tsx b/examples/example-ssr/src/pages/BarPage.tsx index 9d37bf80..f5caade5 100644 --- a/examples/example-ssr/src/pages/BarPage.tsx +++ b/examples/example-ssr/src/pages/BarPage.tsx @@ -1,11 +1,10 @@ -import React, { useRef } from "react"; -import { useStack } from "@cher-ami/router"; -import { transitionsHelper } from "../helpers/transitionsHelper"; +import React, { useRef } from "react" +import { useStack } from "@cher-ami/router" +import { transitionsHelper } from "../helpers/transitionsHelper" - -const componentName = "BarPage"; +const componentName = "BarPage" function BarPage(props, handleRef) { - const rootRef = useRef(null); + const rootRef = useRef(null) useStack({ componentName, @@ -13,13 +12,13 @@ function BarPage(props, handleRef) { rootRef, playIn: () => transitionsHelper(rootRef.current, true, { x: -50 }, { x: 0 }), playOut: () => transitionsHelper(rootRef.current, false, { x: -0 }, { x: 50 }), - }); + }) return (
    e).join(" ")} ref={rootRef}> {componentName}
    - ); + ) } -export default React.forwardRef(BarPage); +export default React.forwardRef(BarPage) diff --git a/examples/example-ssr/src/pages/ContactPage.tsx b/examples/example-ssr/src/pages/ContactPage.tsx index 219047a7..da8da608 100644 --- a/examples/example-ssr/src/pages/ContactPage.tsx +++ b/examples/example-ssr/src/pages/ContactPage.tsx @@ -1,6 +1,6 @@ -import React, {useState, useRef} from "react" -import { useStack } from "@cher-ami/router"; -import {transitionsHelper} from "~/helpers/transitionsHelper" +import React, { useState, useRef } from "react" +import { useStack } from "@cher-ami/router" +import { transitionsHelper } from "~/helpers/transitionsHelper" const componentName = "ContactPage" function ContactPage(props, handleRef) { @@ -12,7 +12,7 @@ function ContactPage(props, handleRef) { rootRef, playIn: () => transitionsHelper(rootRef.current, true, { x: -50 }, { x: 0 }), playOut: () => transitionsHelper(rootRef.current, false, { x: -0 }, { x: 50 }), - }); + }) return (
    diff --git a/examples/example-ssr/src/pages/FooPage.tsx b/examples/example-ssr/src/pages/FooPage.tsx index 59509573..44b19909 100644 --- a/examples/example-ssr/src/pages/FooPage.tsx +++ b/examples/example-ssr/src/pages/FooPage.tsx @@ -1,13 +1,13 @@ -import React, { useRef } from "react"; -import { useRouter, useStack } from "@cher-ami/router"; -import { transitionsHelper } from "../helpers/transitionsHelper"; -import { useLang } from "@cher-ami/router"; +import React, { useRef } from "react" +import { useRouter, useStack } from "@cher-ami/router" +import { transitionsHelper } from "../helpers/transitionsHelper" +import { useLang } from "@cher-ami/router" -const componentName = "FooPage"; +const componentName = "FooPage" function FooPage(props, handleRef) { - const rootRef = useRef(null); - const [lang] = useLang(); - const router = useRouter(); + const rootRef = useRef(null) + const [lang] = useLang() + const router = useRouter() useStack({ componentName, @@ -15,14 +15,14 @@ function FooPage(props, handleRef) { rootRef, playIn: () => transitionsHelper(rootRef.current, true, { x: -50 }, { x: 0 }), playOut: () => transitionsHelper(rootRef.current, false, { x: -0 }, { x: 50 }), - }); + }) return (
    e).join(" ")} ref={rootRef}> {componentName} - langKey:{" "} {router.langService && router.langService.currentLang.key}
    - ); + ) } -export default React.forwardRef(FooPage); +export default React.forwardRef(FooPage) diff --git a/examples/example-ssr/src/pages/HomePage.tsx b/examples/example-ssr/src/pages/HomePage.tsx index c8625b3d..fb05e1a8 100644 --- a/examples/example-ssr/src/pages/HomePage.tsx +++ b/examples/example-ssr/src/pages/HomePage.tsx @@ -1,19 +1,17 @@ -import React, { useState, useRef, useContext, useEffect } from "react"; -import { useLang, useStack } from "@cher-ami/router"; -import { transitionsHelper } from "~/helpers/transitionsHelper"; -import { GlobalDataContext } from "~/store/GlobalDataContext"; +import React, { useState, useRef, useContext, useEffect } from "react" +import { useLang, useStack } from "@cher-ami/router" +import { transitionsHelper } from "~/helpers/transitionsHelper" +import { GlobalDataContext } from "~/store/GlobalDataContext" -const componentName = "HomePage"; +const componentName = "HomePage" function HomePage(props, handleRef) { - const rootRef = useRef(null); - const [n, setN] = useState(0); - const globalData = useContext(GlobalDataContext); -// console.log("globalData",globalData) - const [lang] = useLang(); + const rootRef = useRef(null) + const [n, setN] = useState(0) + const globalData = useContext(GlobalDataContext) + // console.log("globalData",globalData) + const [lang] = useLang() - useEffect(()=> - { - },[]) + useEffect(() => {}, []) useStack({ componentName, @@ -21,7 +19,7 @@ function HomePage(props, handleRef) { rootRef, playIn: () => transitionsHelper(rootRef.current, true, { x: -50 }, { x: 0 }), playOut: () => transitionsHelper(rootRef.current, false, { x: -0 }, { x: 50 }), - }); + }) return (
    @@ -36,7 +34,7 @@ function HomePage(props, handleRef) {
    {user.name}
    ))}
    - ); + ) } -export default React.forwardRef(HomePage); +export default React.forwardRef(HomePage) diff --git a/examples/example-ssr/src/pages/NotFoundPage.tsx b/examples/example-ssr/src/pages/NotFoundPage.tsx index 36039596..1f94e868 100644 --- a/examples/example-ssr/src/pages/NotFoundPage.tsx +++ b/examples/example-ssr/src/pages/NotFoundPage.tsx @@ -1,6 +1,6 @@ -import React, {useState, useRef} from "react" -import { useStack } from "@cher-ami/router"; -import {transitionsHelper} from "../helpers/transitionsHelper" +import React, { useState, useRef } from "react" +import { useStack } from "@cher-ami/router" +import { transitionsHelper } from "../helpers/transitionsHelper" const componentName = "NotFoundPage" function NotFoundPage(props, handleRef) { @@ -12,15 +12,16 @@ function NotFoundPage(props, handleRef) { rootRef, playIn: () => transitionsHelper(rootRef.current, true, { x: -50 }, { x: 0 }), playOut: () => transitionsHelper(rootRef.current, false, { x: -0 }, { x: 50 }), - }); - + }) return (
    - NOT FOUND NOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT - NOT FOUND NOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT - NOT FOUND NOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT - NOT FOUND NOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT + NOT FOUND NOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT + FOUNDNOT FOUNDNOT FOUNDNOT NOT FOUND NOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT + FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT NOT FOUND NOT FOUNDNOT + FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT NOT + FOUND NOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT FOUNDNOT + FOUNDNOT FOUNDNOT
    ) } diff --git a/examples/example-ssr/src/routes.ts b/examples/example-ssr/src/routes.ts index ffeea954..92762108 100644 --- a/examples/example-ssr/src/routes.ts +++ b/examples/example-ssr/src/routes.ts @@ -1,11 +1,11 @@ -import HomePage from "./pages/HomePage"; -import AboutPage from "./pages/AboutPage"; -import ContactPage from "./pages/ContactPage"; -import NotFoundPage from "./pages/NotFoundPage"; -import { TRoute } from "@cher-ami/router"; -import ArticlePage from "./pages/ArticlePage"; -import FooPage from "./pages/FooPage"; -import BarPage from "./pages/BarPage"; +import HomePage from "./pages/HomePage" +import AboutPage from "./pages/AboutPage" +import ContactPage from "./pages/ContactPage" +import NotFoundPage from "./pages/NotFoundPage" +import { TRoute } from "@cher-ami/router" +import ArticlePage from "./pages/ArticlePage" +import FooPage from "./pages/FooPage" +import BarPage from "./pages/BarPage" export enum EPages { HOME = "home", @@ -23,9 +23,9 @@ export const routes: TRoute[] = [ component: HomePage, name: EPages.HOME, getStaticProps: async (props, currentLang) => { - const res = await fetch("https://worldtimeapi.org/api/ip"); - const time = await res.json(); - return { time }; + const res = await fetch("https://worldtimeapi.org/api/ip") + const time = await res.json() + return { time } }, }, { @@ -33,9 +33,9 @@ export const routes: TRoute[] = [ component: AboutPage, name: EPages.ABOUT, getStaticProps: async () => { - const res = await fetch("https://jsonplaceholder.typicode.com/todos"); - const todo = await res.json(); - return { todo }; + const res = await fetch("https://jsonplaceholder.typicode.com/todos") + const todo = await res.json() + return { todo } }, children: [ { @@ -43,9 +43,9 @@ export const routes: TRoute[] = [ component: FooPage, name: EPages.FOO, getStaticProps: async () => { - const res = await fetch("https://jsonplaceholder.typicode.com/todos/1"); - const todo = await res.json(); - return { todo }; + const res = await fetch("https://jsonplaceholder.typicode.com/todos/1") + const todo = await res.json() + return { todo } }, }, { @@ -63,10 +63,10 @@ export const routes: TRoute[] = [ color: "blue", }, getStaticProps: async (props) => { - const res = await fetch("https://jsonplaceholder.typicode.com/todos"); - const todo = await res.json(); - const mySlug = props.params.slug; - return { todo, mySlug }; + const res = await fetch("https://jsonplaceholder.typicode.com/todos") + const todo = await res.json() + const mySlug = props.params.slug + return { todo, mySlug } }, }, { @@ -79,4 +79,4 @@ export const routes: TRoute[] = [ component: NotFoundPage, name: EPages.NOT_FOUND, }, -]; +] diff --git a/examples/example-ssr/src/server/helpers/CherScripts.tsx b/examples/example-ssr/src/server/helpers/CherScripts.tsx index 46129354..5c339545 100644 --- a/examples/example-ssr/src/server/helpers/CherScripts.tsx +++ b/examples/example-ssr/src/server/helpers/CherScripts.tsx @@ -9,9 +9,5 @@ export const ScriptTag = ({ tag, attr }: TScript): JSX.Element => { } export const CherScripts = ({ scripts }: { scripts: TScript[] }): JSX.Element => ( - <> - {scripts?.map((script, i) => ( - - ))} - + <>{scripts?.map((script, i) => )} ) diff --git a/examples/example-ssr/src/server/index-server.tsx b/examples/example-ssr/src/server/index-server.tsx index d578fa6c..d59e0939 100644 --- a/examples/example-ssr/src/server/index-server.tsx +++ b/examples/example-ssr/src/server/index-server.tsx @@ -1,17 +1,17 @@ -import fetch from "cross-fetch"; -import * as React from "react"; -import { routes } from "~/routes"; -import { App } from "~/components/App"; -import { languages, showDefaultLangInUrl } from "~/languages"; -import { LangService, requestStaticPropsFromRoute, Router } from "@cher-ami/router"; -import { loadEnv } from "vite"; -import { TScriptsObj } from "../../prerender/helpers/ManifestParser"; -import { CherScripts } from "~/server/helpers/CherScripts"; -import { RawScript } from "~/server/helpers/RawScript"; -import { ViteDevScripts } from "~/server/helpers/ViteDevScripts"; -import { ReactElement } from "react"; -import { preventSlashes } from "~/server/helpers/preventSlashes"; -import { GlobalDataContext } from "~/store/GlobalDataContext"; +import fetch from "cross-fetch" +import * as React from "react" +import { routes } from "~/routes" +import { App } from "~/components/App" +import { languages, showDefaultLangInUrl } from "~/languages" +import { LangService, requestStaticPropsFromRoute, Router } from "@cher-ami/router" +import { loadEnv } from "vite" +import { TScriptsObj } from "../../prerender/helpers/ManifestParser" +import { CherScripts } from "~/server/helpers/CherScripts" +import { RawScript } from "~/server/helpers/RawScript" +import { ViteDevScripts } from "~/server/helpers/ViteDevScripts" +import { ReactElement } from "react" +import { preventSlashes } from "~/server/helpers/preventSlashes" +import { GlobalDataContext } from "~/store/GlobalDataContext" // ----------------------------------------------------------------------------- PREPARE diff --git a/examples/example-ssr/vite.config.ts b/examples/example-ssr/vite.config.ts index 2108c02a..ba93bcf1 100644 --- a/examples/example-ssr/vite.config.ts +++ b/examples/example-ssr/vite.config.ts @@ -1,8 +1,8 @@ -import { resolve } from "path"; -import { defineConfig } from "vite"; -import debug from "@wbe/debug"; -import react from "@vitejs/plugin-react"; -const log = debug("config:vite.config"); +import { resolve } from "path" +import { defineConfig } from "vite" +import debug from "@wbe/debug" +import react from "@vitejs/plugin-react" +const log = debug("config:vite.config") /** * Vite config @@ -10,7 +10,7 @@ const log = debug("config:vite.config"); */ export default defineConfig(({ command, mode }) => { return { - base: '/', + base: "/", build: { assetsDir: "./", write: true, @@ -29,5 +29,5 @@ export default defineConfig(({ command, mode }) => { "~": resolve(__dirname, "src"), }, }, - }; -}); + } +}) diff --git a/examples/example-ssr/vite.scripts.config.ts b/examples/example-ssr/vite.scripts.config.ts index fd0aad2f..6e2a6c4a 100644 --- a/examples/example-ssr/vite.scripts.config.ts +++ b/examples/example-ssr/vite.scripts.config.ts @@ -1,11 +1,11 @@ -import { resolve } from "path"; -import { defineConfig } from "vite"; -import debug from "@wbe/debug"; -const log = debug("config:vite.config"); +import { resolve } from "path" +import { defineConfig } from "vite" +import debug from "@wbe/debug" +const log = debug("config:vite.config") export default defineConfig(({ command, mode }) => { return { - base: '/', + base: "/", build: { assetsDir: "./", write: true, @@ -37,5 +37,5 @@ export default defineConfig(({ command, mode }) => { "~": resolve(__dirname, "src"), }, }, - }; -}); + } +}) diff --git a/package.json b/package.json index 9c38100a..b8b16e51 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "size-why": "size-limit -- --why", "test": "vitest", "test:watch": "vitest --watch", - "pre-publish": "npm run build && npm run test" + "pre-publish": "npm run build && npm run test", + "prepare": "husky install" }, "size-limit": [ { @@ -47,8 +48,8 @@ "@wbe/debug": "^1.2.0", "history": "^5.3.0", "path-to-regexp": "^6.2.1", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": ">=16.8.0", + "react-dom": ">=16.8.0" }, "peerDependencies": { "react": ">=16.8.0", @@ -61,7 +62,9 @@ "@types/node": "^20.8.7", "@types/react": "^18.2.29", "@types/react-dom": "^18.2.14", + "husky": "^8.0.3", "jsdom": "^22.1.0", + "lint-staged": "^15.0.1", "prettier": "^3.0.3", "react-test-renderer": "^18.2.0", "size-limit": "^9.0.0", @@ -70,14 +73,13 @@ "typescript": "^5.2.2", "vitest": "^0.34.6" }, - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } - }, "lint-staged": { "*.{tsx,ts,jsx,js,less,css,json,md}": [ - "prettier --write" + "prettier --write ." ] + }, + "prettier": { + "printWidth": 90, + "semi": false } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 41e2f91f..cfe56b55 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,10 +14,10 @@ importers: specifier: ^6.2.1 version: 6.2.1 react: - specifier: ^18.2.0 + specifier: '>=16.8.0' version: 18.2.0 react-dom: - specifier: ^18.2.0 + specifier: '>=16.8.0' version: 18.2.0(react@18.2.0) devDependencies: '@size-limit/preset-small-lib': @@ -38,9 +38,15 @@ importers: '@types/react-dom': specifier: ^18.2.14 version: 18.2.14 + husky: + specifier: ^8.0.3 + version: 8.0.3 jsdom: specifier: ^22.1.0 version: 22.1.0 + lint-staged: + specifier: ^15.0.1 + version: 15.0.1 prettier: specifier: ^3.0.3 version: 3.0.3 @@ -908,11 +914,23 @@ packages: - supports-color dev: true + /ansi-escapes@5.0.0: + resolution: {integrity: sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==} + engines: {node: '>=12'} + dependencies: + type-fest: 1.4.0 + dev: true + /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} dev: true + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -932,6 +950,11 @@ packages: engines: {node: '>=10'} dev: true + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: true + /any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} dev: true @@ -1136,6 +1159,21 @@ packages: fsevents: 2.3.3 dev: true + /cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + restore-cursor: 4.0.0 + dev: true + + /cli-truncate@3.1.0: + resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + slice-ansi: 5.0.0 + string-width: 5.1.2 + dev: true + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -1157,6 +1195,10 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true + /colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + dev: true + /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -1164,6 +1206,11 @@ packages: delayed-stream: 1.0.0 dev: true + /commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + dev: true + /commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -1387,6 +1434,10 @@ packages: webidl-conversions: 7.0.0 dev: true + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: true @@ -1395,6 +1446,10 @@ packages: resolution: {integrity: sha512-j9IcGmfkyN5MBH/0Xzg45GDHasXsnwEJDM6Xnr9H7GlGUni+JH4q6xp6Nk7NV5LjTmoEFBqhILIqg1McJrv6uA==} dev: true + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true + /encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} @@ -1468,6 +1523,10 @@ packages: engines: {node: '>= 0.6'} dev: true + /eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + dev: true + /execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -1483,6 +1542,21 @@ packages: strip-final-newline: 2.0.0 dev: true + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + dev: true + /express@4.18.2: resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} engines: {node: '>= 0.10.0'} @@ -1662,6 +1736,11 @@ packages: engines: {node: '>=10'} dev: true + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + dev: true + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1803,6 +1882,17 @@ packages: engines: {node: '>=10.17.0'} dev: true + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + dev: true + + /husky@8.0.3: + resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} + engines: {node: '>=14'} + hasBin: true + dev: true + /iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -1905,6 +1995,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + dev: true + /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -1955,6 +2050,11 @@ packages: engines: {node: '>=8'} dev: true + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -2081,6 +2181,37 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true + /lint-staged@15.0.1: + resolution: {integrity: sha512-2IU5OWmCaxch0X0+IBF4/v7sutpB+F3qoXbro43pYjQTOo5wumckjxoxn47pQBqqBsCWrD5HnI2uG/zJA7isew==} + engines: {node: '>=18.12.0'} + hasBin: true + dependencies: + chalk: 5.3.0 + commander: 11.1.0 + debug: 4.3.4 + execa: 8.0.1 + lilconfig: 2.1.0 + listr2: 7.0.1 + micromatch: 4.0.5 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.3.2 + transitivePeerDependencies: + - supports-color + dev: true + + /listr2@7.0.1: + resolution: {integrity: sha512-nz+7hwgbDp8eWNoDgzdl4hA/xDSLrNRzPu1TLgOYs6l5Y+Ma6zVWWy9Oyt9TQFONwKoSPoka3H50D3vD5EuNwg==} + engines: {node: '>=16.0.0'} + dependencies: + cli-truncate: 3.1.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 5.0.1 + rfdc: 1.3.0 + wrap-ansi: 8.1.0 + dev: true + /load-tsconfig@0.2.5: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2099,6 +2230,17 @@ packages: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true + /log-update@5.0.1: + resolution: {integrity: sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + ansi-escapes: 5.0.0 + cli-cursor: 4.0.0 + slice-ansi: 5.0.0 + strip-ansi: 7.1.0 + wrap-ansi: 8.1.0 + dev: true + /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -2190,6 +2332,11 @@ packages: engines: {node: '>=6'} dev: true + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -2319,6 +2466,13 @@ packages: path-key: 3.1.1 dev: true + /npm-run-path@5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + /nwsapi@2.2.7: resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} dev: true @@ -2380,6 +2534,13 @@ packages: mimic-fn: 2.1.0 dev: true + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + /p-limit@4.0.0: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2408,6 +2569,11 @@ packages: engines: {node: '>=8'} dev: true + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} dev: true @@ -2438,6 +2604,12 @@ packages: engines: {node: '>=8.6'} dev: true + /pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + dev: true + /pirates@4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} @@ -2658,11 +2830,23 @@ packages: engines: {node: '>=8'} dev: true + /restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} dev: true + /rfdc@1.3.0: + resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} + dev: true + /rollup@3.29.4: resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} @@ -2800,6 +2984,11 @@ packages: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + /simple-update-notifier@2.0.0: resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} engines: {node: '>=10'} @@ -2825,6 +3014,14 @@ packages: engines: {node: '>=8'} dev: true + /slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + dev: true + /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} @@ -2857,11 +3054,37 @@ packages: internal-slot: 1.0.5 dev: true + /string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + dev: true + + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + dev: true + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + /strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} dev: true + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + /strip-literal@1.3.0: resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} dependencies: @@ -3088,6 +3311,11 @@ packages: engines: {node: '>=4'} dev: true + /type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + dev: true + /type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -3392,6 +3620,15 @@ packages: stackback: 0.0.2 dev: true + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + dev: true + /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true @@ -3426,6 +3663,11 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true + /yaml@2.3.2: + resolution: {integrity: sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==} + engines: {node: '>= 14'} + dev: true + /yaml@2.3.3: resolution: {integrity: sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==} engines: {node: '>= 14'} diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 1b0245c8..f7e330ce 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -4,50 +4,50 @@ import React, { PropsWithChildren, useCallback, useMemo, -} from "react"; -import { createUrl, TOpenRouteParams } from "../core/core"; -import { joinPaths, removeLastCharFromString } from "../core/helpers"; -import { useRouter } from "../hooks/useRouter"; -import { useLocation } from "../hooks/useLocation"; -import debug from "@wbe/debug"; +} from "react" +import { createUrl, TOpenRouteParams } from "../core/core" +import { joinPaths, removeLastCharFromString } from "../core/helpers" +import { useRouter } from "../hooks/useRouter" +import { useLocation } from "../hooks/useLocation" +import debug from "@wbe/debug" // exclude href because it collides with "to" -type TAnchorWithoutHref = Omit, "href">; +type TAnchorWithoutHref = Omit, "href"> export interface ILinkProps extends PropsWithChildren { - to: string | TOpenRouteParams; - onClick?: () => void; - className?: string; - children?: React.ReactNode; + to: string | TOpenRouteParams + onClick?: () => void + className?: string + children?: React.ReactNode } -const log = debug("router:Link"); +const log = debug("router:Link") /** * @name Link */ function Link(props: ILinkProps, ref: MutableRefObject) { - const { history, staticLocation } = useRouter(); - const [location] = useLocation(); + const { history, staticLocation } = useRouter() + const [location] = useLocation() // Compute URL - const url = useMemo(() => createUrl(props.to), [props.to]); + const url = useMemo(() => createUrl(props.to), [props.to]) // Link is active if its URL is the current URL const handleClick = useCallback( (event): void => { - event.preventDefault(); - props.onClick?.(); - history?.push(url); + event.preventDefault() + props.onClick?.() + history?.push(url) }, - [url, history] - ); + [url, history], + ) - const [isActive, setIsActive] = React.useState(); + const [isActive, setIsActive] = React.useState() React.useEffect(() => { - const loc = history ? location : staticLocation; - setIsActive(loc === url || loc === removeLastCharFromString(url, "/", true)); - }, [history, staticLocation, location, url]); + const loc = history ? location : staticLocation + setIsActive(loc === url || loc === removeLastCharFromString(url, "/", true)) + }, [history, staticLocation, location, url]) return ( ) { href={url} ref={ref} /> - ); + ) } -const ForwardLink = React.forwardRef(Link); -export { ForwardLink as Link }; +const ForwardLink = React.forwardRef(Link) +export { ForwardLink as Link } diff --git a/src/components/Router.tsx b/src/components/Router.tsx index b9682779..bcb9a6c8 100644 --- a/src/components/Router.tsx +++ b/src/components/Router.tsx @@ -1,6 +1,6 @@ -import debug from "@wbe/debug"; -import { BrowserHistory, HashHistory, MemoryHistory } from "history"; -import { Match } from "path-to-regexp"; +import debug from "@wbe/debug" +import { BrowserHistory, HashHistory, MemoryHistory } from "history" +import { Match } from "path-to-regexp" import React, { createContext, memo, @@ -9,65 +9,65 @@ import React, { useMemo, useReducer, useRef, -} from "react"; -import { formatRoutes, TParams, TQueryParams } from "../core/core"; -import { getNotFoundRoute, getRouteFromUrl } from "../core/core"; -import { Routers } from "../core/Routers"; -import LangService, { TLanguage } from "../core/LangService"; -import { staticPropsCache } from "../core/staticPropsCache"; -import { isSSR, removeLastCharFromString } from "../core/helpers"; +} from "react" +import { formatRoutes, TParams, TQueryParams } from "../core/core" +import { getNotFoundRoute, getRouteFromUrl } from "../core/core" +import { Routers } from "../core/Routers" +import LangService, { TLanguage } from "../core/LangService" +import { staticPropsCache } from "../core/staticPropsCache" +import { isSSR, removeLastCharFromString } from "../core/helpers" // -------------------------------------------------------------------------------- TYPES export type TRouteProps = { - params?: TParams; - [x: string]: any; -}; + params?: TParams + [x: string]: any +} export type TRoute = Partial<{ - path: string | { [x: string]: string }; - component: React.ComponentType; - base: string; - name: string; - parser: Match; - props: TRouteProps; - children: TRoute[]; - url: string; - params?: TParams; - queryParams?: TQueryParams; - hash?: string; - getStaticProps: (props: TRouteProps, currentLang: TLanguage) => Promise; - _fullUrl: string; // full URL who not depends on current instance - _fullPath: string; // full Path /base/:lang/foo/second-foo - _langPath: { [x: string]: string } | null; - _context: TRoute; -}>; + path: string | { [x: string]: string } + component: React.ComponentType + base: string + name: string + parser: Match + props: TRouteProps + children: TRoute[] + url: string + params?: TParams + queryParams?: TQueryParams + hash?: string + getStaticProps: (props: TRouteProps, currentLang: TLanguage) => Promise + _fullUrl: string // full URL who not depends on current instance + _fullPath: string // full Path /base/:lang/foo/second-foo + _langPath: { [x: string]: string } | null + _context: TRoute +}> export interface IRouterContextStackStates { - unmountPreviousPage?: () => void; - previousPageIsMount?: boolean; + unmountPreviousPage?: () => void + previousPageIsMount?: boolean } export interface IRouterContext extends IRouterContextStackStates { - base: string; - routes: TRoute[]; - history: BrowserHistory | HashHistory | MemoryHistory | undefined; - staticLocation: string; - currentRoute: TRoute; - previousRoute: TRoute; - langService: LangService; - routeIndex: number; - previousPageIsMount: boolean; - unmountPreviousPage: () => void; - getPaused: () => boolean; - setPaused: (value: boolean) => void; + base: string + routes: TRoute[] + history: BrowserHistory | HashHistory | MemoryHistory | undefined + staticLocation: string + currentRoute: TRoute + previousRoute: TRoute + langService: LangService + routeIndex: number + previousPageIsMount: boolean + unmountPreviousPage: () => void + getPaused: () => boolean + setPaused: (value: boolean) => void } // -------------------------------------------------------------------------------- PREPARE / CONTEXT -const componentName = "Router"; -const log = debug(`router:${componentName}`); -const isServer = isSSR(); +const componentName = "Router" +const log = debug(`router:${componentName}`) +const isServer = isSSR() /** * Router Context * Router instance will be keep on this context @@ -87,13 +87,13 @@ export const RouterContext = createContext({ unmountPreviousPage: () => {}, getPaused: () => false, setPaused: (value: boolean) => {}, -}); -RouterContext.displayName = "RouterContext"; +}) +RouterContext.displayName = "RouterContext" Router.defaultProps = { base: "/", id: 1, -}; +} // -------------------------------------------------------------------------------- COMPONENT @@ -103,15 +103,15 @@ Router.defaultProps = { * @returns JSX.Element */ function Router(props: { - children: ReactNode; - routes: TRoute[]; - base: string; - history?: BrowserHistory | HashHistory | MemoryHistory | undefined; - staticLocation?: string; - middlewares?: ((routes: TRoute[]) => TRoute[])[]; - langService?: LangService; - id?: number | string; - initialStaticProps?: { props: any; name: string; url: string }; + children: ReactNode + routes: TRoute[] + base: string + history?: BrowserHistory | HashHistory | MemoryHistory | undefined + staticLocation?: string + middlewares?: ((routes: TRoute[]) => TRoute[])[] + langService?: LangService + id?: number | string + initialStaticProps?: { props: any; name: string; url: string } }): JSX.Element { /** * 0. LangService @@ -121,10 +121,10 @@ function Router(props: { const langService = useMemo(() => { if (!Routers.langService || (props.langService && isServer)) { - Routers.langService = props.langService; + Routers.langService = props.langService } - return Routers.langService; - }, [props.langService]); + return Routers.langService + }, [props.langService]) /** * 1. routes @@ -141,14 +141,14 @@ function Router(props: { langService, props.middlewares, props.id, - ); + ) // if is the first instance, register routes in Routers if (!Routers.routes || isServer) { - Routers.routes = routesList; + Routers.routes = routesList } - return routesList; - }, [props.routes, langService, props.middlewares, props.id]); + return routesList + }, [props.routes, langService, props.middlewares, props.id]) /** * 2. base @@ -158,9 +158,9 @@ function Router(props: { */ if (!Routers.base) { - Routers.base = props.base; + Routers.base = props.base } - const base = Routers.base; + const base = Routers.base /** * 3. history @@ -169,9 +169,9 @@ function Router(props: { */ if (props.history && !Routers.history) { - Routers.history = props.history; + Routers.history = props.history } - const history = Routers.history; + const history = Routers.history /** * 4 static location @@ -179,15 +179,15 @@ function Router(props: { */ if (props.staticLocation) { - Routers.staticLocation = props.staticLocation; + Routers.staticLocation = props.staticLocation } - const staticLocation: string | undefined = Routers.staticLocation; + const staticLocation: string | undefined = Routers.staticLocation /** * 5. reset is fist route visited */ if (isServer) { - Routers.isFirstRoute = true; + Routers.isFirstRoute = true } // -------------------------------------------------------------------------------- ROUTE CHANGE @@ -208,9 +208,9 @@ function Router(props: { currentRoute: action.value, routeIndex: state.routeIndex + 1, previousPageIsMount: true, - }; + } case "unmount-previous-page": - return { ...state, previousPageIsMount: false }; + return { ...state, previousPageIsMount: false } } }, { @@ -219,23 +219,23 @@ function Router(props: { previousPageIsMount: false, routeIndex: 0, }, - ); + ) /** * Enable paused on Router instance */ - const _waitingUrl = useRef(null); - const _paused = useRef(false); - const getPaused = () => _paused.current; + const _waitingUrl = useRef(null) + const _paused = useRef(false) + const getPaused = () => _paused.current const setPaused = (value: boolean) => { - _paused.current = value; + _paused.current = value if (!value && _waitingUrl.current) { - handleHistory(_waitingUrl.current); - _waitingUrl.current = null; + handleHistory(_waitingUrl.current) + _waitingUrl.current = null } - }; + } - const currentRouteRef = useRef(); + const currentRouteRef = useRef() /** * Handle history @@ -245,8 +245,8 @@ function Router(props: { const handleHistory = async (url = ""): Promise => { if (_paused.current) { - _waitingUrl.current = url; - return; + _waitingUrl.current = url + return } const matchingRoute = getRouteFromUrl({ @@ -254,33 +254,33 @@ function Router(props: { pRoutes: routes, pBase: props.base, id: props.id, - }); + }) - const notFoundRoute = getNotFoundRoute(props.routes); + const notFoundRoute = getNotFoundRoute(props.routes) if (!matchingRoute && !notFoundRoute) { - log(props.id, "matchingRoute not found & 'notFoundRoute' not found, return."); - return; + log(props.id, "matchingRoute not found & 'notFoundRoute' not found, return.") + return } const currentRouteUrl = - currentRouteRef.current?._context?.url ?? currentRouteRef.current?.url; - const matchingRouteUrl = matchingRoute?._context?.url ?? matchingRoute?.url; + currentRouteRef.current?._context?.url ?? currentRouteRef.current?.url + const matchingRouteUrl = matchingRoute?._context?.url ?? matchingRoute?.url if (currentRouteUrl === matchingRouteUrl) { - log(props.id, "this is the same route 'url', return."); - return; + log(props.id, "this is the same route 'url', return.") + return } // new route - const newRoute: TRoute = matchingRoute || notFoundRoute; - if (!newRoute) return; + const newRoute: TRoute = matchingRoute || notFoundRoute + if (!newRoute) return // prepare cache for new route data staticProps - const cache = staticPropsCache(); + const cache = staticPropsCache() // check if new route data as been store in cache // the matcher will match even if the URL ends with a slash - const fullUrl = removeLastCharFromString(newRoute._fullUrl, "/"); - const [urlWithoutHash] = fullUrl.split("#"); + const fullUrl = removeLastCharFromString(newRoute._fullUrl, "/") + const [urlWithoutHash] = fullUrl.split("#") /** * Request static props and cache it @@ -290,13 +290,13 @@ function Router(props: { const request = await newRoute.getStaticProps( newRoute.props, langService?.currentLang, - ); - Object.assign(newRoute.props, request); - cache.set(urlWithoutHash, request); + ) + Object.assign(newRoute.props, request) + cache.set(urlWithoutHash, request) } catch (e) { - console.error("requestStaticProps failed"); + console.error("requestStaticProps failed") } - }; + } // SERVER (first route) // prettier-ignore @@ -337,13 +337,13 @@ function Router(props: { } // Final process: update context currentRoute from dispatch method \o/ ! - dispatch({ type: "update-current-route", value: newRoute }); + dispatch({ type: "update-current-route", value: newRoute }) // & register this new route as currentRoute in local and in Routers store - currentRouteRef.current = newRoute; - Routers.currentRoute = newRoute; - Routers.isFirstRoute = false; - }; + currentRouteRef.current = newRoute + Routers.currentRoute = newRoute + Routers.isFirstRoute = false + } /** * Here we go! @@ -353,46 +353,46 @@ function Router(props: { * - Dispatch new routes states from RouterContext */ const historyListener = useMemo(() => { - if (!routes) return; + if (!routes) return const historyListener = () => { if (staticLocation && history) { - console.error(`You can't set history and staticLocation props together, return.`); - return; + console.error(`You can't set history and staticLocation props together, return.`) + return } // server if (staticLocation) { - handleHistory(staticLocation); - return; + handleHistory(staticLocation) + return // client } else if (history) { handleHistory( window.location.pathname + window.location.search + window.location.hash, - ); + ) return history?.listen(({ location }) => { - handleHistory(location.pathname + location.search + location.hash); - }); + handleHistory(location.pathname + location.search + location.hash) + }) // log error } else { - console.error(`An history or staticLocation props is required, return.`); - return; + console.error(`An history or staticLocation props is required, return.`) + return } - }; - return historyListener(); - }, [routes, history]); + } + return historyListener() + }, [routes, history]) // remove listener useEffect(() => { return () => { - log(props.id, "Stop to listen history."); - historyListener(); - }; - }, [historyListener]); + log(props.id, "Stop to listen history.") + historyListener() + } + }, [historyListener]) // -------------------------------------------------------------------------------- RENDER - const { currentRoute, previousRoute, routeIndex, previousPageIsMount } = reducerState; - const unmountPreviousPage = () => dispatch({ type: "unmount-previous-page" }); + const { currentRoute, previousRoute, routeIndex, previousPageIsMount } = reducerState + const unmountPreviousPage = () => dispatch({ type: "unmount-previous-page" }) return ( - ); + ) } -Router.displayName = componentName; -const MemoizedRouter = memo(Router); -export { MemoizedRouter as Router }; +Router.displayName = componentName +const MemoizedRouter = memo(Router) +export { MemoizedRouter as Router } diff --git a/src/components/Stack.tsx b/src/components/Stack.tsx index 22c2afe5..0173704c 100644 --- a/src/components/Stack.tsx +++ b/src/components/Stack.tsx @@ -1,23 +1,23 @@ -import React from "react"; -import { IRouterContext } from "./Router"; -import debug from "@wbe/debug"; -import { IRouteStack } from "../hooks/useStack"; -import { useRouter } from "../hooks/useRouter"; -import { isSSR } from "../core/helpers"; +import React from "react" +import { IRouterContext } from "./Router" +import debug from "@wbe/debug" +import { IRouteStack } from "../hooks/useStack" +import { useRouter } from "../hooks/useRouter" +import { isSSR } from "../core/helpers" export type TManageTransitions = { - previousPage: IRouteStack; - currentPage: IRouteStack; - unmountPreviousPage: () => void; -}; + previousPage: IRouteStack + currentPage: IRouteStack + unmountPreviousPage: () => void +} interface IProps { - className?: string; - manageTransitions?: (T: TManageTransitions) => Promise; + className?: string + manageTransitions?: (T: TManageTransitions) => Promise } -const componentName = "Stack"; -const log = debug(`router:${componentName}`); +const componentName = "Stack" +const log = debug(`router:${componentName}`) /** * @name Stack @@ -29,10 +29,10 @@ function Stack(props: IProps): JSX.Element { previousRoute, unmountPreviousPage, previousPageIsMount, - } = useRouter() as IRouterContext; + } = useRouter() as IRouterContext - const prevRef = React.useRef(null); - const currentRef = React.useRef(null); + const prevRef = React.useRef(null) + const currentRef = React.useRef(null) // Create the default sequential transition used // if manageTransitions props doesn't exist @@ -43,39 +43,39 @@ function Stack(props: IProps): JSX.Element { unmountPreviousPage, }: TManageTransitions): Promise => { return new Promise(async (resolve) => { - const $current = currentPage?.$element; - if ($current) $current.style.visibility = "hidden"; + const $current = currentPage?.$element + if ($current) $current.style.visibility = "hidden" if (previousPage) { - await previousPage.playOut(); - unmountPreviousPage(); + await previousPage.playOut() + unmountPreviousPage() } if (currentPage) { - await currentPage.isReadyPromise(); - if ($current) $current.style.visibility = "visible"; - await currentPage.playIn(); + await currentPage.isReadyPromise() + if ($current) $current.style.visibility = "visible" + await currentPage.playIn() } - resolve(); - }); + resolve() + }) }, - [] - ); + [], + ) // 2. animate when route state changed React[isSSR() ? "useEffect" : "useLayoutEffect"](() => { - if (!currentRoute) return; - (props.manageTransitions || sequencialTransition)({ + if (!currentRoute) return + ;(props.manageTransitions || sequencialTransition)({ previousPage: prevRef.current, currentPage: currentRef.current, unmountPreviousPage, } as TManageTransitions).then(() => { - unmountPreviousPage(); - }); - }, [routeIndex]); + unmountPreviousPage() + }) + }, [routeIndex]) const [PrevRoute, CurrRoute] = [ previousRoute?._context ?? previousRoute, currentRoute?._context ?? currentRoute, - ]; + ] return (
    e).join(" ")}> @@ -94,7 +94,7 @@ function Stack(props: IProps): JSX.Element { /> )}
    - ); + ) } -export { Stack }; +export { Stack } diff --git a/src/core/LangService.ts b/src/core/LangService.ts index f6200e8d..4f846c7f 100644 --- a/src/core/LangService.ts +++ b/src/core/LangService.ts @@ -1,52 +1,52 @@ -import { Routers } from "./Routers"; -import { compileUrl, createUrl } from "./core"; -import { isSSR, joinPaths, removeLastCharFromString } from "./helpers"; -import { TRoute } from "../components/Router"; -import debug from "@wbe/debug"; +import { Routers } from "./Routers" +import { compileUrl, createUrl } from "./core" +import { isSSR, joinPaths, removeLastCharFromString } from "./helpers" +import { TRoute } from "../components/Router" +import debug from "@wbe/debug" -const log = debug(`router:LangService`); +const log = debug(`router:LangService`) export type TLanguage = { - key: T | string; - name?: string; - default?: boolean; -}; + key: T | string + name?: string + default?: boolean +} class LangService { /** * contains available languages */ - public languages: TLanguage[]; + public languages: TLanguage[] /** * Current language object */ - public currentLang: TLanguage; + public currentLang: TLanguage /** * Default language object */ - public defaultLang: TLanguage; + public defaultLang: TLanguage /** * Browser language */ - public browserLang: TLanguage; + public browserLang: TLanguage /** * Show default language in URL */ - public showDefaultLangInUrl: boolean; + public showDefaultLangInUrl: boolean /** * Base URL of the app */ - public base: string; + public base: string /** * Static Location used for SSR context */ - public staticLocation: string; + public staticLocation: string /** * Init languages service @@ -61,22 +61,22 @@ class LangService { base = "/", staticLocation, }: { - languages: TLanguage[]; - showDefaultLangInUrl?: boolean; - base?: string; - staticLocation?: string; + languages: TLanguage[] + showDefaultLangInUrl?: boolean + base?: string + staticLocation?: string }) { if (languages?.length === 0) { - throw new Error("ERROR, no language is set."); + throw new Error("ERROR, no language is set.") } - this.languages = languages; + this.languages = languages // remove extract / at the end, if exist - this.base = removeLastCharFromString(base, "/", true); - this.staticLocation = staticLocation; - this.defaultLang = this.getDefaultLang(languages); - this.currentLang = this.getLangFromString() || this.defaultLang; - this.browserLang = this.getBrowserLang(languages); - this.showDefaultLangInUrl = showDefaultLangInUrl; + this.base = removeLastCharFromString(base, "/", true) + this.staticLocation = staticLocation + this.defaultLang = this.getDefaultLang(languages) + this.currentLang = this.getLangFromString() || this.defaultLang + this.browserLang = this.getBrowserLang(languages) + this.showDefaultLangInUrl = showDefaultLangInUrl } /** @@ -100,12 +100,12 @@ class LangService { currentRoute: TRoute = Routers.currentRoute, ): void { if (toLang.key === this.currentLang.key) { - log("setLang: This is the same language, exit."); - return; + log("setLang: This is the same language, exit.") + return } if (!this.langIsAvailable(toLang)) { - log(`setLang: lang ${toLang.key} is not available in languages list, exit.`); - return; + log(`setLang: lang ${toLang.key} is not available in languages list, exit.`) + return } // Translate currentRoute URL to new lang URL @@ -116,29 +116,29 @@ class LangService { ...(currentRoute.props?.params || {}), lang: toLang.key, }, - }); - log("preparedNewUrl", preparedNewUrl); + }) + log("preparedNewUrl", preparedNewUrl) // create newUrl variable to set in each condition - let newUrl: string; + let newUrl: string // choose force page reload in condition below - let chooseForcePageReload = forcePageReload; + let chooseForcePageReload = forcePageReload // 1. if default language should be always visible in URL if (this.showDefaultLangInUrl) { - newUrl = preparedNewUrl; + newUrl = preparedNewUrl } // 2. if toLang is default lang, need to REMOVE lang from URL else if (!this.showDefaultLangInUrl && this.isDefaultLangKey(toLang.key)) { - const urlPartToRemove = `${this.base}/${toLang.key}`; + const urlPartToRemove = `${this.base}/${toLang.key}` const newUrlWithoutBaseAndLang = preparedNewUrl.substring( urlPartToRemove.length, preparedNewUrl.length, - ); - newUrl = joinPaths([this.base, newUrlWithoutBaseAndLang]); - chooseForcePageReload = true; - log("2. after remove lang from URL", newUrl); + ) + newUrl = joinPaths([this.base, newUrlWithoutBaseAndLang]) + chooseForcePageReload = true + log("2. after remove lang from URL", newUrl) } // 3. if current lang is default lang, add /currentLang.key after base @@ -146,36 +146,36 @@ class LangService { const newUrlWithoutBase = preparedNewUrl.substring( this.base.length, preparedNewUrl.length, - ); - newUrl = joinPaths([this.base, "/", toLang.key as string, "/", newUrlWithoutBase]); - log("3. after add lang in URL", newUrl); + ) + newUrl = joinPaths([this.base, "/", toLang.key as string, "/", newUrlWithoutBase]) + log("3. after add lang in URL", newUrl) } // 4. other cases else { - newUrl = preparedNewUrl; - log("4, other case"); + newUrl = preparedNewUrl + log("4, other case") } if (!newUrl) { - log("newUrl is no set, do not reload or refresh, return.", newUrl); - return; + log("newUrl is no set, do not reload or refresh, return.", newUrl) + return } // register current language (not useful if we reload the app.) - this.currentLang = toLang; + this.currentLang = toLang // remove last / if exist and if he is not alone - newUrl = removeLastCharFromString(newUrl, "/", true); + newUrl = removeLastCharFromString(newUrl, "/", true) // reload or refresh with new URL - this.reloadOrRefresh(newUrl, chooseForcePageReload); + this.reloadOrRefresh(newUrl, chooseForcePageReload) } public redirectToBrowserLang(forcePageReload: boolean = true) { - log("browserLang object", this.browserLang); + log("browserLang object", this.browserLang) // If browser language doesn't match, redirect to default lang if (!this.browserLang) { - log("browserLang is not set, redirect to defaultLang"); - this.redirectToDefaultLang(forcePageReload); - return; + log("browserLang is not set, redirect to defaultLang") + this.redirectToDefaultLang(forcePageReload) + return } // We want to redirect only in case user is on / or /base/ if ( @@ -185,10 +185,10 @@ class LangService { // prepare path and build URL const newUrl = compileUrl(joinPaths([this.base, "/:lang"]), { lang: this.browserLang.key, - }); - log("redirect: to browser language >", { newUrl }); + }) + log("redirect: to browser language >", { newUrl }) // reload or refresh all application - this.reloadOrRefresh(newUrl, forcePageReload); + this.reloadOrRefresh(newUrl, forcePageReload) } } @@ -197,15 +197,15 @@ class LangService { * @param forcePageReload */ public redirectToDefaultLang(forcePageReload: boolean = true): void { - if (isSSR()) return; + if (isSSR()) return if (!this.showDefaultLangInUrl) { - log("redirect: URLs have a lang param or language is valid, don't redirect."); - return; + log("redirect: URLs have a lang param or language is valid, don't redirect.") + return } if (this.langIsAvailable(this.getLangFromString())) { - log("redirect: lang from URL is valid, don't redirect"); - return; + log("redirect: lang from URL is valid, don't redirect") + return } // We want to redirect only in case user is on / or /base/ if ( @@ -215,10 +215,10 @@ class LangService { // prepare path & build new URL const newUrl = compileUrl(joinPaths([this.base, "/:lang"]), { lang: this.defaultLang.key, - }); - log("redirect to default lang >", { newUrl }); + }) + log("redirect to default lang >", { newUrl }) // reload or refresh all application - this.reloadOrRefresh(newUrl, forcePageReload); + this.reloadOrRefresh(newUrl, forcePageReload) } } @@ -226,7 +226,7 @@ class LangService { * Current lang is default lang */ public isDefaultLangKey(langKey = this.currentLang.key): boolean { - return langKey === this.defaultLang.key; + return langKey === this.defaultLang.key } /** @@ -235,11 +235,11 @@ class LangService { public showLangInUrl(): boolean { // if option is true, always display lang in URL if (this.showDefaultLangInUrl) { - return true; + return true // if this option is false } else { // show in URL only if whe are not on the default lang - return !this.isDefaultLangKey(this.currentLang.key); + return !this.isDefaultLangKey(this.currentLang.key) } } @@ -262,7 +262,7 @@ class LangService { showLangInUrl = this.showLangInUrl(), ): TRoute[] { if (routes?.some((el) => !!el._langPath)) { - return routes; + return routes } /** @@ -274,7 +274,7 @@ class LangService { removeLastCharFromString( joinPaths([pShowLang && "/:lang", pPath !== "/" ? pPath : "/"]), "/", - ); + ) /** * Patch routes @@ -293,15 +293,15 @@ class LangService { */ const patchRoutes = (pRoutes, children = false) => { return pRoutes.map((route: TRoute) => { - const path = this.getLangPathByLang(route); - const hasChildren = route.children?.length > 0; - const showLang = !children && showLangInUrl; + const path = this.getLangPathByLang(route) + const hasChildren = route.children?.length > 0 + const showLang = !children && showLangInUrl - let langPath = {}; + let langPath = {} if (typeof route.path === "object") { Object.keys(route.path).forEach((lang) => { - langPath[lang] = patchLangParam(route.path[lang], showLang); - }); + langPath[lang] = patchLangParam(route.path[lang], showLang) + }) } // even if route.path is not an object, add his value to route.langPath object property @@ -309,8 +309,8 @@ class LangService { this.languages .map((el) => el.key) .forEach((key: string) => { - langPath[key] = patchLangParam(route.path as string, showLang); - }); + langPath[key] = patchLangParam(route.path as string, showLang) + }) } return { @@ -318,10 +318,10 @@ class LangService { path: patchLangParam(path, showLang), _langPath: Object.entries(langPath).length !== 0 ? langPath : null, ...(hasChildren ? { children: patchRoutes(route.children, true) } : {}), - }; - }); - }; - return patchRoutes(routes); + } + }) + } + return patchRoutes(routes) } // --------------------------------------------------------------------------- LOCAL @@ -344,15 +344,15 @@ class LangService { lang = this.getLangFromString(this.staticLocation || window.location.pathname)?.key || this.defaultLang.key, ): string { - let selectedPath: string; + let selectedPath: string if (typeof route.path === "string") { - selectedPath = route.path; + selectedPath = route.path } else if (typeof route.path === "object") { Object.keys(route.path).find((el) => { - if (el === lang) selectedPath = route.path?.[el]; - }); + if (el === lang) selectedPath = route.path?.[el] + }) } - return selectedPath; + return selectedPath } /** @@ -361,7 +361,7 @@ class LangService { * @param languages */ protected getDefaultLang(languages: TLanguage[]): TLanguage { - return languages.find((el) => el?.default) ?? languages[0]; + return languages.find((el) => el?.default) ?? languages[0] } /** @@ -369,16 +369,16 @@ class LangService { * @protected */ protected getBrowserLang(languages: TLanguage[]): TLanguage { - if (typeof navigator === "undefined") return; + if (typeof navigator === "undefined") return - const browserLanguage = navigator.language; - log("Browser language detected", browserLanguage); + const browserLanguage = navigator.language + log("Browser language detected", browserLanguage) return languages.find((lang) => lang.key.includes("-") ? (lang.key as string) === browserLanguage.toLowerCase() : (lang.key as string) === browserLanguage.toLowerCase().split("-")[0], - ); + ) } /** @@ -388,11 +388,11 @@ class LangService { public getLangFromString( pathname = this.staticLocation || window.location.pathname, ): TLanguage { - let pathnameWithoutBase = pathname.replace(this.base, "/"); - const firstPart = joinPaths([pathnameWithoutBase]).split("/")[1]; + let pathnameWithoutBase = pathname.replace(this.base, "/") + const firstPart = joinPaths([pathnameWithoutBase]).split("/")[1] return this.languages.find((language) => { - return firstPart === language.key; - }); + return firstPart === language.key + }) } /** @@ -403,7 +403,7 @@ class LangService { langObject: TLanguage, languesList = this.languages, ): boolean { - return languesList.some((lang) => lang.key === langObject?.key); + return languesList.some((lang) => lang.key === langObject?.key) } /** @@ -413,9 +413,9 @@ class LangService { * @protected */ protected reloadOrRefresh(newUrl: string, forcePageReload = true): void { - if (isSSR()) return; - forcePageReload ? window?.open(newUrl, "_self") : Routers.history.push(newUrl); + if (isSSR()) return + forcePageReload ? window?.open(newUrl, "_self") : Routers.history.push(newUrl) } } -export default LangService; +export default LangService diff --git a/src/core/Routers.ts b/src/core/Routers.ts index 89e5e065..1eaf0d67 100644 --- a/src/core/Routers.ts +++ b/src/core/Routers.ts @@ -1,48 +1,48 @@ -import LangService from "../core/LangService"; -import { BrowserHistory, HashHistory, MemoryHistory } from "history"; -import { TRoute } from "../components/Router"; +import LangService from "../core/LangService" +import { BrowserHistory, HashHistory, MemoryHistory } from "history" +import { TRoute } from "../components/Router" export type TRouters = { /** * Base URL */ - base: string; + base: string /** * Global routes list */ - routes: TRoute[]; + routes: TRoute[] /** * Global browser history */ - history: HashHistory | MemoryHistory | BrowserHistory; + history: HashHistory | MemoryHistory | BrowserHistory /** * Global static location */ - staticLocation: string; + staticLocation: string /** * Global route counter increment on each history push */ - routeCounter: number; + routeCounter: number /** * Global is first route state * Is first route is true if routerCounter === 1 */ - isFirstRoute: boolean; + isFirstRoute: boolean /** * Store current route * Allows to always know what is last currentRoute path (for LangSerivce) */ - currentRoute: TRoute; + currentRoute: TRoute /** * LangService instance (stored in Router) */ - langService: LangService; + langService: LangService /** * Cache of getStaticProps Promise results */ - staticPropsCache: { [x:string]: any } -}; + staticPropsCache: { [x: string]: any } +} /** * ROUTERS object allows to keep safe globales values between Routers instances @@ -57,5 +57,5 @@ export const Routers: TRouters = { isFirstRoute: true, currentRoute: undefined, langService: undefined, - staticPropsCache: {} -}; + staticPropsCache: {}, +} diff --git a/src/core/core.ts b/src/core/core.ts index b8446e39..7b01893c 100644 --- a/src/core/core.ts +++ b/src/core/core.ts @@ -1,22 +1,22 @@ -import { Routers } from "./Routers"; -import debug from "@wbe/debug"; -import { compile, match } from "path-to-regexp"; -import { TRoute } from "../components/Router"; -import LangService from "./LangService"; -import { joinPaths, removeLastCharFromString } from "./helpers"; +import { Routers } from "./Routers" +import debug from "@wbe/debug" +import { compile, match } from "path-to-regexp" +import { TRoute } from "../components/Router" +import LangService from "./LangService" +import { joinPaths, removeLastCharFromString } from "./helpers" -const componentName: string = "core"; -const log = debug(`router:${componentName}`); +const componentName: string = "core" +const log = debug(`router:${componentName}`) -export type TParams = { [x: string]: any }; -export type TQueryParams = { [x: string]: string }; +export type TParams = { [x: string]: any } +export type TQueryParams = { [x: string]: string } export type TOpenRouteParams = { - name: string; - params?: TParams; - queryParams?: TQueryParams; - hash?: string; -}; + name: string + params?: TParams + queryParams?: TQueryParams + hash?: string +} // ----------------------------------------------------------------------------- PUBLIC @@ -35,20 +35,20 @@ export function createUrl( allRoutes: TRoute[] = Routers.routes, langService = Routers.langService, ): string { - if (!allRoutes) return; - let urlToPush: string; + if (!allRoutes) return + let urlToPush: string if (typeof args === "object" && !langService) { log( "route.path object is not supported without langService. Use should use route.path string instead.", - ); + ) } // in case we receive a string if (typeof args === "string") { - urlToPush = args as string; + urlToPush = args as string if (!!langService) { - urlToPush = addLangToUrl(urlToPush); + urlToPush = addLangToUrl(urlToPush) } } @@ -59,40 +59,40 @@ export function createUrl( args.params = { ...args.params, lang: langService.currentLang.key, - }; + } } // add params to URL if exist - let queryParams = ""; + let queryParams = "" if (args?.queryParams) { - queryParams = "?"; + queryParams = "?" queryParams += Object.keys(args.queryParams) .map((key) => `${key}=${args?.queryParams[key]}`) - .join("&"); + .join("&") } // add hash to URL if exist - let hash = ""; + let hash = "" if (args?.hash) { - hash = "#" + args.hash; + hash = "#" + args.hash } // Get URL by the route name - urlToPush = getUrlByRouteName(allRoutes, args) + queryParams + hash; + urlToPush = getUrlByRouteName(allRoutes, args) + queryParams + hash // in other case return. } else { - console.warn("createUrl param isn't valid. to use createUrl return.", args); - return; + console.warn("createUrl param isn't valid. to use createUrl return.", args) + return } // Add base const addBaseToUrl = (url: string, base = Routers.base): string => - joinPaths([base === "/" ? "" : base, url]); + joinPaths([base === "/" ? "" : base, url]) // compile base if contains lang params - const newBase = compileUrl(base, { lang: Routers.langService?.currentLang.key }); + const newBase = compileUrl(base, { lang: Routers.langService?.currentLang.key }) // in each case, add base URL - urlToPush = addBaseToUrl(urlToPush, newBase); - return urlToPush; + urlToPush = addBaseToUrl(urlToPush, newBase) + return urlToPush } /** @@ -111,9 +111,9 @@ export function getSubRouterBase( ): string { // case langService is init, and we don't want to show default lang in URL, and we are on default lang. // /:lang is return as path, but we want to get no lang in returned base string - const addLang = Routers.langService?.showLangInUrl() && addLangToUrl ? "/:lang" : ""; - const pathAfterLang = path === "/:lang" ? getLangPath("/") : getLangPath(path); - return joinPaths([base, addLang, pathAfterLang]); + const addLang = Routers.langService?.showLangInUrl() && addLangToUrl ? "/:lang" : "" + const pathAfterLang = path === "/:lang" ? getLangPath("/") : getLangPath(path) + return joinPaths([base, addLang, pathAfterLang]) } /** @@ -129,11 +129,11 @@ export function getSubRouterRoutes( // case langService is init, and we don't want to show default lang in URL, and we are on default lang. // /:lang is return as path, but we want to search path with "/" instead const formattedPath = - !Routers.langService?.showLangInUrl() && path === "/:lang" ? "/" : path; + !Routers.langService?.showLangInUrl() && path === "/:lang" ? "/" : path return routes.find((route) => { - return getLangPath(route.path) === getLangPath(formattedPath); - })?.children; + return getLangPath(route.path) === getLangPath(formattedPath) + })?.children } /** @@ -153,15 +153,15 @@ export function getPathByRouteName( // specific case, we want to retrieve path of route with "/" route and langService is used, // we need to patch it with lang param if (route.path === "/" && Routers.langService) { - return "/:lang"; + return "/:lang" } else { - return route.path; + return route.path } } else { if (route.children) { - const next = getPathByRouteName(route.children, name); + const next = getPathByRouteName(route.children, name) if (next) { - return next; + return next } } } @@ -175,8 +175,8 @@ export function getPathByRouteName( * @param history */ export function openRoute(args: string | TOpenRouteParams, history = Routers?.history) { - const url = typeof args === "string" ? args : createUrl(args); - history?.push(url); + const url = typeof args === "string" ? args : createUrl(args) + history?.push(url) } /** @@ -193,31 +193,31 @@ export async function requestStaticPropsFromRoute({ middlewares, id, }: { - url: string; - base: string; - routes: TRoute[]; - langService?: LangService; - middlewares?: ((routes: TRoute[]) => TRoute[])[]; - id?: string | number; + url: string + base: string + routes: TRoute[] + langService?: LangService + middlewares?: ((routes: TRoute[]) => TRoute[])[] + id?: string | number }): Promise<{ props: any; name: string; url: string }> { const currentRoute = getRouteFromUrl({ pUrl: url, pBase: base, pRoutes: formatRoutes(routes, langService, middlewares, id), id, - }); + }) - const notFoundRoute = getNotFoundRoute(routes); + const notFoundRoute = getNotFoundRoute(routes) if (!currentRoute && !notFoundRoute) { - log(id, "currentRoute not found & 'notFoundRoute' not found, return."); - return; + log(id, "currentRoute not found & 'notFoundRoute' not found, return.") + return } // get out if (!currentRoute) { - log("No currentRoute, return"); - return; + log("No currentRoute, return") + return } // prepare returned obj @@ -225,7 +225,7 @@ export async function requestStaticPropsFromRoute({ props: null, name: currentRoute.name, url, - }; + } // await promise from getStaticProps if (currentRoute?.getStaticProps) { @@ -233,26 +233,26 @@ export async function requestStaticPropsFromRoute({ SSR_STATIC_PROPS.props = await currentRoute.getStaticProps( currentRoute.props, langService?.currentLang, - ); + ) } catch (e) { - log("fetch getStatic Props data error"); + log("fetch getStatic Props data error") } } - return SSR_STATIC_PROPS; + return SSR_STATIC_PROPS } // ----------------------------------------------------------------------------- MATCHER type TGetRouteFromUrl = { - pUrl: string; - pRoutes?: TRoute[]; - pBase?: string; - pCurrentRoute?: TRoute; - pMatcher?: any; - pParent?: TRoute; - id?: number | string; - urlWithoutHashAndQuery?: string; -}; + pUrl: string + pRoutes?: TRoute[] + pBase?: string + pCurrentRoute?: TRoute + pMatcher?: any + pParent?: TRoute + id?: number | string + urlWithoutHashAndQuery?: string +} /** * Get current route from URL, using path-to-regex @@ -265,10 +265,10 @@ export function getRouteFromUrl({ pMatcher, id, }: TGetRouteFromUrl): TRoute { - if (!pRoutes || pRoutes?.length === 0) return; + if (!pRoutes || pRoutes?.length === 0) return // extract queryParams params and hash - const { queryParams, hash, urlWithoutHashAndQuery } = extractQueryParamsAndHash(pUrl); + const { queryParams, hash, urlWithoutHashAndQuery } = extractQueryParamsAndHash(pUrl) function next({ pUrl, @@ -285,17 +285,17 @@ export function getRouteFromUrl({ const currentRoutePath = removeLastCharFromString( joinPaths([pBase, currentRoute.path as string]), "/", - ); - const matcher = match(currentRoutePath)(urlWithoutHashAndQuery); + ) + const matcher = match(currentRoutePath)(urlWithoutHashAndQuery) // prettier-ignore log(id, `url "${urlWithoutHashAndQuery}" match path "${currentRoutePath}"?`,!!matcher); // if current route path match with the param url if (matcher) { - const params = pMatcher?.params || matcher?.params; + const params = pMatcher?.params || matcher?.params const formatRouteObj = (route) => { - if (!route) return; + if (!route) return return { path: route?.path, url: compile(route.path as string)(params), @@ -315,17 +315,17 @@ export function getRouteFromUrl({ _fullPath: currentRoutePath, _fullUrl: pUrl, _langPath: route?._langPath, - }; - }; + } + } - const formattedCurrentRoute = formatRouteObj(currentRoute); + const formattedCurrentRoute = formatRouteObj(currentRoute) const routeObj = { ...formattedCurrentRoute, _context: pParent ? formatRouteObj(pParent) : formattedCurrentRoute, - }; + } - log(id, "match", routeObj); - return routeObj; + log(id, "match", routeObj) + return routeObj } // if not match @@ -339,17 +339,17 @@ export function getRouteFromUrl({ pParent: pParent || currentRoute, pBase: currentRoutePath, // parent base pMatcher: matcher, - }); + }) // only if matching, return this match, else continue to next iteration if (matchingChildren) { - return matchingChildren; + return matchingChildren } } } } - return next({ pUrl, urlWithoutHashAndQuery, pRoutes, pBase, pMatcher, id }); + return next({ pUrl, urlWithoutHashAndQuery, pRoutes, pBase, pMatcher, id }) } /** @@ -360,44 +360,44 @@ export function getRouteFromUrl({ export function getNotFoundRoute(routes: TRoute[]): TRoute { return routes?.find( (el) => el.path === "/:rest" || el.component?.displayName === "NotFoundPage", - ); + ) } export const extractQueryParamsAndHash = ( url: string, ): { - queryParams: { [x: string]: string }; - hash: string; - urlWithoutHashAndQuery: string; + queryParams: { [x: string]: string } + hash: string + urlWithoutHashAndQuery: string } => { - let queryParams = {}; - let hash = null; - const queryIndex = url.indexOf("?"); - const hashIndex = url.indexOf("#"); + let queryParams = {} + let hash = null + const queryIndex = url.indexOf("?") + const hashIndex = url.indexOf("#") if (queryIndex === -1 && hashIndex === -1) { - return { queryParams, hash, urlWithoutHashAndQuery: url }; + return { queryParams, hash, urlWithoutHashAndQuery: url } } // Extract hash if (hashIndex !== -1) { - hash = url.slice(hashIndex + 1); + hash = url.slice(hashIndex + 1) } // Extract queryParams parameters if (queryIndex !== -1) { const queryString = url.slice( queryIndex + 1, hashIndex !== -1 ? hashIndex : undefined, - ); - const searchParams = new URLSearchParams(queryString); - searchParams.forEach((value, key) => (queryParams[key] = value)); + ) + const searchParams = new URLSearchParams(queryString) + searchParams.forEach((value, key) => (queryParams[key] = value)) } // finally remove queryParams and hash from pathname for (let e of ["?", "#"]) { - url = url.includes(e) ? url.split(e)[0] : url; + url = url.includes(e) ? url.split(e)[0] : url } - return { queryParams, hash, urlWithoutHashAndQuery: url }; -}; + return { queryParams, hash, urlWithoutHashAndQuery: url } +} // ----------------------------------------------------------------------------- ROUTES @@ -420,8 +420,8 @@ export const extractQueryParamsAndHash = ( */ export function patchMissingRootRoute(routes: TRoute[] = Routers.routes): TRoute[] { if (!routes) { - log("routes doesnt exist, return", routes); - return; + log("routes doesnt exist, return", routes) + return } const rootPathExist = routes.some( (route) => @@ -431,15 +431,15 @@ export function patchMissingRootRoute(routes: TRoute[] = Routers.routes): TRoute )) || route.path === "/" || route.path === "/:lang", - ); + ) if (!rootPathExist) { routes.unshift({ path: "/", component: null, name: `auto-generate-slash-route-${Math.random()}`, - }); + }) } - return routes; + return routes } /** @@ -457,7 +457,7 @@ export function applyMiddlewaresToRoutes( (routes, middleware) => middleware(routes), preMiddlewareRoutes, ) || preMiddlewareRoutes - ); + ) } /** @@ -472,23 +472,23 @@ export function formatRoutes( id?: number | string, ): TRoute[] { if (!routes) { - console.error(id, "props.routes is missing or empty, return."); - return; + console.error(id, "props.routes is missing or empty, return.") + return } // For each instances - let routesList = patchMissingRootRoute(routes); + let routesList = patchMissingRootRoute(routes) // subRouter instances shouldn't inquired middlewares and LangService if (middlewares) { - routesList = applyMiddlewaresToRoutes(routesList, middlewares); + routesList = applyMiddlewaresToRoutes(routesList, middlewares) } // Only for first instance if (langService) { - routesList = langService.addLangParamToRoutes(routesList); + routesList = langService.addLangParamToRoutes(routesList) } - return routesList; + return routesList } // ----------------------------------------------------------------------------- URLS / PATH @@ -499,7 +499,7 @@ export function formatRoutes( * compile("foo/:id")({id: example-client}) // "foo/example-client" */ export function compileUrl(path: string, params?: TParams): string { - return compile(path)(params); + return compile(path)(params) } /** @@ -515,19 +515,19 @@ export function getFullPathByPath( lang: string = Routers.langService?.currentLang.key || undefined, basePath: string = null, ): string { - let localPath: string[] = [basePath]; + let localPath: string[] = [basePath] for (let route of routes) { - const langPath = route._langPath?.[lang]; - const routePath = route.path as string; + const langPath = route._langPath?.[lang] + const routePath = route.path as string const pathMatch = - (langPath === path || routePath === path) && route.name === routeName; + (langPath === path || routePath === path) && route.name === routeName // if path match on first level, keep path in local array and return it, stop here. if (pathMatch) { - localPath.push(langPath || routePath); - return joinPaths(localPath); + localPath.push(langPath || routePath) + return joinPaths(localPath) } // if not matching but as children, return it @@ -539,11 +539,11 @@ export function getFullPathByPath( routeName, lang, joinPaths(localPath), - ); + ) // return recursive Fn only if match, else continue to next iteration if (matchChildrenPath) { // keep path in local array - localPath.push(langPath || routePath); + localPath.push(langPath || routePath) // Return the function after localPath push return getFullPathByPath( route.children, @@ -551,7 +551,7 @@ export function getFullPathByPath( routeName, lang, joinPaths(localPath), - ); + ) } } } @@ -567,17 +567,17 @@ export function getUrlByRouteName(pRoutes: TRoute[], pParams: TOpenRouteParams): const next = (routes: TRoute[], params: TOpenRouteParams): string => { for (let route of routes) { const match = - route?.name === params.name || route.component?.displayName === params.name; + route?.name === params.name || route.component?.displayName === params.name if (match) { if (!route?.path) { - log("getUrlByRouteName > There is no route with this name, exit", params.name); - return; + log("getUrlByRouteName > There is no route with this name, exit", params.name) + return } let path = typeof route.path === "object" ? route.path[Object.keys(route.path)[0]] - : route.path; + : route.path // get full path const _fullPath = getFullPathByPath( @@ -585,22 +585,22 @@ export function getUrlByRouteName(pRoutes: TRoute[], pParams: TOpenRouteParams): path, route.name, pParams?.params?.lang, - ); + ) // build URL // console.log("_fullPath", _fullPath, params); - return compileUrl(_fullPath, params.params); + return compileUrl(_fullPath, params.params) } // if route has children else if (route.children?.length > 0) { // getUrlByRouteName > no match, recall recursively on children - const match = next(route.children, params); + const match = next(route.children, params) // return recursive Fn only if match, else, continue to next iteration - if (match) return match; + if (match) return match } } - }; - return next(pRoutes, pParams); + } + return next(pRoutes, pParams) } /** @@ -614,20 +614,20 @@ export function getLangPath( langPath: string | { [p: string]: string }, lang: string = Routers.langService?.currentLang.key, ) { - let path; + let path if (typeof langPath === "string") { - path = langPath; + path = langPath } else if (typeof langPath === "object") { - path = langPath?.[lang]; + path = langPath?.[lang] } const removeLangFromPath = (path: string): string => { if (path?.includes(`/:lang`)) { - return path?.replace("/:lang", ""); - } else return path; - }; + return path?.replace("/:lang", "") + } else return path + } - return removeLangFromPath(path); + return removeLangFromPath(path) } /** @@ -651,7 +651,7 @@ export function addLangToUrl( lang: string = Routers.langService?.currentLang.key, enable = Routers.langService?.showLangInUrl(), ): string { - if (!enable) return url; - url = joinPaths([`/${lang}`, url === "/" ? "" : url]); - return url; + if (!enable) return url + url = joinPaths([`/${lang}`, url === "/" ? "" : url]) + return url } diff --git a/src/core/helpers.ts b/src/core/helpers.ts index 49efeceb..cad14bd9 100644 --- a/src/core/helpers.ts +++ b/src/core/helpers.ts @@ -4,8 +4,8 @@ * @param join */ export function joinPaths(paths: string[], join: string = ""): string { - const preparePath = paths?.filter((str) => str).join(join); - return preventSlashes(preparePath); + const preparePath = paths?.filter((str) => str).join(join) + return preventSlashes(preparePath) } /** @@ -15,7 +15,7 @@ export function joinPaths(paths: string[], join: string = ""): string { * @param str */ export function preventSlashes(str: string): string { - return str.replace(/(https?:\/\/)|(\/)+/g, "$1$2"); + return str.replace(/(https?:\/\/)|(\/)+/g, "$1$2") } /** @@ -27,16 +27,16 @@ export function preventSlashes(str: string): string { export function removeLastCharFromString( str: string, lastChar: string, - exeptIfStringIsLastChar = true + exeptIfStringIsLastChar = true, ): string { - if (exeptIfStringIsLastChar && str === lastChar) return str; - if (str?.endsWith(lastChar)) str = str.slice(0, -1); - return str; + if (exeptIfStringIsLastChar && str === lastChar) return str + if (str?.endsWith(lastChar)) str = str.slice(0, -1) + return str } /** * Check if we are in SRR context */ export function isSSR() { - return !(typeof window != "undefined" && window.document); + return !(typeof window != "undefined" && window.document) } diff --git a/src/core/staticPropsCache.ts b/src/core/staticPropsCache.ts index 28659e19..a2557574 100644 --- a/src/core/staticPropsCache.ts +++ b/src/core/staticPropsCache.ts @@ -1,7 +1,7 @@ -import { Routers } from "./Routers"; -import debug from "@wbe/debug"; -const componentName: string = "cache"; -const log = debug(`router:${componentName}`); +import { Routers } from "./Routers" +import debug from "@wbe/debug" +const componentName: string = "cache" +const log = debug(`router:${componentName}`) /** * Cache used to store getStaticProps result @@ -12,25 +12,25 @@ export function staticPropsCache(cache = Routers.staticPropsCache) { * Get data in static props cache */ const get = (key: string): any => { - const dataAlreadyExist = Object.keys(cache).some((el) => el === key); + const dataAlreadyExist = Object.keys(cache).some((el) => el === key) if (!dataAlreadyExist) { - log(`"${key}" data doesn't exist in cache.`); - return null; + log(`"${key}" data doesn't exist in cache.`) + return null } - const dataCache = cache[key]; - log("data is already in cache, return it.", dataCache); - return dataCache; - }; + const dataCache = cache[key] + log("data is already in cache, return it.", dataCache) + return dataCache + } /** * Set Data in static props cache */ const set = (key: string, data): void => { - cache[key] = data; - log("cache after set", cache); - }; + cache[key] = data + log("cache after set", cache) + } return Object.freeze({ get, set, - }); + }) } diff --git a/src/hooks/useHistory.ts b/src/hooks/useHistory.ts index 3c0b106c..62301dc6 100644 --- a/src/hooks/useHistory.ts +++ b/src/hooks/useHistory.ts @@ -1,24 +1,24 @@ -import { BrowserHistory, HashHistory, MemoryHistory, Update } from "history"; -import React from "react"; -import { useRouter } from "./useRouter"; -import debug from "@wbe/debug"; +import { BrowserHistory, HashHistory, MemoryHistory, Update } from "history" +import React from "react" +import { useRouter } from "./useRouter" +import debug from "@wbe/debug" -const log = debug("router:useHistory"); +const log = debug("router:useHistory") /** * Handle router history */ export function useHistory( callback?: (e: Update) => void, - deps: any[] = [] + deps: any[] = [], ): BrowserHistory | HashHistory | MemoryHistory { - const { history } = useRouter(); + const { history } = useRouter() React.useEffect(() => { return history?.listen((e) => { - callback?.(e); - }); - }, [history, ...deps]); + callback?.(e) + }) + }, [history, ...deps]) - return history; + return history } diff --git a/src/hooks/useLang.ts b/src/hooks/useLang.ts index 522858b7..23d6a04c 100644 --- a/src/hooks/useLang.ts +++ b/src/hooks/useLang.ts @@ -1,39 +1,39 @@ -import LangService, { TLanguage } from "../core/LangService"; -import debug from "@wbe/debug"; -import React from "react"; -import { Routers } from "../core/Routers"; -import { useHistory } from "../hooks/useHistory"; -const log = debug("router:useLang"); +import LangService, { TLanguage } from "../core/LangService" +import debug from "@wbe/debug" +import React from "react" +import { Routers } from "../core/Routers" +import { useHistory } from "../hooks/useHistory" +const log = debug("router:useLang") /** * useLang */ export const useLang = ( - langService: LangService = Routers.langService + langService: LangService = Routers.langService, ): [lang: TLanguage, setLang: (lang: TLanguage | string, force: boolean) => void] => { - const [lang, setLang] = React.useState(langService?.currentLang); + const [lang, setLang] = React.useState(langService?.currentLang) // each time history change, set the current language in state useHistory(() => { if (!langService) { console.warn( - `useLang - LangService isn't init. You need to create a LangService instance before using this hook. https://github.com/cher-ami/router/tree/main#LangService` - ); - return; + `useLang - LangService isn't init. You need to create a LangService instance before using this hook. https://github.com/cher-ami/router/tree/main#LangService`, + ) + return } - setLang(langService.currentLang); - }, []); + setLang(langService.currentLang) + }, []) // Prepare setLocation function, who push in history function setNewLang(lang: TLanguage | string, force = true): void { - let langToPush; + let langToPush if (typeof lang === "string") { - langToPush = langService.languages.find((el) => el.key === lang); + langToPush = langService.languages.find((el) => el.key === lang) } else { - langToPush = lang; + langToPush = lang } - langService.setLang(langToPush, force); + langService.setLang(langToPush, force) } - return [lang, setNewLang]; -}; + return [lang, setNewLang] +} diff --git a/src/hooks/useLocation.ts b/src/hooks/useLocation.ts index e440584e..ad970288 100644 --- a/src/hooks/useLocation.ts +++ b/src/hooks/useLocation.ts @@ -1,27 +1,30 @@ -import { useState } from "react"; -import { useHistory } from "../hooks/useHistory"; -import { createUrl, TOpenRouteParams } from "../core/core"; -import debug from "@wbe/debug"; -import { useRouter } from "./useRouter"; -const log = debug("router:useLocation"); +import { useState } from "react" +import { useHistory } from "../hooks/useHistory" +import { createUrl, TOpenRouteParams } from "../core/core" +import debug from "@wbe/debug" +import { useRouter } from "./useRouter" +const log = debug("router:useLocation") /** * useLocation */ export const useLocation = (): [string, (param: string | TOpenRouteParams) => void] => { - const { staticLocation } = useRouter(); + const { staticLocation } = useRouter() const history = useHistory((event) => { - setPathname(event.location.pathname + event.location.search + event.location.hash); - }, []); + setPathname(event.location.pathname + event.location.search + event.location.hash) + }, []) // Get dynamic current location - const [pathname, setPathname] = useState(staticLocation || history?.location.pathname + history?.location.search + history?.location.hash); + const [pathname, setPathname] = useState( + staticLocation || + history?.location.pathname + history?.location.search + history?.location.hash, + ) // Prepare setLocation function, who push in history function setLocation(args: string & TOpenRouteParams): void { - history?.push(createUrl(args)); + history?.push(createUrl(args)) } - return [pathname, setLocation]; -}; + return [pathname, setLocation] +} diff --git a/src/hooks/useRouteCounter.ts b/src/hooks/useRouteCounter.ts index 89d13889..a6ab808c 100644 --- a/src/hooks/useRouteCounter.ts +++ b/src/hooks/useRouteCounter.ts @@ -1,33 +1,33 @@ -import { useHistory } from "../hooks/useHistory"; -import { useState } from "react"; -import { Routers } from "../core/Routers"; +import { useHistory } from "../hooks/useHistory" +import { useState } from "react" +import { Routers } from "../core/Routers" /** * use Route Counter */ export const useRouteCounter = (): { - routeCounter: number; - isFirstRoute: boolean; - resetCounter: () => void; + routeCounter: number + isFirstRoute: boolean + resetCounter: () => void } => { // get current route count - const [routeCounter, setRouteCounter] = useState(Routers.routeCounter); + const [routeCounter, setRouteCounter] = useState(Routers.routeCounter) // check if is first route - const [isFirstRoute, setIsFirstRoute] = useState(Routers.isFirstRoute); + const [isFirstRoute, setIsFirstRoute] = useState(Routers.isFirstRoute) // handle history useHistory(() => { - Routers.routeCounter = routeCounter + 1; - setRouteCounter(routeCounter + 1); + Routers.routeCounter = routeCounter + 1 + setRouteCounter(routeCounter + 1) - Routers.isFirstRoute = false; - setIsFirstRoute(false); - }, [routeCounter, isFirstRoute]); + Routers.isFirstRoute = false + setIsFirstRoute(false) + }, [routeCounter, isFirstRoute]) // allow to reset counter if needed (after first redirection for example-client) const resetCounter = () => { - setRouteCounter(1); - setIsFirstRoute(true); - }; + setRouteCounter(1) + setIsFirstRoute(true) + } - return { routeCounter, isFirstRoute, resetCounter }; -}; + return { routeCounter, isFirstRoute, resetCounter } +} diff --git a/src/hooks/useRouter.ts b/src/hooks/useRouter.ts index afbef055..af302c8a 100644 --- a/src/hooks/useRouter.ts +++ b/src/hooks/useRouter.ts @@ -1,13 +1,13 @@ -import { useContext } from "react"; +import { useContext } from "react" import { IRouterContext, IRouterContextStackStates, RouterContext, -} from "../components/Router"; +} from "../components/Router" /** * Returns current router instance context * Instance depend of inside witch provider this function is called */ export const useRouter = () => - useContext>(RouterContext); + useContext>(RouterContext) diff --git a/src/hooks/useStack.ts b/src/hooks/useStack.ts index 9d6575e0..2e7104e6 100644 --- a/src/hooks/useStack.ts +++ b/src/hooks/useStack.ts @@ -4,20 +4,20 @@ import { useEffect, useImperativeHandle, useMemo, -} from "react"; +} from "react" export interface IUseStack extends Omit { - handleRef: ForwardedRef; - rootRef: MutableRefObject; + handleRef: ForwardedRef + rootRef: MutableRefObject } export interface IRouteStack { - componentName: string; - playIn?: () => Promise; - playOut?: () => Promise; - isReady?: boolean; - $element: HTMLElement; - isReadyPromise?: () => Promise; + componentName: string + playIn?: () => Promise + playOut?: () => Promise + isReady?: boolean + $element: HTMLElement + isReadyPromise?: () => Promise } /** @@ -34,17 +34,17 @@ export const useStack = ({ }: IUseStack) => { // create deferred promise who we can resolve in the scope const deferredPromise = useMemo(() => { - const deferred: any = {}; + const deferred: any = {} deferred.promise = new Promise((resolve) => { - deferred.resolve = resolve; - }); - return deferred; - }, []); + deferred.resolve = resolve + }) + return deferred + }, []) // resolve deferred if isReady param is true useEffect(() => { - isReady && deferredPromise.resolve(); - }, [isReady]); + isReady && deferredPromise.resolve() + }, [isReady]) useImperativeHandle( handleRef, @@ -57,10 +57,10 @@ export const useStack = ({ isReady, isReadyPromise: () => deferredPromise.promise, $element: rootRef.current, - }; + } - return handleRouteCallback; + return handleRouteCallback }, - [deferredPromise] - ); -}; + [deferredPromise], + ) +} diff --git a/src/index.ts b/src/index.ts index 2ea41a6b..40498842 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ -export { Routers } from "./core/Routers"; -export { Router } from "./components/Router"; -export type { TRoute } from "./components/Router"; -export { Link } from "./components/Link"; +export { Routers } from "./core/Routers" +export { Router } from "./components/Router" +export type { TRoute } from "./components/Router" +export { Link } from "./components/Link" export { createUrl, openRoute, @@ -9,19 +9,19 @@ export { getSubRouterRoutes, getPathByRouteName, requestStaticPropsFromRoute, -} from "./core/core"; -export type { TOpenRouteParams } from "./core/core"; +} from "./core/core" +export type { TOpenRouteParams } from "./core/core" -export { Stack } from "./components/Stack"; -export type { TManageTransitions } from "./components/Stack"; +export { Stack } from "./components/Stack" +export type { TManageTransitions } from "./components/Stack" -export { useRouter } from "./hooks/useRouter"; -export { useLocation } from "./hooks/useLocation"; -export { useHistory } from "./hooks/useHistory"; -export { useRouteCounter } from "./hooks/useRouteCounter"; -export { useStack } from "./hooks/useStack"; -export { useLang } from "./hooks/useLang"; -export type { IRouteStack } from "./hooks/useStack"; +export { useRouter } from "./hooks/useRouter" +export { useLocation } from "./hooks/useLocation" +export { useHistory } from "./hooks/useHistory" +export { useRouteCounter } from "./hooks/useRouteCounter" +export { useStack } from "./hooks/useStack" +export { useLang } from "./hooks/useLang" +export type { IRouteStack } from "./hooks/useStack" -export { default as LangService } from "./core/LangService"; -export type { TLanguage } from "./core/LangService"; +export { default as LangService } from "./core/LangService" +export type { TLanguage } from "./core/LangService" diff --git a/src/tests/LangService.test.tsx b/src/tests/LangService.test.tsx index dbdba4b5..a1e69ac0 100644 --- a/src/tests/LangService.test.tsx +++ b/src/tests/LangService.test.tsx @@ -2,22 +2,22 @@ * @vitest-environment jsdom */ -import { vi, it, expect, describe, beforeAll, afterEach } from "vitest"; -import React from "react"; -import { Router } from "../components/Router"; -import LangService from "../core/LangService"; -import { Link } from "../components/Link"; -import { act, render } from "@testing-library/react"; -import { TRoute } from "../components/Router"; -import { createBrowserHistory } from "history"; +import { vi, it, expect, describe, beforeAll, afterEach } from "vitest" +import React from "react" +import { Router } from "../components/Router" +import LangService from "../core/LangService" +import { Link } from "../components/Link" +import { act, render } from "@testing-library/react" +import { TRoute } from "../components/Router" +import { createBrowserHistory } from "history" -const locales = [{ key: "en" }, { key: "fr" }, { key: "de" }]; +const locales = [{ key: "en" }, { key: "fr" }, { key: "de" }] const routesList: TRoute[] = [ { path: "/", name: "home" }, { path: "/foo", name: "foo" }, -]; +] -const mockClickHandler = vi.fn(); +const mockClickHandler = vi.fn() const App = ({ base = "/", to = "/foo", langService }) => { return ( { children={"foo"} /> - ); -}; + ) +} -const windowOpenMock = vi.fn(); +const windowOpenMock = vi.fn() beforeAll(() => { - const location = window.location; - delete global.window.location; - global.window.location = Object.assign({}, location); - global.window.open = windowOpenMock; -}); + const location = window.location + delete global.window.location + global.window.location = Object.assign({}, location) + global.window.open = windowOpenMock +}) afterEach(() => { - windowOpenMock.mockClear(); -}); + windowOpenMock.mockClear() +}) /** * redirect @@ -55,37 +55,37 @@ afterEach(() => { * */ it("should redirect to default lang", () => { - const langService = new LangService({ languages: locales, base: "/" }); - render(); + const langService = new LangService({ languages: locales, base: "/" }) + render() act(() => { - langService.redirectToDefaultLang(true); - }); - const defaultLangKey = langService.defaultLang.key; - expect(window.open).toHaveBeenCalledWith(`/${defaultLangKey}`, "_self"); -}); + langService.redirectToDefaultLang(true) + }) + const defaultLangKey = langService.defaultLang.key + expect(window.open).toHaveBeenCalledWith(`/${defaultLangKey}`, "_self") +}) it("should redirect to default lang with custom base", () => { - const langService = new LangService({ languages: locales, base: "/" }); - render(); + const langService = new LangService({ languages: locales, base: "/" }) + render() act(() => { - langService.redirectToDefaultLang(true); - }); - const defaultLangKey = langService.defaultLang.key; - expect(window.open).toHaveBeenCalledWith(`/${defaultLangKey}`, "_self"); -}); + langService.redirectToDefaultLang(true) + }) + const defaultLangKey = langService.defaultLang.key + expect(window.open).toHaveBeenCalledWith(`/${defaultLangKey}`, "_self") +}) it("should not redirect to default lang if showDefaultLangInUrl is set to false", () => { const langService = new LangService({ languages: locales, base: "/", showDefaultLangInUrl: false, - }); - render(); + }) + render() act(() => { - langService.redirectToDefaultLang(true); - }); - expect(window.open).toHaveBeenCalledTimes(0); -}); + langService.redirectToDefaultLang(true) + }) + expect(window.open).toHaveBeenCalledTimes(0) +}) /** * Add lang path param allows to test the same array before and after middleware @@ -106,7 +106,7 @@ const routesListLang: TRoute[] = [ { path: "/:id" }, ], }, -]; +] const patchedRoutesListLang: TRoute[] = [ { @@ -124,13 +124,13 @@ const patchedRoutesListLang: TRoute[] = [ { path: "/:id", _langPath: { en: "/:id", fr: "/:id", de: "/:id" } }, ], }, -]; +] describe("addLangParamToRoutes", () => { - const langService = new LangService({ languages: locales, base: "/" }); + const langService = new LangService({ languages: locales, base: "/" }) it("should patch all first level routes if LangService is init", () => { expect(langService.addLangParamToRoutes(routesListLang, true)).toEqual( patchedRoutesListLang, - ); - }); -}); + ) + }) +}) diff --git a/src/tests/Link.test.tsx b/src/tests/Link.test.tsx index f4c61867..79ed96a1 100644 --- a/src/tests/Link.test.tsx +++ b/src/tests/Link.test.tsx @@ -2,31 +2,31 @@ * @vitest-environment jsdom */ -import { vi, it, expect, describe } from "vitest"; -import React from "react"; -import { render, fireEvent } from "@testing-library/react"; -import { Routers } from "../core/Routers"; -import LangService from "../core/LangService"; -import { TOpenRouteParams } from "../core/core"; -import { Link } from ".."; -import { Router } from ".."; -import { TRoute } from ".."; -import { createBrowserHistory } from "history"; +import { vi, it, expect, describe } from "vitest" +import React from "react" +import { render, fireEvent } from "@testing-library/react" +import { Routers } from "../core/Routers" +import LangService from "../core/LangService" +import { TOpenRouteParams } from "../core/core" +import { Link } from ".." +import { Router } from ".." +import { TRoute } from ".." +import { createBrowserHistory } from "history" -const locales = [{ key: "en" }, { key: "fr" }, { key: "de" }]; +const locales = [{ key: "en" }, { key: "fr" }, { key: "de" }] const routesList: TRoute[] = [ { path: "/", component: null, name: "HomePage" }, { path: "/foo", component: null, name: "FooPage" }, { path: "/bar/:id", component: null, name: "BarPage" }, -]; +] -const mockClickHandler = vi.fn(); +const mockClickHandler = vi.fn() const App = ({ base = "/", to }: { base: string; to: string | TOpenRouteParams }) => { const langService = new LangService({ languages: locales, base, showDefaultLangInUrl: false, - }); + }) return ( - ); -}; + ) +} describe("Link", () => { it("should renders proper attributes", () => { - const { container } = render(); - const link: any = container.firstChild; - expect(link.tagName).toBe("A"); - expect(link.className).toBe("Link containerLink"); - expect(link.getAttribute("href")).toBe("/foo"); - expect(link.textContent).toBe("Foo"); - }); + const { container } = render() + const link: any = container.firstChild + expect(link.tagName).toBe("A") + expect(link.className).toBe("Link containerLink") + expect(link.getAttribute("href")).toBe("/foo") + expect(link.textContent).toBe("Foo") + }) // Can't test base URL added on link because the base /master is store in "Routers" object // and this one is used by Link > createUrl() @@ -58,7 +58,7 @@ describe("Link", () => { // const { container } = render(); // const link: any = container.firstChild; // expect(link.getAttribute("href")).toBe("/master/foo"); - }); + }) // FIXME now we need to create LangService instance it("should show default lang in href link", async () => { @@ -66,38 +66,38 @@ describe("Link", () => { // const { container } = await render(); // const href = (container.firstChild as HTMLLinkElement).getAttribute("href"); // expect(href).toBe("/foo"); - }); + }) // FIXME now we need to create LangService instance - it("shouldn't show default lang in href link", async () => {}); + it("shouldn't show default lang in href link", async () => {}) it("should execute callback on click", () => { - const { container } = render(); - fireEvent.click(container.firstChild); - expect(mockClickHandler.mock.calls.length).toBe(1); - fireEvent.click(container.firstChild); - fireEvent.click(container.firstChild); - expect(mockClickHandler.mock.calls.length).toBe(3); - }); + const { container } = render() + fireEvent.click(container.firstChild) + expect(mockClickHandler.mock.calls.length).toBe(1) + fireEvent.click(container.firstChild) + fireEvent.click(container.firstChild) + expect(mockClickHandler.mock.calls.length).toBe(3) + }) it("should return the right href URL", () => { - const { container } = render(); - fireEvent.click(container.firstChild); - expect(Routers.history.location.pathname).toBe("/foo"); - }); + const { container } = render() + fireEvent.click(container.firstChild) + expect(Routers.history.location.pathname).toBe("/foo") + }) it("should return the right href URL with param", () => { const { container } = render( , - ); - fireEvent.click(container.firstChild); - expect(Routers.history.location.pathname).toBe("/bar/test"); - }); + ) + fireEvent.click(container.firstChild) + expect(Routers.history.location.pathname).toBe("/bar/test") + }) it("should push in history on click", () => { - const { container } = render(); - fireEvent.click(container.firstChild); - expect(Routers.history.location.pathname).toBe("/bar"); - expect(Routers.history.action).toBe("PUSH"); - }); -}); + const { container } = render() + fireEvent.click(container.firstChild) + expect(Routers.history.location.pathname).toBe("/bar") + expect(Routers.history.action).toBe("PUSH") + }) +}) diff --git a/src/tests/Router.test.tsx b/src/tests/Router.test.tsx index 95861d63..ca24f5af 100644 --- a/src/tests/Router.test.tsx +++ b/src/tests/Router.test.tsx @@ -2,11 +2,11 @@ * @vitest-environment jsdom */ -import { it, expect, describe } from "vitest"; -import React from "react"; -import { TRoute, Router } from ".."; -import { render } from "@testing-library/react"; -import { createBrowserHistory } from "history"; +import { it, expect, describe } from "vitest" +import React from "react" +import { TRoute, Router } from ".." +import { render } from "@testing-library/react" +import { createBrowserHistory } from "history" describe("Router", () => { const routesList: TRoute[] = [ @@ -15,19 +15,19 @@ describe("Router", () => { component: null, name: "Home", }, - ]; + ] it("should be defined", () => { - expect(Router).toBeDefined(); - }); + expect(Router).toBeDefined() + }) it("should return a children", () => { const { container } = render(
    app
    , - ); - const router = container.firstChild; - expect(router.textContent).toBe("app"); - }); -}); + ) + const router = container.firstChild + expect(router.textContent).toBe("app") + }) +}) diff --git a/src/tests/Stack.test.ts b/src/tests/Stack.test.ts index 026c09d4..86bc78d8 100644 --- a/src/tests/Stack.test.ts +++ b/src/tests/Stack.test.ts @@ -1,8 +1,8 @@ -import { Stack } from ".."; -import { it, expect, describe } from "vitest"; +import { Stack } from ".." +import { it, expect, describe } from "vitest" describe("Stack", () => { it("should be defined", () => { - expect(Stack).toBeDefined(); - }); -}); + expect(Stack).toBeDefined() + }) +}) diff --git a/src/tests/_fixtures/routeList.ts b/src/tests/_fixtures/routeList.ts index a98c0e4c..e682fe26 100644 --- a/src/tests/_fixtures/routeList.ts +++ b/src/tests/_fixtures/routeList.ts @@ -1,4 +1,4 @@ -import { TRoute } from ".."; +import { TRoute } from ".." export const routeList: TRoute[] = [ { @@ -24,8 +24,8 @@ export const routeList: TRoute[] = [ getStaticProps: async (props, currentLang) => new Promise((resolve) => { setTimeout(() => { - resolve({ data: {} }); - }, 100); + resolve({ data: {} }) + }, 100) }), children: [ { @@ -62,8 +62,8 @@ export const routeList: TRoute[] = [ getStaticProps: async (props) => new Promise((resolve) => { setTimeout(() => { - resolve({ fetchData: {} }); - }, 100); + resolve({ fetchData: {} }) + }, 100) }), }, ], @@ -102,4 +102,4 @@ export const routeList: TRoute[] = [ path: "/:rest", name: "NotFoundPage", }, -]; +] diff --git a/src/tests/core.test.ts b/src/tests/core.test.ts index cc129016..23f84d9a 100644 --- a/src/tests/core.test.ts +++ b/src/tests/core.test.ts @@ -2,7 +2,7 @@ * @vitest-environment jsdom */ -import { it, expect, describe } from "vitest"; +import { it, expect, describe } from "vitest" import { compileUrl, getPathByRouteName, @@ -18,10 +18,10 @@ import { getSubRouterRoutes, patchMissingRootRoute, applyMiddlewaresToRoutes, -} from "../core/core"; -import { preventSlashes } from "../core/helpers"; -import { routeList } from "./_fixtures/routeList"; -import { Routers, LangService } from ".."; +} from "../core/core" +import { preventSlashes } from "../core/helpers" +import { routeList } from "./_fixtures/routeList" +import { Routers, LangService } from ".." /** * Public @@ -33,14 +33,14 @@ import { Routers, LangService } from ".."; describe("public", () => { describe("createUrl", () => { it("should create URL properly", () => { - const base = "/"; - expect(createUrl("/", base, routeList)).toBe("/"); - expect(createUrl("/foo", base, routeList)).toBe("/foo"); - expect(createUrl({ name: "ZooPage" }, base, routeList)).toBe("/hello/foo/zoo"); - }); + const base = "/" + expect(createUrl("/", base, routeList)).toBe("/") + expect(createUrl("/foo", base, routeList)).toBe("/foo") + expect(createUrl({ name: "ZooPage" }, base, routeList)).toBe("/hello/foo/zoo") + }) it("should create URL with params and hash", () => { - const base = "/custom-base/"; + const base = "/custom-base/" const routes = [ { path: "/a" }, { @@ -48,11 +48,11 @@ describe("public", () => { name: "b-page", children: [{ path: "/c", name: "c-page" }, { path: "/d" }], }, - ]; + ] // test single param expect( createUrl({ name: "b-page", queryParams: { foo: "bar" } }, base, routes), - ).toBe(`${base}b?foo=bar`); + ).toBe(`${base}b?foo=bar`) // test multiple params expect( @@ -61,15 +61,15 @@ describe("public", () => { base, routes, ), - ).toBe(`${base}b?foo=bar&zoo=a,b`); + ).toBe(`${base}b?foo=bar&zoo=a,b`) // test hash expect(createUrl({ name: "b-page", hash: "hello" }, base, routes)).toBe( `${base}b#hello`, - ); + ) expect(createUrl({ name: "c-page", hash: "hello" }, base, routes)).toBe( `${base}b/c#hello`, - ); + ) // test both expect( @@ -78,61 +78,61 @@ describe("public", () => { base, routes, ), - ).toBe(`${base}b/c?foo=bar#hello`); - }); - }); + ).toBe(`${base}b/c?foo=bar#hello`) + }) + }) describe("getSubRouterBase", () => { it("should return subRouter base URL", () => { - expect(getSubRouterBase("/foo", "")).toBe("/foo"); - expect(getSubRouterBase("/foo", "/")).toBe("/foo"); - expect(getSubRouterBase("/foo", "/hello/")).toBe("/hello/foo"); - expect(getSubRouterBase("/foo", "/hello")).toBe("/hello/foo"); + expect(getSubRouterBase("/foo", "")).toBe("/foo") + expect(getSubRouterBase("/foo", "/")).toBe("/foo") + expect(getSubRouterBase("/foo", "/hello/")).toBe("/hello/foo") + expect(getSubRouterBase("/foo", "/hello")).toBe("/hello/foo") expect(getSubRouterBase("/foo", "/custom/base/hello/")).toBe( "/custom/base/hello/foo", - ); + ) Routers.langService = new LangService({ languages: [{ key: "en" }, { key: "fr" }], - }); - const langPathTest = { en: "/foo-en", fr: "/foo-fr" }; - expect(getSubRouterBase(langPathTest, "/base/", true)).toBe("/base/:lang/foo-en"); - expect(getSubRouterBase(langPathTest, "/base/", false)).toBe("/base/foo-en"); - Routers.langService = undefined; - }); + }) + const langPathTest = { en: "/foo-en", fr: "/foo-fr" } + expect(getSubRouterBase(langPathTest, "/base/", true)).toBe("/base/:lang/foo-en") + expect(getSubRouterBase(langPathTest, "/base/", false)).toBe("/base/foo-en") + Routers.langService = undefined + }) it("should return subRouter base URL with 'showDefaultLangInUrl: false' option", () => { Routers.langService = new LangService({ languages: [{ key: "en" }, { key: "fr" }], showDefaultLangInUrl: false, - }); - ["/", "/foo", "/foo/bar/biz"].forEach((e) => { - expect(getSubRouterBase(e, "/base/", true)).toBe(`/base${e}`); - expect(getSubRouterBase(e, "/base/", false)).toBe(`/base${e}`); - }); - Routers.langService = undefined; - }); - }); + }) + ;["/", "/foo", "/foo/bar/biz"].forEach((e) => { + expect(getSubRouterBase(e, "/base/", true)).toBe(`/base${e}`) + expect(getSubRouterBase(e, "/base/", false)).toBe(`/base${e}`) + }) + Routers.langService = undefined + }) + }) describe("getSubRouterRoutes", () => { it("should return subRouter route list", () => { - const homeChildren = getSubRouterRoutes("/", routeList); - expect(homeChildren).toEqual(routeList.find((e) => e.name === "HomePage").children); - const aboutChildren = getSubRouterRoutes("/about", routeList); + const homeChildren = getSubRouterRoutes("/", routeList) + expect(homeChildren).toEqual(routeList.find((e) => e.name === "HomePage").children) + const aboutChildren = getSubRouterRoutes("/about", routeList) expect(aboutChildren).toEqual( routeList.find((e) => e.name === "AboutPage").children, - ); - }); - }); + ) + }) + }) describe("getPathByRouteName", () => { it("should return the right path with name", () => { - expect(getPathByRouteName(routeList, "HelloPage")).toEqual("/hello"); - expect(getPathByRouteName(routeList, "EndPage")).toEqual("/end"); - expect(getPathByRouteName(routeList, "FooPage")).toEqual("/foo"); - expect(getPathByRouteName(routeList, "ZooPage")).toEqual("/zoo/:id?"); - }); - }); + expect(getPathByRouteName(routeList, "HelloPage")).toEqual("/hello") + expect(getPathByRouteName(routeList, "EndPage")).toEqual("/end") + expect(getPathByRouteName(routeList, "FooPage")).toEqual("/foo") + expect(getPathByRouteName(routeList, "ZooPage")).toEqual("/zoo/:id?") + }) + }) describe("getStaticPropsFromRoute", () => { it("should return promise result of staticProps request", async () => { @@ -140,15 +140,15 @@ describe("public", () => { url: "/hello", base: "/", routes: routeList, - }); + }) expect(ssrStaticProps).toEqual({ props: { data: {} }, name: "HelloPage", url: "/hello", - }); - }); - }); -}); + }) + }) + }) +}) /** * Matcher @@ -157,7 +157,7 @@ describe("public", () => { * */ describe("matcher", () => { - const base = "/custom/base"; + const base = "/custom/base" describe("getRouteFromUrl", () => { it("should get right route from URL", () => { @@ -165,59 +165,59 @@ describe("matcher", () => { pUrl: preventSlashes(`${base}/bar/my-id`), pRoutes: routeList, pBase: base, - }); - expect(getRoute._fullUrl).toBe(`${base}/bar/my-id`); - expect(getRoute._fullPath).toBe(`${base}/bar/:id`); - expect(getRoute.path).toBe("/bar/:id"); - expect(getRoute.url).toBe("/bar/my-id"); - expect(getRoute.name).toBe(`BarPage`); - const routeProps = { params: { id: "my-id" }, color: "blue" }; - expect(getRoute.props).toEqual(routeProps); + }) + expect(getRoute._fullUrl).toBe(`${base}/bar/my-id`) + expect(getRoute._fullPath).toBe(`${base}/bar/:id`) + expect(getRoute.path).toBe("/bar/:id") + expect(getRoute.url).toBe("/bar/my-id") + expect(getRoute.name).toBe(`BarPage`) + const routeProps = { params: { id: "my-id" }, color: "blue" } + expect(getRoute.props).toEqual(routeProps) // no parent route, so context object need to return same route information - expect(getRoute._context.props).toEqual(routeProps); + expect(getRoute._context.props).toEqual(routeProps) getRoute = getRouteFromUrl({ pUrl: "/hello-2", pRoutes: routeList, pBase: "/", - }); - expect(getRoute._fullPath).toBe(`/hello-2`); + }) + expect(getRoute._fullPath).toBe(`/hello-2`) getRoute = getRouteFromUrl({ pUrl: "/end", pRoutes: routeList, pBase: "/", - }); - expect(getRoute.name).toBe(`EndPage`); - }); + }) + expect(getRoute.name).toBe(`EndPage`) + }) it("should get right route from URL with subRoute", () => { const getRoute = getRouteFromUrl({ pUrl: "/about/route2/super-param/foo4", pRoutes: routeList, pBase: "/", - }); - - expect(getRoute._fullPath).toBe(`/about/route2/:testParam?/foo4`); - expect(getRoute.path).toBe("/foo4"); - expect(getRoute._fullUrl).toBe(`/about/route2/super-param/foo4`); - expect(getRoute.url).toBe("/foo4"); - expect(getRoute.base).toBe("/about/route2/:testParam?"); - expect(getRoute.name).toBe("Foo4Page"); + }) + + expect(getRoute._fullPath).toBe(`/about/route2/:testParam?/foo4`) + expect(getRoute.path).toBe("/foo4") + expect(getRoute._fullUrl).toBe(`/about/route2/super-param/foo4`) + expect(getRoute.url).toBe("/foo4") + expect(getRoute.base).toBe("/about/route2/:testParam?") + expect(getRoute.name).toBe("Foo4Page") expect(getRoute.props).toEqual({ color: "red", params: { testParam: "super-param" }, - }); - }); + }) + }) it("should not get route from bad URL and return undefined", () => { const getRoute = getRouteFromUrl({ pUrl: preventSlashes(`${base}/bar/foo/bar/`), pRoutes: routeList, pBase: base, - }); - expect(getRoute).toBeUndefined(); - }); + }) + expect(getRoute).toBeUndefined() + }) it("should get route from URL with params and hash", () => { const pRoutes = [ @@ -226,39 +226,39 @@ describe("matcher", () => { path: "/b", children: [{ path: "/c" }, { path: "/d" }], }, - ]; + ] // only params - let getRoute = getRouteFromUrl({ pRoutes, pUrl: "/b?foo=bar&lang=en" }); - expect(getRoute.queryParams).toEqual({ foo: "bar", lang: "en" }); - expect(getRoute._fullPath).toEqual("/b"); + let getRoute = getRouteFromUrl({ pRoutes, pUrl: "/b?foo=bar&lang=en" }) + expect(getRoute.queryParams).toEqual({ foo: "bar", lang: "en" }) + expect(getRoute._fullPath).toEqual("/b") // only hash - getRoute = getRouteFromUrl({ pRoutes, pUrl: "/b/c#hash" }); - expect(getRoute._fullPath).toEqual("/b/c"); - expect(getRoute.queryParams).toEqual({}); - expect(getRoute.hash).toEqual("hash"); + getRoute = getRouteFromUrl({ pRoutes, pUrl: "/b/c#hash" }) + expect(getRoute._fullPath).toEqual("/b/c") + expect(getRoute.queryParams).toEqual({}) + expect(getRoute.hash).toEqual("hash") // params and hash - getRoute = getRouteFromUrl({ pRoutes, pUrl: "/b/c?foo=bar#hash" }); - expect(getRoute.queryParams).toEqual({ foo: "bar" }); - expect(getRoute.hash).toEqual("hash"); + getRoute = getRouteFromUrl({ pRoutes, pUrl: "/b/c?foo=bar#hash" }) + expect(getRoute.queryParams).toEqual({ foo: "bar" }) + expect(getRoute.hash).toEqual("hash") // not hash and params - getRoute = getRouteFromUrl({ pRoutes, pUrl: "/a" }); - expect(getRoute.queryParams).toEqual({}); - expect(getRoute.hash).toEqual(null); - }); - }); + getRoute = getRouteFromUrl({ pRoutes, pUrl: "/a" }) + expect(getRoute.queryParams).toEqual({}) + expect(getRoute.hash).toEqual(null) + }) + }) describe("getNotFoundRoute", () => { it("should return not found route", () => { expect(getNotFoundRoute(routeList)).toEqual({ path: "/:rest", name: "NotFoundPage", - }); - }); - }); -}); + }) + }) + }) +}) /*** * Routes @@ -269,31 +269,31 @@ describe("matcher", () => { describe("routes", () => { describe("patchMissingRootRoute", () => { it("should patch missing route", () => { - const pathchedRoutes = patchMissingRootRoute(routeList[0].children); - const firstRouteAdded = pathchedRoutes[0]; - expect(firstRouteAdded.path).toBe("/"); - expect(firstRouteAdded.name).toContain("auto-generate-slash-route"); - }); + const pathchedRoutes = patchMissingRootRoute(routeList[0].children) + const firstRouteAdded = pathchedRoutes[0] + expect(firstRouteAdded.path).toBe("/") + expect(firstRouteAdded.name).toContain("auto-generate-slash-route") + }) it("should not patch missing route if '/' route already exist", () => { - const pathchedRoutes = patchMissingRootRoute(routeList); - const firstRouteAdded = pathchedRoutes[0]; - expect(firstRouteAdded.path).toBe("/"); - expect(firstRouteAdded.name).toBe("HomePage"); - }); - }); + const pathchedRoutes = patchMissingRootRoute(routeList) + const firstRouteAdded = pathchedRoutes[0] + expect(firstRouteAdded.path).toBe("/") + expect(firstRouteAdded.name).toBe("HomePage") + }) + }) describe("applyMiddlewaresToRoutes", () => { it("should apply middleware to routes", () => { // TODO - const transformFn = (r) => r.forEach((e) => (e.path = `-${e.path}`)); - const routes = [{ path: "/" }, { path: "/foo" }]; - const afterMiddlewareRoutes = [{ path: "-/" }, { path: "-/foo" }]; - const transformRoutes = applyMiddlewaresToRoutes(routes, [transformFn]); - expect(transformRoutes).toEqual(afterMiddlewareRoutes); - }); - }); -}); + const transformFn = (r) => r.forEach((e) => (e.path = `-${e.path}`)) + const routes = [{ path: "/" }, { path: "/foo" }] + const afterMiddlewareRoutes = [{ path: "-/" }, { path: "-/foo" }] + const transformRoutes = applyMiddlewaresToRoutes(routes, [transformFn]) + expect(transformRoutes).toEqual(afterMiddlewareRoutes) + }) + }) +}) /*** * Urls / paths @@ -304,50 +304,50 @@ describe("routes", () => { describe("URLs and paths", () => { describe("compileUrl", () => { it("should build url", () => { - const parh = compileUrl("/foo/:id/bar", { id: "2" }); - expect(parh).toBe("/foo/2/bar"); - }); - }); + const parh = compileUrl("/foo/:id/bar", { id: "2" }) + expect(parh).toBe("/foo/2/bar") + }) + }) describe("getFullPathByPath", () => { it("should return the full path", () => { - expect(getFullPathByPath(routeList, "/foo", "FooPage")).toBe("/hello/foo"); - expect(getFullPathByPath(routeList, "/yes", "YesPage")).toBe("/hello/foo/bla/yes"); + expect(getFullPathByPath(routeList, "/foo", "FooPage")).toBe("/hello/foo") + expect(getFullPathByPath(routeList, "/yes", "YesPage")).toBe("/hello/foo/bla/yes") expect(getFullPathByPath(routeList, "/", "FirstLevelRoute-2")).toBe( "/hello/foo/bla/", - ); - expect(getFullPathByPath(routeList, "/no", "NoPage")).toBe("/hello/foo/bla/no"); - }); - }); + ) + expect(getFullPathByPath(routeList, "/no", "NoPage")).toBe("/hello/foo/bla/no") + }) + }) describe("getUrlByRouteName", () => { it("should return full URL with only page name and params", () => { - expect(getUrlByRouteName(routeList, { name: "HelloPage" })).toBe("/hello"); - expect(getUrlByRouteName(routeList, { name: "FooPage" })).toBe("/hello/foo"); + expect(getUrlByRouteName(routeList, { name: "HelloPage" })).toBe("/hello") + expect(getUrlByRouteName(routeList, { name: "FooPage" })).toBe("/hello/foo") expect(getUrlByRouteName(routeList, { name: "BlaPage", params: { id: 2 } })).toBe( "/hello/foo/bla", - ); + ) expect(getUrlByRouteName(routeList, { name: "NoPage", params: { id: 4 } })).toBe( "/hello/foo/bla/no", - ); - }); - }); + ) + }) + }) describe("getLangPath", () => { it("should format routes properly", () => { - const path = "/:lang/foo"; - const pathObj = { fr: "/:lang/foo-fr", en: "/:lang/foo-en" }; - expect(getLangPath(path, "fr")).toEqual("/foo"); - expect(getLangPath(pathObj, "en")).toEqual("/foo-en"); - expect(getLangPath(pathObj, "de")).toBeUndefined(); - }); - }); + const path = "/:lang/foo" + const pathObj = { fr: "/:lang/foo-fr", en: "/:lang/foo-en" } + expect(getLangPath(path, "fr")).toEqual("/foo") + expect(getLangPath(pathObj, "en")).toEqual("/foo-en") + expect(getLangPath(pathObj, "de")).toBeUndefined() + }) + }) describe("addLangToUrl", () => { it("should add lang to Url", () => { - const url = "/foo/en/bar"; - expect(addLangToUrl(url, "en", true)).toBe(`/en${url}`); - expect(addLangToUrl(url, "en", false)).toBe(`${url}`); - }); - }); -}); + const url = "/foo/en/bar" + expect(addLangToUrl(url, "en", true)).toBe(`/en${url}`) + expect(addLangToUrl(url, "en", false)).toBe(`${url}`) + }) + }) +}) diff --git a/src/tests/helpers.test.ts b/src/tests/helpers.test.ts index 6bd5d62d..471bd294 100644 --- a/src/tests/helpers.test.ts +++ b/src/tests/helpers.test.ts @@ -1,38 +1,38 @@ -import { it, expect, describe } from "vitest"; -import { joinPaths, preventSlashes, removeLastCharFromString } from "../core/helpers"; +import { it, expect, describe } from "vitest" +import { joinPaths, preventSlashes, removeLastCharFromString } from "../core/helpers" describe("joinPaths", () => { it("should join paths without error", () => { - expect(joinPaths(["/foo", "/bar"])).toBe("/foo/bar"); - }); + expect(joinPaths(["/foo", "/bar"])).toBe("/foo/bar") + }) it("should join paths and remove multi slash", () => { - expect(joinPaths(["////foo/////////", "///bar//////////"])).toBe("/foo/bar/"); - }); -}); + expect(joinPaths(["////foo/////////", "///bar//////////"])).toBe("/foo/bar/") + }) +}) describe("preventSlashes", () => { it("should remove multi slashes", () => { - expect(preventSlashes("///foo/")).toBe("/foo/"); - expect(preventSlashes("///foo/bar/////zoo")).toBe("/foo/bar/zoo"); - }); -}); + expect(preventSlashes("///foo/")).toBe("/foo/") + expect(preventSlashes("///foo/bar/////zoo")).toBe("/foo/bar/zoo") + }) +}) describe("removeLastCharFromString", () => { it("should not remove last character if string === lastChar", () => { - const entry = "/"; - const result = removeLastCharFromString("/", "/", true); - expect(result).toEqual(entry); - }); + const entry = "/" + const result = removeLastCharFromString("/", "/", true) + expect(result).toEqual(entry) + }) it("should remove last character if string is not lastChar", () => { - const entry = "/"; - const result = removeLastCharFromString(entry, "/", false); - expect(result).toEqual(""); - }); + const entry = "/" + const result = removeLastCharFromString(entry, "/", false) + expect(result).toEqual("") + }) it("should remove last charater from string", () => { - const entry = "/master/"; - const result = removeLastCharFromString(entry, "/"); - expect(result).toEqual("/master"); - }); -}); + const entry = "/master/" + const result = removeLastCharFromString(entry, "/") + expect(result).toEqual("/master") + }) +}) diff --git a/src/tests/staticPropsCache.test.ts b/src/tests/staticPropsCache.test.ts index 80eac881..b3c8559e 100644 --- a/src/tests/staticPropsCache.test.ts +++ b/src/tests/staticPropsCache.test.ts @@ -1,25 +1,25 @@ -import { staticPropsCache } from "../core/staticPropsCache"; -import { expect, describe, it } from "vitest"; +import { staticPropsCache } from "../core/staticPropsCache" +import { expect, describe, it } from "vitest" describe("staticPropsCache", () => { - const store = {}; - const cache = staticPropsCache(store); + const store = {} + const cache = staticPropsCache(store) - const key = "/test"; - const data = { data: [] }; - const key2 = "/test2"; - const data2 = { data2: [] }; + const key = "/test" + const data = { data: [] } + const key2 = "/test2" + const data2 = { data2: [] } it("should set in cache", () => { - cache.set(key, data); - expect(store).toEqual({ [key]: data }); + cache.set(key, data) + expect(store).toEqual({ [key]: data }) - cache.set(key2, data2); - expect(store).toEqual({ [key]: data, [key2]: data2 }); - }); + cache.set(key2, data2) + expect(store).toEqual({ [key]: data, [key2]: data2 }) + }) it("should get from cache", () => { - expect(cache.get(key)).toEqual(data); - expect(cache.get(key2)).toEqual(data2); - }); -}); + expect(cache.get(key)).toEqual(data) + expect(cache.get(key2)).toEqual(data2) + }) +}) diff --git a/tsup.config.ts b/tsup.config.ts index fb57f3e3..f1c52535 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -1,5 +1,5 @@ -import { defineConfig } from "tsup"; -import { spawn } from "child_process"; +import { defineConfig } from "tsup" +import { spawn } from "child_process" export default defineConfig({ entry: { index: "src/index.ts" }, @@ -12,7 +12,7 @@ export default defineConfig({ external: ["@wbe/debug", "history", "path-to-regexp", "react", "react-dom"], sourcemap: true, async onSuccess() { - const process = spawn("npx", ["size-limit"], { shell: true }); - process.stdout.on("data", (data) => console.log(data.toString())); + const process = spawn("npx", ["size-limit"], { shell: true }) + process.stdout.on("data", (data) => console.log(data.toString())) }, -}); +}) diff --git a/turbo.json b/turbo.json index 8f6bb8cf..24744b27 100644 --- a/turbo.json +++ b/turbo.json @@ -12,4 +12,3 @@ } } } -