create-express-esm 1.1.10 → 1.1.12

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 CHANGED
@@ -29,15 +29,13 @@
29
29
 
30
30
  ```bash
31
31
  npx create-express-esm
32
- npm install -g create-express-esm
33
- npm install create-express-esm
32
+ create-express-esm
34
33
  ```
35
34
 
36
35
  또는 전역으로 설치하여 사용할 수도 있습니다
37
36
 
38
37
  ```
39
38
  npm install -g create-express-esm
40
- create-express-esm
41
39
  ```
42
40
 
43
41
  ## 📂 Project Structure (폴더 구조)
package/bin/cli.js CHANGED
@@ -1,52 +1,69 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { input, select, confirm } from '@inquirer/prompts';
3
+ import * as p from '@clack/prompts';
4
4
  import fs from 'fs-extra';
5
5
  import path from 'path';
6
6
  import chalk from 'chalk';
7
7
  import { execSync } from 'child_process';
8
8
  import { fileURLToPath } from 'url';
9
9
 
10
+ // ESM 환경에서 __dirname 구현
10
11
  const __filename = fileURLToPath(import.meta.url);
11
12
  const __dirname = path.dirname(__filename);
12
13
 
13
14
  async function run() {
14
- console.log(chalk.blue.bold('\n🚀 Create Express ESM 시작!\n'));
15
+ console.clear();
16
+
17
+ // 1. 시작 인사 (Intro)
18
+ p.intro(`${chalk.bgBlue.white(' create-express-esm ')} ${chalk.dim('v1.1.9')}`);
15
19
 
16
20
  try {
17
- // 1. 사용자 질문
18
- const projectName = await input({
19
- message: '생성할 프로젝트 이름을 입력하세요:',
20
- default: 'my-app',
21
- });
22
-
23
- const language = await select({
24
- message: '사용할 언어를 선택하세요:',
25
- choices: [
26
- { name: 'JavaScript (ESM)', value: 'js' },
27
- { name: 'TypeScript', value: 'ts' },
28
- ],
29
- });
30
-
31
- const useTest = await confirm({
32
- message: 'Vitest 테스트 환경을 추가하시겠습니까?',
33
- default: true,
34
- });
21
+ // 2. 사용자 질문 그룹 (Group)
22
+ const project = await p.group(
23
+ {
24
+ projectName: () =>
25
+ p.text({
26
+ message: '프로젝트 이름을 입력하세요:',
27
+ placeholder: 'my-app',
28
+ validate: (value) => {
29
+ if (value.length === 0) return '프로젝트 이름은 필수입니다!';
30
+ if (fs.existsSync(path.join(process.cwd(), value))) return '해당 폴더가 이미 존재합니다.';
31
+ },
32
+ }),
33
+ language: () =>
34
+ p.select({
35
+ message: '사용할 언어를 선택하세요:',
36
+ options: [
37
+ { value: 'js', label: 'JavaScript (ESM)' },
38
+ { value: 'ts', label: 'TypeScript' },
39
+ ],
40
+ }),
41
+ useTest: () =>
42
+ p.confirm({
43
+ message: 'Vitest 테스트 환경을 추가하시겠습니까?',
44
+ initialValue: true,
45
+ }),
46
+ },
47
+ {
48
+ onCancel: () => {
49
+ p.cancel('프로젝트 생성이 취소되었습니다.');
50
+ process.exit(0);
51
+ },
52
+ }
53
+ );
35
54
 
55
+ const { projectName, language, useTest } = project;
36
56
  const targetPath = path.join(process.cwd(), projectName);
37
57
  const templatePath = path.join(__dirname, '../template', language);
38
58
 
39
- // 2. 폴더 존재 여부 확인
40
- if (fs.existsSync(targetPath)) {
41
- console.error(chalk.red(`\n❌ 오류: '${projectName}' 폴더가 이미 존재합니다.`));
42
- process.exit(1);
43
- }
59
+ // 3. 파일 구성 시작 (Spinner)
60
+ const s = p.spinner();
61
+ s.start('프로젝트 템플릿을 복사하는 중...');
44
62
 
45
- // 3. 기본 템플릿 복사
46
- console.log(chalk.cyan(`\n📂 [${language.toUpperCase()}] 템플릿 구성을 시작합니다...`));
63
+ // 템플릿 전체 복사 (Vitest 파일 포함)
47
64
  await fs.copy(templatePath, targetPath);
48
65
 
49
- // 4. 도트 파일 변환 (_env -> .env)
66
+ // 도트 파일 변환 (예: _env -> .env)
50
67
  const renameMap = {
51
68
  'gitignore': '.gitignore',
52
69
  '_gitignore': '.gitignore',
@@ -55,109 +72,74 @@ async function run() {
55
72
 
56
73
  for (const [oldName, newName] of Object.entries(renameMap)) {
57
74
  const oldFilePath = path.join(targetPath, oldName);
58
- const newFilePath = path.join(targetPath, newName);
59
75
  if (await fs.pathExists(oldFilePath)) {
60
- await fs.move(oldFilePath, newFilePath, { overwrite: true });
76
+ await fs.move(oldFilePath, path.join(targetPath, newName), { overwrite: true });
61
77
  if (newName === '.env') {
62
- await fs.copy(newFilePath, path.join(targetPath, '.env.example'));
78
+ await fs.copy(path.join(targetPath, '.env'), path.join(targetPath, '.env.example'));
63
79
  }
64
80
  }
65
81
  }
66
-
67
- // 5. package.json 동적 수정
82
+
83
+ // 4. package.json 동적 최적화
68
84
  const pkgPath = path.join(targetPath, 'package.json');
69
85
  const pkg = await fs.readJson(pkgPath);
70
86
  pkg.name = projectName;
71
87
 
72
- // [추가된 부분] TypeScript 환경에서 ESM 에러를 방지하기 위한 tsx 설정
88
+ // TypeScript 선택 실행 환경(tsx) 강제 설정
73
89
  if (language === 'ts') {
74
- console.log(chalk.yellow(`⚙️ TypeScript ESM 실행 환경(tsx)을 최적화하는 중...`));
75
-
76
- // ts-node 대신 tsx를 사용하여 .js 확장자 임포트 문제 해결
77
90
  pkg.scripts.dev = "nodemon --exec tsx src/server.ts";
78
-
79
- // 의존성 교체
80
- pkg.devDependencies = {
81
- ...pkg.devDependencies,
82
- "tsx": "^4.7.0"
83
- };
84
-
85
- // 기존에 ts-node가 있다면 제거 (중복 방지)
86
- delete pkg.devDependencies['ts-node'];
91
+ pkg.devDependencies["tsx"] = "^4.7.0";
92
+ // 구형 ts-node 제거
93
+ if (pkg.devDependencies["ts-node"]) delete pkg.devDependencies["ts-node"];
87
94
  }
88
95
 
89
- // Vitest 설정 (이슈 #3 구현 부분)
90
- if (useTest) {
91
- console.log(chalk.yellow(`🧪 Vitest 설정 샘플 테스트를 생성하는 중...`));
92
-
93
- pkg.scripts = {
94
- ...pkg.scripts,
95
- "test": "vitest",
96
- "test:ui": "vitest --ui",
97
- "test:run": "vitest run"
98
- };
99
-
100
- const testDeps = {
101
- "vitest": "^1.0.0",
102
- "supertest": "^6.3.3"
103
- };
104
-
105
- if (language === 'ts') {
106
- testDeps["@types/supertest"] = "^2.0.12";
107
- }
108
-
109
- pkg.devDependencies = {
110
- ...pkg.devDependencies,
111
- ...testDeps
112
- };
113
-
114
- // Vitest 설정 파일 생성
115
- const configExt = language === 'ts' ? 'ts' : 'js';
116
- const configContent = `import { defineConfig } from 'vitest/config';
117
-
118
- export default defineConfig({
119
- test: {
120
- globals: true,
121
- environment: 'node',
122
- },
123
- });`;
124
- await fs.writeFile(path.join(targetPath, `vitest.config.${configExt}`), configContent);
125
-
126
- // 샘플 테스트 파일 생성
127
- const testFileExt = language === 'ts' ? 'ts' : 'js';
128
- const testContent = `import { describe, it, expect } from 'vitest';
129
- import request from 'supertest';
130
- import app from './app.js';
131
-
132
- describe('API Health Check Test', () => {
133
- it('GET / 요청이 성공해야 한다', async () => {
134
- const res = await request(app).get('/');
135
- expect(res.status).toBe(200);
136
- expect(res.text).toContain('Server is Running');
137
- });
138
- });`;
139
- await fs.writeFile(path.join(targetPath, `src/app.test.${testFileExt}`), testContent);
96
+ // 테스트 환경 사용 여부에 따른 처리
97
+ const configExt = language === 'ts' ? 'ts' : 'js';
98
+ const testFileExt = language === 'ts' ? 'ts' : 'js';
99
+
100
+ if (!useTest) {
101
+ // 사용자가 원치 않으면 복사된 테스트 파일 삭제
102
+ await fs.remove(path.join(targetPath, `vitest.config.${configExt}`));
103
+ await fs.remove(path.join(targetPath, `src/app.test.${testFileExt}`));
104
+
105
+ // package.json에서 관련 설정 제거
106
+ delete pkg.scripts.test;
107
+ delete pkg.scripts["test:ui"];
108
+ delete pkg.scripts["test:run"];
109
+ delete pkg.devDependencies.vitest;
110
+ delete pkg.devDependencies.supertest;
111
+ if (pkg.devDependencies["@types/supertest"]) delete pkg.devDependencies["@types/supertest"];
112
+ } else {
113
+ // 사용자가 원하면 스크립트가 확실히 있는지 보장
114
+ pkg.scripts.test = "vitest";
115
+ pkg.scripts["test:ui"] = "vitest --ui";
140
116
  }
141
117
 
142
118
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
143
- console.log(chalk.green(`✅ 모든 구성 완료!`));
119
+ s.stop('파일 구성 완료!');
120
+
121
+ // 5. 의존성 설치 (Spinner)
122
+ const installSpinner = p.spinner();
123
+ installSpinner.start('의존성 패키지를 설치하는 중... (npm install)');
124
+
125
+ try {
126
+ execSync('npm install', { cwd: targetPath, stdio: 'ignore' });
127
+ installSpinner.stop('설치 완료!');
128
+ } catch (e) {
129
+ installSpinner.stop(chalk.red('설치 실패 (수동 설치가 필요할 수 있습니다)'));
130
+ }
144
131
 
145
- // 6. 패키지 자동 설치
146
- console.log(chalk.yellow(`\n📦 의존성 패키지를 설치합니다... (npm install)`));
147
- execSync('npm install', { cwd: targetPath, stdio: 'inherit' });
132
+ // 6. 마무리 (Note & Outro)
133
+ p.note(
134
+ chalk.cyan(`cd ${projectName}\n${useTest ? 'npm test\n' : ''}npm run dev`),
135
+ '시작하려면 다음 명령어를 입력하세요'
136
+ );
148
137
 
149
- console.log(chalk.green(`\n프로젝트 생성 성공!`));
150
- console.log(chalk.white(`\n다음 명령어를 입력해 보세요:\n`));
151
- console.log(chalk.cyan(` cd ${projectName}`));
152
- if (useTest) console.log(chalk.cyan(` npm test`));
153
- console.log(chalk.cyan(` npm run dev\n`));
138
+ p.outro(chalk.green('모든 준비가 끝났습니다. 즐거운 개발 되세요!'));
154
139
 
155
140
  } catch (error) {
156
- if (error.name === 'ExitPromptError') { // 오타 수정: ExitPnromptError -> ExitPromptError
157
- console.log(chalk.yellow('\n\n👋 설치를 중단했습니다.'));
158
- } else {
159
- console.error(chalk.red('\n❌ 오류 발생:'), error);
160
- }
141
+ p.cancel(`오류 발생: ${error.message}`);
142
+ process.exit(1);
161
143
  }
162
144
  }
163
145
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-express-esm",
3
- "version": "1.1.10",
3
+ "version": "1.1.12",
4
4
  "description": "A modern CLI tool to bootstrap Express.js applications with ES Modules and Layered Architecture.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -25,7 +25,7 @@
25
25
  "author": "munjuin",
26
26
  "license": "MIT",
27
27
  "dependencies": {
28
- "@inquirer/prompts": "^8.1.0",
28
+ "@clack/prompts": "^0.11.0",
29
29
  "chalk": "^5.6.2",
30
30
  "commander": "^14.0.2",
31
31
  "fs-extra": "^11.3.2",
package/template/js/_env CHANGED
@@ -1,2 +1,2 @@
1
- PORT=8080PORT=3000
1
+ PORT=3000
2
2
  NODE_ENV=development