mfer 1.5.0 → 2.1.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 +90 -1
- package/dist/commands/init.js +83 -3
- package/dist/commands/lib/build.js +112 -0
- package/dist/commands/lib/deploy.js +140 -0
- package/dist/commands/lib/index.js +12 -0
- package/dist/commands/lib/list.js +57 -0
- package/dist/commands/lib/publish.js +173 -0
- package/dist/index.js +3 -1
- package/dist/utils/config-utils.js +6 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# mfer (Micro Frontend Runner)
|
|
2
2
|
|
|
3
|
-
[](https://opensource.org/licenses/MIT)
|
|
3
|
+
[](https://opensource.org/licenses/MIT) [](https://www.npmjs.com/package/mfer)
|
|
4
4
|
|
|
5
5
|
A powerful CLI tool designed to simplify the management and execution of multiple micro frontend applications. mfer helps developers run, update, and organize their micro frontend projects with minimal configuration and maximum efficiency.
|
|
6
6
|
|
|
@@ -98,6 +98,7 @@ mfer pull frontend
|
|
|
98
98
|
- [`mfer pull`](#mfer-pull-group_name) - Pull latest changes from git repositories
|
|
99
99
|
- [`mfer install`](#mfer-install-group_name) - Install dependencies for micro frontends
|
|
100
100
|
- [`mfer clone`](#mfer-clone-group_name) - Clone repositories that don't exist locally
|
|
101
|
+
- [`mfer lib`](#mfer-lib) - Manage internal npm packages
|
|
101
102
|
- [`mfer config`](#mfer-config) - Manage configuration settings
|
|
102
103
|
- [`mfer help`](#mfer-help) - Display help information
|
|
103
104
|
|
|
@@ -214,6 +215,87 @@ mfer help run # Show help for run command
|
|
|
214
215
|
mfer help config # Show help for config command
|
|
215
216
|
```
|
|
216
217
|
|
|
218
|
+
### `mfer lib`
|
|
219
|
+
|
|
220
|
+
Manage internal npm packages and their distribution to micro frontends.
|
|
221
|
+
|
|
222
|
+
**Subcommands:**
|
|
223
|
+
|
|
224
|
+
- [`mfer lib build`](#mfer-lib-build-lib-name) - Build internal npm packages
|
|
225
|
+
- [`mfer lib deploy`](#mfer-lib-deploy-lib-name) - Copy built libraries to micro frontends
|
|
226
|
+
- [`mfer lib publish`](#mfer-lib-publish-lib-name) - Build and deploy libraries to micro frontends
|
|
227
|
+
- [`mfer lib list`](#mfer-lib-list) - List configured libraries and their status
|
|
228
|
+
|
|
229
|
+
### `mfer lib build [lib-name]`
|
|
230
|
+
|
|
231
|
+
Build internal npm packages.
|
|
232
|
+
|
|
233
|
+
**Arguments:**
|
|
234
|
+
|
|
235
|
+
- `lib-name`: Name of the library to build (builds all if not specified)
|
|
236
|
+
|
|
237
|
+
**Options:**
|
|
238
|
+
|
|
239
|
+
- `-s, --select`: Prompt to select which libraries to build
|
|
240
|
+
|
|
241
|
+
**Examples:**
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
mfer lib build # Build all libraries
|
|
245
|
+
mfer lib build my-shared-utils # Build specific library
|
|
246
|
+
mfer lib build --select # Select libraries to build interactively
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### `mfer lib deploy [lib-name]`
|
|
250
|
+
|
|
251
|
+
Copy built libraries to micro frontends.
|
|
252
|
+
|
|
253
|
+
**Arguments:**
|
|
254
|
+
|
|
255
|
+
- `lib-name`: Name of the library to deploy (deploys all if not specified)
|
|
256
|
+
|
|
257
|
+
**Options:**
|
|
258
|
+
|
|
259
|
+
- `-s, --select`: Prompt to select which libraries to deploy
|
|
260
|
+
|
|
261
|
+
**Examples:**
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
mfer lib deploy # Deploy all libraries
|
|
265
|
+
mfer lib deploy my-shared-utils # Deploy specific library
|
|
266
|
+
mfer lib deploy --select # Select libraries to deploy interactively
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### `mfer lib publish [lib-name]`
|
|
270
|
+
|
|
271
|
+
Build and deploy libraries to micro frontends.
|
|
272
|
+
|
|
273
|
+
**Arguments:**
|
|
274
|
+
|
|
275
|
+
- `lib-name`: Name of the library to publish (publishes all if not specified)
|
|
276
|
+
|
|
277
|
+
**Options:**
|
|
278
|
+
|
|
279
|
+
- `-s, --select`: Prompt to select which libraries to publish
|
|
280
|
+
|
|
281
|
+
**Examples:**
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
mfer lib publish # Publish all libraries
|
|
285
|
+
mfer lib publish my-shared-utils # Publish specific library
|
|
286
|
+
mfer lib publish --select # Select libraries to publish interactively
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### `mfer lib list`
|
|
290
|
+
|
|
291
|
+
List configured libraries and their status.
|
|
292
|
+
|
|
293
|
+
**Example:**
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
mfer lib list # Show all configured libraries and their build status
|
|
297
|
+
```
|
|
298
|
+
|
|
217
299
|
## ⚙️ Configuration
|
|
218
300
|
|
|
219
301
|
mfer uses a YAML configuration file located at `~/.mfer/config.yaml`. Here's an example structure:
|
|
@@ -221,6 +303,11 @@ mfer uses a YAML configuration file located at `~/.mfer/config.yaml`. Here's an
|
|
|
221
303
|
```yaml
|
|
222
304
|
base_github_url: "https://github.com/your-username"
|
|
223
305
|
mfe_directory: "/path/to/your/micro-frontends"
|
|
306
|
+
lib_directory: "/path/to/your/internal-libs"
|
|
307
|
+
libs:
|
|
308
|
+
- my-shared-utils
|
|
309
|
+
- my-design-system
|
|
310
|
+
- my-common-components
|
|
224
311
|
groups:
|
|
225
312
|
all:
|
|
226
313
|
- my-main-app
|
|
@@ -238,6 +325,8 @@ groups:
|
|
|
238
325
|
|
|
239
326
|
- **`base_github_url`**: Your GitHub base URL for repository operations
|
|
240
327
|
- **`mfe_directory`**: Path to the directory containing all your micro frontend projects
|
|
328
|
+
- **`lib_directory`**: Path to the directory containing your internal npm packages (optional)
|
|
329
|
+
- **`libs`**: List of internal npm package names to manage (optional)
|
|
241
330
|
- **`groups`**: Named collections of micro frontend projects
|
|
242
331
|
- **`all`**: Default group containing all projects (required)
|
|
243
332
|
- **Custom groups**: Any additional groups you want to create
|
package/dist/commands/init.js
CHANGED
|
@@ -12,7 +12,7 @@ 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
|
-
function createAndSaveConfig(githubUsername, mfeDirectory, allGroup = []) {
|
|
15
|
+
function createAndSaveConfig(githubUsername, mfeDirectory, allGroup = [], libDirectory, libs = []) {
|
|
16
16
|
const repositories = allGroup.length > 0 ? allGroup : ["my_mfe_1", "my_mfe_2"];
|
|
17
17
|
const newConfig = {
|
|
18
18
|
base_github_url: `https://github.com/${githubUsername}`,
|
|
@@ -21,6 +21,10 @@ function createAndSaveConfig(githubUsername, mfeDirectory, allGroup = []) {
|
|
|
21
21
|
all: repositories,
|
|
22
22
|
},
|
|
23
23
|
};
|
|
24
|
+
if (libDirectory && libs.length > 0) {
|
|
25
|
+
newConfig.lib_directory = libDirectory;
|
|
26
|
+
newConfig.libs = libs;
|
|
27
|
+
}
|
|
24
28
|
saveConfig(newConfig);
|
|
25
29
|
const successMessage = allGroup.length > 0
|
|
26
30
|
? "Configuration created successfully!"
|
|
@@ -32,6 +36,10 @@ function createAndSaveConfig(githubUsername, mfeDirectory, allGroup = []) {
|
|
|
32
36
|
console.log(chalk.yellow("\nNote: Placeholder repository names have been added to show proper YAML syntax."));
|
|
33
37
|
console.log(chalk.yellow("Please replace 'my_mfe_1' and 'my_mfe_2' with your actual repository names."));
|
|
34
38
|
}
|
|
39
|
+
if (libDirectory && libs.length > 0) {
|
|
40
|
+
console.log(chalk.green(`\nLibrary configuration added: ${libs.length} librar${libs.length === 1 ? "y" : "ies"} configured.`));
|
|
41
|
+
console.log(chalk.yellow("You can now use 'mfer lib build', 'mfer lib deploy', and 'mfer lib publish' commands."));
|
|
42
|
+
}
|
|
35
43
|
}
|
|
36
44
|
function getFoldersFromDirectory(directoryPath) {
|
|
37
45
|
try {
|
|
@@ -98,6 +106,44 @@ function promptForFolderSelection(folders) {
|
|
|
98
106
|
}
|
|
99
107
|
});
|
|
100
108
|
}
|
|
109
|
+
function promptForLibDirectory() {
|
|
110
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
111
|
+
try {
|
|
112
|
+
const useLibs = yield confirm({
|
|
113
|
+
message: "Do you have internal npm packages/libraries that need to be built and deployed to your micro frontends?",
|
|
114
|
+
default: false,
|
|
115
|
+
});
|
|
116
|
+
if (!useLibs) {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
return yield input({
|
|
120
|
+
message: [
|
|
121
|
+
"Enter the path to the folder containing your internal npm packages/libraries.",
|
|
122
|
+
" (Tip: Drag a folder from your file explorer into this terminal to paste its path)",
|
|
123
|
+
" >>>",
|
|
124
|
+
].join("\n"),
|
|
125
|
+
validate: (val) => val && val.trim() !== "" ? true : "Folder path cannot be empty",
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function promptForLibSelection(libs) {
|
|
134
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
try {
|
|
136
|
+
return yield checkbox({
|
|
137
|
+
message: "Select which libraries to include in the configuration:",
|
|
138
|
+
choices: libs.map((lib) => ({ name: lib, value: lib })),
|
|
139
|
+
validate: (arr) => arr.length > 0 ? true : "Select at least one library",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
101
147
|
const initCommand = new Command("init")
|
|
102
148
|
.description("setup a new configuration")
|
|
103
149
|
.option("-f, --force", "force re-initialization even if config exists and is valid")
|
|
@@ -138,11 +184,45 @@ const initCommand = new Command("init")
|
|
|
138
184
|
if (interrupted) {
|
|
139
185
|
return;
|
|
140
186
|
}
|
|
141
|
-
|
|
187
|
+
const libDirectory = yield promptForLibDirectory();
|
|
188
|
+
if (interrupted) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
let libs = [];
|
|
192
|
+
if (libDirectory) {
|
|
193
|
+
const availableLibs = getFoldersFromDirectory(libDirectory);
|
|
194
|
+
if (availableLibs.length > 0) {
|
|
195
|
+
libs = yield promptForLibSelection(availableLibs);
|
|
196
|
+
if (interrupted) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
console.log(chalk.yellow("No library directories found in the specified path."));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
createAndSaveConfig(githubUsername, mfeDirectory, selectedFolders, libDirectory, libs);
|
|
142
205
|
}
|
|
143
206
|
else {
|
|
144
207
|
console.log("Add the names of your micro frontends to the 'groups' section.");
|
|
145
|
-
|
|
208
|
+
const libDirectory = yield promptForLibDirectory();
|
|
209
|
+
if (interrupted) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
let libs = [];
|
|
213
|
+
if (libDirectory) {
|
|
214
|
+
const availableLibs = getFoldersFromDirectory(libDirectory);
|
|
215
|
+
if (availableLibs.length > 0) {
|
|
216
|
+
libs = yield promptForLibSelection(availableLibs);
|
|
217
|
+
if (interrupted) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
console.log(chalk.yellow("No library directories found in the specified path."));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
createAndSaveConfig(githubUsername, mfeDirectory, [], libDirectory, libs);
|
|
146
226
|
}
|
|
147
227
|
}
|
|
148
228
|
catch (error) {
|
|
@@ -0,0 +1,112 @@
|
|
|
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 { spawn } from "child_process";
|
|
13
|
+
import * as fs from "fs";
|
|
14
|
+
import * as path from "path";
|
|
15
|
+
import { checkbox } from "@inquirer/prompts";
|
|
16
|
+
import { currentConfig, configExists, warnOfMissingConfig, } from "../../utils/config-utils.js";
|
|
17
|
+
const buildCommand = new Command("build")
|
|
18
|
+
.description("Build internal npm packages")
|
|
19
|
+
.argument("[lib-name]", "Name of the library to build (builds all if not specified)")
|
|
20
|
+
.option("-s, --select", "prompt to select which libraries to build")
|
|
21
|
+
.action((libName, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
|
+
var _a, _b;
|
|
23
|
+
if (!configExists) {
|
|
24
|
+
warnOfMissingConfig();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (!currentConfig.lib_directory || !currentConfig.libs) {
|
|
28
|
+
console.log(chalk.red("Error: Library configuration not found in config file."));
|
|
29
|
+
console.log(chalk.yellow("Please run 'mfer init' to configure library settings."));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
let libsToBuild;
|
|
33
|
+
if (options === null || options === void 0 ? void 0 : options.select) {
|
|
34
|
+
try {
|
|
35
|
+
console.log(chalk.blue(`Select libraries to build:`));
|
|
36
|
+
const selectedLibs = yield checkbox({
|
|
37
|
+
message: "Choose which libraries to build:",
|
|
38
|
+
choices: currentConfig.libs.map((lib) => ({ name: lib, value: lib })),
|
|
39
|
+
validate: (arr) => arr.length > 0 ? true : "Select at least one library",
|
|
40
|
+
});
|
|
41
|
+
libsToBuild = selectedLibs;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (error instanceof Error &&
|
|
45
|
+
(((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes("SIGINT")) ||
|
|
46
|
+
((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes("User force closed")))) {
|
|
47
|
+
console.log(chalk.yellow("\nReceived SIGINT. Stopping..."));
|
|
48
|
+
process.exit(130);
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
libsToBuild = libName
|
|
55
|
+
? [libName].filter((lib) => currentConfig.libs.includes(lib))
|
|
56
|
+
: currentConfig.libs;
|
|
57
|
+
if (libName && !currentConfig.libs.includes(libName)) {
|
|
58
|
+
console.log(chalk.red(`Error: Library '${libName}' not found in configuration.`));
|
|
59
|
+
console.log(chalk.yellow(`Available libraries: ${currentConfig.libs.join(", ")}`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (libsToBuild.length === 0) {
|
|
64
|
+
console.log(chalk.yellow("No libraries to build."));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const libsText = (options === null || options === void 0 ? void 0 : options.select)
|
|
68
|
+
? `selected libraries (${libsToBuild.length})`
|
|
69
|
+
: libName
|
|
70
|
+
? `library '${libName}'`
|
|
71
|
+
: `all libraries (${libsToBuild.length})`;
|
|
72
|
+
console.log(chalk.blue(`Building ${libsText}...`));
|
|
73
|
+
for (const lib of libsToBuild) {
|
|
74
|
+
const libPath = path.join(currentConfig.lib_directory, lib);
|
|
75
|
+
if (!fs.existsSync(libPath)) {
|
|
76
|
+
console.log(chalk.red(`Error: Library directory not found: ${libPath}`));
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
console.log(chalk.blue(`\nBuilding ${chalk.bold(lib)}...`));
|
|
80
|
+
try {
|
|
81
|
+
yield buildLibrary(libPath, lib);
|
|
82
|
+
console.log(chalk.green(`✓ ${lib} built successfully`));
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
console.log(chalk.red(`✗ Failed to build ${lib}: ${error}`));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
console.log(chalk.green("\nBuild process completed!"));
|
|
89
|
+
}));
|
|
90
|
+
function buildLibrary(libPath, _libName) {
|
|
91
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
const buildProcess = spawn("npm", ["run", "build"], {
|
|
94
|
+
stdio: "inherit",
|
|
95
|
+
cwd: libPath,
|
|
96
|
+
shell: true,
|
|
97
|
+
});
|
|
98
|
+
buildProcess.on("close", (code) => {
|
|
99
|
+
if (code === 0) {
|
|
100
|
+
resolve();
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
reject(new Error(`Build process exited with code ${code}`));
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
buildProcess.on("error", (error) => {
|
|
107
|
+
reject(error);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
export default buildCommand;
|
|
@@ -0,0 +1,140 @@
|
|
|
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 * as fs from "fs";
|
|
13
|
+
import * as path from "path";
|
|
14
|
+
import { checkbox } from "@inquirer/prompts";
|
|
15
|
+
import { currentConfig, configExists, warnOfMissingConfig, } from "../../utils/config-utils.js";
|
|
16
|
+
const deployCommand = new Command("deploy")
|
|
17
|
+
.description("Copy built libraries to micro frontends")
|
|
18
|
+
.argument("[lib-name]", "Name of the library to deploy (deploys all if not specified)")
|
|
19
|
+
.option("-s, --select", "prompt to select which libraries to deploy")
|
|
20
|
+
.action((libName, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
21
|
+
var _a, _b;
|
|
22
|
+
if (!configExists) {
|
|
23
|
+
warnOfMissingConfig();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (!currentConfig.lib_directory || !currentConfig.libs) {
|
|
27
|
+
console.log(chalk.red("Error: Library configuration not found in config file."));
|
|
28
|
+
console.log(chalk.yellow("Please run 'mfer init' to configure library settings."));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
let libsToDeploy;
|
|
32
|
+
if (options === null || options === void 0 ? void 0 : options.select) {
|
|
33
|
+
try {
|
|
34
|
+
console.log(chalk.blue(`Select libraries to deploy:`));
|
|
35
|
+
const selectedLibs = yield checkbox({
|
|
36
|
+
message: "Choose which libraries to deploy:",
|
|
37
|
+
choices: currentConfig.libs.map((lib) => ({ name: lib, value: lib })),
|
|
38
|
+
validate: (arr) => arr.length > 0 ? true : "Select at least one library",
|
|
39
|
+
});
|
|
40
|
+
libsToDeploy = selectedLibs;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (error instanceof Error &&
|
|
44
|
+
(((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes("SIGINT")) ||
|
|
45
|
+
((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes("User force closed")))) {
|
|
46
|
+
console.log(chalk.yellow("\nReceived SIGINT. Stopping..."));
|
|
47
|
+
process.exit(130);
|
|
48
|
+
}
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
libsToDeploy = libName
|
|
54
|
+
? [libName].filter((lib) => currentConfig.libs.includes(lib))
|
|
55
|
+
: currentConfig.libs;
|
|
56
|
+
if (libName && !currentConfig.libs.includes(libName)) {
|
|
57
|
+
console.log(chalk.red(`Error: Library '${libName}' not found in configuration.`));
|
|
58
|
+
console.log(chalk.yellow(`Available libraries: ${currentConfig.libs.join(", ")}`));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (libsToDeploy.length === 0) {
|
|
63
|
+
console.log(chalk.yellow("No libraries to deploy."));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const libsText = (options === null || options === void 0 ? void 0 : options.select)
|
|
67
|
+
? `selected libraries (${libsToDeploy.length})`
|
|
68
|
+
: libName
|
|
69
|
+
? `library '${libName}'`
|
|
70
|
+
: `all libraries (${libsToDeploy.length})`;
|
|
71
|
+
console.log(chalk.blue(`Deploying ${libsText}...`));
|
|
72
|
+
const mfeDirectories = currentConfig.groups.all.map((mfe) => path.join(currentConfig.mfe_directory, mfe));
|
|
73
|
+
for (const lib of libsToDeploy) {
|
|
74
|
+
const libDistPath = path.join(currentConfig.lib_directory, lib, "dist");
|
|
75
|
+
if (!fs.existsSync(libDistPath)) {
|
|
76
|
+
console.log(chalk.red(`Error: Build directory not found for ${lib}: ${libDistPath}`));
|
|
77
|
+
console.log(chalk.yellow(`Please run 'mfer lib build ${lib}' first.`));
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
console.log(chalk.blue(`\nDeploying ${chalk.bold(lib)}...`));
|
|
81
|
+
let deployedCount = 0;
|
|
82
|
+
for (const mfeDir of mfeDirectories) {
|
|
83
|
+
if (!fs.existsSync(mfeDir)) {
|
|
84
|
+
console.log(chalk.yellow(`Warning: MFE directory not found: ${mfeDir}`));
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const targetPath = path.join(mfeDir, "node_modules", lib);
|
|
88
|
+
if (fs.existsSync(targetPath)) {
|
|
89
|
+
try {
|
|
90
|
+
yield copyLibraryToMfe(libDistPath, targetPath, lib);
|
|
91
|
+
console.log(chalk.green(` ✓ Deployed to ${path.basename(mfeDir)}`));
|
|
92
|
+
deployedCount++;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.log(chalk.red(` ✗ Failed to deploy to ${path.basename(mfeDir)}: ${error}`));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
console.log(chalk.gray(` - Skipped ${path.basename(mfeDir)} (not installed)`));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (deployedCount > 0) {
|
|
103
|
+
console.log(chalk.green(`✓ ${lib} deployed to ${deployedCount} MFE${deployedCount === 1 ? "" : "s"}`));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
console.log(chalk.yellow(`⚠ ${lib} not deployed (not found in any MFE's node_modules)`));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
console.log(chalk.green("\nDeploy process completed!"));
|
|
110
|
+
}));
|
|
111
|
+
function copyLibraryToMfe(sourcePath, targetPath, _libName) {
|
|
112
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
+
return new Promise((resolve, _reject) => {
|
|
114
|
+
if (fs.existsSync(targetPath)) {
|
|
115
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
116
|
+
}
|
|
117
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
118
|
+
copyDirectoryRecursive(sourcePath, targetPath);
|
|
119
|
+
resolve();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function copyDirectoryRecursive(source, target) {
|
|
124
|
+
if (!fs.existsSync(target)) {
|
|
125
|
+
fs.mkdirSync(target, { recursive: true });
|
|
126
|
+
}
|
|
127
|
+
const items = fs.readdirSync(source);
|
|
128
|
+
for (const item of items) {
|
|
129
|
+
const sourcePath = path.join(source, item);
|
|
130
|
+
const targetPath = path.join(target, item);
|
|
131
|
+
const stat = fs.statSync(sourcePath);
|
|
132
|
+
if (stat.isDirectory()) {
|
|
133
|
+
copyDirectoryRecursive(sourcePath, targetPath);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export default deployCommand;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import buildCommand from "./build.js";
|
|
3
|
+
import deployCommand from "./deploy.js";
|
|
4
|
+
import publishCommand from "./publish.js";
|
|
5
|
+
import listCommand from "./list.js";
|
|
6
|
+
const libCommand = new Command("lib")
|
|
7
|
+
.description("Manage internal npm packages and their distribution to micro frontends")
|
|
8
|
+
.addCommand(buildCommand)
|
|
9
|
+
.addCommand(deployCommand)
|
|
10
|
+
.addCommand(publishCommand)
|
|
11
|
+
.addCommand(listCommand);
|
|
12
|
+
export default libCommand;
|
|
@@ -0,0 +1,57 @@
|
|
|
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 * as fs from "fs";
|
|
13
|
+
import * as path from "path";
|
|
14
|
+
import { currentConfig, configExists, warnOfMissingConfig, } from "../../utils/config-utils.js";
|
|
15
|
+
const listCommand = new Command("list")
|
|
16
|
+
.description("List configured libraries and their status")
|
|
17
|
+
.action(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
18
|
+
if (!configExists) {
|
|
19
|
+
warnOfMissingConfig();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (!currentConfig.lib_directory || !currentConfig.libs) {
|
|
23
|
+
console.log(chalk.red("Error: Library configuration not found in config file."));
|
|
24
|
+
console.log(chalk.yellow("Please run 'mfer init' to configure library settings."));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
console.log(chalk.blue("Configured Libraries:"));
|
|
28
|
+
console.log(chalk.gray("─".repeat(50)));
|
|
29
|
+
if (currentConfig.libs.length === 0) {
|
|
30
|
+
console.log(chalk.yellow("No libraries configured."));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
for (const lib of currentConfig.libs) {
|
|
34
|
+
const libPath = path.join(currentConfig.lib_directory, lib);
|
|
35
|
+
const distPath = path.join(libPath, "dist");
|
|
36
|
+
const libExists = fs.existsSync(libPath);
|
|
37
|
+
const distExists = fs.existsSync(distPath);
|
|
38
|
+
let status = "";
|
|
39
|
+
if (!libExists) {
|
|
40
|
+
status = chalk.red("✗ Directory not found");
|
|
41
|
+
}
|
|
42
|
+
else if (!distExists) {
|
|
43
|
+
status = chalk.yellow("⚠ Not built");
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
status = chalk.green("✓ Built");
|
|
47
|
+
}
|
|
48
|
+
console.log(`${chalk.bold(lib)}`);
|
|
49
|
+
console.log(` Path: ${libPath}`);
|
|
50
|
+
console.log(` Status: ${status}`);
|
|
51
|
+
console.log("");
|
|
52
|
+
}
|
|
53
|
+
console.log(chalk.gray("─".repeat(50)));
|
|
54
|
+
console.log(chalk.blue(`Library Directory: ${currentConfig.lib_directory}`));
|
|
55
|
+
console.log(chalk.blue(`Total Libraries: ${currentConfig.libs.length}`));
|
|
56
|
+
}));
|
|
57
|
+
export default listCommand;
|
|
@@ -0,0 +1,173 @@
|
|
|
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 { spawn } from "child_process";
|
|
13
|
+
import * as fs from "fs";
|
|
14
|
+
import * as path from "path";
|
|
15
|
+
import { checkbox } from "@inquirer/prompts";
|
|
16
|
+
import { currentConfig, configExists, warnOfMissingConfig, } from "../../utils/config-utils.js";
|
|
17
|
+
const publishCommand = new Command("publish")
|
|
18
|
+
.description("Build and deploy libraries to micro frontends")
|
|
19
|
+
.argument("[lib-name]", "Name of the library to publish (publishes all if not specified)")
|
|
20
|
+
.option("-s, --select", "prompt to select which libraries to publish")
|
|
21
|
+
.action((libName, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
|
+
var _a, _b;
|
|
23
|
+
if (!configExists) {
|
|
24
|
+
warnOfMissingConfig();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (!currentConfig.lib_directory || !currentConfig.libs) {
|
|
28
|
+
console.log(chalk.red("Error: Library configuration not found in config file."));
|
|
29
|
+
console.log(chalk.yellow("Please run 'mfer init' to configure library settings."));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
let libsToPublish;
|
|
33
|
+
if (options === null || options === void 0 ? void 0 : options.select) {
|
|
34
|
+
try {
|
|
35
|
+
console.log(chalk.blue(`Select libraries to publish:`));
|
|
36
|
+
const selectedLibs = yield checkbox({
|
|
37
|
+
message: "Choose which libraries to publish:",
|
|
38
|
+
choices: currentConfig.libs.map((lib) => ({ name: lib, value: lib })),
|
|
39
|
+
validate: (arr) => arr.length > 0 ? true : "Select at least one library",
|
|
40
|
+
});
|
|
41
|
+
libsToPublish = selectedLibs;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (error instanceof Error &&
|
|
45
|
+
(((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes("SIGINT")) ||
|
|
46
|
+
((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes("User force closed")))) {
|
|
47
|
+
console.log(chalk.yellow("\nReceived SIGINT. Stopping..."));
|
|
48
|
+
process.exit(130);
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
libsToPublish = libName
|
|
55
|
+
? [libName].filter((lib) => currentConfig.libs.includes(lib))
|
|
56
|
+
: currentConfig.libs;
|
|
57
|
+
if (libName && !currentConfig.libs.includes(libName)) {
|
|
58
|
+
console.log(chalk.red(`Error: Library '${libName}' not found in configuration.`));
|
|
59
|
+
console.log(chalk.yellow(`Available libraries: ${currentConfig.libs.join(", ")}`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (libsToPublish.length === 0) {
|
|
64
|
+
console.log(chalk.yellow("No libraries to publish."));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const libsText = (options === null || options === void 0 ? void 0 : options.select)
|
|
68
|
+
? `selected libraries (${libsToPublish.length})`
|
|
69
|
+
: libName
|
|
70
|
+
? `library '${libName}'`
|
|
71
|
+
: `all libraries (${libsToPublish.length})`;
|
|
72
|
+
console.log(chalk.blue(`Publishing ${libsText}...`));
|
|
73
|
+
const mfeDirectories = currentConfig.groups.all.map((mfe) => path.join(currentConfig.mfe_directory, mfe));
|
|
74
|
+
for (const lib of libsToPublish) {
|
|
75
|
+
const libPath = path.join(currentConfig.lib_directory, lib);
|
|
76
|
+
if (!fs.existsSync(libPath)) {
|
|
77
|
+
console.log(chalk.red(`Error: Library directory not found: ${libPath}`));
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
console.log(chalk.blue(`\nPublishing ${chalk.bold(lib)}...`));
|
|
81
|
+
console.log(chalk.blue(` Building ${lib}...`));
|
|
82
|
+
try {
|
|
83
|
+
yield buildLibrary(libPath, lib);
|
|
84
|
+
console.log(chalk.green(` ✓ ${lib} built successfully`));
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
console.log(chalk.red(` ✗ Failed to build ${lib}: ${error}`));
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
console.log(chalk.blue(` Deploying ${lib}...`));
|
|
91
|
+
const libDistPath = path.join(libPath, "dist");
|
|
92
|
+
let deployedCount = 0;
|
|
93
|
+
for (const mfeDir of mfeDirectories) {
|
|
94
|
+
if (!fs.existsSync(mfeDir)) {
|
|
95
|
+
console.log(chalk.yellow(` Warning: MFE directory not found: ${mfeDir}`));
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const targetPath = path.join(mfeDir, "node_modules", lib);
|
|
99
|
+
if (fs.existsSync(targetPath)) {
|
|
100
|
+
try {
|
|
101
|
+
yield copyLibraryToMfe(libDistPath, targetPath, lib);
|
|
102
|
+
console.log(chalk.green(` ✓ Deployed to ${path.basename(mfeDir)}`));
|
|
103
|
+
deployedCount++;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.log(chalk.red(` ✗ Failed to deploy to ${path.basename(mfeDir)}: ${error}`));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
console.log(chalk.gray(` - Skipped ${path.basename(mfeDir)} (not installed)`));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (deployedCount > 0) {
|
|
114
|
+
console.log(chalk.green(` ✓ ${lib} published to ${deployedCount} MFE${deployedCount === 1 ? "" : "s"}`));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
console.log(chalk.yellow(` ⚠ ${lib} not deployed (not found in any MFE's node_modules)`));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
console.log(chalk.green("\nPublish process completed!"));
|
|
121
|
+
}));
|
|
122
|
+
function buildLibrary(libPath, _libName) {
|
|
123
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
const buildProcess = spawn("npm", ["run", "build"], {
|
|
126
|
+
stdio: "inherit",
|
|
127
|
+
cwd: libPath,
|
|
128
|
+
shell: true,
|
|
129
|
+
});
|
|
130
|
+
buildProcess.on("close", (code) => {
|
|
131
|
+
if (code === 0) {
|
|
132
|
+
resolve();
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
reject(new Error(`Build process exited with code ${code}`));
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
buildProcess.on("error", (error) => {
|
|
139
|
+
reject(error);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
function copyLibraryToMfe(sourcePath, targetPath, _libName) {
|
|
145
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
146
|
+
return new Promise((resolve, _reject) => {
|
|
147
|
+
if (fs.existsSync(targetPath)) {
|
|
148
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
149
|
+
}
|
|
150
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
151
|
+
copyDirectoryRecursive(sourcePath, targetPath);
|
|
152
|
+
resolve();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function copyDirectoryRecursive(source, target) {
|
|
157
|
+
if (!fs.existsSync(target)) {
|
|
158
|
+
fs.mkdirSync(target, { recursive: true });
|
|
159
|
+
}
|
|
160
|
+
const items = fs.readdirSync(source);
|
|
161
|
+
for (const item of items) {
|
|
162
|
+
const sourcePath = path.join(source, item);
|
|
163
|
+
const targetPath = path.join(target, item);
|
|
164
|
+
const stat = fs.statSync(sourcePath);
|
|
165
|
+
if (stat.isDirectory()) {
|
|
166
|
+
copyDirectoryRecursive(sourcePath, targetPath);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export default publishCommand;
|
package/dist/index.js
CHANGED
|
@@ -6,11 +6,12 @@ import runCommand from "./commands/run.js";
|
|
|
6
6
|
import installCommand from "./commands/install.js";
|
|
7
7
|
import cloneCommand from "./commands/clone.js";
|
|
8
8
|
import pullCommand from "./commands/pull.js";
|
|
9
|
+
import libCommand from "./commands/lib/index.js";
|
|
9
10
|
import { loadConfig } from "./utils/config-utils.js";
|
|
10
11
|
program
|
|
11
12
|
.name("mfer")
|
|
12
13
|
.description("Micro Frontend Runner (mfer) - A CLI for running your project's micro frontends.")
|
|
13
|
-
.version("1.
|
|
14
|
+
.version("2.1.0", "-v, --version", "mfer CLI version")
|
|
14
15
|
.hook("preAction", () => {
|
|
15
16
|
console.log();
|
|
16
17
|
})
|
|
@@ -23,5 +24,6 @@ program.addCommand(runCommand);
|
|
|
23
24
|
program.addCommand(installCommand);
|
|
24
25
|
program.addCommand(cloneCommand);
|
|
25
26
|
program.addCommand(pullCommand);
|
|
27
|
+
program.addCommand(libCommand);
|
|
26
28
|
loadConfig();
|
|
27
29
|
program.parse();
|
|
@@ -27,7 +27,7 @@ export const isConfigValid = () => {
|
|
|
27
27
|
try {
|
|
28
28
|
const configFile = fs.readFileSync(configPath, "utf8");
|
|
29
29
|
const config = YAML.parse(configFile);
|
|
30
|
-
|
|
30
|
+
const hasRequiredFields = config &&
|
|
31
31
|
typeof config === "object" &&
|
|
32
32
|
config.base_github_url &&
|
|
33
33
|
config.mfe_directory &&
|
|
@@ -35,7 +35,11 @@ export const isConfigValid = () => {
|
|
|
35
35
|
typeof config.groups === "object" &&
|
|
36
36
|
config.groups.all &&
|
|
37
37
|
Array.isArray(config.groups.all) &&
|
|
38
|
-
config.groups.all.length > 0
|
|
38
|
+
config.groups.all.length > 0;
|
|
39
|
+
if (config.lib_directory && (!config.libs || !Array.isArray(config.libs))) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return hasRequiredFields;
|
|
39
43
|
}
|
|
40
44
|
catch (_a) {
|
|
41
45
|
return false;
|