@juho0719/cckit 0.2.4 → 0.2.5

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.
@@ -0,0 +1,449 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ source "$SCRIPT_DIR/common.sh"
6
+
7
+ # ── Defaults ──────────────────────────────────────────────────────────────────
8
+ PM="bun"
9
+ PACKAGES=()
10
+
11
+ # ── Usage ─────────────────────────────────────────────────────────────────────
12
+ usage() {
13
+ error "사용법: bash monorepo.sh <project-name> [--packages pkg1 pkg2 ...] [--pm bun|npm|pnpm]"
14
+ exit 1
15
+ }
16
+
17
+ if [ $# -lt 1 ]; then
18
+ usage
19
+ fi
20
+
21
+ PROJECT_ARG="$1"
22
+ shift
23
+
24
+ # ── Flag parsing ───────────────────────────────────────────────────────────────
25
+ while [ $# -gt 0 ]; do
26
+ case "$1" in
27
+ --pm)
28
+ PM="$2"; shift 2 ;;
29
+ --packages)
30
+ shift
31
+ while [ $# -gt 0 ] && [[ "$1" != --* ]]; do
32
+ PACKAGES+=("$1")
33
+ shift
34
+ done
35
+ ;;
36
+ *)
37
+ error "알 수 없는 옵션: $1"; usage ;;
38
+ esac
39
+ done
40
+
41
+ # ── Package manager helpers ────────────────────────────────────────────────────
42
+ case "$PM" in
43
+ bun)
44
+ PKG_INIT="bun init -y"
45
+ PKG_EXEC="bun -e"
46
+ PKG_INSTALL="bun install"
47
+ PKG_RUN="bun run"
48
+ ;;
49
+ npm)
50
+ PKG_INIT="npm init -y"
51
+ PKG_EXEC="node -e"
52
+ PKG_INSTALL="npm install"
53
+ PKG_RUN="npm run"
54
+ ;;
55
+ pnpm)
56
+ PKG_INIT="pnpm init"
57
+ PKG_EXEC="node -e"
58
+ PKG_INSTALL="pnpm install"
59
+ PKG_RUN="pnpm run"
60
+ ;;
61
+ *)
62
+ error "지원하지 않는 패키지 매니저: $PM (bun|npm|pnpm)"; exit 1 ;;
63
+ esac
64
+
65
+ # ── Prerequisite check ─────────────────────────────────────────────────────────
66
+ step "사전 조건 확인"
67
+ check_command "$PM" "https://bun.sh / https://npmjs.com / https://pnpm.io"
68
+ check_command git "https://git-scm.com"
69
+
70
+ # ── Resolve target directory ───────────────────────────────────────────────────
71
+ resolve_target_dir "$PROJECT_ARG"
72
+
73
+ cd "$TARGET_DIR"
74
+
75
+ ESCAPED_NAME=$(echo "$PROJECT_NAME" | sed "s/'/\\\\'/g")
76
+
77
+ # ── Root init ─────────────────────────────────────────────────────────────────
78
+ step "${PM} init — 루트 프로젝트 초기화"
79
+ $PKG_INIT 2>&1 | head -10
80
+ rm -f index.ts README.md
81
+ rm -rf .cursor
82
+ success "${PM} init 완료"
83
+
84
+ # ── Root package.json ─────────────────────────────────────────────────────────
85
+ step "루트 package.json — 모노레포 + Turborepo 설정"
86
+
87
+ if [ "$PM" = "bun" ]; then
88
+ PM_VERSION=$(bun --version)
89
+ PKG_MANAGER_FIELD="bun@${PM_VERSION}"
90
+ elif [ "$PM" = "npm" ]; then
91
+ PM_VERSION=$(npm --version)
92
+ PKG_MANAGER_FIELD="npm@${PM_VERSION}"
93
+ elif [ "$PM" = "pnpm" ]; then
94
+ PM_VERSION=$(pnpm --version)
95
+ PKG_MANAGER_FIELD="pnpm@${PM_VERSION}"
96
+ fi
97
+
98
+ # Determine workspace syntax based on PM
99
+ if [ "$PM" = "pnpm" ]; then
100
+ # pnpm uses pnpm-workspace.yaml
101
+ cat > pnpm-workspace.yaml <<'EOF'
102
+ packages:
103
+ - 'packages/*'
104
+ EOF
105
+ WORKSPACE_FIELD="null"
106
+ else
107
+ WORKSPACE_FIELD='["packages/*"]'
108
+ fi
109
+
110
+ $PKG_EXEC "
111
+ const fs = require('fs');
112
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
113
+ pkg.name = '${ESCAPED_NAME}';
114
+ pkg.private = true;
115
+ if ('${WORKSPACE_FIELD}' !== 'null') {
116
+ pkg.workspaces = ${WORKSPACE_FIELD};
117
+ }
118
+ pkg.packageManager = '${PKG_MANAGER_FIELD}';
119
+ delete pkg.module;
120
+ delete pkg.peerDependencies;
121
+ pkg.scripts = {
122
+ build: 'turbo run build',
123
+ dev: 'turbo run dev',
124
+ lint: 'turbo run lint',
125
+ 'check-types': 'turbo run check-types',
126
+ test: 'turbo run test',
127
+ clean: 'turbo run clean',
128
+ format: 'prettier --write \"**/*.{ts,tsx,js,json,md}\"',
129
+ 'format:check': 'prettier --check \"**/*.{ts,tsx,js,json,md}\"',
130
+ };
131
+ pkg.devDependencies = {
132
+ 'turbo': '^2.5.3',
133
+ 'prettier': '^3.5.2',
134
+ ...(pkg.devDependencies || {}),
135
+ };
136
+ fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
137
+ "
138
+ success "package.json 업데이트 완료"
139
+
140
+ # ── turbo.json ────────────────────────────────────────────────────────────────
141
+ step "turbo.json 생성"
142
+ cat > turbo.json <<'EOF'
143
+ {
144
+ "$schema": "https://turborepo.dev/schema.json",
145
+ "ui": "tui",
146
+ "tasks": {
147
+ "build": {
148
+ "dependsOn": ["^build"],
149
+ "inputs": ["$TURBO_DEFAULT$"],
150
+ "outputs": ["dist/**"]
151
+ },
152
+ "dev": {
153
+ "dependsOn": ["^build"],
154
+ "cache": false,
155
+ "persistent": true
156
+ },
157
+ "lint": {
158
+ "dependsOn": ["^lint"]
159
+ },
160
+ "check-types": {
161
+ "dependsOn": ["^check-types"]
162
+ },
163
+ "test": {
164
+ "dependsOn": ["^build"],
165
+ "inputs": ["$TURBO_DEFAULT$", "tests/**"],
166
+ "outputs": []
167
+ },
168
+ "clean": {
169
+ "cache": false
170
+ }
171
+ }
172
+ }
173
+ EOF
174
+ success "turbo.json 생성 완료"
175
+
176
+ # ── typescript-config ─────────────────────────────────────────────────────────
177
+ step "packages/typescript-config — 공유 tsconfig"
178
+ TS_CONFIG_DIR="packages/typescript-config"
179
+ mkdir -p "$TS_CONFIG_DIR"
180
+ (cd "$TS_CONFIG_DIR" && $PKG_INIT) >/dev/null 2>&1
181
+ rm -f "$TS_CONFIG_DIR/index.ts" "$TS_CONFIG_DIR/README.md" "$TS_CONFIG_DIR/tsconfig.json"
182
+ rm -rf "$TS_CONFIG_DIR/.cursor"
183
+
184
+ $PKG_EXEC "
185
+ const fs = require('fs');
186
+ const pkg = JSON.parse(fs.readFileSync('${TS_CONFIG_DIR}/package.json', 'utf8'));
187
+ pkg.name = '@${ESCAPED_NAME}/typescript-config';
188
+ pkg.version = '0.0.0';
189
+ pkg.private = true;
190
+ pkg.files = ['base.json', 'library.json'];
191
+ delete pkg.module;
192
+ delete pkg.type;
193
+ delete pkg.peerDependencies;
194
+ delete pkg.devDependencies;
195
+ fs.writeFileSync('${TS_CONFIG_DIR}/package.json', JSON.stringify(pkg, null, 2) + '\n');
196
+ "
197
+
198
+ cat > "$TS_CONFIG_DIR/base.json" <<'EOF'
199
+ {
200
+ "$schema": "https://json.schemastore.org/tsconfig",
201
+ "compilerOptions": {
202
+ "target": "ES2022",
203
+ "module": "ESNext",
204
+ "moduleResolution": "bundler",
205
+ "lib": ["ES2022"],
206
+ "strict": true,
207
+ "noUncheckedIndexedAccess": true,
208
+ "noImplicitReturns": true,
209
+ "noFallthroughCasesInSwitch": true,
210
+ "verbatimModuleSyntax": true,
211
+ "declaration": true,
212
+ "declarationMap": true,
213
+ "sourceMap": true,
214
+ "esModuleInterop": true,
215
+ "skipLibCheck": true,
216
+ "isolatedModules": true
217
+ },
218
+ "exclude": ["node_modules", "dist"]
219
+ }
220
+ EOF
221
+
222
+ cat > "$TS_CONFIG_DIR/library.json" <<'EOF'
223
+ {
224
+ "$schema": "https://json.schemastore.org/tsconfig",
225
+ "extends": "./base.json",
226
+ "compilerOptions": {
227
+ "outDir": "./dist",
228
+ "rootDir": "./src"
229
+ }
230
+ }
231
+ EOF
232
+ success "typescript-config 생성 완료"
233
+
234
+ # ── eslint-config ─────────────────────────────────────────────────────────────
235
+ step "packages/eslint-config — 공유 ESLint 설정"
236
+ ESLINT_CONFIG_DIR="packages/eslint-config"
237
+ mkdir -p "$ESLINT_CONFIG_DIR"
238
+ (cd "$ESLINT_CONFIG_DIR" && $PKG_INIT) >/dev/null 2>&1
239
+ rm -f "$ESLINT_CONFIG_DIR/index.ts" "$ESLINT_CONFIG_DIR/README.md" "$ESLINT_CONFIG_DIR/tsconfig.json"
240
+ rm -rf "$ESLINT_CONFIG_DIR/.cursor"
241
+
242
+ $PKG_EXEC "
243
+ const fs = require('fs');
244
+ const pkg = JSON.parse(fs.readFileSync('${ESLINT_CONFIG_DIR}/package.json', 'utf8'));
245
+ pkg.name = '@${ESCAPED_NAME}/eslint-config';
246
+ pkg.version = '0.0.0';
247
+ pkg.private = true;
248
+ pkg.type = 'module';
249
+ pkg.files = ['base.js'];
250
+ delete pkg.module;
251
+ delete pkg.peerDependencies;
252
+ pkg.dependencies = {
253
+ '@typescript-eslint/eslint-plugin': '^8.24.1',
254
+ '@typescript-eslint/parser': '^8.24.1',
255
+ 'eslint': '^9.20.1',
256
+ };
257
+ pkg.devDependencies = {};
258
+ fs.writeFileSync('${ESLINT_CONFIG_DIR}/package.json', JSON.stringify(pkg, null, 2) + '\n');
259
+ "
260
+
261
+ cat > "$ESLINT_CONFIG_DIR/base.js" <<'EOF'
262
+ import tsParser from "@typescript-eslint/parser";
263
+ import tsPlugin from "@typescript-eslint/eslint-plugin";
264
+
265
+ export default [
266
+ {
267
+ ignores: ["**/dist/**", "**/node_modules/**", "**/.turbo/**"],
268
+ },
269
+ {
270
+ files: ["**/*.ts", "**/*.tsx"],
271
+ languageOptions: {
272
+ parser: tsParser,
273
+ parserOptions: {
274
+ projectService: true,
275
+ },
276
+ },
277
+ plugins: {
278
+ "@typescript-eslint": tsPlugin,
279
+ },
280
+ rules: {
281
+ ...tsPlugin.configs["recommended"].rules,
282
+ "@typescript-eslint/no-unused-vars": [
283
+ "error",
284
+ { argsIgnorePattern: "^_" },
285
+ ],
286
+ "@typescript-eslint/no-explicit-any": "error",
287
+ "@typescript-eslint/consistent-type-imports": [
288
+ "error",
289
+ { prefer: "type-imports" },
290
+ ],
291
+ },
292
+ },
293
+ ];
294
+ EOF
295
+ success "eslint-config 생성 완료"
296
+
297
+ # ── Prettier ──────────────────────────────────────────────────────────────────
298
+ step "Prettier 설정"
299
+ cat > .prettierrc <<'EOF'
300
+ {
301
+ "semi": false,
302
+ "singleQuote": true,
303
+ "tabWidth": 2,
304
+ "trailingComma": "all",
305
+ "printWidth": 100,
306
+ "arrowParens": "always"
307
+ }
308
+ EOF
309
+
310
+ cat > .prettierignore <<'EOF'
311
+ node_modules
312
+ dist
313
+ .turbo
314
+ *.lock
315
+ bun.lock
316
+ EOF
317
+ success "Prettier 설정 완료"
318
+
319
+ # ── .gitignore ────────────────────────────────────────────────────────────────
320
+ step ".gitignore 보강"
321
+ cat >> .gitignore <<'EOF'
322
+
323
+ # Turborepo
324
+ .turbo/
325
+
326
+ # Build
327
+ dist/
328
+ *.tsbuildinfo
329
+
330
+ # Environment
331
+ .env
332
+ .env.local
333
+ .env.*.local
334
+
335
+ # Editor
336
+ .vscode/
337
+ .idea/
338
+ .cursor/
339
+ EOF
340
+ success ".gitignore 업데이트 완료"
341
+
342
+ # ── Extra packages ────────────────────────────────────────────────────────────
343
+ if [ ${#PACKAGES[@]} -gt 0 ]; then
344
+ step "패키지 스캐폴딩: ${PACKAGES[*]}"
345
+ for PKG in "${PACKAGES[@]}"; do
346
+ PKG_DIR="packages/$PKG"
347
+ info "패키지 생성: $PKG"
348
+ mkdir -p "$PKG_DIR/src"
349
+ (cd "$PKG_DIR" && $PKG_INIT) >/dev/null 2>&1
350
+ rm -f "$PKG_DIR/index.ts" "$PKG_DIR/README.md"
351
+ rm -rf "$PKG_DIR/.cursor"
352
+
353
+ if [ "$PM" = "bun" ]; then
354
+ BUILD_SCRIPT="bun build ./src/index.ts --outdir ./dist --target bun --format esm --sourcemap=external"
355
+ DEV_SCRIPT="bun --watch ./src/index.ts"
356
+ TEST_SCRIPT="bun test"
357
+ TYPES_DEP="'@types/bun': 'latest',"
358
+ else
359
+ BUILD_SCRIPT="tsc"
360
+ DEV_SCRIPT="tsc --watch"
361
+ TEST_SCRIPT="echo \"no test specified\" && exit 0"
362
+ TYPES_DEP=""
363
+ fi
364
+
365
+ $PKG_EXEC "
366
+ const fs = require('fs');
367
+ const pkg = JSON.parse(fs.readFileSync('${PKG_DIR}/package.json', 'utf8'));
368
+ pkg.name = '@${ESCAPED_NAME}/${PKG}';
369
+ pkg.version = '0.0.0';
370
+ pkg.private = true;
371
+ pkg.type = 'module';
372
+ pkg.main = './dist/index.js';
373
+ pkg.types = './dist/index.d.ts';
374
+ delete pkg.module;
375
+ delete pkg.peerDependencies;
376
+ pkg.scripts = {
377
+ build: '${BUILD_SCRIPT}',
378
+ dev: '${DEV_SCRIPT}',
379
+ 'check-types': 'tsc --noEmit',
380
+ lint: 'eslint ./src',
381
+ test: '${TEST_SCRIPT}',
382
+ clean: 'rm -rf dist',
383
+ };
384
+ pkg.devDependencies = {
385
+ '@${ESCAPED_NAME}/typescript-config': 'workspace:*',
386
+ '@${ESCAPED_NAME}/eslint-config': 'workspace:*',
387
+ ${TYPES_DEP}
388
+ };
389
+ fs.writeFileSync('${PKG_DIR}/package.json', JSON.stringify(pkg, null, 2) + '\n');
390
+ "
391
+
392
+ cat > "$PKG_DIR/tsconfig.json" <<TSCONFIG_EOF
393
+ {
394
+ "extends": "@${PROJECT_NAME}/typescript-config/library.json",
395
+ "compilerOptions": {
396
+ "outDir": "./dist",
397
+ "rootDir": "./src",
398
+ "types": ["bun-types"]
399
+ },
400
+ "include": ["src/**/*.ts"],
401
+ "exclude": ["node_modules", "dist"]
402
+ }
403
+ TSCONFIG_EOF
404
+
405
+ cat > "$PKG_DIR/eslint.config.mjs" <<ESLINT_PKG_EOF
406
+ import baseConfig from '@${PROJECT_NAME}/eslint-config/base.js'
407
+
408
+ export default [...baseConfig]
409
+ ESLINT_PKG_EOF
410
+
411
+ cat > "$PKG_DIR/src/index.ts" <<'SRC_EOF'
412
+ export {}
413
+ SRC_EOF
414
+
415
+ success " @${PROJECT_NAME}/${PKG} 생성 완료"
416
+ done
417
+ else
418
+ warn "추가 패키지 없음 — 나중에 수동으로 추가하세요"
419
+ fi
420
+
421
+ # ── Install dependencies ───────────────────────────────────────────────────────
422
+ step "의존성 설치 (${PM} install)"
423
+ $PKG_INSTALL 2>&1 | tail -5
424
+ success "의존성 설치 완료"
425
+
426
+ # ── Git commit ────────────────────────────────────────────────────────────────
427
+ step "Git 초기화 및 커밋"
428
+ git_init_commit "chore: initial monorepo setup (${PM} + TypeScript + Turborepo)"
429
+
430
+ # ── Summary ───────────────────────────────────────────────────────────────────
431
+ echo ""
432
+ echo -e "${GREEN}${BOLD}✓ 모노레포 초기화 완료: ${PROJECT_NAME}${RESET}"
433
+ echo ""
434
+ echo -e " ${BOLD}구조:${RESET}"
435
+ echo -e " packages/"
436
+ echo -e " typescript-config/ — 공유 tsconfig"
437
+ echo -e " eslint-config/ — 공유 ESLint 설정"
438
+ for PKG in "${PACKAGES[@]:-}"; do
439
+ [ -n "$PKG" ] && echo -e " ${CYAN}${PKG}/${RESET} — @${PROJECT_NAME}/${PKG}"
440
+ done
441
+ echo ""
442
+ echo -e " ${BOLD}명령어:${RESET}"
443
+ echo -e " $PKG_RUN build — 전체 빌드"
444
+ echo -e " $PKG_RUN dev — 개발 모드"
445
+ echo -e " $PKG_RUN lint — 린트 검사"
446
+ echo -e " $PKG_RUN check-types — 타입 검사"
447
+ echo -e " $PKG_RUN test — 테스트 실행"
448
+ echo -e " $PKG_RUN format — 코드 포맷팅"
449
+ echo ""