In my current project, we are have a large Vue 2 frontend set up as a monorepo with lots of subpackages that (roughly) correspond to the views of our app.
- project_folder
|- package.json
|- tsconfig.json
|- ...
|- packages
|- viewA
|- package.json
|- tsconfig.json (extends ../../tsconfig.json)
|- viewB
|- package.json
|- tsconfig.json (extends ../../tsconfig.json)
|- ...
|- core
|- tsconfig.json (extends ../../tsconfig.json, sets `paths`)
|
| (contains webpack config, configures module federation, exposes ../federation/urqlClient.ts)
|- vue.config.js
|- federation
|- tsconfig.json (extends ../../tsconfig.json, tries to overwrite `paths` to remove it)
|- urqlClient.ts (wrapper around urql, provides a common import location for all micro-fes)
We are now migrating this project to Vue 3 and a microfrontend architecture using Webpack Module Federation. This works fine. We can take a single view, put it into it's own repo and rebuild it with Vue 3.
This leads to a lot of duplicated dependencies. It also means that each view bundles and uses its own instances of dependencies. For example, we are using the urql GraphQL client, which has a neat automatic cache. But that's reset each time we navigate to another micro-fe, because each uses it's own urql instance (and existing ones are discarded as the micro-fe is destroyed).
Webpack Module Federation would allow us to share the urql dependency, leading to smaller bundles and intact caching.
I have successfully built this, by hosting dependencies within their own "remote" (a separate endpoint).
We are now looking to include shared dependencies directly in the shell. This makes sense conceptually and allows us to load dependencies eagerly (no async import, I hope).
The problem: These dependencies in turn have peer dependencies, notably Vue 3. But the shell itself remains powered by Vue 2 during the migration period. Since there is no way to explicitly import { x } from "vue@2"
, we need (dirty?!) hacks to make this work. Here's how it works (at runtime):
- Install Vue 2 under an alias:
dependencies.vue2: "npm:vue@2"
- Install Vue 3
- Use a webpack alias to map imports of "vue" to "vue2" (makes the shell work)
- Use the TS
paths
config to remap imports of "vue" to "vue2" on the type level - Set "vue" as a shared dependency for Webpack Module Federation, which (interestingly) ignores the alias and does resolve "vue" which is Vue 3.
This setup actually works. The project builds, micro frontends get to share the urql instance (expose
d by the shell) and everybody is happy.
Everybody except the typescript compiler when it tries to process the urql dependency which depends on Vue 3 but also imports from "vue", which TS now thinks is Vue 2. So even though everything is in fact working fine, TS gives me 48 errors that occur only because I am unable to teach it to use a different paths
config for the one subpackage that contains the federated, Vue 3 dependent, code. Because the tsconfig.json
in that package is not consulted when compiled through webpack.
If you need further information about our setup, please ask. It's hard to compile this down to (only) the relevant information.