mfer 1.4.0 → 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 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
- By default, mfer runs `npm start` in each project directory.
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
@@ -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 === 'function') {
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('SIGINT', handleSigint);
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.message) {
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 || process.env.VISUAL || (os.platform() === "win32" ? "notepad" : "vi");
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
  });
@@ -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) && fs.statSync(directoryPath).isDirectory()) {
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 (e) {
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('SIGINT', handleSigint);
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 && (((_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')))) {
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
  }
@@ -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('SIGINT', handleSigint);
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;
@@ -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 === 'function') {
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('SIGINT', handleSigint);
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.message) {
104
+ else if (err && typeof err === "object" && "message" in err) {
105
105
  console.error(err.message);
106
106
  }
107
107
  });
@@ -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 && (((_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')))) {
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 ? `selected MFEs from group '${groupName}'` : `group '${groupName}'`;
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 === 'function') {
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('SIGINT', handleSigint);
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.message) {
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.0", "-v, --version", "mfer CLI version")
14
- .hook("preAction", (thisCommand, actionCommand) => {
13
+ .version("1.4.2", "-v, --version", "mfer CLI version")
14
+ .hook("preAction", () => {
15
15
  console.log();
16
16
  })
17
- .hook("postAction", (thisCommand, actionCommand) => {
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 === 'object' &&
31
+ typeof config === "object" &&
30
32
  config.base_github_url &&
31
33
  config.mfe_directory &&
32
34
  config.groups &&
33
- typeof config.groups === 'object' &&
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 (e) {
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 (e) {
51
- console.log(`Error writing config file!\n\n${e}`);
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 || process.env.VISUAL || (os.platform() === "win32" ? "notepad" : "vi");
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.0",
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:watch": "vitest"
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": {