byuckchon-frontend-cli 1.9.6 → 1.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "byuckchon-frontend-cli",
3
- "version": "1.9.6",
3
+ "version": "1.10.0",
4
4
  "description": "Byuckchon Frontend Workbench — project starter + AI chat + codebase RAG + OpenAPI codegen",
5
5
  "type": "module",
6
6
  "engines": {
@@ -14,9 +14,12 @@
14
14
  "start": "node bin/index.js",
15
15
  "chat": "node bin/index.js chat",
16
16
  "init": "node bin/index.js init",
17
- "config": "node bin/index.js config show"
17
+ "config": "node bin/index.js config show",
18
+ "release": "changeset publish",
19
+ "version-packages": "changeset version"
18
20
  },
19
21
  "dependencies": {
22
+ "@byuckchon-frontend/settings": "^1.6.0",
20
23
  "@ai-sdk/anthropic": "^3.0.85",
21
24
  "@ai-sdk/openai": "^3.0.73",
22
25
  "ai": "^6.0.208",
@@ -65,5 +68,8 @@
65
68
  "repository": {
66
69
  "type": "git",
67
70
  "url": "git+https://github.com/RevolutionaryWarrior/byuckchon-frontend-cli.git"
71
+ },
72
+ "devDependencies": {
73
+ "@changesets/cli": "^2.29.8"
68
74
  }
69
75
  }
@@ -11,13 +11,15 @@ import {
11
11
  apiRootForFramework,
12
12
  scaffoldApiConventionDoc,
13
13
  } from '../generators/apiConventionDoc.js';
14
+ import { scaffoldReviewAutomation } from '../generators/scaffoldReviewAutomation.js';
15
+ import { detectMonorepoRoot } from '../context/monorepo.js';
14
16
  import { ensureRequiredDependencies } from '../utils/ensureRequiredDependencies.js';
15
17
 
16
18
  /**
17
19
  * `bc adopt`
18
20
  *
19
- * 기존 프로젝트(현재 디렉터리)에 bc.config.json 필수 의존성을 추가한다.
20
- * 기존 소스 코드는 건드리지 않는다.
21
+ * 기존 프로젝트(현재 디렉터리)에 bc.config.json, 필수 의존성, PR 리뷰 자동화를 추가한다.
22
+ * 기존 소스 코드와 이미 존재하는 tools/workflow는 건드리지 않는다.
21
23
  *
22
24
  * 흐름:
23
25
  * 1) 자동 감지(framework, styling, language, routing 등) 결과를 보여주고
@@ -176,6 +178,48 @@ export async function adoptCommand(opts = {}) {
176
178
  } catch {
177
179
  /* 문서 스캐폴드 실패는 치명적이지 않음 */
178
180
  }
181
+
182
+ // PR 리뷰 자동화는 모노레포 루트에 한 번만 둔다. 기존 tools/workflow는 덮어쓰지 않는다.
183
+ try {
184
+ const monorepoRoot = await detectMonorepoRoot(cwd);
185
+ const reviewProjectRoot = monorepoRoot ?? cwd;
186
+ const projectType = monorepoRoot ? 'monorepo' : 'single';
187
+ const reviewAutomation = await scaffoldReviewAutomation({
188
+ projectRoot: reviewProjectRoot,
189
+ projectType,
190
+ overwrite: false,
191
+ // PR Check 는 스크립트 이름·브랜치·패키지 매니저를 전제하므로
192
+ // 기존 프로젝트에 말없이 깔면 CI 가 곧바로 실패한다. bc init 전용으로 둔다.
193
+ exclude: ['pr-check'],
194
+ });
195
+
196
+ if (reviewAutomation.workflows.length) {
197
+ console.log(
198
+ chalk.green(
199
+ ` ✓ PR 워크플로 추가: ${path.join(reviewProjectRoot, '.github', 'workflows')}`,
200
+ ),
201
+ );
202
+ console.log(chalk.dim(` ${reviewAutomation.workflows.join(', ')}`));
203
+ console.log(
204
+ chalk.dim(
205
+ ' (PR Check 워크플로는 기존 프로젝트의 스크립트·브랜치 설정을 전제하므로 추가하지 않습니다)',
206
+ ),
207
+ );
208
+ } else {
209
+ console.log(
210
+ chalk.dim(
211
+ ` PR 워크플로 유지: ${path.join(reviewProjectRoot, '.github', 'workflows')} (이미 존재)`,
212
+ ),
213
+ );
214
+ }
215
+ } catch (error) {
216
+ console.log(
217
+ chalk.yellow(
218
+ ` ⚠ PR 워크플로를 추가하지 못했습니다: ${error.message}`,
219
+ ),
220
+ );
221
+ }
222
+
179
223
  console.log();
180
224
  console.log(chalk.dim(' 다음:'));
