pjdev2d-cli 1.0.4 → 1.1.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pjdev2d-cli",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
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",
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
+ */