create-web-0to1 0.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.
Files changed (193) hide show
  1. package/README.md +68 -0
  2. package/internal/engine/create-feature-crud-script.ts +134 -0
  3. package/internal/engine/create-feature-crud.template.mjs +601 -0
  4. package/internal/engine/create-feature-script.ts +142 -0
  5. package/internal/engine/generator-engine.ts +546 -0
  6. package/internal/engine/standalone-feature-preset.ts +34 -0
  7. package/internal/meta/preset-plan.ts +41 -0
  8. package/internal/meta/runtime-copy-plan.ts +220 -0
  9. package/internal/meta/runtime-layout.ts +262 -0
  10. package/internal/meta/scaffold-manifest.ts +169 -0
  11. package/internal/meta/standalone-dependency-manifest.ts +75 -0
  12. package/package.json +45 -0
  13. package/scripts/create-app.mjs +1612 -0
  14. package/source/core/auth/auth-events.ts +13 -0
  15. package/source/core/error/app-error.ts +85 -0
  16. package/source/core/error/handle-app-error.client.ts +35 -0
  17. package/source/core/lib/dayjs.ts +25 -0
  18. package/source/core/query/query-client.ts +126 -0
  19. package/source/core/request/request-core.ts +210 -0
  20. package/source/core/routes/route-paths.ts +4 -0
  21. package/source/core/ui/button.tsx +24 -0
  22. package/source/core/ui/modal-store.ts +32 -0
  23. package/source/core/ui/text-input-field.tsx +36 -0
  24. package/source/core/utils/build-query-string.ts +30 -0
  25. package/source/core/utils/format/date.ts +41 -0
  26. package/source/core/utils/format/index.ts +3 -0
  27. package/source/core/utils/format/number.ts +13 -0
  28. package/source/core/utils/format/text.ts +15 -0
  29. package/source/core/utils/schema-utils.ts +27 -0
  30. package/source/wrappers/monorepo/core/internal.ts +21 -0
  31. package/source/wrappers/monorepo/core/src/index.ts +4 -0
  32. package/source/wrappers/monorepo/core-next/src/auth.client.ts +1 -0
  33. package/source/wrappers/monorepo/core-next/src/auth.server.ts +94 -0
  34. package/source/wrappers/monorepo/core-next/src/bootstrap.client.tsx +21 -0
  35. package/source/wrappers/monorepo/core-next/src/bootstrap.tsx +18 -0
  36. package/source/wrappers/monorepo/core-next/src/index.ts +1 -0
  37. package/source/wrappers/monorepo/core-react/src/app-providers.tsx +36 -0
  38. package/source/wrappers/monorepo/core-react/src/auth.ts +42 -0
  39. package/source/wrappers/monorepo/core-react/src/hydration.tsx +21 -0
  40. package/source/wrappers/monorepo/core-react/src/index.ts +7 -0
  41. package/source/wrappers/monorepo/core-react/src/provider.tsx +49 -0
  42. package/source/wrappers/monorepo/core-react/src/query-client.ts +48 -0
  43. package/source/wrappers/monorepo/core-react/src/query-error-handler.ts +62 -0
  44. package/source/wrappers/monorepo/core-react/src/query-keys.ts +22 -0
  45. package/source/wrappers/monorepo/request/core-fetch.ts +27 -0
  46. package/source/wrappers/monorepo/request/core-request.ts +93 -0
  47. package/source/wrappers/next/auth/auth-error-listener.tsx +34 -0
  48. package/source/wrappers/next/error/handle-app-error.server.ts +41 -0
  49. package/source/wrappers/next/query/hydration.tsx +20 -0
  50. package/source/wrappers/next/query/providers.tsx +35 -0
  51. package/source/wrappers/next/request/request.client.ts +24 -0
  52. package/source/wrappers/next/request/request.server.ts +64 -0
  53. package/source/wrappers/next/request/request.ts +52 -0
  54. package/source/wrappers/next/ui/global-modal.tsx +29 -0
  55. package/source/wrappers/react/auth/auth-error-listener.tsx +34 -0
  56. package/source/wrappers/react/query/providers.tsx +31 -0
  57. package/source/wrappers/react/request/request.client.ts +24 -0
  58. package/source/wrappers/react/request/request.ts +51 -0
  59. package/source/wrappers/react/ui/global-modal.tsx +27 -0
  60. package/templates/monorepo/.dockerignore +38 -0
  61. package/templates/monorepo/README.md +292 -0
  62. package/templates/monorepo/_gitignore +38 -0
  63. package/templates/monorepo/_npmrc +1 -0
  64. package/templates/monorepo/apps/project/Dockerfile +32 -0
  65. package/templates/monorepo/apps/project/eslint.config.mjs +4 -0
  66. package/templates/monorepo/apps/project/index.html +14 -0
  67. package/templates/monorepo/apps/project/index.ts +15 -0
  68. package/templates/monorepo/apps/project/package.json +21 -0
  69. package/templates/monorepo/apps/project/tsconfig.json +9 -0
  70. package/templates/monorepo/apps/project/vite.config.ts +6 -0
  71. package/templates/monorepo/apps/web/Dockerfile +43 -0
  72. package/templates/monorepo/apps/web/README.md +111 -0
  73. package/templates/monorepo/apps/web/_gitignore +36 -0
  74. package/templates/monorepo/apps/web/app/favicon.ico +0 -0
  75. package/templates/monorepo/apps/web/app/global-error.tsx +12 -0
  76. package/templates/monorepo/apps/web/app/globals.css +0 -0
  77. package/templates/monorepo/apps/web/app/layout.tsx +28 -0
  78. package/templates/monorepo/apps/web/app/page.tsx +7 -0
  79. package/templates/monorepo/apps/web/app/providers.tsx +25 -0
  80. package/templates/monorepo/apps/web/eslint.config.js +4 -0
  81. package/templates/monorepo/apps/web/next-env.d.ts +6 -0
  82. package/templates/monorepo/apps/web/next.config.js +4 -0
  83. package/templates/monorepo/apps/web/package.json +31 -0
  84. package/templates/monorepo/apps/web/public/file-text.svg +3 -0
  85. package/templates/monorepo/apps/web/public/globe.svg +10 -0
  86. package/templates/monorepo/apps/web/public/next.svg +1 -0
  87. package/templates/monorepo/apps/web/public/turborepo-dark.svg +19 -0
  88. package/templates/monorepo/apps/web/public/turborepo-light.svg +19 -0
  89. package/templates/monorepo/apps/web/public/vercel.svg +10 -0
  90. package/templates/monorepo/apps/web/public/window.svg +3 -0
  91. package/templates/monorepo/apps/web/tsconfig.json +20 -0
  92. package/templates/monorepo/package.json +24 -0
  93. package/templates/monorepo/packages/core/eslint.config.mjs +4 -0
  94. package/templates/monorepo/packages/core/package.json +32 -0
  95. package/templates/monorepo/packages/core/tsconfig.json +8 -0
  96. package/templates/monorepo/packages/core-next/eslint.config.mjs +13 -0
  97. package/templates/monorepo/packages/core-next/package.json +43 -0
  98. package/templates/monorepo/packages/core-next/tsconfig.json +8 -0
  99. package/templates/monorepo/packages/core-react/eslint.config.mjs +4 -0
  100. package/templates/monorepo/packages/core-react/package.json +34 -0
  101. package/templates/monorepo/packages/core-react/tsconfig.json +8 -0
  102. package/templates/monorepo/packages/eslint-config/README.md +3 -0
  103. package/templates/monorepo/packages/eslint-config/base.js +57 -0
  104. package/templates/monorepo/packages/eslint-config/next.js +22 -0
  105. package/templates/monorepo/packages/eslint-config/package.json +25 -0
  106. package/templates/monorepo/packages/eslint-config/react-internal.js +33 -0
  107. package/templates/monorepo/packages/typescript-config/base.json +19 -0
  108. package/templates/monorepo/packages/typescript-config/nextjs.json +12 -0
  109. package/templates/monorepo/packages/typescript-config/package.json +9 -0
  110. package/templates/monorepo/packages/typescript-config/react-library.json +7 -0
  111. package/templates/monorepo/packages/ui/eslint.config.mjs +4 -0
  112. package/templates/monorepo/packages/ui/package.json +26 -0
  113. package/templates/monorepo/packages/ui/src/button.tsx +20 -0
  114. package/templates/monorepo/packages/ui/src/card.tsx +27 -0
  115. package/templates/monorepo/packages/ui/src/code.tsx +11 -0
  116. package/templates/monorepo/packages/ui/tsconfig.json +8 -0
  117. package/templates/monorepo/pnpm-workspace.yaml +9 -0
  118. package/templates/monorepo/turbo/generators/config.js +1336 -0
  119. package/templates/monorepo/turbo/generators/templates/next-app/Dockerfile.tpl +30 -0
  120. package/templates/monorepo/turbo/generators/templates/next-app/README.md.tpl +118 -0
  121. package/templates/monorepo/turbo/generators/templates/next-app/app/global-error.tsx.tpl +12 -0
  122. package/templates/monorepo/turbo/generators/templates/next-app/app/globals.css.tpl +1 -0
  123. package/templates/monorepo/turbo/generators/templates/next-app/app/layout.tsx.tpl +29 -0
  124. package/templates/monorepo/turbo/generators/templates/next-app/app/page.tsx.tpl +7 -0
  125. package/templates/monorepo/turbo/generators/templates/next-app/app/providers.tsx.tpl +25 -0
  126. package/templates/monorepo/turbo/generators/templates/next-app/eslint.config.js.tpl +4 -0
  127. package/templates/monorepo/turbo/generators/templates/next-app/next.config.js.tpl +6 -0
  128. package/templates/monorepo/turbo/generators/templates/next-app/tsconfig.json.tpl +18 -0
  129. package/templates/monorepo/turbo/generators/templates/vite-app/Dockerfile.tpl +22 -0
  130. package/templates/monorepo/turbo/generators/templates/vite-app/README.plain.md.tpl +90 -0
  131. package/templates/monorepo/turbo/generators/templates/vite-app/README.react.md.tpl +107 -0
  132. package/templates/monorepo/turbo/generators/templates/vite-app/eslint.config.mjs.tpl +4 -0
  133. package/templates/monorepo/turbo/generators/templates/vite-app/index.html.tpl +12 -0
  134. package/templates/monorepo/turbo/generators/templates/vite-app/index.ts.tpl +22 -0
  135. package/templates/monorepo/turbo/generators/templates/vite-app/tsconfig.json.tpl +9 -0
  136. package/templates/monorepo/turbo/generators/templates/vite-app/vite.config.ts.tpl +6 -0
  137. package/templates/monorepo/turbo.json +28 -0
  138. package/templates/next/.env.example +2 -0
  139. package/templates/next/.prettierignore +9 -0
  140. package/templates/next/.prettierrc.json +9 -0
  141. package/templates/next/README.md +246 -0
  142. package/templates/next/_gitignore +44 -0
  143. package/templates/next/eslint.config.mjs +51 -0
  144. package/templates/next/next.config.ts +7 -0
  145. package/templates/next/package.json +24 -0
  146. package/templates/next/postcss.config.mjs +7 -0
  147. package/templates/next/scripts/create-feature-crud.mjs +5 -0
  148. package/templates/next/scripts/create-feature.mjs +5 -0
  149. package/templates/next/src/app/error.tsx +33 -0
  150. package/templates/next/src/app/globals.css +35 -0
  151. package/templates/next/src/app/layout.tsx +39 -0
  152. package/templates/next/src/app/login/page.tsx +17 -0
  153. package/templates/next/src/app/page.tsx +32 -0
  154. package/templates/next/src/app/providers.tsx +20 -0
  155. package/templates/next/tsconfig.json +34 -0
  156. package/templates/react/.env.example +1 -0
  157. package/templates/react/.prettierignore +10 -0
  158. package/templates/react/.prettierrc.json +9 -0
  159. package/templates/react/README.md +250 -0
  160. package/templates/react/_gitignore +31 -0
  161. package/templates/react/eslint.config.mjs +64 -0
  162. package/templates/react/package.json +19 -0
  163. package/templates/react/scripts/create-feature-crud.mjs +5 -0
  164. package/templates/react/scripts/create-feature.mjs +5 -0
  165. package/templates/react/src/app/app.tsx +15 -0
  166. package/templates/react/src/app/error-boundary.tsx +59 -0
  167. package/templates/react/src/app/frame.tsx +32 -0
  168. package/templates/react/src/app/globals.css +43 -0
  169. package/templates/react/src/app/not-found-page.tsx +23 -0
  170. package/templates/react/src/app/providers.tsx +16 -0
  171. package/templates/react/src/app/router.tsx +62 -0
  172. package/templates/react/src/main.tsx +12 -0
  173. package/templates/react/src/pages/index/page.tsx +36 -0
  174. package/templates/react/src/pages/login/page.tsx +18 -0
  175. package/templates/react/tsconfig.app.json +30 -0
  176. package/templates/react/tsconfig.json +4 -0
  177. package/templates/react/tsconfig.node.json +24 -0
  178. package/templates/react/vite.config.ts +14 -0
  179. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/00-/354/240/204/353/260/230/354/240/201/354/235/270-/355/217/264/353/215/224/352/265/254/354/241/260.md +150 -0
  180. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/01-/352/265/254/354/241/260/354/231/200-/353/235/274/354/232/260/355/214/205.md +186 -0
  181. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/02-/354/204/234/353/262/204/354/231/200-/355/201/264/353/235/274/354/235/264/354/226/270/355/212/270.md +86 -0
  182. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/03-/354/203/201/355/203/234/352/264/200/353/246/254.md +84 -0
  183. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/04-API/354/231/200-/353/215/260/354/235/264/355/204/260.md +199 -0
  184. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/05-/354/227/220/353/237/254/354/231/200-UI-/354/203/201/355/203/234.md +159 -0
  185. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/06-/355/217/274.md +116 -0
  186. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/07-/354/212/244/355/203/200/354/235/274/353/247/201/352/263/274-/354/240/221/352/267/274/354/204/261.md +73 -0
  187. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/08-/353/204/244/354/235/264/353/260/215-/354/204/244/354/240/225-/355/217/254/353/247/267/355/214/205.md +98 -0
  188. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/09-/353/235/274/354/232/260/355/212/270-/354/240/225/354/235/230.md +169 -0
  189. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/10-/354/273/244/353/260/213-/354/273/250/353/262/244/354/205/230.md +64 -0
  190. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/11-/352/270/260/355/203/200-/354/233/220/354/271/231.md +187 -0
  191. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/12-/354/213/244/353/254/264-/353/215/260/354/235/264/355/204/260-/355/214/250/355/204/264.md +302 -0
  192. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/13-/354/204/261/353/212/245-/354/233/220/354/271/231.md +175 -0
  193. package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/README.md +39 -0
