create-sonamu 0.0.1 → 0.0.3
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 +365 -2
- package/index.js +632 -0
- package/package.json +30 -20
- package/template/src/README.md +274 -0
- package/template/src/packages/api/.swcrc +18 -0
- package/template/src/packages/api/custom-sequencer.ts +23 -0
- package/template/src/packages/api/database/docker-compose.yml +19 -0
- package/template/src/packages/api/database/fixtures/init.sh +15 -0
- package/template/src/packages/api/database/scripts/dump.sh +62 -0
- package/template/src/packages/api/database/scripts/seed.sh +60 -0
- package/template/src/packages/api/package.json +55 -0
- package/template/src/packages/api/package.json.bak +55 -0
- package/template/src/packages/api/src/application/.gitkeep +1 -0
- package/template/src/packages/api/src/i18n/en.ts +59 -0
- package/template/src/packages/api/src/i18n/ko.ts +57 -0
- package/template/src/packages/api/src/index.ts +6 -0
- package/template/src/packages/api/src/migrations/.gitkeep +1 -0
- package/template/src/packages/api/src/sonamu.config.ts +162 -0
- package/template/src/packages/api/src/testing/fixture.ts +6 -0
- package/template/src/packages/api/src/testing/global.ts +6 -0
- package/template/src/packages/api/src/testing/setup-mocks.ts +44 -0
- package/template/src/packages/api/src/typings/fastify.d.ts +7 -0
- package/template/src/packages/api/src/typings/sonamu.d.ts +19 -0
- package/template/src/packages/api/src/utils/subset-loaders.ts +11 -0
- package/template/src/packages/api/tsconfig.json +60 -0
- package/template/src/packages/api/tsconfig.schemas.json +5 -0
- package/template/src/packages/api/tsconfig.types.json +5 -0
- package/template/src/packages/api/vitest.config.ts +36 -0
- package/template/src/packages/web/.sonamu.env +2 -0
- package/template/src/{web → packages/web}/index.html +3 -3
- package/template/src/packages/web/package.json +49 -0
- package/template/src/packages/web/package.json.bak +49 -0
- package/template/src/packages/web/src/App.tsx +17 -0
- package/template/src/packages/web/src/admin-common/ApiLogViewer.tsx +285 -0
- package/template/src/packages/web/src/admin-common/CommonModal.tsx +91 -0
- package/template/src/packages/web/src/contexts/sonamu-provider.tsx +41 -0
- package/template/src/packages/web/src/entry-client.tsx +72 -0
- package/template/src/packages/web/src/entry-server.generated.tsx +58 -0
- package/template/src/packages/web/src/i18n/en.ts +63 -0
- package/template/src/packages/web/src/i18n/ko.ts +61 -0
- package/template/src/packages/web/src/routeTree.gen.ts +27 -0
- package/template/src/packages/web/src/routes/__root.tsx +44 -0
- package/template/src/packages/web/src/routes/index.tsx +14 -0
- package/template/src/packages/web/src/styles/tailwind.css +5 -0
- package/template/src/packages/web/src/vite-env.d.ts +2 -0
- package/template/src/packages/web/tailwind.config.ts +8 -0
- package/template/src/{web → packages/web}/tsconfig.json +5 -3
- package/template/src/packages/web/vite.config.ts +51 -0
- package/template/src/api/README.md +0 -3
- package/template/src/api/database/docker-compose.yml +0 -17
- package/template/src/api/package.json +0 -39
- package/template/src/api/sonamu.config.json +0 -11
- package/template/src/api/src/configs/db.ts +0 -25
- package/template/src/api/src/index.ts +0 -36
- package/template/src/api/src/testing/bootstrap.ts +0 -20
- package/template/src/api/src/testing/fixture.ts +0 -18
- package/template/src/api/src/testing/global.ts +0 -7
- package/template/src/api/src/typings/sonamu.d.ts +0 -5
- package/template/src/api/tsconfig.json +0 -115
- package/template/src/api/vite.config.mts +0 -15
- package/template/src/web/package.json +0 -40
- package/template/src/web/public/vite.svg +0 -1
- package/template/src/web/src/App.css +0 -34
- package/template/src/web/src/App.tsx +0 -15
- package/template/src/web/src/assets/react.svg +0 -1
- package/template/src/web/src/index.css +0 -76
- package/template/src/web/src/main.tsx +0 -30
- package/template/src/web/src/pages/index.tsx +0 -11
- package/template/src/web/src/vite-env.d.ts +0 -1
- package/template/src/web/vite.config.ts +0 -20
- /package/template/src/{web/src/services → packages/api/database/dumps}/.gitkeep +0 -0
- /package/template/src/{api/database/scripts/init.sql → packages/web/src/services/.gitkeep} +0 -0
- /package/template/src/{web → packages/web}/tsconfig.node.json +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { plural } from "sonamu/dict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Project EN Dictionary
|
|
5
|
+
*/
|
|
6
|
+
export default {
|
|
7
|
+
"common.all": "All",
|
|
8
|
+
"common.backToList": "Back to List",
|
|
9
|
+
"common.cancel": "Cancel",
|
|
10
|
+
"common.close": "Close",
|
|
11
|
+
"common.confirm": "Confirm",
|
|
12
|
+
"common.create": "Create",
|
|
13
|
+
"common.createdAt": "Created At",
|
|
14
|
+
"common.delete": "Delete",
|
|
15
|
+
"common.edit": "Edit",
|
|
16
|
+
"common.login": "Login",
|
|
17
|
+
"common.logout": "Logout",
|
|
18
|
+
"common.manage": "Manage",
|
|
19
|
+
"common.results": (count: number) =>
|
|
20
|
+
plural(count, { one: `${count} result`, other: `${count} results` }),
|
|
21
|
+
"common.save": "Save",
|
|
22
|
+
"common.search": "Search",
|
|
23
|
+
"common.searchPlaceholder": "Search...",
|
|
24
|
+
"common.searchType": "Search Type",
|
|
25
|
+
"common.sort": "Sort",
|
|
26
|
+
"confirm.delete": "Are you sure you want to delete?",
|
|
27
|
+
"confirm.save": "Do you want to save?",
|
|
28
|
+
"dashboard.adminMenu": "Admin Menu",
|
|
29
|
+
"dashboard.createdAt": "Joined",
|
|
30
|
+
"dashboard.email": "Email",
|
|
31
|
+
"dashboard.loginRequired": "Please login to continue.",
|
|
32
|
+
"dashboard.name": "Name",
|
|
33
|
+
"dashboard.role": "Role",
|
|
34
|
+
"dashboard.title": "Admin Dashboard",
|
|
35
|
+
"dashboard.welcome": "Welcome!",
|
|
36
|
+
"delete.confirm.description":
|
|
37
|
+
"This action cannot be undone. This will permanently delete this item.",
|
|
38
|
+
"delete.confirm.title": "Are you sure?",
|
|
39
|
+
"entity.create": (name: string) => `Create ${name}`,
|
|
40
|
+
"entity.edit": (name: string, id: number) => `Edit ${name} (#${id})`,
|
|
41
|
+
"entity.list": (name: string) => `${name} List`,
|
|
42
|
+
"entity.listManage": (name: string) => `Manage ${name} List`,
|
|
43
|
+
"error.alreadyProcessed": "Already processed",
|
|
44
|
+
"error.badRequest": "Bad request",
|
|
45
|
+
"error.duplicateRow": "Duplicate data",
|
|
46
|
+
"error.forbidden": "Forbidden",
|
|
47
|
+
"error.internalServerError": "Internal server error",
|
|
48
|
+
"error.notFound": "Not found",
|
|
49
|
+
"error.serviceUnavailable": "Service unavailable",
|
|
50
|
+
"error.unauthorized": "Unauthorized",
|
|
51
|
+
notFound: (name: string, id: number) => `${name} ID ${id} not found`,
|
|
52
|
+
"validation.email": "Invalid email format",
|
|
53
|
+
"validation.maxLength": (field: string, max: number) =>
|
|
54
|
+
`${field} must be at most ${max} characters`,
|
|
55
|
+
"validation.minLength": (field: string, min: number) =>
|
|
56
|
+
`${field} must be at least ${min} characters`,
|
|
57
|
+
"validation.required": (field: string) => `${field} is required`,
|
|
58
|
+
"validation.url": "Invalid URL format",
|
|
59
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { josa } from "sonamu/dict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Project KO Dictionary
|
|
5
|
+
*/
|
|
6
|
+
export default {
|
|
7
|
+
"common.all": "전체",
|
|
8
|
+
"common.backToList": "목록으로",
|
|
9
|
+
"common.cancel": "취소",
|
|
10
|
+
"common.close": "닫기",
|
|
11
|
+
"common.confirm": "확인",
|
|
12
|
+
"common.create": "생성",
|
|
13
|
+
"common.createdAt": "등록",
|
|
14
|
+
"common.delete": "삭제",
|
|
15
|
+
"common.edit": "수정",
|
|
16
|
+
"common.login": "로그인",
|
|
17
|
+
"common.logout": "로그아웃",
|
|
18
|
+
"common.manage": "관리",
|
|
19
|
+
"common.results": (count: number) => `${count}개 결과`,
|
|
20
|
+
"common.save": "저장",
|
|
21
|
+
"common.search": "검색",
|
|
22
|
+
"common.searchPlaceholder": "검색...",
|
|
23
|
+
"common.searchType": "검색 유형",
|
|
24
|
+
"common.sort": "정렬",
|
|
25
|
+
"confirm.delete": "정말 삭제하시겠습니까?",
|
|
26
|
+
"confirm.save": "저장하시겠습니까?",
|
|
27
|
+
"dashboard.adminMenu": "관리 메뉴",
|
|
28
|
+
"dashboard.createdAt": "가입일",
|
|
29
|
+
"dashboard.email": "이메일",
|
|
30
|
+
"dashboard.loginRequired": "로그인이 필요합니다.",
|
|
31
|
+
"dashboard.name": "이름",
|
|
32
|
+
"dashboard.role": "역할",
|
|
33
|
+
"dashboard.title": "관리자 대시보드",
|
|
34
|
+
"dashboard.welcome": "환영합니다!",
|
|
35
|
+
"delete.confirm.description": "이 작업은 취소할 수 없습니다. 항목이 영구적으로 삭제됩니다.",
|
|
36
|
+
"delete.confirm.title": "정말 삭제하시겠습니까?",
|
|
37
|
+
"entity.create": (name: string) => `${name} 생성`,
|
|
38
|
+
"entity.edit": (name: string, id: number) => `${name} 수정 (#${id})`,
|
|
39
|
+
"entity.list": (name: string) => `${name} 목록`,
|
|
40
|
+
"entity.listManage": (name: string) => `${name} 목록 관리`,
|
|
41
|
+
"error.alreadyProcessed": "이미 처리되었습니다",
|
|
42
|
+
"error.badRequest": "잘못된 요청입니다",
|
|
43
|
+
"error.duplicateRow": "중복된 데이터입니다",
|
|
44
|
+
"error.forbidden": "권한이 없습니다",
|
|
45
|
+
"error.internalServerError": "서버 오류가 발생했습니다",
|
|
46
|
+
"error.notFound": "찾을 수 없습니다",
|
|
47
|
+
"error.serviceUnavailable": "서비스를 사용할 수 없습니다",
|
|
48
|
+
"error.unauthorized": "인증이 필요합니다",
|
|
49
|
+
notFound: (name: string, id: number) => `존재하지 않는 ${name} ID ${id}`,
|
|
50
|
+
"validation.email": "올바른 이메일 형식이 아닙니다",
|
|
51
|
+
"validation.maxLength": (field: string, max: number) =>
|
|
52
|
+
`${field}${josa(field, "은는")} 최대 ${max}자까지 입력할 수 있습니다`,
|
|
53
|
+
"validation.minLength": (field: string, min: number) =>
|
|
54
|
+
`${field}${josa(field, "은는")} 최소 ${min}자 이상이어야 합니다`,
|
|
55
|
+
"validation.required": (field: string) => `${josa(field, "은는")} 필수입니다`,
|
|
56
|
+
"validation.url": "올바른 URL 형식이 아닙니다",
|
|
57
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# This directory will contain database migration files generated by Sonamu UI.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { CachePresets, defineConfig } from "sonamu";
|
|
3
|
+
import { drivers as cacheDrivers, store } from "sonamu/cache";
|
|
4
|
+
import { drivers } from "sonamu/storage";
|
|
5
|
+
|
|
6
|
+
const host = "localhost";
|
|
7
|
+
const port = 1028;
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
projectName: process.env.PROJECT_NAME ?? "SonamuProject",
|
|
11
|
+
api: {
|
|
12
|
+
dir: "api",
|
|
13
|
+
timezone: "Asia/Seoul",
|
|
14
|
+
route: {
|
|
15
|
+
prefix: "/api",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
i18n: {
|
|
19
|
+
defaultLocale: "ko",
|
|
20
|
+
supportedLocales: ["ko", "en"],
|
|
21
|
+
},
|
|
22
|
+
sync: {
|
|
23
|
+
targets: ["web"],
|
|
24
|
+
},
|
|
25
|
+
database: {
|
|
26
|
+
database: "pg",
|
|
27
|
+
name: process.env.DATABASE_NAME ?? "database_name",
|
|
28
|
+
defaultOptions: {
|
|
29
|
+
connection: {
|
|
30
|
+
host: process.env.DB_HOST || "0.0.0.0",
|
|
31
|
+
port: Number(process.env.DB_PORT) || 5432,
|
|
32
|
+
user: process.env.DB_USER || "postgres",
|
|
33
|
+
password: process.env.DB_PASSWORD,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
server: {
|
|
39
|
+
listen: { port, host },
|
|
40
|
+
plugins: {
|
|
41
|
+
formbody: true,
|
|
42
|
+
qs: true,
|
|
43
|
+
multipart: { limits: { fileSize: 1024 * 1024 * 30 } },
|
|
44
|
+
static: {
|
|
45
|
+
root: path.join(import.meta.dirname, "/../", "public"),
|
|
46
|
+
prefix: "/api/public",
|
|
47
|
+
},
|
|
48
|
+
session: {
|
|
49
|
+
secret: process.env.SESSION_SECRET || "sonamu-secret-key-change-this-in-production",
|
|
50
|
+
salt: process.env.SESSION_SALT || "mq9hDxBCDbsQDR6N",
|
|
51
|
+
cookie: {
|
|
52
|
+
domain: "localhost",
|
|
53
|
+
path: "/",
|
|
54
|
+
maxAge: 60 * 60 * 24 * 365 * 10,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
custom: (_server) => {
|
|
58
|
+
// nothing yet
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
auth: true,
|
|
63
|
+
// auth: {
|
|
64
|
+
// userSerializer: async (user, _request) => user,
|
|
65
|
+
// userDeserializer: async (serialized, _request) => serialized,
|
|
66
|
+
// },
|
|
67
|
+
|
|
68
|
+
apiConfig: {
|
|
69
|
+
contextProvider: (defaultContext, request) => {
|
|
70
|
+
return {
|
|
71
|
+
...defaultContext,
|
|
72
|
+
ip: request.ip,
|
|
73
|
+
session: request.session,
|
|
74
|
+
body: request.body,
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
guardHandler: (_guard, _request, _api) => {
|
|
78
|
+
console.log("NOTHING YET");
|
|
79
|
+
},
|
|
80
|
+
cacheControlHandler: (req) => {
|
|
81
|
+
switch (req.type) {
|
|
82
|
+
case "assets":
|
|
83
|
+
// Hash 포함된 파일: 영구 캐시
|
|
84
|
+
if (req.path.match(/-[a-f0-9]+\./)) {
|
|
85
|
+
return CachePresets.immutable;
|
|
86
|
+
}
|
|
87
|
+
return CachePresets.longLived;
|
|
88
|
+
|
|
89
|
+
case "api":
|
|
90
|
+
// GET 요청만 캐싱 고려
|
|
91
|
+
if (req.method === "GET") {
|
|
92
|
+
// 특정 경로는 짧은 캐시
|
|
93
|
+
if (req.path.startsWith("/api/static-data")) {
|
|
94
|
+
return CachePresets.shortLived;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// 기본: 캐시 없음
|
|
98
|
+
return CachePresets.noCache;
|
|
99
|
+
|
|
100
|
+
case "ssr":
|
|
101
|
+
// SSR 페이지: 10초 캐시
|
|
102
|
+
return CachePresets.ssr;
|
|
103
|
+
|
|
104
|
+
case "csr":
|
|
105
|
+
// CSR fallback (index.html): 1분 캐시
|
|
106
|
+
return CachePresets.shortLived;
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
storage: {
|
|
112
|
+
drivers: {
|
|
113
|
+
fs: drivers.fs({
|
|
114
|
+
location: path.join(import.meta.dirname, "/../public/uploaded"),
|
|
115
|
+
visibility: "public",
|
|
116
|
+
urlBuilder: {
|
|
117
|
+
async generateURL(key) {
|
|
118
|
+
return `/api/public/uploaded/${key}`;
|
|
119
|
+
},
|
|
120
|
+
async generateSignedURL(key) {
|
|
121
|
+
return `/api/public/uploaded/${key}`;
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
}),
|
|
125
|
+
s3: drivers.s3({
|
|
126
|
+
credentials: {
|
|
127
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? "",
|
|
128
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? "",
|
|
129
|
+
},
|
|
130
|
+
region: process.env.S3_REGION ?? "ap-northeast-2",
|
|
131
|
+
bucket: process.env.S3_BUCKET ?? "sonamu_default_bucket",
|
|
132
|
+
visibility: "private",
|
|
133
|
+
}),
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
cache: {
|
|
138
|
+
default: "main",
|
|
139
|
+
stores: {
|
|
140
|
+
main: store().useL1Layer(cacheDrivers.memory({ maxSize: "50mb" })),
|
|
141
|
+
},
|
|
142
|
+
ttl: "5m",
|
|
143
|
+
prefix: "",
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
lifecycle: {
|
|
147
|
+
onStart: () => {
|
|
148
|
+
console.log(`🌲 Server listening on http://${host}:${port}`);
|
|
149
|
+
},
|
|
150
|
+
onShutdown: () => {
|
|
151
|
+
console.log("graceful shutdown");
|
|
152
|
+
},
|
|
153
|
+
onError: (error, _request, reply) => {
|
|
154
|
+
console.error(error);
|
|
155
|
+
reply.status(500).send({
|
|
156
|
+
name: error.name,
|
|
157
|
+
message: error.message,
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { MakeDirectoryOptions, Mode, PathLike, RmOptions } from "fs";
|
|
2
|
+
import type { FileHandle } from "fs/promises";
|
|
3
|
+
import { Naite } from "sonamu";
|
|
4
|
+
import { vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
// GlobalMock: fs/promises
|
|
7
|
+
vi.mock("fs/promises", async (importOriginal) => {
|
|
8
|
+
const actual = (await importOriginal()) as typeof import("fs/promises");
|
|
9
|
+
return {
|
|
10
|
+
...actual,
|
|
11
|
+
access: vi.fn((path: PathLike, mode?: number) => {
|
|
12
|
+
const vfs = Naite.get("mock:fs/promises:virtualFileSystem").result();
|
|
13
|
+
if (vfs.some((v) => v === path)) {
|
|
14
|
+
return Promise.resolve();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return actual.access(path, mode);
|
|
18
|
+
}),
|
|
19
|
+
mkdir: vi.fn(
|
|
20
|
+
async (
|
|
21
|
+
path: PathLike,
|
|
22
|
+
options?: MakeDirectoryOptions | Mode | null,
|
|
23
|
+
): Promise<string | undefined> => {
|
|
24
|
+
Naite.t("fs:mkdir", { path, options });
|
|
25
|
+
if (typeof options === "object" && options?.recursive) {
|
|
26
|
+
return typeof path === "string" ? path : path.toString();
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
},
|
|
30
|
+
),
|
|
31
|
+
writeFile: vi.fn((path: PathLike | FileHandle, data: string | Buffer | Uint8Array) => {
|
|
32
|
+
const filePath = typeof path === "string" ? path : path.toString();
|
|
33
|
+
|
|
34
|
+
Naite.t(`fs/promises:writeFile`, { path: filePath, data });
|
|
35
|
+
}),
|
|
36
|
+
rm: vi.fn(async (path: PathLike, options?: RmOptions) => {
|
|
37
|
+
const filePath = typeof path === "string" ? path : path.toString();
|
|
38
|
+
|
|
39
|
+
Naite.t(`fs/promises:rm`, { path: filePath, options });
|
|
40
|
+
// 실제 삭제하지 않고 기록만 함
|
|
41
|
+
return Promise.resolve();
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** biome-ignore-all lint/correctness/noUnusedImports: d.ts */
|
|
2
|
+
|
|
3
|
+
import type { Session } from "@fastify/secure-session";
|
|
4
|
+
import {} from "sonamu";
|
|
5
|
+
|
|
6
|
+
declare module "sonamu" {
|
|
7
|
+
export interface ContextExtend {
|
|
8
|
+
ip: string;
|
|
9
|
+
session: Session;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface GuardKeys {
|
|
13
|
+
query: true;
|
|
14
|
+
user: true;
|
|
15
|
+
admin: true;
|
|
16
|
+
// 새로운 커스텀 가드키를 추가하는 경우
|
|
17
|
+
// CustomGuardKey: true
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SubsetQuery } from "sonamu";
|
|
2
|
+
|
|
3
|
+
export function getSubsetLoaders(subsets: string[], subsetQueries: Record<string, SubsetQuery>) {
|
|
4
|
+
return subsets.reduce(
|
|
5
|
+
(acc, subset) => {
|
|
6
|
+
acc[subset] = subsetQueries[subset]?.loaders ?? [];
|
|
7
|
+
return acc;
|
|
8
|
+
},
|
|
9
|
+
{} as Record<string, SubsetQuery["loaders"]>,
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
/* Basic Options */
|
|
4
|
+
"target": "esnext",
|
|
5
|
+
"module": "esnext",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"sourceMap": true,
|
|
8
|
+
"lib": ["esnext", "dom"],
|
|
9
|
+
|
|
10
|
+
/* Strict Type-Checking Options */
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noImplicitAny": true,
|
|
13
|
+
"strictNullChecks": true,
|
|
14
|
+
"strictFunctionTypes": true,
|
|
15
|
+
"strictBindCallApply": true,
|
|
16
|
+
"strictPropertyInitialization": true,
|
|
17
|
+
"noImplicitThis": true,
|
|
18
|
+
"alwaysStrict": true,
|
|
19
|
+
|
|
20
|
+
/* Additional Checks */
|
|
21
|
+
"noUnusedLocals": true,
|
|
22
|
+
"noUnusedParameters": true,
|
|
23
|
+
"noImplicitReturns": true,
|
|
24
|
+
"noFallthroughCasesInSwitch": true,
|
|
25
|
+
"useUnknownInCatchVariables": true,
|
|
26
|
+
|
|
27
|
+
/* Module Resolution Options */
|
|
28
|
+
"moduleResolution": "bundler",
|
|
29
|
+
"esModuleInterop": true,
|
|
30
|
+
|
|
31
|
+
/* Experimental Options */
|
|
32
|
+
"experimentalDecorators": true,
|
|
33
|
+
"emitDecoratorMetadata": true,
|
|
34
|
+
|
|
35
|
+
/* Advanced Options */
|
|
36
|
+
"forceConsistentCasingInFileNames": true,
|
|
37
|
+
"noErrorTruncation": true,
|
|
38
|
+
|
|
39
|
+
"resolveJsonModule": true,
|
|
40
|
+
|
|
41
|
+
"skipLibCheck": true,
|
|
42
|
+
"noUncheckedIndexedAccess": true
|
|
43
|
+
},
|
|
44
|
+
"include": ["src/**/*.ts", "src/**/*.json"],
|
|
45
|
+
"exclude": [
|
|
46
|
+
"node_modules",
|
|
47
|
+
"dist/*",
|
|
48
|
+
"public",
|
|
49
|
+
// "src/**/*.test.ts",
|
|
50
|
+
"src/**/*.test-hold.ts",
|
|
51
|
+
"src/**/*.ignore.ts",
|
|
52
|
+
"wasted_src/**",
|
|
53
|
+
"_templates/**",
|
|
54
|
+
"**/__mocks__/**",
|
|
55
|
+
"vite.config.ts"
|
|
56
|
+
],
|
|
57
|
+
"tsc-alias": {
|
|
58
|
+
"resolveFullPaths": true
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getSonamuTestConfig, NaiteVitestReporter } from "sonamu/test";
|
|
2
|
+
import { defineConfig } from "vitest/config";
|
|
3
|
+
import { PrioritySequencer } from "./custom-sequencer";
|
|
4
|
+
|
|
5
|
+
export default defineConfig(async () => ({
|
|
6
|
+
plugins: [],
|
|
7
|
+
test: await getSonamuTestConfig({
|
|
8
|
+
include: ["src/**/*.test.ts"],
|
|
9
|
+
exclude: ["src/**/*.test-hold.ts", "**/node_modules/**", "**/.yarn/**", "**/dist/**"],
|
|
10
|
+
globals: true,
|
|
11
|
+
globalSetup: ["./src/testing/global.ts"],
|
|
12
|
+
setupFiles: ["./src/testing/setup-mocks.ts"],
|
|
13
|
+
sequence: {
|
|
14
|
+
sequencer: PrioritySequencer,
|
|
15
|
+
},
|
|
16
|
+
reporters: ["default", NaiteVitestReporter],
|
|
17
|
+
restoreMocks: true,
|
|
18
|
+
typecheck: {
|
|
19
|
+
enabled: true,
|
|
20
|
+
tsconfig: "./tsconfig.json",
|
|
21
|
+
include: ["src/**/*type-safety.test.ts"],
|
|
22
|
+
},
|
|
23
|
+
coverage: {
|
|
24
|
+
provider: "v8",
|
|
25
|
+
reporter: ["text", "html"],
|
|
26
|
+
include: ["src/**/*.ts"],
|
|
27
|
+
exclude: ["**/*.test.ts", "**/testing/**", "**/node_modules/**", "**/dist/**"],
|
|
28
|
+
},
|
|
29
|
+
includeTaskLocation: true,
|
|
30
|
+
server: {
|
|
31
|
+
deps: {
|
|
32
|
+
inline: ["sonamu"],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
}));
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/svg+xml" href="/
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/sonamu.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
-
<title>
|
|
7
|
+
<title>Sonamu Project</title>
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<div id="root"></div>
|
|
11
|
-
<script type="module" src="/src/
|
|
11
|
+
<script type="module" src="/src/entry-client.tsx"></script>
|
|
12
12
|
</body>
|
|
13
13
|
</html>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-sonamu-web",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Sonamu web client",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "vite",
|
|
9
|
+
"build": "tsc && vite build",
|
|
10
|
+
"preview": "vite preview"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@sonamu-kit/react-components": "^0.1.6",
|
|
14
|
+
"@tanstack/react-query": "^5.90.12",
|
|
15
|
+
"@tanstack/react-router": "1.143.11",
|
|
16
|
+
"axios": "^1.13.2",
|
|
17
|
+
"clsx": "^2.1.1",
|
|
18
|
+
"dotenv": "^16",
|
|
19
|
+
"eventsource": "^4.1.0",
|
|
20
|
+
"jotai": "^2.14.0",
|
|
21
|
+
"qs": "^6.14.1",
|
|
22
|
+
"radashi": "^12.2.0",
|
|
23
|
+
"react": "^19.2.3",
|
|
24
|
+
"react-day-picker": "^8.10.1",
|
|
25
|
+
"react-dom": "^19.2.3",
|
|
26
|
+
"tailwind-merge": "^3.4.0",
|
|
27
|
+
"zod": "^4.1.12"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@iconify/json": "^2.2.421",
|
|
31
|
+
"@svgr/core": "^8.1.0",
|
|
32
|
+
"@svgr/plugin-jsx": "^8.1.0",
|
|
33
|
+
"@svgr/plugin-svgo": "^8.1.0",
|
|
34
|
+
"@tailwindcss/postcss": "^4.0.0",
|
|
35
|
+
"@tailwindcss/vite": "^4.1.17",
|
|
36
|
+
"@tanstack/react-router-devtools": "1.143.11",
|
|
37
|
+
"@tanstack/router-plugin": "1.143.11",
|
|
38
|
+
"@types/node": "25.0.7",
|
|
39
|
+
"@types/qs": "^6.14.0",
|
|
40
|
+
"@types/react": "^19.2.7",
|
|
41
|
+
"@types/react-dom": "^19.2.3",
|
|
42
|
+
"@vitejs/plugin-react-swc": "^4.1.3",
|
|
43
|
+
"sass": "^1.92.1",
|
|
44
|
+
"tailwindcss": "^4.0.0",
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"unplugin-icons": "0.20.2",
|
|
47
|
+
"vite": "7.3.0"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-sonamu-web",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Sonamu web client",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "vite",
|
|
9
|
+
"build": "tsc && vite build",
|
|
10
|
+
"preview": "vite preview"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@sonamu-kit/react-components": "workspace:^",
|
|
14
|
+
"@tanstack/react-query": "catalog:",
|
|
15
|
+
"@tanstack/react-router": "catalog:",
|
|
16
|
+
"axios": "catalog:",
|
|
17
|
+
"clsx": "catalog:",
|
|
18
|
+
"dotenv": "catalog:",
|
|
19
|
+
"eventsource": "catalog:",
|
|
20
|
+
"jotai": "catalog:",
|
|
21
|
+
"qs": "catalog:",
|
|
22
|
+
"radashi": "catalog:",
|
|
23
|
+
"react": "catalog:",
|
|
24
|
+
"react-day-picker": "catalog:",
|
|
25
|
+
"react-dom": "catalog:",
|
|
26
|
+
"tailwind-merge": "catalog:",
|
|
27
|
+
"zod": "catalog:"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@iconify/json": "catalog:",
|
|
31
|
+
"@svgr/core": "catalog:",
|
|
32
|
+
"@svgr/plugin-jsx": "catalog:",
|
|
33
|
+
"@svgr/plugin-svgo": "catalog:",
|
|
34
|
+
"@tailwindcss/postcss": "catalog:",
|
|
35
|
+
"@tailwindcss/vite": "catalog:",
|
|
36
|
+
"@tanstack/react-router-devtools": "catalog:",
|
|
37
|
+
"@tanstack/router-plugin": "catalog:",
|
|
38
|
+
"@types/node": "catalog:",
|
|
39
|
+
"@types/qs": "catalog:",
|
|
40
|
+
"@types/react": "catalog:",
|
|
41
|
+
"@types/react-dom": "catalog:",
|
|
42
|
+
"@vitejs/plugin-react-swc": "catalog:",
|
|
43
|
+
"sass": "catalog:",
|
|
44
|
+
"tailwindcss": "catalog:",
|
|
45
|
+
"typescript": "catalog:",
|
|
46
|
+
"unplugin-icons": "catalog:",
|
|
47
|
+
"vite": "catalog:"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ReactNode, Suspense } from "react";
|
|
2
|
+
|
|
3
|
+
interface AppProps {
|
|
4
|
+
children?: ReactNode;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function App({ children }: AppProps) {
|
|
8
|
+
return (
|
|
9
|
+
<div className="min-h-screen bg-gray-50">
|
|
10
|
+
<div className="max-w-7xl mx-auto">
|
|
11
|
+
<Suspense fallback={<div>Loading...</div>}>{children}</Suspense>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default App;
|