@flutry/cli 1.1.1 → 1.1.21
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 +168 -24
- 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) {
|
|
@@ -95,25 +137,116 @@ function buildEnv(answers, example) {
|
|
|
95
137
|
`SECRET_KEY=${secret('SECRET_KEY')}`,
|
|
96
138
|
`SECRET_SALT=${secret('SECRET_SALT')}`,
|
|
97
139
|
`JWT_SECRET_KEY=${secret('JWT_SECRET_KEY')}`,
|
|
98
|
-
,
|
|
99
140
|
].join('\n');
|
|
100
141
|
}
|
|
101
142
|
async function removeIfPresent(filePath) {
|
|
102
|
-
await fs_1.promises.rm(filePath, {
|
|
143
|
+
await fs_1.promises.rm(filePath, {
|
|
144
|
+
recursive: true,
|
|
145
|
+
force: true,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Configures the generated main.ts file based
|
|
150
|
+
* on the Socket.IO selection.
|
|
151
|
+
*/
|
|
152
|
+
async function configureMainFile(targetDir, socketio) {
|
|
153
|
+
const mainPath = path_1.default.join(targetDir, 'src', 'main.ts');
|
|
154
|
+
if (!(await exists(mainPath))) {
|
|
155
|
+
throw new Error('src/main.ts was not found in the downloaded template.');
|
|
156
|
+
}
|
|
157
|
+
let content = await fs_1.promises.readFile(mainPath, 'utf8');
|
|
158
|
+
const socketConfiguration = socketio
|
|
159
|
+
? `socket: {
|
|
160
|
+
cors: {
|
|
161
|
+
origin: '*',
|
|
162
|
+
},
|
|
163
|
+
},`
|
|
164
|
+
: `socket: false,`;
|
|
165
|
+
content = content.replace(/socket:\s*(?:false|\{[\s\S]*?\n\s*\}),/, socketConfiguration);
|
|
166
|
+
await fs_1.promises.writeFile(mainPath, content, 'utf8');
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Creates the Socket.IO utility used by the generated
|
|
170
|
+
* application.
|
|
171
|
+
*
|
|
172
|
+
* The utility is only generated when Socket.IO is enabled.
|
|
173
|
+
*
|
|
174
|
+
* Generated structure:
|
|
175
|
+
*
|
|
176
|
+
* src/
|
|
177
|
+
* └── utils/
|
|
178
|
+
* └── socket/
|
|
179
|
+
* └── index.ts
|
|
180
|
+
*/
|
|
181
|
+
async function configureSocketUtils(targetDir, socketio) {
|
|
182
|
+
/**
|
|
183
|
+
* Socket.IO was not enabled, therefore no utility
|
|
184
|
+
* files should be generated.
|
|
185
|
+
*/
|
|
186
|
+
if (!socketio) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const socketDirectory = path_1.default.join(targetDir, 'src', 'utils', 'socket');
|
|
190
|
+
const socketFile = path_1.default.join(socketDirectory, 'index.ts');
|
|
191
|
+
/**
|
|
192
|
+
* Make sure the complete utils/socket directory
|
|
193
|
+
* structure exists.
|
|
194
|
+
*/
|
|
195
|
+
await fs_1.promises.mkdir(socketDirectory, {
|
|
196
|
+
recursive: true,
|
|
197
|
+
});
|
|
198
|
+
/**
|
|
199
|
+
* Generate the Socket.IO connection handler.
|
|
200
|
+
*
|
|
201
|
+
* getSocket() returns the Socket.IO instance that was
|
|
202
|
+
* already initialized by HttpServer.
|
|
203
|
+
*/
|
|
204
|
+
const socketContent = `import { logger } from '@flutry/common';
|
|
205
|
+
|
|
206
|
+
import { getSocket } from '@flutry/server';
|
|
207
|
+
|
|
208
|
+
const io = getSocket();
|
|
209
|
+
|
|
210
|
+
io.on('connection', (socket: any) => {
|
|
211
|
+
logger.info('Connect ' + socket.id);
|
|
212
|
+
|
|
213
|
+
socket.on('disconnect', () => {
|
|
214
|
+
logger.info('Disconnect ' + socket.id);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
`;
|
|
218
|
+
await fs_1.promises.writeFile(socketFile, socketContent, 'utf8');
|
|
103
219
|
}
|
|
104
220
|
async function createProject(answers) {
|
|
105
221
|
const targetDir = path_1.default.resolve(process.cwd(), answers.folderName);
|
|
106
|
-
const spinner = (0, ora_1.default)({
|
|
222
|
+
const spinner = (0, ora_1.default)({
|
|
223
|
+
text: chalk_1.default.cyan('Creating project'),
|
|
224
|
+
spinner: 'dots12',
|
|
225
|
+
color: 'cyan',
|
|
226
|
+
}).start();
|
|
107
227
|
try {
|
|
108
|
-
if (await exists(targetDir))
|
|
228
|
+
if (await exists(targetDir)) {
|
|
109
229
|
throw new Error(`Folder "${answers.folderName}" already exists.`);
|
|
230
|
+
}
|
|
110
231
|
spinner.text = chalk_1.default.blue('Downloading project template');
|
|
111
|
-
await (0, degit_1.default)('https://github.com/Flutry-HQ/flutry.git', {
|
|
232
|
+
await (0, degit_1.default)('https://github.com/Flutry-HQ/flutry.git', {
|
|
233
|
+
cache: false,
|
|
234
|
+
force: true,
|
|
235
|
+
}).clone(targetDir);
|
|
112
236
|
spinner.text = chalk_1.default.magenta('Applying project settings');
|
|
113
237
|
const packageJsonPath = path_1.default.join(targetDir, 'package.json');
|
|
114
238
|
const packageJson = JSON.parse(await fs_1.promises.readFile(packageJsonPath, 'utf8'));
|
|
115
239
|
packageJson.name = answers.projectName;
|
|
116
240
|
await fs_1.promises.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8');
|
|
241
|
+
/**
|
|
242
|
+
* Configure the HttpServer Socket.IO option.
|
|
243
|
+
*/
|
|
244
|
+
await configureMainFile(targetDir, answers.socketio);
|
|
245
|
+
/**
|
|
246
|
+
* Generate the Socket.IO utility only
|
|
247
|
+
* when Socket.IO is enabled.
|
|
248
|
+
*/
|
|
249
|
+
await configureSocketUtils(targetDir, answers.socketio);
|
|
117
250
|
await Promise.all([
|
|
118
251
|
fs_1.promises.writeFile(path_1.default.join(targetDir, '.env'), buildEnv(answers, false), 'utf8'),
|
|
119
252
|
fs_1.promises.writeFile(path_1.default.join(targetDir, '.env.example'), buildEnv(answers, true), 'utf8'),
|
|
@@ -131,22 +264,31 @@ async function createProject(answers) {
|
|
|
131
264
|
}
|
|
132
265
|
}
|
|
133
266
|
async function installPackages(targetDir, packageManager) {
|
|
134
|
-
const spinner = (0, ora_1.default)({
|
|
267
|
+
const spinner = (0, ora_1.default)({
|
|
268
|
+
text: chalk_1.default.yellow(`Installing dependencies with ${packageManager}`),
|
|
269
|
+
spinner: 'arc',
|
|
270
|
+
color: 'yellow',
|
|
271
|
+
}).start();
|
|
135
272
|
await new Promise((resolve, reject) => {
|
|
136
273
|
const executable = process.platform === 'win32' ? `${packageManager}.cmd` : packageManager;
|
|
137
274
|
const command = process.platform === 'win32' ? (process.env.ComSpec ?? 'cmd.exe') : executable;
|
|
138
275
|
const commandArgs = process.platform === 'win32' ? ['/d', '/s', '/c', executable, 'install'] : ['install'];
|
|
139
|
-
const child = (0, child_process_1.spawn)(command, commandArgs, {
|
|
276
|
+
const child = (0, child_process_1.spawn)(command, commandArgs, {
|
|
277
|
+
cwd: targetDir,
|
|
278
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
279
|
+
});
|
|
140
280
|
let errorOutput = '';
|
|
141
281
|
child.stderr.on('data', (data) => {
|
|
142
282
|
errorOutput += data.toString();
|
|
143
283
|
});
|
|
144
284
|
child.on('error', reject);
|
|
145
285
|
child.on('close', (code) => {
|
|
146
|
-
if (code === 0)
|
|
286
|
+
if (code === 0) {
|
|
147
287
|
resolve();
|
|
148
|
-
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
149
290
|
reject(new Error(`${packageManager} install failed${errorOutput ? `: ${errorOutput.trim()}` : ''}`));
|
|
291
|
+
}
|
|
150
292
|
});
|
|
151
293
|
});
|
|
152
294
|
spinner.succeed(chalk_1.default.greenBright('Dependencies installed'));
|
|
@@ -173,16 +315,18 @@ async function newCommand() {
|
|
|
173
315
|
try {
|
|
174
316
|
const answers = await promptQuestions();
|
|
175
317
|
const targetDir = await createProject(answers);
|
|
176
|
-
if (answers.installNow)
|
|
318
|
+
if (answers.installNow) {
|
|
177
319
|
await installPackages(targetDir, answers.packageManager);
|
|
320
|
+
}
|
|
178
321
|
console.log(`\n${chalk_1.default.greenBright('╭─')} ${chalk_1.default.bold.green('✓ Project ready')} ${chalk_1.default.greenBright('─'.repeat(19))}`);
|
|
179
322
|
console.log(`${chalk_1.default.greenBright('│')} ${chalk_1.default.gray('Location:')} ${chalk_1.default.cyan(`./${answers.folderName}`)}`);
|
|
180
323
|
console.log(`${chalk_1.default.greenBright('│')} ${chalk_1.default.gray('Next:')}`);
|
|
181
324
|
console.log(`${chalk_1.default.greenBright('│')} ${chalk_1.default.cyan(`cd ${answers.folderName}`)}`);
|
|
182
|
-
if (!answers.installNow)
|
|
325
|
+
if (!answers.installNow) {
|
|
183
326
|
console.log(`${chalk_1.default.greenBright('│')} ${chalk_1.default.yellow(`${answers.packageManager} install`)}`);
|
|
327
|
+
}
|
|
184
328
|
console.log(`${chalk_1.default.greenBright('│')} ${chalk_1.default.green(`${answers.packageManager} dev`)}`);
|
|
185
|
-
console.log(`${chalk_1.default.greenBright('
|
|
329
|
+
console.log(`${chalk_1.default.greenBright('╰────────────────────────────────────')}\n`);
|
|
186
330
|
}
|
|
187
331
|
catch (error) {
|
|
188
332
|
console.error(`\n${chalk_1.default.redBright('✖')} ${error instanceof Error ? error.message : 'Command failed.'}`);
|