UNPKG

create-mfe-kit

Version:

CLI to scaffold Micro Frontend apps using Module Federation

131 lines (115 loc) 3.8 kB
import { ask } from "./util/cliPrompts.js"; import fs from "fs-extra"; import path from "path"; import ora from "ora"; import chalk from "chalk"; import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const TEMPLATE_BASE = path.resolve(__dirname, "../templates"); const CONTAINER_WEBPACK_PATH = path.resolve( process.cwd(), "container/webpack.config.js" ); /** * creates a new mfe app based on the template * * Prompts the port number, copies the template file, updates * `package.json`,`webpack.config.js`, and modifies the container config * * @async * @function * @param {string} appName - The name of the remote app to create. * @returns{Promise<void>} * * @example * createNewRemoteApp("app2") */ export async function createNewRemoteApp(appName) { const { appPort } = await ask({ type: "number", name: "appPort", message: "Port for this app:", validate: (port) => (port > 0 ? true : "Enter a valid port number"), }); const spinner = ora(`Creating new remote app ${appName}`).start(); const targetDir = path.resolve(process.cwd(), appName); const templateAppDir = path.resolve( TEMPLATE_BASE, "../templates/app-template/app1" ); try { await fs.copy(templateAppDir, targetDir); const pkgPath = path.join(targetDir, "package.json"); const pkg = await fs.readJson(pkgPath); pkg.name = appName; if (pkg.scripts?.start) { pkg.scripts.start = pkg.scripts.start.replace( /--port\s+\d+/, `--port ${appPort}` ); } await fs.writeJson(pkgPath, pkg, { spaces: 2 }); const webpackPath = path.join(targetDir, "webpack.config.js"); let webpackContent = await fs.readFile(webpackPath, "utf-8"); webpackContent = webpackContent .replace(/port:\s*\d+/, `port: ${appPort}`) .replace(/name:\s*["']app1["']/, `name: "${appName}"`); await fs.writeFile(webpackPath, webpackContent); await updateContainerRemotes(appName, appPort); spinner.succeed( `Created new remote: ${chalk.magenta(appName)} on port ${chalk.yellow( appPort )}` ); console.log(chalk.cyan(`\n To run your app:`)); console.log(` cd ${appName} >> npm install >> npm start`); } catch (err) { spinner.fail("Failed to create remote app"); console.error(err.message); } } /** * updates the webpacks configuration to include the new remote * * @async * @function * @param {string} appName - The name of the remote app * @param {number} appPort - The port number on which the remote app runs * @returns {Promise<void>} * * @example * updateContainerRemotes("app2",3002) */ async function updateContainerRemotes(appName, appPort) { try { let content = await fs.readFile(CONTAINER_WEBPACK_PATH, "utf-8"); const remotesRegex = /remotes\s*:\s*{([\s\S]*?)}/; const newRemote = ` ${appName}: "${appName}@http://localhost:${appPort}/remoteEntry.js",`; if (!remotesRegex.test(content)) { console.log( chalk.red("Could not find remotes block in container webpack config.") ); return; } content = content.replace(remotesRegex, (match, inside) => { if (inside.includes(`${appName}:`)) { console.log( chalk.yellow(`Remote "${appName}" already exists in container.`) ); return match; } const updated = `${inside.trimEnd()}\n${newRemote}\n`; return `remotes: {\n${updated}}`; }); await fs.writeFile(CONTAINER_WEBPACK_PATH, content); console.log( chalk.green(` Updated container to include remote "${appName}"`) ); } catch (err) { console.log( chalk.red("Failed to update container webpack config:"), err.message ); } }