Vite not bundling single-spa microfrontend application

211 views Asked by At

I'm in the process of migrating the bundler of my micro-frontend application that uses single-spa, from Webpack (CRACO) to Vite. I've added all the required dependencies and created the config file (vite.config.js), but on running the server, I'm unable to locate the bundled JS for the application. Note that, this is a micro frontend application, hence there's no index.html, I tried using the lib mode in vite config, but that still doesn't seem to be working as expected.

vite.config.js

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import vitePluginSingleSpa from "vite-plugin-single-spa";
import path from "path";
import svgr from "vite-plugin-svgr";


export default defineConfig({
  build: {
    outDir:"build",
    rollupOptions: {
      input: "./src/singleSPA.tsx",
      preserveEntrySignatures: true,
      output: {
        entryFileNames:"xyz/abc.js",
      },
    },
  },
  plugins: [
    svgr(),
    react(),
    vitePluginSingleSpa({
      type: "mife",
      serverPort: 3002,
      spaEntryPoint: "./src/singleSPA.tsx", //defaults to src/index.js,
    }),
  ],
  server: {
    hmr: false,
  },
  css: {
    preprocessorOptions: {
      less: {
        relativeUrls: true,
        javascriptEnabled: true,
      },
    },
  },
  resolve: {
    alias: {
      "@assets": path.resolve(__dirname, "./src/assets"),
      "@components": path.resolve(__dirname, "./src/shared"),
      "@store": path.resolve(__dirname, "./src/store/"),
      "@screens": path.resolve(__dirname, "./src/screens"),
      "@utils": path.resolve(__dirname, "./src/utils"),
      "@images": path.resolve(__dirname, "./src/images"),
      "@generated": path.resolve(__dirname, "./src/generated"),
      "@APP_CONFIG": path.resolve(__dirname, "./src/APP_CONFIG.js"),
      "@APOLLO_CLIENT": path.resolve(__dirname, "./src/apolloClient.tsx"),
      "~antd": path.resolve(__dirname, "./node_modules/antd"),
    },
  },
});

craco.config.js (Before migration, for comparison purposes)

/* eslint-disable @typescript-eslint/no-var-requires */
const CracoLessPlugin = require("craco-less");
const CracoAlias = require("craco-alias");
const singleSpaApplicationPlugin = require("craco-plugin-single-spa-application");

module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            javascriptEnabled: true,
          },
        },
      },
    },
    {
      plugin: singleSpaApplicationPlugin,
      options: {
        orgName: "xyz",
        projectName: "abc",
        entry: "src/singleSPA.tsx", //defaults to src/index.js,
        orgPackagesAsExternal: false, // defaults to false. marks packages that has @my-org prefix as external so they are not included in the bundle
        reactPackagesAsExternal:
          process.env.NODE_ENV === "production" ? true : false, // defaults to true. marks react and react-dom as external so they are not included in the bundle
        externals: [], // defaults to []. marks the specified modules as external so they are not included in the bundle
        minimize: process.env.NODE_ENV === "production" ? true : false, // defaults to false, sets optimization.minimize value
      },
    },
    {
      plugin: CracoAlias,
      options: {
        source: "tsconfig",
        // baseUrl SHOULD be specified
        // plugin does not take it from tsconfig
        baseUrl: "./src",
        /* tsConfigPath should point to the file where "baseUrl" and "paths"
         are specified*/
        tsConfigPath: "./tsconfig.paths.json",
      },
    },
  ],
  webpack: {
    configure: {
      /* Any webpack configuration options: https:webpack.js.org/configuration */
      output: {
        devtoolNamespace: "@xyz/abc",
      },
      devtool: "source-map",
      snapshot: {
        managedPaths: [/^(.+?[\\/]node_modules)[\\/]((?!@pqrst)).*[\\/]*/],
      },
    },
  },
};

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "rootDir": "src",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "noImplicitAny": false,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "noFallthroughCasesInSwitch": false,
  },
  "include": ["src"],
  "exclude": ["**/node_modules"],
  "extends": "./tsconfig.paths.json"
}

package.json

{
  "name": "microfrontend",
  "version": "1.0.450",
  "private": true,
  "homepage": "/xyz",
  "type": "module",
  "dependencies": {
   //dependencies here, removed from the snippet as it got too long
  },
  "devDependencies": {
   //devDependencies here, removed from the snippet as it got too long
  },
  "resolutions": {
    "@types/react": "17.0.44",
    "antd": "4.24.9",
  },
  "scripts": {
    "start": "vite --port 3002",
    "start-windows": "set HTTPS=true&&vite serve",
    "build": "vite build",
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix"
    ]
  }
}

singleSPA.tsx

import React from "react";
import ReactDOM from "react-dom";
import singleSpaReact from "single-spa-react";
import Root from "./mainProvider";
import "./set-public-path";
import { cssLifecycleFactory } from "vite-plugin-single-spa/ex";

const lifecycles = singleSpaReact({
  React,
  ReactDOM,
  rootComponent: Root,
  errorBoundary(props) {
    return <h1>An error has occurred {props}</h1>;
  },
  domElementGetter,
});

// export const { bootstrap, mount, unmount } = lifecycles;
const cssLc = cssLifecycleFactory("singleSPA");
export const bootstrap = [cssLc.bootstrap, lifecycles.bootstrap];
export const mount = [cssLc.mount, lifecycles.mount];
export const unmount = [cssLc.unmount, lifecycles.unmount];
function domElementGetter() {
  let el = document.getElementById("mf-content");
  if (!el) {
    el = document.createElement("div");
    el.id = "content-app";
    document.body.appendChild(el);
  }
  return el;
}

The expected behavior is, on navigating to localhost:<port_no>/xyz/abc.js, Vite should be able to spit the bundled JS code of my micro-frontend application. But with my current configuration as shared above, I'm getting 404 status code with the message "This localhost page can't be found". I'm quite lost in figuring out the root cause for this. I went through other related questions on SO and other blogs including the official docs, but didn't find them much useful. Any help would be greatly appreciated.

0

There are 0 answers