@farbenmeer/router 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,5 +2,6 @@ export declare class ImmutableSearchParams extends URLSearchParams {
2
2
  set(key: string, value: string): ImmutableSearchParams;
3
3
  append(name: string, value: string): ImmutableSearchParams;
4
4
  delete(key: string): ImmutableSearchParams;
5
+ get search(): string;
5
6
  }
6
7
  //# sourceMappingURL=immutable-search-params.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"immutable-search-params.d.ts","sourceRoot":"","sources":["../src/immutable-search-params.ts"],"names":[],"mappings":"AAAA,qBAAa,qBAAsB,SAAQ,eAAe;IAC/C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAM9B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAMlC,MAAM,CAAC,GAAG,EAAE,MAAM;CAK5B"}
1
+ {"version":3,"file":"immutable-search-params.d.ts","sourceRoot":"","sources":["../src/immutable-search-params.ts"],"names":[],"mappings":"AAAA,qBAAa,qBAAsB,SAAQ,eAAe;IAC/C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAM9B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAMlC,MAAM,CAAC,GAAG,EAAE,MAAM;IAM3B,IAAI,MAAM,IAAI,MAAM,CAEnB;CACF"}
@@ -14,4 +14,7 @@ export class ImmutableSearchParams extends URLSearchParams {
14
14
  newSearchParams.delete(key);
15
15
  return new ImmutableSearchParams(newSearchParams);
16
16
  }
17
+ get search() {
18
+ return this.size === 0 ? "" : "?" + this.toString();
19
+ }
17
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../src/link.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,SAAS,EAAkB,MAAM,OAAO,CAAC;AAQrE,UAAU,KAAM,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,EAAE,KAAK,2CA4C5E"}
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../src/link.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,SAAS,EAAkB,MAAM,OAAO,CAAC;AASrE,UAAU,KAAM,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,EAAE,KAAK,2CA6B5E"}
package/dist/link.js CHANGED
@@ -1,28 +1,13 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { use, useMemo } from "react";
3
3
  import { PathnameContext, RouteContext, RouterContext, SearchParamsContext, } from "./context";
