UNPKG

@nx/module-federation

Version:

The Nx Plugin for Module Federation contains executors and utilities that support building applications using Module Federation.

141 lines (140 loc) • 7.71 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getBuildTargetNameFromMFDevServer = getBuildTargetNameFromMFDevServer; exports.getRemotes = getRemotes; exports.getModuleFederationConfig = getModuleFederationConfig; const devkit_1 = require("@nx/devkit"); const internal_1 = require("@nx/js/src/internal"); const find_matching_projects_1 = require("nx/src/utils/find-matching-projects"); const pc = require("picocolors"); const path_1 = require("path"); const fs_1 = require("fs"); function extractRemoteProjectsFromConfig(config, pathToManifestFile) { const remotes = []; const dynamicRemotes = []; if (pathToManifestFile && (0, fs_1.existsSync)(pathToManifestFile)) { const moduleFederationManifestJson = (0, fs_1.readFileSync)(pathToManifestFile, 'utf-8'); if (moduleFederationManifestJson) { // This should have shape of // { // "remoteName": "remoteLocation", // } const parsedManifest = JSON.parse(moduleFederationManifestJson); if (Object.keys(parsedManifest).every((key) => typeof key === 'string' && typeof parsedManifest[key] === 'string')) { dynamicRemotes.push(...Object.keys(parsedManifest)); } } } const staticRemotes = config.remotes?.map((r) => (Array.isArray(r) ? r[0] : r)) ?? []; remotes.push(...staticRemotes); return { remotes, dynamicRemotes }; } // Find the target that uses the module-federation-dev-server executor function getBuildTargetNameFromMFDevServer(projectConfig, projectGraph) { if (projectConfig.targets) { for (const [targetKey, targetConfig] of Object.entries(projectConfig.targets)) { const executor = targetConfig.executor || ''; // Extract the portion after the `:` in the executor name const executorParts = executor.split(':'); const executorName = executorParts.length > 1 ? executorParts[1] : executor; if (executorName === 'module-federation-dev-server') { // Extract the buildTarget from the options if (targetConfig.options?.buildTarget) { const parsedTarget = (0, devkit_1.parseTargetString)(targetConfig.options.buildTarget, projectGraph); return parsedTarget.target; } } } } return 'build'; } function collectRemoteProjects(remote, collected, context) { const remoteProject = context.projectGraph.nodes[remote]?.data; if (!context.projectGraph.nodes[remote] || collected.has(remote)) { return; } collected.add(remote); const remoteProjectRoot = remoteProject.root; const buildTargetName = getBuildTargetNameFromMFDevServer(remoteProject, context.projectGraph); let remoteProjectTsConfig = remoteProject.targets?.[buildTargetName]?.options?.tsConfig; const remoteProjectConfig = getModuleFederationConfig(remoteProjectTsConfig, context.root, remoteProjectRoot); const { remotes: remoteProjectRemotes } = extractRemoteProjectsFromConfig(remoteProjectConfig); remoteProjectRemotes.forEach((r) => collectRemoteProjects(r, collected, context)); } function getRemotes(devRemotes, skipRemotes, config, context, pathToManifestFile) { const collectedRemotes = new Set(); const { remotes, dynamicRemotes } = extractRemoteProjectsFromConfig(config, pathToManifestFile); remotes.forEach((r) => collectRemoteProjects(r, collectedRemotes, context)); const remotesToSkip = new Set((0, find_matching_projects_1.findMatchingProjects)(skipRemotes, context.projectGraph.nodes) ?? []); if (remotesToSkip.size > 0) { devkit_1.logger.info(`Remotes not served automatically: ${[...remotesToSkip.values()].join(', ')}`); } const knownRemotes = Array.from(collectedRemotes).filter((r) => !remotesToSkip.has(r)); // With dynamic remotes, the manifest file may contain the names with `_` due to MF limitations on naming // The project graph might contain these names with `-` rather than `_`. Check for both. // This can occur after migration of existing remotes past Nx 19.8 let normalizedDynamicRemotes = dynamicRemotes.map((r) => { if (context.projectGraph.nodes[r.replace(/_/g, '-')]) { return r.replace(/_/g, '-'); } return r; }); const knownDynamicRemotes = normalizedDynamicRemotes.filter((r) => !remotesToSkip.has(r) && context.projectGraph.nodes[r]); devkit_1.logger.info(`NX Starting module federation dev-server for ${pc.bold(context.projectName)} with ${[...knownRemotes, ...knownDynamicRemotes].length} remotes`); const devServeApps = new Set(!devRemotes ? [] : Array.isArray(devRemotes) ? (0, find_matching_projects_1.findMatchingProjects)(devRemotes, context.projectGraph.nodes) : (0, find_matching_projects_1.findMatchingProjects)([devRemotes], context.projectGraph.nodes)); const staticRemotes = knownRemotes.filter((r) => !devServeApps.has(r)); const devServeRemotes = [...knownRemotes, ...knownDynamicRemotes].filter((r) => devServeApps.has(r)); const staticDynamicRemotes = knownDynamicRemotes.filter((r) => !devServeApps.has(r)); const remotePorts = [...devServeRemotes, ...staticDynamicRemotes].map((r) => context.projectGraph.nodes[r].data.targets['serve'].options.port); const staticRemotePort = staticRemotes.length === 0 && remotePorts.length === 0 ? undefined : Math.max(...[ ...remotePorts, ...staticRemotes.map((r) => context.projectGraph.nodes[r].data.targets['serve'].options.port), ]) + (remotesToSkip.size + 1); return { staticRemotes, devRemotes: devServeRemotes, dynamicRemotes: staticDynamicRemotes, remotePorts, staticRemotePort, }; } function getModuleFederationConfig(tsconfigPath, workspaceRoot, projectRoot, pluginName = 'react') { const moduleFederationConfigPathJS = (0, path_1.join)(workspaceRoot, projectRoot, 'module-federation.config.js'); const moduleFederationConfigPathTS = (0, path_1.join)(workspaceRoot, projectRoot, 'module-federation.config.ts'); let moduleFederationConfigPath = moduleFederationConfigPathJS; tsconfigPath = tsconfigPath ?? [ (0, path_1.join)(projectRoot, 'tsconfig.app.json'), (0, path_1.join)(projectRoot, 'tsconfig.json'), (0, path_1.join)(workspaceRoot, 'tsconfig.json'), (0, path_1.join)(workspaceRoot, 'tsconfig.base.json'), ].find((p) => (0, fs_1.existsSync)(p)); if (!tsconfigPath) { throw new Error(`Could not find a tsconfig for remote project located at ${projectRoot}. Please add a tsconfig.app.json or tsconfig.json to the project.`); } // create a no-op so this can be called with issue const fullTSconfigPath = tsconfigPath.startsWith(workspaceRoot) ? tsconfigPath : (0, path_1.join)(workspaceRoot, tsconfigPath); let cleanupTranspiler = () => { }; if ((0, fs_1.existsSync)(moduleFederationConfigPathTS)) { cleanupTranspiler = (0, internal_1.registerTsProject)(fullTSconfigPath); moduleFederationConfigPath = moduleFederationConfigPathTS; } try { const config = require(moduleFederationConfigPath); cleanupTranspiler(); return config.default || config; } catch { throw new Error(`Could not load ${moduleFederationConfigPath}. Was this project generated with "@nx/${pluginName}:host"?\nSee: https://nx.dev/concepts/more-concepts/faster-builds-with-module-federation`); } }