Skip to main content
Version: 2.x

Reuse third party libraries

Disclaimer

For the time being, this feature is applicable only to web components (i.e., layout and application of type compose). In the future it will be extended also to parcels.

Encapsulation and complete separation among components/applications may lead to bundle same third-party dependencies multiple times. For instance, you might use the same framework to build your reusable components, and then compose your pages with components that belongs to different libraries.

Import maps

To mitigate this issue, micro-lc ships with native support for import maps.

Since at present time neither Firefox nor Safari support import maps, micro-lc embeds the es-module-shims import maps polyfill. Any composable content of micro-lc can leverage the import maps technique utility.

Suppose you want to use two web components form two different libraries, both having a direct dependency from RxJS.

my-awesome-web-component-1.ts
import type { MicrolcApi } from '@micro-lc/orchestrator'
import { filter } from 'rxjs'

class MyAwesomeWebComponent extends HTMLElement {
microlcApi?: MicrolcApi

connectedCallback () {
this.microlcApi?.currentApplication$
.pipe(filter(id => id === 'home'))
.subscribe(() => console.log('We are at home!'))
}
}

customElements.define('my-awesome-web-component', MyAwesomeWebComponent)

If you bundle the dependency in the libraries, RxJS will be downloaded twice when visiting a page using both the components. However, if you bundle a version of the libraries leaving the import as a bare module import (i.e., without including it in the final bundle), you can leverage import maps functionality to download the shared dependency just once.

tip

Excluding modules at compile time while crafting your bundle is an option available to most JavaScript bundlers (e.g., Rollup). When employed, it requires the browser who runs it to properly interpret the import as a bare ES6 module import.

A bare module import
import { filter } from 'rxjs'

Otherwise, the browser will fire an error like:

Error: Unable to resolve specifier 'rxjs'

To learn more about the topic, visit ES Module Shims repository.

Usage

Import maps can be declared in different sections of micro-lc configuration depending on their scope.

If you want them to be accessible by the whole application (i.e., both layout and all mounted micro-frontends), you can use top-level importmap configuration key.

Example
micro-lc.config.yaml
importmap:
imports:
react: https://esm.sh/react@next
react-dom: https://esm.sh/react-dom@next
scopes:
https://esm.sh/react-dom@next:
/client: https://esm.sh/react-dom@next/client

On the contrary, if you want to narrow their scope, you need to use key sources.importmap when defining the configuration of a layout, a composable application or a custom composable error page.

Example
micro-lc.config.yaml
# 👇 Custom error pages
settings:
4xx:
404:
integrationMode: compose
config:
sources:
uris: https://my-static-server/my-web-component.js
importmap:
imports:
react: https://esm.sh/react@next
react-dom: https://esm.sh/react-dom@next
scopes:
https://esm.sh/react-dom@next:
/client: https://esm.sh/react-dom@next/client
content:
tag: my-web-component

# 👇 Layout
layout:
sources:
uris: https://my-static-server/my-web-component.js
importmap:
imports:
react: https://esm.sh/react@next
react-dom: https://esm.sh/react-dom@next
scopes:
https://esm.sh/react-dom@next:
/client: https://esm.sh/react-dom@next/client
content:
tag: my-web-component

# 👇 Composable applications
applications:
- compose:
integrationMode: compose
route: ./compose
config:
sources:
uris: https://my-static-server/my-web-component.js
importmap:
imports:
react: https://esm.sh/react@next
react-dom: https://esm.sh/react-dom@next
scopes:
https://esm.sh/react-dom@next:
/client: https://esm.sh/react-dom@next/client
content:
tag: my-web-component

Regardless of where import maps are declared, their configuration is an object of the following shape:

interface ImportMap {
imports?: Record<string, string>
scopes?: Record<string, Record<string, string>>
}

Key imports allows control over what URLs get fetched by JavaScript import statements and import() expressions. It is an object mapping modules to URLs from which they can be fetched.

importmap:
imports:
react: https://esm.sh/react@next

It is often the case that you want to use the same import specifier to refer to multiple versions of a single library, depending on who is importing them. This encapsulates the versions of each dependency in use, and avoids dependency hell. This use case is supported through key scopes which allows you to change the meaning of a specifier within a given scope.

scopes:
https://esm.sh/react-dom@next:
/client: https://esm.sh/react-dom@next/client