@flutry/cli 1.1.1 → 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.
@@ -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) {
@@ -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, { recursive: true, force: true });
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)({ text: chalk_1.default.cyan('Creating project'), spinner: 'dots12', color: 'cyan' }).start();
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', { cache: false, force: true }).clone(targetDir);
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)({ text: chalk_1.default.yellow(`Installing dependencies with ${packageManager}`), spinner: 'arc', color: 'yellow' }).start();
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, { cwd: targetDir, stdio: ['ignore', 'pipe', 'pipe'] });
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
- else
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('╰────────────────────────────────────╯')}\n`);
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.'}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flutry/cli",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Flurty project scaffolding CLI",
5
5
  "bin": {
6
6
  "flutry": "dist/index.js"