11import { exec } from 'child_process' ;
22import fs from 'fs' ;
3+ import { readdir } from 'fs/promises' ;
34import inquirer from 'inquirer' ;
5+ import inquirerPrompt from 'inquirer-autocomplete-prompt' ;
6+
7+ inquirer . registerPrompt ( 'autocomplete' , inquirerPrompt ) ;
48
59const PAGE_DIR = './src/pages' ;
610const PAGE_STYLED_DIR = './src/styles/pageStyled' ;
711const 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
1323const createIndexFileText = name => {
1424 return [ `export * from './${ name } ';` , `export { default } from './${ name } ';` , `` ] . join ( '\n' ) ;
1525} ;
1626
1727const 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+
6992const 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+
82147const 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
94159const 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 }
0 commit comments