diff --git a/src/index.ts b/src/index.ts index c825991..e3f61ab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,7 @@ import { normalizeModuleFederationOptions, } from './utils/normalizeModuleFederationOptions'; import normalizeOptimizeDepsPlugin from './utils/normalizeOptimizeDeps'; +import VirtualModule from './utils/VirtualModule'; import { getHostAutoInitImportId, getHostAutoInitPath, @@ -24,11 +25,21 @@ import { VIRTUAL_EXPOSES } from './virtualModules/virtualExposes'; function federation(mfUserOptions: ModuleFederationOptions): Plugin[] { const options = normalizeModuleFederationOptions(mfUserOptions); - initVirtualModules(); const { name, remotes, shared, filename } = options; if (!name) throw new Error('name is required'); return [ + { + name: 'vite:module-federation-config', + enforce: 'pre', + configResolved(config) { + // Set root path + VirtualModule.setRoot(config.root); + // Ensure virtual package directory exists + VirtualModule.ensureVirtualPackageExists(); + initVirtualModules(); + }, + }, aliasToArrayPlugin, normalizeOptimizeDepsPlugin, ...addEntry({ diff --git a/src/utils/VirtualModule.ts b/src/utils/VirtualModule.ts index 6609e64..90f6123 100644 --- a/src/utils/VirtualModule.ts +++ b/src/utils/VirtualModule.ts @@ -3,8 +3,11 @@ import { dirname, join, parse, resolve } from 'pathe'; import { packageNameDecode, packageNameEncode } from '../utils/packageNameUtils'; import { getNormalizeModuleFederationOptions } from './normalizeModuleFederationOptions'; -const nodeModulesDir = (function findNodeModulesDir(startDir = process.cwd()) { - let currentDir = startDir; +// Cache root path +let rootDir: string | undefined; + +function findNodeModulesDir(root: string = process.cwd()) { + let currentDir = root; while (currentDir !== parse(currentDir).root) { const nodeModulesPath = join(currentDir, 'node_modules'); @@ -15,19 +18,19 @@ const nodeModulesDir = (function findNodeModulesDir(startDir = process.cwd()) { } return ''; -})(); -export const virtualPackageName = '__mf__virtual'; -if (!existsSync(resolve(nodeModulesDir, virtualPackageName))) { - mkdirSync(resolve(nodeModulesDir, virtualPackageName)); } -writeFileSync(resolve(nodeModulesDir, virtualPackageName, 'empty.js'), ''); -writeFileSync( - resolve(nodeModulesDir, virtualPackageName, 'package.json'), - JSON.stringify({ - name: virtualPackageName, - main: 'empty.js', - }) -); + +// Cache nodeModulesDir result to avoid repeated calculations +let cachedNodeModulesDir: string | undefined; + +function getNodeModulesDir() { + if (!cachedNodeModulesDir) { + cachedNodeModulesDir = findNodeModulesDir(rootDir); + } + return cachedNodeModulesDir; +} + +export const virtualPackageName = '__mf__virtual'; const patternMap: { [tag: string]: RegExp; @@ -38,6 +41,7 @@ const cacheMap: { [name: string]: VirtualModule; }; } = {}; + /** * Physically generate files as virtual modules under node_modules/__mf__virtual/* */ @@ -46,6 +50,37 @@ export default class VirtualModule { tag: string; suffix: string; inited: boolean = false; + + /** + * Set the root path for finding node_modules + * @param root - Root path + */ + static setRoot(root: string) { + rootDir = root; + // Reset cache to ensure using the new root path + cachedNodeModulesDir = undefined; + } + + /** + * Ensure virtual package directory exists + */ + static ensureVirtualPackageExists() { + const nodeModulesDir = getNodeModulesDir(); + const virtualPackagePath = resolve(nodeModulesDir, virtualPackageName); + + if (!existsSync(virtualPackagePath)) { + mkdirSync(virtualPackagePath); + writeFileSync(resolve(virtualPackagePath, 'empty.js'), ''); + writeFileSync( + resolve(virtualPackagePath, 'package.json'), + JSON.stringify({ + name: virtualPackageName, + main: 'empty.js', + }) + ); + } + } + static findModule(tag: string, str: string = ''): VirtualModule | undefined { if (!patternMap[tag]) patternMap[tag] = new RegExp(`(.*${packageNameEncode(tag)}(.+?)${packageNameEncode(tag)}.*)`); @@ -54,6 +89,7 @@ export default class VirtualModule { return cacheMap[tag][packageNameDecode(moduleName)] as VirtualModule | undefined; return undefined; } + constructor(name: string, tag: string = '__mf_v__', suffix = '') { this.name = name; this.tag = tag; @@ -61,14 +97,16 @@ export default class VirtualModule { if (!cacheMap[this.tag]) cacheMap[this.tag] = {}; cacheMap[this.tag][this.name] = this; } + getPath() { - return resolve(nodeModulesDir, this.getImportId()); + return resolve(getNodeModulesDir(), this.getImportId()); } + getImportId() { const { name: mfName } = getNormalizeModuleFederationOptions(); - return `${virtualPackageName}/${packageNameEncode(`${mfName}${this.tag}${this.name}${this.tag}`)}${this.suffix}`; } + writeSync(code: string, force?: boolean) { if (!force && this.inited) return; if (!this.inited) { @@ -76,6 +114,7 @@ export default class VirtualModule { } writeFileSync(this.getPath(), code); } + write(code: string) { writeFile(this.getPath(), code, function () {}); }