# mfer (Micro Frontend Runner)

[![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)

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.

## 📋 Table of Contents

- [🚀 Features](#-features)
- [📦 Installation](#-installation)
- [🛠️ Quick Start](#️-quick-start)
- [📋 Commands](#-commands)
- [⚙️ Configuration](#️-configuration)
- [🎯 Use Cases](#-use-cases)
- [🔧 Advanced Usage](#-advanced-usage)
- [🛠️ Development](#️-development)
- [🐛 Troubleshooting](#-troubleshooting)
- [🤝 Contributing](#-contributing)
- [📄 License](#-license)
- [🙏 Acknowledgments](#-acknowledgments)

## 🚀 Features

- **Concurrent Execution**: Run multiple micro frontends simultaneously with organized output
- **Group Management**: Organize micro frontends into logical groups for selective execution
- **Git Integration**: Pull latest changes from all repositories with a single command
- **Smart Configuration**: Interactive setup wizard with TOML-based configuration
- **Cross-Platform**: Works on Windows, macOS, and Linux
- **Graceful Shutdown**: Clean termination of all processes with Ctrl+C

## 📦 Installation

### Prerequisites

- Node.js 18 or higher
- Git (for repository management)

### Install from npm

```bash
npm install -g mfer
```

### Install from source

```bash
git clone https://github.com/srimel/mfer.git
cd mfer
npm install
npm run build
npm install -g .
```

## 🛠️ Quick Start

### 1. Initialize Configuration

Start by setting up your mfer configuration:

```bash
mfer init
```

This interactive wizard will guide you through:

- Setting up your GitHub username
- Specifying the directory containing your micro frontends
- Selecting which projects to include in your default group

### 2. Run Your Micro Frontends

```bash
# Run all micro frontends
mfer run

# Run a specific group
mfer run frontend

# Run with a custom group name
mfer run shared
```

### 3. Update Your Repositories

```bash
# Pull latest changes from all repositories
mfer pull

# Pull from a specific group
mfer pull frontend
```

## 📋 Commands

### Quick Reference

- [`mfer init`](#mfer-init) - Interactive setup wizard
- [`mfer run`](#mfer-run-group_name) - Run micro frontend applications (supports per-MFE run modes)
- [`mfer pull`](#mfer-pull-group_name) - Pull latest changes from git repositories
- [`mfer install`](#mfer-install-group_name) - Install dependencies for micro frontends
- [`mfer clone`](#mfer-clone-group_name) - Clone repositories that don't exist locally
- [`mfer lib`](#mfer-lib) - Manage internal npm packages
- [`mfer config`](#mfer-config) - Manage configuration settings
- [`mfer help`](#mfer-help) - Display help information

### `mfer init`

Interactive setup wizard to create your configuration file.

**Options:**

- `-f, --force`: Force re-initialization even if config exists

**Example:**

```bash
mfer init --force
```

### `mfer run [group_name]`

Run micro frontend applications concurrently.

**Arguments:**

- `group_name`: Name of the group to run (defaults to "all")

**Options:**

- `-c, --command <command>`: Custom command to run for all MFEs (default: npm start)
- `-a, --async`: Run custom command concurrently instead of sequentially (only with `--command`)
- `-m, --mode <mode_name>`: Run MFEs using a named mode from config (see [Per-MFE Run Modes](#per-mfe-run-modes))
- `-s, --select`: Prompt to select which micro frontends to run
- `--config <path>`: Use a specific config file path (overrides default config location)

> **Note:** `--mode` and `--command` are mutually exclusive.

**Examples:**

```bash
mfer run                          # Run all MFEs with default command (npm start)
mfer run frontend                 # Run frontend group with default command
mfer run --mode mock              # Run all MFEs in mock mode (per-MFE commands from config)
mfer run frontend --mode mock     # Run frontend group in mock mode
mfer run --command "npm ci" home  # Run custom command sequentially on home group
mfer run -c "yarn install" shared # Run yarn install sequentially on shared group
mfer run --command "npm ci" --async home  # Run custom command concurrently on home group
mfer run --command "npm run build" --select  # Select MFEs and run build sequentially
mfer run --config ./mfer.config.toml --select  # Use project-local config and select MFEs
```

### `mfer pull [group_name]`

Pull latest changes from git repositories.

**Arguments:**

- `group_name`: Name of the group to pull from (defaults to "all")

**Options:**

- `-s, --select`: Prompt to select which repositories to pull

**Examples:**

```bash
mfer pull         # Pull from all repositories
mfer pull shared  # Pull from shared components group only
mfer pull --select # Select repositories to pull interactively
mfer pull frontend --select # Select repositories from frontend group
```

### `mfer install [group_name]`

Install dependencies for all micro frontends in a group.

**Arguments:**

- `group_name`: Name of the group to install dependencies for (defaults to "all")

**Options:**

- `-s, --select`: Prompt to select which micro frontends to install

**Examples:**

```bash
mfer install          # Install dependencies for all micro frontends
mfer install frontend # Install dependencies for frontend group only
mfer install --select # Select micro frontends to install interactively
mfer install shared --select # Select micro frontends from shared group
```

### `mfer clone [group_name]`

Clone repositories that don't exist locally.

**Arguments:**

- `group_name`: Name of the group to clone repositories from (defaults to "all")

**Example:**

```bash
mfer clone          # Clone all repositories
mfer clone shared   # Clone repositories in shared group only
```

### `mfer config`

Manage your configuration settings.

**Subcommands:**

- `mfer config list`: Display current configuration
- `mfer config edit`: Open configuration file in your default editor

**Example:**

```bash
mfer config list    # Show current configuration
mfer config edit    # Edit configuration in your editor
```

### `mfer help`

Display help information for mfer commands.

**Example:**

```bash
mfer help           # Show general help
mfer help run       # Show help for run command
mfer help config    # Show help for config command
```

### `mfer lib`

Manage internal npm packages and their distribution to micro frontends.

**Subcommands:**

- [`mfer lib build`](#mfer-lib-build-lib-name) - Build internal npm packages
- [`mfer lib deploy`](#mfer-lib-deploy-lib-name) - Copy built libraries to micro frontends
- [`mfer lib publish`](#mfer-lib-publish-lib-name) - Build and deploy libraries to micro frontends
- [`mfer lib list`](#mfer-lib-list) - List configured libraries and their status
- [`mfer lib pull`](#mfer-lib-pull-lib-name) - Pull latest changes from library git repositories
- [`mfer lib install`](#mfer-lib-install-lib-name) - Install dependencies for libraries

### `mfer lib build [lib-name]`

Build internal npm packages.

**Arguments:**

- `lib-name`: Name of the library to build (builds all if not specified)

**Options:**

- `-s, --select`: Prompt to select which libraries to build

**Examples:**

```bash
mfer lib build                    # Build all libraries
mfer lib build my-shared-utils    # Build specific library
mfer lib build --select           # Select libraries to build interactively
```

### `mfer lib deploy [lib-name]`

Copy built libraries to micro frontends.

**Arguments:**

- `lib-name`: Name of the library to deploy (deploys all if not specified)

**Options:**

- `-s, --select`: Prompt to select which libraries to deploy

**Examples:**

```bash
mfer lib deploy                    # Deploy all libraries
mfer lib deploy my-shared-utils    # Deploy specific library
mfer lib deploy --select           # Select libraries to deploy interactively
```

### `mfer lib publish [lib-name]`

Build and deploy libraries to micro frontends.

**Arguments:**

- `lib-name`: Name of the library to publish (publishes all if not specified)

**Options:**

- `-s, --select`: Prompt to select which libraries to publish

**Examples:**

```bash
mfer lib publish                    # Publish all libraries
mfer lib publish my-shared-utils    # Publish specific library
mfer lib publish --select           # Select libraries to publish interactively
```

### `mfer lib list`

List configured libraries and their status.

**Example:**

```bash
mfer lib list    # Show all configured libraries and their build status
```

### `mfer lib pull [lib-name]`

Pull latest changes from library git repositories.

**Arguments:**

- `lib-name`: Name of the library to pull from (pulls all if not specified)

**Options:**

- `-s, --select`: Prompt to select which libraries to pull

**Examples:**

```bash
mfer lib pull                    # Pull from all libraries
mfer lib pull my-shared-utils    # Pull from specific library only
mfer lib pull --select           # Select libraries to pull interactively
mfer lib pull my-design-system --select # Select libraries from specific library
```

### `mfer lib install [lib-name]`

Install dependencies for libraries.

**Arguments:**

- `lib-name`: Name of the library to install dependencies for (installs all if not specified)

**Options:**

- `-s, --select`: Prompt to select which libraries to install

**Examples:**

```bash
mfer lib install                    # Install dependencies for all libraries
mfer lib install my-shared-utils    # Install dependencies for specific library only
mfer lib install --select           # Select libraries to install interactively
mfer lib install my-design-system --select # Select libraries from specific library
```

## ⚙️ Configuration

mfer uses a TOML configuration file. Default location: `~/.mfer/config.toml`

Override options (highest precedence first):

- CLI: `mfer --config ./path/to/config.toml ...`
- Env: `MFER_CONFIG=./path/to/config.toml`
- Default: `~/.mfer/config.toml`

Here's an example structure:

```toml
base_github_url = "https://github.com/your-username"
mfe_directory = "/path/to/your/micro-frontends"
lib_directory = "/path/to/your/internal-libs"
libs = ["my-shared-utils", "my-design-system", "my-common-components"]

[groups]
all = ["my-main-app", "my-admin-panel", "my-shared-components"]
main = ["my-main-app", "my-shared-components"]
admin = ["my-admin-panel", "my-shared-components"]

# optional, per-MFE configuration
[[mfes.my-main-app.modes]]
mode_name = "mock"
command = "npm run start:mocked"

# optional lifecycle hooks for `mfer run`
[hooks]
pre_run = "echo preparing run for $MFER_GROUP_NAME"
post_run = "echo completed run for $MFER_GROUP_NAME"
```

> **Migrating from v3.x?** v4.0.0 is a breaking change: YAML config is no longer supported. Run `mfer config migrate` to convert your existing `~/.mfer/config.yaml` to TOML automatically.

### Configuration Options

- **`base_github_url`**: Your GitHub base URL for repository operations
- **`mfe_directory`**: Path to the directory containing all your micro frontend projects
- **`lib_directory`**: Path to the directory containing your internal npm packages (optional)
- **`libs`**: List of internal npm package names to manage (optional)
- **`groups`**: Named collections of micro frontend projects
  - **`all`**: Default group containing all projects (required)
  - **Custom groups**: Any additional groups you want to create
- **`mfes`**: Per-MFE configuration (optional). Each key is an MFE name matching one in `groups`.
  - **`modes`**: Array of named run modes for the MFE. Each mode has:
    - **`mode_name`**: Name used with `mfer run --mode <name>`
    - **`command`**: The command to run for this MFE when the mode is active
- **`hooks`**: Optional run lifecycle hooks
  - **`pre_run`**: Command executed before `mfer run` starts MFE processes
  - **`post_run`**: Command executed after `mfer run` finishes successfully

### Per-MFE Run Modes

Run modes let individual MFEs use a different start command while the rest of the group continue using `npm start`. This is useful when some MFEs support a mocked/stubbed backend while others don't need it.

**Config example** — `root-config` runs with a mocked API, everything else runs normally:

```toml
[groups]
all = ["root-config", "mfe-nav", "mfe-dashboard"]

[[mfes.root-config.modes]]
mode_name = "mock"
command = "npm run start:mocked"
```

**Usage:**

```bash
mfer run --mode mock
# root-config → npm run start:mocked
# mfe-nav     → npm start  (no mock mode defined, falls back to default)
# mfe-dashboard → npm start
```

- MFEs that **do not** have the mode defined silently fall back to `npm start`.
- If **no MFE** in the group has the requested mode, a warning is printed but the command still runs (all MFEs use `npm start`).
- `--mode` cannot be combined with `--command`; they are mutually exclusive.

### Run Lifecycle Hooks

When configured, `pre_run` and `post_run` hooks run in the configured `mfe_directory`.

Environment variables provided to hooks:

- `MFER_HOOK_NAME` (`pre_run` or `post_run`)
- `MFER_GROUP_NAME` (resolved group name)
- `MFER_MFE_DIRECTORY` (configured mfe root directory)
- `MFER_SELECTED_MFES` (comma-separated selected MFEs)

### Editing Configuration

You can edit your configuration in several ways:

1. **Interactive editor** (recommended):

   ```bash
   mfer config edit
   ```

2. **Direct file editing**:

   ```bash
   # On macOS/Linux
   nano ~/.mfer/config.toml

   # On Windows
   notepad %USERPROFILE%\.mfer\config.toml
   ```

3. **Migrate a legacy YAML config**:

   ```bash
   mfer config migrate
   ```

   Converts `~/.mfer/config.yaml` → `~/.mfer/config.toml`. Prompts before overwriting an existing TOML file and optionally removes the legacy YAML afterward.

## 🎯 Use Cases

### Development Workflow

```bash
# Start your day
mfer pull          # Get latest changes
mfer run main      # Start main application

# Later, switch to admin panel work
mfer run admin     # Start admin panel
```

### Project Organization

Organize your micro frontends into logical groups:

```toml
[groups]
all = ["main-app", "admin-panel", "user-dashboard", "shared-components", "design-system"]
core = ["main-app", "shared-components", "design-system"]
admin = ["admin-panel", "user-dashboard", "shared-components"]
ui = ["shared-components", "design-system"]
```

### Team Collaboration

- Share configuration files with your team
- Standardize development environment setup
- Ensure everyone runs the same services

## 🔧 Advanced Usage

### Custom Start Commands

By default, mfer runs `npm start` in each project directory. You can customize this in two ways:

- **Same command for all MFEs**: use `--command` (e.g. `mfer run --command "yarn start"`)
- **Per-MFE commands via modes**: define `mfes[name].modes` in config and use `--mode` (see [Per-MFE Run Modes](#per-mfe-run-modes))

### Environment Variables

mfer respects your existing environment setup and will use the same Node.js and npm versions you have configured.

### Process Management

- All processes are managed concurrently with organized output
- Use Ctrl+C to gracefully shut down all running services
- Failed processes are reported with detailed error information

## 🛠️ Development

### Local Development Setup

For contributing to mfer or setting up a local development environment:

```bash
git clone https://github.com/srimel/mfer.git
cd mfer
npm install
npm run build
npm install -g .
```

### Testing with Playground

The project includes a comprehensive playground for testing mfer commands with sample micro frontends. The playground contains:

- **libs/common-utils**: A shared utility library
- **frontends/mfe1, mfe2, mfe3**: Three React micro frontends with different themes
- Complete setup instructions and testing scenarios

📖 **[View detailed local development guide →](./docs/local-development.md)**

## 🐛 Troubleshooting

### Common Issues

**"No configuration file detected"**

```bash
# Run the initialization wizard
mfer init
```

**"Group not found"**

```bash
# Check available groups
mfer config list

# Edit configuration to add missing group
mfer config edit
```

**"Directory does not exist"**

- Ensure the `mfe_directory` path in your configuration is correct
- Use absolute paths for better reliability
- Check that the directory exists and is accessible

**"Not a git repository"**

- Ensure all projects in your configuration are valid git repositories
- Run `mfer clone` to clone missing repositories

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🙏 Acknowledgments

Built with:

- [Commander.js](https://github.com/tj/commander.js) - CLI framework
- [Inquirer](https://github.com/SBoudrias/Inquirer.js) - Interactive prompts
- [Concurrently](https://github.com/open-cli-tools/concurrently) - Process management
- [Chalk](https://github.com/chalk/chalk) - Terminal styling
- [smol-toml](https://github.com/squirrelchat/smol-toml) - Configuration parsing
- [YAML](https://github.com/eemeli/yaml) - Legacy config parsing for `mfer config migrate`
