@module-federation/enhanced
Version:
This package provides enhanced features for module federation.
347 lines (344 loc) • 20.3 kB
JavaScript
;
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