@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.
@@ -9,6 +9,7 @@ type Answers = {
9
9
  dbHost: string;
10
10
  dbPort: string;
11
11
  dbType: 'mysql' | 'mariadb';
12
+ socketio: boolean;
12
13
  packageManager: PackageManager;
13
14
  installNow: boolean;
14
15
  };
@@ -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)({ message: `${chalk_1.default.yellow('🗄️')} Enable database?`, default: false });
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)({ message: `${chalk_1.default.yellow('◈')} Database name:`, default: 'flutrydb' }),
40
- dbUser: await (0, prompts_1.input)({ message: `${chalk_1.default.yellow('◈')} Database user:`, default: 'root' }),
41
- dbPass: await (0, prompts_1.password)({ message: `${chalk_1.default.yellow('◈')} Database password:`, mask: '*' }),
42
- dbHost: await (0, prompts_1.input)({ message: `${chalk_1.default.yellow('◈')} Database host:`, default: 'localhost' }),
43
- dbPort: await (0, prompts_1.input)({ message: `${chalk_1.default.yellow('◈')} Database port:`, default: DEFAULTS.dbPort, validate: validatePort }),
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
- { name: 'MariaDB', value: 'mariadb' },
48
- { name: 'MySQL', value: 'mysql' },
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
- { name: 'npm', value: 'npm' },
65
- { name: 'Yarn', value: 'yarn' },
66
- { name: 'pnpm', value: 'pnpm' },
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)({ message: `${chalk_1.default.green('⚡')} Install dependencies now?`, default: true }),
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, { recursive: true, force: true });
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)({ text: chalk_1.default.cyan('Creating project'), spinner: 'dots12', color: 'cyan' }).start();
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', { cache: false, force: true }).clone(targetDir);
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)({ text: chalk_1.default.yellow(`Installing dependencies with ${packageManager}`), spinner: 'arc', color: 'yellow' }).start();
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, { cwd: targetDir, stdio: ['ignore', 'pipe', 'pipe'] });
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
- else
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('╰────────────────────────────────────╯')}\n`);
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.'}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flutry/cli",
3
- "version": "1.1.1",
3
+ "version": "1.1.21",
4
4
  "description": "Flurty project scaffolding CLI",
5
5
  "bin": {
6
6
  "flutry": "dist/index.js"