crossroad 3.0.2 → 3.0.3

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.
Files changed (3) hide show
  1. package/index.d.ts +86 -93
  2. package/index.min.js +1 -1
  3. package/package.json +5 -4
package/index.d.ts CHANGED
@@ -1,102 +1,95 @@
1
- import React from "react";
1
+ import * as React from 'react';
2
+ import React__default, { ReactNode, ReactElement } from 'react';
2
3
 
3
- // Helper type to parse parameter types from path string
4
- type ParseParamType<T extends string> = T extends `${infer Name}<${infer Type}>`
5
- ? Type extends "number"
6
- ? { [K in Name]: number }
7
- : Type extends "string"
8
- ? { [K in Name]: string }
9
- : Type extends "date"
10
- ? { [K in Name]: Date }
11
- : Type extends "boolean"
12
- ? { [K in Name]: boolean }
13
- : { [K in Name]: string } // default to string
14
- : T extends `${infer Name}`
15
- ? { [K in Name]: string }
16
- : never;
17
-
18
- // Extracts all params from path and combines them
19
- type ExtractParams<T extends string> =
20
- T extends `${infer Prefix}/:${infer Param}/${infer Rest}`
21
- ? ParseParamType<Param> & ExtractParams<`/${Rest}`>
22
- : T extends `${infer Prefix}/:${infer Param}`
23
- ? ParseParamType<Param>
24
- : {};
25
-
26
- // Main Route component with type inference
27
- interface RouteProps<
28
- T extends Record<string, string | number | boolean | Date> = {},
29
- > extends Omit<
30
- {
31
- path?: string;
32
- scrollUp?: boolean;
33
- component?: React.FunctionComponent<T>;
34
- render?: (params: T) => React.ReactNode;
35
- children?: React.ReactNode;
36
- },
37
- "component" | "render"
38
- > {
39
- path?: string;
4
+ interface RouterProps {
5
+ scrollUp?: boolean;
6
+ url?: string;
7
+ children?: React__default.ReactNode;
40
8
  }
9
+ declare const _default$6: ({ scrollUp, url: baseUrl, children }: RouterProps) => React__default.JSX.Element;
41
10
 
42
- // Infer params from path string literal
43
- type InferParamsFromPath<T extends string> = T extends `${string}/:${string}`
44
- ? ExtractParams<T>
45
- : {};
46
-
47
- // Modified Route declaration with path-based inference
48
- declare const Route: <P extends string = string>(
49
- props: RouteProps<InferParamsFromPath<P>> & { path?: P } & (
50
- | {
51
- component: React.FunctionComponent<InferParamsFromPath<P>>;
52
- render?: never;
53
- }
54
- | {
55
- render: (params: InferParamsFromPath<P>) => React.ReactNode;
56
- component?: never;
57
- }
58
- | {}
59
- ),
60
- ) => React.ReactNode;
11
+ type ParseParamType<T extends string> = T extends `${infer Name}<${infer Type}>` ? Type extends "number" ? {
12
+ [K in Name]: number;
13
+ } : Type extends "string" ? {
14
+ [K in Name]: string;
15
+ } : Type extends "date" ? {
16
+ [K in Name]: Date;
17
+ } : Type extends "boolean" ? {
18
+ [K in Name]: boolean;
19
+ } : {
20
+ [K in Name]: string;
21
+ } : {
22
+ [K in T]: string;
23
+ };
24
+ type ExtractParams<T extends string> = T extends `${infer _Prefix}/:${infer Param}/${infer Rest}` ? ParseParamType<Param> & ExtractParams<`/${Rest}`> : T extends `${infer _Prefix}/:${infer Param}` ? ParseParamType<Param> : {};
25
+ type InferParamsFromPath<T extends string> = T extends `${string}/:${string}` ? ExtractParams<T> : {};
26
+ interface RoutePropsBase {
27
+ path?: string;
28
+ scrollUp?: boolean;
29
+ children?: React__default.ReactNode;
30
+ }
31
+ type RouteProps<T extends Record<string, string | number | boolean | Date> = {}> = RoutePropsBase & ({
32
+ component: React__default.FunctionComponent<T>;
33
+ render?: never;
34
+ } | {
35
+ render: (params: T) => React__default.ReactNode;
36
+ component?: never;
37
+ } | {
38
+ component?: never;
39
+ render?: never;
40
+ });
41
+ type RouteType = <P extends string = string>(props: RouteProps<InferParamsFromPath<P>> & {
42
+ path?: P;
43
+ }) => React__default.ReactNode;
44
+ declare const _default$5: RouteType;
61
45
 
62
- // Rest of your original declarations remain the same
63
- type FC<T = {}> = React.FC<React.PropsWithChildren<T>>;
64
- declare const Router: FC<{ scrollUp?: boolean; url?: string }>;
65
- declare const Switch: FC<{
66
- redirect?: string | { path: string } | (() => string);
67
- }>;
68
- type Query = Record<string, string>;
69
- type Url = URL & {
70
- path: string;
71
- query: Query;
72
- hash?: string;
46
+ type Query = Record<string, string | string[]>;
47
+ type Url = {
48
+ path: string;
49
+ query: Query;
50
+ hash?: string;
51
+ params?: Params;
73
52
  };
74
53
  type UrlSet = {
75
- path?: string;
76
- query?: Query;
77
- hash?: string;
54
+ path?: string;
55
+ query?: Query;
56
+ hash?: string;
78
57
  };
79
58
  type Params = Record<string, string | number | boolean | Date>;
80
- type Callable<T = string> = React.Dispatch<React.SetStateAction<T>>;
81
- declare const Context: React.Context<[Url, Callable<UrlSet | string>]>;
82
- declare function useUrl(): [Url, Callable<UrlSet | string>];
83
- declare function usePath(): [string, Callable<string>];
84
- declare function useQuery(): [Query, Callable<Query>];
85
- declare function useQuery(key: string): [string, Callable<string>];
86
- declare function useHash(): [string, Callable<string>];
87
- declare function useParams<T = Params>(): T;
88
- declare function useParams<T = string | number | boolean | Date>(
89
- key: string,
90
- ): T;
91
-
92
- export default Router;
93
- export {
94
- Route,
95
- Switch,
96
- useUrl,
97
- usePath,
98
- useQuery,
99
- useHash,
100
- useParams,
101
- Context,
59
+ type SetUrlOptions = {
60
+ mode?: "push" | "replace";
102
61
  };
62
+ type NewUrlValue = Url | UrlSet | string;
63
+ type SetUrl = (newUrl: NewUrlValue | ((prev: Url) => NewUrlValue), opts?: SetUrlOptions) => void;
64
+
65
+ interface SwitchProps {
66
+ redirect?: string | {
67
+ path: string;
68
+ } | ((url: Url) => string);
69
+ children?: ReactNode;
70
+ }
71
+ declare const _default$4: ({ redirect, children }: SwitchProps) => ReactElement | null;
72
+
73
+ declare const _default$3: React.Context<[Url, SetUrl] | undefined>;
74
+
75
+ declare const _default$2: () => [Url, SetUrl];
76
+
77
+ type PathUpdater = (path: string) => string;
78
+ type SetPath = (path: string | PathUpdater, opts?: SetUrlOptions) => void;
79
+ declare const _default$1: () => [string, SetPath];
80
+
81
+ type QueryValue = string | string[] | undefined;
82
+ type SetQueryProp = (value: string | ((prev: QueryValue) => string), opts?: SetUrlOptions) => void;
83
+ type QueryUpdater = (query: Query) => Query | string;
84
+ type SetQuery = (newQuery: Query | string | QueryUpdater, opts?: SetUrlOptions) => void;
85
+ declare function useQuery(key: string): [QueryValue, SetQueryProp];
86
+ declare function useQuery(): [Query, SetQuery];
87
+
88
+ type HashUpdater = (hash: string | undefined) => string;
89
+ type SetHash = (hash: string | HashUpdater, opts?: SetUrlOptions) => void;
90
+ declare const _default: () => [string | undefined, SetHash];
91
+
92
+ declare function useParams(): Params;
93
+ declare function useParams<T = string | number | boolean | Date>(key: string): T;
94
+
95
+ export { _default$3 as Context, _default$5 as Route, _default$4 as Switch, _default$6 as default, _default as useHash, useParams, _default$1 as usePath, useQuery, _default$2 as useUrl };
package/index.min.js CHANGED
@@ -1 +1 @@
1
- import t,{createContext as e,useState as r,useRef as n,useCallback as o,useEffect as i,useContext as s}from"react";var a=e(void 0);const u=t=>t.length>1?t:t[0],c=t=>{if("string"!=typeof t)return t;const e={query:{}},r=new URL(t,"http://localhost:3000/");e.path=(r.pathname.replace(/\/$/,"")||"/").replaceAll("%3C","<").replaceAll("%3E",">"),e.query={};for(const[t]of r.searchParams)e.query[t]=u(r.searchParams.getAll(t));return r.hash&&(e.hash=r.hash.replace(/^#/,"")),e},l=t=>{if("string"==typeof t)return t;const{path:e,query:r={},hash:n}=t||{};let o=e||"/";const i=new URLSearchParams(Object.entries(r).map(([t,e])=>(Array.isArray(e)?e:[e]).map(e=>[t,e])).flat().filter(([t,e])=>e)).toString();return i&&(o+="?"+i),n&&(o+="#"+n),o};var p=()=>"undefined"==typeof window,h=({scrollUp:e,url:s,children:u})=>{const h=s||(p()?"/":window.location.href),[f,y]=r(()=>c(h)),d=n(f),m=o((t,{mode:r="push"}={})=>{if(!history[r+"State"])throw new Error(`Invalid mode "${r}"`);y(e=>{const r="function"==typeof t?t(e):t;return l(e)===l(r)?e:c(r)});const n=d.current,o="function"==typeof t?t(n):t;if(l(n)!==l(o)){const t=c(o);d.current=t,history[r+"State"]({},null,l(t)),e&&window.scrollTo(0,0)}},[]);return i(()=>{d.current=f},[f]),i(()=>{if(p())return;const t=()=>y(c(window.location.href)),e=t=>{const e=t.target,r=(t=>{if(!t)return null;const e=t.getAttribute("href");if(!e)return null;const r=e.trim();return/^[a-zA-Z][a-zA-Z0-9+\-.]*:/.test(r)||null!==t.getAttribute("target")?null:r})(e?.closest("a")??null);if(!r)return!1;t.preventDefault();const[n,o]=r.split("#");n?m(r):o&&(window.location.hash="#"+o)};return window.addEventListener("popstate",t),document.addEventListener("click",e),()=>{window.removeEventListener("popstate",t),document.removeEventListener("click",e)}},[m]),t.createElement(a.Provider,{value:[f,m]},u)};function f(t,e,r={}){let n=JSON.parse(JSON.stringify(c(t))),o=JSON.parse(JSON.stringify(c(e)));if(o.path=o.path.replace(/\/$/,"")||"/",n.path=n.path.replace(/\/$/,"")||"/",n.path.endsWith("*")){n.path=n.path.replace(/\/?\*/,"")||"/";const t=n.path.split("/").filter(Boolean).length;o.path="/"+o.path.slice(1).split("/").slice(0,t).join("/")}if(Object.entries(n.query).length)for(const t in n.query){if(!(t in o.query))return!1;if(n.query[t]&&n.query[t]!==o.query[t])return!1}if(!n.path.includes(":"))return n.path===o.path&&r;if(n.path.split("/").length!==o.path.split("/").length)return!1;const i={},s=n.path.split("/").every((t,e)=>{const n=o.path.split("/")[e];if(t.startsWith(":")){let e=t.slice(1),o="string";e.includes("<")&&([e,o]=e.split("<"),o=o.slice(0,-1));const s=decodeURIComponent(n);return i[e]="number"===o?Number(s):"date"===o?new Date(/^\d+$/.test(s)?Number(s):s):"boolean"===o?"true"===s:s,r}return n===t});return s&&Object.assign(r,i),s&&r}var y=()=>{const t=s(a);if(!t)throw new Error("Wrap your App with <Router>");return t},d=({path:e="*",scrollUp:r,component:n,render:o,children:i})=>{const s=y(),u=f(e,s[0]);if(!u)return null;if(r&&window.scrollTo(0,0),n){const e=n;i=t.createElement(e,u)}else if(o)i=o(u);else if(!i)throw new Error("Route needs prop `component`, `render` or `children`");return t.createElement(a.Provider,{value:[{...s[0],params:u},s[1]]},i)};var m=({redirect:t,children:e})=>{const[r,n]=y(),o=(t=>(Array.isArray(t)?t:[t]).filter(t=>null!=t&&"object"==typeof t&&"props"in t))(e).find(t=>f(t.props.path||"*",r))||null;return i(()=>{if(!t)return;if(o)return;const e="function"==typeof t?t(r):t;n(l(e))},[t,o]),o},w=()=>{const[t,e]=y(),r=o((t,r)=>{e(e=>{const r="function"==typeof t?t(e.path):t;return{...e,path:"string"==typeof r?r:"/"}},r)},[]);return[t.path,r]};const q=t=>l({query:t});function g(t){return t?(t=>{const[e,r]=y(),n=o((e,n)=>{r(r=>{const n=r.query[t],o="function"==typeof e?e(n):e;if(o===n)return r;if(o)return{...r,query:{...r.query,[t]:o}};{const{[t]:e,...n}=r.query;return{...r,query:n}}},n)},[]);return[e.query[t],n]})(t):(()=>{const[t,e]=y(),r=o((t,r)=>{e(e=>{let r="function"==typeof t?t(e.query):t;"string"==typeof r&&(r=c("/?"+r.replace(/^\?/,"")).query);const n=c(q(r)).query;return q(n)===q(e.query)?e:{...e,query:n}},r)},[]);return[t.query,r]})()}var v=()=>{const[t,e]=y(),r=o((t,r)=>{e(e=>{const r="function"==typeof t?t(e.hash):t,n="string"!=typeof r?"":r.replace(/^#/,"");return{...e,hash:n}},r)},[]);return[t.hash,r]};function A(t){const[{params:e}]=s(a);return t?t in e?e[t]:"":e||{}}export{a as Context,d as Route,m as Switch,h as default,v as useHash,A as useParams,w as usePath,g as useQuery,y as useUrl};
1
+ import t,{createContext as e,useState as r,useRef as n,useCallback as o,useEffect as i,useContext as s}from"react";var a=e(void 0);const u=t=>t.length>1?t:t[0],c=t=>{if("string"!=typeof t)return t;const e={query:{}},r=new URL(t,"http://localhost:3000/");e.path=(r.pathname.replace(/\/$/,"")||"/").replaceAll("%3C","<").replaceAll("%3E",">"),e.query={};for(const[t]of r.searchParams)e.query[t]=u(r.searchParams.getAll(t));return r.hash&&(e.hash=r.hash.replace(/^#/,"")),e},l=t=>{if("string"==typeof t)return t;const{path:e,query:r={},hash:n}=t||{};let o=e||"/";const i=new URLSearchParams(Object.entries(r).map(([t,e])=>(Array.isArray(e)?e:[e]).map(e=>[t,e])).flat().filter(([t,e])=>e)).toString();return i&&(o+="?"+i),n&&(o+="#"+n),o};var p=()=>"undefined"==typeof window,h=({scrollUp:e,url:s,children:u})=>{const h=s||(p()?"/":window.location.href),[f,y]=r(()=>c(h)),d=n(f),m=o((t,{mode:r="push"}={})=>{if(!history[r+"State"])throw new Error(`Invalid mode "${r}"`);y(e=>{const r="function"==typeof t?t(e):t;return l(e)===l(r)?e:c(r)});const n=d.current,o="function"==typeof t?t(n):t;if(l(n)!==l(o)){const t=c(o);d.current=t,history[r+"State"]({},null,l(t)),e&&window.scrollTo(0,0)}},[]);return i(()=>{d.current=f},[f]),i(()=>{if(p())return;const t=()=>y(c(window.location.href)),e=t=>{const e=t.target,r=(t=>{if(!t)return null;const e=t.getAttribute("href");if(!e)return null;const r=e.trim();return/^[a-zA-Z][a-zA-Z0-9+\-.]*:/.test(r)||null!==t.getAttribute("target")?null:r})(e?.closest("a")??null);if(!r)return!1;t.preventDefault();const[n,o]=r.split("#");n?m(r):o&&(window.location.hash="#"+o)};return window.addEventListener("popstate",t),document.addEventListener("click",e),()=>{window.removeEventListener("popstate",t),document.removeEventListener("click",e)}},[m]),t.createElement(a.Provider,{value:[f,m]},u)};function f(t,e,r={}){let n=JSON.parse(JSON.stringify(c(t))),o=JSON.parse(JSON.stringify(c(e)));if(o.path=o.path.replace(/\/$/,"")||"/",n.path=n.path.replace(/\/$/,"")||"/",n.path.endsWith("*")){n.path=n.path.replace(/\/?\*/,"")||"/";const t=n.path.split("/").filter(Boolean).length;o.path="/"+o.path.slice(1).split("/").slice(0,t).join("/")}if(Object.entries(n.query).length)for(const t in n.query){if(!(t in o.query))return!1;if(n.query[t]&&n.query[t]!==o.query[t])return!1}if(!n.path.includes(":"))return n.path===o.path&&r;if(n.path.split("/").length!==o.path.split("/").length)return!1;const i={},s=n.path.split("/").every((t,e)=>{const n=o.path.split("/")[e];if(t.startsWith(":")){let e=t.slice(1),o="string";e.includes("<")&&([e,o]=e.split("<"),o=o.slice(0,-1));const s=decodeURIComponent(n);return i[e]="number"===o?Number(s):"date"===o?new Date(/^\d+$/.test(s)?Number(s):s):"boolean"===o?"true"===s:s,r}return n===t});return s&&Object.assign(r,i),s&&r}var y=()=>{const t=s(a);if(!t)throw new Error("Wrap your App with <Router>");return t};const d=({path:e="*",scrollUp:r,component:n,render:o,children:i})=>{const s=y(),u=f(e,s[0]);if(!u)return null;if(r&&window.scrollTo(0,0),n){const e=n;i=t.createElement(e,u)}else if(o)i=o(u);else if(!i)throw new Error("Route needs prop `component`, `render` or `children`");return t.createElement(a.Provider,{value:[{...s[0],params:u},s[1]]},i)};var m=({redirect:t,children:e})=>{const[r,n]=y(),o=(t=>(Array.isArray(t)?t:[t]).filter(t=>null!=t&&"object"==typeof t&&"props"in t))(e).find(t=>f(t.props.path||"*",r))||null;return i(()=>{if(!t)return;if(o)return;const e="function"==typeof t?t(r):t;n(l(e))},[t,o]),o},w=()=>{const[t,e]=y(),r=o((t,r)=>{e(e=>{const r="function"==typeof t?t(e.path):t;return{...e,path:"string"==typeof r?r:"/"}},r)},[]);return[t.path,r]};const q=t=>l({query:t});function g(t){return t?(t=>{const[e,r]=y(),n=o((e,n)=>{r(r=>{const n=r.query[t],o="function"==typeof e?e(n):e;if(o===n)return r;if(o)return{...r,query:{...r.query,[t]:o}};{const{[t]:e,...n}=r.query;return{...r,query:n}}},n)},[]);return[e.query[t],n]})(t):(()=>{const[t,e]=y(),r=o((t,r)=>{e(e=>{let r="function"==typeof t?t(e.query):t;"string"==typeof r&&(r=c("/?"+r.replace(/^\?/,"")).query);const n=c(q(r)).query;return q(n)===q(e.query)?e:{...e,query:n}},r)},[]);return[t.query,r]})()}var v=()=>{const[t,e]=y(),r=o((t,r)=>{e(e=>{const r="function"==typeof t?t(e.hash):t,n="string"!=typeof r?"":r.replace(/^#/,"");return{...e,hash:n}},r)},[]);return[t.hash,r]};function A(t){const[{params:e}]=s(a);return t?t in e?e[t]:"":e||{}}export{a as Context,d as Route,m as Switch,h as default,v as useHash,A as useParams,w as usePath,g as useQuery,y as useUrl};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crossroad",
3
- "version": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "A React library to handle navigation easily in your WebApp",
5
5
  "homepage": "https://crossroad.page/",
6
6
  "repository": "https://github.com/franciscop/crossroad.git",
@@ -18,8 +18,8 @@
18
18
  },
19
19
  "license": "MIT",
20
20
  "scripts": {
21
- "build": "rollup -c",
22
- "typecheck": "tsc",
21
+ "build": "tsc -p tsconfig.build.json && rollup -c",
22
+ "lint": "tsc",
23
23
  "size": "echo $(gzip -c index.min.js | wc -c) bytes",
24
24
  "start": "jest --watch",
25
25
  "test": "jest --coverage"
@@ -42,6 +42,7 @@
42
42
  "@babel/preset-env": "^7.29.2",
43
43
  "@babel/preset-react": "^7.28.5",
44
44
  "@babel/preset-typescript": "^7.28.5",
45
+ "@rollup/plugin-babel": "^6.0.0",
45
46
  "@rollup/plugin-terser": "^1.0.0",
46
47
  "@types/jest": "^30.0.0",
47
48
  "@types/node": "^25.5.2",
@@ -54,7 +55,7 @@
54
55
  "react-dom": "^18.3.1",
55
56
  "react-test": "^0.22.1",
56
57
  "rollup": "^4.60.1",
57
- "rollup-plugin-babel": "^4.4.0",
58
+ "rollup-plugin-dts": "^6.4.1",
58
59
  "typescript": "^6.0.2"
59
60
  },
60
61
  "peerDependencies": {