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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # mfer (Micro Frontend Runner)
2
2
 
3
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![npm version](https://img.shields.io/npm/v/mfer.svg)](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
@@ -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
- createAndSaveConfig(githubUsername, mfeDirectory, selectedFolders);
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
- createAndSaveConfig(githubUsername, mfeDirectory);
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.5.0", "-v, --version", "mfer CLI version")
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
- return (config &&
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mfer",
3
- "version": "1.5.0",
3
+ "version": "2.1.0",
4
4
  "description": "CLI tool designed to sensibly run micro-frontends from the terminal.",
5
5
  "bin": {
6
6
  "mfer": "dist/index.js"