mfer 3.0.1 → 3.2.4
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 +58 -2
- package/dist/commands/install.js +9 -39
- package/dist/commands/lib/index.js +5 -1
- package/dist/commands/lib/install.js +52 -0
- package/dist/commands/lib/pull.js +68 -0
- package/dist/commands/pull.js +20 -70
- package/dist/commands/run.js +4 -22
- package/dist/commands/update.js +41 -0
- package/dist/index.js +5 -1
- package/dist/utils/command-utils.js +181 -0
- package/dist/utils/version-utils.js +84 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -151,11 +151,17 @@ Pull latest changes from git repositories.
|
|
|
151
151
|
|
|
152
152
|
- `group_name`: Name of the group to pull from (defaults to "all")
|
|
153
153
|
|
|
154
|
-
**
|
|
154
|
+
**Options:**
|
|
155
|
+
|
|
156
|
+
- `-s, --select`: Prompt to select which repositories to pull
|
|
157
|
+
|
|
158
|
+
**Examples:**
|
|
155
159
|
|
|
156
160
|
```bash
|
|
157
161
|
mfer pull # Pull from all repositories
|
|
158
162
|
mfer pull shared # Pull from shared components group only
|
|
163
|
+
mfer pull --select # Select repositories to pull interactively
|
|
164
|
+
mfer pull frontend --select # Select repositories from frontend group
|
|
159
165
|
```
|
|
160
166
|
|
|
161
167
|
### `mfer install [group_name]`
|
|
@@ -166,11 +172,17 @@ Install dependencies for all micro frontends in a group.
|
|
|
166
172
|
|
|
167
173
|
- `group_name`: Name of the group to install dependencies for (defaults to "all")
|
|
168
174
|
|
|
169
|
-
**
|
|
175
|
+
**Options:**
|
|
176
|
+
|
|
177
|
+
- `-s, --select`: Prompt to select which micro frontends to install
|
|
178
|
+
|
|
179
|
+
**Examples:**
|
|
170
180
|
|
|
171
181
|
```bash
|
|
172
182
|
mfer install # Install dependencies for all micro frontends
|
|
173
183
|
mfer install frontend # Install dependencies for frontend group only
|
|
184
|
+
mfer install --select # Select micro frontends to install interactively
|
|
185
|
+
mfer install shared --select # Select micro frontends from shared group
|
|
174
186
|
```
|
|
175
187
|
|
|
176
188
|
### `mfer clone [group_name]`
|
|
@@ -226,6 +238,8 @@ Manage internal npm packages and their distribution to micro frontends.
|
|
|
226
238
|
- [`mfer lib deploy`](#mfer-lib-deploy-lib-name) - Copy built libraries to micro frontends
|
|
227
239
|
- [`mfer lib publish`](#mfer-lib-publish-lib-name) - Build and deploy libraries to micro frontends
|
|
228
240
|
- [`mfer lib list`](#mfer-lib-list) - List configured libraries and their status
|
|
241
|
+
- [`mfer lib pull`](#mfer-lib-pull-lib-name) - Pull latest changes from library git repositories
|
|
242
|
+
- [`mfer lib install`](#mfer-lib-install-lib-name) - Install dependencies for libraries
|
|
229
243
|
|
|
230
244
|
### `mfer lib build [lib-name]`
|
|
231
245
|
|
|
@@ -297,6 +311,48 @@ List configured libraries and their status.
|
|
|
297
311
|
mfer lib list # Show all configured libraries and their build status
|
|
298
312
|
```
|
|
299
313
|
|
|
314
|
+
### `mfer lib pull [lib-name]`
|
|
315
|
+
|
|
316
|
+
Pull latest changes from library git repositories.
|
|
317
|
+
|
|
318
|
+
**Arguments:**
|
|
319
|
+
|
|
320
|
+
- `lib-name`: Name of the library to pull from (pulls all if not specified)
|
|
321
|
+
|
|
322
|
+
**Options:**
|
|
323
|
+
|
|
324
|
+
- `-s, --select`: Prompt to select which libraries to pull
|
|
325
|
+
|
|
326
|
+
**Examples:**
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
mfer lib pull # Pull from all libraries
|
|
330
|
+
mfer lib pull my-shared-utils # Pull from specific library only
|
|
331
|
+
mfer lib pull --select # Select libraries to pull interactively
|
|
332
|
+
mfer lib pull my-design-system --select # Select libraries from specific library
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### `mfer lib install [lib-name]`
|
|
336
|
+
|
|
337
|
+
Install dependencies for libraries.
|
|
338
|
+
|
|
339
|
+
**Arguments:**
|
|
340
|
+
|
|
341
|
+
- `lib-name`: Name of the library to install dependencies for (installs all if not specified)
|
|
342
|
+
|
|
343
|
+
**Options:**
|
|
344
|
+
|
|
345
|
+
- `-s, --select`: Prompt to select which libraries to install
|
|
346
|
+
|
|
347
|
+
**Examples:**
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
mfer lib install # Install dependencies for all libraries
|
|
351
|
+
mfer lib install my-shared-utils # Install dependencies for specific library only
|
|
352
|
+
mfer lib install --select # Select libraries to install interactively
|
|
353
|
+
mfer lib install my-design-system --select # Select libraries from specific library
|
|
354
|
+
```
|
|
355
|
+
|
|
300
356
|
## ⚙️ Configuration
|
|
301
357
|
|
|
302
358
|
mfer uses a YAML configuration file located at `~/.mfer/config.yaml`. Here's an example structure:
|
package/dist/commands/install.js
CHANGED
|
@@ -10,12 +10,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { Command } from "commander";
|
|
11
11
|
import { configExists, currentConfig, warnOfMissingConfig, } from "../utils/config-utils.js";
|
|
12
12
|
import chalk from "chalk";
|
|
13
|
-
import
|
|
14
|
-
import { spawnSync } from "child_process";
|
|
13
|
+
import { runNpmInstallSequentially, promptForMFESelection, } from "../utils/command-utils.js";
|
|
15
14
|
const installCommand = new Command("install")
|
|
16
15
|
.description("run 'npm install' in micro-frontend applications synchronously")
|
|
17
16
|
.argument("[group_name]", "name of the group as specified in the configuration", "all")
|
|
18
|
-
.
|
|
17
|
+
.option("-s, --select", "prompt to select which micro frontends to install")
|
|
18
|
+
.action((groupName, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
19
|
if (!configExists) {
|
|
20
20
|
warnOfMissingConfig();
|
|
21
21
|
return;
|
|
@@ -32,42 +32,12 @@ const installCommand = new Command("install")
|
|
|
32
32
|
console.log(`${messagePrefix}: group '${groupName}' has no micro frontends defined.`);
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
let interrupted = false;
|
|
39
|
-
const handleSigint = () => {
|
|
40
|
-
interrupted = true;
|
|
41
|
-
console.log(chalk.yellow("\nReceived SIGINT. Stopping installs..."));
|
|
42
|
-
};
|
|
43
|
-
process.once("SIGINT", handleSigint);
|
|
44
|
-
for (const mfe of group) {
|
|
45
|
-
if (interrupted)
|
|
46
|
-
break;
|
|
47
|
-
const cwd = path.join(mfeDir, mfe);
|
|
48
|
-
console.log(chalk.blue(`[${mfe}] Running 'npm install' in ${cwd}`));
|
|
49
|
-
const result = spawnSync("npm", ["install"], {
|
|
50
|
-
cwd,
|
|
51
|
-
stdio: "inherit",
|
|
52
|
-
shell: true,
|
|
53
|
-
});
|
|
54
|
-
if (result.status !== 0) {
|
|
55
|
-
hadError = true;
|
|
56
|
-
console.error(chalk.red(` MFE ${mfe} failed to install (cwd: ${cwd}) with exit code ${result.status}`));
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
console.log(chalk.green(` MFE ${mfe} installed successfully (cwd: ${cwd})`));
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (hadError) {
|
|
63
|
-
console.error(chalk.red("One or more installs failed."));
|
|
64
|
-
process.exitCode = 1;
|
|
65
|
-
}
|
|
66
|
-
else if (interrupted) {
|
|
67
|
-
process.exitCode = 130;
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
console.log(chalk.green("All installs completed successfully."));
|
|
35
|
+
let selectedMFEs = group;
|
|
36
|
+
if (options.select) {
|
|
37
|
+
selectedMFEs = yield promptForMFESelection(groupName, group);
|
|
71
38
|
}
|
|
39
|
+
const mfeDir = currentConfig.mfe_directory;
|
|
40
|
+
const contextName = `group: ${groupName}`;
|
|
41
|
+
yield runNpmInstallSequentially(selectedMFEs, mfeDir, contextName, options);
|
|
72
42
|
}));
|
|
73
43
|
export default installCommand;
|
|
@@ -3,10 +3,14 @@ import buildCommand from "./build.js";
|
|
|
3
3
|
import deployCommand from "./deploy.js";
|
|
4
4
|
import publishCommand from "./publish.js";
|
|
5
5
|
import listCommand from "./list.js";
|
|
6
|
+
import pullCommand from "./pull.js";
|
|
7
|
+
import installCommand from "./install.js";
|
|
6
8
|
const libCommand = new Command("lib")
|
|
7
9
|
.description("Manage internal npm packages and their distribution to micro frontends")
|
|
8
10
|
.addCommand(buildCommand)
|
|
9
11
|
.addCommand(deployCommand)
|
|
10
12
|
.addCommand(publishCommand)
|
|
11
|
-
.addCommand(listCommand)
|
|
13
|
+
.addCommand(listCommand)
|
|
14
|
+
.addCommand(pullCommand)
|
|
15
|
+
.addCommand(installCommand);
|
|
12
16
|
export default libCommand;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { Command } from "commander";
|
|
11
|
+
import { configExists, currentConfig, warnOfMissingConfig, } from "../../utils/config-utils.js";
|
|
12
|
+
import chalk from "chalk";
|
|
13
|
+
import { runNpmInstallSequentially, promptForLibrarySelection, } from "../../utils/command-utils.js";
|
|
14
|
+
const installCommand = new Command("install")
|
|
15
|
+
.description("run 'npm install' in library directories")
|
|
16
|
+
.argument("[lib-name]", "name of the library to install dependencies for (installs all if not specified)")
|
|
17
|
+
.option("-s, --select", "prompt to select which libraries to install")
|
|
18
|
+
.action((libName, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
|
+
if (!configExists) {
|
|
20
|
+
warnOfMissingConfig();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (!currentConfig.lib_directory || !currentConfig.libs) {
|
|
24
|
+
console.log(chalk.red("Error: Library configuration not found in config file."));
|
|
25
|
+
console.log(chalk.yellow("Please run 'mfer init' to configure library settings."));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (!Array.isArray(currentConfig.libs) || currentConfig.libs.length === 0) {
|
|
29
|
+
console.log(chalk.red("Error: No libraries configured in config file."));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
let targetLibs;
|
|
33
|
+
if (libName) {
|
|
34
|
+
if (!currentConfig.libs.includes(libName)) {
|
|
35
|
+
console.log(chalk.red(`Error: Library '${libName}' not found in configuration.`));
|
|
36
|
+
console.log(`Available libraries: ${chalk.green(currentConfig.libs.join(" "))}`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
targetLibs = [libName];
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
targetLibs = currentConfig.libs;
|
|
43
|
+
}
|
|
44
|
+
let selectedLibs = targetLibs;
|
|
45
|
+
if (options.select) {
|
|
46
|
+
selectedLibs = yield promptForLibrarySelection(targetLibs);
|
|
47
|
+
}
|
|
48
|
+
const libDir = currentConfig.lib_directory;
|
|
49
|
+
const contextName = libName ? `library '${libName}'` : "all libraries";
|
|
50
|
+
yield runNpmInstallSequentially(selectedLibs, libDir, contextName, options);
|
|
51
|
+
}));
|
|
52
|
+
export default installCommand;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { Command } from "commander";
|
|
11
|
+
import { configExists, currentConfig, warnOfMissingConfig, } from "../../utils/config-utils.js";
|
|
12
|
+
import chalk from "chalk";
|
|
13
|
+
import { validateGitRepositories, runGitPullConcurrently, promptForLibrarySelection, } from "../../utils/command-utils.js";
|
|
14
|
+
const pullCommand = new Command("pull")
|
|
15
|
+
.description("pull latest changes from library git repositories")
|
|
16
|
+
.argument("[lib-name]", "name of the library to pull from (pulls all if not specified)")
|
|
17
|
+
.option("-s, --select", "prompt to select which libraries to pull")
|
|
18
|
+
.action((libName, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
|
+
if (!configExists) {
|
|
20
|
+
warnOfMissingConfig();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (!currentConfig.lib_directory || !currentConfig.libs) {
|
|
24
|
+
console.log(chalk.red("Error: Library configuration not found in config file."));
|
|
25
|
+
console.log(chalk.yellow("Please run 'mfer init' to configure library settings."));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (!Array.isArray(currentConfig.libs) || currentConfig.libs.length === 0) {
|
|
29
|
+
console.log(chalk.red("Error: No libraries configured in config file."));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
let targetLibs;
|
|
33
|
+
if (libName) {
|
|
34
|
+
if (!currentConfig.libs.includes(libName)) {
|
|
35
|
+
console.log(chalk.red(`Error: Library '${libName}' not found in configuration.`));
|
|
36
|
+
console.log(`Available libraries: ${chalk.green(currentConfig.libs.join(" "))}`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
targetLibs = [libName];
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
targetLibs = currentConfig.libs;
|
|
43
|
+
}
|
|
44
|
+
const libDir = currentConfig.lib_directory;
|
|
45
|
+
console.log(chalk.blue(`Validating library repositories...`));
|
|
46
|
+
const { validRepos, invalidRepos } = validateGitRepositories(targetLibs, libDir);
|
|
47
|
+
if (invalidRepos.length > 0) {
|
|
48
|
+
console.log(chalk.yellow("\nSkipping invalid repositories:"));
|
|
49
|
+
invalidRepos.forEach(({ name, reason }) => {
|
|
50
|
+
console.log(chalk.yellow(` ${name}: ${reason}`));
|
|
51
|
+
});
|
|
52
|
+
console.log();
|
|
53
|
+
}
|
|
54
|
+
if (validRepos.length === 0) {
|
|
55
|
+
console.log(chalk.red("No valid git repositories found to pull from."));
|
|
56
|
+
if (invalidRepos.some((repo) => repo.reason.includes("Directory does not exist"))) {
|
|
57
|
+
console.log(chalk.blue("\nTip: Make sure your libraries are cloned to the configured directory."));
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
let selectedLibs = validRepos;
|
|
62
|
+
if (options.select) {
|
|
63
|
+
selectedLibs = yield promptForLibrarySelection(validRepos);
|
|
64
|
+
}
|
|
65
|
+
const contextName = libName ? `library '${libName}'` : "all libraries";
|
|
66
|
+
yield runGitPullConcurrently(selectedLibs, libDir, contextName, options);
|
|
67
|
+
}));
|
|
68
|
+
export default pullCommand;
|
package/dist/commands/pull.js
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
import { Command } from "commander";
|
|
2
11
|
import { configExists, currentConfig, warnOfMissingConfig, } from "../utils/config-utils.js";
|
|
3
|
-
import concurrently from "concurrently";
|
|
4
12
|
import chalk from "chalk";
|
|
5
|
-
import
|
|
6
|
-
import fs from "fs";
|
|
7
|
-
import { spawnSync } from "child_process";
|
|
13
|
+
import { validateGitRepositories, runGitPullConcurrently, promptForMFESelection, } from "../utils/command-utils.js";
|
|
8
14
|
const pullCommand = new Command("pull")
|
|
9
15
|
.description("pull latest changes from git repositories")
|
|
10
16
|
.argument("[group_name]", "name of the group as specified in the configuration", "all")
|
|
11
|
-
.
|
|
17
|
+
.option("-s, --select", "prompt to select which repositories to pull")
|
|
18
|
+
.action((groupName, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
12
19
|
if (!configExists) {
|
|
13
20
|
warnOfMissingConfig();
|
|
14
21
|
return;
|
|
@@ -26,32 +33,8 @@ const pullCommand = new Command("pull")
|
|
|
26
33
|
return;
|
|
27
34
|
}
|
|
28
35
|
const mfeDir = currentConfig.mfe_directory;
|
|
29
|
-
const validRepos = [];
|
|
30
|
-
const invalidRepos = [];
|
|
31
36
|
console.log(chalk.blue(`Validating repositories in group: ${groupName}...`));
|
|
32
|
-
|
|
33
|
-
const repoPath = path.join(mfeDir, repo);
|
|
34
|
-
if (!fs.existsSync(repoPath)) {
|
|
35
|
-
invalidRepos.push({
|
|
36
|
-
name: repo,
|
|
37
|
-
reason: `Directory does not exist: ${repoPath}`,
|
|
38
|
-
});
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
const gitResult = spawnSync("git", ["rev-parse", "--git-dir"], {
|
|
42
|
-
cwd: repoPath,
|
|
43
|
-
stdio: "pipe",
|
|
44
|
-
shell: true,
|
|
45
|
-
});
|
|
46
|
-
if (gitResult.status !== 0) {
|
|
47
|
-
invalidRepos.push({
|
|
48
|
-
name: repo,
|
|
49
|
-
reason: `Not a git repository: ${repoPath}`,
|
|
50
|
-
});
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
validRepos.push(repo);
|
|
54
|
-
}
|
|
37
|
+
const { validRepos, invalidRepos } = validateGitRepositories(group, mfeDir);
|
|
55
38
|
if (invalidRepos.length > 0) {
|
|
56
39
|
console.log(chalk.yellow("\nSkipping invalid repositories:"));
|
|
57
40
|
invalidRepos.forEach(({ name, reason }) => {
|
|
@@ -66,44 +49,11 @@ const pullCommand = new Command("pull")
|
|
|
66
49
|
}
|
|
67
50
|
return;
|
|
68
51
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const concurrentlyResult = concurrently(commands, {
|
|
77
|
-
prefix: "{name} |",
|
|
78
|
-
killOthersOn: ["failure"],
|
|
79
|
-
restartTries: 0,
|
|
80
|
-
});
|
|
81
|
-
const handleSigint = () => {
|
|
82
|
-
console.log(chalk.yellow("\nReceived SIGINT. Stopping all git pull operations..."));
|
|
83
|
-
concurrentlyResult.commands.forEach((cmd) => {
|
|
84
|
-
if (cmd && typeof cmd.kill === "function") {
|
|
85
|
-
cmd.kill();
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
process.exit(0);
|
|
89
|
-
};
|
|
90
|
-
process.once("SIGINT", handleSigint);
|
|
91
|
-
concurrentlyResult.result.then(() => {
|
|
92
|
-
console.log(chalk.green(`\nSuccessfully pulled latest changes for all repositories in group: ${groupName}`));
|
|
93
|
-
}, (err) => {
|
|
94
|
-
console.error(chalk.red("One or more repositories failed to pull."));
|
|
95
|
-
if (Array.isArray(err)) {
|
|
96
|
-
err.forEach((fail) => {
|
|
97
|
-
var _a, _b;
|
|
98
|
-
const name = ((_a = fail.command) === null || _a === void 0 ? void 0 : _a.name) || "unknown";
|
|
99
|
-
const exitCode = fail.exitCode;
|
|
100
|
-
const cwd = ((_b = fail.command) === null || _b === void 0 ? void 0 : _b.cwd) || "unknown";
|
|
101
|
-
console.error(chalk.yellow(` Repository ${name} failed to pull (cwd: ${cwd}) with exit code ${exitCode}`));
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
else if (err && typeof err === "object" && "message" in err) {
|
|
105
|
-
console.error(err.message);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
});
|
|
52
|
+
let selectedRepos = validRepos;
|
|
53
|
+
if (options.select) {
|
|
54
|
+
selectedRepos = yield promptForMFESelection(groupName, validRepos);
|
|
55
|
+
}
|
|
56
|
+
const contextName = `group: ${groupName}`;
|
|
57
|
+
yield runGitPullConcurrently(selectedRepos, mfeDir, contextName, options);
|
|
58
|
+
}));
|
|
109
59
|
export default pullCommand;
|
package/dist/commands/run.js
CHANGED
|
@@ -12,7 +12,7 @@ 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 {
|
|
15
|
+
import { promptForMFESelection } from "../utils/command-utils.js";
|
|
16
16
|
import { spawn } from "child_process";
|
|
17
17
|
const DEFAULT_RUN_COMMAND = "npm start";
|
|
18
18
|
const runCommand = new Command("run")
|
|
@@ -20,9 +20,8 @@ const runCommand = new Command("run")
|
|
|
20
20
|
.argument("[group_name]", "name of the group as specified in the configuration", "all")
|
|
21
21
|
.option("-s, --select", "prompt to select which micro frontends to run")
|
|
22
22
|
.option("-c, --command <command>", "custom command to run (default: npm start)")
|
|
23
|
-
.option("-a, --async", "run custom command concurrently instead of sequentially")
|
|
23
|
+
.option("-a, --async", "run custom command concurrently instead of sequentially (only works with --command option)")
|
|
24
24
|
.action((groupName, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
-
var _a, _b;
|
|
26
25
|
if (!configExists) {
|
|
27
26
|
warnOfMissingConfig();
|
|
28
27
|
return;
|
|
@@ -53,23 +52,7 @@ const runCommand = new Command("run")
|
|
|
53
52
|
}
|
|
54
53
|
let selectedMFEs = group;
|
|
55
54
|
if (options.select) {
|
|
56
|
-
|
|
57
|
-
console.log(chalk.blue(`Select micro frontends to run from group '${groupName}':`));
|
|
58
|
-
selectedMFEs = yield checkbox({
|
|
59
|
-
message: "Choose which micro frontends to run:",
|
|
60
|
-
choices: group.map((mfe) => ({ name: mfe, value: mfe })),
|
|
61
|
-
validate: (arr) => arr.length > 0 ? true : "Select at least one micro frontend",
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
catch (error) {
|
|
65
|
-
if (error instanceof Error &&
|
|
66
|
-
(((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes("SIGINT")) ||
|
|
67
|
-
((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes("User force closed")))) {
|
|
68
|
-
console.log(chalk.yellow("\nReceived SIGINT. Stopping..."));
|
|
69
|
-
process.exit(130);
|
|
70
|
-
}
|
|
71
|
-
throw error;
|
|
72
|
-
}
|
|
55
|
+
selectedMFEs = yield promptForMFESelection(groupName, group);
|
|
73
56
|
}
|
|
74
57
|
const mfeDir = currentConfig.mfe_directory;
|
|
75
58
|
const commandToRun = options.command || DEFAULT_RUN_COMMAND;
|
|
@@ -80,8 +63,7 @@ const runCommand = new Command("run")
|
|
|
80
63
|
const commandText = options.command
|
|
81
64
|
? `custom command '${commandToRun}'`
|
|
82
65
|
: "default command";
|
|
83
|
-
|
|
84
|
-
console.log(chalk.green(`Running ${commandText} on micro frontends in ${groupText} ${executionMode}...`));
|
|
66
|
+
console.log(chalk.green(`Running ${commandText} on micro frontends in ${groupText}...`));
|
|
85
67
|
if (isAsync || !options.command) {
|
|
86
68
|
yield runConcurrently(selectedMFEs, commandToRun, mfeDir);
|
|
87
69
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { Command } from "commander";
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import { spawnSync } from "child_process";
|
|
13
|
+
import { getInstalledVersion, getLatestVersion, } from "../utils/version-utils.js";
|
|
14
|
+
const updateCommand = new Command("update")
|
|
15
|
+
.description("update mfer to the latest version")
|
|
16
|
+
.action(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
|
+
const installedVersion = getInstalledVersion();
|
|
18
|
+
console.log(chalk.blue(`Current version: ${installedVersion}`));
|
|
19
|
+
console.log(chalk.blue("Checking for updates..."));
|
|
20
|
+
const latestVersion = yield getLatestVersion();
|
|
21
|
+
if (!latestVersion) {
|
|
22
|
+
console.log(chalk.red("Error: Could not fetch the latest version from npm."));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (latestVersion === installedVersion) {
|
|
26
|
+
console.log(chalk.green("You are already on the latest version."));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
console.log(chalk.yellow(`New version available: ${latestVersion}`));
|
|
30
|
+
console.log(chalk.blue("Updating mfer..."));
|
|
31
|
+
const result = spawnSync("npm", ["install", "-g", "mfer@latest"], {
|
|
32
|
+
stdio: "inherit",
|
|
33
|
+
shell: true,
|
|
34
|
+
});
|
|
35
|
+
if (result.status !== 0) {
|
|
36
|
+
console.log(chalk.red(`Error: Update failed with exit code ${result.status}. Try running: npm install -g mfer@latest`));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
console.log(chalk.green(`Successfully updated mfer to ${latestVersion}.`));
|
|
40
|
+
}));
|
|
41
|
+
export default updateCommand;
|
package/dist/index.js
CHANGED
|
@@ -7,11 +7,13 @@ import installCommand from "./commands/install.js";
|
|
|
7
7
|
import cloneCommand from "./commands/clone.js";
|
|
8
8
|
import pullCommand from "./commands/pull.js";
|
|
9
9
|
import libCommand from "./commands/lib/index.js";
|
|
10
|
+
import updateCommand from "./commands/update.js";
|
|
10
11
|
import { loadConfig } from "./utils/config-utils.js";
|
|
12
|
+
import { checkForUpdateNotification } from "./utils/version-utils.js";
|
|
11
13
|
program
|
|
12
14
|
.name("mfer")
|
|
13
15
|
.description("Micro Frontend Runner (mfer) - A CLI for running your project's micro frontends.")
|
|
14
|
-
.version("3.
|
|
16
|
+
.version("3.2.4", "-v, --version", "mfer CLI version")
|
|
15
17
|
.hook("preAction", () => {
|
|
16
18
|
console.log();
|
|
17
19
|
})
|
|
@@ -25,5 +27,7 @@ program.addCommand(installCommand);
|
|
|
25
27
|
program.addCommand(cloneCommand);
|
|
26
28
|
program.addCommand(pullCommand);
|
|
27
29
|
program.addCommand(libCommand);
|
|
30
|
+
program.addCommand(updateCommand);
|
|
28
31
|
loadConfig();
|
|
29
32
|
program.parse();
|
|
33
|
+
checkForUpdateNotification().catch(() => { });
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
import { checkbox } from "@inquirer/prompts";
|
|
12
|
+
import { spawnSync } from "child_process";
|
|
13
|
+
import concurrently from "concurrently";
|
|
14
|
+
import path from "path";
|
|
15
|
+
import fs from "fs";
|
|
16
|
+
export function promptForMFESelection(groupName, mfes) {
|
|
17
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
var _a, _b;
|
|
19
|
+
try {
|
|
20
|
+
console.log(chalk.blue(`Select micro frontends to operate on from group '${groupName}':`));
|
|
21
|
+
const selectedMFEs = yield checkbox({
|
|
22
|
+
message: "Choose which micro frontends to operate on:",
|
|
23
|
+
choices: mfes.map((mfe) => ({ name: mfe, value: mfe })),
|
|
24
|
+
validate: (arr) => arr.length > 0 ? true : "Select at least one micro frontend",
|
|
25
|
+
});
|
|
26
|
+
return selectedMFEs;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (error instanceof Error &&
|
|
30
|
+
(((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes("SIGINT")) ||
|
|
31
|
+
((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes("User force closed")))) {
|
|
32
|
+
console.log(chalk.yellow("\nReceived SIGINT. Stopping..."));
|
|
33
|
+
process.exit(130);
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export function promptForLibrarySelection(libs) {
|
|
40
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
try {
|
|
43
|
+
console.log(chalk.blue("Select libraries to operate on:"));
|
|
44
|
+
const selectedLibs = yield checkbox({
|
|
45
|
+
message: "Choose which libraries to operate on:",
|
|
46
|
+
choices: libs.map((lib) => ({ name: lib, value: lib })),
|
|
47
|
+
validate: (arr) => arr.length > 0 ? true : "Select at least one library",
|
|
48
|
+
});
|
|
49
|
+
return selectedLibs;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (error instanceof Error &&
|
|
53
|
+
(((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes("SIGINT")) ||
|
|
54
|
+
((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes("User force closed")))) {
|
|
55
|
+
console.log(chalk.yellow("\nReceived SIGINT. Stopping..."));
|
|
56
|
+
process.exit(130);
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export function validateGitRepositories(repos, baseDir) {
|
|
63
|
+
const validRepos = [];
|
|
64
|
+
const invalidRepos = [];
|
|
65
|
+
for (const repo of repos) {
|
|
66
|
+
const repoPath = path.join(baseDir, repo);
|
|
67
|
+
if (!fs.existsSync(repoPath)) {
|
|
68
|
+
invalidRepos.push({
|
|
69
|
+
name: repo,
|
|
70
|
+
reason: `Directory does not exist: ${repoPath}`,
|
|
71
|
+
});
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const gitResult = spawnSync("git", ["rev-parse", "--git-dir"], {
|
|
75
|
+
cwd: repoPath,
|
|
76
|
+
stdio: "pipe",
|
|
77
|
+
shell: true,
|
|
78
|
+
});
|
|
79
|
+
if (gitResult.status !== 0) {
|
|
80
|
+
invalidRepos.push({
|
|
81
|
+
name: repo,
|
|
82
|
+
reason: `Not a git repository: ${repoPath}`,
|
|
83
|
+
});
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
validRepos.push(repo);
|
|
87
|
+
}
|
|
88
|
+
return { validRepos, invalidRepos };
|
|
89
|
+
}
|
|
90
|
+
export function runGitPullConcurrently(repos, baseDir, contextName, options) {
|
|
91
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
92
|
+
const commands = repos.map((repo) => ({
|
|
93
|
+
command: "git pull",
|
|
94
|
+
name: repo,
|
|
95
|
+
cwd: path.join(baseDir, repo),
|
|
96
|
+
prefixColor: "green",
|
|
97
|
+
}));
|
|
98
|
+
const repoText = options.select
|
|
99
|
+
? `selected repositories from ${contextName}`
|
|
100
|
+
: `${repos.length} repositories in ${contextName}`;
|
|
101
|
+
console.log(chalk.green(`Pulling latest changes for ${repoText}...`));
|
|
102
|
+
const concurrentlyResult = concurrently(commands, {
|
|
103
|
+
prefix: "{name} |",
|
|
104
|
+
killOthersOn: ["failure"],
|
|
105
|
+
restartTries: 0,
|
|
106
|
+
});
|
|
107
|
+
const handleSigint = () => {
|
|
108
|
+
console.log(chalk.yellow("\nReceived SIGINT. Stopping all git pull operations..."));
|
|
109
|
+
concurrentlyResult.commands.forEach((cmd) => {
|
|
110
|
+
if (cmd && typeof cmd.kill === "function") {
|
|
111
|
+
cmd.kill();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
process.exit(0);
|
|
115
|
+
};
|
|
116
|
+
process.once("SIGINT", handleSigint);
|
|
117
|
+
return concurrentlyResult.result.then(() => {
|
|
118
|
+
const successText = options.select
|
|
119
|
+
? `selected repositories from ${contextName}`
|
|
120
|
+
: `all repositories in ${contextName}`;
|
|
121
|
+
console.log(chalk.green(`\nSuccessfully pulled latest changes for ${successText}`));
|
|
122
|
+
}, (err) => {
|
|
123
|
+
console.error(chalk.red("One or more repositories failed to pull."));
|
|
124
|
+
if (Array.isArray(err)) {
|
|
125
|
+
err.forEach((fail) => {
|
|
126
|
+
var _a, _b;
|
|
127
|
+
const name = ((_a = fail.command) === null || _a === void 0 ? void 0 : _a.name) || "unknown";
|
|
128
|
+
const exitCode = fail.exitCode;
|
|
129
|
+
const cwd = ((_b = fail.command) === null || _b === void 0 ? void 0 : _b.cwd) || "unknown";
|
|
130
|
+
console.error(chalk.yellow(` Repository ${name} failed to pull (cwd: ${cwd}) with exit code ${exitCode}`));
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
else if (err && typeof err === "object" && "message" in err) {
|
|
134
|
+
console.error(err.message);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
export function runNpmInstallSequentially(items, baseDir, contextName, options) {
|
|
140
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
141
|
+
let hadError = false;
|
|
142
|
+
const groupText = options.select
|
|
143
|
+
? `selected items from ${contextName}`
|
|
144
|
+
: `${contextName}`;
|
|
145
|
+
console.log(chalk.green(`Running 'npm install' in ${groupText}`));
|
|
146
|
+
let interrupted = false;
|
|
147
|
+
const handleSigint = () => {
|
|
148
|
+
interrupted = true;
|
|
149
|
+
console.log(chalk.yellow("\nReceived SIGINT. Stopping installs..."));
|
|
150
|
+
};
|
|
151
|
+
process.once("SIGINT", handleSigint);
|
|
152
|
+
for (const item of items) {
|
|
153
|
+
if (interrupted)
|
|
154
|
+
break;
|
|
155
|
+
const cwd = path.join(baseDir, item);
|
|
156
|
+
console.log(chalk.blue(`[${item}] Running 'npm install' in ${cwd}`));
|
|
157
|
+
const result = spawnSync("npm", ["install", "--no-fund"], {
|
|
158
|
+
cwd,
|
|
159
|
+
stdio: "inherit",
|
|
160
|
+
shell: true,
|
|
161
|
+
});
|
|
162
|
+
if (result.status !== 0) {
|
|
163
|
+
hadError = true;
|
|
164
|
+
console.error(chalk.red(` ${item} failed to install (cwd: ${cwd}) with exit code ${result.status}`));
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
console.log(chalk.green(` ${item} installed successfully (cwd: ${cwd})\n`));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (hadError) {
|
|
171
|
+
console.error(chalk.red("One or more installs failed."));
|
|
172
|
+
process.exitCode = 1;
|
|
173
|
+
}
|
|
174
|
+
else if (interrupted) {
|
|
175
|
+
process.exitCode = 130;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
console.log(chalk.green("All installs completed successfully."));
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import * as fs from "fs";
|
|
11
|
+
import * as path from "path";
|
|
12
|
+
import * as os from "os";
|
|
13
|
+
import chalk from "chalk";
|
|
14
|
+
import { spawnSync } from "child_process";
|
|
15
|
+
const NOTIFIED_VERSION_FILE = path.join(os.homedir(), ".mfer", ".last-notified-version");
|
|
16
|
+
export function getInstalledVersion() {
|
|
17
|
+
const packageJsonPath = path.join(path.dirname(new URL(import.meta.url).pathname), "../../package.json");
|
|
18
|
+
try {
|
|
19
|
+
const raw = fs.readFileSync(packageJsonPath, "utf8");
|
|
20
|
+
return JSON.parse(raw).version;
|
|
21
|
+
}
|
|
22
|
+
catch (_a) {
|
|
23
|
+
return "unknown";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function getLatestVersion() {
|
|
27
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
28
|
+
try {
|
|
29
|
+
const result = spawnSync("npm", ["view", "mfer", "version"], {
|
|
30
|
+
stdio: "pipe",
|
|
31
|
+
shell: true,
|
|
32
|
+
timeout: 10000,
|
|
33
|
+
});
|
|
34
|
+
if (result.status !== 0 || !result.stdout) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return result.stdout.toString().trim();
|
|
38
|
+
}
|
|
39
|
+
catch (_a) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function getLastNotifiedVersion() {
|
|
45
|
+
try {
|
|
46
|
+
return fs.readFileSync(NOTIFIED_VERSION_FILE, "utf8").trim();
|
|
47
|
+
}
|
|
48
|
+
catch (_a) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function setLastNotifiedVersion(version) {
|
|
53
|
+
try {
|
|
54
|
+
const dir = path.dirname(NOTIFIED_VERSION_FILE);
|
|
55
|
+
if (!fs.existsSync(dir)) {
|
|
56
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
fs.writeFileSync(NOTIFIED_VERSION_FILE, version);
|
|
59
|
+
}
|
|
60
|
+
catch (_a) {
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export function checkForUpdateNotification() {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
try {
|
|
66
|
+
const installedVersion = getInstalledVersion();
|
|
67
|
+
if (installedVersion === "unknown")
|
|
68
|
+
return;
|
|
69
|
+
const latestVersion = yield getLatestVersion();
|
|
70
|
+
if (!latestVersion)
|
|
71
|
+
return;
|
|
72
|
+
if (latestVersion === installedVersion)
|
|
73
|
+
return;
|
|
74
|
+
const lastNotified = getLastNotifiedVersion();
|
|
75
|
+
if (lastNotified === latestVersion)
|
|
76
|
+
return;
|
|
77
|
+
console.log(chalk.yellow(`\n Update available: ${installedVersion} → ${latestVersion}`));
|
|
78
|
+
console.log(chalk.yellow(` Run ${chalk.bold("mfer update")} to update.\n`));
|
|
79
|
+
setLastNotifiedVersion(latestVersion);
|
|
80
|
+
}
|
|
81
|
+
catch (_a) {
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|