I have a simple application written in vanilla javascript and using Module Federation to wrap things up. So far, I've separated the javascript and the styling into two separate "apps":
├ src/
│ ├ lib/
│ │ └ myApp.js
│ ├ scss/
│ │ └ styles.scss
│ ├ index.js
│ └ styles.js
└ webpack.config.js
The index.js
imports myApp.js
that has all the logic and styles.js
simply imports a SASS-file with all necessary styling like this:
import './scss/signing-widget.scss';
The ModuleFederationPlugin
in webpack.config.js
is setup as follows:
module.exports = {
entry: {
index: ['./src/index.js'],
styles: ['./src/styles.js'],
},
...
plugins: [
new ModuleFederationPlugin({
name: 'myApp',
filename: 'remoteEntry.js',
exposes: [
'./myApp': './src/index.js'
'./myAppStyles': './src/styles.js'
],
shared: [
require('./package.json').dependencies
],
})
],
...
And to implement and use myApp
you need to do the following:
<html>
<head>
...
<script defer src="http://path-to-my-app/index.js"></script>
<script defer src="http://path-to-my-app/styles.css"></script>
<script defer src="http://path-to-my-app/remoteEntry.js"></script>
</head>
<body>
<my-app></my-app>
</body>
</html>
But, I only want to implement the app by only importing the remoteEntry.js
like this:
<html>
<head>
...
<script defer src="http://path-to-my-app/remoteEntry.js"></script>
</head>
<body>
<my-app></my-app>
</body>
</html>
But I can't figure out how to do it and I've done a lot of research but I haven't found any example nor documentation on how to achieve this with ModuleFederationPlugin
. Can someone help me on this matter?
Thanks in advance, Clydefrog
CodePudding user response:
I ended up making my own "mounting" script. I'll share my solution to anyone who find this interesting or experiencing the same problem.
├ src/
│ ├ lib/
│ │ └ myApp.js
│ ├ scss/
│ │ └ styles.scss
│ ├ index.js
│ ├ mount.js <------ NEW MOUNT
│ └ styles.js
└ webpack.config.js
Instead of manipulating or change the rmeoteEntry.js
to auto import modules, I created a simple javascript file (mount.js
) that detects its own script
-tag. Then it extracts the base URL and then iterates through each file that needs to get imported with the same base URL:
mount.js
(() => {
const parseUrlString = (url) => !url || url.length <= 0 ? '' : new URL(url).origin;
var startsWith = '^',
contains = '*',
endsWith = '$',
scriptElement = document.querySelector(`script[src${endsWith}="widgetMount.js"]`),
url = parseUrlString(scriptElement?.getAttribute('src')),
head = document.getElementsByTagName('head')[0];
[
'styles.css',
'index.js',
'remoteEntry.js',
].forEach((filename) => {
var newElement;
switch (filename.split('.')[1]) {
case 'css':
newElement = document.createElement('link');
newElement.setAttribute('rel', 'stylesheet');
newElement.href = `${url}/${filename}`;
break;
case 'js':
newElement = document.createElement('script');
newElement.setAttribute('defer', '');
// newElement.setAttribute('async', '');
newElement.type = 'module';
newElement.src = `${url}/${filename}`;
break;
}
head.appendChild(newElement);
});
})();
Adding it to webpack.config.js
in my remote application:
module.exports = {
entry: {
...,
widgetMount: ['./src/mount.js'], // widget mount
...
},
...
plugins: [
new ModuleFederationPlugin({
...,
exposes: [
...,
'./myMount': './src/mount.js'
...
],
...,
})
],
...
}
Last but not least, adding the mount into my hosting application:
<html>
<head>
...
<script defer src="http://path-to-my-app/widgetMount.js"></script>
</head>
<body>
<my-app></my-app>
</body>
</html>
And Voilà:
<html>
<head>
...
<script defer src="http://path-to-my-app/widgetMount.js"></script>
<link rel="stylesheet" href="http://path-to-my-app/styles.css">
<script defer src="http://path-to-my-app/index.js" type="module"></script>
<script defer src="http://path-to-my-app/remoteEntry.js" type="module"></script>
</head>
<body>
<my-app></my-app>
</body>
</html>