create-manifest 1.3.0 → 1.3.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.
@@ -1,6 +1,6 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
2
  import axios from 'axios';
3
- import { exec as execCp } from 'node:child_process';
3
+ import { exec as execCp, spawn } from 'node:child_process';
4
4
  import * as fs from 'node:fs';
5
5
  import * as crypto from 'node:crypto';
6
6
  import * as path from 'node:path';
@@ -14,10 +14,46 @@ import { updatePackageJsonFile } from '../utils/UpdatePackageJsonFile.js';
14
14
  import { updateSettingsJsonFile } from '../utils/UpdateSettingsJsonFile.js';
15
15
  import { getLatestPackageVersion } from '../utils/GetLatestPackageVersion.js';
16
16
  import { getBackendFileContent } from '../utils/GetBackendFileContent.js';
17
- import { input, select } from '@inquirer/prompts';
17
+ import { input } from '@inquirer/prompts';
18
18
  import { slugify } from '../utils/helpers.js';
19
19
  import chalk from 'chalk';
20
20
  const exec = promisify(execCp);
21
+ /**
22
+ * Execute a command in a specific directory with cross-platform support
23
+ * @param command - The command to execute
24
+ * @param cwd - The working directory
25
+ * @returns Promise with stdout and stderr
26
+ */
27
+ async function execInDirectory(command, cwd) {
28
+ return exec(command, { cwd });
29
+ }
30
+ /**
31
+ * Spawn a command in a specific directory that runs in the background
32
+ * @param command - The command to execute
33
+ * @param args - The command arguments
34
+ * @param cwd - The working directory
35
+ * @returns ChildProcess
36
+ */
37
+ function spawnInDirectory(command, args, cwd) {
38
+ const isWindows = process.platform === 'win32';
39
+ if (isWindows) {
40
+ // On Windows, use shell to resolve npm command
41
+ return spawn(command, args, {
42
+ cwd,
43
+ stdio: 'pipe',
44
+ detached: false,
45
+ shell: true
46
+ });
47
+ }
48
+ else {
49
+ // On Unix systems, spawn directly
50
+ return spawn(command, args, {
51
+ cwd,
52
+ stdio: 'pipe',
53
+ detached: false
54
+ });
55
+ }
56
+ }
21
57
  export default class CreateManifest extends Command {
22
58
  static description = 'Create a new Manifest project with the default files and folders.';
23
59
  static args = {
@@ -58,7 +94,8 @@ export default class CreateManifest extends Command {
58
94
  // * 1 Create a folder named after the first argument or ask for it.
59
95
  const { argv } = await this.parse(CreateManifest);
60
96
  let projectName = argv[0];
61
- let isMonorepo = argv[1] === 'monorepo';
97
+ // let isMonorepo: boolean = argv[1] === 'monorepo'
98
+ const isMonorepo = false; // Hide this feature.
62
99
  if (!projectName) {
63
100
  projectName = await input({
64
101
  message: 'What name would you like to use for the new workspace?',
@@ -74,24 +111,24 @@ export default class CreateManifest extends Command {
74
111
  }
75
112
  });
76
113
  }
77
- if (!isMonorepo) {
78
- const projectType = await select({
79
- message: 'What type of project would you like to develop?',
80
- choices: [
81
- {
82
- name: 'A full-stack app (monorepo)',
83
- value: 'monorepo',
84
- description: 'Creates a monorepo with both "web" and "api" folders'
85
- },
86
- {
87
- name: 'A standalone backend',
88
- value: 'standalone',
89
- description: 'Creates a backend-only project'
90
- }
91
- ]
92
- });
93
- isMonorepo = projectType === 'monorepo';
94
- }
114
+ // if (!isMonorepo) {
115
+ // const projectType = await select({
116
+ // message: 'What type of project would you like to develop?',
117
+ // choices: [
118
+ // {
119
+ // name: 'A full-stack app (monorepo)',
120
+ // value: 'monorepo',
121
+ // description: 'Creates a monorepo with both "web" and "api" folders'
122
+ // },
123
+ // {
124
+ // name: 'A standalone backend',
125
+ // value: 'standalone',
126
+ // description: 'Creates a backend-only project'
127
+ // }
128
+ // ]
129
+ // })
130
+ // isMonorepo = projectType === 'monorepo'
131
+ // }
95
132
  projectName = slugify(projectName);
96
133
  const spinner = ora(`Creating your Manifest project in ${projectName} folder...`).start();
97
134
  const rootFolderPath = path.join(process.cwd(), projectName);
@@ -297,7 +334,7 @@ export default class CreateManifest extends Command {
297
334
  spinner.start('Installing dependencies...');
298
335
  // Install deps.
299
336
  try {
300
- await exec(`cd ${projectName} && npm install --silent`);
337
+ await execInDirectory(`npm install --silent`, projectName);
301
338
  }
302
339
  catch (error) {
303
340
  spinner.fail(`Execution error: ${error}`);
@@ -321,19 +358,30 @@ export default class CreateManifest extends Command {
321
358
  fs.writeFileSync(envFilePath, envContent);
322
359
  spinner.succeed();
323
360
  spinner.start('Building the database...');
324
- let serveTask = null;
361
+ let serverProcess = null;
325
362
  try {
326
- // We run the manifest script to build the database.
327
- serveTask = exec(`cd ${projectName} && npm run start`);
363
+ // We run the manifest script to build the database in the background
364
+ serverProcess = spawnInDirectory('npm', ['run', 'start'], projectName);
365
+ // Wait for the server to be ready
328
366
  await this.waitForServerToBeReady();
329
367
  spinner.succeed();
330
368
  }
331
369
  catch (error) {
332
370
  spinner.fail(`Execution error: ${error}`);
371
+ // If server failed to start, try to kill it
372
+ if (serverProcess && serverProcess.pid) {
373
+ try {
374
+ await this.silentKill(serverProcess.pid);
375
+ }
376
+ catch {
377
+ // Ignore errors when killing the process
378
+ }
379
+ }
380
+ throw error;
333
381
  }
334
382
  spinner.start('Seeding initial data...');
335
383
  try {
336
- await exec(`cd ${projectName} && npm run seed`);
384
+ await execInDirectory(`npm run seed`, projectName);
337
385
  }
338
386
  catch (error) {
339
387
  spinner.fail(`Execution error: ${error}`);
@@ -347,7 +395,15 @@ export default class CreateManifest extends Command {
347
395
  console.log(chalk.bold(` cd ${projectName}`));
348
396
  console.log(chalk.bold(' npm run start'));
349
397
  console.log();
350
- await this.silentKill(serveTask?.child?.pid || 0);
398
+ // Kill the background server process if it exists
399
+ if (serverProcess && serverProcess.pid) {
400
+ try {
401
+ await this.silentKill(serverProcess.pid);
402
+ }
403
+ catch {
404
+ // Ignore errors when killing the process
405
+ }
406
+ }
351
407
  process.exit();
352
408
  }
353
409
  /**
@@ -357,10 +413,16 @@ export default class CreateManifest extends Command {
357
413
  *
358
414
  **/
359
415
  async isServerReady() {
360
- return axios
361
- .get('http://localhost:1111/api/health')
362
- .then(() => true)
363
- .catch(() => false);
416
+ try {
417
+ const response = await axios.get('http://localhost:1111/api/health', {
418
+ timeout: 5000 // 5 second timeout
419
+ });
420
+ return response.status === 200;
421
+ }
422
+ catch {
423
+ // Server is not ready yet
424
+ return false;
425
+ }
364
426
  }
365
427
  /**
366
428
  * Wait for the server to be ready.
@@ -370,12 +432,18 @@ export default class CreateManifest extends Command {
370
432
  **/
371
433
  async waitForServerToBeReady() {
372
434
  let serverReady = false;
373
- while (!serverReady) {
435
+ let attempts = 0;
436
+ const maxAttempts = 30; // 30 seconds timeout
437
+ while (!serverReady && attempts < maxAttempts) {
374
438
  serverReady = await this.isServerReady();
375
439
  if (!serverReady) {
440
+ attempts++;
376
441
  await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1s before retrying
377
442
  }
378
443
  }
444
+ if (!serverReady) {
445
+ throw new Error('Server failed to start within 30 seconds');
446
+ }
379
447
  }
380
448
  /**
381
449
  * Transform a JSON with comments to a JSON without comments.
@@ -43,5 +43,5 @@
43
43
  "enableJsonFlag": false
44
44
  }
45
45
  },
46
- "version": "1.3.0"
46
+ "version": "1.3.2"
47
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-manifest",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "author": "Manifest",
5
5
  "description": "Create a new Manifest backend",
6
6
  "homepage": "https://manifest.build",