Skip to content
This repository was archived by the owner on Jun 11, 2024. It is now read-only.

Commit e6a10e6

Browse files
committed
코드 제너레이터 업데이트
1 parent b617b76 commit e6a10e6

File tree

3 files changed

+170
-11
lines changed

3 files changed

+170
-11
lines changed

generator.mjs

Lines changed: 136 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,46 @@
11
import { exec } from 'child_process';
22
import fs from 'fs';
3+
import { readdir } from 'fs/promises';
34
import inquirer from 'inquirer';
5+
import inquirerPrompt from 'inquirer-autocomplete-prompt';
6+
7+
inquirer.registerPrompt('autocomplete', inquirerPrompt);
48

59
const PAGE_DIR = './src/pages';
610
const PAGE_STYLED_DIR = './src/styles/pageStyled';
711
const COMPONENT_DIR = './src/components';
12+
const FEATURES_DIR = './src/features';
813

9-
function capitalize(str) {
14+
const capitalize = str => {
1015
return str.charAt(0).toUpperCase() + str.slice(1);
11-
}
16+
};
17+
18+
const getDirectories = async source =>
19+
(await readdir(source, { withFileTypes: true }))
20+
.filter(dirent => dirent.isDirectory())
21+
.map(dirent => dirent.name);
1222

1323
const createIndexFileText = name => {
1424
return [`export * from './${name}';`, `export { default } from './${name}';`, ``].join('\n');
1525
};
1626

1727
const createComponentFileText = name => {
1828
return [
29+
`import { ReactNode } from 'react';`,
30+
``,
1931
`import clsx from 'clsx';`,
2032
``,
2133
`import { ${name}Styled } from './styled';`,
2234
``,
2335
`export interface ${name}Props {`,
2436
` className?: string;`,
37+
` children?: ReactNode;`,
2538
`}`,
2639
``,
27-
`const ${name} = ({ className }: ${name}Props) => {`,
40+
`const ${name} = ({ className, children }: ${name}Props) => {`,
2841
` return (`,
2942
` <${name}Styled className={clsx('${name}', className)}>`,
30-
` `,
43+
` {children}`,
3144
` </${name}Styled>`,
3245
` );`,
3346
`};`,
@@ -66,6 +79,16 @@ const createPageFileText = name => {
6679
].join('\n');
6780
};
6881

82+
const createHookFileText = name => {
83+
// prettier-ignore
84+
return [
85+
`export const use${name} = () => {`,
86+
` return {};`,
87+
`}`,
88+
``,
89+
].join('\n');
90+
};
91+
6992
const createPromptInput = options => {
7093
const { name = 'name', label } = options;
7194

@@ -79,16 +102,58 @@ const createPromptInput = options => {
79102
};
80103
};
81104

105+
const editParentComponentExportFile = async parentComponentName => {
106+
const parentComponentDir = `${COMPONENT_DIR}/${parentComponentName}`;
107+
const parentComponentExportFile = `${parentComponentDir}/index.ts`;
108+
109+
const subComponentNames = await getDirectories(parentComponentDir);
110+
111+
let texts = [
112+
`// 자동으로 생성된 파일입니다. 수정하지 마세요.`,
113+
`import _${parentComponentName} from './${parentComponentName}';`,
114+
];
115+
116+
texts.push(
117+
...subComponentNames.map(
118+
subComponentName => `import ${subComponentName} from './${subComponentName}';`,
119+
),
120+
);
121+
122+
texts.push(
123+
...[
124+
``,
125+
`type _${parentComponentName} = typeof _${parentComponentName};`,
126+
``,
127+
`interface ${parentComponentName}Type extends _${parentComponentName} {`,
128+
...subComponentNames.map(
129+
subComponentName => ` ${subComponentName}: typeof ${subComponentName};`,
130+
),
131+
`}`,
132+
``,
133+
`const ${parentComponentName} = _${parentComponentName} as ${parentComponentName}Type;`,
134+
``,
135+
...subComponentNames.map(
136+
subComponentName => `${parentComponentName}.${subComponentName} = ${subComponentName};`,
137+
),
138+
``,
139+
`export default ${parentComponentName};`,
140+
``,
141+
],
142+
);
143+
144+
fs.writeFileSync(parentComponentExportFile, texts.join('\n'));
145+
};
146+
82147
const createComponentAndFileOpen = (dir, name) => {
83-
fs.mkdirSync(dir);
148+
fs.mkdirSync(dir, { recursive: true });
84149
fs.writeFileSync(`${dir}/styled.ts`, createStyledFileText(name));
85150
fs.writeFileSync(`${dir}/${name}.tsx`, createComponentFileText(name));
86151
fs.writeFileSync(`${dir}/index.ts`, createIndexFileText(name));
87152

88153
console.log(`🎉 Component [${name}] created`);
89154
console.log(`📂 Open file...`);
90155

91-
exec(`code -g ${dir}/${name}.tsx:12:7`);
156+
exec(`code -g ${dir}/${name}.tsx:15:17`);
92157
};
93158

94159
const start = async () => {
@@ -97,12 +162,37 @@ const start = async () => {
97162
type: 'list',
98163
name: 'type',
99164
message: 'Choose type',
100-
choices: ['component', 'page'],
101-
default: 'component',
165+
choices: ['feature', 'page', 'component', 'sub-component'],
166+
default: 'feature',
102167
},
103168
]);
104169

