Vite + Single-SPA: A Recap up to vite-plugin-single-spa v0.3.1
Tags: frontend, webdev, singlespa, vue
Published: 2023-11-11 17:41:25

Hello, everyone. I haven't been around for some time, but not because I have abandoned the subject. On the contrary, I have been working hard on my primary project. At this point in time, I have successfully converted the custom implementation we had to a set of root + micro-frontend projects joined together with `single-spa`. Yes, I also converted the *Create React App* projects we had to *Vite*.

In short: It looks like my primary objective has been fulfilled.

I must tell you, however, that some things changed along the way. The most notable one is that I did not convert the root project to *Svelte*. It remains in *React* for the time being. My timetable, however, contemplates full conversion of all projects.

## Revised Objectives

So, because I set expectations of what would happen at the beginning of this series of articles, allow me to present the revised list of objectives, so we are all on the same page:

> My primary objective remains: Migrate the entire codebase from React to Svelte with the assistance of `single-spa`.

My secondary objectives, however, have suffered some transformation: No longer will I go with the root project to *Svelte* as my first transformation, but instead I'll make the project go on-demand transformation to *Svelte* as the priorities arrive. The general rule to follow, and it has already proven to be a great path, is to aggressively convert the pieces that require modification/maintenance into `single-spa` micro-frontends with *Vite + Svelte* instead of correcting the *React* codebase.

The consequence of this rule is the spawn of additional micro-frontends that might be too fine-grained. To counter this, we will allow the number of micro-frontends to grow until a point is reached where some can coalesce into one. This will be repeated until the entire codebase is converted.

Thankfully, we are using Kubernetes, meaning we can easily deploy all these pods until everything settles into a reasonable number and scheme of micro-frontends.

Enough chit-chat, I would say, let's cover the new learnings since the last article that led to the current iteration of `vite-plugin-single-spa`.

## CSS Injection

The previous article in the series describes our learning towards gaining full CSS injection in *Vite + XXX* projects. Well, just like the previous article hinted, `vite-plugin-single-spa` **v0.1.0** introduced a dynamic ESM module that produces `single-spa` lifecycle functions to control the injection of CSS for us automatically when the project is built; CSS injection is not necessary while running micro-frontends in serve mode because *Vite* takes care of this for us.

After this, I experimented a little bit with *Vite + Vue* router-enabled projects. The project created using `npm create vue@latest` comes with dynamic imports for *Vue* components. This causes CSS splitting when the project is built by *Vite*. To account for this, `vite-plugin-single-spa` **v0.2.0** adds a `projectId` property to its list of options. This identifier is added to all CSS asset names created by *Vite* during the building process. Then, the dynamic module uses this identifier to enable and disable the CSS link elements when the micro-frontend is mounted and unmounted.

More testing is needed for this feature, especially with other technologies such as *React Router*. However, it looks promising according to my primary objective's project: Routing seems to be working just fine in my *React* projects, which was another secondary objective. However, my project's projects don't split CSS.

If you are in the routing and CSS splitting business using `vite-plugin-single-spa`, make sure you [report any issues](https://github.com/WJSoftware/vite-plugin-single-spa/issues) you find along the way so we can all enjoy a quality plug-in.

## Asset-Serving in Serve Mode

Great news! As it turned out, *Vite* is (and has always been) able to serve assets in serve mode. I was simply unaware of the correct setting. As of **v0.2.0**, `vite-plugin-single-spa` sets the `server.origin` property so images and other assets coming from micro-frontends are properly served while in serve mode.

## Improving Import Mapping

While experimenting with *Vue* micro-frontends and a *Vue* root project, I decided to explore the route of shared libraries. The `single-spa` documentation recommends that we only import large libraries like *React* and *Vue* as shared libraries.

One great way to do this is to import these libraries from *unpkg.com* or a similar CDN. Combine this with *Vite*'s (or really, *rollup*'s) ability to exclude libraries from the project's build and we have a solution.

As of **v0.3.1** (v0.3.0 had a major flaw) of `vite-plugin-single-spa`, the options `importMap.dev` and `importMap.build` have been redefined: These can now take an array of import map file names instead of a single file name. This allows us developers to create a third file (or more if deemed appropriate) to cater to shared libraries.

Because this is something new to this article series, allow me to pose a quick example:

Add the file `src/importMap.shared.json` to a root project. Declare in this file the URL's for things like *Vue* or *React* libraries.

```json
{
    "imports": {
        "vue": "https://cdn.jsdelivr.net/npm/vue@3.3.8/+esm",
        "vue-router": "https://cdn.jsdelivr.net/npm/vue-router@4.2.5/+esm"
    }
}
```

This import map uses the *JSDelivr* network to download both `vue` and `vue-router`. We can now use this file in both development and build modes to mount built *Vue* micro-frontends. Why not to mount `Vue` micro-frontends not built? Well, because *Vite* server will always try to serve the `vue` and `vue-router` modules. The import maps will only be in effect when mounting built micro-frontends. When mounting micro-frontends running in serve mode, just have those NPM packages installed as dev dependencies.

Now `vite.config.ts` for the root project would look like this (for a *Svelte* root project):

```typescript
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import vitePluginSingleSpa from 'vite-plugin-single-spa';

export default defineConfig({
  plugins: [svelte(), vitePluginSingleSpa({
    type: 'root',
    imo: '3.1.0',
    importMaps: {
      dev: ['src/importMap.Dev.json', 'src/importMap.shared.json'],
      build: ['src/ImportMap.json', 'src/importMap.shared.json']
    }
  })]
});
```

We are importing the shared JSON import map in both cases, without having to repeat the import maps in two different files. this is our gain.

---

Ok, folks, this has brought us up-to-date in the `vite-plugin-single-spa` topic as of today. However, there's more coming!

Our next topic will be `single-spa` parcels and their unique case regarding CSS mounting. Spoiler: This requires a new version of `vite-plugin-single-spa` which is currently in the making! The next article will explain my findings, as per usual, and how the plug-in could or would tackle the problem.

In the meantime, happy coding!