mfer 1.4.1 → 1.4.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.
- package/README.md +45 -3
- package/dist/commands/clone.js +7 -7
- package/dist/commands/config/sub-commands/edit-config.js +5 -3
- package/dist/commands/init.js +13 -30
- package/dist/commands/install.js +2 -2
- package/dist/commands/pull.js +9 -9
- package/dist/commands/run.js +13 -9
- package/dist/index.js +3 -3
- package/dist/utils/config-utils.js +11 -7
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -30,15 +30,18 @@ A powerful CLI tool designed to simplify the management and execution of multipl
|
|
|
30
30
|
## 📦 Installation
|
|
31
31
|
|
|
32
32
|
### Prerequisites
|
|
33
|
+
|
|
33
34
|
- Node.js 18 or higher
|
|
34
35
|
- Git (for repository management)
|
|
35
36
|
|
|
36
37
|
### Install from npm
|
|
38
|
+
|
|
37
39
|
```bash
|
|
38
40
|
npm install -g mfer
|
|
39
41
|
```
|
|
40
42
|
|
|
41
43
|
### Install from source
|
|
44
|
+
|
|
42
45
|
```bash
|
|
43
46
|
git clone https://github.com/srimel/mfer.git
|
|
44
47
|
cd mfer
|
|
@@ -50,6 +53,7 @@ npm install -g .
|
|
|
50
53
|
## 🛠️ Quick Start
|
|
51
54
|
|
|
52
55
|
### 1. Initialize Configuration
|
|
56
|
+
|
|
53
57
|
Start by setting up your mfer configuration:
|
|
54
58
|
|
|
55
59
|
```bash
|
|
@@ -57,11 +61,13 @@ mfer init
|
|
|
57
61
|
```
|
|
58
62
|
|
|
59
63
|
This interactive wizard will guide you through:
|
|
64
|
+
|
|
60
65
|
- Setting up your GitHub username
|
|
61
66
|
- Specifying the directory containing your micro frontends
|
|
62
67
|
- Selecting which projects to include in your default group
|
|
63
68
|
|
|
64
69
|
### 2. Run Your Micro Frontends
|
|
70
|
+
|
|
65
71
|
```bash
|
|
66
72
|
# Run all micro frontends
|
|
67
73
|
mfer run
|
|
@@ -74,6 +80,7 @@ mfer run shared
|
|
|
74
80
|
```
|
|
75
81
|
|
|
76
82
|
### 3. Update Your Repositories
|
|
83
|
+
|
|
77
84
|
```bash
|
|
78
85
|
# Pull latest changes from all repositories
|
|
79
86
|
mfer pull
|
|
@@ -85,6 +92,7 @@ mfer pull frontend
|
|
|
85
92
|
## 📋 Commands
|
|
86
93
|
|
|
87
94
|
### Quick Reference
|
|
95
|
+
|
|
88
96
|
- [`mfer init`](#mfer-init) - Interactive setup wizard
|
|
89
97
|
- [`mfer run`](#mfer-run-group_name) - Run micro frontend applications
|
|
90
98
|
- [`mfer pull`](#mfer-pull-group_name) - Pull latest changes from git repositories
|
|
@@ -94,81 +102,101 @@ mfer pull frontend
|
|
|
94
102
|
- [`mfer help`](#mfer-help) - Display help information
|
|
95
103
|
|
|
96
104
|
### `mfer init`
|
|
105
|
+
|
|
97
106
|
Interactive setup wizard to create your configuration file.
|
|
98
107
|
|
|
99
108
|
**Options:**
|
|
109
|
+
|
|
100
110
|
- `-f, --force`: Force re-initialization even if config exists
|
|
101
111
|
|
|
102
112
|
**Example:**
|
|
113
|
+
|
|
103
114
|
```bash
|
|
104
115
|
mfer init --force
|
|
105
116
|
```
|
|
106
117
|
|
|
107
118
|
### `mfer run [group_name]`
|
|
119
|
+
|
|
108
120
|
Run micro frontend applications concurrently.
|
|
109
121
|
|
|
110
122
|
**Arguments:**
|
|
123
|
+
|
|
111
124
|
- `group_name`: Name of the group to run (defaults to "all")
|
|
112
125
|
|
|
113
126
|
**Example:**
|
|
127
|
+
|
|
114
128
|
```bash
|
|
115
129
|
mfer run # Run all micro frontends
|
|
116
130
|
mfer run frontend # Run only frontend group
|
|
117
131
|
```
|
|
118
132
|
|
|
119
133
|
### `mfer pull [group_name]`
|
|
134
|
+
|
|
120
135
|
Pull latest changes from git repositories.
|
|
121
136
|
|
|
122
137
|
**Arguments:**
|
|
138
|
+
|
|
123
139
|
- `group_name`: Name of the group to pull from (defaults to "all")
|
|
124
140
|
|
|
125
141
|
**Example:**
|
|
142
|
+
|
|
126
143
|
```bash
|
|
127
144
|
mfer pull # Pull from all repositories
|
|
128
145
|
mfer pull shared # Pull from shared components group only
|
|
129
146
|
```
|
|
130
147
|
|
|
131
148
|
### `mfer install [group_name]`
|
|
149
|
+
|
|
132
150
|
Install dependencies for all micro frontends in a group.
|
|
133
151
|
|
|
134
152
|
**Arguments:**
|
|
153
|
+
|
|
135
154
|
- `group_name`: Name of the group to install dependencies for (defaults to "all")
|
|
136
155
|
|
|
137
156
|
**Example:**
|
|
157
|
+
|
|
138
158
|
```bash
|
|
139
159
|
mfer install # Install dependencies for all micro frontends
|
|
140
160
|
mfer install frontend # Install dependencies for frontend group only
|
|
141
161
|
```
|
|
142
162
|
|
|
143
163
|
### `mfer clone [group_name]`
|
|
164
|
+
|
|
144
165
|
Clone repositories that don't exist locally.
|
|
145
166
|
|
|
146
167
|
**Arguments:**
|
|
168
|
+
|
|
147
169
|
- `group_name`: Name of the group to clone repositories from (defaults to "all")
|
|
148
170
|
|
|
149
171
|
**Example:**
|
|
172
|
+
|
|
150
173
|
```bash
|
|
151
174
|
mfer clone # Clone all repositories
|
|
152
175
|
mfer clone shared # Clone repositories in shared group only
|
|
153
176
|
```
|
|
154
177
|
|
|
155
178
|
### `mfer config`
|
|
179
|
+
|
|
156
180
|
Manage your configuration settings.
|
|
157
181
|
|
|
158
182
|
**Subcommands:**
|
|
183
|
+
|
|
159
184
|
- `mfer config list`: Display current configuration
|
|
160
185
|
- `mfer config edit`: Open configuration file in your default editor
|
|
161
186
|
|
|
162
187
|
**Example:**
|
|
188
|
+
|
|
163
189
|
```bash
|
|
164
190
|
mfer config list # Show current configuration
|
|
165
191
|
mfer config edit # Edit configuration in your editor
|
|
166
192
|
```
|
|
167
193
|
|
|
168
194
|
### `mfer help`
|
|
195
|
+
|
|
169
196
|
Display help information for mfer commands.
|
|
170
197
|
|
|
171
198
|
**Example:**
|
|
199
|
+
|
|
172
200
|
```bash
|
|
173
201
|
mfer help # Show general help
|
|
174
202
|
mfer help run # Show help for run command
|
|
@@ -208,15 +236,17 @@ groups:
|
|
|
208
236
|
You can edit your configuration in several ways:
|
|
209
237
|
|
|
210
238
|
1. **Interactive editor** (recommended):
|
|
239
|
+
|
|
211
240
|
```bash
|
|
212
241
|
mfer config edit
|
|
213
242
|
```
|
|
214
243
|
|
|
215
244
|
2. **Direct file editing**:
|
|
245
|
+
|
|
216
246
|
```bash
|
|
217
247
|
# On macOS/Linux
|
|
218
248
|
nano ~/.mfer/config.yaml
|
|
219
|
-
|
|
249
|
+
|
|
220
250
|
# On Windows
|
|
221
251
|
notepad %USERPROFILE%\.mfer\config.yaml
|
|
222
252
|
```
|
|
@@ -224,6 +254,7 @@ You can edit your configuration in several ways:
|
|
|
224
254
|
## 🎯 Use Cases
|
|
225
255
|
|
|
226
256
|
### Development Workflow
|
|
257
|
+
|
|
227
258
|
```bash
|
|
228
259
|
# Start your day
|
|
229
260
|
mfer pull # Get latest changes
|
|
@@ -234,6 +265,7 @@ mfer run admin # Start admin panel
|
|
|
234
265
|
```
|
|
235
266
|
|
|
236
267
|
### Project Organization
|
|
268
|
+
|
|
237
269
|
Organize your micro frontends into logical groups:
|
|
238
270
|
|
|
239
271
|
```yaml
|
|
@@ -258,6 +290,7 @@ groups:
|
|
|
258
290
|
```
|
|
259
291
|
|
|
260
292
|
### Team Collaboration
|
|
293
|
+
|
|
261
294
|
- Share configuration files with your team
|
|
262
295
|
- Standardize development environment setup
|
|
263
296
|
- Ensure everyone runs the same services
|
|
@@ -265,16 +298,19 @@ groups:
|
|
|
265
298
|
## 🔧 Advanced Usage
|
|
266
299
|
|
|
267
300
|
### Custom Start Commands
|
|
268
|
-
|
|
301
|
+
|
|
302
|
+
By default, mfer runs `npm start` in each project directory.
|
|
269
303
|
You can currently only customize this by modifying the run command in the source code.
|
|
270
304
|
|
|
271
|
-
Adding configurable custom start commands is something I plan on adding in the near future.
|
|
305
|
+
Adding configurable custom start commands is something I plan on adding in the near future.
|
|
272
306
|
I also welcome anyone to open a PR for that!
|
|
273
307
|
|
|
274
308
|
### Environment Variables
|
|
309
|
+
|
|
275
310
|
mfer respects your existing environment setup and will use the same Node.js and npm versions you have configured.
|
|
276
311
|
|
|
277
312
|
### Process Management
|
|
313
|
+
|
|
278
314
|
- All processes are managed concurrently with organized output
|
|
279
315
|
- Use Ctrl+C to gracefully shut down all running services
|
|
280
316
|
- Failed processes are reported with detailed error information
|
|
@@ -284,12 +320,14 @@ mfer respects your existing environment setup and will use the same Node.js and
|
|
|
284
320
|
### Common Issues
|
|
285
321
|
|
|
286
322
|
**"No configuration file detected"**
|
|
323
|
+
|
|
287
324
|
```bash
|
|
288
325
|
# Run the initialization wizard
|
|
289
326
|
mfer init
|
|
290
327
|
```
|
|
291
328
|
|
|
292
329
|
**"Group not found"**
|
|
330
|
+
|
|
293
331
|
```bash
|
|
294
332
|
# Check available groups
|
|
295
333
|
mfer config list
|
|
@@ -299,15 +337,18 @@ mfer config edit
|
|
|
299
337
|
```
|
|
300
338
|
|
|
301
339
|
**"Directory does not exist"**
|
|
340
|
+
|
|
302
341
|
- Ensure the `mfe_directory` path in your configuration is correct
|
|
303
342
|
- Use absolute paths for better reliability
|
|
304
343
|
- Check that the directory exists and is accessible
|
|
305
344
|
|
|
306
345
|
**"Not a git repository"**
|
|
346
|
+
|
|
307
347
|
- Ensure all projects in your configuration are valid git repositories
|
|
308
348
|
- Run `mfer clone` to clone missing repositories
|
|
309
349
|
|
|
310
350
|
### Development Mode
|
|
351
|
+
|
|
311
352
|
For local development of mfer itself:
|
|
312
353
|
|
|
313
354
|
```bash
|
|
@@ -331,6 +372,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|
|
331
372
|
## 🙏 Acknowledgments
|
|
332
373
|
|
|
333
374
|
Built with:
|
|
375
|
+
|
|
334
376
|
- [Commander.js](https://github.com/tj/commander.js) - CLI framework
|
|
335
377
|
- [Inquirer](https://github.com/SBoudrias/Inquirer.js) - Interactive prompts
|
|
336
378
|
- [Concurrently](https://github.com/open-cli-tools/concurrently) - Process management
|
package/dist/commands/clone.js
CHANGED
|
@@ -36,7 +36,7 @@ const cloneCommand = new Command("clone")
|
|
|
36
36
|
const gitResult = spawnSync("git", ["rev-parse", "--git-dir"], {
|
|
37
37
|
cwd: repoPath,
|
|
38
38
|
stdio: "pipe",
|
|
39
|
-
shell: true
|
|
39
|
+
shell: true,
|
|
40
40
|
});
|
|
41
41
|
if (gitResult.status === 0) {
|
|
42
42
|
existingRepos.push(repo);
|
|
@@ -50,7 +50,7 @@ const cloneCommand = new Command("clone")
|
|
|
50
50
|
}
|
|
51
51
|
if (existingRepos.length > 0) {
|
|
52
52
|
console.log(chalk.green(`\nRepositories already exist (${existingRepos.length}):`));
|
|
53
|
-
existingRepos.forEach(repo => {
|
|
53
|
+
existingRepos.forEach((repo) => {
|
|
54
54
|
console.log(chalk.green(` ✓ ${repo}`));
|
|
55
55
|
});
|
|
56
56
|
console.log();
|
|
@@ -73,7 +73,7 @@ const cloneCommand = new Command("clone")
|
|
|
73
73
|
command: `git clone ${baseUrl}/${repo}.git`,
|
|
74
74
|
name: repo,
|
|
75
75
|
cwd: mfeDir,
|
|
76
|
-
prefixColor: "green"
|
|
76
|
+
prefixColor: "green",
|
|
77
77
|
}));
|
|
78
78
|
console.log(chalk.green(`Cloning ${reposToClone.length} repositories in group: ${groupName}...`));
|
|
79
79
|
const concurrentlyResult = concurrently(commands, {
|
|
@@ -83,14 +83,14 @@ const cloneCommand = new Command("clone")
|
|
|
83
83
|
});
|
|
84
84
|
const handleSigint = () => {
|
|
85
85
|
console.log(chalk.yellow("\nReceived SIGINT. Stopping all clone operations..."));
|
|
86
|
-
concurrentlyResult.commands.forEach(cmd => {
|
|
87
|
-
if (cmd && typeof cmd.kill ===
|
|
86
|
+
concurrentlyResult.commands.forEach((cmd) => {
|
|
87
|
+
if (cmd && typeof cmd.kill === "function") {
|
|
88
88
|
cmd.kill();
|
|
89
89
|
}
|
|
90
90
|
});
|
|
91
91
|
process.exit(0);
|
|
92
92
|
};
|
|
93
|
-
process.once(
|
|
93
|
+
process.once("SIGINT", handleSigint);
|
|
94
94
|
concurrentlyResult.result.then(() => {
|
|
95
95
|
console.log(chalk.green(`\nSuccessfully cloned all repositories in group: ${groupName}`));
|
|
96
96
|
console.log(chalk.blue(`Repositories are located in: ${mfeDir}`));
|
|
@@ -105,7 +105,7 @@ const cloneCommand = new Command("clone")
|
|
|
105
105
|
console.error(chalk.yellow(` Repository ${name} failed to clone (cwd: ${cwd}) with exit code ${exitCode}`));
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
|
-
else if (err && err
|
|
108
|
+
else if (err && typeof err === "object" && "message" in err) {
|
|
109
109
|
console.error(err.message);
|
|
110
110
|
}
|
|
111
111
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import { configExists, configPath, warnOfMissingConfig } from "../../../utils/config-utils.js";
|
|
2
|
+
import { configExists, configPath, warnOfMissingConfig, } from "../../../utils/config-utils.js";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { spawn } from "child_process";
|
|
5
5
|
import * as os from "os";
|
|
@@ -10,12 +10,14 @@ export const editConfigCommand = new Command("edit")
|
|
|
10
10
|
warnOfMissingConfig();
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
-
const editor = process.env.EDITOR ||
|
|
13
|
+
const editor = process.env.EDITOR ||
|
|
14
|
+
process.env.VISUAL ||
|
|
15
|
+
(os.platform() === "win32" ? "notepad" : "vi");
|
|
14
16
|
console.log(chalk.green(`Opening config file in editor: ${editor}\n`));
|
|
15
17
|
spawn(editor, [configPath], {
|
|
16
18
|
stdio: "ignore",
|
|
17
19
|
detached: true,
|
|
18
|
-
shell: true
|
|
20
|
+
shell: true,
|
|
19
21
|
}).unref();
|
|
20
22
|
process.exit(0);
|
|
21
23
|
});
|
package/dist/commands/init.js
CHANGED
|
@@ -12,14 +12,6 @@ import { configExists, isConfigValid, saveConfig, configPath, } from "../utils/c
|
|
|
12
12
|
import chalk from "chalk";
|
|
13
13
|
import * as fs from "fs";
|
|
14
14
|
import { input, confirm, checkbox } from "@inquirer/prompts";
|
|
15
|
-
const templateConfig = {
|
|
16
|
-
base_github_url: "https://github.com/your-username",
|
|
17
|
-
mfe_directory: "path/to/folder/containing/microfrontends",
|
|
18
|
-
groups: {
|
|
19
|
-
all: ["repo_name_1", "repo_name_2", "repo_name_3"],
|
|
20
|
-
customGroup1: ["repo_name2", "repo_name_3"],
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
15
|
function createAndSaveConfig(githubUsername, mfeDirectory, allGroup = []) {
|
|
24
16
|
const repositories = allGroup.length > 0 ? allGroup : ["my_mfe_1", "my_mfe_2"];
|
|
25
17
|
const newConfig = {
|
|
@@ -43,18 +35,18 @@ function createAndSaveConfig(githubUsername, mfeDirectory, allGroup = []) {
|
|
|
43
35
|
}
|
|
44
36
|
function getFoldersFromDirectory(directoryPath) {
|
|
45
37
|
try {
|
|
46
|
-
if (fs.existsSync(directoryPath) &&
|
|
38
|
+
if (fs.existsSync(directoryPath) &&
|
|
39
|
+
fs.statSync(directoryPath).isDirectory()) {
|
|
47
40
|
const entries = fs.readdirSync(directoryPath, { withFileTypes: true });
|
|
48
|
-
return entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
41
|
+
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
49
42
|
}
|
|
50
43
|
}
|
|
51
|
-
catch (
|
|
44
|
+
catch (_a) {
|
|
52
45
|
}
|
|
53
46
|
return [];
|
|
54
47
|
}
|
|
55
48
|
function promptForGitHubInfo() {
|
|
56
49
|
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
-
var _a, _b;
|
|
58
50
|
try {
|
|
59
51
|
const usesGithub = yield confirm({
|
|
60
52
|
message: "Do you use GitHub to host your repositories?",
|
|
@@ -66,53 +58,42 @@ function promptForGitHubInfo() {
|
|
|
66
58
|
}
|
|
67
59
|
const githubUsername = yield input({
|
|
68
60
|
message: "What is your GitHub username?",
|
|
69
|
-
validate: (val) => val && val.trim() !== "" ? true : "Username cannot be empty"
|
|
61
|
+
validate: (val) => val && val.trim() !== "" ? true : "Username cannot be empty",
|
|
70
62
|
});
|
|
71
63
|
return { usesGithub: true, githubUsername };
|
|
72
64
|
}
|
|
73
65
|
catch (error) {
|
|
74
|
-
if (error instanceof Error && (((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('SIGINT')) || ((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('User force closed')))) {
|
|
75
|
-
throw error;
|
|
76
|
-
}
|
|
77
66
|
throw error;
|
|
78
67
|
}
|
|
79
68
|
});
|
|
80
69
|
}
|
|
81
70
|
function promptForMFEDirectory() {
|
|
82
71
|
return __awaiter(this, void 0, void 0, function* () {
|
|
83
|
-
var _a, _b;
|
|
84
72
|
try {
|
|
85
73
|
return yield input({
|
|
86
74
|
message: [
|
|
87
75
|
"Enter the path to the folder containing all your micro frontends.",
|
|
88
76
|
" (Tip: Drag a folder from your file explorer into this terminal to paste its path)",
|
|
89
|
-
" >>>"
|
|
77
|
+
" >>>",
|
|
90
78
|
].join("\n"),
|
|
91
|
-
validate: (val) => val && val.trim() !== "" ? true : "Folder path cannot be empty"
|
|
79
|
+
validate: (val) => val && val.trim() !== "" ? true : "Folder path cannot be empty",
|
|
92
80
|
});
|
|
93
81
|
}
|
|
94
82
|
catch (error) {
|
|
95
|
-
if (error instanceof Error && (((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('SIGINT')) || ((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('User force closed')))) {
|
|
96
|
-
throw error;
|
|
97
|
-
}
|
|
98
83
|
throw error;
|
|
99
84
|
}
|
|
100
85
|
});
|
|
101
86
|
}
|
|
102
87
|
function promptForFolderSelection(folders) {
|
|
103
88
|
return __awaiter(this, void 0, void 0, function* () {
|
|
104
|
-
var _a, _b;
|
|
105
89
|
try {
|
|
106
90
|
return yield checkbox({
|
|
107
91
|
message: "Select which folders to include in the default 'all' group:",
|
|
108
|
-
choices: folders.map(f => ({ name: f, value: f })),
|
|
109
|
-
validate: (arr) => arr.length > 0 ? true : "Select at least one folder"
|
|
92
|
+
choices: folders.map((f) => ({ name: f, value: f })),
|
|
93
|
+
validate: (arr) => (arr.length > 0 ? true : "Select at least one folder"),
|
|
110
94
|
});
|
|
111
95
|
}
|
|
112
96
|
catch (error) {
|
|
113
|
-
if (error instanceof Error && (((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('SIGINT')) || ((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('User force closed')))) {
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
97
|
throw error;
|
|
117
98
|
}
|
|
118
99
|
});
|
|
@@ -128,7 +109,7 @@ const initCommand = new Command("init")
|
|
|
128
109
|
console.log(chalk.yellow("\nReceived SIGINT. Stopping initialization..."));
|
|
129
110
|
process.exit(130);
|
|
130
111
|
};
|
|
131
|
-
process.once(
|
|
112
|
+
process.once("SIGINT", handleSigint);
|
|
132
113
|
if (configExists && isConfigValid() && !options.force) {
|
|
133
114
|
const messagePrefix = chalk.red("Error");
|
|
134
115
|
const mferCommandHint = chalk.blue("mfer config edit");
|
|
@@ -165,7 +146,9 @@ const initCommand = new Command("init")
|
|
|
165
146
|
}
|
|
166
147
|
}
|
|
167
148
|
catch (error) {
|
|
168
|
-
if (error instanceof Error &&
|
|
149
|
+
if (error instanceof Error &&
|
|
150
|
+
(((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes("SIGINT")) ||
|
|
151
|
+
((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes("User force closed")))) {
|
|
169
152
|
console.log(chalk.yellow("\nReceived SIGINT. Stopping initialization..."));
|
|
170
153
|
process.exit(130);
|
|
171
154
|
}
|
package/dist/commands/install.js
CHANGED
|
@@ -40,7 +40,7 @@ const installCommand = new Command("install")
|
|
|
40
40
|
interrupted = true;
|
|
41
41
|
console.log(chalk.yellow("\nReceived SIGINT. Stopping installs..."));
|
|
42
42
|
};
|
|
43
|
-
process.once(
|
|
43
|
+
process.once("SIGINT", handleSigint);
|
|
44
44
|
for (const mfe of group) {
|
|
45
45
|
if (interrupted)
|
|
46
46
|
break;
|
|
@@ -49,7 +49,7 @@ const installCommand = new Command("install")
|
|
|
49
49
|
const result = spawnSync("npm", ["install"], {
|
|
50
50
|
cwd,
|
|
51
51
|
stdio: "inherit",
|
|
52
|
-
shell: true
|
|
52
|
+
shell: true,
|
|
53
53
|
});
|
|
54
54
|
if (result.status !== 0) {
|
|
55
55
|
hadError = true;
|
package/dist/commands/pull.js
CHANGED
|
@@ -34,19 +34,19 @@ const pullCommand = new Command("pull")
|
|
|
34
34
|
if (!fs.existsSync(repoPath)) {
|
|
35
35
|
invalidRepos.push({
|
|
36
36
|
name: repo,
|
|
37
|
-
reason: `Directory does not exist: ${repoPath}
|
|
37
|
+
reason: `Directory does not exist: ${repoPath}`,
|
|
38
38
|
});
|
|
39
39
|
continue;
|
|
40
40
|
}
|
|
41
41
|
const gitResult = spawnSync("git", ["rev-parse", "--git-dir"], {
|
|
42
42
|
cwd: repoPath,
|
|
43
43
|
stdio: "pipe",
|
|
44
|
-
shell: true
|
|
44
|
+
shell: true,
|
|
45
45
|
});
|
|
46
46
|
if (gitResult.status !== 0) {
|
|
47
47
|
invalidRepos.push({
|
|
48
48
|
name: repo,
|
|
49
|
-
reason: `Not a git repository: ${repoPath}
|
|
49
|
+
reason: `Not a git repository: ${repoPath}`,
|
|
50
50
|
});
|
|
51
51
|
continue;
|
|
52
52
|
}
|
|
@@ -61,7 +61,7 @@ const pullCommand = new Command("pull")
|
|
|
61
61
|
}
|
|
62
62
|
if (validRepos.length === 0) {
|
|
63
63
|
console.log(chalk.red("No valid git repositories found to pull from."));
|
|
64
|
-
if (invalidRepos.some(repo => repo.reason.includes("Directory does not exist"))) {
|
|
64
|
+
if (invalidRepos.some((repo) => repo.reason.includes("Directory does not exist"))) {
|
|
65
65
|
console.log(chalk.blue("\nTip: Run 'mfer init' to clone repositories that don't exist yet."));
|
|
66
66
|
}
|
|
67
67
|
return;
|
|
@@ -70,7 +70,7 @@ const pullCommand = new Command("pull")
|
|
|
70
70
|
command: "git pull",
|
|
71
71
|
name: repo,
|
|
72
72
|
cwd: path.join(mfeDir, repo),
|
|
73
|
-
prefixColor: "green"
|
|
73
|
+
prefixColor: "green",
|
|
74
74
|
}));
|
|
75
75
|
console.log(chalk.green(`Pulling latest changes for ${validRepos.length} repositories in group: ${groupName}...`));
|
|
76
76
|
const concurrentlyResult = concurrently(commands, {
|
|
@@ -80,14 +80,14 @@ const pullCommand = new Command("pull")
|
|
|
80
80
|
});
|
|
81
81
|
const handleSigint = () => {
|
|
82
82
|
console.log(chalk.yellow("\nReceived SIGINT. Stopping all git pull operations..."));
|
|
83
|
-
concurrentlyResult.commands.forEach(cmd => {
|
|
84
|
-
if (cmd && typeof cmd.kill ===
|
|
83
|
+
concurrentlyResult.commands.forEach((cmd) => {
|
|
84
|
+
if (cmd && typeof cmd.kill === "function") {
|
|
85
85
|
cmd.kill();
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
88
|
process.exit(0);
|
|
89
89
|
};
|
|
90
|
-
process.once(
|
|
90
|
+
process.once("SIGINT", handleSigint);
|
|
91
91
|
concurrentlyResult.result.then(() => {
|
|
92
92
|
console.log(chalk.green(`\nSuccessfully pulled latest changes for all repositories in group: ${groupName}`));
|
|
93
93
|
}, (err) => {
|
|
@@ -101,7 +101,7 @@ const pullCommand = new Command("pull")
|
|
|
101
101
|
console.error(chalk.yellow(` Repository ${name} failed to pull (cwd: ${cwd}) with exit code ${exitCode}`));
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
|
-
else if (err && err
|
|
104
|
+
else if (err && typeof err === "object" && "message" in err) {
|
|
105
105
|
console.error(err.message);
|
|
106
106
|
}
|
|
107
107
|
});
|
package/dist/commands/run.js
CHANGED
|
@@ -42,12 +42,14 @@ const runCommand = new Command("run")
|
|
|
42
42
|
console.log(chalk.blue(`Select micro frontends to run from group '${groupName}':`));
|
|
43
43
|
selectedMFEs = yield checkbox({
|
|
44
44
|
message: "Choose which micro frontends to run:",
|
|
45
|
-
choices: group.map(mfe => ({ name: mfe, value: mfe })),
|
|
46
|
-
validate: (arr) => arr.length > 0 ? true : "Select at least one micro frontend"
|
|
45
|
+
choices: group.map((mfe) => ({ name: mfe, value: mfe })),
|
|
46
|
+
validate: (arr) => arr.length > 0 ? true : "Select at least one micro frontend",
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
catch (error) {
|
|
50
|
-
if (error instanceof Error &&
|
|
50
|
+
if (error instanceof Error &&
|
|
51
|
+
(((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes("SIGINT")) ||
|
|
52
|
+
((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes("User force closed")))) {
|
|
51
53
|
console.log(chalk.yellow("\nReceived SIGINT. Stopping..."));
|
|
52
54
|
process.exit(130);
|
|
53
55
|
}
|
|
@@ -59,9 +61,11 @@ const runCommand = new Command("run")
|
|
|
59
61
|
command: RUN_COMMAND,
|
|
60
62
|
name: mfe,
|
|
61
63
|
cwd: path.join(mfeDir, mfe),
|
|
62
|
-
prefixColor: "blue"
|
|
64
|
+
prefixColor: "blue",
|
|
63
65
|
}));
|
|
64
|
-
const groupText = options.select
|
|
66
|
+
const groupText = options.select
|
|
67
|
+
? `selected MFEs from group '${groupName}'`
|
|
68
|
+
: `group '${groupName}'`;
|
|
65
69
|
console.log(chalk.green(`Running micro frontends in ${groupText}...`));
|
|
66
70
|
const concurrentlyResult = concurrently(commands, {
|
|
67
71
|
prefix: "{name} |",
|
|
@@ -70,14 +74,14 @@ const runCommand = new Command("run")
|
|
|
70
74
|
});
|
|
71
75
|
const handleSigint = () => {
|
|
72
76
|
console.log(chalk.yellow("\nReceived SIGINT. Stopping all micro frontends..."));
|
|
73
|
-
concurrentlyResult.commands.forEach(cmd => {
|
|
74
|
-
if (cmd && typeof cmd.kill ===
|
|
77
|
+
concurrentlyResult.commands.forEach((cmd) => {
|
|
78
|
+
if (cmd && typeof cmd.kill === "function") {
|
|
75
79
|
cmd.kill();
|
|
76
80
|
}
|
|
77
81
|
});
|
|
78
82
|
process.exit(0);
|
|
79
83
|
};
|
|
80
|
-
process.once(
|
|
84
|
+
process.once("SIGINT", handleSigint);
|
|
81
85
|
concurrentlyResult.result.then(() => { }, (err) => {
|
|
82
86
|
console.error(chalk.red("One or more micro frontends failed to start."));
|
|
83
87
|
if (Array.isArray(err)) {
|
|
@@ -89,7 +93,7 @@ const runCommand = new Command("run")
|
|
|
89
93
|
console.error(chalk.yellow(` MFE ${name} failed to start (cwd: ${cwd}) with exit code ${exitCode}`));
|
|
90
94
|
});
|
|
91
95
|
}
|
|
92
|
-
else if (err && err
|
|
96
|
+
else if (err && typeof err === "object" && "message" in err) {
|
|
93
97
|
console.error(err.message);
|
|
94
98
|
}
|
|
95
99
|
});
|
package/dist/index.js
CHANGED
|
@@ -10,11 +10,11 @@ import { loadConfig } from "./utils/config-utils.js";
|
|
|
10
10
|
program
|
|
11
11
|
.name("mfer")
|
|
12
12
|
.description("Micro Frontend Runner (mfer) - A CLI for running your project's micro frontends.")
|
|
13
|
-
.version("1.4.
|
|
14
|
-
.hook("preAction", (
|
|
13
|
+
.version("1.4.2", "-v, --version", "mfer CLI version")
|
|
14
|
+
.hook("preAction", () => {
|
|
15
15
|
console.log();
|
|
16
16
|
})
|
|
17
|
-
.hook("postAction", (
|
|
17
|
+
.hook("postAction", () => {
|
|
18
18
|
console.log();
|
|
19
19
|
});
|
|
20
20
|
program.addCommand(configCommand);
|
|
@@ -11,7 +11,9 @@ export const loadConfig = () => {
|
|
|
11
11
|
if (configExists) {
|
|
12
12
|
const configFile = fs.readFileSync(configPath, "utf8");
|
|
13
13
|
currentConfig = YAML.parse(configFile);
|
|
14
|
+
return currentConfig;
|
|
14
15
|
}
|
|
16
|
+
return undefined;
|
|
15
17
|
};
|
|
16
18
|
export const warnOfMissingConfig = () => {
|
|
17
19
|
if (!configExists) {
|
|
@@ -26,16 +28,16 @@ export const isConfigValid = () => {
|
|
|
26
28
|
const configFile = fs.readFileSync(configPath, "utf8");
|
|
27
29
|
const config = YAML.parse(configFile);
|
|
28
30
|
return (config &&
|
|
29
|
-
typeof config ===
|
|
31
|
+
typeof config === "object" &&
|
|
30
32
|
config.base_github_url &&
|
|
31
33
|
config.mfe_directory &&
|
|
32
34
|
config.groups &&
|
|
33
|
-
typeof config.groups ===
|
|
35
|
+
typeof config.groups === "object" &&
|
|
34
36
|
config.groups.all &&
|
|
35
37
|
Array.isArray(config.groups.all) &&
|
|
36
38
|
config.groups.all.length > 0);
|
|
37
39
|
}
|
|
38
|
-
catch (
|
|
40
|
+
catch (_a) {
|
|
39
41
|
return false;
|
|
40
42
|
}
|
|
41
43
|
};
|
|
@@ -47,16 +49,18 @@ export const saveConfig = (newConfig) => {
|
|
|
47
49
|
}
|
|
48
50
|
fs.writeFileSync(configPath, YAML.stringify(newConfig));
|
|
49
51
|
}
|
|
50
|
-
catch (
|
|
51
|
-
console.log(`Error writing config file!\n\n${
|
|
52
|
+
catch (error) {
|
|
53
|
+
console.log(`Error writing config file!\n\n${error}`);
|
|
52
54
|
}
|
|
53
55
|
};
|
|
54
56
|
export const editConfig = () => {
|
|
55
|
-
const editor = process.env.EDITOR ||
|
|
57
|
+
const editor = process.env.EDITOR ||
|
|
58
|
+
process.env.VISUAL ||
|
|
59
|
+
(os.platform() === "win32" ? "notepad" : "vi");
|
|
56
60
|
console.log(chalk.green(`Opening config file in editor: ${editor}\n`));
|
|
57
61
|
spawn(editor, [configPath], {
|
|
58
62
|
stdio: "ignore",
|
|
59
63
|
detached: true,
|
|
60
|
-
shell: true
|
|
64
|
+
shell: true,
|
|
61
65
|
}).unref();
|
|
62
66
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mfer",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"description": "CLI tool designed to sensibly run micro-frontends from the terminal.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mfer": "dist/index.js"
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"clean": "rimraf dist",
|
|
12
12
|
"watch": "tsc --watch",
|
|
13
13
|
"test": "vitest run",
|
|
14
|
-
"test:
|
|
14
|
+
"test:coverage": "vitest run --coverage",
|
|
15
|
+
"lint": "eslint . --ext .ts && prettier --check .",
|
|
16
|
+
"lint:fix": "eslint . --ext .ts --fix && prettier --write ."
|
|
15
17
|
},
|
|
16
18
|
"keywords": [
|
|
17
19
|
"micro frontends",
|
|
@@ -39,8 +41,15 @@
|
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
43
|
"@types/node": "^24.0.3",
|
|
44
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
45
|
+
"eslint": "^9.33.0",
|
|
46
|
+
"eslint-config-prettier": "^10.1.8",
|
|
47
|
+
"globals": "^16.3.0",
|
|
48
|
+
"jiti": "^2.5.1",
|
|
49
|
+
"prettier": "3.6.2",
|
|
42
50
|
"rimraf": "^6.0.1",
|
|
43
51
|
"typescript": "^5.8.3",
|
|
52
|
+
"typescript-eslint": "^8.40.0",
|
|
44
53
|
"vitest": "^3.2.4"
|
|
45
54
|
},
|
|
46
55
|
"dependencies": {
|