@@ -0,0 +1,30 @@
1
+ FROM node:lts-alpine AS base
2
+ RUN npm install -g pnpm turbo
3
+ WORKDIR /app
4
+
5
+ FROM base AS pruner
6
+ COPY . .
7
+ RUN turbo prune __APPNAME__ --docker
8
+
9
+ FROM base AS builder
10
+ WORKDIR /app
11
+ COPY --from=pruner /app/out/json/ .
12
+ COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
13
+ RUN pnpm install --frozen-lockfile
14
+ COPY --from=pruner /app/out/full/ .
15
+ RUN pnpm turbo build --filter=__APPNAME__
16
+
17
+ FROM node:lts-alpine AS runner
18
+ WORKDIR /app
19
+
20
+ ENV NODE_ENV=production
21
+ ENV PORT=3000
22
+ ENV HOSTNAME=0.0.0.0
23
+
24
+ COPY --from=builder /app/apps/__APPNAME__/public ./apps/__APPNAME__/public
25
+ COPY --from=builder /app/apps/__APPNAME__/.next/standalone ./
26
+ COPY --from=builder /app/apps/__APPNAME__/.next/static ./apps/__APPNAME__/.next/static
27
+
28
+ EXPOSE 3000
29
+
30
+ CMD ["node", "apps/__APPNAME__/server.js"]
@@ -0,0 +1,118 @@
1
+ # [[APPNAME]]
2
+
3
+ `[[APP_PATH]]`는 monorepo generator로 만든 Next.js 앱입니다. `create-next-app` 기본 scaffold 위에 공용 설정과 core wiring이 이미 연결되어 있습니다.
4
+
5
+ ## 빠른 시작
6
+
7
+ 레포 루트에서 실행하세요.
8
+
9
+ ```bash
10
+ pnpm install
11
+ pnpm --filter "./apps/[[APPNAME]]" dev
12
+ ```
13
+
14
+ 자주 쓰는 명령:
15
+
16
+ ```bash
17
+ pnpm --filter "./apps/[[APPNAME]]" lint
18
+ pnpm --filter "./apps/[[APPNAME]]" check-types
19
+ pnpm --filter "./apps/[[APPNAME]]" build
20
+ ```
21
+
22
+ ## 환경 변수
23
+
24
+ generator가 `[[ENV_FILE_PATH]]`를 같이 만들었습니다.
25
+
26
+ ```env
27
+ [[ENV_SNIPPET]]
28
+ ```
29
+
30
+ - `[[SITE_ID_ENV_KEY]]`: 현재 값은 `[[SITE_ID]]`
31
+ - `[[BASE_URL_ENV_KEY]]`: 현재 값은 `[[BASE_URL]]`
32
+
33
+ ## 기본 연결
34
+
35
+ - `[[LAYOUT_PATH]]`: `CoreBootstrap`와 `Providers`를 연결합니다.
36
+ - `[[PROVIDERS_PATH]]`: `AppProviders`와 전역 query 에러 핸들러를 연결합니다.
37
+ - `[[PAGE_PATH]]`: 첫 페이지 예시입니다.
38
+ - `[[GLOBAL_ERROR_PATH]]`: 전역 에러 UI입니다.
39
+
40
+ 지금 layout은 이미 `process.env.[[SITE_ID_ENV_KEY]]`와 `process.env.[[BASE_URL_ENV_KEY]]`를 읽도록 연결되어 있습니다. 보통은 `.env.local` 값만 바꾸시면 됩니다.
41
+
42
+ ## API 사용 규칙
43
+
44
+ API 함수는 항상 `@repo/core/api/*`에서 직접 import합니다.
45
+
46
+ ```tsx
47
+ import { testFetch } from "@repo/core/api/test";
48
+
49
+ export default async function Page() {
50
+ const data = await testFetch();
51
+
52
+ return <pre>{JSON.stringify(data, null, 2)}</pre>;
53
+ }
54
+ ```
55
+
56
+ 다음 패턴은 피해주세요.
57
+
58
+ - `@repo/core` 루트에 API를 다시 모으기
59
+ - `@repo/core-next`에서 API를 가져오기
60
+
61
+ ## 새 API 추가하기
62
+
63
+ 새 API는 `packages/core/src/api/*.ts`에 파일 단위로 추가합니다.
64
+
65
+ ```ts
66
+ import { z } from "zod";
67
+
68
+ import { coreFetch, type CoreFetchOptions } from "../api";
69
+ import { parseWithSchema } from "../schema-utils";
70
+
71
+ const articleSchema = z.object({
72
+ id: z.number(),
73
+ title: z.string(),
74
+ });
75
+
76
+ const articleListSchema = z.array(articleSchema);
77
+
78
+ export async function listArticles(options?: CoreFetchOptions) {
79
+ const payload = await coreFetch<unknown>("/articles", {
80
+ ...options,
81
+ baseUrl: "https://api.example.com",
82
+ });
83
+
84
+ return parseWithSchema(articleListSchema, payload);
85
+ }
86
+ ```
87
+
88
+ 앱에서는 바로 이렇게 가져다 쓰시면 됩니다.
89
+
90
+ ```ts
91
+ import { listArticles } from "@repo/core/api/article";
92
+ ```
93
+
94
+ ## 인증 포함 호출
95
+
96
+ 서버 컴포넌트나 route handler에서는 `withServerAuth()`를 붙이면 됩니다.
97
+
98
+ ```tsx
99
+ import { testFetch } from "@repo/core/api/test";
100
+ import { withServerAuth } from "@repo/core-next/server-auth";
101
+
102
+ const authTestFetch = withServerAuth(testFetch);
103
+ const data = await authTestFetch();
104
+ ```
105
+
106
+ 클라이언트 호출에서 쿠키를 같이 보내려면 `withClientAuth()`를 사용하세요.
107
+
108
+ ```tsx
109
+ import { testFetch } from "@repo/core/api/test";
110
+ import { withClientAuth } from "@repo/core-next/client-auth";
111
+
112
+ const authTestFetch = withClientAuth(testFetch);
113
+ const data = await authTestFetch();
114
+ ```
115
+
116
+ ## 더 보기
117
+
118
+ 공통 규칙과 패키지 구조는 [루트 README]([[ROOT_README_PATH]])를 참고해주세요.
@@ -0,0 +1,12 @@
1
+ "use client";
2
+
3
+ export default function GlobalError({ error }: { error: Error }) {
4
+ return (
5
+ <html>
6
+ <body>
7
+ <h2>Error</h2>
8
+ <pre>{error.message}</pre>
9
+ </body>
10
+ </html>
11
+ );
12
+ }
@@ -0,0 +1,29 @@
1
+ import "./globals.css";
2
+
3
+ import { CoreBootstrap } from "@repo/core-next/bootstrap";
4
+ import type { Metadata } from "next";
5
+
6
+ import { Providers } from "./providers";
7
+
8
+ export const metadata: Metadata = {
9
+ title: "__APPNAME__",
10
+ description: "__APPNAME__ application",
11
+ };
12
+
13
+ const SITE_ID = process.env.NEXT_PUBLIC_SITE_ID ?? "a";
14
+ const BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || undefined;
15
+
16
+ export default function RootLayout({
17
+ children,
18
+ }: Readonly<{
19
+ children: React.ReactNode;
20
+ }>) {
21
+ return (
22
+ <html lang="ko">
23
+ <body>
24
+ <CoreBootstrap siteId={SITE_ID} baseUrl={BASE_URL} />
25
+ <Providers>{children}</Providers>
26
+ </body>
27
+ </html>
28
+ );
29
+ }
@@ -0,0 +1,7 @@
1
+ import { testFetch } from "@repo/core/api/test";
2
+
3
+ export default async function Home() {
4
+ const data = await testFetch();
5
+
6
+ return <div>{JSON.stringify(data)}</div>;
7
+ }
@@ -0,0 +1,25 @@
1
+ "use client";
2
+
3
+ import {
4
+ AppProviders,
5
+ createClientQueryErrorHandler,
6
+ } from "@repo/core-next";
7
+ import type { ReactNode } from "react";
8
+
9
+ const handleClientQueryError = createClientQueryErrorHandler({
10
+ onUnauthorized: () => {
11
+ window.location.href = "/login";
12
+ },
13
+ });
14
+
15
+ type ProvidersProps = {
16
+ children: ReactNode;
17
+ };
18
+
19
+ export function Providers({ children }: ProvidersProps) {
20
+ return (
21
+ <AppProviders onGlobalError={handleClientQueryError}>
22
+ {children}
23
+ </AppProviders>
24
+ );
25
+ }
@@ -0,0 +1,4 @@
1
+ import { nextJsConfig } from "@repo/eslint-config/next-js";
2
+
3
+ /** @type {import("eslint").Linter.Config[]} */
4
+ export default nextJsConfig;
@@ -0,0 +1,6 @@
1
+ /** @type {import("next").NextConfig} */
2
+ const nextConfig = {
3
+ output: "standalone",
4
+ };
5
+
6
+ export default nextConfig;
@@ -0,0 +1,18 @@
1
+ {
2
+ "extends": "@repo/typescript-config/nextjs.json",
3
+ "compilerOptions": {
4
+ "plugins": [
5
+ {
6
+ "name": "next"
7
+ }
8
+ ]
9
+ },
10
+ "include": [
11
+ "**/*.ts",
12
+ "**/*.tsx",
13
+ "next-env.d.ts",
14
+ "next.config.js",
15
+ ".next/types/**/*.ts"
16
+ ],
17
+ "exclude": ["node_modules"]
18
+ }
@@ -0,0 +1,22 @@
1
+ FROM node:lts-alpine AS base
2
+ RUN npm install -g pnpm turbo
3
+ WORKDIR /app
4
+
5
+ FROM base AS pruner
6
+ COPY . .
7
+ RUN turbo prune __APPNAME__ --out-dir=out --docker
8
+
9
+ FROM base AS builder
10
+ WORKDIR /app
11
+ COPY --from=pruner /app/out/json/ .
12
+ COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
13
+ RUN pnpm install --frozen-lockfile
14
+ COPY --from=pruner /app/out/full/ .
15
+ RUN pnpm turbo build --filter=__APPNAME__
16
+
17
+ FROM nginx:stable-alpine AS runner
18
+ COPY --from=builder /app/apps/__APPNAME__/dist /usr/share/nginx/html
19
+
20
+ EXPOSE 80
21
+
22
+ CMD ["nginx", "-g", "daemon off;"]
@@ -0,0 +1,90 @@
1
+ # [[APPNAME]]
2
+
3
+ `[[APP_PATH]]`는 monorepo generator로 만든 Vite 앱입니다. 공용 설정과 `initCore()` 기본 wiring이 이미 연결되어 있습니다.
4
+
5
+ ## 빠른 시작
6
+
7
+ 레포 루트에서 실행하세요.
8
+
9
+ ```bash
10
+ pnpm install
11
+ pnpm --filter "./apps/[[APPNAME]]" dev
12
+ ```
13
+
14
+ 자주 쓰는 명령:
15
+
16
+ ```bash
17
+ pnpm --filter "./apps/[[APPNAME]]" lint
18
+ pnpm --filter "./apps/[[APPNAME]]" check-types
19
+ pnpm --filter "./apps/[[APPNAME]]" build
20
+ ```
21
+
22
+ ## 환경 변수
23
+
24
+ generator가 `[[ENV_FILE_PATH]]`를 같이 만들었습니다.
25
+
26
+ ```env
27
+ [[ENV_SNIPPET]]
28
+ ```
29
+
30
+ - `[[SITE_ID_ENV_KEY]]`: 현재 값은 `[[SITE_ID]]`
31
+ - `[[BASE_URL_ENV_KEY]]`: 현재 값은 `[[BASE_URL]]`
32
+
33
+ ## 기본 연결
34
+
35
+ - `[[ENTRY_PATH]]`: env 값을 읽어 앱 시작 시 `initCore()`를 호출합니다.
36
+
37
+ 지금 엔트리 파일은 이미 `import.meta.env.[[SITE_ID_ENV_KEY]]`와 `import.meta.env.[[BASE_URL_ENV_KEY]]`를 읽어서 `initCore()`를 호출하도록 연결되어 있습니다.
38
+
39
+ ## API 사용 규칙
40
+
41
+ API 함수는 항상 `@repo/core/api/*`에서 직접 import합니다.
42
+
43
+ ```ts
44
+ import { testFetch } from "@repo/core/api/test";
45
+
46
+ async function bootstrap() {
47
+ const data = await testFetch();
48
+
49
+ document.body.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
50
+ }
51
+
52
+ void bootstrap();
53
+ ```
54
+
55
+ ## 새 API 추가하기
56
+
57
+ 새 API는 `packages/core/src/api/*.ts`에 파일 단위로 추가합니다.
58
+
59
+ ```ts
60
+ import { z } from "zod";
61
+
62
+ import { coreFetch, type CoreFetchOptions } from "../api";
63
+ import { parseWithSchema } from "../schema-utils";
64
+
65
+ const articleSchema = z.object({
66
+ id: z.number(),
67
+ title: z.string(),
68
+ });
69
+
70
+ const articleListSchema = z.array(articleSchema);
71
+
72
+ export async function listArticles(options?: CoreFetchOptions) {
73
+ const payload = await coreFetch<unknown>("/articles", {
74
+ ...options,
75
+ baseUrl: "https://api.example.com",
76
+ });
77
+
78
+ return parseWithSchema(articleListSchema, payload);
79
+ }
80
+ ```
81
+
82
+ 앱에서는 바로 이렇게 가져다 쓰시면 됩니다.
83
+
84
+ ```ts
85
+ import { listArticles } from "@repo/core/api/article";
86
+ ```
87
+
88
+ ## 더 보기
89
+
90
+ 공통 규칙과 패키지 구조는 [루트 README]([[ROOT_README_PATH]])를 참고해주세요.
@@ -0,0 +1,107 @@
1
+ # [[APPNAME]]
2
+
3
+ `[[APP_PATH]]`는 monorepo generator로 만든 React + Vite 앱입니다. 공용 설정과 provider wiring이 이미 연결되어 있습니다.
4
+
5
+ ## 빠른 시작
6
+
7
+ 레포 루트에서 실행하세요.
8
+
9
+ ```bash
10
+ pnpm install
11
+ pnpm --filter "./apps/[[APPNAME]]" dev
12
+ ```
13
+
14
+ 자주 쓰는 명령:
15
+
16
+ ```bash
17
+ pnpm --filter "./apps/[[APPNAME]]" lint
18
+ pnpm --filter "./apps/[[APPNAME]]" check-types
19
+ pnpm --filter "./apps/[[APPNAME]]" build
20
+ ```
21
+
22
+ ## 환경 변수
23
+
24
+ generator가 `[[ENV_FILE_PATH]]`를 같이 만들었습니다.
25
+
26
+ ```env
27
+ [[ENV_SNIPPET]]
28
+ ```
29
+
30
+ - `[[SITE_ID_ENV_KEY]]`: 현재 값은 `[[SITE_ID]]`
31
+ - `[[BASE_URL_ENV_KEY]]`: 현재 값은 `[[BASE_URL]]`
32
+
33
+ ## 기본 연결
34
+
35
+ - `[[ENTRY_PATH]]`: env 값을 읽어 `initCore()`와 `<Providers><App /></Providers>`를 연결합니다.
36
+ - `[[PROVIDERS_PATH]]`: `AppProviders`와 전역 query 에러 핸들러를 연결합니다.
37
+
38
+ 지금 엔트리 파일은 이미 `import.meta.env.[[SITE_ID_ENV_KEY]]`와 `import.meta.env.[[BASE_URL_ENV_KEY]]`를 읽어서 `initCore()`를 호출하도록 연결되어 있습니다.
39
+
40
+ ## API 사용 규칙
41
+
42
+ API 함수는 항상 `@repo/core/api/*`에서 직접 import합니다.
43
+
44
+ ```tsx
45
+ import { useEffect, useState } from "react";
46
+
47
+ import { testFetch } from "@repo/core/api/test";
48
+
49
+ export function Example() {
50
+ const [items, setItems] = useState<unknown[]>([]);
51
+
52
+ useEffect(() => {
53
+ testFetch().then(setItems);
54
+ }, []);
55
+
56
+ return <pre>{JSON.stringify(items, null, 2)}</pre>;
57
+ }
58
+ ```
59
+
60
+ ## 새 API 추가하기
61
+
62
+ 새 API는 `packages/core/src/api/*.ts`에 파일 단위로 추가합니다.
63
+
64
+ ```ts
65
+ import { z } from "zod";
66
+
67
+ import { coreFetch, type CoreFetchOptions } from "../api";
68
+ import { parseWithSchema } from "../schema-utils";
69
+
70
+ const articleSchema = z.object({
71
+ id: z.number(),
72
+ title: z.string(),
73
+ });
74
+
75
+ const articleListSchema = z.array(articleSchema);
76
+
77
+ export async function listArticles(options?: CoreFetchOptions) {
78
+ const payload = await coreFetch<unknown>("/articles", {
79
+ ...options,
80
+ baseUrl: "https://api.example.com",
81
+ });
82
+
83
+ return parseWithSchema(articleListSchema, payload);
84
+ }
85
+ ```
86
+
87
+ 앱에서는 바로 이렇게 가져다 쓰시면 됩니다.
88
+
89
+ ```ts
90
+ import { listArticles } from "@repo/core/api/article";
91
+ ```
92
+
93
+ ## 인증 포함 호출
94
+
95
+ 브라우저에서 쿠키를 같이 보내려면 `withClientAuth()`를 사용하세요.
96
+
97
+ ```ts
98
+ import { testFetch } from "@repo/core/api/test";
99
+ import { withClientAuth } from "@repo/core-react";
100
+
101
+ const authTestFetch = withClientAuth(testFetch);
102
+ const data = await authTestFetch();
103
+ ```
104
+
105
+ ## 더 보기
106
+
107
+ 공통 규칙과 패키지 구조는 [루트 README]([[ROOT_README_PATH]])를 참고해주세요.
@@ -0,0 +1,4 @@
1
+ import { config } from "@repo/eslint-config/base";
2
+
3
+ /** @type {import("eslint").Linter.Config} */
4
+ export default config;
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>__APPNAME__</title>
7
+ </head>
8
+ <body>
9
+ <main id="app"></main>
10
+ <script type="module" src="/index.ts"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,22 @@
1
+ import { initCore, normalizeAppError } from "@repo/core";
2
+ import { testFetch } from "@repo/core/api/test";
3
+
4
+ initCore("a");
5
+
6
+ const app = document.querySelector("#app");
7
+
8
+ (async () => {
9
+ try {
10
+ const data = await testFetch();
11
+
12
+ if (app) {
13
+ app.textContent = JSON.stringify(data);
14
+ }
15
+ } catch (error) {
16
+ const appError = normalizeAppError(error);
17
+
18
+ if (app) {
19
+ app.textContent = appError.message;
20
+ }
21
+ }
22
+ })();
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "@repo/typescript-config/base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "moduleResolution": "nodenext"
6
+ },
7
+ "include": ["src", "./index.ts"],
8
+ "exclude": ["node_modules", "dist"]
9
+ }
@@ -0,0 +1,6 @@
1
+ import { defineConfig } from "vite";
2
+ import tsConfigPaths from "vite-tsconfig-paths";
3
+
4
+ export default defineConfig({
5
+ plugins: [tsConfigPaths()],
6
+ });
@@ -0,0 +1,28 @@
1
+ {
2
+ "$schema": "https://turborepo.dev/schema.json",
3
+ "globalEnv": [
4
+ "NODE_ENV",
5
+ "NEXT_PUBLIC_SITE_ID",
6
+ "NEXT_PUBLIC_API_BASE_URL",
7
+ "VITE_SITE_ID",
8
+ "VITE_API_BASE_URL"
9
+ ],
10
+ "ui": "tui",
11
+ "tasks": {
12
+ "build": {
13
+ "dependsOn": ["^build"],
14
+ "inputs": ["$TURBO_DEFAULT$", ".env*"],
15
+ "outputs": [".next/**", "!.next/cache/**", "dist/**"]
16
+ },
17
+ "lint": {
18
+ "dependsOn": ["^lint"]
19
+ },
20
+ "check-types": {
21
+ "dependsOn": ["^check-types"]
22
+ },
23
+ "dev": {
24
+ "cache": false,
25
+ "persistent": true
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,2 @@
1
+ API_BASE_URL=http://localhost:4000
2
+ NEXT_PUBLIC_API_BASE_URL=http://localhost:4000
@@ -0,0 +1,9 @@
1
+ .next
2
+ out
3
+ build
4
+ coverage
5
+ node_modules
6
+ package-lock.json
7
+ bun.lock
8
+ pnpm-lock.yaml
9
+ yarn.lock
@@ -0,0 +1,9 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true,
4
+ "trailingComma": "all",
5
+ "printWidth": 100,
6
+ "tabWidth": 2,
7
+ "useTabs": false,
8
+ "arrowParens": "always"
9
+ }