pjdev2d-cli 1.1.3 → 1.2.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.
- package/package.json +2 -1
- package/registry.json +5 -0
- package/templates/react-router/hook/useRouterSearch.ts +131 -0
- package/templates/react-router/router/guards/private.route.tsx +21 -0
- package/templates/react-router/router/guards/public.route.tsx +21 -0
- package/templates/react-router/router/index.ts +144 -0
- package/templates/react-router/router/layouts/auth.layout.tsx +5 -0
- package/templates/react-router/router/layouts/error.layout.tsx +6 -0
- package/templates/react-router/router/layouts/main.layout.tsx +5 -0
- package/templates/react-router/router/path.ts +7 -0
- package/templates/react-router/routes/not-found/index.tsx +3 -0
- package/templates/react-router/routes/page-auth/index.tsx +3 -0
- package/templates/react-router/routes/page-one/index.tsx +3 -0
- package/templates/react-router/routes/page-one/search.page-one.ts +5 -0
- package/templates/react-router/routes/page-root/index.tsx +3 -0
- package/templates/react-router/routes/page-two/index.tsx +3 -0
- package/templates/react-router/routes/page-two/search.page-two.ts +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pjdev2d-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI to install reusable React components like shadcn/ui",
|
|
6
6
|
"main": "bin/cli.js",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"fs-extra": "^11.3.5",
|
|
31
31
|
"inquirer": "^13.4.3",
|
|
32
32
|
"react": "^19.2.6",
|
|
33
|
+
"react-router-dom": "^7.16.0",
|
|
33
34
|
"tailwind-merge": "^3.6.0"
|
|
34
35
|
}
|
|
35
36
|
}
|
package/registry.json
CHANGED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { useCallback, useMemo, useRef } from "react";
|
|
2
|
+
import { useSearchParams } from "react-router-dom";
|
|
3
|
+
|
|
4
|
+
export function useRouterSearch<T extends Record<string, any>>({
|
|
5
|
+
defaultParams,
|
|
6
|
+
}: {
|
|
7
|
+
defaultParams: T;
|
|
8
|
+
}) {
|
|
9
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
10
|
+
|
|
11
|
+
const defaultRef = useRef(defaultParams);
|
|
12
|
+
defaultRef.current = defaultParams;
|
|
13
|
+
|
|
14
|
+
const search = useMemo(() => {
|
|
15
|
+
const result = { ...defaultRef.current };
|
|
16
|
+
const keys = Object.keys(defaultRef.current);
|
|
17
|
+
|
|
18
|
+
for (const key of keys) {
|
|
19
|
+
const fallback = defaultRef.current[key];
|
|
20
|
+
if (Array.isArray(fallback)) {
|
|
21
|
+
const val = searchParams.getAll(key);
|
|
22
|
+
if (val.length > 0) {
|
|
23
|
+
result[key as keyof T] = parseValue(val, fallback) as any;
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
const val = searchParams.get(key);
|
|
27
|
+
if (val !== null) {
|
|
28
|
+
result[key as keyof T] = parseValue(val, fallback) as any;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}, [searchParams]);
|
|
34
|
+
|
|
35
|
+
const setSearch = useCallback(
|
|
36
|
+
(
|
|
37
|
+
updates: Partial<Record<keyof T, any>>,
|
|
38
|
+
options?: { resetPage?: boolean; replace?: boolean }
|
|
39
|
+
) => {
|
|
40
|
+
const next = new URLSearchParams(searchParams);
|
|
41
|
+
const keys = Object.keys(defaultRef.current);
|
|
42
|
+
|
|
43
|
+
for (const key of keys) {
|
|
44
|
+
const value = updates[key as keyof T];
|
|
45
|
+
if (value !== undefined) {
|
|
46
|
+
if (value === null || areEqual(value, defaultRef.current[key])) {
|
|
47
|
+
next.delete(key);
|
|
48
|
+
} else if (Array.isArray(value)) {
|
|
49
|
+
next.delete(key);
|
|
50
|
+
value.forEach((val) => {
|
|
51
|
+
if (val !== undefined && val !== null && val !== "") {
|
|
52
|
+
next.append(key, String(val));
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
} else {
|
|
56
|
+
next.set(key, String(value));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Reset pagination
|
|
62
|
+
* when filters/search changes
|
|
63
|
+
*/
|
|
64
|
+
if (options?.resetPage && "page" in defaultRef.current) {
|
|
65
|
+
next.delete("page");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
setSearchParams(next, {
|
|
69
|
+
replace: options?.replace,
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
[searchParams, setSearchParams]
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return { search, setSearch };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function areEqual(a: any, b: any) {
|
|
79
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
80
|
+
if (a.length !== b.length) return false;
|
|
81
|
+
const sortedA = [...a].sort();
|
|
82
|
+
const sortedB = [...b].sort();
|
|
83
|
+
return sortedA.every((val, index) => val === sortedB[index]);
|
|
84
|
+
}
|
|
85
|
+
return a === b;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function parseValue(value: string | string[], fallback: any): any {
|
|
89
|
+
if (Array.isArray(fallback)) {
|
|
90
|
+
const fallbackVal = fallback[0];
|
|
91
|
+
const valArray = Array.isArray(value) ? value : [value];
|
|
92
|
+
return valArray.map((item) => parseSingleValue(item, fallbackVal));
|
|
93
|
+
}
|
|
94
|
+
const singleVal = Array.isArray(value) ? value[0] : value;
|
|
95
|
+
return parseSingleValue(singleVal, fallback);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function parseSingleValue(value: string, fallback: any) {
|
|
99
|
+
if (typeof fallback === "number") {
|
|
100
|
+
const parsed = Number(value);
|
|
101
|
+
return Number.isNaN(parsed) ? fallback : parsed;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Boolean parsing
|
|
106
|
+
*/
|
|
107
|
+
if (typeof fallback === "boolean") {
|
|
108
|
+
return value === "true";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* String fallback
|
|
113
|
+
*/
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/*
|
|
118
|
+
# NOTE: Array parameter usage example
|
|
119
|
+
|
|
120
|
+
1. Define the default parameter as an array:
|
|
121
|
+
export const DefaultSearch = {
|
|
122
|
+
color: [] as string[],
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
2. Set values (e.g., in a component):
|
|
126
|
+
setSearch({ color: ["red", "blue"] });
|
|
127
|
+
// Resulting URL query: ?color=red&color=blue
|
|
128
|
+
|
|
129
|
+
3. Read values (e.g., in a component):
|
|
130
|
+
const colors = search.color; // Returns ["red", "blue"]
|
|
131
|
+
*/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Outlet } from "react-router-dom";
|
|
2
|
+
|
|
3
|
+
export function PrivateRoute() {
|
|
4
|
+
return <Outlet />;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
|
|
9
|
+
# NOTE : use this when you want to redirect user to login page if he is already logged out
|
|
10
|
+
|
|
11
|
+
import { Navigate, Outlet } from "react-router-dom";
|
|
12
|
+
|
|
13
|
+
export default function PrivateRoute() {
|
|
14
|
+
const token = getLocalAuthToken();
|
|
15
|
+
if (!token) {
|
|
16
|
+
return <Navigate to={"/"} replace />;
|
|
17
|
+
}
|
|
18
|
+
return <Outlet />;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
*/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Outlet } from "react-router-dom";
|
|
2
|
+
|
|
3
|
+
export function PublicRoute() {
|
|
4
|
+
return <Outlet />;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
|
|
9
|
+
# NOTE : use this when you want to redirect user to main page if he is already logged in
|
|
10
|
+
|
|
11
|
+
import { Navigate, Outlet } from "react-router-dom";
|
|
12
|
+
|
|
13
|
+
export default function PublicRoute() {
|
|
14
|
+
const token = getLocalAuthToken();
|
|
15
|
+
if (token) {
|
|
16
|
+
return <Navigate to={PATH.desired_path_name} replace />;
|
|
17
|
+
}
|
|
18
|
+
return <Outlet />;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
*/
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { createBrowserRouter, RouteObject } from "react-router-dom";
|
|
2
|
+
import { PATH } from "./path";
|
|
3
|
+
import { PublicRoute } from "./guards/public.route";
|
|
4
|
+
import { PrivateRoute } from "./guards/private.route";
|
|
5
|
+
import { AuthLayout } from "./layouts/auth.layout";
|
|
6
|
+
import { ErrorLayout } from "./layouts/error.layout";
|
|
7
|
+
import { MainLayout } from "./layouts/main.layout";
|
|
8
|
+
|
|
9
|
+
const PublicRoutes: RouteObject[] = [
|
|
10
|
+
{
|
|
11
|
+
path: PATH.page_root(),
|
|
12
|
+
Component: PublicRoute,
|
|
13
|
+
children: [
|
|
14
|
+
{
|
|
15
|
+
Component: AuthLayout,
|
|
16
|
+
ErrorBoundary: ErrorLayout,
|
|
17
|
+
children: [
|
|
18
|
+
{
|
|
19
|
+
path: PATH.page_auth(),
|
|
20
|
+
lazy: async () => {
|
|
21
|
+
const module = await import("../routes/page-auth");
|
|
22
|
+
return { Component: module.default };
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
path: PATH.not_found(),
|
|
29
|
+
lazy: async () => {
|
|
30
|
+
const module = await import("../routes/not-found");
|
|
31
|
+
return { Component: module.default };
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const PrivateRoutes: RouteObject[] = [
|
|
39
|
+
{
|
|
40
|
+
Component: PrivateRoute,
|
|
41
|
+
children: [
|
|
42
|
+
{
|
|
43
|
+
Component: MainLayout,
|
|
44
|
+
ErrorBoundary: ErrorLayout,
|
|
45
|
+
children: [
|
|
46
|
+
{
|
|
47
|
+
path: PATH.page_root(),
|
|
48
|
+
ErrorBoundary: ErrorLayout,
|
|
49
|
+
lazy: async () => {
|
|
50
|
+
const module = await import("../routes/page-root");
|
|
51
|
+
return { Component: module.default };
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
path: PATH.page_one(),
|
|
56
|
+
ErrorBoundary: ErrorLayout,
|
|
57
|
+
lazy: async () => {
|
|
58
|
+
const module = await import("../routes/page-one");
|
|
59
|
+
return { Component: module.default };
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
path: PATH.page_two(),
|
|
64
|
+
ErrorBoundary: ErrorLayout,
|
|
65
|
+
lazy: async () => {
|
|
66
|
+
const module = await import("../routes/page-two");
|
|
67
|
+
return { Component: module.default };
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
export const router = createBrowserRouter([...PublicRoutes, ...PrivateRoutes]);
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
|
|
80
|
+
currently i am using new way of using react-router version like
|
|
81
|
+
Component: PrivateLayout,
|
|
82
|
+
lazy: async () => ...
|
|
83
|
+
|
|
84
|
+
the old way is
|
|
85
|
+
|
|
86
|
+
{
|
|
87
|
+
path: "/",
|
|
88
|
+
element: (
|
|
89
|
+
<PublicRoute>
|
|
90
|
+
<AuthLayout />
|
|
91
|
+
</PublicRoute>
|
|
92
|
+
),
|
|
93
|
+
children: [
|
|
94
|
+
{
|
|
95
|
+
path: PATHS.LOGIN,
|
|
96
|
+
element: <Login />,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
| Feature | `errorElement` | `ErrorBoundary` |
|
|
104
|
+
| -------------------- | --------------------------------- | -------------------------- |
|
|
105
|
+
| What you pass | React Element | Component Function |
|
|
106
|
+
| Syntax | `errorElement: <ErrorPage />` | `ErrorBoundary: ErrorPage` |
|
|
107
|
+
| Requires JSX | Yes | No |
|
|
108
|
+
| Works in `.ts` file | No (unless `React.createElement`) | Yes |
|
|
109
|
+
| React Router version | v6.4+ | Newer preferred API |
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
# ANCHOR : 2 ways to show the error boundry page data
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# NOTE : WAY-1 inside layout i.e inplace of outlet
|
|
118
|
+
{
|
|
119
|
+
path: PATH.page_two(),
|
|
120
|
+
ErrorBoundary: ErrorLayout,
|
|
121
|
+
lazy: async () => {
|
|
122
|
+
const module = await import("../routes/page-two");
|
|
123
|
+
return { Component: module.default };
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
---------------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
# NOTE : WAY-2 replace entire layout with error boundary
|
|
130
|
+
{
|
|
131
|
+
Component: AuthLayout,
|
|
132
|
+
ErrorBoundary: ErrorLayout,
|
|
133
|
+
children: [
|
|
134
|
+
{
|
|
135
|
+
path: PATH.page_auth(),
|
|
136
|
+
lazy: async () => {
|
|
137
|
+
const module = await import("../routes/page-auth");
|
|
138
|
+
return { Component: module.default };
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
*/
|