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
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
+
import { versions } from '../constants/versions.js';
|
|
5
|
+
import { readSettingsAsset } from '../utils/settingsAssets.js';
|
|
4
6
|
import { createApp } from './createApp.js';
|
|
7
|
+
import { scaffoldReviewAutomation } from './scaffoldReviewAutomation.js';
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* 새 pnpm 모노레포를 스캐폴드한다. (marketd-frontend 구조 참고)
|
|
@@ -32,6 +35,7 @@ export async function createMonorepo(config) {
|
|
|
32
35
|
await writeRootFiles(root, config, scope);
|
|
33
36
|
await writeConfigTypescript(root, scope);
|
|
34
37
|
await writeConfigEslint(root, scope);
|
|
38
|
+
await scaffoldReviewAutomation({ projectRoot: root, projectType: 'monorepo' });
|
|
35
39
|
|
|
36
40
|
// 최초 앱 하나만 생성. 이후 추가는 `bc add`.
|
|
37
41
|
const appName = config.appName;
|
|
@@ -71,15 +75,17 @@ async function writeRootFiles(root, config, scope) {
|
|
|
71
75
|
typecheck: 'turbo run typecheck',
|
|
72
76
|
'tokens:build': 'turbo run tokens:build',
|
|
73
77
|
format:
|
|
74
|
-
'prettier --write "**/*.{ts,tsx,js,jsx,json,md,yml,yaml}" --ignore-path .gitignore',
|
|
78
|
+
'prettier --write "**/*.{ts,tsx,js,jsx,cjs,json,md,yml,yaml}" --ignore-path .gitignore',
|
|
75
79
|
'format:check':
|
|
76
|
-
'prettier --check "**/*.{ts,tsx,js,jsx,json,md,yml,yaml}" --ignore-path .gitignore',
|
|
80
|
+
'prettier --check "**/*.{ts,tsx,js,jsx,cjs,json,md,yml,yaml}" --ignore-path .gitignore',
|
|
77
81
|
clean: 'turbo run clean && rm -rf node_modules .turbo',
|
|
78
82
|
preinstall: 'npx only-allow pnpm',
|
|
79
83
|
// 최초 앱 실행 단축키 (bc add 시 앱마다 추가됨).
|
|
80
84
|
[config.appName]: `pnpm --filter @${scope}/${config.appName} dev`,
|
|
81
85
|
},
|
|
82
86
|
devDependencies: {
|
|
87
|
+
// 루트의 prettier.config.mjs 와 tsconfig.base.json 이 직접 참조한다.
|
|
88
|
+
'@byuckchon-frontend/settings': versions['@byuckchon-frontend/settings'],
|
|
83
89
|
'@types/node': '^22.0.0',
|
|
84
90
|
eslint: '^9.18.0',
|
|
85
91
|
'eslint-config-prettier': '^10.1.8',
|
|
@@ -100,58 +106,22 @@ packages:
|
|
|
100
106
|
`,
|
|
101
107
|
);
|
|
102
108
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
globalDependencies: ['tsconfig.base.json', '.env', '.env.*', '!.env*.local'],
|
|
107
|
-
globalEnv: ['NODE_ENV', 'CI'],
|
|
108
|
-
tasks: {
|
|
109
|
-
build: {
|
|
110
|
-
dependsOn: ['^build'],
|
|
111
|
-
outputs: ['dist/**', 'build/**', '.next/**', '!.next/cache/**', 'out/**'],
|
|
112
|
-
inputs: [
|
|
113
|
-
'$TURBO_DEFAULT$',
|
|
114
|
-
'!**/*.md',
|
|
115
|
-
'!**/*.test.ts',
|
|
116
|
-
'!**/*.test.tsx',
|
|
117
|
-
'!**/*.spec.ts',
|
|
118
|
-
'!**/*.spec.tsx',
|
|
119
|
-
],
|
|
120
|
-
},
|
|
121
|
-
dev: { cache: false, persistent: true },
|
|
122
|
-
lint: { dependsOn: ['^build'], outputs: [] },
|
|
123
|
-
typecheck: { dependsOn: ['^build'], outputs: ['.tsbuildinfo', '**/*.tsbuildinfo'] },
|
|
124
|
-
'tokens:build': { outputs: ['src/tokens.css'] },
|
|
125
|
-
clean: { cache: false },
|
|
126
|
-
},
|
|
127
|
-
});
|
|
109
|
+
// turbo / .npmrc / .nvmrc 는 참조 문법이 없어서 내용을 복사한다.
|
|
110
|
+
// 이후 settings 가 바뀌면 `npx byuckchon-settings-sync` 로 갱신한다.
|
|
111
|
+
await write(path.join(root, 'turbo.json'), await readSettingsAsset('project/turbo.json'));
|
|
128
112
|
|
|
113
|
+
// 공통 옵션은 @byuckchon-frontend/settings 가 관리한다.
|
|
114
|
+
// 이 파일은 모노레포 전용 예외를 얹는 자리로만 남긴다.
|
|
129
115
|
await writeJson(path.join(root, 'tsconfig.base.json'), {
|
|
130
116
|
$schema: 'https://json.schemastore.org/tsconfig',
|
|
131
|
-
display: `${
|
|
117
|
+
display: `${config.projectName} Base`,
|
|
118
|
+
extends: '@byuckchon-frontend/settings/tsconfig/base.json',
|
|
132
119
|
compilerOptions: {
|
|
133
|
-
target: 'ES2022',
|
|
134
|
-
lib: ['ES2022'],
|
|
135
|
-
module: 'ESNext',
|
|
136
|
-
moduleResolution: 'Bundler',
|
|
137
|
-
strict: true,
|
|
138
120
|
noUncheckedIndexedAccess: true,
|
|
139
121
|
noImplicitOverride: true,
|
|
140
|
-
noFallthroughCasesInSwitch: true,
|
|
141
|
-
useUnknownInCatchVariables: true,
|
|
142
|
-
exactOptionalPropertyTypes: false,
|
|
143
|
-
esModuleInterop: true,
|
|
144
122
|
allowSyntheticDefaultImports: true,
|
|
145
|
-
forceConsistentCasingInFileNames: true,
|
|
146
|
-
resolveJsonModule: true,
|
|
147
|
-
isolatedModules: true,
|
|
148
|
-
verbatimModuleSyntax: false,
|
|
149
|
-
skipLibCheck: true,
|
|
150
123
|
incremental: true,
|
|
151
|
-
composite: false,
|
|
152
|
-
types: [],
|
|
153
124
|
},
|
|
154
|
-
exclude: ['node_modules', 'dist', 'build', '.turbo', '.next', 'coverage'],
|
|
155
125
|
});
|
|
156
126
|
|
|
157
127
|
await writeJson(path.join(root, 'tsconfig.json'), {
|
|
@@ -160,42 +130,19 @@ packages:
|
|
|
160
130
|
include: [],
|
|
161
131
|
});
|
|
162
132
|
|
|
163
|
-
await write(
|
|
164
|
-
path.join(root, '.npmrc'),
|
|
165
|
-
`# pnpm 동작 설정
|
|
166
|
-
# React 버전 통일 및 (향후 Expo/RN 도입 대비) hoisted linker 사용.
|
|
167
|
-
node-linker=hoisted
|
|
168
|
-
|
|
169
|
-
public-hoist-pattern[]=*react*
|
|
170
|
-
public-hoist-pattern[]=*@types/*
|
|
171
|
-
public-hoist-pattern[]=*eslint*
|
|
172
|
-
public-hoist-pattern[]=*prettier*
|
|
173
|
-
|
|
174
|
-
strict-peer-dependencies=false
|
|
175
|
-
auto-install-peers=true
|
|
176
|
-
save-exact=false
|
|
177
|
-
save-prefix=^
|
|
178
|
-
prefer-frozen-lockfile=true
|
|
179
|
-
`,
|
|
180
|
-
);
|
|
133
|
+
await write(path.join(root, '.npmrc'), await readSettingsAsset('project/npmrc'));
|
|
181
134
|
|
|
182
|
-
await write(path.join(root, '.nvmrc'), '
|
|
135
|
+
await write(path.join(root, '.nvmrc'), await readSettingsAsset('project/nvmrc'));
|
|
183
136
|
|
|
184
137
|
await write(
|
|
185
138
|
path.join(root, 'prettier.config.mjs'),
|
|
186
|
-
|
|
139
|
+
`// 포맷 규칙은 @byuckchon-frontend/settings 가 관리합니다.
|
|
140
|
+
// 이 모노레포 전용 예외가 필요하면 펼쳐서 덮어쓰세요.
|
|
141
|
+
import byuckchon from '@byuckchon-frontend/settings/prettier';
|
|
142
|
+
|
|
143
|
+
/** @type {import("prettier").Config} */
|
|
187
144
|
export default {
|
|
188
|
-
|
|
189
|
-
singleQuote: true,
|
|
190
|
-
trailingComma: 'all',
|
|
191
|
-
tabWidth: 2,
|
|
192
|
-
printWidth: 100,
|
|
193
|
-
arrowParens: 'always',
|
|
194
|
-
endOfLine: 'lf',
|
|
195
|
-
bracketSpacing: true,
|
|
196
|
-
bracketSameLine: false,
|
|
197
|
-
jsxSingleQuote: false,
|
|
198
|
-
plugins: ['prettier-plugin-tailwindcss'],
|
|
145
|
+
...byuckchon,
|
|
199
146
|
overrides: [
|
|
200
147
|
{
|
|
201
148
|
files: ['*.json', '*.md', '*.yml', '*.yaml'],
|
|
@@ -203,7 +150,7 @@ export default {
|
|
|
203
150
|
},
|
|
204
151
|
],
|
|
205
152
|
};
|
|
206
|
-
|
|
153
|
+
`
|
|
207
154
|
);
|
|
208
155
|
|
|
209
156
|
await write(
|
|
@@ -311,106 +258,54 @@ async function writeConfigTypescript(root, scope) {
|
|
|
311
258
|
const dir = path.join(root, 'packages', 'config-typescript');
|
|
312
259
|
await fs.mkdir(dir, { recursive: true });
|
|
313
260
|
|
|
261
|
+
// 프리셋 본체는 @byuckchon-frontend/settings 가 관리한다.
|
|
262
|
+
// 이 패키지는 모노레포 전용 예외를 얹을 자리로만 남긴다.
|
|
314
263
|
await writeJson(path.join(dir, 'package.json'), {
|
|
315
264
|
name: `@${scope}/config-typescript`,
|
|
316
265
|
version: '0.0.0',
|
|
317
266
|
private: true,
|
|
318
|
-
description:
|
|
267
|
+
description: 'settings 의 TypeScript 프리셋을 이 모노레포용으로 감싼 패키지',
|
|
319
268
|
files: ['base.json', 'library.json', 'react.json', 'next.json', 'node.json'],
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
await writeJson(path.join(dir, 'base.json'), {
|
|
323
|
-
$schema: 'https://json.schemastore.org/tsconfig',
|
|
324
|
-
display: 'Base',
|
|
325
|
-
extends: '../../tsconfig.base.json',
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
await writeJson(path.join(dir, 'react.json'), {
|
|
329
|
-
$schema: 'https://json.schemastore.org/tsconfig',
|
|
330
|
-
display: 'React Web (Vite)',
|
|
331
|
-
extends: './base.json',
|
|
332
|
-
compilerOptions: {
|
|
333
|
-
lib: ['ES2022', 'DOM', 'DOM.Iterable'],
|
|
334
|
-
jsx: 'react-jsx',
|
|
335
|
-
moduleResolution: 'Bundler',
|
|
336
|
-
module: 'ESNext',
|
|
337
|
-
noEmit: true,
|
|
338
|
-
allowImportingTsExtensions: true,
|
|
339
|
-
useDefineForClassFields: true,
|
|
340
|
-
types: ['vite/client'],
|
|
341
|
-
noUncheckedIndexedAccess: false,
|
|
342
|
-
},
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
await writeJson(path.join(dir, 'next.json'), {
|
|
346
|
-
$schema: 'https://json.schemastore.org/tsconfig',
|
|
347
|
-
display: 'Next.js (App Router)',
|
|
348
|
-
extends: './base.json',
|
|
349
|
-
compilerOptions: {
|
|
350
|
-
lib: ['ES2022', 'DOM', 'DOM.Iterable'],
|
|
351
|
-
jsx: 'preserve',
|
|
352
|
-
module: 'ESNext',
|
|
353
|
-
moduleResolution: 'Bundler',
|
|
354
|
-
noEmit: true,
|
|
355
|
-
allowJs: true,
|
|
356
|
-
incremental: true,
|
|
357
|
-
plugins: [{ name: 'next' }],
|
|
358
|
-
noUncheckedIndexedAccess: false,
|
|
269
|
+
dependencies: {
|
|
270
|
+
'@byuckchon-frontend/settings': versions['@byuckchon-frontend/settings'],
|
|
359
271
|
},
|
|
360
272
|
});
|
|
361
273
|
|
|
362
|
-
|
|
274
|
+
const preset = (display, name) => ({
|
|
363
275
|
$schema: 'https://json.schemastore.org/tsconfig',
|
|
364
|
-
display
|
|
365
|
-
extends:
|
|
366
|
-
compilerOptions: {
|
|
367
|
-
declaration: true,
|
|
368
|
-
declarationMap: true,
|
|
369
|
-
sourceMap: true,
|
|
370
|
-
outDir: 'dist',
|
|
371
|
-
rootDir: 'src',
|
|
372
|
-
composite: true,
|
|
373
|
-
},
|
|
276
|
+
display,
|
|
277
|
+
extends: `@byuckchon-frontend/settings/tsconfig/${name}.json`,
|
|
374
278
|
});
|
|
375
279
|
|
|
376
|
-
await writeJson(path.join(dir, '
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
lib: ['ES2022'],
|
|
382
|
-
module: 'NodeNext',
|
|
383
|
-
moduleResolution: 'NodeNext',
|
|
384
|
-
types: ['node'],
|
|
385
|
-
},
|
|
386
|
-
});
|
|
280
|
+
await writeJson(path.join(dir, 'base.json'), preset('Base', 'base'));
|
|
281
|
+
await writeJson(path.join(dir, 'react.json'), preset('React Web (Vite)', 'react'));
|
|
282
|
+
await writeJson(path.join(dir, 'next.json'), preset('Next.js (App Router)', 'next'));
|
|
283
|
+
await writeJson(path.join(dir, 'library.json'), preset('Library (packages/*)', 'library'));
|
|
284
|
+
await writeJson(path.join(dir, 'node.json'), preset('Node (scripts)', 'node'));
|
|
387
285
|
}
|
|
388
286
|
|
|
389
287
|
async function writeConfigEslint(root, scope) {
|
|
390
288
|
const dir = path.join(root, 'packages', 'config-eslint');
|
|
391
289
|
await fs.mkdir(dir, { recursive: true });
|
|
392
290
|
|
|
291
|
+
// 규칙 본체는 @byuckchon-frontend/settings 가 관리한다.
|
|
292
|
+
// 이 패키지는 모노레포 전용 예외를 얹을 자리로만 남긴다.
|
|
393
293
|
await writeJson(path.join(dir, 'package.json'), {
|
|
394
294
|
name: `@${scope}/config-eslint`,
|
|
395
295
|
version: '0.0.0',
|
|
396
296
|
private: true,
|
|
397
297
|
type: 'module',
|
|
398
|
-
description:
|
|
298
|
+
description: 'settings 의 ESLint 프리셋을 이 모노레포용으로 감싼 패키지',
|
|
399
299
|
main: './base.js',
|
|
400
300
|
exports: {
|
|
401
301
|
'.': './base.js',
|
|
402
302
|
'./base': './base.js',
|
|
403
303
|
'./react': './react.js',
|
|
304
|
+
'./next': './next.js',
|
|
404
305
|
},
|
|
405
|
-
files: ['base.js', 'react.js'],
|
|
306
|
+
files: ['base.js', 'react.js', 'next.js'],
|
|
406
307
|
dependencies: {
|
|
407
|
-
'@
|
|
408
|
-
'eslint-config-prettier': '^10.1.8',
|
|
409
|
-
'eslint-plugin-import': '^2.32.0',
|
|
410
|
-
'eslint-plugin-react': '^7.37.4',
|
|
411
|
-
'eslint-plugin-react-hooks': '^5.1.0',
|
|
412
|
-
globals: '^15.14.0',
|
|
413
|
-
'typescript-eslint': '^8.59.1',
|
|
308
|
+
'@byuckchon-frontend/settings': versions['@byuckchon-frontend/settings'],
|
|
414
309
|
},
|
|
415
310
|
peerDependencies: {
|
|
416
311
|
eslint: '^9.0.0',
|
|
@@ -418,97 +313,17 @@ async function writeConfigEslint(root, scope) {
|
|
|
418
313
|
},
|
|
419
314
|
});
|
|
420
315
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
import js from '@eslint/js';
|
|
425
|
-
import tseslint from 'typescript-eslint';
|
|
426
|
-
import prettier from 'eslint-config-prettier';
|
|
427
|
-
import importPlugin from 'eslint-plugin-import';
|
|
428
|
-
import globals from 'globals';
|
|
316
|
+
const reexport = (name, named) => `// 규칙 본체는 @byuckchon-frontend/settings 가 관리합니다.
|
|
317
|
+
// 이 모노레포에만 해당하는 예외는 아래 배열에 이어붙이세요.
|
|
318
|
+
import byuckchon from '@byuckchon-frontend/settings/eslint/${name}';
|
|
429
319
|
|
|
430
|
-
/**
|
|
431
|
-
|
|
432
|
-
* @type {import("eslint").Linter.Config[]}
|
|
433
|
-
*/
|
|
434
|
-
export const baseConfig = [
|
|
435
|
-
{
|
|
436
|
-
ignores: [
|
|
437
|
-
'**/node_modules/**',
|
|
438
|
-
'**/dist/**',
|
|
439
|
-
'**/build/**',
|
|
440
|
-
'**/out/**',
|
|
441
|
-
'**/.next/**',
|
|
442
|
-
'**/.turbo/**',
|
|
443
|
-
'**/coverage/**',
|
|
444
|
-
'**/*.d.ts',
|
|
445
|
-
],
|
|
446
|
-
},
|
|
447
|
-
js.configs.recommended,
|
|
448
|
-
...tseslint.configs.recommended,
|
|
449
|
-
{
|
|
450
|
-
languageOptions: {
|
|
451
|
-
ecmaVersion: 2022,
|
|
452
|
-
sourceType: 'module',
|
|
453
|
-
globals: { ...globals.es2022 },
|
|
454
|
-
},
|
|
455
|
-
plugins: { import: importPlugin },
|
|
456
|
-
rules: {
|
|
457
|
-
'no-console': ['warn', { allow: ['warn', 'error'] }],
|
|
458
|
-
'@typescript-eslint/consistent-type-imports': [
|
|
459
|
-
'warn',
|
|
460
|
-
{ prefer: 'type-imports', fixStyle: 'inline-type-imports' },
|
|
461
|
-
],
|
|
462
|
-
'@typescript-eslint/no-unused-vars': [
|
|
463
|
-
'warn',
|
|
464
|
-
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
|
465
|
-
],
|
|
466
|
-
},
|
|
467
|
-
},
|
|
468
|
-
prettier,
|
|
469
|
-
];
|
|
470
|
-
|
|
471
|
-
export default baseConfig;
|
|
472
|
-
`,
|
|
473
|
-
);
|
|
474
|
-
|
|
475
|
-
await write(
|
|
476
|
-
path.join(dir, 'react.js'),
|
|
477
|
-
`// @ts-check
|
|
478
|
-
import react from 'eslint-plugin-react';
|
|
479
|
-
import reactHooks from 'eslint-plugin-react-hooks';
|
|
480
|
-
import globals from 'globals';
|
|
320
|
+
/** @type {import("eslint").Linter.Config[]} */
|
|
321
|
+
export const ${named} = [...byuckchon];
|
|
481
322
|
|
|
482
|
-
|
|
323
|
+
export default ${named};
|
|
324
|
+
`;
|
|
483
325
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
*/
|
|
488
|
-
export const reactConfig = [
|
|
489
|
-
...baseConfig,
|
|
490
|
-
{
|
|
491
|
-
files: ['**/*.{js,jsx,ts,tsx}'],
|
|
492
|
-
languageOptions: {
|
|
493
|
-
globals: { ...globals.browser, ...globals.es2022 },
|
|
494
|
-
parserOptions: { ecmaFeatures: { jsx: true } },
|
|
495
|
-
},
|
|
496
|
-
plugins: { react, 'react-hooks': reactHooks },
|
|
497
|
-
settings: { react: { version: 'detect' } },
|
|
498
|
-
rules: {
|
|
499
|
-
...react.configs.recommended.rules,
|
|
500
|
-
...react.configs['jsx-runtime'].rules,
|
|
501
|
-
...reactHooks.configs.recommended.rules,
|
|
502
|
-
'react/prop-types': 'off',
|
|
503
|
-
'react/react-in-jsx-scope': 'off',
|
|
504
|
-
'react/no-unescaped-entities': 'off',
|
|
505
|
-
'react-hooks/rules-of-hooks': 'warn',
|
|
506
|
-
'@typescript-eslint/no-explicit-any': 'off',
|
|
507
|
-
},
|
|
508
|
-
},
|
|
509
|
-
];
|
|
510
|
-
|
|
511
|
-
export default reactConfig;
|
|
512
|
-
`,
|
|
513
|
-
);
|
|
326
|
+
await write(path.join(dir, 'base.js'), reexport('base', 'baseConfig'));
|
|
327
|
+
await write(path.join(dir, 'react.js'), reexport('react', 'reactConfig'));
|
|
328
|
+
await write(path.join(dir, 'next.js'), reexport('next', 'nextConfig'));
|
|
514
329
|
}
|
|
@@ -16,17 +16,26 @@ export async function createPackageJson(rootDir, config) {
|
|
|
16
16
|
dev: 'vite',
|
|
17
17
|
build: 'tsc -b && vite build',
|
|
18
18
|
'tokens:build': 'style-dictionary build --config token.config.js',
|
|
19
|
+
// PR Check workflow 가 lint / typecheck / build 를 각각 호출한다.
|
|
20
|
+
typecheck: 'tsc -b',
|
|
19
21
|
lint: 'eslint . --ext ts,tsx',
|
|
20
22
|
preview: 'vite preview',
|
|
21
|
-
format:
|
|
23
|
+
format:
|
|
24
|
+
'prettier --write "src/**/*.{ts,tsx,css}" "tools/**/*.{js,cjs,json,md}"',
|
|
25
|
+
'format:check':
|
|
26
|
+
'prettier --check "src/**/*.{ts,tsx,css}" "tools/**/*.{js,cjs,json,md}"',
|
|
22
27
|
}
|
|
23
28
|
: {
|
|
24
29
|
dev: 'next dev',
|
|
25
30
|
build: 'next build',
|
|
26
31
|
'tokens:build': 'style-dictionary build --config token.config.js',
|
|
32
|
+
typecheck: 'tsc --noEmit',
|
|
27
33
|
start: 'next start',
|
|
28
|
-
lint: '
|
|
29
|
-
format:
|
|
34
|
+
lint: 'eslint .',
|
|
35
|
+
format:
|
|
36
|
+
'prettier --write "src/**/*.{ts,tsx,css}" "tools/**/*.{js,cjs,json,md}"',
|
|
37
|
+
'format:check':
|
|
38
|
+
'prettier --check "src/**/*.{ts,tsx,css}" "tools/**/*.{js,cjs,json,md}"',
|
|
30
39
|
},
|
|
31
40
|
dependencies: {
|
|
32
41
|
react: isReact ? versions.react : versions['next-react'],
|
|
@@ -37,9 +46,12 @@ export async function createPackageJson(rootDir, config) {
|
|
|
37
46
|
...(isReact
|
|
38
47
|
? {
|
|
39
48
|
axios: versions.axios,
|
|
49
|
+
'react-router-dom': versions['react-router-dom'],
|
|
40
50
|
}
|
|
41
51
|
: {}),
|
|
42
52
|
zod: versions.zod,
|
|
53
|
+
clsx: versions.clsx,
|
|
54
|
+
'tailwind-merge': versions['tailwind-merge'],
|
|
43
55
|
},
|
|
44
56
|
devDependencies: {
|
|
45
57
|
'@types/react': versions['@types/react'],
|
|
@@ -47,14 +59,10 @@ export async function createPackageJson(rootDir, config) {
|
|
|
47
59
|
'@types/node': versions['@types/node'],
|
|
48
60
|
'@trivago/prettier-plugin-sort-imports':
|
|
49
61
|
versions['@trivago/prettier-plugin-sort-imports'],
|
|
62
|
+
// eslint/prettier/tsconfig/tokens 설정과 motion CSS 가 모두 이 패키지를 참조한다.
|
|
63
|
+
// (ESLint 플러그인도 이 패키지가 의존성으로 들고 온다)
|
|
64
|
+
'@byuckchon-frontend/settings': versions['@byuckchon-frontend/settings'],
|
|
50
65
|
eslint: versions.eslint,
|
|
51
|
-
'eslint-config-expo': versions['eslint-config-expo'],
|
|
52
|
-
'eslint-import-resolver-typescript':
|
|
53
|
-
versions['eslint-import-resolver-typescript'],
|
|
54
|
-
'eslint-plugin-import': versions['eslint-plugin-import'],
|
|
55
|
-
'eslint-plugin-react': versions['eslint-plugin-react'],
|
|
56
|
-
'eslint-plugin-react-hooks': versions['eslint-plugin-react-hooks'],
|
|
57
|
-
'eslint-plugin-unused-imports': versions['eslint-plugin-unused-imports'],
|
|
58
66
|
prettier: versions.prettier,
|
|
59
67
|
'prettier-plugin-tailwindcss': versions['prettier-plugin-tailwindcss'],
|
|
60
68
|
'style-dictionary': versions['style-dictionary'],
|
|
@@ -63,9 +71,6 @@ export async function createPackageJson(rootDir, config) {
|
|
|
63
71
|
...(isReact
|
|
64
72
|
? {
|
|
65
73
|
'@tailwindcss/vite': versions['@tailwindcss/vite'],
|
|
66
|
-
'@typescript-eslint/eslint-plugin':
|
|
67
|
-
versions['@typescript-eslint/eslint-plugin'],
|
|
68
|
-
'@typescript-eslint/parser': versions['@typescript-eslint/parser'],
|
|
69
74
|
'@vitejs/plugin-react': versions['@vitejs/plugin-react'],
|
|
70
75
|
vite: versions.vite,
|
|
71
76
|
'vite-plugin-svgr': versions['vite-plugin-svgr'],
|
|
@@ -74,6 +79,7 @@ export async function createPackageJson(rootDir, config) {
|
|
|
74
79
|
'@tailwindcss/postcss': versions['@tailwindcss/postcss'],
|
|
75
80
|
'@svgr/webpack': versions['@svgr/webpack'],
|
|
76
81
|
'eslint-config-next': versions['eslint-config-next'],
|
|
82
|
+
'@eslint/eslintrc': versions['@eslint/eslintrc'],
|
|
77
83
|
}),
|
|
78
84
|
},
|
|
79
85
|
};
|
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
3
|
-
import { exec as execCallback } from
|
|
4
|
-
import { promisify } from
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { exec as execCallback } from "child_process";
|
|
4
|
+
import { promisify } from "util";
|
|
5
5
|
|
|
6
|
-
import { createBaseFiles } from
|
|
7
|
-
import { createBcConfig } from
|
|
8
|
-
import { createFolders } from
|
|
9
|
-
import { createPackageJson } from
|
|
10
|
-
import { createReadme } from
|
|
11
|
-
import { scaffoldApiConventionDoc } from
|
|
6
|
+
import { createBaseFiles } from "./createBaseFiles.js";
|
|
7
|
+
import { createBcConfig } from "./createBcConfig.js";
|
|
8
|
+
import { createFolders } from "./createFolders.js";
|
|
9
|
+
import { createPackageJson } from "./createPackageJson.js";
|
|
10
|
+
import { createReadme } from "./createReadme.js";
|
|
11
|
+
import { scaffoldApiConventionDoc } from "./apiConventionDoc.js";
|
|
12
|
+
import { scaffoldReviewAutomation } from "./scaffoldReviewAutomation.js";
|
|
13
|
+
import { BYUCKCHON_PACKAGES } from "./install.js";
|
|
12
14
|
|
|
13
15
|
const exec = promisify(execCallback);
|
|
14
|
-
const BYUCKCHON_PACKAGES = [
|
|
15
|
-
'@byuckchon-frontend/hooks',
|
|
16
|
-
'@byuckchon-frontend/utils',
|
|
17
|
-
'@byuckchon-frontend/basic-ui',
|
|
18
|
-
'@byuckchon-frontend/core',
|
|
19
|
-
];
|
|
20
16
|
|
|
21
17
|
export async function createProject(config) {
|
|
22
18
|
const rootDir = path.resolve(config.projectName);
|
|
@@ -27,10 +23,17 @@ export async function createProject(config) {
|
|
|
27
23
|
await createBaseFiles(rootDir, config);
|
|
28
24
|
await createReadme(rootDir, config);
|
|
29
25
|
// API 코드 컨벤션 .md 를 프레임워크에 맞는 API 루트(src/api | src/lib/api)에 깐다.
|
|
30
|
-
await scaffoldApiConventionDoc({
|
|
26
|
+
await scaffoldApiConventionDoc({
|
|
27
|
+
projectRoot: rootDir,
|
|
28
|
+
framework: config.framework,
|
|
29
|
+
});
|
|
31
30
|
await createBcConfig(rootDir, config);
|
|
31
|
+
await scaffoldReviewAutomation({
|
|
32
|
+
projectRoot: rootDir,
|
|
33
|
+
projectType: "single",
|
|
34
|
+
});
|
|
32
35
|
|
|
33
36
|
// 최신 버전(latest 포함) 의존성을 실제로 설치해 lockfile까지 생성
|
|
34
|
-
await exec(
|
|
35
|
-
await exec(`npm install ${BYUCKCHON_PACKAGES.join(
|
|
37
|
+
await exec("npm install", { cwd: rootDir });
|
|
38
|
+
await exec(`npm install ${BYUCKCHON_PACKAGES.join(" ")}`, { cwd: rootDir });
|
|
36
39
|
}
|
|
@@ -10,7 +10,7 @@ export async function createReadme(rootDir, config) {
|
|
|
10
10
|
|
|
11
11
|
| 항목 | 내용 |
|
|
12
12
|
|------|------|
|
|
13
|
-
| Framework | ${isReact ? 'React
|
|
13
|
+
| Framework | ${isReact ? 'React 18' : 'Next.js 15 (App Router)'} |
|
|
14
14
|
| Language | TypeScript |
|
|
15
15
|
| Build Tool | ${isReact ? 'Vite' : 'Next.js built-in'} |
|
|
16
16
|
| Styling | Tailwind CSS |
|
|
@@ -47,8 +47,8 @@ src/
|
|
|
47
47
|
├── components/ 재사용 가능한 UI 컴포넌트${
|
|
48
48
|
isReact
|
|
49
49
|
? `
|
|
50
|
-
├──
|
|
51
|
-
└──
|
|
50
|
+
├── layouts/ 레이아웃 컴포넌트
|
|
51
|
+
└── pages/ 페이지 컴포넌트`
|
|
52
52
|
: `
|
|
53
53
|
└── app/ Next.js App Router (layout, page 등)`
|
|
54
54
|
}
|
|
@@ -61,7 +61,7 @@ ${
|
|
|
61
61
|
각 레이어는 아래 방향으로만 import 해야 합니다.
|
|
62
62
|
|
|
63
63
|
\`\`\`
|
|
64
|
-
lib → store → api → hooks → context → components →
|
|
64
|
+
lib → store → api → hooks → context → components → layouts → pages
|
|
65
65
|
\`\`\`
|
|
66
66
|
|
|
67
67
|
\`assets\`는 모든 레이어에서 자유롭게 참조 가능합니다.`
|