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
package/index.js
ADDED
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
13
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
14
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
15
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
16
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
17
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
18
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
22
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
23
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
24
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
25
|
+
function step(op) {
|
|
26
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
27
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
28
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
29
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
30
|
+
switch (op[0]) {
|
|
31
|
+
case 0: case 1: t = op; break;
|
|
32
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
33
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
34
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
35
|
+
default:
|
|
36
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
37
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
38
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
39
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
40
|
+
if (t[2]) _.ops.pop();
|
|
41
|
+
_.trys.pop(); continue;
|
|
42
|
+
}
|
|
43
|
+
op = body.call(thisArg, _);
|
|
44
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
45
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
49
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
50
|
+
if (ar || !(i in from)) {
|
|
51
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
52
|
+
ar[i] = from[i];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
56
|
+
};
|
|
57
|
+
import { spawn } from "node:child_process";
|
|
58
|
+
import * as fs from "node:fs";
|
|
59
|
+
import * as path from "node:path";
|
|
60
|
+
import { fileURLToPath } from "node:url";
|
|
61
|
+
import chalk from "chalk";
|
|
62
|
+
import minimist from "minimist";
|
|
63
|
+
import ora from "ora";
|
|
64
|
+
import prompts from "prompts";
|
|
65
|
+
// 생성된 파일/디렉토리 전역에서 추적하기 위한 변수
|
|
66
|
+
var createdTargetRoot = null;
|
|
67
|
+
var isCleaningUp = false;
|
|
68
|
+
// Helper: package.json에서 catalog 패키지 추출
|
|
69
|
+
function extractCatalogPackages(packageJsonPath) {
|
|
70
|
+
var packages = new Set();
|
|
71
|
+
if (!fs.existsSync(packageJsonPath))
|
|
72
|
+
return packages;
|
|
73
|
+
var pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
74
|
+
var allDeps = __assign(__assign({}, pkg.dependencies), pkg.devDependencies);
|
|
75
|
+
for (var _i = 0, _a = Object.entries(allDeps); _i < _a.length; _i++) {
|
|
76
|
+
var _b = _a[_i], name_1 = _b[0], version = _b[1];
|
|
77
|
+
if (version === "catalog:") {
|
|
78
|
+
packages.add(name_1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return packages;
|
|
82
|
+
}
|
|
83
|
+
// Helper: pnpm-workspace.yaml에서 catalog 파싱
|
|
84
|
+
function parseCatalogFromWorkspace(workspacePath) {
|
|
85
|
+
if (!fs.existsSync(workspacePath))
|
|
86
|
+
return {};
|
|
87
|
+
var content = fs.readFileSync(workspacePath, "utf-8");
|
|
88
|
+
var catalog = {};
|
|
89
|
+
var lines = content.split("\n");
|
|
90
|
+
var inCatalog = false;
|
|
91
|
+
for (var _i = 0, lines_1 = lines; _i < lines_1.length; _i++) {
|
|
92
|
+
var line = lines_1[_i];
|
|
93
|
+
if (line.trim() === "catalog:") {
|
|
94
|
+
inCatalog = true;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (inCatalog) {
|
|
98
|
+
// catalog 섹션이 끝나면 중단
|
|
99
|
+
if (line && !line.startsWith(" ") && !line.startsWith("\t")) {
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
// 패키지 파싱: " package-name: version"
|
|
103
|
+
var match = line.match(/^\s+["']?([^"':]+)["']?:\s*(.+)$/);
|
|
104
|
+
if (match) {
|
|
105
|
+
var name_2 = match[1], version = match[2];
|
|
106
|
+
catalog[name_2.trim()] = version.trim();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return catalog;
|
|
111
|
+
}
|
|
112
|
+
// Helper: workspace 패키지의 실제 버전 가져오기
|
|
113
|
+
function getWorkspacePackageVersion(packageName, workspaceRoot) {
|
|
114
|
+
var packagePath = path.join(workspaceRoot, "modules", packageName, "package.json");
|
|
115
|
+
if (fs.existsSync(packagePath)) {
|
|
116
|
+
var pkg = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
|
|
117
|
+
return "^".concat(pkg.version);
|
|
118
|
+
}
|
|
119
|
+
return "workspace:^"; // fallback
|
|
120
|
+
}
|
|
121
|
+
function init() {
|
|
122
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
123
|
+
var shutdownHandler, argv, argProjectName, useDefaults, parseYesNo, result, e_1, targetDir, targetRoot, overwrite, result_1, templateRoot, copy, write, files, _i, _a, file, workspaceRoot, parentWorkspacePath, apiPkgPath, webPkgPath, apiPackages, webPackages, allCatalogPackages, parentCatalog, catalogEntries, _b, _c, pkgName, version, workspaceContent, isPnpm, pnpmOption, result_2, error_1, isDatabase, dockerOption, result_3, useDbDefaults, answers, error_2, env, initSqlPath, initSql;
|
|
124
|
+
var _d;
|
|
125
|
+
return __generator(this, function (_e) {
|
|
126
|
+
switch (_e.label) {
|
|
127
|
+
case 0:
|
|
128
|
+
shutdownHandler = function () {
|
|
129
|
+
cleanup();
|
|
130
|
+
process.exit(1);
|
|
131
|
+
};
|
|
132
|
+
process.on("SIGINT", shutdownHandler);
|
|
133
|
+
process.on("SIGTERM", shutdownHandler);
|
|
134
|
+
argv = minimist(process.argv.slice(2), {
|
|
135
|
+
boolean: ["yes", "y", "skip-pnpm", "skip-docker"],
|
|
136
|
+
string: [
|
|
137
|
+
"db-user",
|
|
138
|
+
"db-password",
|
|
139
|
+
"db-name",
|
|
140
|
+
"container-name",
|
|
141
|
+
"docker-project",
|
|
142
|
+
"pnpm",
|
|
143
|
+
"docker",
|
|
144
|
+
],
|
|
145
|
+
alias: {
|
|
146
|
+
y: "yes",
|
|
147
|
+
"docker-pj-name": "docker-project", // --docker-pj-name은 --docker-project의 alias
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
argProjectName = argv._[0];
|
|
151
|
+
useDefaults = argv.yes || argv.y;
|
|
152
|
+
parseYesNo = function (value) {
|
|
153
|
+
if (value === undefined)
|
|
154
|
+
return undefined;
|
|
155
|
+
var lower = value.toLowerCase();
|
|
156
|
+
if (["y", "yes", "true", "1"].includes(lower))
|
|
157
|
+
return true;
|
|
158
|
+
if (["n", "no", "false", "0"].includes(lower))
|
|
159
|
+
return false;
|
|
160
|
+
return undefined;
|
|
161
|
+
};
|
|
162
|
+
_e.label = 1;
|
|
163
|
+
case 1:
|
|
164
|
+
_e.trys.push([1, 5, , 6]);
|
|
165
|
+
if (!argProjectName) return [3 /*break*/, 2];
|
|
166
|
+
result = { targetDir: argProjectName };
|
|
167
|
+
return [3 /*break*/, 4];
|
|
168
|
+
case 2: return [4 /*yield*/, prompts([
|
|
169
|
+
{
|
|
170
|
+
type: "text",
|
|
171
|
+
name: "targetDir",
|
|
172
|
+
message: "Project name:",
|
|
173
|
+
initial: "my_sonamu_app",
|
|
174
|
+
validate: function (value) {
|
|
175
|
+
if (!value) {
|
|
176
|
+
return "Project name is required";
|
|
177
|
+
}
|
|
178
|
+
if (value.includes(" ")) {
|
|
179
|
+
return "Project name cannot contain spaces";
|
|
180
|
+
}
|
|
181
|
+
if (value.includes("-")) {
|
|
182
|
+
return "Project name cannot contain hyphens";
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
], {
|
|
188
|
+
onCancel: createCancelHandler(),
|
|
189
|
+
})];
|
|
190
|
+
case 3:
|
|
191
|
+
result = _e.sent();
|
|
192
|
+
_e.label = 4;
|
|
193
|
+
case 4: return [3 /*break*/, 6];
|
|
194
|
+
case 5:
|
|
195
|
+
e_1 = _e.sent();
|
|
196
|
+
cleanup();
|
|
197
|
+
console.error(e_1);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
return [3 /*break*/, 6];
|
|
200
|
+
case 6:
|
|
201
|
+
targetDir = result.targetDir;
|
|
202
|
+
targetRoot = path.join(process.cwd(), targetDir);
|
|
203
|
+
if (!fs.existsSync(targetRoot)) return [3 /*break*/, 9];
|
|
204
|
+
overwrite = useDefaults;
|
|
205
|
+
if (!!useDefaults) return [3 /*break*/, 8];
|
|
206
|
+
return [4 /*yield*/, prompts({
|
|
207
|
+
type: "confirm",
|
|
208
|
+
name: "overwrite",
|
|
209
|
+
message: "Directory ".concat(targetRoot, " already exists. Overwrite?"),
|
|
210
|
+
initial: true,
|
|
211
|
+
}, {
|
|
212
|
+
onCancel: createCancelHandler(),
|
|
213
|
+
})];
|
|
214
|
+
case 7:
|
|
215
|
+
result_1 = _e.sent();
|
|
216
|
+
overwrite = result_1.overwrite;
|
|
217
|
+
_e.label = 8;
|
|
218
|
+
case 8:
|
|
219
|
+
if (!overwrite) {
|
|
220
|
+
console.log(chalk.yellow("Operation cancelled."));
|
|
221
|
+
process.exit(0);
|
|
222
|
+
}
|
|
223
|
+
// 기존 디렉토리 삭제
|
|
224
|
+
console.log(chalk.yellow("Removing existing directory: ".concat(targetRoot)));
|
|
225
|
+
removeDirectory(targetRoot);
|
|
226
|
+
_e.label = 9;
|
|
227
|
+
case 9:
|
|
228
|
+
createdTargetRoot = targetRoot; // 생성된 디렉토리 추적 시작
|
|
229
|
+
templateRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), "template", "src");
|
|
230
|
+
// 복사 시작 전에 타겟 프로젝트 폴더 생성
|
|
231
|
+
if (!fs.existsSync(targetRoot)) {
|
|
232
|
+
fs.mkdirSync(targetRoot, { recursive: true });
|
|
233
|
+
}
|
|
234
|
+
copy = function (src, dest) {
|
|
235
|
+
var stat = fs.statSync(src);
|
|
236
|
+
var basename = path.basename(src);
|
|
237
|
+
// 제외할 디렉토리/파일 목록
|
|
238
|
+
var excludeList = ["dist", ".git", ".gitkeep", "node_modules", "pnpm-lock.yaml"];
|
|
239
|
+
if (excludeList.includes(basename)) {
|
|
240
|
+
if (basename === ".gitkeep") {
|
|
241
|
+
console.log("".concat(chalk.green("CREATE"), " ").concat(dest.split(".gitkeep")[0]));
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (stat.isDirectory()) {
|
|
246
|
+
// 디렉토리는 생성
|
|
247
|
+
if (!fs.existsSync(dest)) {
|
|
248
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
249
|
+
}
|
|
250
|
+
for (var _i = 0, _a = fs.readdirSync(src); _i < _a.length; _i++) {
|
|
251
|
+
var file = _a[_i];
|
|
252
|
+
var srcFile = path.resolve(src, file);
|
|
253
|
+
var destFile = path.resolve(dest, file);
|
|
254
|
+
copy(srcFile, destFile);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
// 파일은 복사
|
|
259
|
+
fs.copyFileSync(src, dest);
|
|
260
|
+
console.log("".concat(chalk.green("CREATE"), " ").concat(dest));
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
write = function (file) {
|
|
264
|
+
var src = path.join(templateRoot, file);
|
|
265
|
+
var dest = path.join(targetRoot, file);
|
|
266
|
+
copy(src, dest);
|
|
267
|
+
};
|
|
268
|
+
files = fs.readdirSync(templateRoot);
|
|
269
|
+
for (_i = 0, _a = files.filter(function (f) { return f !== "package.json"; }); _i < _a.length; _i++) {
|
|
270
|
+
file = _a[_i];
|
|
271
|
+
write(file);
|
|
272
|
+
}
|
|
273
|
+
workspaceRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
274
|
+
["packages/api", "packages/web"].forEach(function (dir) {
|
|
275
|
+
var pkgPath = path.join(templateRoot, dir, "package.json");
|
|
276
|
+
if (fs.existsSync(pkgPath)) {
|
|
277
|
+
var pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
278
|
+
// Extract just "api" or "web" from "packages/api"
|
|
279
|
+
var pkgType = dir.split("/")[1];
|
|
280
|
+
pkg.name = "".concat(targetDir, "-").concat(pkgType);
|
|
281
|
+
// Replace workspace:^ with actual versions
|
|
282
|
+
var replaceDeps = function (deps) {
|
|
283
|
+
if (!deps)
|
|
284
|
+
return;
|
|
285
|
+
for (var _i = 0, _a = Object.entries(deps); _i < _a.length; _i++) {
|
|
286
|
+
var _b = _a[_i], name_3 = _b[0], version = _b[1];
|
|
287
|
+
if (version === "workspace:^") {
|
|
288
|
+
// Extract package name from scoped package (e.g., @sonamu-kit/react-sui -> react-sui)
|
|
289
|
+
var pkgName = name_3.includes("/") ? name_3.split("/")[1] : name_3;
|
|
290
|
+
deps[name_3] = getWorkspacePackageVersion(pkgName, workspaceRoot);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
replaceDeps(pkg.dependencies);
|
|
295
|
+
replaceDeps(pkg.devDependencies);
|
|
296
|
+
fs.writeFileSync(path.join(targetRoot, dir, "package.json"), JSON.stringify(pkg, null, 2));
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
parentWorkspacePath = path.join(workspaceRoot, "pnpm-workspace.yaml");
|
|
300
|
+
apiPkgPath = path.join(templateRoot, "packages", "api", "package.json");
|
|
301
|
+
webPkgPath = path.join(templateRoot, "packages", "web", "package.json");
|
|
302
|
+
apiPackages = extractCatalogPackages(apiPkgPath);
|
|
303
|
+
webPackages = extractCatalogPackages(webPkgPath);
|
|
304
|
+
allCatalogPackages = new Set(__spreadArray(__spreadArray([], Array.from(apiPackages), true), Array.from(webPackages), true));
|
|
305
|
+
parentCatalog = parseCatalogFromWorkspace(parentWorkspacePath);
|
|
306
|
+
catalogEntries = [];
|
|
307
|
+
for (_b = 0, _c = Array.from(allCatalogPackages).sort(); _b < _c.length; _b++) {
|
|
308
|
+
pkgName = _c[_b];
|
|
309
|
+
version = parentCatalog[pkgName];
|
|
310
|
+
if (version) {
|
|
311
|
+
catalogEntries.push(" \"".concat(pkgName, "\": ").concat(version));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
workspaceContent = "packages:\n - packages/api\n - packages/web\n\ncatalog:\n".concat(catalogEntries.join("\n"), "\n\nonlyBuiltDependencies:\n - \"@parcel/watcher\"\n - \"@swc/core\"\n - bcrypt\n - esbuild\n - libpq\n - sharp\n - sodium-native\n - unrs-resolver\n\noverrides:\n axios@<0.30.0: \">=0.30.0\"\n axios@<0.30.2: \">=0.30.2\"\n axios@>=0.8.1 <0.28.0: \">=0.28.0\"\n mdast-util-to-hast@>=13.0.0 <13.2.1: \">=13.2.1\"\n prismjs@<1.30.0: \">=1.30.0\"\n");
|
|
315
|
+
fs.writeFileSync(path.join(targetRoot, "pnpm-workspace.yaml"), workspaceContent);
|
|
316
|
+
console.log("".concat(chalk.green("CREATE"), " ").concat(path.join(targetRoot, "pnpm-workspace.yaml")));
|
|
317
|
+
console.log("\n\uD83C\uDF32 Created project in ".concat(targetRoot, "\n"));
|
|
318
|
+
isPnpm = true;
|
|
319
|
+
pnpmOption = parseYesNo(argv.pnpm);
|
|
320
|
+
if (!(argv["skip-pnpm"] || pnpmOption === false)) return [3 /*break*/, 10];
|
|
321
|
+
// --skip-pnpm 또는 --pnpm n 옵션으로 스킵
|
|
322
|
+
isPnpm = false;
|
|
323
|
+
return [3 /*break*/, 13];
|
|
324
|
+
case 10:
|
|
325
|
+
if (!(pnpmOption === true || useDefaults)) return [3 /*break*/, 11];
|
|
326
|
+
// --pnpm y 또는 --yes 옵션이면 자동 진행
|
|
327
|
+
isPnpm = true;
|
|
328
|
+
return [3 /*break*/, 13];
|
|
329
|
+
case 11: return [4 /*yield*/, prompts({
|
|
330
|
+
type: "confirm",
|
|
331
|
+
name: "isPnpm",
|
|
332
|
+
message: "Would you like to set up pnpm?",
|
|
333
|
+
initial: true,
|
|
334
|
+
}, {
|
|
335
|
+
onCancel: createCancelHandler(),
|
|
336
|
+
})];
|
|
337
|
+
case 12:
|
|
338
|
+
result_2 = _e.sent();
|
|
339
|
+
isPnpm = result_2.isPnpm;
|
|
340
|
+
_e.label = 13;
|
|
341
|
+
case 13:
|
|
342
|
+
if (!isPnpm) return [3 /*break*/, 18];
|
|
343
|
+
_e.label = 14;
|
|
344
|
+
case 14:
|
|
345
|
+
_e.trys.push([14, 16, , 17]);
|
|
346
|
+
// Install dependencies from the root directory (workspace)
|
|
347
|
+
return [4 /*yield*/, setupPnpm(targetRoot)];
|
|
348
|
+
case 15:
|
|
349
|
+
// Install dependencies from the root directory (workspace)
|
|
350
|
+
_e.sent();
|
|
351
|
+
return [3 /*break*/, 17];
|
|
352
|
+
case 16:
|
|
353
|
+
error_1 = _e.sent();
|
|
354
|
+
cleanup();
|
|
355
|
+
throw error_1;
|
|
356
|
+
case 17: return [3 /*break*/, 19];
|
|
357
|
+
case 18:
|
|
358
|
+
console.log("\nTo set up pnpm, run the following commands:\n");
|
|
359
|
+
console.log(chalk.gray(" $ cd ".concat(targetRoot)));
|
|
360
|
+
console.log(chalk.gray(" $ pnpm install"));
|
|
361
|
+
_e.label = 19;
|
|
362
|
+
case 19:
|
|
363
|
+
isDatabase = true;
|
|
364
|
+
dockerOption = parseYesNo(argv.docker);
|
|
365
|
+
if (!(argv["skip-docker"] || dockerOption === false)) return [3 /*break*/, 20];
|
|
366
|
+
// --skip-docker 또는 --docker n 옵션으로 스킵
|
|
367
|
+
isDatabase = false;
|
|
368
|
+
return [3 /*break*/, 23];
|
|
369
|
+
case 20:
|
|
370
|
+
if (!(dockerOption === true || useDefaults)) return [3 /*break*/, 21];
|
|
371
|
+
// --docker y 또는 --yes 옵션이면 자동 진행
|
|
372
|
+
isDatabase = true;
|
|
373
|
+
return [3 /*break*/, 23];
|
|
374
|
+
case 21: return [4 /*yield*/, prompts({
|
|
375
|
+
type: "confirm",
|
|
376
|
+
name: "isDatabase",
|
|
377
|
+
message: "Would you like to set up a database using Docker?",
|
|
378
|
+
initial: true,
|
|
379
|
+
}, {
|
|
380
|
+
onCancel: createCancelHandler(),
|
|
381
|
+
})];
|
|
382
|
+
case 22:
|
|
383
|
+
result_3 = _e.sent();
|
|
384
|
+
isDatabase = result_3.isDatabase;
|
|
385
|
+
_e.label = 23;
|
|
386
|
+
case 23:
|
|
387
|
+
if (!isDatabase) return [3 /*break*/, 28];
|
|
388
|
+
console.log("\nSetting up a database using Docker...");
|
|
389
|
+
useDbDefaults = useDefaults || dockerOption === true;
|
|
390
|
+
answers = void 0;
|
|
391
|
+
_e.label = 24;
|
|
392
|
+
case 24:
|
|
393
|
+
_e.trys.push([24, 26, , 27]);
|
|
394
|
+
return [4 /*yield*/, promptDatabase(targetDir, argv, useDbDefaults)];
|
|
395
|
+
case 25:
|
|
396
|
+
answers = _e.sent();
|
|
397
|
+
return [3 /*break*/, 27];
|
|
398
|
+
case 26:
|
|
399
|
+
error_2 = _e.sent();
|
|
400
|
+
cleanup();
|
|
401
|
+
throw error_2;
|
|
402
|
+
case 27:
|
|
403
|
+
env = "# Database Configuration\nDB_HOST=0.0.0.0\nDB_PORT=5432\nDB_USER=".concat((_d = answers.DB_USER) !== null && _d !== void 0 ? _d : "postgres", "\nDB_PASSWORD=").concat(answers.DB_PASSWORD, "\nCONTAINER_NAME=").concat(answers.CONTAINER_NAME, "\nDATABASE_NAME=").concat(answers.DATABASE_NAME, "\nPROJECT_NAME=").concat(targetDir, "\n");
|
|
404
|
+
fs.writeFileSync(path.join(targetRoot, "packages", "api", ".env"), env);
|
|
405
|
+
initSqlPath = path.join(targetRoot, "packages", "api", "database", "fixtures", "init.sql");
|
|
406
|
+
if (fs.existsSync(initSqlPath)) {
|
|
407
|
+
initSql = fs.readFileSync(initSqlPath, "utf-8");
|
|
408
|
+
initSql = initSql.replace(/\$\{DATABASE_NAME\}/g, answers.DATABASE_NAME);
|
|
409
|
+
fs.writeFileSync(initSqlPath, initSql);
|
|
410
|
+
}
|
|
411
|
+
return [3 /*break*/, 29];
|
|
412
|
+
case 28:
|
|
413
|
+
console.log("\nTo set up a database using Docker, run the following commands:\n");
|
|
414
|
+
console.log(chalk.gray(" $ cd ".concat(targetRoot, "/packages/api/database")));
|
|
415
|
+
console.log(chalk.gray(" $ docker compose -p ".concat(targetDir, " up -d")));
|
|
416
|
+
console.log("\nOr use your preferred database management tool.");
|
|
417
|
+
_e.label = 29;
|
|
418
|
+
case 29:
|
|
419
|
+
// 성공적으로 완료되면 cleanup 방지
|
|
420
|
+
createdTargetRoot = null;
|
|
421
|
+
return [2 /*return*/, targetRoot];
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
function executeCommand(command_1, args_1, cwd_1) {
|
|
427
|
+
return __awaiter(this, arguments, void 0, function (command, args, cwd, options) {
|
|
428
|
+
var _a, showOutput, child, spinner, startTime, success, output, errorOutput;
|
|
429
|
+
if (options === void 0) { options = {}; }
|
|
430
|
+
return __generator(this, function (_b) {
|
|
431
|
+
_a = options.showOutput, showOutput = _a === void 0 ? false : _a;
|
|
432
|
+
child = spawn(command, args, {
|
|
433
|
+
cwd: cwd,
|
|
434
|
+
stdio: ["inherit", "pipe", "pipe"], // stdin은 상속, stdout/stderr는 pipe로 처리
|
|
435
|
+
env: __assign({}, process.env), // 환경변수 상속
|
|
436
|
+
});
|
|
437
|
+
spinner = ora("Running ".concat(command, " ").concat(args.join(" ")));
|
|
438
|
+
success = true;
|
|
439
|
+
output = "";
|
|
440
|
+
errorOutput = "";
|
|
441
|
+
return [2 /*return*/, new Promise(function (resolve, reject) {
|
|
442
|
+
var _a, _b;
|
|
443
|
+
child.on("spawn", function () {
|
|
444
|
+
spinner.start();
|
|
445
|
+
startTime = Date.now();
|
|
446
|
+
});
|
|
447
|
+
(_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on("data", function (data) {
|
|
448
|
+
output += data.toString();
|
|
449
|
+
});
|
|
450
|
+
(_b = child.stderr) === null || _b === void 0 ? void 0 : _b.on("data", function (data) {
|
|
451
|
+
errorOutput += data.toString();
|
|
452
|
+
});
|
|
453
|
+
child.on("error", function (error) {
|
|
454
|
+
success = false;
|
|
455
|
+
spinner.fail("".concat(command, " ").concat(args.join(" ")));
|
|
456
|
+
console.error(chalk.red("\uD83D\uDEA8 Error: ".concat(command)));
|
|
457
|
+
console.error(error);
|
|
458
|
+
reject(error);
|
|
459
|
+
});
|
|
460
|
+
child.on("close", function (code) {
|
|
461
|
+
if (!success || code !== 0) {
|
|
462
|
+
if (code !== 0) {
|
|
463
|
+
spinner.fail("".concat(command, " ").concat(args.join(" ")));
|
|
464
|
+
console.error(chalk.red("Command failed with exit code ".concat(code, ": ").concat(command, " ").concat(args.join(" "))));
|
|
465
|
+
if (errorOutput) {
|
|
466
|
+
console.error(errorOutput);
|
|
467
|
+
}
|
|
468
|
+
reject(new Error("Command failed with exit code ".concat(code)));
|
|
469
|
+
}
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
var durationS = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
473
|
+
if (showOutput && output.trim()) {
|
|
474
|
+
spinner.succeed("".concat(command, " ").concat(args.join(" "), " ").concat(chalk.dim("".concat(durationS, "s"))));
|
|
475
|
+
console.log(chalk.cyan(output.trim()));
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
spinner.succeed("".concat(command, " ").concat(args.join(" "), " ").concat(chalk.dim("".concat(durationS, "s"))));
|
|
479
|
+
}
|
|
480
|
+
resolve("");
|
|
481
|
+
});
|
|
482
|
+
})];
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
function setupPnpm(projectName, dir) {
|
|
487
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
488
|
+
var cwd, error_3;
|
|
489
|
+
return __generator(this, function (_a) {
|
|
490
|
+
switch (_a.label) {
|
|
491
|
+
case 0:
|
|
492
|
+
cwd = dir ? path.resolve(projectName, dir) : path.resolve(projectName);
|
|
493
|
+
_a.label = 1;
|
|
494
|
+
case 1:
|
|
495
|
+
_a.trys.push([1, 3, , 4]);
|
|
496
|
+
console.log(chalk.blue("Setting up pnpm in ".concat(cwd, "...")));
|
|
497
|
+
return [4 /*yield*/, executeCommand("pnpm", ["install"], cwd)];
|
|
498
|
+
case 2:
|
|
499
|
+
_a.sent();
|
|
500
|
+
console.log(chalk.green("\u2705 pnpm has been set up in ".concat(cwd, "\n")));
|
|
501
|
+
return [3 /*break*/, 4];
|
|
502
|
+
case 3:
|
|
503
|
+
error_3 = _a.sent();
|
|
504
|
+
console.error(chalk.red("\u274C Failed to set up pnpm in ".concat(cwd)));
|
|
505
|
+
console.error(error_3);
|
|
506
|
+
throw error_3;
|
|
507
|
+
case 4: return [2 /*return*/];
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
function promptDatabase(projectName, argv, useDefaults) {
|
|
513
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
514
|
+
var dockerProject, dbUser, containerName, databaseName, dbPassword, answers;
|
|
515
|
+
return __generator(this, function (_a) {
|
|
516
|
+
switch (_a.label) {
|
|
517
|
+
case 0:
|
|
518
|
+
dockerProject = argv["docker-project"];
|
|
519
|
+
dbUser = argv["db-user"];
|
|
520
|
+
containerName = argv["container-name"];
|
|
521
|
+
databaseName = argv["db-name"];
|
|
522
|
+
dbPassword = argv["db-password"];
|
|
523
|
+
// 모든 값이 제공되었으면 프롬프트 스킵
|
|
524
|
+
if (dockerProject && dbUser && containerName && databaseName && dbPassword) {
|
|
525
|
+
return [2 /*return*/, {
|
|
526
|
+
DOCKER_PROJECT_NAME: dockerProject,
|
|
527
|
+
DB_USER: dbUser,
|
|
528
|
+
CONTAINER_NAME: containerName,
|
|
529
|
+
DATABASE_NAME: databaseName,
|
|
530
|
+
DB_PASSWORD: dbPassword,
|
|
531
|
+
}];
|
|
532
|
+
}
|
|
533
|
+
// --yes 옵션이면 기본값 사용
|
|
534
|
+
if (useDefaults) {
|
|
535
|
+
return [2 /*return*/, {
|
|
536
|
+
DOCKER_PROJECT_NAME: dockerProject || "".concat(projectName, "-docker"),
|
|
537
|
+
DB_USER: dbUser || "postgres",
|
|
538
|
+
CONTAINER_NAME: containerName || "".concat(projectName, "-container"),
|
|
539
|
+
DATABASE_NAME: databaseName || projectName,
|
|
540
|
+
DB_PASSWORD: dbPassword || "1234",
|
|
541
|
+
}];
|
|
542
|
+
}
|
|
543
|
+
return [4 /*yield*/, prompts([
|
|
544
|
+
{
|
|
545
|
+
type: dockerProject ? null : "text",
|
|
546
|
+
name: "DOCKER_PROJECT_NAME",
|
|
547
|
+
message: "Enter the Docker project name:",
|
|
548
|
+
initial: dockerProject || "".concat(projectName, "-docker"),
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
type: dbUser ? null : "text",
|
|
552
|
+
name: "DB_USER",
|
|
553
|
+
message: "Enter the database user: ",
|
|
554
|
+
initial: dbUser || "postgres",
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
type: containerName ? null : "text",
|
|
558
|
+
name: "CONTAINER_NAME",
|
|
559
|
+
message: "Enter the container name: ",
|
|
560
|
+
initial: containerName || "".concat(projectName, "-container"),
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
type: databaseName ? null : "text",
|
|
564
|
+
name: "DATABASE_NAME",
|
|
565
|
+
message: "Enter the database name: ",
|
|
566
|
+
initial: databaseName || projectName,
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
type: dbPassword ? null : "password",
|
|
570
|
+
name: "DB_PASSWORD",
|
|
571
|
+
message: "Enter the database password: ",
|
|
572
|
+
initial: dbPassword || "",
|
|
573
|
+
},
|
|
574
|
+
], {
|
|
575
|
+
onCancel: createCancelHandler(),
|
|
576
|
+
})];
|
|
577
|
+
case 1:
|
|
578
|
+
answers = _a.sent();
|
|
579
|
+
return [2 /*return*/, {
|
|
580
|
+
DOCKER_PROJECT_NAME: dockerProject || answers.DOCKER_PROJECT_NAME,
|
|
581
|
+
DB_USER: dbUser || answers.DB_USER,
|
|
582
|
+
CONTAINER_NAME: containerName || answers.CONTAINER_NAME,
|
|
583
|
+
DATABASE_NAME: databaseName || answers.DATABASE_NAME,
|
|
584
|
+
DB_PASSWORD: dbPassword || answers.DB_PASSWORD,
|
|
585
|
+
}];
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
function createCancelHandler() {
|
|
591
|
+
return function () {
|
|
592
|
+
cleanup();
|
|
593
|
+
throw new Error("Operation cancelled.");
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
function removeDirectory(dirPath) {
|
|
597
|
+
if (!fs.existsSync(dirPath)) {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
try {
|
|
601
|
+
fs.rmSync(dirPath, { recursive: true, force: true });
|
|
602
|
+
}
|
|
603
|
+
catch (_error) {
|
|
604
|
+
console.error(chalk.yellow("Warning: Failed to remove ".concat(dirPath)));
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
function cleanup() {
|
|
608
|
+
if (isCleaningUp || !createdTargetRoot) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
isCleaningUp = true;
|
|
612
|
+
console.log(chalk.yellow("\n\n Operation cancelled. Cleaning up created files...\n"));
|
|
613
|
+
try {
|
|
614
|
+
if (fs.existsSync(createdTargetRoot)) {
|
|
615
|
+
removeDirectory(createdTargetRoot);
|
|
616
|
+
console.log(chalk.green("Cleaned up ".concat(createdTargetRoot, "\n")));
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
catch (error) {
|
|
620
|
+
console.error(chalk.red("Failed to clean up: ".concat(error)));
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
init()
|
|
624
|
+
.then(function (createdTarget) {
|
|
625
|
+
console.log(chalk.green("\nProject created successfully!\n"));
|
|
626
|
+
console.log(chalk.green("project was created in ".concat(chalk.blue(createdTarget), "\n")));
|
|
627
|
+
})
|
|
628
|
+
.catch(function (e) {
|
|
629
|
+
cleanup();
|
|
630
|
+
console.error(e);
|
|
631
|
+
process.exit(1);
|
|
632
|
+
});
|