pjdev2d-cli 1.0.4 → 1.1.1
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/tanstack-query/route/index.tsx +0 -0
- package/templates/tanstack-query/services/base-query.ts +0 -0
- package/templates/tanstack-query/services/client.ts +85 -0
- package/templates/tanstack-query/services/module/index.ts +12 -0
- package/templates/tanstack-query/services/module/keys.ts +13 -0
- package/templates/tanstack-query/services/module/mappers.ts +11 -0
- package/templates/tanstack-query/services/module/mutations.ts +25 -0
- package/templates/tanstack-query/services/module/queries.ts +27 -0
- package/templates/tanstack-query/services/module/services.ts +18 -0
- package/templates/tanstack-query/services/mutations.ts +15 -0
- package/templates/tanstack-query/services/queries.ts +67 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pjdev2d-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI to install reusable React components like shadcn/ui",
|
|
6
6
|
"main": "bin/cli.js",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"@babel/core": "^7.29.0",
|
|
24
24
|
"@babel/preset-react": "^7.28.5",
|
|
25
25
|
"@babel/preset-typescript": "^7.29.7",
|
|
26
|
+
"@tanstack/react-query": "^5.100.14",
|
|
26
27
|
"@types/react": "^19.2.14",
|
|
27
28
|
"clsx": "^2.1.1",
|
|
28
29
|
"commander": "^14.0.3",
|
package/registry.json
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/*
|
|
2
|
+
#ANCHOR : TYPE:1 VITE PROJECT
|
|
3
|
+
const BASE_URL = process.env.NEXT_PUBLIC_API_URL;
|
|
4
|
+
|
|
5
|
+
Uses import.meta.env
|
|
6
|
+
Environment variables must start with VITE_ to be exposed to client-side code.
|
|
7
|
+
|
|
8
|
+
---------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
#ANCHOR : TYPE:2 NON-VITE PROJECTS
|
|
11
|
+
const BASE_URL = import.meta.env.VITE_API_URL;
|
|
12
|
+
|
|
13
|
+
Uses process.env
|
|
14
|
+
Environment variables must start with NEXT_PUBLIC_ to be available in the browser.
|
|
15
|
+
|
|
16
|
+
---------------------------------------------------------
|
|
17
|
+
#ANCHOR
|
|
18
|
+
Vite doesn't automatically provide Node's process.env in browser code.
|
|
19
|
+
|
|
20
|
+
| Framework | Access Method | Public Prefix |
|
|
21
|
+
| --------------- | ------------------- | ---------------- |
|
|
22
|
+
| Next.js | `process.env.X` | `NEXT_PUBLIC_` |
|
|
23
|
+
| Vite | `import.meta.env.X` | `VITE_` |
|
|
24
|
+
| Node.js Backend | `process.env.X` | No prefix needed |
|
|
25
|
+
|
|
26
|
+
*/
|
|
27
|
+
// NOTE BASE_URL to change with actual api
|
|
28
|
+
const BASE_URL = "http://localhost:5000/api";
|
|
29
|
+
|
|
30
|
+
//NOTE: apiRequest: This is the core function that all other methods use.
|
|
31
|
+
export async function apiRequest<T>(
|
|
32
|
+
endpoint: string,
|
|
33
|
+
options: RequestInit = {},
|
|
34
|
+
) {
|
|
35
|
+
const response = await fetch(`${BASE_URL}${endpoint}`, {
|
|
36
|
+
...options,
|
|
37
|
+
headers: {
|
|
38
|
+
"Content-Type": "application/json",
|
|
39
|
+
...options.headers,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (!response.ok) throw new Error(`Error Code ${response.status}`);
|
|
44
|
+
return (await response.json()) as T;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function withJsonBody(method: string, body: any, options?: RequestInit) {
|
|
48
|
+
return {
|
|
49
|
+
...options,
|
|
50
|
+
method,
|
|
51
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
52
|
+
/*
|
|
53
|
+
# NOTE : fetch expects JSON bodies as strings
|
|
54
|
+
line this
|
|
55
|
+
body: '{"name":"John"}'
|
|
56
|
+
and not
|
|
57
|
+
body: data
|
|
58
|
+
in case of undefined - does a Undefined Body Check
|
|
59
|
+
body: "undefined"
|
|
60
|
+
*/
|
|
61
|
+
} satisfies RequestInit;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/*
|
|
65
|
+
|
|
66
|
+
Contains all api type calls
|
|
67
|
+
Usage:
|
|
68
|
+
const data = await apiClient.get<User>("/users/1");
|
|
69
|
+
const data = await apiClient.post<User>("/users", { name: "John" });
|
|
70
|
+
const data = await apiClient.put<User>("/users/1", { name: "John" });
|
|
71
|
+
const data = await apiClient.patch<User>("/users/1", { name: "John" });
|
|
72
|
+
const data = await apiClient.delete<User>("/users/1");
|
|
73
|
+
|
|
74
|
+
*/
|
|
75
|
+
export const apiClient = {
|
|
76
|
+
get: apiRequest,
|
|
77
|
+
post: <T>(endpoint: string, body?: any, options?: RequestInit) =>
|
|
78
|
+
apiRequest<T>(endpoint, withJsonBody("POST", body, options)),
|
|
79
|
+
put: <T>(endpoint: string, body?: any, options?: RequestInit) =>
|
|
80
|
+
apiRequest<T>(endpoint, withJsonBody("PUT", body, options)),
|
|
81
|
+
patch: <T>(endpoint: string, body?: any, options?: RequestInit) =>
|
|
82
|
+
apiRequest<T>(endpoint, withJsonBody("PATCH", body, options)),
|
|
83
|
+
delete: <T>(endpoint: string, options?: RequestInit) =>
|
|
84
|
+
apiRequest<T>(endpoint, { ...options, method: "DELETE" }),
|
|
85
|
+
} as const;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { ModuleKeys } from "./keys";
|
|
2
|
+
export { ModuleMappers } from "./mappers";
|
|
3
|
+
export { ModuleService } from "./services";
|
|
4
|
+
export { ModuleQueries } from "./queries";
|
|
5
|
+
export { ModuleMutations } from "./mutations";
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
# NOTE: here we can export types.ts as well if we defining types seperately in a file
|
|
9
|
+
export type * from "./types";
|
|
10
|
+
# NOTE : we can also export a mocks.ts file if we using mocks which contains dummy / fall back data
|
|
11
|
+
export { ModuleMocks } from "./mocks";
|
|
12
|
+
*/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const ModuleKeys = {
|
|
2
|
+
module: () => ["moduleName"] as const,
|
|
3
|
+
fetch_query_name_example: () => [...ModuleKeys.module(), "fetch"] as const,
|
|
4
|
+
create_query_name_example: () => [...ModuleKeys.module(), "create"] as const,
|
|
5
|
+
update_query_name_example: () => [...ModuleKeys.module(), "update"] as const,
|
|
6
|
+
delete_query_name_example: () => [...ModuleKeys.module(), "delete"] as const,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
# NOTE: as const is used at end so that keys are immutable
|
|
11
|
+
# NOTE : ans insted of standard object keys usd as function so its easy and clean to use and maintain in larger scale
|
|
12
|
+
# NOTE : you can change *_name_example to your own query name
|
|
13
|
+
*/
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
# NOTE : you can change *_name_example to your own query name
|
|
3
|
+
# NOTE : you can change raw to your own data after manipulation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const ModuleMappers = {
|
|
7
|
+
fetch_query_name_example(raw: any) {
|
|
8
|
+
const data = raw || "manipulate your data here and then return it";
|
|
9
|
+
return data;
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { mutationOptions, type QueryClient } from "@tanstack/react-query";
|
|
2
|
+
import { ModuleKeys } from "./keys";
|
|
3
|
+
import { ModuleService } from "./services";
|
|
4
|
+
|
|
5
|
+
export const ModuleMutations = {
|
|
6
|
+
create_query_name_example: (queryClient: QueryClient) =>
|
|
7
|
+
mutationOptions({
|
|
8
|
+
mutationFn: (input_name: any) =>
|
|
9
|
+
ModuleService.post_query_name_example(input_name),
|
|
10
|
+
onMutate: () => {
|
|
11
|
+
/* Write optimistic update logic here */
|
|
12
|
+
},
|
|
13
|
+
onSuccess: async () => {
|
|
14
|
+
/*
|
|
15
|
+
# NOTE: In case of POST/PATCH/DELETE operations, it is very important to invalidate the queries that depend on the data that was mutated.
|
|
16
|
+
*/
|
|
17
|
+
await queryClient.invalidateQueries({
|
|
18
|
+
queryKey: ModuleKeys.fetch_query_name_example(),
|
|
19
|
+
});
|
|
20
|
+
},
|
|
21
|
+
onError: (error) => {
|
|
22
|
+
/* Write error logic here */
|
|
23
|
+
},
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { queryOptions } from "@tanstack/react-query";
|
|
2
|
+
|
|
3
|
+
import { ModuleKeys } from "./keys";
|
|
4
|
+
import { ModuleMappers } from "./mappers";
|
|
5
|
+
import { ModuleService } from "./services";
|
|
6
|
+
|
|
7
|
+
export const ModuleQueries = {
|
|
8
|
+
fetch_query_name_example: () =>
|
|
9
|
+
queryOptions({
|
|
10
|
+
queryKey: ModuleKeys.fetch_query_name_example(),
|
|
11
|
+
queryFn: async () => {
|
|
12
|
+
try {
|
|
13
|
+
/*
|
|
14
|
+
# NOTE: ModuleService.get_query_name_example() -> hts the api of query fetch or GET
|
|
15
|
+
# NOTE: ModuleMappers.fetch_query_name_example() -> manipulates the data from api into desird format before returning to ui or page
|
|
16
|
+
*/
|
|
17
|
+
const raw = await ModuleService.get_query_name_example();
|
|
18
|
+
return ModuleMappers.fetch_query_name_example(raw as any);
|
|
19
|
+
} catch {
|
|
20
|
+
/*
|
|
21
|
+
# NOTE: you can change null to empty array [] or fallback data that you will get from mocks.ts file or you can handel some other logic here
|
|
22
|
+
*/
|
|
23
|
+
return ModuleMappers.fetch_query_name_example(null);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { apiClient } from "../client";
|
|
2
|
+
|
|
3
|
+
export const ModuleService = {
|
|
4
|
+
get_query_name_example: () => apiClient.get("/api_name/get"),
|
|
5
|
+
post_query_name_example: (input: any) =>
|
|
6
|
+
apiClient.post("/api_name/post", input),
|
|
7
|
+
patch_query_name_example: (input: any) =>
|
|
8
|
+
apiClient.patch("/api_name/patch", input),
|
|
9
|
+
delete_query_name_example: (id: string) =>
|
|
10
|
+
apiClient.delete(`/api_name/${id}`),
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
# NOTE: you can change *_name_example to your own query name
|
|
15
|
+
# NOTE: you can change /api_name to your own api name
|
|
16
|
+
# NOTE: you can change input to your own data type
|
|
17
|
+
# NOTE: you can change id to your own data type
|
|
18
|
+
*/
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ModuleMutations } from "./module";
|
|
2
|
+
|
|
3
|
+
export const apiMutations = {
|
|
4
|
+
module: ModuleMutations,
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export * from "./module";
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
# NOTE : here you export all the mutations from the module folder so it will be easy to import in components
|
|
11
|
+
|
|
12
|
+
const queryClient = useQueryClient();
|
|
13
|
+
const mutation_name_example = useMutation(ModuleMutations.create_query_name_example(queryClient));
|
|
14
|
+
mutation_name_example.mutate({input_name: data})
|
|
15
|
+
*/
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ModuleQueries } from "./module";
|
|
2
|
+
|
|
3
|
+
export const apiQueries = {
|
|
4
|
+
module: ModuleQueries,
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export * from "./module";
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
# NOTE : here you export all the queries from the module folder so it will be easy to import in components
|
|
11
|
+
|
|
12
|
+
const { data: name } = useQuery(ModuleQueries.fetch_query_name_example());
|
|
13
|
+
const { data: name } = useSuspenseQuery(ModuleQueries.fetch_query_name_example());
|
|
14
|
+
|
|
15
|
+
---------------------------------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
| Feature | `useQuery` | `useSuspenseQuery` |
|
|
18
|
+
| ------------------- | ---------------------------------------------- | --------------------------------------------------- |
|
|
19
|
+
| Loading state | Returns `isLoading`, `isPending`, `isFetching` | Does **not** return loading state |
|
|
20
|
+
| Data type | `data` can be `undefined` | `data` is guaranteed to exist when rendered |
|
|
21
|
+
| Error handling | Use `error` from hook | Error is caught by React Error Boundary |
|
|
22
|
+
| Loading UI | Handle manually (`if (isLoading)`) | Handled by React `<Suspense>` fallback |
|
|
23
|
+
| Component rendering | Renders immediately, then fetches | Suspends rendering until data is ready |
|
|
24
|
+
| Setup complexity | Simpler | Requires `<Suspense>` and usually an Error Boundary |
|
|
25
|
+
| Best for | Most applications | Apps fully using React Suspense |
|
|
26
|
+
|
|
27
|
+
----------------------------------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
# NOTE : Example for useQuery
|
|
30
|
+
const ModuleComponent = () => {
|
|
31
|
+
|
|
32
|
+
const { data, isLoading, error } = useQuery(
|
|
33
|
+
ModuleQueries.fetch_query_name_example()
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
if (isLoading) return <FallBackComponent />;
|
|
37
|
+
if (error) return <ErrorComponent />;
|
|
38
|
+
|
|
39
|
+
return <ModuleComponent data={data} />;
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
-----------------------------------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
# NOTE : Example for useSuspenseQuery
|
|
46
|
+
|
|
47
|
+
const ModuleComponent = () => {
|
|
48
|
+
|
|
49
|
+
const { data } = useSuspenseQuery(
|
|
50
|
+
ModuleQueries.fetch_query_name_example()
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return <ModuleComponent data={data} />;
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# NOTE : Wrapping
|
|
58
|
+
<Suspense fallback={<FallBackComponent />}>
|
|
59
|
+
<ModuleComponent />
|
|
60
|
+
</Suspense>
|
|
61
|
+
|
|
62
|
+
---------------------------------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
Use useQuery when you want to manage loading and errors inside the component.
|
|
65
|
+
Use useSuspenseQuery when your app already uses React Suspense and you want cleaner components with guaranteed data.
|
|
66
|
+
|
|
67
|
+
*/
|