reactjsnginxvitemicro-frontendwebpack-module-federation

MIME type issue in Microfrontends deployment with Vite and Module Federation


I’m building an Admin Panel and using some remote components via Module Federation. While dynamically importing the components, the following error occurs:

Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec. _federation_expose_Page.0d00fb5f.js:1

Below is my configuration for vite.config.js (Host App):

import { defineConfig } from "vite";
import { resolve } from "path";
import react from "@vitejs/plugin-react";
import federation from "@originjs/vite-plugin-federation";

export default defineConfig({
  base: "/",
  plugins: [
    react(),
    federation({
      name: "app",
      remotes: {
        billing:
          "https://billing-microfrontend.delever.uz/assets/remoteEntry.js?v=1.2.2",
        singleOrder:
          "https://order-microfrontend.delever.uz/assets/remoteEntry.js?v=1.3.5",
      },
      shared: ["react", "react-dom", "@tanstack/react-query"],
    }),
  ],
  publicDir: "public",
  build: {
    outDir: "build",
    modulePreload: false,
    target: "esnext",
    minify: false,
    cssCodeSplit: false,
    rollupOptions: {
      onwarn(warning, warn) {
        if (warning.code === "MODULE_LEVEL_DIRECTIVE") {
          return;
        }
        warn(warning);
      },
    },
  },
  // Additional server configurations
  server: {
    port: 7777,
    cors: {
      origin: "*",
      methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
      allowedHeaders: ["X-Requested-With", "content-type", "Authorization"],
    },
  },
  // sass configuration
  css: {
    preprocessorOptions: {
      scss: {
        api: "modern",
      },
    },
  },
  resolve: {
    alias: [
      {
        find: "@",
        replacement: resolve(__dirname, "src"),
      },
    ],
  },
});

Server was configured using nginx:

  1. HOST APP

user nginx;
worker_processes 1;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
  worker_connections 1024;
}

http {
  include /etc/nginx/mime.types;
  default_type application/octet-stream;

  log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';
  access_log /var/log/nginx/access.log main;

  sendfile on;
  keepalive_timeout 65;

  gzip on;
  gzip_types text/plain application/xml application/json application/javascript text/css image/svg+xml font/woff2;
  gzip_proxied no-cache no-store private expired auth;
  gzip_min_length 1000;
  gzip_vary on;

  server {
    listen 80;
    server_name localhost;

    root /build;  # Ensure this is the correct path to your build folder

    location / {
      try_files $uri $uri/ /index.html;  # Catch-all for client-side routing
    }

    location /assets/remoteEntry.js {
      add_header Content-Type application/javascript;  # Correct MIME type
      try_files $uri =404;  # Serve 404 if the file doesn't exist
    }
    
    # Serve static files with caching
    location ~* \.(?:js|css|woff2?|ttf|svg|png|jpg|jpeg|gif|ico|html)$ {
      try_files $uri =404;
      expires 6M;  # Cache static files for 6 months
      access_log off;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
      root /usr/share/nginx/html;
    }
  }
}

REMOTE COMPONENT:

user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
  worker_connections  1024;
}
http {
  include       /etc/nginx/mime.types;
  default_type  application/octet-stream;
  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
  access_log  /var/log/nginx/access.log  main;
  sendfile        on;
  keepalive_timeout  65;
  server {
    gzip on;
    gzip_types      text/plain application/xml;
    gzip_proxied    no-cache no-store private expired auth;
    gzip_min_length 1000;
    listen       3000;
    server_name  localhost;
    location / {
      root   /build;
      index  index.html;
      try_files $uri $uri/ /index.html;
    }
    location /assets/remoteEntry.js {
      root /build;
      
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
      root   /usr/share/nginx/html;
    }
  }
}

I’m providing both nginx configurations because I previously resolved this error with nginx settings by:

  1. Disabling service workers
  2. Updating the Host App’s nginx configuration to the current version.

Interestingly, the billing remote works perfectly, but singleOrder does not, even though both configurations are identical.

I expected both remotes to work without issues since their paths and MIME configurations match.

What could be causing the MIME type issue for the singleOrder remote? Could this be a caching issue or a misconfiguration in nginx or Vite?


Solution

  • The problem was resolved by downgrading the @originjs/vite-plugin-federation package to version 1.3.6 from 1.3.8.

    Source: vite-plugin-federation #660.