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/README.md +233 -264
- package/package.json +8 -2
- package/src/commands/adopt.js +46 -2
- package/src/constants/versions.js +12 -45
- package/src/generators/createApp.js +31 -40
- package/src/generators/createBaseFiles.js +85 -315
- package/src/generators/createFolders.js +22 -6
- package/src/generators/createMonorepo.js +53 -238
- package/src/generators/createPackageJson.js +19 -13
- package/src/generators/createProject.js +22 -19
- package/src/generators/createReadme.js +4 -4
- package/src/generators/install.js +1 -0
- package/src/generators/scaffoldReviewAutomation.js +115 -0
- package/src/utils/ensureRequiredDependencies.js +35 -9
- package/src/utils/settingsAssets.js +27 -0
- package/templates/review-automation/github/workflows/eslint-convention-review.monorepo.yml +88 -0
- package/templates/review-automation/github/workflows/eslint-convention-review.single.yml +44 -0
- package/templates/review-automation/github/workflows/pr-check.monorepo.yml +51 -0
- package/templates/review-automation/github/workflows/pr-check.single.yml +47 -0
- package/templates/review-automation/tools/review.config.mjs +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "byuckchon-frontend-cli",
|
|
3
|
-
"version": "1.
|
|
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
|
}
|
package/src/commands/adopt.js
CHANGED
|
@@ -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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
'
|
|
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({
|
|
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.
|
|
47
|
-
*
|
|
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
|
-
|
|
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(
|
|
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, '.
|
|
132
|
-
await rm(path.join(appDir, '.
|
|
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 프리셋을
|
|
145
|
+
// 공유 ESLint 프리셋을 그대로 쓴다.
|
|
146
|
+
// 규칙 본체는 @byuckchon-frontend/settings 가 관리하고,
|
|
147
|
+
// 이 모노레포 전용 예외는 packages/config-eslint 에서 얹는다.
|
|
137
148
|
await write(
|
|
138
149
|
path.join(appDir, 'eslint.config.mjs'),
|
|
139
|
-
`
|
|
140
|
-
|
|
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
|
}
|