@flutry/cli 1.1.0 → 1.1.2
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/dist/commands/new.d.ts +1 -0
- package/dist/commands/new.js +171 -25
- package/package.json +1 -1
package/dist/commands/new.d.ts
CHANGED
package/dist/commands/new.js
CHANGED
|
@@ -33,19 +33,44 @@ async function promptQuestions() {
|
|
|
33
33
|
default: folderName.toLowerCase().replace(/\s+/g, '-'),
|
|
34
34
|
validate: validatePackageName,
|
|
35
35
|
});
|
|
36
|
-
const database = await (0, prompts_1.confirm)({
|
|
36
|
+
const database = await (0, prompts_1.confirm)({
|
|
37
|
+
message: `${chalk_1.default.yellow('🗄️')} Enable database?`,
|
|
38
|
+
default: true,
|
|
39
|
+
});
|
|
37
40
|
const databaseAnswers = database
|
|
38
41
|
? {
|
|
39
|
-
dbName: await (0, prompts_1.input)({
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
dbName: await (0, prompts_1.input)({
|
|
43
|
+
message: `${chalk_1.default.yellow('◈')} Database name:`,
|
|
44
|
+
default: 'flutrydb',
|
|
45
|
+
}),
|
|
46
|
+
dbUser: await (0, prompts_1.input)({
|
|
47
|
+
message: `${chalk_1.default.yellow('◈')} Database user:`,
|
|
48
|
+
default: 'root',
|
|
49
|
+
}),
|
|
50
|
+
dbPass: await (0, prompts_1.password)({
|
|
51
|
+
message: `${chalk_1.default.yellow('◈')} Database password:`,
|
|
52
|
+
mask: '*',
|
|
53
|
+
}),
|
|
54
|
+
dbHost: await (0, prompts_1.input)({
|
|
55
|
+
message: `${chalk_1.default.yellow('◈')} Database host:`,
|
|
56
|
+
default: 'localhost',
|
|
57
|
+
}),
|
|
58
|
+
dbPort: await (0, prompts_1.input)({
|
|
59
|
+
message: `${chalk_1.default.yellow('◈')} Database port:`,
|
|
60
|
+
default: DEFAULTS.dbPort,
|
|
61
|
+
validate: validatePort,
|
|
62
|
+
}),
|
|
44
63
|
dbType: await (0, prompts_1.select)({
|
|
45
64
|
message: `${chalk_1.default.yellow('◈')} Database type:`,
|
|
46
65
|
choices: [
|
|
47
|
-
{
|
|
48
|
-
|
|
66
|
+
{
|
|
67
|
+
name: 'MariaDB',
|
|
68
|
+
value: 'mariadb',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'MySQL',
|
|
72
|
+
value: 'mysql',
|
|
73
|
+
},
|
|
49
74
|
],
|
|
50
75
|
default: DEFAULTS.dbType,
|
|
51
76
|
}),
|
|
@@ -58,12 +83,25 @@ async function promptQuestions() {
|
|
|
58
83
|
dbPort: '',
|
|
59
84
|
dbType: DEFAULTS.dbType,
|
|
60
85
|
};
|
|
86
|
+
const socketio = await (0, prompts_1.confirm)({
|
|
87
|
+
message: `${chalk_1.default.yellow('🔌')} Enable Socket.IO?`,
|
|
88
|
+
default: true,
|
|
89
|
+
});
|
|
61
90
|
const packageManager = await (0, prompts_1.select)({
|
|
62
91
|
message: `${chalk_1.default.magenta('⚙')} Package manager:`,
|
|
63
92
|
choices: [
|
|
64
|
-
{
|
|
65
|
-
|
|
66
|
-
|
|
93
|
+
{
|
|
94
|
+
name: 'npm',
|
|
95
|
+
value: 'npm',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'Yarn',
|
|
99
|
+
value: 'yarn',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'pnpm',
|
|
103
|
+
value: 'pnpm',
|
|
104
|
+
},
|
|
67
105
|
],
|
|
68
106
|
default: 'npm',
|
|
69
107
|
});
|
|
@@ -72,8 +110,12 @@ async function promptQuestions() {
|
|
|
72
110
|
projectName,
|
|
73
111
|
database,
|
|
74
112
|
...databaseAnswers,
|
|
113
|
+
socketio,
|
|
75
114
|
packageManager,
|
|
76
|
-
installNow: await (0, prompts_1.confirm)({
|
|
115
|
+
installNow: await (0, prompts_1.confirm)({
|
|
116
|
+
message: `${chalk_1.default.green('⚡')} Install dependencies now?`,
|
|
117
|
+
default: true,
|
|
118
|
+
}),
|
|
77
119
|
};
|
|
78
120
|
}
|
|
79
121
|
function buildEnv(answers, example) {
|
|
@@ -82,7 +124,7 @@ function buildEnv(answers, example) {
|
|
|
82
124
|
'# Server',
|
|
83
125
|
`PORT=${DEFAULTS.port}`,
|
|
84
126
|
`HOST=${DEFAULTS.host}`,
|
|
85
|
-
'PREFIX_API
|
|
127
|
+
'PREFIX_API=/',
|
|
86
128
|
'# Database',
|
|
87
129
|
`DB=${answers.database ? 'true' : 'false'}`,
|
|
88
130
|
`DB_HOST=${example ? '' : answers.dbHost}`,
|
|
@@ -91,29 +133,122 @@ function buildEnv(answers, example) {
|
|
|
91
133
|
`DB_USER=${example ? '' : answers.dbUser}`,
|
|
92
134
|
`DB_PASS=${example ? '' : answers.dbPass}`,
|
|
93
135
|
`DB_DIALECT=${answers.dbType}`,
|
|
136
|
+
'# Socket.IO',
|
|
137
|
+
`SOCKETIO=${answers.socketio ? 'true' : 'false'}`,
|
|
94
138
|
'# Secrets',
|
|
95
139
|
`SECRET_KEY=${secret('SECRET_KEY')}`,
|
|
96
140
|
`SECRET_SALT=${secret('SECRET_SALT')}`,
|
|
97
141
|
`JWT_SECRET_KEY=${secret('JWT_SECRET_KEY')}`,
|
|
98
|
-
,
|
|
99
142
|
].join('\n');
|
|
100
143
|
}
|
|
101
144
|
async function removeIfPresent(filePath) {
|
|
102
|
-
await fs_1.promises.rm(filePath, {
|
|
145
|
+
await fs_1.promises.rm(filePath, {
|
|
146
|
+
recursive: true,
|
|
147
|
+
force: true,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Configures the generated main.ts file based
|
|
152
|
+
* on the Socket.IO selection.
|
|
153
|
+
*/
|
|
154
|
+
async function configureMainFile(targetDir, socketio) {
|
|
155
|
+
const mainPath = path_1.default.join(targetDir, 'src', 'main.ts');
|
|
156
|
+
if (!(await exists(mainPath))) {
|
|
157
|
+
throw new Error('src/main.ts was not found in the downloaded template.');
|
|
158
|
+
}
|
|
159
|
+
let content = await fs_1.promises.readFile(mainPath, 'utf8');
|
|
160
|
+
const socketConfiguration = socketio
|
|
161
|
+
? `socket: {
|
|
162
|
+
cors: {
|
|
163
|
+
origin: '*',
|
|
164
|
+
},
|
|
165
|
+
},`
|
|
166
|
+
: `socket: false,`;
|
|
167
|
+
content = content.replace(/socket:\s*(?:false|\{[\s\S]*?\n\s*\}),/, socketConfiguration);
|
|
168
|
+
await fs_1.promises.writeFile(mainPath, content, 'utf8');
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Creates the Socket.IO utility used by the generated
|
|
172
|
+
* application.
|
|
173
|
+
*
|
|
174
|
+
* The utility is only generated when Socket.IO is enabled.
|
|
175
|
+
*
|
|
176
|
+
* Generated structure:
|
|
177
|
+
*
|
|
178
|
+
* src/
|
|
179
|
+
* └── utils/
|
|
180
|
+
* └── socket/
|
|
181
|
+
* └── index.ts
|
|
182
|
+
*/
|
|
183
|
+
async function configureSocketUtils(targetDir, socketio) {
|
|
184
|
+
/**
|
|
185
|
+
* Socket.IO was not enabled, therefore no utility
|
|
186
|
+
* files should be generated.
|
|
187
|
+
*/
|
|
188
|
+
if (!socketio) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const socketDirectory = path_1.default.join(targetDir, 'src', 'utils', 'socket');
|
|
192
|
+
const socketFile = path_1.default.join(socketDirectory, 'index.ts');
|
|
193
|
+
/**
|
|
194
|
+
* Make sure the complete utils/socket directory
|
|
195
|
+
* structure exists.
|
|
196
|
+
*/
|
|
197
|
+
await fs_1.promises.mkdir(socketDirectory, {
|
|
198
|
+
recursive: true,
|
|
199
|
+
});
|
|
200
|
+
/**
|
|
201
|
+
* Generate the Socket.IO connection handler.
|
|
202
|
+
*
|
|
203
|
+
* getSocket() returns the Socket.IO instance that was
|
|
204
|
+
* already initialized by HttpServer.
|
|
205
|
+
*/
|
|
206
|
+
const socketContent = `import { logger } from '@flutry/common';
|
|
207
|
+
|
|
208
|
+
import { getSocket } from '@flutry/server';
|
|
209
|
+
|
|
210
|
+
const io = getSocket();
|
|
211
|
+
|
|
212
|
+
io.on('connection', (socket: any) => {
|
|
213
|
+
logger.info('Connect ' + socket.id);
|
|
214
|
+
|
|
215
|
+
socket.on('disconnect', () => {
|
|
216
|
+
logger.info('Disconnect ' + socket.id);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
`;
|
|
220
|
+
await fs_1.promises.writeFile(socketFile, socketContent, 'utf8');
|
|
103
221
|
}
|
|
104
222
|
async function createProject(answers) {
|
|
105
223
|
const targetDir = path_1.default.resolve(process.cwd(), answers.folderName);
|
|
106
|
-
const spinner = (0, ora_1.default)({
|
|
224
|
+
const spinner = (0, ora_1.default)({
|
|
225
|
+
text: chalk_1.default.cyan('Creating project'),
|
|
226
|
+
spinner: 'dots12',
|
|
227
|
+
color: 'cyan',
|
|
228
|
+
}).start();
|
|
107
229
|
try {
|
|
108
|
-
if (await exists(targetDir))
|
|
230
|
+
if (await exists(targetDir)) {
|
|
109
231
|
throw new Error(`Folder "${answers.folderName}" already exists.`);
|
|
232
|
+
}
|
|
110
233
|
spinner.text = chalk_1.default.blue('Downloading project template');
|
|
111
|
-
await (0, degit_1.default)('https://github.com/Flutry-HQ/flutry.git', {
|
|
234
|
+
await (0, degit_1.default)('https://github.com/Flutry-HQ/flutry.git', {
|
|
235
|
+
cache: false,
|
|
236
|
+
force: true,
|
|
237
|
+
}).clone(targetDir);
|
|
112
238
|
spinner.text = chalk_1.default.magenta('Applying project settings');
|
|
113
239
|
const packageJsonPath = path_1.default.join(targetDir, 'package.json');
|
|
114
240
|
const packageJson = JSON.parse(await fs_1.promises.readFile(packageJsonPath, 'utf8'));
|
|
115
241
|
packageJson.name = answers.projectName;
|
|
116
242
|
await fs_1.promises.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8');
|
|
243
|
+
/**
|
|
244
|
+
* Configure the HttpServer Socket.IO option.
|
|
245
|
+
*/
|
|
246
|
+
await configureMainFile(targetDir, answers.socketio);
|
|
247
|
+
/**
|
|
248
|
+
* Generate the Socket.IO utility only
|
|
249
|
+
* when Socket.IO is enabled.
|
|
250
|
+
*/
|
|
251
|
+
await configureSocketUtils(targetDir, answers.socketio);
|
|
117
252
|
await Promise.all([
|
|
118
253
|
fs_1.promises.writeFile(path_1.default.join(targetDir, '.env'), buildEnv(answers, false), 'utf8'),
|
|
119
254
|
fs_1.promises.writeFile(path_1.default.join(targetDir, '.env.example'), buildEnv(answers, true), 'utf8'),
|
|
@@ -131,22 +266,31 @@ async function createProject(answers) {
|
|
|
131
266
|
}
|
|
132
267
|
}
|
|
133
268
|
async function installPackages(targetDir, packageManager) {
|
|
134
|
-
const spinner = (0, ora_1.default)({
|
|
269
|
+
const spinner = (0, ora_1.default)({
|
|
270
|
+
text: chalk_1.default.yellow(`Installing dependencies with ${packageManager}`),
|
|
271
|
+
spinner: 'arc',
|
|
272
|
+
color: 'yellow',
|
|
273
|
+
}).start();
|
|
135
274
|
await new Promise((resolve, reject) => {
|
|
136
275
|
const executable = process.platform === 'win32' ? `${packageManager}.cmd` : packageManager;
|
|
137
276
|
const command = process.platform === 'win32' ? (process.env.ComSpec ?? 'cmd.exe') : executable;
|
|
138
277
|
const commandArgs = process.platform === 'win32' ? ['/d', '/s', '/c', executable, 'install'] : ['install'];
|
|
139
|
-
const child = (0, child_process_1.spawn)(command, commandArgs, {
|
|
278
|
+
const child = (0, child_process_1.spawn)(command, commandArgs, {
|
|
279
|
+
cwd: targetDir,
|
|
280
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
281
|
+
});
|
|
140
282
|
let errorOutput = '';
|
|
141
283
|
child.stderr.on('data', (data) => {
|
|
142
284
|
errorOutput += data.toString();
|
|
143
285
|
});
|
|
144
286
|
child.on('error', reject);
|
|
145
287
|
child.on('close', (code) => {
|
|
146
|
-
if (code === 0)
|
|
288
|
+
if (code === 0) {
|
|
147
289
|
resolve();
|
|
148
|
-
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
149
292
|
reject(new Error(`${packageManager} install failed${errorOutput ? `: ${errorOutput.trim()}` : ''}`));
|
|
293
|
+
}
|
|
150
294
|
});
|
|
151
295
|
});
|
|
152
296
|
spinner.succeed(chalk_1.default.greenBright('Dependencies installed'));
|
|
@@ -173,16 +317,18 @@ async function newCommand() {
|
|
|
173
317
|
try {
|
|
174
318
|
const answers = await promptQuestions();
|
|
175
319
|
const targetDir = await createProject(answers);
|
|
176
|
-
if (answers.installNow)
|
|
320
|
+
if (answers.installNow) {
|
|
177
321
|
await installPackages(targetDir, answers.packageManager);
|
|
322
|
+
}
|
|
178
323
|
console.log(`\n${chalk_1.default.greenBright('╭─')} ${chalk_1.default.bold.green('✓ Project ready')} ${chalk_1.default.greenBright('─'.repeat(19))}`);
|
|
179
324
|
console.log(`${chalk_1.default.greenBright('│')} ${chalk_1.default.gray('Location:')} ${chalk_1.default.cyan(`./${answers.folderName}`)}`);
|
|
180
325
|
console.log(`${chalk_1.default.greenBright('│')} ${chalk_1.default.gray('Next:')}`);
|
|
181
326
|
console.log(`${chalk_1.default.greenBright('│')} ${chalk_1.default.cyan(`cd ${answers.folderName}`)}`);
|
|
182
|
-
if (!answers.installNow)
|
|
327
|
+
if (!answers.installNow) {
|
|
183
328
|
console.log(`${chalk_1.default.greenBright('│')} ${chalk_1.default.yellow(`${answers.packageManager} install`)}`);
|
|
329
|
+
}
|
|
184
330
|
console.log(`${chalk_1.default.greenBright('│')} ${chalk_1.default.green(`${answers.packageManager} dev`)}`);
|
|
185
|
-
console.log(`${chalk_1.default.greenBright('
|
|
331
|
+
console.log(`${chalk_1.default.greenBright('╰────────────────────────────────────')}\n`);
|
|
186
332
|
}
|
|
187
333
|
catch (error) {
|
|
188
334
|
console.error(`\n${chalk_1.default.redBright('✖')} ${error instanceof Error ? error.message : 'Command failed.'}`);
|