create-modern-react 1.0.0 ā 2.0.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 +270 -72
- package/bin/index.js +13 -13
- package/lib/install.js +103 -32
- package/lib/prompts.js +152 -179
- package/lib/setup.js +267 -159
- package/package.json +17 -8
- package/templates/base/.env.example +9 -0
- package/templates/base/.eslintrc.cjs +37 -0
- package/templates/base/.prettierrc +11 -0
- package/templates/base/components.json +17 -0
- package/templates/base/index.html +2 -1
- package/templates/base/package.json +33 -14
- package/templates/base/postcss.config.js +6 -0
- package/templates/base/src/App.tsx +5 -18
- package/templates/base/src/components/layout/error-boundary.tsx +60 -0
- package/templates/base/src/components/layout/index.ts +2 -0
- package/templates/base/src/components/layout/root-layout.tsx +36 -0
- package/templates/base/src/components/ui/button.tsx +55 -0
- package/templates/base/src/components/ui/card.tsx +85 -0
- package/templates/base/src/components/ui/index.ts +12 -0
- package/templates/base/src/components/ui/input.tsx +24 -0
- package/templates/base/src/components/ui/separator.tsx +29 -0
- package/templates/base/src/components/ui/skeleton.tsx +15 -0
- package/templates/base/src/hooks/index.ts +3 -0
- package/templates/base/src/hooks/use-cancel-token.ts +63 -0
- package/templates/base/src/hooks/use-debounce.ts +29 -0
- package/templates/base/src/hooks/use-loader.ts +39 -0
- package/templates/base/src/index.css +73 -60
- package/templates/base/src/lib/utils.ts +14 -0
- package/templates/base/src/main.tsx +6 -6
- package/templates/base/src/providers/index.tsx +27 -0
- package/templates/base/src/providers/theme-provider.tsx +92 -0
- package/templates/base/src/routes/index.tsx +40 -0
- package/templates/base/src/routes/routes.ts +36 -0
- package/templates/base/src/screens/home/index.tsx +132 -0
- package/templates/base/src/screens/not-found/index.tsx +29 -0
- package/templates/base/src/services/alertify-services.ts +133 -0
- package/templates/base/src/services/api/api-helpers.ts +130 -0
- package/templates/base/src/services/api/axios-instance.ts +77 -0
- package/templates/base/src/services/api/index.ts +9 -0
- package/templates/base/src/services/index.ts +2 -0
- package/templates/base/src/types/index.ts +55 -0
- package/templates/base/src/vite-env.d.ts +31 -0
- package/templates/base/tailwind.config.js +77 -0
- package/templates/base/tsconfig.json +4 -3
- package/templates/base/tsconfig.node.json +22 -0
- package/templates/base/vite.config.ts +65 -4
- package/templates/optional/antd/config-provider.tsx +33 -0
- package/templates/optional/antd/index.ts +2 -0
- package/templates/optional/antd/styles/antd-overrides.css +104 -0
- package/templates/optional/antd/theme.ts +75 -0
- package/templates/optional/husky/.husky/pre-commit +1 -0
- package/templates/optional/husky/.lintstagedrc.json +6 -0
- package/templates/optional/redux/hooks.ts +17 -0
- package/templates/optional/redux/index.ts +13 -0
- package/templates/optional/redux/provider.tsx +33 -0
- package/templates/optional/redux/store/index.ts +45 -0
- package/templates/optional/redux/store/slices/app-slice.ts +62 -0
- package/templates/base/src/App.css +0 -14
package/lib/prompts.js
CHANGED
|
@@ -1,25 +1,43 @@
|
|
|
1
|
-
const inquirer = require(
|
|
2
|
-
const chalk = require(
|
|
3
|
-
const path = require(
|
|
4
|
-
const fs = require(
|
|
5
|
-
const { installDependencies } = require(
|
|
6
|
-
const { setupProject } = require(
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const { installDependencies } = require('./install');
|
|
6
|
+
const { setupProject } = require('./setup');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Core stack (always included):
|
|
10
|
+
* - React 18.3 + TypeScript 5.5 + Vite 5.4 (SWC)
|
|
11
|
+
* - Tailwind CSS 3.4 + tailwind-merge + clsx + CVA
|
|
12
|
+
* - Shadcn/ui components (Button, Input, Card, Skeleton, Separator)
|
|
13
|
+
* - Lucide React icons
|
|
14
|
+
* - Wouter routing (lightweight, 2KB)
|
|
15
|
+
* - Axios with interceptors
|
|
16
|
+
* - ESLint + Prettier (pre-configured)
|
|
17
|
+
* - Path aliases (~/*)
|
|
18
|
+
*/
|
|
7
19
|
|
|
8
20
|
async function createProject(projectName, options) {
|
|
9
|
-
|
|
21
|
+
console.log(chalk.cyan.bold('\nš create-modern-react\n'));
|
|
22
|
+
console.log(chalk.gray('Production-ready React + TypeScript + Tailwind in seconds\n'));
|
|
23
|
+
|
|
24
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
25
|
+
// Prompt 1: Project Name
|
|
26
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
10
27
|
if (!projectName) {
|
|
11
28
|
const nameAnswer = await inquirer.prompt([
|
|
12
29
|
{
|
|
13
|
-
type:
|
|
14
|
-
name:
|
|
15
|
-
message:
|
|
30
|
+
type: 'input',
|
|
31
|
+
name: 'projectName',
|
|
32
|
+
message: 'Project name:',
|
|
16
33
|
validate: (input) => {
|
|
17
|
-
if (!input.trim()) return
|
|
18
|
-
if (!/^[a-zA-Z0-9-_]+$/.test(input))
|
|
19
|
-
return
|
|
34
|
+
if (!input.trim()) return 'Project name is required';
|
|
35
|
+
if (!/^[a-zA-Z0-9-_]+$/.test(input)) {
|
|
36
|
+
return 'Project name can only contain letters, numbers, hyphens, and underscores';
|
|
37
|
+
}
|
|
20
38
|
return true;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
23
41
|
]);
|
|
24
42
|
projectName = nameAnswer.projectName;
|
|
25
43
|
}
|
|
@@ -30,206 +48,161 @@ async function createProject(projectName, options) {
|
|
|
30
48
|
if (fs.existsSync(projectPath)) {
|
|
31
49
|
const overwriteAnswer = await inquirer.prompt([
|
|
32
50
|
{
|
|
33
|
-
type:
|
|
34
|
-
name:
|
|
35
|
-
message: `Directory ${projectName} already exists.
|
|
36
|
-
default: false
|
|
37
|
-
}
|
|
51
|
+
type: 'confirm',
|
|
52
|
+
name: 'overwrite',
|
|
53
|
+
message: `Directory "${projectName}" already exists. Overwrite?`,
|
|
54
|
+
default: false
|
|
55
|
+
}
|
|
38
56
|
]);
|
|
39
57
|
|
|
40
58
|
if (!overwriteAnswer.overwrite) {
|
|
41
|
-
console.log(chalk.yellow(
|
|
59
|
+
console.log(chalk.yellow('\nOperation cancelled.'));
|
|
42
60
|
return;
|
|
43
61
|
}
|
|
44
62
|
|
|
45
63
|
await fs.remove(projectPath);
|
|
46
64
|
}
|
|
47
65
|
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
name: "packageManager",
|
|
53
|
-
message: "Which package manager would you like to use?",
|
|
54
|
-
choices: [
|
|
55
|
-
{ name: "npm", value: "npm" },
|
|
56
|
-
{ name: "yarn", value: "yarn" },
|
|
57
|
-
{ name: "pnpm", value: "pnpm" },
|
|
58
|
-
],
|
|
59
|
-
},
|
|
60
|
-
]);
|
|
61
|
-
|
|
62
|
-
// UI Library Selection
|
|
63
|
-
const uiLibraryAnswer = await inquirer.prompt([
|
|
64
|
-
{
|
|
65
|
-
type: "list",
|
|
66
|
-
name: "uiLibrary",
|
|
67
|
-
message: "Do you want to include a UI component library?",
|
|
68
|
-
choices: [
|
|
69
|
-
{ name: "Ant Design v5 (with theme customization)", value: "antd" },
|
|
70
|
-
{ name: "Material-UI (MUI)", value: "mui" },
|
|
71
|
-
{ name: "Chakra UI", value: "chakra" },
|
|
72
|
-
{ name: "None (custom components only)", value: "none" },
|
|
73
|
-
],
|
|
74
|
-
},
|
|
75
|
-
]);
|
|
76
|
-
|
|
77
|
-
// CSS Framework Selection
|
|
78
|
-
const cssFrameworkAnswer = await inquirer.prompt([
|
|
79
|
-
{
|
|
80
|
-
type: "list",
|
|
81
|
-
name: "cssFramework",
|
|
82
|
-
message: "Which CSS framework would you like to use?",
|
|
83
|
-
choices: [
|
|
84
|
-
{ name: "Tailwind CSS (with custom config)", value: "tailwind" },
|
|
85
|
-
{ name: "CSS Modules", value: "css-modules" },
|
|
86
|
-
{ name: "Styled Components", value: "styled-components" },
|
|
87
|
-
{ name: "Plain CSS only", value: "plain" },
|
|
88
|
-
],
|
|
89
|
-
},
|
|
90
|
-
]);
|
|
91
|
-
|
|
92
|
-
// State Management Selection
|
|
93
|
-
const stateManagementAnswer = await inquirer.prompt([
|
|
94
|
-
{
|
|
95
|
-
type: "list",
|
|
96
|
-
name: "stateManagement",
|
|
97
|
-
message: "Do you need state management?",
|
|
98
|
-
choices: [
|
|
99
|
-
{ name: "Redux Toolkit (with Redux Persist)", value: "redux-toolkit" },
|
|
100
|
-
{ name: "Zustand", value: "zustand" },
|
|
101
|
-
{ name: "Jotai", value: "jotai" },
|
|
102
|
-
{ name: "React state only", value: "none" },
|
|
103
|
-
],
|
|
104
|
-
},
|
|
105
|
-
]);
|
|
106
|
-
|
|
107
|
-
// Data Fetching Selection
|
|
108
|
-
const dataFetchingAnswer = await inquirer.prompt([
|
|
109
|
-
{
|
|
110
|
-
type: "list",
|
|
111
|
-
name: "dataFetching",
|
|
112
|
-
message: "Do you want to include data fetching libraries?",
|
|
113
|
-
choices: [
|
|
114
|
-
{ name: "React Query (TanStack Query)", value: "react-query" },
|
|
115
|
-
{ name: "SWR", value: "swr" },
|
|
116
|
-
{ name: "Apollo Client (for GraphQL)", value: "apollo" },
|
|
117
|
-
{ name: "Fetch API only", value: "none" },
|
|
118
|
-
],
|
|
119
|
-
},
|
|
120
|
-
]);
|
|
121
|
-
|
|
122
|
-
// Routing Selection
|
|
123
|
-
const routingAnswer = await inquirer.prompt([
|
|
66
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
67
|
+
// Prompt 2: Package Manager
|
|
68
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
69
|
+
const { packageManager } = await inquirer.prompt([
|
|
124
70
|
{
|
|
125
|
-
type:
|
|
126
|
-
name:
|
|
127
|
-
message:
|
|
71
|
+
type: 'list',
|
|
72
|
+
name: 'packageManager',
|
|
73
|
+
message: 'Package manager:',
|
|
128
74
|
choices: [
|
|
129
|
-
{ name:
|
|
130
|
-
{ name:
|
|
131
|
-
{ name:
|
|
132
|
-
]
|
|
133
|
-
}
|
|
75
|
+
{ name: 'npm', value: 'npm' },
|
|
76
|
+
{ name: 'yarn', value: 'yarn' },
|
|
77
|
+
{ name: 'pnpm', value: 'pnpm' }
|
|
78
|
+
]
|
|
79
|
+
}
|
|
134
80
|
]);
|
|
135
81
|
|
|
136
|
-
//
|
|
137
|
-
|
|
82
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
83
|
+
// Prompt 3: Optional Features (checkbox - multi-select)
|
|
84
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
85
|
+
console.log(chalk.gray(' (Press <space> to select, <enter> to confirm)\n'));
|
|
86
|
+
const { optionalFeatures } = await inquirer.prompt([
|
|
138
87
|
{
|
|
139
|
-
type:
|
|
140
|
-
name:
|
|
141
|
-
message:
|
|
88
|
+
type: 'checkbox',
|
|
89
|
+
name: 'optionalFeatures',
|
|
90
|
+
message: 'Select optional features:',
|
|
142
91
|
choices: [
|
|
143
|
-
{ name: "Storybook (component development)", value: "storybook" },
|
|
144
92
|
{
|
|
145
|
-
name:
|
|
146
|
-
value:
|
|
147
|
-
checked:
|
|
93
|
+
name: 'Redux Toolkit + Redux Persist (state management)',
|
|
94
|
+
value: 'redux',
|
|
95
|
+
checked: false
|
|
148
96
|
},
|
|
149
97
|
{
|
|
150
|
-
name:
|
|
151
|
-
value:
|
|
152
|
-
checked:
|
|
98
|
+
name: 'Ant Design v5 (replaces Shadcn/ui)',
|
|
99
|
+
value: 'antd',
|
|
100
|
+
checked: false
|
|
153
101
|
},
|
|
154
|
-
{
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
{
|
|
162
|
-
type: "list",
|
|
163
|
-
name: "icons",
|
|
164
|
-
message: "Do you want icon libraries?",
|
|
165
|
-
choices: [
|
|
166
|
-
{ name: "Lucide React", value: "lucide" },
|
|
167
|
-
{ name: "React Icons", value: "react-icons" },
|
|
168
|
-
{ name: "Heroicons", value: "heroicons" },
|
|
169
|
-
{ name: "No icon library", value: "none" },
|
|
170
|
-
],
|
|
171
|
-
},
|
|
102
|
+
{
|
|
103
|
+
name: 'Husky + lint-staged (git hooks)',
|
|
104
|
+
value: 'husky',
|
|
105
|
+
checked: false
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
172
109
|
]);
|
|
173
110
|
|
|
174
|
-
//
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
111
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
112
|
+
// Prompt 4: Initialize Git
|
|
113
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
114
|
+
let initGit = true;
|
|
115
|
+
if (!options.skipGit) {
|
|
116
|
+
const gitAnswer = await inquirer.prompt([
|
|
117
|
+
{
|
|
118
|
+
type: 'confirm',
|
|
119
|
+
name: 'git',
|
|
120
|
+
message: 'Initialize Git repository?',
|
|
121
|
+
default: true
|
|
122
|
+
}
|
|
123
|
+
]);
|
|
124
|
+
initGit = gitAnswer.git;
|
|
125
|
+
} else {
|
|
126
|
+
initGit = false;
|
|
127
|
+
}
|
|
183
128
|
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
129
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
130
|
+
// Prompt 5: Install Dependencies
|
|
131
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
132
|
+
let installDeps = true;
|
|
133
|
+
if (!options.skipInstall) {
|
|
134
|
+
const installAnswer = await inquirer.prompt([
|
|
135
|
+
{
|
|
136
|
+
type: 'confirm',
|
|
137
|
+
name: 'install',
|
|
138
|
+
message: 'Install dependencies?',
|
|
139
|
+
default: true
|
|
140
|
+
}
|
|
141
|
+
]);
|
|
142
|
+
installDeps = installAnswer.install;
|
|
143
|
+
} else {
|
|
144
|
+
installDeps = false;
|
|
145
|
+
}
|
|
193
146
|
|
|
147
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
148
|
+
// Build Configuration Object
|
|
149
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
194
150
|
const config = {
|
|
195
151
|
projectName,
|
|
196
152
|
projectPath,
|
|
197
|
-
packageManager
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
pwa: pwaAnswer.pwa,
|
|
206
|
-
git: gitAnswer.git,
|
|
207
|
-
skipInstall: options.skipInstall,
|
|
208
|
-
skipGit: options.skipGit,
|
|
153
|
+
packageManager,
|
|
154
|
+
// Optional features
|
|
155
|
+
useRedux: optionalFeatures.includes('redux'),
|
|
156
|
+
useAntd: optionalFeatures.includes('antd'),
|
|
157
|
+
useHusky: optionalFeatures.includes('husky'),
|
|
158
|
+
// Flags
|
|
159
|
+
initGit,
|
|
160
|
+
installDeps
|
|
209
161
|
};
|
|
210
162
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
console.log(chalk.
|
|
215
|
-
|
|
216
|
-
|
|
163
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
164
|
+
// Display Configuration Summary
|
|
165
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
166
|
+
console.log(chalk.cyan('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
167
|
+
console.log(chalk.cyan('ā') + chalk.white.bold(' Configuration Summary ') + chalk.cyan('ā'));
|
|
168
|
+
console.log(chalk.cyan('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤'));
|
|
169
|
+
console.log(chalk.cyan('ā') + chalk.white(' Core Stack (always included): ') + chalk.cyan('ā'));
|
|
170
|
+
console.log(chalk.cyan('ā') + chalk.gray(' React 18 + TypeScript + Vite (SWC) ') + chalk.cyan('ā'));
|
|
171
|
+
console.log(chalk.cyan('ā') + chalk.gray(' Tailwind CSS + clsx + CVA ') + chalk.cyan('ā'));
|
|
172
|
+
console.log(chalk.cyan('ā') + chalk.gray(` ${config.useAntd ? 'Ant Design v5' : 'Shadcn/ui components'} `) + chalk.cyan('ā'));
|
|
173
|
+
console.log(chalk.cyan('ā') + chalk.gray(' Wouter routing + Axios ') + chalk.cyan('ā'));
|
|
174
|
+
console.log(chalk.cyan('ā') + chalk.gray(' Lucide icons + ESLint + Prettier ') + chalk.cyan('ā'));
|
|
175
|
+
console.log(chalk.cyan('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤'));
|
|
176
|
+
console.log(chalk.cyan('ā') + chalk.white(' Optional Features: ') + chalk.cyan('ā'));
|
|
177
|
+
console.log(chalk.cyan('ā') + ` Redux Toolkit: ${config.useRedux ? chalk.green('ā') : chalk.gray('ā')} ` + chalk.cyan('ā'));
|
|
178
|
+
console.log(chalk.cyan('ā') + ` Ant Design v5: ${config.useAntd ? chalk.green('ā') : chalk.gray('ā')} ` + chalk.cyan('ā'));
|
|
179
|
+
console.log(chalk.cyan('ā') + ` Husky hooks: ${config.useHusky ? chalk.green('ā') : chalk.gray('ā')} ` + chalk.cyan('ā'));
|
|
180
|
+
console.log(chalk.cyan('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n'));
|
|
181
|
+
|
|
182
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
183
|
+
// Execute Setup
|
|
184
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
217
185
|
await setupProject(config);
|
|
218
186
|
|
|
219
187
|
// Install dependencies
|
|
220
|
-
if (
|
|
188
|
+
if (installDeps) {
|
|
221
189
|
await installDependencies(config);
|
|
222
190
|
}
|
|
223
191
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
192
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
193
|
+
// Success Message
|
|
194
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
195
|
+
const pm = config.actualPackageManager || packageManager;
|
|
196
|
+
const runCmd = pm === 'npm' ? 'npm run' : pm;
|
|
197
|
+
|
|
198
|
+
console.log(chalk.green.bold(`\nā
Project "${projectName}" created successfully!\n`));
|
|
199
|
+
console.log(chalk.white('Next steps:\n'));
|
|
228
200
|
console.log(chalk.gray(` cd ${projectName}`));
|
|
229
|
-
if (
|
|
230
|
-
console.log(chalk.gray(` ${
|
|
201
|
+
if (!installDeps) {
|
|
202
|
+
console.log(chalk.gray(` ${pm} install`));
|
|
231
203
|
}
|
|
232
|
-
console.log(chalk.gray(` ${
|
|
204
|
+
console.log(chalk.gray(` ${runCmd} dev\n`));
|
|
205
|
+
console.log(chalk.cyan('Happy coding! š\n'));
|
|
233
206
|
}
|
|
234
207
|
|
|
235
208
|
module.exports = { createProject };
|