module-federation-examples: Module Federation with Webpacker/Rails host resulting in ScriptLoadError

We’ve been successfully testing Module Federation with apps that use React and Webpack directly but recently ran into the need to integrate an app that uses Webpack through Webpacker and for some reason we’re running into ScriptLoadError issues anytime we try to load the remote-entry that comes from the Rails/Webpacker side.

On the Rails/Webpacker remote the remote-entry.js returns 200 but the issue appears to occur at evaluation time:

image

We’ve also noticed that the remote-entry.js output from Webpacker/Rails-side is significantly smaller (in code) than the one we get from our

The Rails Webpacker remote side is using Webpack 5.51.1. Our host is using Webpack 5.50.0.

Remote Webpack/Rails app

Compile Webpack config output

{
  mode: 'development',
  output: {
    filename: 'js/[name].js',
    chunkFilename: 'js/[name].chunk.js',
    hotUpdateChunkFilename: 'js/[id].[fullhash].hot-update.js',
    path: '<removed>/webpacker-module-federation/public/packs',
    publicPath: '/packs/'
  },
  entry: {
    application: '<removed>/webpacker-module-federation/app/javascript/packs/application.js',
    hello_react: '<removed>/webpacker-module-federation/app/javascript/packs/hello_react.jsx'
  },
  resolve: {
    extensions: [ '.js', '.jsx', '.mjs', '.ts', '.tsx', '.coffee' ],
    modules: [
      '<removed>/webpacker-module-federation/app/javascript',
      'node_modules'
    ],
    plugins: [ [Object] ]
  },
  plugins: [
    EnvironmentPlugin { keys: [Array], defaultValues: [Object] },
    WebpackAssetsManifest {
      hooks: [Object],
      options: [Object],
      assets: [Object: null prototype] {},
      assetNames: Map(0) {},
      currentAsset: null,
      compiler: null,
      [Symbol(isMerging)]: false
    },
    ModuleFederationPlugin { _options: [Object] }
  ],
  resolveLoader: { modules: [ 'node_modules' ], plugins: [ [Object] ] },
  optimization: { splitChunks: { chunks: 'all' }, runtimeChunk: 'single' },
  module: {
    strictExportPresence: true,
    rules: [ [Object], [Object], [Object] ]
  },
  devtool: 'cheap-module-source-map',
  stats: {
    colors: true,
    entrypoints: false,
    errorDetails: true,
    modules: false,
    moduleTrace: false
  },
  devServer: {
    devMiddleware: { publicPath: '/packs/' },
    compress: true,
    allowedHosts: 'all',
    host: 'localhost',
    port: 3035,
    https: false,
    hot: false,
    liveReload: true,
    historyApiFallback: { disableDotRule: true },
    headers: { 'Access-Control-Allow-Origin': '*' },
    static: {
      publicPath: '<removed>/webpacker-module-federation/public/packs',
      watch: [Object]
    },
    client: { overlay: true }
  }
}

Here’s the generated remote-entry.js from that same remote:

"use strict";
var demo;
(self["webpackChunkwebpacker_module_federation"] = self["webpackChunkwebpacker_module_federation"] || []).push([["demo"],{

/***/ "webpack/container/entry/demo":
/*!***********************!*\
  !*** container entry ***!
  \***********************/
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {

var moduleMap = {
	"./app": function() {
		return Promise.all([__webpack_require__.e("vendors-node_modules_prop-types_index_js"), __webpack_require__.e("webpack_sharing_consume_default_react-dom_react-dom-webpack_sharing_consume_default_react_react"), __webpack_require__.e("app_javascript_packs_hello_react_jsx")]).then(function() { return function() { return (__webpack_require__(/*! ./app/javascript/packs/hello_react */ "./app/javascript/packs/hello_react.jsx")); }; });
	}
};
var get = function(module, getScope) {
	__webpack_require__.R = getScope;
	getScope = (
		__webpack_require__.o(moduleMap, module)
			? moduleMap[module]()
			: Promise.resolve().then(function() {
				throw new Error('Module "' + module + '" does not exist in container.');
			})
	);
	__webpack_require__.R = undefined;
	return getScope;
};
var init = function(shareScope, initScope) {
	if (!__webpack_require__.S) return;
	var oldScope = __webpack_require__.S["default"];
	var name = "default"
	if(oldScope && oldScope !== shareScope) throw new Error("Container initialization failed as it has already been initialized with a different share scope");
	__webpack_require__.S[name] = shareScope;
	return __webpack_require__.I(name, initScope);
};

// This exports getters to disallow modifications
__webpack_require__.d(exports, {
	get: function() { return get; },
	init: function() { return init; }
});

/***/ })

},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
/******/ __webpack_require__.O(0, ["vendors-node_modules_react-dom_index_js-node_modules_react_index_js-node_modules_webpack-dev--3a93d3","webpack_sharing_consume_default_react_react-webpack_sharing_provide_default_react-dom-webpack-f6baa5"], function() { return __webpack_exec__("./node_modules/webpack-dev-server/client/index.js?protocol=ws%3A&hostname=localhost&port=3035&pathname=%2Fws&logging=info"), __webpack_exec__("webpack/container/entry/demo"); });
/******/ var __webpack_exports__ = __webpack_require__.O();
/******/ demo = __webpack_exports__;
/******/ }
]);
//# sourceMappingURL=remote-entry.js.map

Host

Module config

new ModuleFederationPlugin({
        name: "ilx",
        remotes: {
          demo: `demo@http://localhost:3035/packs/remote-entry.js`,
        },
        shared: {
          react: {
            eager: true,
            singleton: true,
          },
          "react-dom": {
            eager: true,
            singleton: true,
          },
        },
      }),

We’re aware that it’s strange that everything from the webpack-dev-server compiled via Webpacker is hosted at /packs/ but that’s the convention. We’re obviously fairly confused by these issues and could use any guidance you might have. Thanks.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 3
  • Comments: 22 (11 by maintainers)

Commits related to this issue

Most upvoted comments

Nah I think what you’ve got should be fine

@ScriptedAlchemy I celebrated too quickly. Unsurprisingly setting publicPath: 'auto' led to the Rails Webpacker setup no longer working appropriately. Likely because the particular location of packs in app/javascripts/packs/ within Rails isn’t compatible with that setting.

So the remote entry works from the host but the remote app starts having 404s when attempting to load assets. We need an explicit solution to this that doesn’t rely on whatever auto does internally.

I’m prepared to schedule a group call (with my co-worker @meglkts) with you soon but I think there’s a large missed opportunity in appropriate error handling within the logic that fetches chunks. Is there anything we can do to raise exceptions at the appropriate level when individual chunks result in 404s?