mfer 3.2.4 → 3.3.0
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 +57 -13
- package/dist/commands/run.js +33 -12
- package/dist/index.js +2 -2
- package/dist/utils/command-utils.js +8 -0
- package/dist/utils/config-utils.js +18 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,7 +95,7 @@ mfer pull frontend
|
|
|
95
95
|
### Quick Reference
|
|
96
96
|
|
|
97
97
|
- [`mfer init`](#mfer-init) - Interactive setup wizard
|
|
98
|
-
- [`mfer run`](#mfer-run-group_name) - Run micro frontend applications
|
|
98
|
+
- [`mfer run`](#mfer-run-group_name) - Run micro frontend applications (supports per-MFE run modes)
|
|
99
99
|
- [`mfer pull`](#mfer-pull-group_name) - Pull latest changes from git repositories
|
|
100
100
|
- [`mfer install`](#mfer-install-group_name) - Install dependencies for micro frontends
|
|
101
101
|
- [`mfer clone`](#mfer-clone-group_name) - Clone repositories that don't exist locally
|
|
@@ -127,20 +127,24 @@ Run micro frontend applications concurrently.
|
|
|
127
127
|
|
|
128
128
|
**Options:**
|
|
129
129
|
|
|
130
|
-
- `-c, --command <command>`: Custom command to run (default: npm start)
|
|
131
|
-
- `-a, --async`: Run custom command concurrently instead of sequentially
|
|
130
|
+
- `-c, --command <command>`: Custom command to run for all MFEs (default: npm start)
|
|
131
|
+
- `-a, --async`: Run custom command concurrently instead of sequentially (only with `--command`)
|
|
132
|
+
- `-m, --mode <mode_name>`: Run MFEs using a named mode from config (see [Per-MFE Run Modes](#per-mfe-run-modes))
|
|
132
133
|
- `-s, --select`: Prompt to select which micro frontends to run
|
|
133
134
|
|
|
135
|
+
> **Note:** `--mode` and `--command` are mutually exclusive.
|
|
136
|
+
|
|
134
137
|
**Examples:**
|
|
135
138
|
|
|
136
139
|
```bash
|
|
137
|
-
mfer run
|
|
138
|
-
mfer run frontend
|
|
139
|
-
mfer run --
|
|
140
|
-
mfer run
|
|
140
|
+
mfer run # Run all MFEs with default command (npm start)
|
|
141
|
+
mfer run frontend # Run frontend group with default command
|
|
142
|
+
mfer run --mode mock # Run all MFEs in mock mode (per-MFE commands from config)
|
|
143
|
+
mfer run frontend --mode mock # Run frontend group in mock mode
|
|
144
|
+
mfer run --command "npm ci" home # Run custom command sequentially on home group
|
|
145
|
+
mfer run -c "yarn install" shared # Run yarn install sequentially on shared group
|
|
141
146
|
mfer run --command "npm ci" --async home # Run custom command concurrently on home group
|
|
142
|
-
mfer run
|
|
143
|
-
mfer run --command "npm run build" --select # Select MFEs and run build command sequentially
|
|
147
|
+
mfer run --command "npm run build" --select # Select MFEs and run build sequentially
|
|
144
148
|
```
|
|
145
149
|
|
|
146
150
|
### `mfer pull [group_name]`
|
|
@@ -376,6 +380,11 @@ groups:
|
|
|
376
380
|
admin:
|
|
377
381
|
- my-admin-panel
|
|
378
382
|
- my-shared-components
|
|
383
|
+
mfes: # optional, per-MFE configuration
|
|
384
|
+
my-main-app:
|
|
385
|
+
modes:
|
|
386
|
+
- mode_name: mock
|
|
387
|
+
command: npm run start:mocked
|
|
379
388
|
```
|
|
380
389
|
|
|
381
390
|
### Configuration Options
|
|
@@ -387,6 +396,42 @@ groups:
|
|
|
387
396
|
- **`groups`**: Named collections of micro frontend projects
|
|
388
397
|
- **`all`**: Default group containing all projects (required)
|
|
389
398
|
- **Custom groups**: Any additional groups you want to create
|
|
399
|
+
- **`mfes`**: Per-MFE configuration (optional). Each key is an MFE name matching one in `groups`.
|
|
400
|
+
- **`modes`**: Array of named run modes for the MFE. Each mode has:
|
|
401
|
+
- **`mode_name`**: Name used with `mfer run --mode <name>`
|
|
402
|
+
- **`command`**: The command to run for this MFE when the mode is active
|
|
403
|
+
|
|
404
|
+
### Per-MFE Run Modes
|
|
405
|
+
|
|
406
|
+
Run modes let individual MFEs use a different start command while the rest of the group continue using `npm start`. This is useful when some MFEs support a mocked/stubbed backend while others don't need it.
|
|
407
|
+
|
|
408
|
+
**Config example** — `root-config` runs with a mocked API, everything else runs normally:
|
|
409
|
+
|
|
410
|
+
```yaml
|
|
411
|
+
groups:
|
|
412
|
+
all:
|
|
413
|
+
- root-config
|
|
414
|
+
- mfe-nav
|
|
415
|
+
- mfe-dashboard
|
|
416
|
+
mfes:
|
|
417
|
+
root-config:
|
|
418
|
+
modes:
|
|
419
|
+
- mode_name: mock
|
|
420
|
+
command: npm run start:mocked
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Usage:**
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
mfer run --mode mock
|
|
427
|
+
# root-config → npm run start:mocked
|
|
428
|
+
# mfe-nav → npm start (no mock mode defined, falls back to default)
|
|
429
|
+
# mfe-dashboard → npm start
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
- MFEs that **do not** have the mode defined silently fall back to `npm start`.
|
|
433
|
+
- If **no MFE** in the group has the requested mode, a warning is printed but the command still runs (all MFEs use `npm start`).
|
|
434
|
+
- `--mode` cannot be combined with `--command`; they are mutually exclusive.
|
|
390
435
|
|
|
391
436
|
### Editing Configuration
|
|
392
437
|
|
|
@@ -456,11 +501,10 @@ groups:
|
|
|
456
501
|
|
|
457
502
|
### Custom Start Commands
|
|
458
503
|
|
|
459
|
-
By default, mfer runs `npm start` in each project directory.
|
|
460
|
-
You can currently only customize this by modifying the run command in the source code.
|
|
504
|
+
By default, mfer runs `npm start` in each project directory. You can customize this in two ways:
|
|
461
505
|
|
|
462
|
-
|
|
463
|
-
|
|
506
|
+
- **Same command for all MFEs**: use `--command` (e.g. `mfer run --command "yarn start"`)
|
|
507
|
+
- **Per-MFE commands via modes**: define `mfes[name].modes` in config and use `--mode` (see [Per-MFE Run Modes](#per-mfe-run-modes))
|
|
464
508
|
|
|
465
509
|
### Environment Variables
|
|
466
510
|
|
package/dist/commands/run.js
CHANGED
|
@@ -12,15 +12,15 @@ import { configExists, currentConfig, warnOfMissingConfig, } from "../utils/conf
|
|
|
12
12
|
import concurrently from "concurrently";
|
|
13
13
|
import chalk from "chalk";
|
|
14
14
|
import path from "path";
|
|
15
|
-
import { promptForMFESelection } from "../utils/command-utils.js";
|
|
15
|
+
import { promptForMFESelection, resolveRunCommand, } from "../utils/command-utils.js";
|
|
16
16
|
import { spawn } from "child_process";
|
|
17
|
-
const DEFAULT_RUN_COMMAND = "npm start";
|
|
18
17
|
const runCommand = new Command("run")
|
|
19
18
|
.description("run micro-frontend applications")
|
|
20
19
|
.argument("[group_name]", "name of the group as specified in the configuration", "all")
|
|
21
20
|
.option("-s, --select", "prompt to select which micro frontends to run")
|
|
22
21
|
.option("-c, --command <command>", "custom command to run (default: npm start)")
|
|
23
22
|
.option("-a, --async", "run custom command concurrently instead of sequentially (only works with --command option)")
|
|
23
|
+
.option("-m, --mode <mode_name>", "run MFEs using a named mode from config")
|
|
24
24
|
.action((groupName, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
25
|
if (!configExists) {
|
|
26
26
|
warnOfMissingConfig();
|
|
@@ -33,11 +33,16 @@ const runCommand = new Command("run")
|
|
|
33
33
|
console.log(`${messagePrefix}: custom command cannot be empty`);
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
-
if (options.async && !options.command) {
|
|
36
|
+
if (options.async && !options.command && !options.mode) {
|
|
37
37
|
const messagePrefix = chalk.red("Error");
|
|
38
38
|
console.log(`${messagePrefix}: --async can only be used with --command option`);
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
+
if (options.mode && options.command) {
|
|
42
|
+
const messagePrefix = chalk.red("Error");
|
|
43
|
+
console.log(`${messagePrefix}: --mode and --command cannot be used together`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
41
46
|
const group = currentConfig.groups[groupName];
|
|
42
47
|
if (!group) {
|
|
43
48
|
const messagePrefix = chalk.red("Error");
|
|
@@ -54,26 +59,42 @@ const runCommand = new Command("run")
|
|
|
54
59
|
if (options.select) {
|
|
55
60
|
selectedMFEs = yield promptForMFESelection(groupName, group);
|
|
56
61
|
}
|
|
62
|
+
if (options.mode) {
|
|
63
|
+
const hasAnyMfeWithMode = selectedMFEs.some((mfe) => {
|
|
64
|
+
var _a, _b, _c;
|
|
65
|
+
return (_c = (_b = (_a = currentConfig.mfes) === null || _a === void 0 ? void 0 : _a[mfe]) === null || _b === void 0 ? void 0 : _b.modes) === null || _c === void 0 ? void 0 : _c.some((m) => m.mode_name === options.mode);
|
|
66
|
+
});
|
|
67
|
+
if (!hasAnyMfeWithMode) {
|
|
68
|
+
console.log(chalk.yellow(`Warning: no MFE in the selected group has a '${options.mode}' mode defined. All MFEs will use the default command.`));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
57
71
|
const mfeDir = currentConfig.mfe_directory;
|
|
58
|
-
const commandToRun = options.command || DEFAULT_RUN_COMMAND;
|
|
59
72
|
const isAsync = options.async && options.command;
|
|
73
|
+
const mfeCommands = selectedMFEs.map((mfe) => ({
|
|
74
|
+
mfe,
|
|
75
|
+
command: options.command
|
|
76
|
+
? options.command
|
|
77
|
+
: resolveRunCommand(mfe, options.mode, currentConfig),
|
|
78
|
+
}));
|
|
60
79
|
const groupText = options.select
|
|
61
80
|
? `selected MFEs from group '${groupName}'`
|
|
62
81
|
: `group '${groupName}'`;
|
|
63
82
|
const commandText = options.command
|
|
64
|
-
? `custom command '${
|
|
65
|
-
:
|
|
83
|
+
? `custom command '${options.command}'`
|
|
84
|
+
: options.mode
|
|
85
|
+
? `mode '${options.mode}'`
|
|
86
|
+
: "default command";
|
|
66
87
|
console.log(chalk.green(`Running ${commandText} on micro frontends in ${groupText}...`));
|
|
67
88
|
if (isAsync || !options.command) {
|
|
68
|
-
yield runConcurrently(
|
|
89
|
+
yield runConcurrently(mfeCommands, mfeDir);
|
|
69
90
|
}
|
|
70
91
|
else {
|
|
71
|
-
yield runSequentially(
|
|
92
|
+
yield runSequentially(mfeCommands, mfeDir);
|
|
72
93
|
}
|
|
73
94
|
}));
|
|
74
|
-
function runSequentially(
|
|
95
|
+
function runSequentially(mfeCommands, mfeDir) {
|
|
75
96
|
return __awaiter(this, void 0, void 0, function* () {
|
|
76
|
-
for (const mfe of
|
|
97
|
+
for (const { mfe, command } of mfeCommands) {
|
|
77
98
|
const cwd = path.join(mfeDir, mfe);
|
|
78
99
|
console.log(chalk.blue(`\n[${mfe}] Running: ${command}`));
|
|
79
100
|
try {
|
|
@@ -101,9 +122,9 @@ function runSequentially(mfes, command, mfeDir) {
|
|
|
101
122
|
}
|
|
102
123
|
});
|
|
103
124
|
}
|
|
104
|
-
function runConcurrently(
|
|
125
|
+
function runConcurrently(mfeCommands, mfeDir) {
|
|
105
126
|
return __awaiter(this, void 0, void 0, function* () {
|
|
106
|
-
const commands =
|
|
127
|
+
const commands = mfeCommands.map(({ mfe, command }) => ({
|
|
107
128
|
command,
|
|
108
129
|
name: mfe,
|
|
109
130
|
cwd: path.join(mfeDir, mfe),
|
package/dist/index.js
CHANGED
|
@@ -9,11 +9,11 @@ import pullCommand from "./commands/pull.js";
|
|
|
9
9
|
import libCommand from "./commands/lib/index.js";
|
|
10
10
|
import updateCommand from "./commands/update.js";
|
|
11
11
|
import { loadConfig } from "./utils/config-utils.js";
|
|
12
|
-
import { checkForUpdateNotification } from "./utils/version-utils.js";
|
|
12
|
+
import { checkForUpdateNotification, getInstalledVersion, } from "./utils/version-utils.js";
|
|
13
13
|
program
|
|
14
14
|
.name("mfer")
|
|
15
15
|
.description("Micro Frontend Runner (mfer) - A CLI for running your project's micro frontends.")
|
|
16
|
-
.version(
|
|
16
|
+
.version(getInstalledVersion(), "-v, --version", "mfer CLI version")
|
|
17
17
|
.hook("preAction", () => {
|
|
18
18
|
console.log();
|
|
19
19
|
})
|
|
@@ -13,6 +13,14 @@ import { spawnSync } from "child_process";
|
|
|
13
13
|
import concurrently from "concurrently";
|
|
14
14
|
import path from "path";
|
|
15
15
|
import fs from "fs";
|
|
16
|
+
export const DEFAULT_RUN_COMMAND = "npm start";
|
|
17
|
+
export function resolveRunCommand(mfe, modeName, config) {
|
|
18
|
+
var _a, _b, _c, _d;
|
|
19
|
+
if (!modeName)
|
|
20
|
+
return DEFAULT_RUN_COMMAND;
|
|
21
|
+
const mode = (_c = (_b = (_a = config.mfes) === null || _a === void 0 ? void 0 : _a[mfe]) === null || _b === void 0 ? void 0 : _b.modes) === null || _c === void 0 ? void 0 : _c.find((m) => m.mode_name === modeName);
|
|
22
|
+
return (_d = mode === null || mode === void 0 ? void 0 : mode.command) !== null && _d !== void 0 ? _d : DEFAULT_RUN_COMMAND;
|
|
23
|
+
}
|
|
16
24
|
export function promptForMFESelection(groupName, mfes) {
|
|
17
25
|
return __awaiter(this, void 0, void 0, function* () {
|
|
18
26
|
var _a, _b;
|
|
@@ -39,6 +39,24 @@ export const isConfigValid = () => {
|
|
|
39
39
|
if (config.lib_directory && (!config.libs || !Array.isArray(config.libs))) {
|
|
40
40
|
return false;
|
|
41
41
|
}
|
|
42
|
+
if (config.mfes && typeof config.mfes === "object") {
|
|
43
|
+
for (const mfeConfig of Object.values(config.mfes)) {
|
|
44
|
+
if (mfeConfig &&
|
|
45
|
+
mfeConfig.modes !== undefined) {
|
|
46
|
+
const modes = mfeConfig.modes;
|
|
47
|
+
if (!Array.isArray(modes))
|
|
48
|
+
return false;
|
|
49
|
+
for (const mode of modes) {
|
|
50
|
+
if (!mode ||
|
|
51
|
+
typeof mode !== "object" ||
|
|
52
|
+
!mode.mode_name ||
|
|
53
|
+
!mode.command) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
42
60
|
return hasRequiredFields;
|
|
43
61
|
}
|
|
44
62
|
catch (_a) {
|