mfer 1.0.1 → 1.0.2

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
@@ -4,6 +4,29 @@
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
 
7
+ ## 📋 Table of Contents
8
+
9
+ - [🚀 Features](#-features)
10
+ - [📦 Installation](#-installation)
11
+ - [🛠️ Quick Start](#️-quick-start)
12
+ - [📋 Commands](#-commands)
13
+ - [mfer init](#mfer-init)
14
+ - [mfer run](#mfer-run)
15
+ - [mfer pull](#mfer-pull)
16
+ - [mfer install](#mfer-install)
17
+ - [mfer clone](#mfer-clone)
18
+ - [mfer config](#mfer-config)
19
+ - [mfer config list](#mfer-config-list)
20
+ - [mfer config edit](#mfer-config-edit)
21
+ - [mfer help](#mfer-help)
22
+ - [⚙️ Configuration](#️-configuration)
23
+ - [🎯 Use Cases](#-use-cases)
24
+ - [🔧 Advanced Usage](#-advanced-usage)
25
+ - [🐛 Troubleshooting](#-troubleshooting)
26
+ - [🤝 Contributing](#-contributing)
27
+ - [📄 License](#-license)
28
+ - [🙏 Acknowledgments](#-acknowledgments)
29
+
7
30
  ## 🚀 Features
8
31
 
9
32
  - **Concurrent Execution**: Run multiple micro frontends simultaneously with organized output
@@ -105,18 +128,52 @@ mfer pull # Pull from all repositories
105
128
  mfer pull shared # Pull from shared components group only
106
129
  ```
107
130
 
131
+ ### `mfer install [group_name]`
132
+ Install dependencies for all micro frontends in a group.
133
+
134
+ **Arguments:**
135
+ - `group_name`: Name of the group to install dependencies for (defaults to "all")
136
+
137
+ **Example:**
138
+ ```bash
139
+ mfer install # Install dependencies for all micro frontends
140
+ mfer install frontend # Install dependencies for frontend group only
141
+ ```
142
+
143
+ ### `mfer clone [group_name]`
144
+ Clone repositories that don't exist locally.
145
+
146
+ **Arguments:**
147
+ - `group_name`: Name of the group to clone repositories from (defaults to "all")
148
+
149
+ **Example:**
150
+ ```bash
151
+ mfer clone # Clone all repositories
152
+ mfer clone shared # Clone repositories in shared group only
153
+ ```
154
+
108
155
  ### `mfer config`
109
156
  Manage your configuration settings.
110
157
 
111
158
  **Subcommands:**
112
- - `mfer config edit`: Open configuration file in your default editor
113
159
  - `mfer config list`: Display current configuration
160
+ - `mfer config edit`: Open configuration file in your default editor
114
161
 
115
- ### `mfer install`
116
- Install dependencies for all micro frontends in a group.
162
+ **Example:**
163
+ ```bash
164
+ mfer config list # Show current configuration
165
+ mfer config edit # Edit configuration in your editor
166
+ ```
117
167
 
118
- ### `mfer clone`
119
- Clone repositories that don't exist locally.
168
+ ### `mfer help`
169
+ Display help information for mfer commands.
170
+
171
+ **Example:**
172
+ ```bash
173
+ mfer help # Show general help
174
+ mfer help run # Show help for run command
175
+ mfer help config # Show help for config command
176
+ ```
120
177
 
121
178
  ## ⚙️ Configuration
122
179
 
@@ -257,6 +314,8 @@ npm run build
257
314
  npm install -g .
258
315
  ```
259
316
 
317
+ Refer to [local development](./docs/local-development.md) docs for more information.
318
+
260
319
  ## 🤝 Contributing
261
320
 
262
321
  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.
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { loadConfig } from "./utils/config-utils.js";
10
10
  program
11
11
  .name("mfer")
12
12
  .description("Micro Frontend Runner (mfer) - A CLI for running your project's micro frontends.")
13
- .version("1.0.1", "-v, --version", "mfer CLI version")
13
+ .version("1.0.2", "-v, --version", "mfer CLI version")
14
14
  .hook("preAction", (thisCommand, actionCommand) => {
15
15
  console.log();
16
16
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mfer",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "CLI tool designed to sensibly run micro-frontends from the terminal.",
5
5
  "bin": {
6
6
  "mfer": "dist/index.js"
@@ -1 +0,0 @@
1
- export {};
@@ -1,16 +0,0 @@
1
- import { Command } from "commander";
2
- import { currentConfig } from "../../utils/config-utils.js";
3
- const configCommand = new Command("config")
4
- .description("prism configuration settings")
5
- .option("-l, --list", "list the current configuration")
6
- .action((options) => {
7
- const { list, path } = options;
8
- if (list) {
9
- console.log(`\nCurrent configuration:\n`);
10
- console.log(currentConfig);
11
- console.log("");
12
- }
13
- if (path) {
14
- }
15
- });
16
- export default configCommand;
@@ -1,8 +0,0 @@
1
- import { Command } from "commander";
2
- import { currentConfig } from "../../../utils/config-utils.js";
3
- export const listConfigCommand = new Command("list")
4
- .description("display the current configuration settings")
5
- .action(() => {
6
- console.log("current configuration!");
7
- console.log(currentConfig);
8
- });
@@ -1,16 +0,0 @@
1
- import { Command } from "commander";
2
- import { currentConfig } from "../utils/config-utils.js";
3
- const configCommand = new Command("config")
4
- .description("prism configuration settings")
5
- .option("-l, --list", "list the current configuration")
6
- .action((options) => {
7
- const { list, path } = options;
8
- if (list) {
9
- console.log(`\nCurrent configuration:\n`);
10
- console.log(currentConfig);
11
- console.log("");
12
- }
13
- if (path) {
14
- }
15
- });
16
- export default configCommand;
@@ -1,250 +0,0 @@
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 { describe, it, expect, vi, beforeEach } from 'vitest';
11
- import * as fs from 'fs';
12
- import * as path from 'path';
13
- import * as os from 'os';
14
- import { spawn } from 'child_process';
15
- import YAML from 'yaml';
16
- vi.mock('fs');
17
- vi.mock('path');
18
- vi.mock('os');
19
- vi.mock('child_process');
20
- vi.mock('yaml');
21
- vi.mock('chalk', () => {
22
- const mockChalk = {
23
- red: vi.fn((text) => text),
24
- blue: { bold: vi.fn((text) => text) },
25
- green: vi.fn((text) => text)
26
- };
27
- return Object.assign({ default: mockChalk }, mockChalk);
28
- });
29
- describe('config-utils', () => {
30
- const mockFs = vi.mocked(fs);
31
- const mockPath = vi.mocked(path);
32
- const mockOs = vi.mocked(os);
33
- const mockSpawn = vi.mocked(spawn);
34
- const mockYaml = vi.mocked(YAML);
35
- beforeEach(() => {
36
- vi.clearAllMocks();
37
- mockOs.homedir.mockReturnValue('/mock/home');
38
- mockPath.join.mockReturnValue('/mock/home/.mfer/config.yaml');
39
- mockPath.dirname.mockReturnValue('/mock/home/.mfer');
40
- });
41
- describe('loadConfig', () => {
42
- it('should load config when file exists', () => __awaiter(void 0, void 0, void 0, function* () {
43
- const mockConfig = {
44
- base_github_url: 'https://github.com',
45
- mfe_directory: '/path/to/mfe',
46
- groups: {
47
- all: ['app1', 'app2']
48
- }
49
- };
50
- mockFs.existsSync.mockReturnValue(true);
51
- mockFs.readFileSync.mockReturnValue('mock yaml content');
52
- mockYaml.parse.mockReturnValue(mockConfig);
53
- const { loadConfig } = yield import('../config-utils.js');
54
- loadConfig();
55
- expect(mockFs.readFileSync).toHaveBeenCalledWith('/mock/home/.mfer/config.yaml', 'utf8');
56
- expect(mockYaml.parse).toHaveBeenCalledWith('mock yaml content');
57
- }));
58
- it('should not load config when file does not exist', () => __awaiter(void 0, void 0, void 0, function* () {
59
- mockFs.existsSync.mockReturnValue(false);
60
- const { loadConfig } = yield import('../config-utils.js');
61
- loadConfig();
62
- expect(loadConfig).toBeDefined();
63
- }));
64
- });
65
- describe('warnOfMissingConfig', () => {
66
- it('should display warning when config does not exist', () => __awaiter(void 0, void 0, void 0, function* () {
67
- mockFs.existsSync.mockReturnValue(false);
68
- const { warnOfMissingConfig } = yield import('../config-utils.js');
69
- warnOfMissingConfig();
70
- expect(warnOfMissingConfig).toBeDefined();
71
- }));
72
- it('should not display warning when config exists', () => __awaiter(void 0, void 0, void 0, function* () {
73
- mockFs.existsSync.mockReturnValue(true);
74
- const { warnOfMissingConfig } = yield import('../config-utils.js');
75
- warnOfMissingConfig();
76
- expect(warnOfMissingConfig).toBeDefined();
77
- }));
78
- });
79
- describe('isConfigValid', () => {
80
- it('should return false when config file does not exist', () => __awaiter(void 0, void 0, void 0, function* () {
81
- mockFs.existsSync.mockReturnValue(false);
82
- const { isConfigValid } = yield import('../config-utils.js');
83
- const result = isConfigValid();
84
- expect(typeof result).toBe('boolean');
85
- }));
86
- it('should return false when YAML parsing fails', () => __awaiter(void 0, void 0, void 0, function* () {
87
- mockFs.existsSync.mockReturnValue(true);
88
- mockFs.readFileSync.mockReturnValue('invalid yaml');
89
- mockYaml.parse.mockImplementation(() => {
90
- throw new Error('Invalid YAML');
91
- });
92
- const { isConfigValid } = yield import('../config-utils.js');
93
- const result = isConfigValid();
94
- expect(result).toBe(false);
95
- }));
96
- it('should return false when config is missing required fields', () => __awaiter(void 0, void 0, void 0, function* () {
97
- const invalidConfigs = [
98
- null,
99
- undefined,
100
- {},
101
- { base_github_url: 'https://github.com' },
102
- { base_github_url: 'https://github.com', mfe_directory: '/path' },
103
- { base_github_url: 'https://github.com', mfe_directory: '/path', groups: {} },
104
- { base_github_url: 'https://github.com', mfe_directory: '/path', groups: { all: [] } }
105
- ];
106
- mockFs.existsSync.mockReturnValue(true);
107
- mockFs.readFileSync.mockReturnValue('mock yaml content');
108
- const { isConfigValid } = yield import('../config-utils.js');
109
- for (const config of invalidConfigs) {
110
- mockYaml.parse.mockReturnValue(config);
111
- const result = isConfigValid();
112
- expect(result).toBeFalsy();
113
- mockYaml.parse.mockClear();
114
- }
115
- }));
116
- it('should return true when config has all required fields', () => __awaiter(void 0, void 0, void 0, function* () {
117
- const validConfig = {
118
- base_github_url: 'https://github.com',
119
- mfe_directory: '/path/to/mfe',
120
- groups: {
121
- all: ['app1', 'app2'],
122
- frontend: ['app1'],
123
- backend: ['app2']
124
- }
125
- };
126
- mockFs.existsSync.mockReturnValue(true);
127
- mockFs.readFileSync.mockReturnValue('mock yaml content');
128
- mockYaml.parse.mockReturnValue(validConfig);
129
- const { isConfigValid } = yield import('../config-utils.js');
130
- const result = isConfigValid();
131
- expect(result).toBe(true);
132
- }));
133
- });
134
- describe('saveConfig', () => {
135
- it('should save config successfully', () => __awaiter(void 0, void 0, void 0, function* () {
136
- const mockConfig = {
137
- base_github_url: 'https://github.com',
138
- mfe_directory: '/path/to/mfe',
139
- groups: {
140
- all: ['app1', 'app2']
141
- }
142
- };
143
- mockFs.existsSync.mockReturnValue(false);
144
- mockYaml.stringify.mockReturnValue('mock yaml string');
145
- const { saveConfig } = yield import('../config-utils.js');
146
- saveConfig(mockConfig);
147
- expect(mockPath.dirname).toHaveBeenCalledWith('/mock/home/.mfer/config.yaml');
148
- expect(mockFs.mkdirSync).toHaveBeenCalledWith('/mock/home/.mfer', { recursive: true });
149
- expect(mockYaml.stringify).toHaveBeenCalledWith(mockConfig);
150
- expect(mockFs.writeFileSync).toHaveBeenCalledWith('/mock/home/.mfer/config.yaml', 'mock yaml string');
151
- }));
152
- it('should create directory if it does not exist', () => __awaiter(void 0, void 0, void 0, function* () {
153
- const mockConfig = {
154
- base_github_url: 'https://github.com',
155
- mfe_directory: '/path/to/mfe',
156
- groups: {
157
- all: ['app1', 'app2']
158
- }
159
- };
160
- mockFs.existsSync.mockReturnValue(false);
161
- mockYaml.stringify.mockReturnValue('mock yaml string');
162
- const { saveConfig } = yield import('../config-utils.js');
163
- saveConfig(mockConfig);
164
- expect(mockFs.mkdirSync).toHaveBeenCalledWith('/mock/home/.mfer', { recursive: true });
165
- }));
166
- it('should handle errors when saving config', () => __awaiter(void 0, void 0, void 0, function* () {
167
- const mockConfig = {
168
- base_github_url: 'https://github.com',
169
- mfe_directory: '/path/to/mfe',
170
- groups: {
171
- all: ['app1', 'app2']
172
- }
173
- };
174
- const mockError = new Error('Write error');
175
- mockFs.writeFileSync.mockImplementation(() => {
176
- throw mockError;
177
- });
178
- const { saveConfig } = yield import('../config-utils.js');
179
- saveConfig(mockConfig);
180
- expect(saveConfig).toBeDefined();
181
- }));
182
- });
183
- describe('editConfig', () => {
184
- it('should open config file with default editor on Windows', () => __awaiter(void 0, void 0, void 0, function* () {
185
- mockOs.platform.mockReturnValue('win32');
186
- const mockProcess = { env: {} };
187
- vi.stubGlobal('process', mockProcess);
188
- const mockSpawnInstance = {
189
- unref: vi.fn()
190
- };
191
- mockSpawn.mockReturnValue(mockSpawnInstance);
192
- const { editConfig } = yield import('../config-utils.js');
193
- editConfig();
194
- expect(mockSpawn).toHaveBeenCalledWith('notepad', ['/mock/home/.mfer/config.yaml'], {
195
- stdio: 'ignore',
196
- detached: true,
197
- shell: true
198
- });
199
- expect(mockSpawnInstance.unref).toHaveBeenCalled();
200
- }));
201
- it('should use EDITOR environment variable when available', () => __awaiter(void 0, void 0, void 0, function* () {
202
- mockOs.platform.mockReturnValue('linux');
203
- const mockProcess = { env: { EDITOR: 'vim' } };
204
- vi.stubGlobal('process', mockProcess);
205
- const mockSpawnInstance = {
206
- unref: vi.fn()
207
- };
208
- mockSpawn.mockReturnValue(mockSpawnInstance);
209
- const { editConfig } = yield import('../config-utils.js');
210
- editConfig();
211
- expect(mockSpawn).toHaveBeenCalledWith('vim', ['/mock/home/.mfer/config.yaml'], {
212
- stdio: 'ignore',
213
- detached: true,
214
- shell: true
215
- });
216
- }));
217
- it('should use VISUAL environment variable when EDITOR is not available', () => __awaiter(void 0, void 0, void 0, function* () {
218
- mockOs.platform.mockReturnValue('linux');
219
- const mockProcess = { env: { VISUAL: 'code' } };
220
- vi.stubGlobal('process', mockProcess);
221
- const mockSpawnInstance = {
222
- unref: vi.fn()
223
- };
224
- mockSpawn.mockReturnValue(mockSpawnInstance);
225
- const { editConfig } = yield import('../config-utils.js');
226
- editConfig();
227
- expect(mockSpawn).toHaveBeenCalledWith('code', ['/mock/home/.mfer/config.yaml'], {
228
- stdio: 'ignore',
229
- detached: true,
230
- shell: true
231
- });
232
- }));
233
- it('should use vi as fallback on non-Windows platforms', () => __awaiter(void 0, void 0, void 0, function* () {
234
- mockOs.platform.mockReturnValue('linux');
235
- const mockProcess = { env: {} };
236
- vi.stubGlobal('process', mockProcess);
237
- const mockSpawnInstance = {
238
- unref: vi.fn()
239
- };
240
- mockSpawn.mockReturnValue(mockSpawnInstance);
241
- const { editConfig } = yield import('../config-utils.js');
242
- editConfig();
243
- expect(mockSpawn).toHaveBeenCalledWith('vi', ['/mock/home/.mfer/config.yaml'], {
244
- stdio: 'ignore',
245
- detached: true,
246
- shell: true
247
- });
248
- }));
249
- });
250
- });