@leo-h/create-nodejs-app 1.0.37 → 1.0.38
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 +1 -1
- package/templates/react-vite/.env.development +8 -0
- package/templates/react-vite/.env.example +4 -4
- package/templates/react-vite/api/errors.ts +14 -0
- package/templates/react-vite/{src/api → api}/swr-fetcher.ts +11 -10
- package/templates/react-vite/eslint.config.js +1 -1
- package/templates/react-vite/gitignore +4 -0
- package/templates/react-vite/orval.config.ts +5 -13
- package/templates/react-vite/package.json +23 -24
- package/templates/react-vite/pnpm-lock.yaml +566 -516
- package/templates/react-vite/scripts/orval-generate-api-definition.ts +2 -19
- package/templates/react-vite/src/app.tsx +1 -9
- package/templates/react-vite/src/pages/_layouts/app.tsx +7 -5
- package/templates/react-vite/src/pages/_layouts/auth.tsx +7 -5
- package/templates/react-vite/src/pages/app/dashboard/dashboard.tsx +2 -1
- package/templates/react-vite/src/pages/auth/sign-in/index.tsx +4 -2
- package/templates/react-vite/src/private-env.ts +18 -0
- package/templates/react-vite/src/public-env.ts +30 -0
- package/templates/react-vite/src/routes.tsx +29 -45
- package/templates/react-vite/tsconfig.app.json +3 -2
- package/templates/react-vite/tsconfig.json +2 -1
- package/templates/react-vite/tsconfig.node.json +8 -2
- package/templates/react-vite/vite.config.ts +1 -1
- package/templates/react-vite/src/api/errors/api-error.ts +0 -7
- package/templates/react-vite/src/api/errors/api-unexpected-response-error.ts +0 -8
- package/templates/react-vite/src/env.ts +0 -19
- package/templates/react-vite/src/hooks/use-current-route-handle-params.ts +0 -16
@@ -1,9 +1,9 @@
|
|
1
|
+
import { privateEnv } from "@/private-env";
|
1
2
|
import { execSync } from "node:child_process";
|
2
3
|
import { existsSync } from "node:fs";
|
3
4
|
import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
4
5
|
import { dirname, resolve } from "node:path";
|
5
6
|
import { generate } from "orval";
|
6
|
-
import { z } from "zod";
|
7
7
|
import { orvalCustomConfig } from "../orval.config";
|
8
8
|
|
9
9
|
export type OrvalCustomConfig = {
|
@@ -19,25 +19,8 @@ export type OrvalCustomConfig = {
|
|
19
19
|
};
|
20
20
|
};
|
21
21
|
|
22
|
-
const envSchemaFromNodeEnvironment = z.object({
|
23
|
-
API_JSON_DOCS_URL: z
|
24
|
-
.string()
|
25
|
-
.url()
|
26
|
-
.transform(val => new URL(val).toString()),
|
27
|
-
});
|
28
|
-
|
29
|
-
const parsedEnv = envSchemaFromNodeEnvironment.safeParse(process.env);
|
30
|
-
|
31
|
-
if (!parsedEnv.success) {
|
32
|
-
console.error(parsedEnv.error.flatten().fieldErrors);
|
33
|
-
|
34
|
-
throw new Error("Invalid environment variables.");
|
35
|
-
}
|
36
|
-
|
37
|
-
const env = parsedEnv.data;
|
38
|
-
|
39
22
|
(async () => {
|
40
|
-
const apiJsonDocsResponse = await fetch(
|
23
|
+
const apiJsonDocsResponse = await fetch(privateEnv.API_JSON_DOCS_URL);
|
41
24
|
const apiJsonDocs = await apiJsonDocsResponse.text();
|
42
25
|
const apiJsonDocsDirPath = dirname(orvalCustomConfig.apiDocs.outputPath);
|
43
26
|
|
@@ -1,14 +1,6 @@
|
|
1
|
-
import { Helmet, HelmetProvider } from "react-helmet-async";
|
2
1
|
import { RouterProvider } from "react-router/dom";
|
3
|
-
import { env } from "./env";
|
4
2
|
import { router } from "./routes";
|
5
3
|
|
6
4
|
export function App() {
|
7
|
-
return
|
8
|
-
<HelmetProvider>
|
9
|
-
<Helmet titleTemplate={`%s | ${env.APP_NAME}`}></Helmet>
|
10
|
-
|
11
|
-
<RouterProvider router={router} />
|
12
|
-
</HelmetProvider>
|
13
|
-
);
|
5
|
+
return <RouterProvider router={router} />;
|
14
6
|
}
|
@@ -1,13 +1,15 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
3
|
-
import { Outlet } from "react-router";
|
1
|
+
import { privateRoutes } from "@/routes";
|
2
|
+
import { Outlet, useLocation } from "react-router";
|
4
3
|
|
5
4
|
export function AppLayout() {
|
6
|
-
const {
|
5
|
+
const { pathname } = useLocation();
|
6
|
+
const currentPublicRoute = Object.values(privateRoutes).find(privateRoute => {
|
7
|
+
return privateRoute.path === pathname;
|
8
|
+
});
|
7
9
|
|
8
10
|
return (
|
9
11
|
<>
|
10
|
-
<
|
12
|
+
{currentPublicRoute && <title>{currentPublicRoute.title}</title>}
|
11
13
|
|
12
14
|
<div>
|
13
15
|
<Outlet />
|
@@ -1,13 +1,15 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
3
|
-
import { Outlet } from "react-router";
|
1
|
+
import { publicRoutes } from "@/routes";
|
2
|
+
import { Outlet, useLocation } from "react-router";
|
4
3
|
|
5
4
|
export function AuthLayout() {
|
6
|
-
const {
|
5
|
+
const { pathname } = useLocation();
|
6
|
+
const currentPublicRoute = Object.values(publicRoutes).find(publicRoute => {
|
7
|
+
return publicRoute.path === pathname;
|
8
|
+
});
|
7
9
|
|
8
10
|
return (
|
9
11
|
<>
|
10
|
-
<
|
12
|
+
{currentPublicRoute && <title>{currentPublicRoute.title}</title>}
|
11
13
|
|
12
14
|
<div>
|
13
15
|
<Outlet />
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { publicRoutes } from "@/routes";
|
1
2
|
import { Link } from "react-router";
|
2
3
|
import "./styles.css";
|
3
4
|
|
@@ -6,7 +7,7 @@ export function Dashboard() {
|
|
6
7
|
<div className="dashboard-wrapper">
|
7
8
|
<h1>Hello world!</h1>
|
8
9
|
|
9
|
-
<Link to=
|
10
|
+
<Link to={publicRoutes.signIn.path}>Sign out</Link>
|
10
11
|
</div>
|
11
12
|
);
|
12
13
|
}
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { privateRoutes } from "@/routes";
|
1
2
|
import { zodResolver } from "@hookform/resolvers/zod";
|
2
3
|
import { useForm } from "react-hook-form";
|
3
4
|
import { useNavigate } from "react-router";
|
@@ -12,6 +13,8 @@ const signInFormSchema = z.object({
|
|
12
13
|
type SignInForm = z.infer<typeof signInFormSchema>;
|
13
14
|
|
14
15
|
export function SignIn() {
|
16
|
+
const navigate = useNavigate();
|
17
|
+
|
15
18
|
const signInForm = useForm<SignInForm>({
|
16
19
|
resolver: zodResolver(signInFormSchema),
|
17
20
|
defaultValues: {
|
@@ -19,10 +22,9 @@ export function SignIn() {
|
|
19
22
|
password: "",
|
20
23
|
},
|
21
24
|
});
|
22
|
-
const navigate = useNavigate();
|
23
25
|
const onSignIn = async (input: SignInForm) => {
|
24
26
|
console.log(input);
|
25
|
-
navigate(
|
27
|
+
navigate(privateRoutes.dashboard.path);
|
26
28
|
};
|
27
29
|
|
28
30
|
return (
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { z } from "zod";
|
2
|
+
|
3
|
+
const schema = z.object({
|
4
|
+
API_JSON_DOCS_URL: z
|
5
|
+
.string()
|
6
|
+
.url()
|
7
|
+
.transform(url => new URL(url)),
|
8
|
+
});
|
9
|
+
|
10
|
+
const parsedEnv = schema.safeParse(process.env);
|
11
|
+
|
12
|
+
if (!parsedEnv.success) {
|
13
|
+
console.error(parsedEnv.error.flatten().fieldErrors);
|
14
|
+
|
15
|
+
throw new Error("Invalid private environment variables.");
|
16
|
+
}
|
17
|
+
|
18
|
+
export const privateEnv = parsedEnv.data;
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { z } from "zod";
|
2
|
+
|
3
|
+
type PublicEnvironmentVariables = {
|
4
|
+
PUBLIC_APP_NAME: string;
|
5
|
+
PUBLIC_API_BASE_URL: string;
|
6
|
+
};
|
7
|
+
|
8
|
+
const publicEnvironmentVariables = import.meta.env as ImportMetaEnv &
|
9
|
+
PublicEnvironmentVariables;
|
10
|
+
|
11
|
+
const schema = z.object({
|
12
|
+
APP_NAME: z.string(),
|
13
|
+
API_BASE_URL: z
|
14
|
+
.string()
|
15
|
+
.url()
|
16
|
+
.transform(url => new URL(url)),
|
17
|
+
});
|
18
|
+
|
19
|
+
const parsedEnv = schema.safeParse({
|
20
|
+
APP_NAME: publicEnvironmentVariables.PUBLIC_APP_NAME,
|
21
|
+
API_BASE_URL: publicEnvironmentVariables.PUBLIC_API_BASE_URL,
|
22
|
+
});
|
23
|
+
|
24
|
+
if (!parsedEnv.success) {
|
25
|
+
console.error(parsedEnv.error.flatten().fieldErrors);
|
26
|
+
|
27
|
+
throw new Error("Invalid public environment variables.");
|
28
|
+
}
|
29
|
+
|
30
|
+
export const publicEnv = parsedEnv.data;
|
@@ -1,65 +1,49 @@
|
|
1
|
-
import { createBrowserRouter } from "react-router";
|
2
|
-
import {
|
3
|
-
CustomRouteDefinition,
|
4
|
-
ReactRouterRouteDefinition,
|
5
|
-
} from "./@types/routes";
|
1
|
+
import { createBrowserRouter, RouteObject } from "react-router";
|
6
2
|
import { AppLayout } from "./pages/_layouts/app";
|
7
3
|
import { AuthLayout } from "./pages/_layouts/auth";
|
8
|
-
import { SignIn } from "./pages/auth/sign-in";
|
9
4
|
import { Dashboard } from "./pages/app/dashboard/dashboard";
|
5
|
+
import { SignIn } from "./pages/auth/sign-in";
|
6
|
+
import { publicEnv } from "./public-env";
|
10
7
|
|
11
|
-
export const
|
12
|
-
{
|
13
|
-
path: "/
|
14
|
-
|
8
|
+
export const publicRoutes = Object.freeze({
|
9
|
+
signIn: {
|
10
|
+
path: "/",
|
11
|
+
element: <SignIn />,
|
12
|
+
title: createTitleTemplate("Sign In"),
|
15
13
|
},
|
16
|
-
|
14
|
+
} as const);
|
17
15
|
|
18
|
-
export const
|
19
|
-
{
|
16
|
+
export const privateRoutes = Object.freeze({
|
17
|
+
dashboard: {
|
20
18
|
path: "/dashboard",
|
21
|
-
|
19
|
+
element: <Dashboard />,
|
20
|
+
title: createTitleTemplate("Dashboard"),
|
22
21
|
},
|
23
|
-
|
22
|
+
} as const);
|
24
23
|
|
25
24
|
export const router = createBrowserRouter([
|
26
25
|
{
|
27
26
|
element: <AuthLayout />,
|
28
27
|
children: [
|
29
|
-
{
|
30
|
-
path:
|
31
|
-
element:
|
28
|
+
...Object.values(publicRoutes).map<RouteObject>(publicRoute => ({
|
29
|
+
path: publicRoute.path,
|
30
|
+
element: publicRoute.element,
|
32
31
|
index: true,
|
33
|
-
|
34
|
-
|
35
|
-
title: "Sign In",
|
36
|
-
},
|
37
|
-
},
|
38
|
-
},
|
39
|
-
{
|
40
|
-
path: "/sign-in",
|
41
|
-
element: <SignIn />,
|
42
|
-
index: true,
|
43
|
-
handle: {
|
44
|
-
metadata: {
|
45
|
-
title: "Sign In",
|
46
|
-
},
|
47
|
-
},
|
48
|
-
},
|
49
|
-
] satisfies ReactRouterRouteDefinition<typeof authNavigationRoutes>[],
|
32
|
+
})),
|
33
|
+
],
|
50
34
|
},
|
51
35
|
{
|
52
36
|
element: <AppLayout />,
|
53
37
|
children: [
|
54
|
-
{
|
55
|
-
path:
|
56
|
-
element:
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
},
|
61
|
-
},
|
62
|
-
},
|
63
|
-
] satisfies ReactRouterRouteDefinition<typeof appNavigationRoutes>[],
|
38
|
+
...Object.values(privateRoutes).map<RouteObject>(publicRoute => ({
|
39
|
+
path: publicRoute.path,
|
40
|
+
element: publicRoute.element,
|
41
|
+
index: true,
|
42
|
+
})),
|
43
|
+
],
|
64
44
|
},
|
65
45
|
]);
|
46
|
+
|
47
|
+
function createTitleTemplate(title: string) {
|
48
|
+
return `${title} | ${publicEnv.APP_NAME}`;
|
49
|
+
}
|
@@ -18,7 +18,13 @@
|
|
18
18
|
"noUnusedLocals": true,
|
19
19
|
"noUnusedParameters": true,
|
20
20
|
"noFallthroughCasesInSwitch": true,
|
21
|
-
"noUncheckedSideEffectImports": true
|
21
|
+
"noUncheckedSideEffectImports": true,
|
22
|
+
|
23
|
+
/* Custom */
|
24
|
+
"baseUrl": ".",
|
25
|
+
"paths": {
|
26
|
+
"@/*": ["./src/*"]
|
27
|
+
}
|
22
28
|
},
|
23
|
-
"include": ["vite.config.ts"]
|
29
|
+
"include": ["vite.config.ts", "scripts"]
|
24
30
|
}
|
@@ -1,8 +0,0 @@
|
|
1
|
-
const defaultMessage =
|
2
|
-
"Houve uma falha inesperada ao se comunicar com nosso servidor. Por favor, contate nosso suporte." as const;
|
3
|
-
|
4
|
-
export class ApiUnexpectedResponseError {
|
5
|
-
static message = defaultMessage;
|
6
|
-
|
7
|
-
constructor(public message = defaultMessage) {}
|
8
|
-
}
|
@@ -1,19 +0,0 @@
|
|
1
|
-
import { z } from "zod";
|
2
|
-
|
3
|
-
const schema = z.object({
|
4
|
-
APP_NAME: z.string(),
|
5
|
-
APP_API_BASE_URL: z
|
6
|
-
.string()
|
7
|
-
.url()
|
8
|
-
.transform(val => new URL(val).toString()),
|
9
|
-
});
|
10
|
-
|
11
|
-
const parsedEnv = schema.safeParse(import.meta.env);
|
12
|
-
|
13
|
-
if (!parsedEnv.success) {
|
14
|
-
console.error(parsedEnv.error.flatten().fieldErrors);
|
15
|
-
|
16
|
-
throw new Error("Invalid environment variables.");
|
17
|
-
}
|
18
|
-
|
19
|
-
export const env = parsedEnv.data;
|
@@ -1,16 +0,0 @@
|
|
1
|
-
import { ReactRouterRouteHandleDefinition } from "@/@types/routes";
|
2
|
-
import { useLocation, useMatches } from "react-router";
|
3
|
-
|
4
|
-
export function useCurrentRouteHandleParams() {
|
5
|
-
const { pathname } = useLocation();
|
6
|
-
const routeMatches = useMatches();
|
7
|
-
const currentRouteHandleParams = routeMatches.find(routeMatch => {
|
8
|
-
return routeMatch.pathname === pathname && routeMatch.handle;
|
9
|
-
});
|
10
|
-
|
11
|
-
return {
|
12
|
-
pathname,
|
13
|
-
handleParams:
|
14
|
-
currentRouteHandleParams?.handle as ReactRouterRouteHandleDefinition,
|
15
|
-
};
|
16
|
-
}
|