byuckchon-frontend-cli 1.0.2 → 1.1.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 +25 -1
- package/package.json +1 -1
- package/src/commands/init.js +17 -13
- package/src/generators/createBaseFiles.js +211 -190
- package/src/generators/createPackageJson.js +1 -1
package/README.md
CHANGED
|
@@ -1,2 +1,26 @@
|
|
|
1
1
|
# byuckchon-frontend-cli
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
[byuckchon](https://www.byuckchon.com) 컨벤션에 맞게 React(Vite) 또는 Next.js(App Router) TypeScript 프로젝트를 생성하는 CLI.
|
|
4
|
+
|
|
5
|
+
## 요구 사항
|
|
6
|
+
|
|
7
|
+
- [Node.js](https://nodejs.org/) (LTS 권장)
|
|
8
|
+
|
|
9
|
+
## 사용법
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx byuckchon-frontend-cli
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
프로젝트 이름과 프레임워크를 묻는 프롬프트에 따라 답하면, 현재 디렉터리에 새 폴더가 만들어집니다.
|
|
16
|
+
|
|
17
|
+
생성이 끝나면 안내에 따라 다음을 실행하세요.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cd <프로젝트-이름>
|
|
21
|
+
npm run dev
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 라이선스
|
|
25
|
+
|
|
26
|
+
MIT
|
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -1,37 +1,41 @@
|
|
|
1
|
-
import chalk from
|
|
1
|
+
import chalk from "chalk";
|
|
2
2
|
|
|
3
|
-
import { createProject } from
|
|
4
|
-
import { askInitQuestions } from
|
|
3
|
+
import { createProject } from "../generators/createProject.js";
|
|
4
|
+
import { askInitQuestions } from "../prompts/initPrompts.js";
|
|
5
5
|
|
|
6
6
|
export async function initCommand() {
|
|
7
|
-
console.log(
|
|
7
|
+
console.log(
|
|
8
|
+
chalk.bold.cyan("\n byuckchon-frontend-cli — 프로젝트 생성기\n")
|
|
9
|
+
);
|
|
8
10
|
|
|
9
11
|
try {
|
|
10
12
|
const answers = await askInitQuestions();
|
|
11
13
|
|
|
12
14
|
console.log(
|
|
13
15
|
chalk.dim(
|
|
14
|
-
`\n ${answers.framework} 프로젝트를 생성하는 중... (${answers.projectName})\n
|
|
15
|
-
)
|
|
16
|
+
`\n ${answers.framework} 프로젝트를 생성하는 중... (${answers.projectName})\n`
|
|
17
|
+
)
|
|
16
18
|
);
|
|
17
19
|
|
|
18
20
|
await createProject({ ...answers, typescript: true });
|
|
19
21
|
|
|
20
22
|
console.log(
|
|
21
|
-
chalk.bold.green(
|
|
23
|
+
chalk.bold.green(
|
|
24
|
+
`\n ✓ ${answers.projectName} 프로젝트가 생성되었습니다!\n`
|
|
25
|
+
)
|
|
22
26
|
);
|
|
23
|
-
console.log(chalk.yellow(
|
|
27
|
+
console.log(chalk.yellow(" 다음 명령어로 시작하세요:\n"));
|
|
24
28
|
console.log(chalk.white(` cd ${answers.projectName}`));
|
|
25
|
-
console.log(chalk.white(
|
|
29
|
+
console.log(chalk.white(" npm run dev\n"));
|
|
26
30
|
} catch (error) {
|
|
27
|
-
if (error.code ===
|
|
31
|
+
if (error.code === "EEXIST") {
|
|
28
32
|
console.error(
|
|
29
|
-
chalk.red(`\n 오류: '${error.path}' 폴더가 이미 존재합니다.\n`)
|
|
33
|
+
chalk.red(`\n 오류: '${error.path}' 폴더가 이미 존재합니다.\n`)
|
|
30
34
|
);
|
|
31
35
|
} else {
|
|
32
36
|
console.error(
|
|
33
|
-
chalk.red(
|
|
34
|
-
error.message
|
|
37
|
+
chalk.red("\n 프로젝트 생성 중 오류가 발생했습니다:"),
|
|
38
|
+
error.message
|
|
35
39
|
);
|
|
36
40
|
}
|
|
37
41
|
process.exit(1);
|
|
@@ -1,34 +1,114 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
3
|
|
|
4
4
|
async function write(filePath, content) {
|
|
5
|
-
await fs.writeFile(filePath, content,
|
|
5
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
const TOKEN_CONFIG_JS = `import StyleDictionary from "style-dictionary";
|
|
9
|
+
|
|
10
|
+
// kebab-case 변환
|
|
11
|
+
StyleDictionary.registerTransform({
|
|
12
|
+
name: "name/kebab",
|
|
13
|
+
type: "name",
|
|
14
|
+
transform: (token) =>
|
|
15
|
+
token.path
|
|
16
|
+
.join("-")
|
|
17
|
+
.replace(/([a-z])([A-Z])/g, "$1-$2")
|
|
18
|
+
.toLowerCase(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// color는 Tailwind 유틸리티로, typography는 .text-* 클래스로 생성
|
|
22
|
+
StyleDictionary.registerFormat({
|
|
23
|
+
name: "css/tailwind-theme",
|
|
24
|
+
format: ({ dictionary }) => {
|
|
25
|
+
let css = "";
|
|
26
|
+
const withPx = (value) =>
|
|
27
|
+
typeof value === "string" && /^\\d+(\\.\\d+)?$/.test(value)
|
|
28
|
+
? \`\${value}px\`
|
|
29
|
+
: value;
|
|
30
|
+
|
|
31
|
+
css += "@theme {\\n";
|
|
32
|
+
dictionary.allTokens.forEach((token) => {
|
|
33
|
+
if (token.$type === "color") {
|
|
34
|
+
css += \` --color-\${token.name}: \${token.$value};\\n\`;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
css += "}\\n\\n";
|
|
38
|
+
|
|
39
|
+
css += "@layer components {\\n";
|
|
40
|
+
dictionary.allTokens.forEach((token) => {
|
|
41
|
+
if (token.$type === "typography" && token.$value) {
|
|
42
|
+
const typo = token.$value;
|
|
43
|
+
css += \` .text-\${token.name} {\\n\`;
|
|
44
|
+
if (typo.fontSize) {
|
|
45
|
+
css += \` font-size: \${withPx(typo.fontSize)};\\n\`;
|
|
46
|
+
}
|
|
47
|
+
if (typo.lineHeight) {
|
|
48
|
+
css += \` line-height: \${withPx(typo.lineHeight)};\\n\`;
|
|
49
|
+
}
|
|
50
|
+
if (typo.letterSpacing) {
|
|
51
|
+
css += \` letter-spacing: \${typo.letterSpacing};\\n\`;
|
|
52
|
+
}
|
|
53
|
+
if (typo.fontWeight) {
|
|
54
|
+
css += \` font-weight: \${typo.fontWeight};\\n\`;
|
|
55
|
+
}
|
|
56
|
+
if (typo.fontFamily) {
|
|
57
|
+
css += \` font-family: \${typo.fontFamily};\\n\`;
|
|
58
|
+
}
|
|
59
|
+
css += " }\\n";
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
css += "}\\n";
|
|
63
|
+
|
|
64
|
+
return css;
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export default {
|
|
69
|
+
source: ["src/tokens.json"],
|
|
70
|
+
platforms: {
|
|
71
|
+
css: {
|
|
72
|
+
transforms: ["name/kebab"], // 일단 attribute/cti 제거
|
|
73
|
+
buildPath: "src/",
|
|
74
|
+
files: [
|
|
75
|
+
{
|
|
76
|
+
destination: "tokens.css",
|
|
77
|
+
format: "css/tailwind-theme",
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
`;
|
|
84
|
+
|
|
8
85
|
// ─── 공통 설정 파일 ────────────────────────────────────────────────────────────
|
|
9
86
|
|
|
10
87
|
async function createPrettierConfig(rootDir) {
|
|
11
88
|
const config = {
|
|
12
89
|
semi: true,
|
|
13
|
-
trailingComma:
|
|
90
|
+
trailingComma: "all",
|
|
14
91
|
singleQuote: true,
|
|
15
92
|
tabWidth: 2,
|
|
16
93
|
useTabs: false,
|
|
17
94
|
printWidth: 80,
|
|
18
95
|
plugins: [
|
|
19
|
-
|
|
20
|
-
|
|
96
|
+
"@trivago/prettier-plugin-sort-imports",
|
|
97
|
+
"prettier-plugin-tailwindcss",
|
|
21
98
|
],
|
|
22
|
-
importOrder: [
|
|
99
|
+
importOrder: ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"],
|
|
23
100
|
importOrderSeparation: true,
|
|
24
101
|
importOrderSortSpecifiers: true,
|
|
25
102
|
};
|
|
26
|
-
await write(
|
|
103
|
+
await write(
|
|
104
|
+
path.join(rootDir, ".prettierrc"),
|
|
105
|
+
JSON.stringify(config, null, 2)
|
|
106
|
+
);
|
|
27
107
|
}
|
|
28
108
|
|
|
29
109
|
async function createEslintConfig(rootDir) {
|
|
30
110
|
await write(
|
|
31
|
-
path.join(rootDir,
|
|
111
|
+
path.join(rootDir, ".eslintrc.cjs"),
|
|
32
112
|
`module.exports = {
|
|
33
113
|
env: {
|
|
34
114
|
browser: true,
|
|
@@ -62,13 +142,13 @@ async function createEslintConfig(rootDir) {
|
|
|
62
142
|
},
|
|
63
143
|
},
|
|
64
144
|
};
|
|
65
|
-
|
|
145
|
+
`
|
|
66
146
|
);
|
|
67
147
|
}
|
|
68
148
|
|
|
69
149
|
async function createNextEslintConfig(rootDir) {
|
|
70
150
|
await write(
|
|
71
|
-
path.join(rootDir,
|
|
151
|
+
path.join(rootDir, ".eslintrc.cjs"),
|
|
72
152
|
`// 현 파일이 eslint config type 을 따른다는 선언
|
|
73
153
|
/** @type {import("eslint").Linter.Config} */
|
|
74
154
|
|
|
@@ -116,7 +196,7 @@ module.exports = {
|
|
|
116
196
|
// 정렬 제외 파일 목록
|
|
117
197
|
ignorePatterns: ["node_modules/", ".next/", "out/", "build/", "next-env.d.ts"],
|
|
118
198
|
};
|
|
119
|
-
|
|
199
|
+
`
|
|
120
200
|
);
|
|
121
201
|
}
|
|
122
202
|
|
|
@@ -164,31 +244,34 @@ dist-ssr
|
|
|
164
244
|
out/
|
|
165
245
|
`;
|
|
166
246
|
|
|
167
|
-
await write(
|
|
247
|
+
await write(
|
|
248
|
+
path.join(rootDir, ".gitignore"),
|
|
249
|
+
base + (framework === "next" ? nextExtra : "")
|
|
250
|
+
);
|
|
168
251
|
}
|
|
169
252
|
|
|
170
253
|
async function createVscodeSettings(rootDir) {
|
|
171
|
-
await fs.mkdir(path.join(rootDir,
|
|
254
|
+
await fs.mkdir(path.join(rootDir, ".vscode"), { recursive: true });
|
|
172
255
|
await write(
|
|
173
|
-
path.join(rootDir,
|
|
256
|
+
path.join(rootDir, ".vscode/settings.json"),
|
|
174
257
|
JSON.stringify(
|
|
175
258
|
{
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
259
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
260
|
+
"editor.formatOnSave": true,
|
|
261
|
+
"eslint.validate": [
|
|
262
|
+
"javascript",
|
|
263
|
+
"typescript",
|
|
264
|
+
"javascriptreact",
|
|
265
|
+
"typescriptreact",
|
|
183
266
|
],
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
267
|
+
"editor.codeActionsOnSave": {
|
|
268
|
+
"source.organizeImports": "always",
|
|
269
|
+
"source.fixAll.eslint": "always",
|
|
187
270
|
},
|
|
188
271
|
},
|
|
189
272
|
null,
|
|
190
|
-
2
|
|
191
|
-
)
|
|
273
|
+
2
|
|
274
|
+
)
|
|
192
275
|
);
|
|
193
276
|
}
|
|
194
277
|
|
|
@@ -197,7 +280,7 @@ async function createVscodeSettings(rootDir) {
|
|
|
197
280
|
async function createReactBaseFiles(rootDir, config) {
|
|
198
281
|
// index.html
|
|
199
282
|
await write(
|
|
200
|
-
path.join(rootDir,
|
|
283
|
+
path.join(rootDir, "index.html"),
|
|
201
284
|
`<!doctype html>
|
|
202
285
|
<html lang="ko">
|
|
203
286
|
<head>
|
|
@@ -210,13 +293,16 @@ async function createReactBaseFiles(rootDir, config) {
|
|
|
210
293
|
<script type="module" src="/src/main.tsx"></script>
|
|
211
294
|
</body>
|
|
212
295
|
</html>
|
|
213
|
-
|
|
296
|
+
`
|
|
297
|
+
);
|
|
298
|
+
await write(
|
|
299
|
+
path.join(rootDir, "public/robots.txt"),
|
|
300
|
+
`User-agent: *\nDisallow: /\n`
|
|
214
301
|
);
|
|
215
|
-
await write(path.join(rootDir, 'public/robots.txt'), `User-agent: *\nDisallow: /\n`);
|
|
216
302
|
|
|
217
303
|
// vite.config.ts
|
|
218
304
|
await write(
|
|
219
|
-
path.join(rootDir,
|
|
305
|
+
path.join(rootDir, "vite.config.ts"),
|
|
220
306
|
`import tailwindcss from '@tailwindcss/vite';
|
|
221
307
|
import react from '@vitejs/plugin-react';
|
|
222
308
|
import { defineConfig } from 'vite';
|
|
@@ -241,74 +327,77 @@ export default defineConfig({
|
|
|
241
327
|
},
|
|
242
328
|
},
|
|
243
329
|
});
|
|
244
|
-
|
|
330
|
+
`
|
|
245
331
|
);
|
|
246
332
|
|
|
247
333
|
// tsconfig.json
|
|
248
334
|
await write(
|
|
249
|
-
path.join(rootDir,
|
|
335
|
+
path.join(rootDir, "tsconfig.json"),
|
|
250
336
|
JSON.stringify(
|
|
251
337
|
{
|
|
252
338
|
files: [],
|
|
253
|
-
references: [
|
|
339
|
+
references: [
|
|
340
|
+
{ path: "./tsconfig.app.json" },
|
|
341
|
+
{ path: "./tsconfig.node.json" },
|
|
342
|
+
],
|
|
254
343
|
},
|
|
255
344
|
null,
|
|
256
|
-
2
|
|
257
|
-
)
|
|
345
|
+
2
|
|
346
|
+
)
|
|
258
347
|
);
|
|
259
348
|
|
|
260
349
|
// tsconfig.app.json
|
|
261
350
|
await write(
|
|
262
|
-
path.join(rootDir,
|
|
351
|
+
path.join(rootDir, "tsconfig.app.json"),
|
|
263
352
|
JSON.stringify(
|
|
264
353
|
{
|
|
265
354
|
compilerOptions: {
|
|
266
|
-
tsBuildInfoFile:
|
|
267
|
-
target:
|
|
355
|
+
tsBuildInfoFile: "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
356
|
+
target: "ES2020",
|
|
268
357
|
useDefineForClassFields: true,
|
|
269
|
-
lib: [
|
|
270
|
-
module:
|
|
358
|
+
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
359
|
+
module: "ESNext",
|
|
271
360
|
skipLibCheck: true,
|
|
272
|
-
moduleResolution:
|
|
361
|
+
moduleResolution: "bundler",
|
|
273
362
|
allowImportingTsExtensions: true,
|
|
274
363
|
isolatedModules: true,
|
|
275
|
-
moduleDetection:
|
|
364
|
+
moduleDetection: "force",
|
|
276
365
|
noEmit: true,
|
|
277
|
-
jsx:
|
|
366
|
+
jsx: "react-jsx",
|
|
278
367
|
strict: true,
|
|
279
368
|
noUnusedLocals: true,
|
|
280
369
|
noUnusedParameters: true,
|
|
281
370
|
noFallthroughCasesInSwitch: true,
|
|
282
371
|
noUncheckedSideEffectImports: true,
|
|
283
|
-
baseUrl:
|
|
372
|
+
baseUrl: ".",
|
|
284
373
|
paths: {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
374
|
+
"@/*": ["src/*"],
|
|
375
|
+
"@icons/*": ["src/assets/icons/*"],
|
|
376
|
+
"@images/*": ["src/assets/images/*"],
|
|
288
377
|
},
|
|
289
378
|
},
|
|
290
|
-
include: [
|
|
379
|
+
include: ["src", "src/svg.d.ts"],
|
|
291
380
|
},
|
|
292
381
|
null,
|
|
293
|
-
2
|
|
294
|
-
)
|
|
382
|
+
2
|
|
383
|
+
)
|
|
295
384
|
);
|
|
296
385
|
|
|
297
386
|
// tsconfig.node.json
|
|
298
387
|
await write(
|
|
299
|
-
path.join(rootDir,
|
|
388
|
+
path.join(rootDir, "tsconfig.node.json"),
|
|
300
389
|
JSON.stringify(
|
|
301
390
|
{
|
|
302
391
|
compilerOptions: {
|
|
303
|
-
tsBuildInfoFile:
|
|
304
|
-
target:
|
|
305
|
-
lib: [
|
|
306
|
-
module:
|
|
392
|
+
tsBuildInfoFile: "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
393
|
+
target: "ES2022",
|
|
394
|
+
lib: ["ES2023"],
|
|
395
|
+
module: "ESNext",
|
|
307
396
|
skipLibCheck: true,
|
|
308
|
-
moduleResolution:
|
|
397
|
+
moduleResolution: "bundler",
|
|
309
398
|
allowImportingTsExtensions: true,
|
|
310
399
|
isolatedModules: true,
|
|
311
|
-
moduleDetection:
|
|
400
|
+
moduleDetection: "force",
|
|
312
401
|
noEmit: true,
|
|
313
402
|
strict: true,
|
|
314
403
|
noUnusedLocals: true,
|
|
@@ -316,125 +405,47 @@ export default defineConfig({
|
|
|
316
405
|
noFallthroughCasesInSwitch: true,
|
|
317
406
|
noUncheckedSideEffectImports: true,
|
|
318
407
|
},
|
|
319
|
-
include: [
|
|
408
|
+
include: ["vite.config.ts"],
|
|
320
409
|
},
|
|
321
410
|
null,
|
|
322
|
-
2
|
|
323
|
-
)
|
|
411
|
+
2
|
|
412
|
+
)
|
|
324
413
|
);
|
|
325
414
|
|
|
326
415
|
await createPrettierConfig(rootDir);
|
|
327
416
|
await createEslintConfig(rootDir);
|
|
328
|
-
await createGitignore(rootDir,
|
|
417
|
+
await createGitignore(rootDir, "react");
|
|
329
418
|
await createVscodeSettings(rootDir);
|
|
330
|
-
await write(
|
|
331
|
-
path.join(rootDir, 'token.config.js'),
|
|
332
|
-
`import StyleDictionary from "style-dictionary";
|
|
333
|
-
|
|
334
|
-
// kebab-case 변환
|
|
335
|
-
StyleDictionary.registerTransform({
|
|
336
|
-
name: "name/kebab",
|
|
337
|
-
type: "name",
|
|
338
|
-
transform: (token) =>
|
|
339
|
-
token.path
|
|
340
|
-
.join("-")
|
|
341
|
-
.replace(/([a-z])([A-Z])/g, "$1-$2")
|
|
342
|
-
.toLowerCase(),
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
// color는 Tailwind 유틸리티로, typography는 .text-* 클래스로 생성
|
|
346
|
-
StyleDictionary.registerFormat({
|
|
347
|
-
name: "css/tailwind-theme",
|
|
348
|
-
format: ({ dictionary }) => {
|
|
349
|
-
let css = "";
|
|
350
|
-
const withPx = (value) =>
|
|
351
|
-
typeof value === "string" && /^\\d+(\\.\\d+)?$/.test(value)
|
|
352
|
-
? \`\${value}px\`
|
|
353
|
-
: value;
|
|
354
|
-
|
|
355
|
-
css += "@theme {\\n";
|
|
356
|
-
dictionary.allTokens.forEach((token) => {
|
|
357
|
-
if (token.$type === "color") {
|
|
358
|
-
css += \` --color-\${token.name}: \${token.$value};\\n\`;
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
css += "}\\n\\n";
|
|
362
|
-
|
|
363
|
-
css += "@layer components {\\n";
|
|
364
|
-
dictionary.allTokens.forEach((token) => {
|
|
365
|
-
if (token.$type === "typography" && token.$value) {
|
|
366
|
-
const typo = token.$value;
|
|
367
|
-
css += \` .text-\${token.name} {\\n\`;
|
|
368
|
-
if (typo.fontSize) {
|
|
369
|
-
css += \` font-size: \${withPx(typo.fontSize)};\\n\`;
|
|
370
|
-
}
|
|
371
|
-
if (typo.lineHeight) {
|
|
372
|
-
css += \` line-height: \${withPx(typo.lineHeight)};\\n\`;
|
|
373
|
-
}
|
|
374
|
-
if (typo.letterSpacing) {
|
|
375
|
-
css += \` letter-spacing: \${typo.letterSpacing};\\n\`;
|
|
376
|
-
}
|
|
377
|
-
if (typo.fontWeight) {
|
|
378
|
-
css += \` font-weight: \${typo.fontWeight};\\n\`;
|
|
379
|
-
}
|
|
380
|
-
if (typo.fontFamily) {
|
|
381
|
-
css += \` font-family: \${typo.fontFamily};\\n\`;
|
|
382
|
-
}
|
|
383
|
-
css += " }\\n";
|
|
384
|
-
}
|
|
385
|
-
});
|
|
386
|
-
css += "}\\n";
|
|
387
|
-
|
|
388
|
-
return css;
|
|
389
|
-
},
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
export default {
|
|
393
|
-
source: ["src/tokens.json"],
|
|
394
|
-
platforms: {
|
|
395
|
-
css: {
|
|
396
|
-
transforms: ["name/kebab"], // 일단 attribute/cti 제거
|
|
397
|
-
buildPath: "src/",
|
|
398
|
-
files: [
|
|
399
|
-
{
|
|
400
|
-
destination: "tokens.css",
|
|
401
|
-
format: "css/tailwind-theme",
|
|
402
|
-
},
|
|
403
|
-
],
|
|
404
|
-
},
|
|
405
|
-
},
|
|
406
|
-
};
|
|
407
|
-
`,
|
|
408
|
-
);
|
|
419
|
+
await write(path.join(rootDir, "token.config.js"), TOKEN_CONFIG_JS);
|
|
409
420
|
|
|
410
421
|
// src/App.css
|
|
411
422
|
await write(
|
|
412
|
-
path.join(rootDir,
|
|
423
|
+
path.join(rootDir, "src/App.css"),
|
|
413
424
|
`@import "./tokens.css";
|
|
414
425
|
@import 'tailwindcss';
|
|
415
|
-
|
|
426
|
+
`
|
|
416
427
|
);
|
|
417
|
-
await write(path.join(rootDir,
|
|
418
|
-
await write(path.join(rootDir,
|
|
428
|
+
await write(path.join(rootDir, "src/tokens.css"), "");
|
|
429
|
+
await write(path.join(rootDir, "src/tokens.json"), "{}\n");
|
|
419
430
|
|
|
420
431
|
// src/main.tsx
|
|
421
432
|
await write(
|
|
422
|
-
path.join(rootDir,
|
|
433
|
+
path.join(rootDir, "src/main.tsx"),
|
|
423
434
|
`import { createRoot } from 'react-dom/client';
|
|
424
435
|
|
|
425
436
|
import App from './App.tsx';
|
|
426
437
|
|
|
427
438
|
createRoot(document.getElementById('root')!).render(<App />);
|
|
428
|
-
|
|
439
|
+
`
|
|
429
440
|
);
|
|
430
441
|
|
|
431
442
|
await write(
|
|
432
|
-
path.join(rootDir,
|
|
433
|
-
`/// <reference types="vite/client" />\n
|
|
443
|
+
path.join(rootDir, "src/vite-env.d.ts"),
|
|
444
|
+
`/// <reference types="vite/client" />\n`
|
|
434
445
|
);
|
|
435
446
|
|
|
436
447
|
await write(
|
|
437
|
-
path.join(rootDir,
|
|
448
|
+
path.join(rootDir, "src/global.d.ts"),
|
|
438
449
|
`declare module '*.svg' {
|
|
439
450
|
import React from 'react';
|
|
440
451
|
export const ReactComponent: React.FunctionComponent<
|
|
@@ -456,12 +467,12 @@ declare module '*.webp' {
|
|
|
456
467
|
const value: any;
|
|
457
468
|
export = value;
|
|
458
469
|
}
|
|
459
|
-
|
|
470
|
+
`
|
|
460
471
|
);
|
|
461
472
|
|
|
462
473
|
// src/App.tsx
|
|
463
474
|
await write(
|
|
464
|
-
path.join(rootDir,
|
|
475
|
+
path.join(rootDir, "src/App.tsx"),
|
|
465
476
|
`import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
466
477
|
|
|
467
478
|
import './App.css';
|
|
@@ -487,7 +498,7 @@ function App() {
|
|
|
487
498
|
}
|
|
488
499
|
|
|
489
500
|
export default App;
|
|
490
|
-
|
|
501
|
+
`
|
|
491
502
|
);
|
|
492
503
|
}
|
|
493
504
|
|
|
@@ -496,7 +507,7 @@ export default App;
|
|
|
496
507
|
async function createNextBaseFiles(rootDir, config) {
|
|
497
508
|
// next.config.ts
|
|
498
509
|
await write(
|
|
499
|
-
path.join(rootDir,
|
|
510
|
+
path.join(rootDir, "next.config.ts"),
|
|
500
511
|
`import type { NextConfig } from "next";
|
|
501
512
|
|
|
502
513
|
const nextConfig: NextConfig = {
|
|
@@ -520,73 +531,83 @@ const nextConfig: NextConfig = {
|
|
|
520
531
|
};
|
|
521
532
|
|
|
522
533
|
export default nextConfig;
|
|
523
|
-
|
|
534
|
+
`
|
|
535
|
+
);
|
|
536
|
+
await write(
|
|
537
|
+
path.join(rootDir, "public/robots.txt"),
|
|
538
|
+
`User-agent: *\nDisallow: /\n`
|
|
524
539
|
);
|
|
525
|
-
await write(path.join(rootDir, 'public/robots.txt'), `User-agent: *\nDisallow: /\n`);
|
|
526
540
|
|
|
527
541
|
// tsconfig.json (Next.js)
|
|
528
542
|
await write(
|
|
529
|
-
path.join(rootDir,
|
|
543
|
+
path.join(rootDir, "tsconfig.json"),
|
|
530
544
|
JSON.stringify(
|
|
531
545
|
{
|
|
532
546
|
compilerOptions: {
|
|
533
|
-
target:
|
|
534
|
-
lib: [
|
|
547
|
+
target: "ES2017",
|
|
548
|
+
lib: ["dom", "dom.iterable", "esnext"],
|
|
535
549
|
allowJs: true,
|
|
536
550
|
skipLibCheck: true,
|
|
537
551
|
strict: true,
|
|
538
552
|
noEmit: true,
|
|
539
553
|
esModuleInterop: true,
|
|
540
|
-
module:
|
|
541
|
-
moduleResolution:
|
|
554
|
+
module: "esnext",
|
|
555
|
+
moduleResolution: "bundler",
|
|
542
556
|
resolveJsonModule: true,
|
|
543
557
|
isolatedModules: true,
|
|
544
|
-
jsx:
|
|
558
|
+
jsx: "preserve",
|
|
545
559
|
incremental: true,
|
|
546
|
-
plugins: [{ name:
|
|
547
|
-
paths: {
|
|
560
|
+
plugins: [{ name: "next" }],
|
|
561
|
+
paths: { "@/*": ["./src/*"] },
|
|
548
562
|
},
|
|
549
|
-
include: [
|
|
550
|
-
|
|
563
|
+
include: [
|
|
564
|
+
"next-env.d.ts",
|
|
565
|
+
"**/*.ts",
|
|
566
|
+
"**/*.tsx",
|
|
567
|
+
".next/types/**/*.ts",
|
|
568
|
+
],
|
|
569
|
+
exclude: ["node_modules"],
|
|
551
570
|
},
|
|
552
571
|
null,
|
|
553
|
-
2
|
|
554
|
-
)
|
|
572
|
+
2
|
|
573
|
+
)
|
|
555
574
|
);
|
|
556
575
|
|
|
557
576
|
await createPrettierConfig(rootDir);
|
|
558
577
|
await createNextEslintConfig(rootDir);
|
|
559
|
-
await createGitignore(rootDir,
|
|
578
|
+
await createGitignore(rootDir, "next");
|
|
560
579
|
await createVscodeSettings(rootDir);
|
|
561
580
|
await write(
|
|
562
|
-
path.join(rootDir,
|
|
581
|
+
path.join(rootDir, "postcss.config.mjs"),
|
|
563
582
|
`const config = {
|
|
564
583
|
plugins: ["@tailwindcss/postcss"],
|
|
565
584
|
};
|
|
566
585
|
|
|
567
586
|
export default config;
|
|
568
|
-
|
|
587
|
+
`
|
|
569
588
|
);
|
|
570
589
|
|
|
571
590
|
// src/app/globals.css
|
|
572
591
|
await write(
|
|
573
|
-
path.join(rootDir,
|
|
574
|
-
`@import "../
|
|
592
|
+
path.join(rootDir, "src/app/globals.css"),
|
|
593
|
+
`@import "../tokens.css";
|
|
575
594
|
@import 'tailwindcss';
|
|
576
|
-
|
|
595
|
+
`
|
|
577
596
|
);
|
|
578
|
-
await write(path.join(rootDir,
|
|
597
|
+
await write(path.join(rootDir, "token.config.js"), TOKEN_CONFIG_JS);
|
|
598
|
+
await write(path.join(rootDir, "src/tokens.css"), "");
|
|
599
|
+
await write(path.join(rootDir, "src/tokens.json"), "{}\n");
|
|
579
600
|
|
|
580
601
|
// src/app/layout.tsx
|
|
581
602
|
await write(
|
|
582
|
-
path.join(rootDir,
|
|
603
|
+
path.join(rootDir, "src/app/layout.tsx"),
|
|
583
604
|
`import type { Metadata } from 'next';
|
|
584
605
|
|
|
585
606
|
import './globals.css';
|
|
586
607
|
|
|
587
608
|
export const metadata: Metadata = {
|
|
588
609
|
title: '${config.projectName}',
|
|
589
|
-
description: 'Generated by cli
|
|
610
|
+
description: 'Generated by byuckchon-frontend-cli',
|
|
590
611
|
};
|
|
591
612
|
|
|
592
613
|
export default function RootLayout({
|
|
@@ -603,12 +624,12 @@ export default function RootLayout({
|
|
|
603
624
|
</html>
|
|
604
625
|
);
|
|
605
626
|
}
|
|
606
|
-
|
|
627
|
+
`
|
|
607
628
|
);
|
|
608
629
|
|
|
609
630
|
// src/app/page.tsx
|
|
610
631
|
await write(
|
|
611
|
-
path.join(rootDir,
|
|
632
|
+
path.join(rootDir, "src/app/page.tsx"),
|
|
612
633
|
`export default function MainPage() {
|
|
613
634
|
return (
|
|
614
635
|
<main className="flex min-h-screen items-center justify-center bg-gray-50">
|
|
@@ -622,29 +643,29 @@ export default function RootLayout({
|
|
|
622
643
|
</main>
|
|
623
644
|
);
|
|
624
645
|
}
|
|
625
|
-
|
|
646
|
+
`
|
|
626
647
|
);
|
|
627
648
|
|
|
628
649
|
await write(
|
|
629
|
-
path.join(rootDir,
|
|
650
|
+
path.join(rootDir, "src/app/error.tsx"),
|
|
630
651
|
`'use client';
|
|
631
652
|
|
|
632
653
|
export default function Error() {
|
|
633
654
|
return <div>Something went wrong.</div>;
|
|
634
655
|
}
|
|
635
|
-
|
|
656
|
+
`
|
|
636
657
|
);
|
|
637
658
|
|
|
638
659
|
await write(
|
|
639
|
-
path.join(rootDir,
|
|
660
|
+
path.join(rootDir, "src/app/not-found.tsx"),
|
|
640
661
|
`export default function NotFound() {
|
|
641
662
|
return <div>Page not found.</div>;
|
|
642
663
|
}
|
|
643
|
-
|
|
664
|
+
`
|
|
644
665
|
);
|
|
645
666
|
|
|
646
667
|
await write(
|
|
647
|
-
path.join(rootDir,
|
|
668
|
+
path.join(rootDir, "src/global.d.ts"),
|
|
648
669
|
`declare module '*.svg' {
|
|
649
670
|
import React from 'react';
|
|
650
671
|
export const ReactComponent: React.FunctionComponent<
|
|
@@ -666,26 +687,26 @@ declare module '*.webp' {
|
|
|
666
687
|
const value: any;
|
|
667
688
|
export = value;
|
|
668
689
|
}
|
|
669
|
-
|
|
690
|
+
`
|
|
670
691
|
);
|
|
671
692
|
|
|
672
693
|
await write(
|
|
673
|
-
path.join(rootDir,
|
|
694
|
+
path.join(rootDir, "src/types.d.ts"),
|
|
674
695
|
`declare module "*.svg" {
|
|
675
696
|
import React from "react";
|
|
676
697
|
const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
|
|
677
698
|
export default ReactComponent;
|
|
678
699
|
}
|
|
679
|
-
|
|
700
|
+
`
|
|
680
701
|
);
|
|
681
702
|
}
|
|
682
703
|
|
|
683
704
|
// ─── 진입점 ───────────────────────────────────────────────────────────────────
|
|
684
705
|
|
|
685
706
|
export async function createBaseFiles(rootDir, config) {
|
|
686
|
-
if (config.framework ===
|
|
707
|
+
if (config.framework === "react") {
|
|
687
708
|
await createReactBaseFiles(rootDir, config);
|
|
688
|
-
} else if (config.framework ===
|
|
709
|
+
} else if (config.framework === "next") {
|
|
689
710
|
await createNextBaseFiles(rootDir, config);
|
|
690
711
|
}
|
|
691
712
|
}
|