create-rex-app 1.0.2 → 1.0.3
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/index.js +148 -128
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import path from 'path';
|
|
@@ -7,142 +7,162 @@ import { exec } from 'child_process';
|
|
|
7
7
|
import util from 'util';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import ora from 'ora';
|
|
10
|
-
import { confirm, select } from '@inquirer/prompts';
|
|
10
|
+
import { confirm, select, input } from '@inquirer/prompts';
|
|
11
11
|
|
|
12
12
|
const execPromise = util.promisify(exec);
|
|
13
|
-
|
|
14
13
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
-
const projectName = process.argv[2] || 'react-express-app';
|
|
16
|
-
const currentDir = process.cwd();
|
|
17
|
-
const targetPath = path.join(currentDir, projectName);
|
|
18
|
-
|
|
19
|
-
console.log();
|
|
20
|
-
console.log(`Creating a new Rex app in ${chalk.green(targetPath)}.`);
|
|
21
|
-
console.log();
|
|
22
|
-
|
|
23
|
-
// 1. The Safe Empty Check
|
|
24
|
-
if (fs.existsSync(targetPath)) {
|
|
25
|
-
const files = fs.readdirSync(targetPath);
|
|
26
|
-
if (files.length > 0) {
|
|
27
|
-
console.error(chalk.red(`The directory ${chalk.green(projectName)} contains files that could conflict.`));
|
|
28
|
-
console.error(chalk.red('Either try using a new directory name, or remove the existing files.'));
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
} else {
|
|
32
|
-
fs.mkdirSync(targetPath, { recursive: true });
|
|
33
|
-
}
|
|
34
14
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
15
|
+
async function run() {
|
|
16
|
+
try {
|
|
17
|
+
let projectName = process.argv[2];
|
|
18
|
+
|
|
19
|
+
// 1. Get Project Name
|
|
20
|
+
if (!projectName) {
|
|
21
|
+
projectName = await input({
|
|
22
|
+
message: 'What is your project named?',
|
|
23
|
+
transformer: (input, { isFinal }) => {
|
|
24
|
+
if (isFinal) return chalk.cyan(input);
|
|
25
|
+
return chalk.dim(input);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
48
28
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
29
|
+
|
|
30
|
+
const currentDir = process.cwd();
|
|
31
|
+
const targetPath = path.join(currentDir, projectName);
|
|
32
|
+
|
|
33
|
+
console.log();
|
|
34
|
+
console.log(`Creating a new Rex app in ${chalk.green(targetPath)}.`);
|
|
35
|
+
console.log();
|
|
36
|
+
|
|
37
|
+
// The Safe Empty Check
|
|
38
|
+
if (fs.existsSync(targetPath)) {
|
|
39
|
+
const files = fs.readdirSync(targetPath);
|
|
40
|
+
if (files.length > 0) {
|
|
41
|
+
console.error(chalk.red(`The directory ${chalk.green(projectName)} contains files that could conflict.`));
|
|
42
|
+
console.error(chalk.red('Either try using a new directory name, or remove the existing files.'));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
54
47
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
48
|
+
|
|
49
|
+
// 2. Template Selection Prompt
|
|
50
|
+
const templateChoice = await select({
|
|
51
|
+
message: 'Which Rex template would you like to use?',
|
|
52
|
+
choices: [
|
|
53
|
+
{
|
|
54
|
+
name: chalk.hex('#7C3AED')('Vanilla Rex (Standard)'),
|
|
55
|
+
value: 'template',
|
|
56
|
+
description: 'The blazing fast, lightweight setup.',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: chalk.hex('#F97316')('Mongo Rex (Standard + MongoDB + JWT Auth)'),
|
|
60
|
+
value: 'template-mongo',
|
|
61
|
+
description: 'Includes Mongoose, Auth flow, and protected routes.',
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
theme: {
|
|
65
|
+
prefix: chalk.hex('#F97316')('?'),
|
|
66
|
+
style: {
|
|
67
|
+
highlight: (text) => chalk.hex('#7C3AED')(text),
|
|
68
|
+
}
|
|
69
|
+
}
|
|
75
70
|
});
|
|
76
|
-
} else {
|
|
77
|
-
fs.copyFileSync(src, dest);
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
copyRecursiveSync(templatePath, targetPath);
|
|
83
|
-
|
|
84
|
-
const gitignorePath = path.join(targetPath, '_gitignore');
|
|
85
|
-
if (fs.existsSync(gitignorePath)) {
|
|
86
|
-
fs.renameSync(gitignorePath, path.join(targetPath, '.gitignore'));
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const envPath = path.join(targetPath, '_env');
|
|
90
|
-
if (fs.existsSync(envPath)) {
|
|
91
|
-
fs.renameSync(envPath, path.join(targetPath, '.env'));
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
copySpinner.succeed(chalk.green('Initialized project files.'));
|
|
95
|
-
} catch (error) {
|
|
96
|
-
copySpinner.fail(chalk.red('Failed to extract template.'));
|
|
97
|
-
console.error(error);
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
71
|
|
|
101
|
-
|
|
102
|
-
console.log();
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
72
|
+
const templatePath = path.join(__dirname, templateChoice);
|
|
73
|
+
console.log();
|
|
74
|
+
|
|
75
|
+
// 3. Copy files
|
|
76
|
+
const copySpinner = ora(`Initializing ${templateChoice} files...`).start();
|
|
77
|
+
const copyRecursiveSync = (src, dest) => {
|
|
78
|
+
const exists = fs.existsSync(src);
|
|
79
|
+
const stats = exists && fs.statSync(src);
|
|
80
|
+
const isDirectory = exists && stats.isDirectory();
|
|
81
|
+
|
|
82
|
+
if (isDirectory) {
|
|
83
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
84
|
+
fs.readdirSync(src).forEach((childItemName) => {
|
|
85
|
+
if (childItemName === 'node_modules') return;
|
|
86
|
+
copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName));
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
fs.copyFileSync(src, dest);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
copyRecursiveSync(templatePath, targetPath);
|
|
94
|
+
|
|
95
|
+
const gitignorePath = path.join(targetPath, '_gitignore');
|
|
96
|
+
if (fs.existsSync(gitignorePath)) {
|
|
97
|
+
fs.renameSync(gitignorePath, path.join(targetPath, '.gitignore'));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const envPath = path.join(targetPath, '_env');
|
|
101
|
+
if (fs.existsSync(envPath)) {
|
|
102
|
+
fs.renameSync(envPath, path.join(targetPath, '.env'));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
copySpinner.succeed(chalk.green('Initialized project files.'));
|
|
106
|
+
|
|
107
|
+
// 4. Interactive Prompt for Dependencies
|
|
108
|
+
console.log();
|
|
109
|
+
const shouldInstall = await confirm({
|
|
110
|
+
message: 'Would you like to install dependencies now?',
|
|
111
|
+
default: true
|
|
112
|
+
});
|
|
121
113
|
|
|
122
|
-
|
|
123
|
-
console.log(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
console.log();
|
|
134
|
-
|
|
135
|
-
if (projectName !== '.') {
|
|
136
|
-
console.log(chalk.cyan(` cd ${projectName}`));
|
|
137
|
-
}
|
|
138
|
-
if (!shouldInstall) {
|
|
139
|
-
console.log(chalk.cyan(` npm install`));
|
|
140
|
-
}
|
|
114
|
+
// 5. Auto-Install
|
|
115
|
+
console.log();
|
|
116
|
+
if (shouldInstall) {
|
|
117
|
+
const installSpinner = ora('Installing packages. This might take a couple of minutes...').start();
|
|
118
|
+
try {
|
|
119
|
+
await execPromise('npm install', { cwd: targetPath });
|
|
120
|
+
installSpinner.succeed(chalk.green('Installed dependencies.'));
|
|
121
|
+
} catch (error) {
|
|
122
|
+
installSpinner.fail(chalk.yellow('Installation failed. You can run `npm install` manually later.'));
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
console.log(chalk.dim('Skipped dependency installation.'));
|
|
126
|
+
}
|
|
141
127
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
128
|
+
console.log();
|
|
129
|
+
console.log(`${chalk.green('Success!')} Created ${chalk.cyan(projectName)} at ${chalk.cyan(targetPath)}`);
|
|
130
|
+
console.log('Inside that directory, you can run several commands:');
|
|
131
|
+
console.log();
|
|
132
|
+
console.log(chalk.cyan(` npm run dev`));
|
|
133
|
+
console.log(' Starts the development server.');
|
|
134
|
+
console.log();
|
|
135
|
+
console.log(chalk.cyan(` npm run build`));
|
|
136
|
+
console.log(' Bundles the app into static files for production.');
|
|
137
|
+
console.log();
|
|
138
|
+
console.log('We suggest that you begin by typing:');
|
|
139
|
+
console.log();
|
|
140
|
+
|
|
141
|
+
if (projectName !== '.') {
|
|
142
|
+
console.log(chalk.cyan(` cd ${projectName}`));
|
|
143
|
+
}
|
|
144
|
+
if (!shouldInstall) {
|
|
145
|
+
console.log(chalk.cyan(` npm install`));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (templateChoice === 'template-mongo') {
|
|
149
|
+
console.log(chalk.yellow(`\n ⚠️ Don't forget to configure your .env file with your MONGO_URI and JWT_SECRET!`));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
console.log(chalk.cyan(` npm run dev`));
|
|
153
|
+
console.log();
|
|
154
|
+
|
|
155
|
+
} catch (error) {
|
|
156
|
+
if (error.name === 'ExitPromptError') {
|
|
157
|
+
console.log('\n');
|
|
158
|
+
console.log(chalk.yellow('Process interrupted, exiting the installation!'));
|
|
159
|
+
process.exit(0);
|
|
160
|
+
} else {
|
|
161
|
+
console.error(chalk.red('\nAn unexpected error occurred:'));
|
|
162
|
+
console.error(error);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
145
166
|
}
|
|
146
167
|
|
|
147
|
-
|
|
148
|
-
console.log();
|
|
168
|
+
run();
|