105170
switch (type) {
171+
case 'feature': {
172+
const { pageName, componentName } = await inquirer.prompt([
173+
createPromptInput({ name: 'pageName', label: 'Page name (camelCase)' }),
174+
createPromptInput({
175+
name: 'componentName',
176+
label: 'Component name (PascalCase)',
177+
}),
178+
]);
179+
180+
const pageDir = `${FEATURES_DIR}/${pageName}`;
181+
const componentDir = `${pageDir}/${componentName}`;
182+
183+
if (fs.existsSync(componentDir)) {
184+
console.log(`🛑 Component [${componentName}] already exists`);
185+
process.exit(0);
186+
}
187+
188+
if (!fs.existsSync(pageDir)) {
189+
fs.mkdirSync(pageDir, { recursive: true });
190+
}
191+
192+
createComponentAndFileOpen(componentDir, componentName);
193+
break;
194+
}
195+
106196
case 'component': {
107197
const { componentName } = await inquirer.prompt([
108198
createPromptInput({
@@ -119,6 +209,43 @@ const start = async () => {
119209
}
120210

121211
createComponentAndFileOpen(componentDir, componentName);
212+
213+
break;
214+
}
215+
216+
case 'sub-component': {
217+
const componentNames = await getDirectories(COMPONENT_DIR);
218+
219+
const { parentComponentName } = await inquirer.prompt([
220+
{
221+
type: 'autocomplete',
222+
name: 'parentComponentName',
223+
message: 'Choose component',
224+
source: (_, input) => {
225+
return componentNames.filter(name =>
226+
name.toLowerCase().includes((input || '').toLowerCase()),
227+
);
228+
},
229+
},
230+
]);
231+
232+
const { componentName } = await inquirer.prompt([
233+
createPromptInput({
234+
name: 'componentName',
235+
label: 'Sub component name (PascalCase)',
236+
}),
237+
]);
238+
239+
const componentDir = `${COMPONENT_DIR}/${parentComponentName}/${componentName}`;
240+
241+
if (fs.existsSync(componentDir)) {
242+
console.log(`🛑 Component [${componentName}] already exists`);
243+
process.exit(0);
244+
}
245+
246+
createComponentAndFileOpen(componentDir, componentName);
247+
await editParentComponentExportFile(parentComponentName);
248+
122249
break;
123250
}
124251

@@ -187,7 +314,7 @@ const start = async () => {
187314
console.log(`🎉 Page [${name}] created`);
188315
console.log(`📂 Open file...`);
189316

190-
exec(`code -g ${pagePath}:8:7`);
317+
exec(`code -g ${pagePath}:6:7`);
191318
break;
192319
}
193320
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"eslint-plugin-prettier": "^4.2.1",
5555
"eslint-plugin-react": "^7.30.1",
5656
"inquirer": "^9.1.0",
57+
"inquirer-autocomplete-prompt": "^3.0.0",
5758
"prettier": "^2.7.1",
5859
"typescript": "^4.6.4",
5960
"typescript-styled-plugin": "^0.18.2",
@@ -66,4 +67,4 @@
6667
"resolutions": {
6768
"react-virtualized": "git+https://[email protected]/remorses/react-virtualized-fixed-import.git#9.22.3"
6869
}
69-
}
70+
}

yarn.lock

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,13 @@ ansi-escapes@^5.0.0:
955955
dependencies:
956956
type-fest "^1.0.2"
957957

958+
ansi-escapes@^6.0.0:
959+
version "6.0.0"
960+
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-6.0.0.tgz#68c580e87a489f6df3d761028bb93093fde6bd8a"
961+
integrity sha512-IG23inYII3dWlU2EyiAiGj6Bwal5GzsgPMwjYGvc1HPE2dgbj4ZB5ToWBKSquKw74nB3TIuOwaI6/jSULzfgrw==
962+
dependencies:
963+
type-fest "^3.0.0"
964+
958965
ansi-regex@^5.0.1:
959966
version "5.0.1"
960967
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
@@ -2376,6 +2383,14 @@ figures@^4.0.1:
23762383
escape-string-regexp "^5.0.0"
23772384
is-unicode-supported "^1.2.0"
23782385

2386+
figures@^5.0.0:
2387+
version "5.0.0"
2388+
resolved "https://registry.yarnpkg.com/figures/-/figures-5.0.0.tgz#126cd055052dea699f8a54e8c9450e6ecfc44d5f"
2389+
integrity sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==
2390+
dependencies:
2391+
escape-string-regexp "^5.0.0"
2392+
is-unicode-supported "^1.2.0"
2393+
23792394
file-entry-cache@^6.0.1:
23802395
version "6.0.1"
23812396
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@@ -2842,6 +2857,17 @@ ini@^1.3.4:
28422857
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
28432858
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
28442859

2860+
inquirer-autocomplete-prompt@^3.0.0:
2861+
version "3.0.0"
2862+
resolved "https://registry.yarnpkg.com/inquirer-autocomplete-prompt/-/inquirer-autocomplete-prompt-3.0.0.tgz#b00478882feb326b34e0754a1695d912e387a63b"
2863+
integrity sha512-nsPWllBQB3qhvpVgV1UIJN4xo3yz7Qv8y1+zrNVpJUNPxtUZ7btCum/4UCAs5apPCe/FVhKH1V6Wx0cAwkreyg==
2864+
dependencies:
2865+
ansi-escapes "^6.0.0"
2866+
figures "^5.0.0"
2867+
picocolors "^1.0.0"
2868+
run-async "^2.4.1"
2869+
rxjs "^7.5.6"
2870+
28452871
inquirer@^9.1.0:
28462872
version "9.1.0"
28472873
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-9.1.0.tgz#446a09abe2e5a18973322bee89b42a6c304f2cd3"
@@ -4288,7 +4314,7 @@ rollup@^2.75.6:
42884314
optionalDependencies:
42894315
fsevents "~2.3.2"
42904316

4291-
run-async@^2.4.0:
4317+
run-async@^2.4.0, run-async@^2.4.1:
42924318
version "2.4.1"
42934319
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
42944320
integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
@@ -4772,6 +4798,11 @@ type-fest@^2.17.0:
47724798
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.18.0.tgz#fdef3a74e0a9e68ebe46054836650fb91ac3881e"
47734799
integrity sha512-pRS+/yrW5TjPPHNOvxhbNZexr2bS63WjrMU8a+VzEBhUi9Tz1pZeD+vQz3ut0svZ46P+SRqMEPnJmk2XnvNzTw==
47744800

4801+
type-fest@^3.0.0:
4802+
version "3.2.0"
4803+
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.2.0.tgz#2c8b49e775d9e314a73ea6fcee0b2e8549d5f886"
4804+
integrity sha512-Il3wdLRzWvbAEtocgxGQA9YOoRVeVUGOMBtel5LdEpNeEAol6GJTLw8GbX6Z8EIMfvfhoOXs2bwOijtAZdK5og==
4805+
47754806
typed-emitter@^2.1.0:
47764807
version "2.1.0"
47774808
resolved "https://registry.yarnpkg.com/typed-emitter/-/typed-emitter-2.1.0.tgz#ca78e3d8ef1476f228f548d62e04e3d4d3fd77fb"

0 commit comments

Comments
 (0)