22import process from 'node:process' ;
33import { rm } from 'node:fs/promises' ;
44import { resolve } from 'node:path' ;
5+ import { URL } from 'node:url' ;
6+ import { existsSync } from 'node:fs' ;
57
68import chalk from 'chalk' ;
79import { program } from 'commander' ;
810
911import { cloneTemplate } from './cloneTemplate.js' ;
1012import { isGitInstalled } from './isGitInstalled.js' ;
11- import { promptTemplate } from './prompts/promptTemplate/promptTemplate.js' ;
12- import { promptDirName } from './prompts/promptDirName.js' ;
13- import { promptGitRepo } from './prompts/promptGitRepo.js' ;
13+ import { promptTemplate } from './templates/promptTemplate/promptTemplate.js' ;
1414import { spawnWithSpinner } from './spawnWithSpinner.js' ;
1515import { lines } from './utils/lines.js' ;
16- import type { TemplateRepository } from './types.js' ;
16+ import type { TemplateRepository } from './templates/types.js' ;
17+ import { input } from './prompts/input.js' ;
18+ import { createCustomTheme } from './createCustomTheme.js' ;
1719
1820import packageJson from '../package.json' ;
1921
@@ -22,73 +24,118 @@ program
2224 . description ( packageJson . description )
2325 . version ( packageJson . version )
2426 . action ( async ( ) => {
27+ const theme = createCustomTheme ( ) ;
28+
2529 // Check if git is installed.
2630 if ( ! await isGitInstalled ( ) ) {
27- console . error ( 'To run this CLI tool, you must have git installed. Installation guide: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git' ) ;
31+ console . log (
32+ theme . style . error ( 'To run this CLI tool, you must have git installed. Installation guide: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git' ) ,
33+ ) ;
2834 process . exit ( 1 ) ;
2935 }
3036
31- // Prompt the project root directory name .
37+ // Step 1: Prompt the project root directory.
3238 let rootDir : string | null = null ;
3339 try {
34- rootDir = await promptDirName ( { defaultValue : 'mini-app' } ) ;
40+ rootDir = await input ( {
41+ message : 'Directory name:' ,
42+ required : true ,
43+ default : 'mini-app' ,
44+ hint : 'This directory will be used as a root directory for the project. It is allowed to use alphanumeric latin letters, dashes and dots.' ,
45+ theme,
46+ validate ( value ) {
47+ if ( [ '.' , '..' ] . includes ( value ) ) {
48+ return 'Value is not a valid directory name' ;
49+ }
50+
51+ if ( ! value . match ( / ^ [ a - z A - Z 0 - 9 \- . ] + $ / ) ) {
52+ return 'Value contains invalid symbols' ;
53+ }
54+
55+ if ( existsSync ( resolve ( value ) ) ) {
56+ return `Directory "${ value } " already exists` ;
57+ }
58+ } ,
59+ } ) ;
3560 } catch {
3661 process . exit ( 0 ) ;
3762 }
3863
39- // Prompt the target template.
64+ // Step 2: Prompt the target template.
4065 let repository : TemplateRepository ;
4166 try {
42- const { repository : promptRepo } = await promptTemplate ( { } ) ;
67+ const { repository : promptRepo } = await promptTemplate ( { theme } ) ;
4368 repository = promptRepo ;
4469 } catch {
4570 process . exit ( 0 ) ;
4671 }
4772
48- // Prompt Git repo information.
73+ // Step 3: Prompt future Git repository information.
4974 let gitRepo : string | undefined ;
5075 try {
51- gitRepo = await promptGitRepo ( { } ) ;
76+ gitRepo = await input ( {
77+ message : 'Git remote repository URL:' ,
78+ validate ( value ) {
79+ // Check if it is SSH connection string.
80+ if ( value . match ( / \w + @ [ \w \- . ] + : [ \w - ] + \/ [ \w . / ] + / ) ) {
81+ return ;
82+ }
83+
84+ // Check if the specified value is URL.
85+ try {
86+ new URL ( value ) ;
87+ return '' ;
88+ } catch {
89+ return 'Value is not considered as URL link or SSH connection string.' ;
90+ }
91+ } ,
92+ theme,
93+ hint : lines (
94+ 'This value will be used to connect created project with your remote Git repository. It should either be an HTTPS link or SSH connection string.' ,
95+ `Leave value empty and press ${ theme . style . key ( 'enter' ) } to skip this step.` ,
96+ chalk . bold ( 'Examples' ) ,
97+ 'SSH: [email protected] :user/repo.git' , 98+ 'URL: https://github.com/user/repo.git' ,
99+ ) ,
100+ } ) ;
52101 } catch {
53102 process . exit ( 0 ) ;
54103 }
55104
56- // Clone the template.
105+ // Step 4: Clone the template.
57106 try {
58- await cloneTemplate ( rootDir , repository ) ;
107+ await cloneTemplate ( rootDir , repository , theme ) ;
59108 } catch {
60109 process . exit ( 1 ) ;
61110 }
62111
63- // Remove the .git folder.
112+ // Step 5: Remove the .git folder.
64113 try {
65114 await spawnWithSpinner ( {
66- title : 'Removing the .git directory.' ,
115+ message : 'Removing the .git directory.' ,
67116 command : ( ) => rm ( resolve ( rootDir , '.git' ) , { recursive : true } ) ,
68- titleFail : ( err : string ) => `Failed to remove the .git directory. Error: ${ err } ` ,
69- titleSuccess : '.git directory removed.' ,
117+ messageFail : ( err : string ) => `Failed to remove the .git directory. Error: ${ err } ` ,
118+ messageSuccess : '.git directory removed.' ,
119+ theme,
70120 } ) ;
71121 } catch {
72122 process . exit ( 1 ) ;
73123 }
74124
75- // Initialize new .git folder if required .
125+ // Step 6: Initialize a new .git folder and configure remote .
76126 if ( gitRepo ) {
77127 try {
78128 await spawnWithSpinner ( {
79- title : `Initializing Git repository: ${ gitRepo } ` ,
129+ message : `Initializing Git repository: ${ gitRepo } ` ,
80130 command : [
81131 `cd "${ rootDir } "` ,
82132 'git init' ,
83- 'git add -A' ,
84- 'git commit -m "first commit"' ,
85- 'git branch -M master' ,
86133 `git remote add origin "${ gitRepo } "` ,
87- 'git push -u origin master' ,
88134 ] . join ( ' && ' ) ,
89- titleFail : ( error ) => `Failed to initialize Git repository. ${ error } ` ,
90- titleSuccess : 'Git repository initialized.' ,
91- } )
135+ messageFail : ( error ) => `Failed to initialize Git repository. ${ error } ` ,
136+ messageSuccess : `Git repository initialized. Remote "origin" was set to "${ gitRepo } "` ,
137+ theme,
138+ } ) ;
92139 } catch {
93140 // We are not doing anything as long as this step is not really that important.
94141 // Nevertheless, a developer will be notified about something went wrong.
0 commit comments