UNPKG

@module-federation/enhanced

Version:

This package provides enhanced features for module federation.

347 lines (344 loc) • 20.3 kB
'use strict'; Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } }); const require_runtime = require('../../_virtual/_rolldown/runtime.js'); const require_lib_container_options = require('../container/options.js'); const require_utils = require('../../utils.js'); const require_lib_container_runtime_FederationRuntimePlugin = require('../container/runtime/FederationRuntimePlugin.js'); const require_lib_sharing_utils = require('./utils.js'); const require_lib_sharing_ProvideForSharedDependency = require('./ProvideForSharedDependency.js'); const require_lib_sharing_ProvideSharedDependency = require('./ProvideSharedDependency.js'); const require_lib_sharing_ProvideSharedModuleFactory = require('./ProvideSharedModuleFactory.js'); const require_schemas_sharing_ProvideSharedPlugin_check = require('../../schemas/sharing/ProvideSharedPlugin.check.js'); const require_schemas_sharing_ProvideSharedPlugin = require('../../schemas/sharing/ProvideSharedPlugin.js'); let _module_federation_sdk_normalize_webpack_path = require("@module-federation/sdk/normalize-webpack-path"); //#region src/lib/sharing/ProvideSharedPlugin.ts const { satisfy, parseRange } = require((0, _module_federation_sdk_normalize_webpack_path.normalizeWebpackPath)("webpack/lib/util/semver")); const WebpackError = require((0, _module_federation_sdk_normalize_webpack_path.normalizeWebpackPath)("webpack/lib/WebpackError")); const validate = require_utils.createSchemaValidation((require_schemas_sharing_ProvideSharedPlugin_check.init_ProvideSharedPlugin_check(), require_runtime.__toCommonJS(require_schemas_sharing_ProvideSharedPlugin_check.ProvideSharedPlugin_check_exports)).validate, () => (require_schemas_sharing_ProvideSharedPlugin.init_ProvideSharedPlugin(), require_runtime.__toCommonJS(require_schemas_sharing_ProvideSharedPlugin.ProvideSharedPlugin_exports)).default, { name: "Provide Shared Plugin", baseDataPath: "options" }); /** * @typedef {Object} ProvideOptions * @property {string} shareKey * @property {string | string[]} shareScope * @property {string | undefined | false} version * @property {boolean} eager * @property {string} [request] The actual request to use for importing the module * @property {{ version?: string; request?: string | RegExp; fallbackVersion?: string }} [exclude] Options for excluding certain versions or requests * @property {{ version?: string; request?: string | RegExp; fallbackVersion?: string }} [include] Options for including only certain versions or requests */ /** @typedef {Map<string, { config: ProvideOptions, version: string | undefined | false }>} ResolvedProvideMap */ var ProvideSharedPlugin = class { /** * @param {ProvideSharedPluginOptions} options options */ constructor(options) { validate(options); this._provides = require_lib_container_options.parseOptions(options.provides, (item) => { if (Array.isArray(item)) throw new Error("Unexpected array of provides"); return { shareKey: item, version: void 0, shareScope: options.shareScope || "default", eager: false, requiredVersion: false, strictVersion: false, singleton: false, layer: void 0, request: item, exclude: void 0, include: void 0, allowNodeModulesSuffixMatch: false, treeShakingMode: void 0 }; }, (item, key) => { const request = item.request || key; return { shareScope: item.shareScope || options.shareScope || "default", shareKey: item.shareKey || request, version: item.version, eager: !!item.eager, requiredVersion: item.requiredVersion, strictVersion: item.strictVersion, singleton: !!item.singleton, layer: item.layer, request, exclude: item.exclude, include: item.include, allowNodeModulesSuffixMatch: !!item.allowNodeModulesSuffixMatch, treeShakingMode: item.treeShakingMode }; }); this._provides.sort(([a], [b]) => { if (a < b) return -1; if (b < a) return 1; return 0; }); } /** * Apply the plugin * @param {Compiler} compiler the compiler instance * @returns {void} */ apply(compiler) { new require_lib_container_runtime_FederationRuntimePlugin.default().apply(compiler); process.env["FEDERATION_WEBPACK_PATH"] = process.env["FEDERATION_WEBPACK_PATH"] || (0, _module_federation_sdk_normalize_webpack_path.getWebpackPath)(compiler); const compilationData = /* @__PURE__ */ new WeakMap(); compiler.hooks.compilation.tap("ProvideSharedPlugin", (compilation, { normalModuleFactory }) => { const resolvedProvideMap = /* @__PURE__ */ new Map(); const matchProvides = /* @__PURE__ */ new Map(); const prefixMatchProvides = /* @__PURE__ */ new Map(); for (const [request, config] of this._provides) { const actualRequest = config.request || request; const lookupKey = require_lib_sharing_utils.createLookupKeyForSharing(actualRequest, config.layer); if (/^(\/|[A-Za-z]:\\|\\\\|\.\.?(\/|$))/.test(actualRequest)) { if (this.shouldProvideSharedModule(config)) resolvedProvideMap.set(lookupKey, { config, version: config.version, resource: actualRequest }); } else if (/^(\/|[A-Za-z]:\\|\\\\)/.test(actualRequest)) { if (this.shouldProvideSharedModule(config)) resolvedProvideMap.set(lookupKey, { config, version: config.version, resource: actualRequest }); } else if (actualRequest.endsWith("/")) prefixMatchProvides.set(lookupKey, config); else matchProvides.set(lookupKey, config); } compilationData.set(compilation, resolvedProvideMap); normalModuleFactory.hooks.module.tap("ProvideSharedPlugin", (module, { resource, resourceResolveData }, resolveData) => { const moduleLayer = module.layer; const lookupKeyForResource = require_lib_sharing_utils.createLookupKeyForSharing(resource || "", moduleLayer || void 0); if (resource && resolvedProvideMap.has(lookupKeyForResource)) return module; const { request: originalRequestString } = resolveData; const originalRequestLookupKey = require_lib_sharing_utils.createLookupKeyForSharing(originalRequestString, moduleLayer || void 0); const configFromOriginalDirect = matchProvides.get(originalRequestLookupKey); if (configFromOriginalDirect !== void 0 && resource && !resolvedProvideMap.has(lookupKeyForResource)) { if (require_lib_sharing_utils.testRequestFilters(originalRequestString, configFromOriginalDirect.include?.request, configFromOriginalDirect.exclude?.request)) { this.provideSharedModule(compilation, resolvedProvideMap, originalRequestString, configFromOriginalDirect, resource, resourceResolveData); resolveData.cacheable = false; } } if (resource && !resolvedProvideMap.has(lookupKeyForResource)) for (const [prefixLookupKey, originalPrefixConfig] of prefixMatchProvides) { const configuredPrefix = originalPrefixConfig.request || prefixLookupKey.split("?")[0]; if (originalPrefixConfig.layer) { if (!moduleLayer) continue; if (moduleLayer !== originalPrefixConfig.layer) continue; } if (originalRequestString.startsWith(configuredPrefix)) { if (resolvedProvideMap.has(lookupKeyForResource)) continue; const remainder = originalRequestString.slice(configuredPrefix.length); if (!require_lib_sharing_utils.testRequestFilters(remainder, originalPrefixConfig.include?.request, originalPrefixConfig.exclude?.request)) continue; const finalShareKey = originalPrefixConfig.shareKey ? originalPrefixConfig.shareKey + remainder : configuredPrefix + remainder; if (originalPrefixConfig.include?.request && originalPrefixConfig.singleton) require_lib_sharing_utils.addSingletonFilterWarning(compilation, finalShareKey, "include", "request", originalPrefixConfig.include.request, originalRequestString, resource); if (originalPrefixConfig.exclude?.request && originalPrefixConfig.singleton) require_lib_sharing_utils.addSingletonFilterWarning(compilation, finalShareKey, "exclude", "request", originalPrefixConfig.exclude.request, originalRequestString, resource); const configForSpecificModule = { ...originalPrefixConfig, shareKey: finalShareKey, request: originalRequestString, _originalPrefix: configuredPrefix, include: originalPrefixConfig.include ? { ...originalPrefixConfig.include } : void 0, exclude: originalPrefixConfig.exclude ? { ...originalPrefixConfig.exclude } : void 0 }; this.provideSharedModule(compilation, resolvedProvideMap, originalRequestString, configForSpecificModule, resource, resourceResolveData); resolveData.cacheable = false; break; } } if (resource && !resolvedProvideMap.has(lookupKeyForResource)) { const modulePathAfterNodeModules = require_lib_sharing_utils.extractPathAfterNodeModules(resource); if (modulePathAfterNodeModules) { const reconstructedLookupKey = require_lib_sharing_utils.createLookupKeyForSharing(modulePathAfterNodeModules, moduleLayer || void 0); const configFromReconstructedDirect = matchProvides.get(reconstructedLookupKey); if (configFromReconstructedDirect !== void 0 && configFromReconstructedDirect.allowNodeModulesSuffixMatch && !resolvedProvideMap.has(lookupKeyForResource)) { this.provideSharedModule(compilation, resolvedProvideMap, modulePathAfterNodeModules, configFromReconstructedDirect, resource, resourceResolveData); resolveData.cacheable = false; } if (resource && !resolvedProvideMap.has(lookupKeyForResource)) for (const [prefixLookupKey, originalPrefixConfig] of prefixMatchProvides) { if (!originalPrefixConfig.allowNodeModulesSuffixMatch) continue; const configuredPrefix = originalPrefixConfig.request || prefixLookupKey.split("?")[0]; if (originalPrefixConfig.layer) { if (!moduleLayer) continue; if (moduleLayer !== originalPrefixConfig.layer) continue; } if (modulePathAfterNodeModules.startsWith(configuredPrefix)) { if (resolvedProvideMap.has(lookupKeyForResource)) continue; const remainder = modulePathAfterNodeModules.slice(configuredPrefix.length); if (!require_lib_sharing_utils.testRequestFilters(remainder, originalPrefixConfig.include?.request, originalPrefixConfig.exclude?.request)) continue; const finalShareKey = originalPrefixConfig.shareKey ? originalPrefixConfig.shareKey + remainder : configuredPrefix + remainder; if (originalPrefixConfig.include?.request && originalPrefixConfig.singleton) require_lib_sharing_utils.addSingletonFilterWarning(compilation, finalShareKey, "include", "request", originalPrefixConfig.include.request, modulePathAfterNodeModules, resource); if (originalPrefixConfig.exclude?.request && originalPrefixConfig.singleton) require_lib_sharing_utils.addSingletonFilterWarning(compilation, finalShareKey, "exclude", "request", originalPrefixConfig.exclude.request, modulePathAfterNodeModules, resource); const configForSpecificModule = { ...originalPrefixConfig, shareKey: finalShareKey, request: modulePathAfterNodeModules, _originalPrefix: configuredPrefix, include: originalPrefixConfig.include ? { ...originalPrefixConfig.include } : void 0, exclude: originalPrefixConfig.exclude ? { ...originalPrefixConfig.exclude } : void 0 }; this.provideSharedModule(compilation, resolvedProvideMap, modulePathAfterNodeModules, configForSpecificModule, resource, resourceResolveData); resolveData.cacheable = false; break; } } } } return module; }); }); compiler.hooks.finishMake.tapPromise("ProvideSharedPlugin", async (compilation) => { const resolvedProvideMap = compilationData.get(compilation); if (!resolvedProvideMap) return; const filteredEntries = Array.from(resolvedProvideMap).filter(([resourceKey, { config, version, resource }]) => { const actualResource = resource || resourceKey; if (config.include) { let versionIncludeFailed = false; if (typeof config.include.version === "string") if (typeof version === "string" && version) { if (!satisfy(parseRange(config.include.version), version)) versionIncludeFailed = true; } else versionIncludeFailed = true; let requestIncludeFailed = false; if (config.include.request) { const includeRequestValue = config.include.request; let testString = actualResource; if (config._originalPrefix && actualResource.startsWith(config._originalPrefix)) testString = actualResource.slice(config._originalPrefix.length); if (!(includeRequestValue instanceof RegExp ? includeRequestValue.test(testString) : testString === includeRequestValue)) requestIncludeFailed = true; } const shouldSkipVersion = typeof config.include.version === "string" && versionIncludeFailed; const shouldSkipRequest = config.include.request && requestIncludeFailed; if (shouldSkipVersion || shouldSkipRequest) return false; } if (config.exclude) { let versionExcludeMatches = false; if (typeof config.exclude.version === "string" && typeof version === "string" && version) { if (satisfy(parseRange(config.exclude.version), version)) versionExcludeMatches = true; } let requestExcludeMatches = false; if (config.exclude.request) { const excludeRequestValue = config.exclude.request; let testString = actualResource; if (config._originalPrefix && actualResource.startsWith(config._originalPrefix)) testString = actualResource.slice(config._originalPrefix.length); if (excludeRequestValue instanceof RegExp ? excludeRequestValue.test(testString) : testString === excludeRequestValue) requestExcludeMatches = true; } if (versionExcludeMatches || requestExcludeMatches) return false; } return true; }); await Promise.all(filteredEntries.map(([resourceKey, { config, version, resource }]) => { return new Promise((resolve, reject) => { compilation.addInclude(compiler.context, new require_lib_sharing_ProvideSharedDependency.default(config.shareScope, config.shareKey, version || false, resource || resourceKey, config.eager, config.requiredVersion, config.strictVersion, config.singleton, config.layer, config.treeShakingMode), { name: void 0 }, (err) => { if (err) return reject(err); resolve(); }); }); })); }); compiler.hooks.compilation.tap("ProvideSharedPlugin", (compilation, { normalModuleFactory }) => { compilation.dependencyFactories.set(require_lib_sharing_ProvideForSharedDependency.default, normalModuleFactory); compilation.dependencyFactories.set(require_lib_sharing_ProvideSharedDependency.default, new require_lib_sharing_ProvideSharedModuleFactory.default()); }); } provideSharedModule(compilation, resolvedProvideMap, key, config, resource, resourceResolveData) { let version = config.version; if (version === void 0) { let details = ""; if (!resourceResolveData) details = `No resolve data provided from resolver.`; else { const descriptionFileData = resourceResolveData.descriptionFileData; if (!descriptionFileData) details = "No description file (usually package.json) found. Add description file with name and version, or manually specify version in shared config."; else if (!descriptionFileData.version) if (resourceResolveData.descriptionFilePath) try { const path = require("path"); const fs = require("fs"); const parentPkgPath = path.resolve(path.dirname(resourceResolveData.descriptionFilePath), "..", "package.json"); if (fs.existsSync(parentPkgPath)) { const parentVersion = require_lib_sharing_utils.getRequiredVersionFromDescriptionFile(JSON.parse(fs.readFileSync(parentPkgPath, "utf8")), key); if (parentVersion) { version = parentVersion; details = `Using version from parent package.json dependencies: ${version}`; } else details = `No version in description file (usually package.json). Add version to description file ${resourceResolveData.descriptionFilePath}, or manually specify version in shared config.`; } else details = `No version in description file (usually package.json). Add version to description file ${resourceResolveData.descriptionFilePath}, or manually specify version in shared config.`; } catch (e) { details = `No version in description file (usually package.json). Add version to description file ${resourceResolveData.descriptionFilePath}, or manually specify version in shared config.`; } else details = `No version in description file (usually package.json). Add version to description file ${resourceResolveData.descriptionFilePath}, or manually specify version in shared config.`; else version = descriptionFileData.version; } if (!version) { const error = new WebpackError(`No version specified and unable to automatically determine one. ${details}`); error.file = `shared module ${key} -> ${resource}`; compilation.warnings.push(error); } } if (config.include) { let versionIncludeFailed = false; if (typeof config.include.version === "string") if (typeof version === "string" && version) { if (!satisfy(parseRange(config.include.version), version)) versionIncludeFailed = true; } else versionIncludeFailed = true; let requestIncludeFailed = false; if (config.include.request) { const includeRequestValue = config.include.request; if (!(includeRequestValue instanceof RegExp ? includeRequestValue.test(resource) : resource === includeRequestValue)) requestIncludeFailed = true; } const shouldSkipVersion = typeof config.include.version === "string" && versionIncludeFailed; const shouldSkipRequest = config.include.request && requestIncludeFailed; if (shouldSkipVersion || shouldSkipRequest) { if (shouldSkipVersion) { const error = new WebpackError(`Provided module "${key}" version "${version}" does not satisfy include filter "${config.include.version}"`); error.file = `shared module ${key} -> ${resource}`; compilation.warnings.push(error); } return; } if (config.include.version && config.singleton) require_lib_sharing_utils.addSingletonFilterWarning(compilation, config.shareKey || key, "include", "version", config.include.version, key, resource); } if (config.exclude) { let versionExcludeMatches = false; if (typeof config.exclude.version === "string" && typeof version === "string" && version) { if (satisfy(parseRange(config.exclude.version), version)) versionExcludeMatches = true; } let requestExcludeMatches = false; if (config.exclude.request) { const excludeRequestValue = config.exclude.request; if (excludeRequestValue instanceof RegExp ? excludeRequestValue.test(resource) : resource === excludeRequestValue) requestExcludeMatches = true; } if (versionExcludeMatches || requestExcludeMatches) { if (versionExcludeMatches) { const error = new WebpackError(`Provided module "${key}" version "${version}" matches exclude filter "${config.exclude.version}"`); error.file = `shared module ${key} -> ${resource}`; compilation.warnings.push(error); } return; } if (config.exclude.version && config.singleton) require_lib_sharing_utils.addSingletonFilterWarning(compilation, config.shareKey || key, "exclude", "version", config.exclude.version, key, resource); } const lookupKey = require_lib_sharing_utils.createLookupKeyForSharing(resource, config.layer); resolvedProvideMap.set(lookupKey, { config, version, resource, layer: config.layer }); } shouldProvideSharedModule(config) { if (!config.version) return true; const version = config.version; if (typeof version !== "string") return true; if (config.include?.version) { const includeVersion = config.include.version; if (typeof includeVersion === "string") { if (!satisfy(parseRange(includeVersion), version)) return false; } } if (config.exclude?.version) { const excludeVersion = config.exclude.version; if (typeof excludeVersion === "string") { if (satisfy(parseRange(excludeVersion), version)) return false; } } return true; } }; //#endregion exports.default = ProvideSharedPlugin; //# sourceMappingURL=ProvideSharedPlugin.js.map