181
225
  if (!process.env.ANTHROPIC_API_KEY) {
@@ -1,49 +1,16 @@
1
- export const versions = {
2
- // Core frameworks
3
- react: '^18.3.1',
4
- 'react-dom': '^18.3.1',
5
- 'next-react': '^19.2.1',
6
- 'next-react-dom': '^19.2.1',
7
- next: '15.1.9',
8
-
9
- // Build tool (React only)
10
- vite: '^6.0.0',
11
- '@vitejs/plugin-react': '^4.3.0',
12
- 'vite-plugin-svgr': '^4.3.0',
13
- '@svgr/webpack': '^8.1.0',
14
-
15
- // TypeScript
16
- typescript: '^5.7.0',
17
- '@types/react': '^18.3.3',
18
- '@types/react-dom': '^18.3.0',
19
- '@types/node': '^22.0.0',
1
+ /**
2
+ * 검증된 스택 버전 매트릭스.
3
+ *
4
+ * 본체는 @byuckchon-frontend/settings 가 관리한다.
5
+ * 여기서는 settings 자신의 버전만 얹는다 — 자기 자신의 버전을 자기가 들고 있으면
6
+ * 어긋나기 때문에, CLI 와 함께 설치된 settings 의 실제 버전을 읽어서 쓴다.
7
+ */
20
8
 
21
- // State & Data
22
- zustand: '^5.0.3',
23
- axios: '^1.8.4',
24
- '@tanstack/react-query': '^5.74.4',
9
+ import { versions as shared } from '@byuckchon-frontend/settings/versions';
25
10
 
26
- // Styling
27
- tailwindcss: '^4.1.4',
28
- '@tailwindcss/vite': '^4.1.4',
29
- '@tailwindcss/postcss': '^4',
11
+ import { settingsVersion } from '../utils/settingsAssets.js';
30
12
 
31
- // Linting
32
- eslint: '^8.57.0',
33
- 'eslint-config-expo': '^8.0.0',
34
- 'eslint-config-next': '^15.0.0',
35
- 'eslint-import-resolver-typescript': '^3.6.0',
36
- 'eslint-plugin-import': '^2.29.0',
37
- 'eslint-plugin-react': '^7.34.0',
38
- 'eslint-plugin-react-hooks': '^4.6.0',
39
- '@typescript-eslint/eslint-plugin': '^7.0.0',
40
- '@typescript-eslint/parser': '^7.0.0',
41
-
42
- // Formatting
43
- prettier: '^3.3.0',
44
- 'prettier-plugin-tailwindcss': '^0.6.11',
45
- 'style-dictionary': '^5.4.0',
46
- zod: '^3.24.3',
47
- '@trivago/prettier-plugin-sort-imports': '^5.2.2',
48
- 'eslint-plugin-unused-imports': '^4.1.4',
13
+ export const versions = {
14
+ ...shared,
15
+ '@byuckchon-frontend/settings': `^${settingsVersion()}`,
49
16
  };
@@ -27,7 +27,10 @@ export async function createApp({ appDir, config, scope }) {
27
27
 
28
28
  await createFolders(appDir, config);
29
29
  await createBaseFiles(appDir, config);
30
- await scaffoldApiConventionDoc({ projectRoot: appDir, framework: config.framework });
30
+ await scaffoldApiConventionDoc({
31
+ projectRoot: appDir,
32
+ framework: config.framework,
33
+ });
31
34
  await createBcConfig(appDir, config);
32
35
 
33
36
  await createAppPackageJson(appDir, config, scope);
@@ -43,8 +46,8 @@ async function rm(target) {
43
46
  }
44
47
 
45
48
  /**
46
- * 워크스페이스 앱용 package.json. 모노레포에서는 React 버전을 하나로 통일하려고
47
- * (pnpm hoist 충돌 방지) react/react-dom 모두 19 계열로 맞춘다.
49
+ * 워크스페이스 앱용 package.json.
50
+ * 단일 프로젝트와 동일하게 Vite React는 React 18, Next는 React 19 사용한다.
48
51
  */
49
52
  async function createAppPackageJson(appDir, config, scope) {
50
53
  const isReact = config.framework === 'react';
@@ -77,28 +80,30 @@ async function createAppPackageJson(appDir, config, scope) {
77
80
  type: 'module',
78
81
  scripts,
79
82
  dependencies: {
80
- react: versions['next-react'],
81
- 'react-dom': versions['next-react-dom'],
83
+ react: isReact ? versions.react : versions['next-react'],
84
+ 'react-dom': isReact ? versions['react-dom'] : versions['next-react-dom'],
82
85
  ...(isReact ? {} : { next: versions.next }),
83
86
  zustand: versions.zustand,
84
87
  ...(isReact
85
88
  ? {
86
89
  axios: versions.axios,
90
+ 'react-router-dom': versions['react-router-dom'],
87
91
  '@tanstack/react-query': versions['@tanstack/react-query'],
88
92
  }
89
93
  : {}),
90
94
  zod: versions.zod,
95
+ clsx: versions.clsx,
96
+ 'tailwind-merge': versions['tailwind-merge'],
91
97
  },
92
98
  devDependencies: {
93
99
  [`@${scope}/config-eslint`]: 'workspace:*',
94
100
  [`@${scope}/config-typescript`]: 'workspace:*',
101
+ // global.d.ts, App.css(motion), token.config.js 가 직접 참조한다.
102
+ '@byuckchon-frontend/settings': versions['@byuckchon-frontend/settings'],
95
103
  '@types/react': versions['@types/react'],
96
104
  '@types/react-dom': versions['@types/react-dom'],
97
105
  '@types/node': versions['@types/node'],
98
- // 모노레포 앱은 flat config(@scope/config-eslint) 를 쓰므로 ESLint 9 필요.
99
- // (단일 프로젝트의 eslintrc + eslint 8 과 별개)
100
- eslint: '^9.18.0',
101
- 'eslint-plugin-unused-imports': versions['eslint-plugin-unused-imports'],
106
+ eslint: versions.eslint,
102
107
  prettier: versions.prettier,
103
108
  'prettier-plugin-tailwindcss': versions['prettier-plugin-tailwindcss'],
104
109
  'style-dictionary': versions['style-dictionary'],
@@ -118,7 +123,10 @@ async function createAppPackageJson(appDir, config, scope) {
118
123
  },
119
124
  };
120
125
 
121
- await write(path.join(appDir, 'package.json'), JSON.stringify(pkg, null, 2) + '\n');
126
+ await write(
127
+ path.join(appDir, 'package.json'),
128
+ JSON.stringify(pkg, null, 2) + '\n'
129
+ );
122
130
  }
123
131
 
124
132
  /**
@@ -128,33 +136,19 @@ async function applyMonorepoConventions(appDir, config, scope) {
128
136
  const isReact = config.framework === 'react';
129
137
 
130
138
  // 루트에서 관리하는 설정들은 앱 레벨에서 제거.
131
- await rm(path.join(appDir, '.eslintrc.cjs'));
132
- await rm(path.join(appDir, '.prettierrc'));
139
+ await rm(path.join(appDir, 'eslint.config.js'));
140
+ await rm(path.join(appDir, 'eslint.config.mjs'));
141
+ await rm(path.join(appDir, 'prettier.config.js'));
133
142
  await rm(path.join(appDir, '.gitignore'));
134
143
  await rm(path.join(appDir, '.vscode'));
135
144
 
136
- // 공유 ESLint 프리셋을 extends 하는 flat config.
145
+ // 공유 ESLint 프리셋을 그대로 쓴다.
146
+ // 규칙 본체는 @byuckchon-frontend/settings 가 관리하고,
147
+ // 이 모노레포 전용 예외는 packages/config-eslint 에서 얹는다.
137
148
  await write(
138
149
  path.join(appDir, 'eslint.config.mjs'),
139
- `import { reactConfig } from '@${scope}/config-eslint/react';
140
- import unusedImports from 'eslint-plugin-unused-imports';
141
-
142
- export default [
143
- ...reactConfig,
144
- {
145
- plugins: { 'unused-imports': unusedImports },
146
- rules: {
147
- 'no-unused-vars': 'off',
148
- '@typescript-eslint/no-unused-vars': 'off',
149
- 'unused-imports/no-unused-imports': 'error',
150
- 'unused-imports/no-unused-vars': [
151
- 'warn',
152
- { vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_' },
153
- ],
154
- },
155
- },
156
- ];
157
- `,
150
+ `export { default } from '@${scope}/config-eslint/${isReact ? 'react' : 'next'}';
151
+ `
158
152
  );
159
153
 
160
154
  if (isReact) {
@@ -167,11 +161,8 @@ export default [
167
161
  {
168
162
  extends: `@${scope}/config-typescript/react.json`,
169
163
  compilerOptions: {
164
+ // 공통 옵션은 config-typescript → settings 프리셋에 있다.
170
165
  tsBuildInfoFile: './node_modules/.tmp/tsconfig.tsbuildinfo',
171
- noUnusedLocals: true,
172
- noUnusedParameters: true,
173
- noUncheckedSideEffectImports: true,
174
- moduleDetection: 'force',
175
166
  baseUrl: '.',
176
167
  paths: {
177
168
  '@/*': ['src/*'],
@@ -182,8 +173,8 @@ export default [
182
173
  include: ['src', 'vite.config.ts'],
183
174
  },
184
175
  null,
185
- 2,
186
- ) + '\n',
176
+ 2
177
+ ) + '\n'
187
178
  );
188
179
  } else {
189
180
  await write(
@@ -204,8 +195,8 @@ export default [
204
195
  exclude: ['node_modules'],
205
196
  },
206
197
  null,
207
- 2,
208
- ) + '\n',
198
+ 2
199
+ ) + '\n'
209
200
  );
210
201
  }
211
202
  }