4
+ import { resolve } from "./path";
4
5
  export function Link({ href, replace, children, onClick, ...rawProps }) {
5
6
  const { matchedPathname: parentPathname } = use(RouteContext);
6
7
  const router = use(RouterContext);
7
8
  const pathname = use(PathnameContext);
8
9
  const searchParams = use(SearchParamsContext);
9
- const target = useMemo(() => {
10
- if (href.startsWith("/")) {
11
- return href;
12
- }
13
- if (href.startsWith("?")) {
14
- return pathname + href;
15
- }
16
- if (href.startsWith("#")) {
17
- return (pathname +
18
- (searchParams.size > 0 ? "?" + searchParams.toString() : "") +
19
- href);
20
- }
21
- if (!href) {
22
- return parentPathname;
23
- }
24
- return parentPathname + "/" + href;
25
- }, [href, parentPathname, pathname]);
10
+ const target = useMemo(() => resolve(href, { pathname, parentPathname, searchParams }), [href, parentPathname, pathname]);
26
11
  return (_jsx("a", { href: target, onClick: (event) => {
27
12
  onClick?.(event);
28
13
  if (event.defaultPrevented)
@@ -0,0 +1,8 @@
1
+ export declare function mockHistory(pathname?: string): {
2
+ location: URL;
3
+ history: {
4
+ pushState: import("vitest").Mock<(_state: any, _unused: string, url: string) => void>;
5
+ replaceState: import("vitest").Mock<(_state: any, _unused: string, url: string) => void>;
6
+ };
7
+ };
8
+ //# sourceMappingURL=mock-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-history.d.ts","sourceRoot":"","sources":["../src/mock-history.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,QAAQ,SAAM;;;kDAMV,GAAG,WAAW,MAAM,OAAO,MAAM;qDAG9B,GAAG,WAAW,MAAM,OAAO,MAAM;;EAKnE"}
@@ -0,0 +1,15 @@
1
+ import { vi } from "vitest";
2
+ export function mockHistory(pathname = "/") {
3
+ const location = new URL(pathname, "http://localhost:3000");
4
+ return {
5
+ location,
6
+ history: {
7
+ pushState: vi.fn((_state, _unused, url) => {
8
+ location.href = new URL(url, location.href).href;
9
+ }),
10
+ replaceState: vi.fn((_state, _unused, url) => {
11
+ location.href = new URL(url, location.href).href;
12
+ }),
13
+ },
14
+ };
15
+ }
package/dist/path.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { ImmutableSearchParams } from "./immutable-search-params";
2
+ interface Options {
3
+ pathname: string;
4
+ parentPathname: string;
5
+ searchParams: ImmutableSearchParams;
6
+ }
7
+ export declare function resolve(path: string, { pathname, parentPathname, searchParams }: Options): string;
8
+ export declare function removeTrailingSlash(path: string): string;
9
+ export {};
10
+ //# sourceMappingURL=path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,UAAU,OAAO;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,qBAAqB,CAAC;CACrC;AAED,wBAAgB,OAAO,CACrB,IAAI,EAAE,MAAM,EACZ,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,EAAE,OAAO,UAepD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGxD"}
package/dist/path.js ADDED
@@ -0,0 +1,21 @@
1
+ import { ImmutableSearchParams } from "./immutable-search-params";
2
+ export function resolve(path, { pathname, parentPathname, searchParams }) {
3
+ if (path.startsWith("/")) {
4
+ return path;
5
+ }
6
+ if (path.startsWith("?")) {
7
+ return pathname + path;
8
+ }
9
+ if (path.startsWith("#")) {
10
+ return pathname + searchParams.search + path;
11
+ }
12
+ if (!path) {
13
+ return parentPathname;
14
+ }
15
+ return parentPathname === "/" ? `/${path}` : `${parentPathname}/${path}`;
16
+ }
17
+ export function removeTrailingSlash(path) {
18
+ if (path === "/")
19
+ return path;
20
+ return path.endsWith("/") ? path.slice(0, -1) : path;
21
+ }
package/dist/route.js CHANGED
@@ -48,6 +48,9 @@ function buildFullPath(parentPath, path) {
48
48
  return path;
49
49
  }
50
50
  if (path) {
51
+ if (parentPath === "/") {
52
+ return "/" + path;
53
+ }
51
54
  return parentPath + "/" + path;
52
55
  }
53
56
  return parentPath;
package/dist/router.d.ts CHANGED
@@ -11,6 +11,6 @@ interface Props {
11
11
  replaceState: (state: any, title: string, url: string) => void;
12
12
  };
13
13
  }
14
- export declare function Router(props: Props): import("react/jsx-runtime").JSX.Element;
14
+ export declare function Router({ history, location, children, }: Props): import("react/jsx-runtime").JSX.Element;
15
15
  export {};
16
16
  //# sourceMappingURL=router.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAS3E,UAAU,KAAK;IACb,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,OAAO,CAAC,EAAE;QACR,SAAS,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC5D,YAAY,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;KAChE,CAAC;CACH;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,2CAwDlC"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAU3E,UAAU,KAAK;IACb,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,OAAO,CAAC,EAAE;QACR,SAAS,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC5D,YAAY,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;KAChE,CAAC;CACH;AAED,wBAAgB,MAAM,CAAC,EACrB,OAAwB,EACxB,QAA0B,EAC1B,QAAQ,GACT,EAAE,KAAK,2CAwCP"}
package/dist/router.js CHANGED
@@ -2,39 +2,28 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { startTransition, useMemo, useState } from "react";
3
3
  import { HashContext, PathnameContext, RouterContext, SearchParamsContext, } from "./context";
4
4
  import { ImmutableSearchParams } from "./immutable-search-params";
5
- export function Router(props) {
6
- const [pathname, setPathname] = useState(removeTrailingSlash(props.location?.pathname ?? window.location.pathname));
7
- const [search, setSearch] = useState(props.location?.search ?? window.location.search);
8
- const [hash, setHash] = useState(props.location?.hash ?? window.location.hash);
9
- const searchParams = useMemo(() => new ImmutableSearchParams(props.location?.search ?? search), [props.location?.search, search]);
5
+ import { removeTrailingSlash } from "./path";
6
+ export function Router({ history = window.history, location = window.location, children, }) {
7
+ const [pathname, setPathname] = useState(removeTrailingSlash(location.pathname));
8
+ const [searchParams, setSearchParams] = useState(new ImmutableSearchParams(location.search));
9
+ const [hash, setHash] = useState(location.hash);
10
10
  const routerContextValue = useMemo(() => ({
11
11
  push: (url) => {
12
- (props.history ?? window.history).pushState(null, "", url);
13
- if (!props.location) {
14
- startTransition(() => {
15
- setPathname(removeTrailingSlash(window.location.pathname));
16
- setSearch(window.location.search);
17
- setHash(window.location.hash);
18
- });
19
- }
12
+ history.pushState(null, "", url);
13
+ startTransition(() => {
14
+ setPathname(removeTrailingSlash(location.pathname));
15
+ setSearchParams(new ImmutableSearchParams(location.search));
16
+ setHash(location.hash);
17
+ });
20
18
  },
21
19
  replace: (url) => {
22
- (props.history ?? window.history).replaceState(null, "", url);
23
- if (!props.location) {
24
- startTransition(() => {
25
- setPathname(removeTrailingSlash(window.location.pathname));
26
- setSearch(window.location.search);
27
- setHash(window.location.hash);
28
- });
29
- }
20
+ history.replaceState(null, "", url);
21
+ startTransition(() => {
22
+ setPathname(removeTrailingSlash(location.pathname));
23
+ setSearchParams(new ImmutableSearchParams(location.search));
24
+ setHash(location.hash);
25
+ });
30
26
  },
31
- }), []);
32
- return (_jsx(RouterContext, { value: routerContextValue, children: _jsx(PathnameContext, { value: props.location?.pathname
33
- ? removeTrailingSlash(props.location.pathname)
34
- : pathname, children: _jsx(SearchParamsContext, { value: searchParams, children: _jsx(HashContext, { value: hash, children: props.children }) }) }) }));
35
- }
36
- function removeTrailingSlash(path) {
37
- if (path === "/")
38
- return path;
39
- return path.endsWith("/") ? path.slice(0, -1) : path;
27
+ }), [location, history]);
28
+ return (_jsx(RouterContext, { value: routerContextValue, children: _jsx(PathnameContext, { value: pathname, children: _jsx(SearchParamsContext, { value: searchParams, children: _jsx(HashContext, { value: hash, children: children }) }) }) }));
40
29
  }
@@ -1,2 +1,2 @@
1
- export declare function useParams<T extends Record<string, string[]> = Record<string, string[]>>(): T;
1
+ export declare function useParams<T extends Record<string, string | string[]> = Record<string, string | string[]>>(): T;
2
2
  //# sourceMappingURL=use-params.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-params.d.ts","sourceRoot":"","sources":["../src/use-params.ts"],"names":[],"mappings":"AAGA,wBAAgB,SAAS,CACvB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,KAI5C,CAAC,CACnB"}
1
+ {"version":3,"file":"use-params.d.ts","sourceRoot":"","sources":["../src/use-params.ts"],"names":[],"mappings":"AAGA,wBAAgB,SAAS,CACvB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,MAAM,CAClD,MAAM,EACN,MAAM,GAAG,MAAM,EAAE,CAClB,KAIgB,CAAC,CACnB"}
@@ -1,5 +1,4 @@
1
- export declare function useRouter(): {
2
- push: (href: string) => void;
3
- replace: (href: string) => void;
4
- };
1
+ import { type ContextType } from "react";
2
+ import { RouterContext } from "./context";
3
+ export declare function useRouter(): ContextType<typeof RouterContext>;
5
4
  //# sourceMappingURL=use-router.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-router.d.ts","sourceRoot":"","sources":["../src/use-router.ts"],"names":[],"mappings":"AAGA,wBAAgB,SAAS;;;EAExB"}
1
+ {"version":3,"file":"use-router.d.ts","sourceRoot":"","sources":["../src/use-router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,OAAO,CAAC;AACvD,OAAO,EAGL,aAAa,EAEd,MAAM,WAAW,CAAC;AAGnB,wBAAgB,SAAS,IAAI,WAAW,CAAC,OAAO,aAAa,CAAC,CA6B7D"}
@@ -1,5 +1,25 @@
1
- import { use } from "react";
2
- import { RouterContext } from "./context";
1
+ import { use, useMemo } from "react";
2
+ import { PathnameContext, RouteContext, RouterContext, SearchParamsContext, } from "./context";
3
+ import { resolve } from "./path";
3
4
  export function useRouter() {
4
- return use(RouterContext);
5
+ const { push, replace } = use(RouterContext);
6
+ const { matchedPathname: parentPathname } = use(RouteContext);
7
+ const pathname = use(PathnameContext);
8
+ const searchParams = use(SearchParamsContext);
9
+ return useMemo(() => ({
10
+ push(href) {
11
+ push(resolve(href, {
12
+ pathname,
13
+ searchParams,
14
+ parentPathname,
15
+ }));
16
+ },
17
+ replace(href) {
18
+ replace(resolve(href, {
19
+ pathname,
20
+ searchParams,
21
+ parentPathname,
22
+ }));
23
+ },
24
+ }), [pathname, searchParams, parentPathname]);
5
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farbenmeer/router",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "author": {
5
5
  "name": "Michel Smola",
6
6
  "email": "michel.smola@farbenmeer.de"
@@ -18,14 +18,14 @@
18
18
  "dist"
19
19
  ],
20
20
  "devDependencies": {
21
- "@happy-dom/global-registrator": "^20.0.0",
22
21
  "@testing-library/dom": "^10.4.1",
23
- "@testing-library/jest-dom": "^6.8.0",
24
- "@testing-library/react": "^16.3.0",
25
22
  "@types/node": "^25.0.3",
26
23
  "@types/react": "^19.2.7",
24
+ "@vitejs/plugin-react": "^5.1.2",
25
+ "@vitest/browser-playwright": "^4.0.16",
27
26
  "react": "^19.1.1",
28
- "vitest": "^4.0.16"
27
+ "vitest": "^4.0.16",
28
+ "vitest-browser-react": "^2.0.2"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "react": "^19.1.1",