Daniyal Ahmed
Daniyal Ahmed

Reputation: 11

"ScriptExternalLoadError: Loading script failed" when using Module Federation with Angular and React in a monorepo with custom-webpack configuration

I am trying to implement Micro Frontends using Angular 13 and React, using Webpack 5 and the Module Federation plugin. I have a remote Angular application that I am trying to import into my host React application. I have followed the documentation and guides, but I am getting an error ScriptExternalLoadError: Loading script failed. when trying to load the remote Angular application.

Here are the details of my implementation:

webpack-config Angular Application

const path = require('path');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
  mode: 'development',
  devtool: 'source-map',
  entry: './src/bootstrap.ts',
  optimization: {
    splitChunks: false,
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
    chunkFilename: '[id].chunk.js',
    scriptType: 'text/javascript'
  },
  resolve: {
    extensions: ['.ts', '.js'],
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: [
          {
            loader: 'ts-loader',
            options: {
              transpileOnly: true,
            },
          },
        ],
      },
      {
        test: /\.html$/,
        loader: 'html-loader',
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
  devServer: {
    static: {
      directory: path.join(__dirname, 'dist'),
    },
    compress: true,
    port: 8082,
    hot: true,
    historyApiFallback: true,
  },
  plugins : [
    new ModuleFederationPlugin({
      name : 'cubs', 
      library: { type: 'var', name: 'profile' },
      filename : 'remoteEntry.js',
      exposes : {
        './angular' : './src/bootstrap'
      }
    }),
  ]
};

package-json

{
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test"
  },
  "private": true,
  "dependencies": {
    "@angular-builders/custom-webpack": "^13.1.0",
    "@angular/animations": "~13.3.0",
    "@angular/common": "~13.3.0",
    "@angular/compiler": "~13.3.0",
    "@angular/core": "~13.3.0",
    "@angular/forms": "~13.3.0",
    "@angular/platform-browser": "~13.3.0",
    "@angular/platform-browser-dynamic": "~13.3.0",
    "@angular/router": "~13.3.0",
    "clean-webpack-plugin": "^4.0.0",
    "html-webpack-plugin": "^5.5.1",
    "i": "^0.3.7",
    "npm": "^9.6.6",
    "rxjs": "~7.5.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.11.4"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~13.3.11",
    "@angular/cli": "~13.3.11",
    "@angular/compiler-cli": "~13.3.0",
    "@types/jasmine": "~3.10.0",
    "@types/node": "^12.11.1",
    "jasmine-core": "~4.0.0",
    "karma": "~6.3.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.1.0",
    "karma-jasmine": "~4.0.0",
    "karma-jasmine-html-reporter": "~1.7.0",
    "style-loader": "^3.3.2",
    "ts-loader": "^9.4.2",
    "typescript": "~4.6.2"
  }
}

angular.json

{
    "builder": "@angular-builders/custom-webpack:browser",
    "options": {
    "customWebpackConfig": {
      "path": "./custom-webpack.config.js"
},

bootstrap.ts

import 'zone.js';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { AppComponent } from './app/app.component';

export function mount(container: HTMLElement, props: any) {
  const appComponent = new AppComponent();
  const element = appComponent.getElement();
  container.appendChild(element);
  console.log("Component mounted")
}

platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .catch((err) => console.error(err));

In my React host Application webpack-dev.config

const { merge } = require('webpack-merge')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const commonConfig = require('./webpack.common')
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin')

const devConfig = {
    mode : 'development', 
    devServer : {
        port : 8080,
        historyApiFallback : {
            index : 'index.html'
        }
    },
    plugins : [
        new ModuleFederationPlugin({
            name : 'container', 
            remotes : {
                reactapp : 'reactapp@http://localhost:8081/remoteEntry.js',
                angular : "angular@http://localhost:8082/remoteEntry.js"
            }
        }), 
        new HtmlWebpackPlugin({
            template : '/public/index.html'
        })
    ]
}

module.exports = merge(commonConfig, devConfig)

React Component thats imported in the App.js

import React, { useRef, useEffect } from 'react';
import 'angular/angular'

const AngularAppWrapper = () => {

  const appRef = useRef(null);
    console.log("Making an attempt")
    console.log(mount);
  useEffect(() => {
    if (appRef.current) {
      mount(appRef.current, {});
    }
  }, []);

  return <div ref={appRef} />;
};

export default AngularAppWrapper;

Host react Bootstrap

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);


reportWebVitals();

Upvotes: 0

Views: 4243

Answers (2)

busybee
busybee

Reputation: 194

The following code fixed the script error for me:

optimization: {
  runtimeChunk: false
},

Upvotes: 0

ManAnRuck
ManAnRuck

Reputation: 251

I encountered a similar issue with ScriptExternalLoadError when using Module Federation with Webpack. The solution that worked for me was to set optimization.minimize to false in the Webpack configuration. This essentially disables the code minimization step during the build process.

Here's how to do it:

// webpack.config.js

module.exports = {
  // ...other configurations
  optimization: {
    minimize: false
  }
};

By doing this, you might notice that the bundle size will increase because the code isn't being minified. However, it should resolve the ScriptExternalLoadError you're experiencing. Keep in mind that you should ideally only do this for debugging, and find a more permanent solution for production.

Upvotes: 1

Related Questions