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.
- package/README.md +68 -0
- package/internal/engine/create-feature-crud-script.ts +134 -0
- package/internal/engine/create-feature-crud.template.mjs +601 -0
- package/internal/engine/create-feature-script.ts +142 -0
- package/internal/engine/generator-engine.ts +546 -0
- package/internal/engine/standalone-feature-preset.ts +34 -0
- package/internal/meta/preset-plan.ts +41 -0
- package/internal/meta/runtime-copy-plan.ts +220 -0
- package/internal/meta/runtime-layout.ts +262 -0
- package/internal/meta/scaffold-manifest.ts +169 -0
- package/internal/meta/standalone-dependency-manifest.ts +75 -0
- package/package.json +45 -0
- package/scripts/create-app.mjs +1612 -0
- package/source/core/auth/auth-events.ts +13 -0
- package/source/core/error/app-error.ts +85 -0
- package/source/core/error/handle-app-error.client.ts +35 -0
- package/source/core/lib/dayjs.ts +25 -0
- package/source/core/query/query-client.ts +126 -0
- package/source/core/request/request-core.ts +210 -0
- package/source/core/routes/route-paths.ts +4 -0
- package/source/core/ui/button.tsx +24 -0
- package/source/core/ui/modal-store.ts +32 -0
- package/source/core/ui/text-input-field.tsx +36 -0
- package/source/core/utils/build-query-string.ts +30 -0
- package/source/core/utils/format/date.ts +41 -0
- package/source/core/utils/format/index.ts +3 -0
- package/source/core/utils/format/number.ts +13 -0
- package/source/core/utils/format/text.ts +15 -0
- package/source/core/utils/schema-utils.ts +27 -0
- package/source/wrappers/monorepo/core/internal.ts +21 -0
- package/source/wrappers/monorepo/core/src/index.ts +4 -0
- package/source/wrappers/monorepo/core-next/src/auth.client.ts +1 -0
- package/source/wrappers/monorepo/core-next/src/auth.server.ts +94 -0
- package/source/wrappers/monorepo/core-next/src/bootstrap.client.tsx +21 -0
- package/source/wrappers/monorepo/core-next/src/bootstrap.tsx +18 -0
- package/source/wrappers/monorepo/core-next/src/index.ts +1 -0
- package/source/wrappers/monorepo/core-react/src/app-providers.tsx +36 -0
- package/source/wrappers/monorepo/core-react/src/auth.ts +42 -0
- package/source/wrappers/monorepo/core-react/src/hydration.tsx +21 -0
- package/source/wrappers/monorepo/core-react/src/index.ts +7 -0
- package/source/wrappers/monorepo/core-react/src/provider.tsx +49 -0
- package/source/wrappers/monorepo/core-react/src/query-client.ts +48 -0
- package/source/wrappers/monorepo/core-react/src/query-error-handler.ts +62 -0
- package/source/wrappers/monorepo/core-react/src/query-keys.ts +22 -0
- package/source/wrappers/monorepo/request/core-fetch.ts +27 -0
- package/source/wrappers/monorepo/request/core-request.ts +93 -0
- package/source/wrappers/next/auth/auth-error-listener.tsx +34 -0
- package/source/wrappers/next/error/handle-app-error.server.ts +41 -0
- package/source/wrappers/next/query/hydration.tsx +20 -0
- package/source/wrappers/next/query/providers.tsx +35 -0
- package/source/wrappers/next/request/request.client.ts +24 -0
- package/source/wrappers/next/request/request.server.ts +64 -0
- package/source/wrappers/next/request/request.ts +52 -0
- package/source/wrappers/next/ui/global-modal.tsx +29 -0
- package/source/wrappers/react/auth/auth-error-listener.tsx +34 -0
- package/source/wrappers/react/query/providers.tsx +31 -0
- package/source/wrappers/react/request/request.client.ts +24 -0
- package/source/wrappers/react/request/request.ts +51 -0
- package/source/wrappers/react/ui/global-modal.tsx +27 -0
- package/templates/monorepo/.dockerignore +38 -0
- package/templates/monorepo/README.md +292 -0
- package/templates/monorepo/_gitignore +38 -0
- package/templates/monorepo/_npmrc +1 -0
- package/templates/monorepo/apps/project/Dockerfile +32 -0
- package/templates/monorepo/apps/project/eslint.config.mjs +4 -0
- package/templates/monorepo/apps/project/index.html +14 -0
- package/templates/monorepo/apps/project/index.ts +15 -0
- package/templates/monorepo/apps/project/package.json +21 -0
- package/templates/monorepo/apps/project/tsconfig.json +9 -0
- package/templates/monorepo/apps/project/vite.config.ts +6 -0
- package/templates/monorepo/apps/web/Dockerfile +43 -0
- package/templates/monorepo/apps/web/README.md +111 -0
- package/templates/monorepo/apps/web/_gitignore +36 -0
- package/templates/monorepo/apps/web/app/favicon.ico +0 -0
- package/templates/monorepo/apps/web/app/global-error.tsx +12 -0
- package/templates/monorepo/apps/web/app/globals.css +0 -0
- package/templates/monorepo/apps/web/app/layout.tsx +28 -0
- package/templates/monorepo/apps/web/app/page.tsx +7 -0
- package/templates/monorepo/apps/web/app/providers.tsx +25 -0
- package/templates/monorepo/apps/web/eslint.config.js +4 -0
- package/templates/monorepo/apps/web/next-env.d.ts +6 -0
- package/templates/monorepo/apps/web/next.config.js +4 -0
- package/templates/monorepo/apps/web/package.json +31 -0
- package/templates/monorepo/apps/web/public/file-text.svg +3 -0
- package/templates/monorepo/apps/web/public/globe.svg +10 -0
- package/templates/monorepo/apps/web/public/next.svg +1 -0
- package/templates/monorepo/apps/web/public/turborepo-dark.svg +19 -0
- package/templates/monorepo/apps/web/public/turborepo-light.svg +19 -0
- package/templates/monorepo/apps/web/public/vercel.svg +10 -0
- package/templates/monorepo/apps/web/public/window.svg +3 -0
- package/templates/monorepo/apps/web/tsconfig.json +20 -0
- package/templates/monorepo/package.json +24 -0
- package/templates/monorepo/packages/core/eslint.config.mjs +4 -0
- package/templates/monorepo/packages/core/package.json +32 -0
- package/templates/monorepo/packages/core/tsconfig.json +8 -0
- package/templates/monorepo/packages/core-next/eslint.config.mjs +13 -0
- package/templates/monorepo/packages/core-next/package.json +43 -0
- package/templates/monorepo/packages/core-next/tsconfig.json +8 -0
- package/templates/monorepo/packages/core-react/eslint.config.mjs +4 -0
- package/templates/monorepo/packages/core-react/package.json +34 -0
- package/templates/monorepo/packages/core-react/tsconfig.json +8 -0
- package/templates/monorepo/packages/eslint-config/README.md +3 -0
- package/templates/monorepo/packages/eslint-config/base.js +57 -0
- package/templates/monorepo/packages/eslint-config/next.js +22 -0
- package/templates/monorepo/packages/eslint-config/package.json +25 -0
- package/templates/monorepo/packages/eslint-config/react-internal.js +33 -0
- package/templates/monorepo/packages/typescript-config/base.json +19 -0
- package/templates/monorepo/packages/typescript-config/nextjs.json +12 -0
- package/templates/monorepo/packages/typescript-config/package.json +9 -0
- package/templates/monorepo/packages/typescript-config/react-library.json +7 -0
- package/templates/monorepo/packages/ui/eslint.config.mjs +4 -0
- package/templates/monorepo/packages/ui/package.json +26 -0
- package/templates/monorepo/packages/ui/src/button.tsx +20 -0
- package/templates/monorepo/packages/ui/src/card.tsx +27 -0
- package/templates/monorepo/packages/ui/src/code.tsx +11 -0
- package/templates/monorepo/packages/ui/tsconfig.json +8 -0
- package/templates/monorepo/pnpm-workspace.yaml +9 -0
- package/templates/monorepo/turbo/generators/config.js +1336 -0
- package/templates/monorepo/turbo/generators/templates/next-app/Dockerfile.tpl +30 -0
- package/templates/monorepo/turbo/generators/templates/next-app/README.md.tpl +118 -0
- package/templates/monorepo/turbo/generators/templates/next-app/app/global-error.tsx.tpl +12 -0
- package/templates/monorepo/turbo/generators/templates/next-app/app/globals.css.tpl +1 -0
- package/templates/monorepo/turbo/generators/templates/next-app/app/layout.tsx.tpl +29 -0
- package/templates/monorepo/turbo/generators/templates/next-app/app/page.tsx.tpl +7 -0
- package/templates/monorepo/turbo/generators/templates/next-app/app/providers.tsx.tpl +25 -0
- package/templates/monorepo/turbo/generators/templates/next-app/eslint.config.js.tpl +4 -0
- package/templates/monorepo/turbo/generators/templates/next-app/next.config.js.tpl +6 -0
- package/templates/monorepo/turbo/generators/templates/next-app/tsconfig.json.tpl +18 -0
- package/templates/monorepo/turbo/generators/templates/vite-app/Dockerfile.tpl +22 -0
- package/templates/monorepo/turbo/generators/templates/vite-app/README.plain.md.tpl +90 -0
- package/templates/monorepo/turbo/generators/templates/vite-app/README.react.md.tpl +107 -0
- package/templates/monorepo/turbo/generators/templates/vite-app/eslint.config.mjs.tpl +4 -0
- package/templates/monorepo/turbo/generators/templates/vite-app/index.html.tpl +12 -0
- package/templates/monorepo/turbo/generators/templates/vite-app/index.ts.tpl +22 -0
- package/templates/monorepo/turbo/generators/templates/vite-app/tsconfig.json.tpl +9 -0
- package/templates/monorepo/turbo/generators/templates/vite-app/vite.config.ts.tpl +6 -0
- package/templates/monorepo/turbo.json +28 -0
- package/templates/next/.env.example +2 -0
- package/templates/next/.prettierignore +9 -0
- package/templates/next/.prettierrc.json +9 -0
- package/templates/next/README.md +246 -0
- package/templates/next/_gitignore +44 -0
- package/templates/next/eslint.config.mjs +51 -0
- package/templates/next/next.config.ts +7 -0
- package/templates/next/package.json +24 -0
- package/templates/next/postcss.config.mjs +7 -0
- package/templates/next/scripts/create-feature-crud.mjs +5 -0
- package/templates/next/scripts/create-feature.mjs +5 -0
- package/templates/next/src/app/error.tsx +33 -0
- package/templates/next/src/app/globals.css +35 -0
- package/templates/next/src/app/layout.tsx +39 -0
- package/templates/next/src/app/login/page.tsx +17 -0
- package/templates/next/src/app/page.tsx +32 -0
- package/templates/next/src/app/providers.tsx +20 -0
- package/templates/next/tsconfig.json +34 -0
- package/templates/react/.env.example +1 -0
- package/templates/react/.prettierignore +10 -0
- package/templates/react/.prettierrc.json +9 -0
- package/templates/react/README.md +250 -0
- package/templates/react/_gitignore +31 -0
- package/templates/react/eslint.config.mjs +64 -0
- package/templates/react/package.json +19 -0
- package/templates/react/scripts/create-feature-crud.mjs +5 -0
- package/templates/react/scripts/create-feature.mjs +5 -0
- package/templates/react/src/app/app.tsx +15 -0
- package/templates/react/src/app/error-boundary.tsx +59 -0
- package/templates/react/src/app/frame.tsx +32 -0
- package/templates/react/src/app/globals.css +43 -0
- package/templates/react/src/app/not-found-page.tsx +23 -0
- package/templates/react/src/app/providers.tsx +16 -0
- package/templates/react/src/app/router.tsx +62 -0
- package/templates/react/src/main.tsx +12 -0
- package/templates/react/src/pages/index/page.tsx +36 -0
- package/templates/react/src/pages/login/page.tsx +18 -0
- package/templates/react/tsconfig.app.json +30 -0
- package/templates/react/tsconfig.json +4 -0
- package/templates/react/tsconfig.node.json +24 -0
- package/templates/react/vite.config.ts +14 -0
- 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
- 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
- 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
- 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
- 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
- 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
- package/templates/shared/docs//352/260/234/353/260/234/354/233/220/354/271/231/06-/355/217/274.md +116 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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 @@
|
|
|
1
|
+
|
|
@@ -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,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,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,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,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
|
+
}
|