@leo-h/create-nodejs-app 1.0.36 → 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.
Files changed (29) hide show
  1. package/package.json +1 -1
  2. package/templates/nest/package.json +1 -1
  3. package/templates/nest/pnpm-lock.yaml +31 -28
  4. package/templates/react-vite/.env.development +8 -0
  5. package/templates/react-vite/.env.example +4 -4
  6. package/templates/react-vite/api/errors.ts +14 -0
  7. package/templates/react-vite/{src/api → api}/swr-fetcher.ts +11 -10
  8. package/templates/react-vite/eslint.config.js +1 -1
  9. package/templates/react-vite/gitignore +4 -0
  10. package/templates/react-vite/orval.config.ts +5 -13
  11. package/templates/react-vite/package.json +23 -24
  12. package/templates/react-vite/pnpm-lock.yaml +566 -516
  13. package/templates/react-vite/scripts/orval-generate-api-definition.ts +2 -19
  14. package/templates/react-vite/src/app.tsx +1 -9
  15. package/templates/react-vite/src/pages/_layouts/app.tsx +7 -5
  16. package/templates/react-vite/src/pages/_layouts/auth.tsx +7 -5
  17. package/templates/react-vite/src/pages/app/dashboard/dashboard.tsx +2 -1
  18. package/templates/react-vite/src/pages/auth/sign-in/index.tsx +4 -2
  19. package/templates/react-vite/src/private-env.ts +18 -0
  20. package/templates/react-vite/src/public-env.ts +30 -0
  21. package/templates/react-vite/src/routes.tsx +29 -45
  22. package/templates/react-vite/tsconfig.app.json +3 -2
  23. package/templates/react-vite/tsconfig.json +2 -1
  24. package/templates/react-vite/tsconfig.node.json +8 -2
  25. package/templates/react-vite/vite.config.ts +1 -1
  26. package/templates/react-vite/src/api/errors/api-error.ts +0 -7
  27. package/templates/react-vite/src/api/errors/api-unexpected-response-error.ts +0 -8
  28. package/templates/react-vite/src/env.ts +0 -19
  29. 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(env.API_JSON_DOCS_URL);
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 { useCurrentRouteHandleParams } from "@/hooks/use-current-route-handle-params";
2
- import { Helmet } from "react-helmet-async";
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 { handleParams } = useCurrentRouteHandleParams();
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
- <Helmet title={handleParams.metadata.title} />
12
+ {currentPublicRoute && <title>{currentPublicRoute.title}</title>}
11
13
 
12
14
  <div>
13
15
  <Outlet />
@@ -1,13 +1,15 @@
1
- import { useCurrentRouteHandleParams } from "@/hooks/use-current-route-handle-params";
2
- import { Helmet } from "react-helmet-async";
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 { handleParams } = useCurrentRouteHandleParams();
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
- <Helmet title={handleParams.metadata.title} />
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="/sign-in">Sign out</Link>
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("/dashboard");
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 authNavigationRoutes = [
12
- {
13
- path: "/sign-in",
14
- label: "Sign In",
8
+ export const publicRoutes = Object.freeze({
9
+ signIn: {
10
+ path: "/",
11
+ element: <SignIn />,
12
+ title: createTitleTemplate("Sign In"),
15
13
  },
16
- ] as const satisfies CustomRouteDefinition[];
14
+ } as const);
17
15
 
18
- export const appNavigationRoutes = [
19
- {
16
+ export const privateRoutes = Object.freeze({
17
+ dashboard: {
20
18
  path: "/dashboard",
21
- label: "Dashboard",
19
+ element: <Dashboard />,
20
+ title: createTitleTemplate("Dashboard"),
22
21
  },
23
- ] as const satisfies CustomRouteDefinition[];
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: <SignIn />,
28
+ ...Object.values(publicRoutes).map<RouteObject>(publicRoute => ({
29
+ path: publicRoute.path,
30
+ element: publicRoute.element,
32
31
  index: true,
33
- handle: {
34
- metadata: {
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: "/dashboard",
56
- element: <Dashboard />,
57
- handle: {
58
- metadata: {
59
- title: "Dashboard",
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
+ }
@@ -25,8 +25,9 @@
25
25
  /* Custom */
26
26
  "baseUrl": ".",
27
27
  "paths": {
28
- "@/*": ["./src/*"]
28
+ "@/*": ["./src/*"],
29
+ "@api/*": ["./api/*"]
29
30
  }
30
31
  },
31
- "include": ["src"]
32
+ "include": ["src", "api"]
32
33
  }
@@ -7,7 +7,8 @@
7
7
  "compilerOptions": {
8
8
  "baseUrl": ".",
9
9
  "paths": {
10
- "@/*": ["./src/*"]
10
+ "@/*": ["./src/*"],
11
+ "@api/*": ["./api/*"]
11
12
  }
12
13
  }
13
14
  }
@@ -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
  }
@@ -5,5 +5,5 @@ import tsconfigPaths from "vite-tsconfig-paths";
5
5
  // https://vite.dev/config/
6
6
  export default defineConfig({
7
7
  plugins: [react(), tsconfigPaths()],
8
- envPrefix: "APP",
8
+ envPrefix: "PUBLIC",
9
9
  });
@@ -1,7 +0,0 @@
1
- export class ApiError {
2
- public constructor(
3
- public readonly statusCode: number,
4
- public readonly error: string,
5
- public readonly message: string,
6
- ) {}
7
- }
@@ -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
- }