How to share assets [css,images,...] in module federation with react - webpack-5

Basically I want to find a way to share example some css file or image using module federation in my case I want to use react.
I did some research by I don't find a solid information abou that.
Some recomendations ?

One solution that I found was using css-module, you can use webpack or browserify in any case your css will be transform in js and you can expose it and consume it like other object/component,...
Eg:
[ Home Content Component]
import styles from './exampleCss.module.scss'; // using css module
export const globalHomeStyle = styles; //exporting the new transformed js.
webpack.config.js
...
plugins: [
new ModuleFederationPlugin({
name: 'home',
filename: 'remoteEntry.js',
remotes: {
...
},
exposes: {
'./HomeContent': './src/HomeContent.jsx',
}
Consuming the style from Home microfrontend in PDP Microfrontend:
webpack.config.js //calling the microfrontend
...
plugins: [
new ModuleFederationPlugin({
name: 'home',
filename: 'remoteEntry.js',
remotes: {
home: 'home#http://localhost:3000/remoteEntry.js',
},
exposes: {
...
}
Using the Style in PDP Microfrontend:
import { globalHomeStyle } from 'home/HomeContent';
...rest implementation
<div className='flex'>
<div className={`font-bold text-3x1 flex-grow ${globalHomeStyle.exampleCss}`}>
I hope this can be useful, if you find other solutions don't be shy add them here 👍

Related

Unable to instrument expo app with istanbul or cypress/instrument

I have been trying this for a while now, without success.
I have an expo app and I am using cypress to test some use cases using the web version of the app (generated by expo).
However, I would like to generate some code coverage as well. I have read the official documentation for it, that recommends to babel-plugin-istanbul do to so, but it does not seem to work with expo.
I am not sure why this would not work.
Edit
I removed the files previously pointed here as they were not important.
Thanks for a wonderful documentation provided by a hero without a cape, I figured that my settings were wrong.
All I needed to do was:
install babel-plugin-istanbul
Update babel.config.js
module.exports = {
presets: ['babel-preset-expo'],
plugins: [
'istanbul',
[
'module-resolver',
{
root: ['.'],
extensions: [
'.js',
],
alias: {
'#': './',
},
},
],
],
};
update cypress/support/index.js
import '#cypress/code-coverage/support';
update cypress/plugins/index.js
module.exports = (on, config) => {
require('#cypress/code-coverage/task')(on, config);
// add other tasks to be registered here
// IMPORTANT to return the config object
// with the any changed environment variables
return config;
};
and voilà!

Loading remote Angular MFE into React shell using Webpack's Module federation

Can some one help me with an example to load remote Angular micro frontend application into React Shell using webpack's Module federation concept?
I have checked https://www.angulararchitects.io/en/aktuelles/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly/ where React is loaded in angular. But I am looking for other way.
With webpack, you can put the bootstrapping of angular app in a mount method and export this method. This must be done in another file to avoid conflicts for angular to run independently.
(Make sure to have the bootstrap.js file imported in main.ts, as used by module federation)
remote/src/load.ts
const mount = ()=>{
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
export{mount}
expose this new file with webpack from angular
remote/webpack.config.ts
output: {
scriptType: 'text/javascript',
},
plugins: [
new ModuleFederationPlugin({
name: "remoteMfe",
filename: "remoteEntry.js",
exposes: {
'./webcomponent':'./src/loadApp.ts',
},
})
load the exposed module in react app using webpack
host/webpack.config.js
plugins: [
new ModuleFederationPlugin(
{
name: 'host',
filename:
'remoteEntry.js',
remotes: {
RemoteMFE:
'remoteMfe#http://localhost:3000/remoteEntry.js',
},
}
),
import the mount method that was exposed from angular remote and get the root element(i.e. ) from angular mfe. This can be used as a regular DOM element
host/src/Example.js
const ExampleComponent = () => {
useEffect(() => {
mount();
}, []);
return <div className="left-sidebar-module"><app-root></app-root></div>;
};

My CSS made with Tailwind doesn't work on build with gridsome for netlify

When I build (netflify build) my Gridsome personal website, tailwind CSS classes doesn't work and the website look's like without CSS.
I have already tried to build without git, reinstall tailwind...
I show my gridsome config if that's the problem:
const tailwind = require('tailwindcss');
const purgecss = require('#fullhuman/postcss-purgecss');
const postcssPlugins = [
tailwind(),
]
if (process.env.NODE_ENV === 'production') postcssPlugins.push(purgecss());
module.exports = {
siteName: 'Zolder | Works',
plugins: [],
css: {
loaderOptions: {
postcss: {
plugins: postcssPlugins,
},
},
},
}
I had this issue as well. I used the Tailwind Plugin for Gridsome and it worked locally but when deploying to Netlify, the Tailwind css wasn't getting added.
Referencing this starter file: https://github.com/drehimself/gridsome-portfolio-starter/blob/master/src/layouts/Default.vue
I added the main.css with all the Tailwind imports file to the end of the Default Layout template instead, and this worked for me.
You can add Tailwind to your Gridsome project with these steps:
edit gridsome.config.js
module.exports = {
siteName: "Zolder",
plugins: [],
chainWebpack: config => {
config.module
.rule("postcss-loader")
.test(/.css$/)
.use(["tailwindcss", "autoprefixer"])
.loader("postcss-loader");
}
};
create a global.css file in ./src/styles
#tailwind base;
#tailwind components;
#tailwind utilities;
import global.css in main.js
import './styles/global.css'

Webpack 4 - Migrating from CommonsChunkPlugin to SplitChunksPlugin

We have a traditional server rendered application (non SPA) where each page is augmented with vuejs
Our existing webpack 3 configuration is
webpack.config.js
var webpack = require('webpack')
var path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
entry: {
shared: './shared.js',
pageA: './pageA.js',
// pageB: './pageB.js',
// pageC: './pageC.js',
// etc
},
resolve: {
alias: { vue: 'vue/dist/vue.esm.js' },
},
output: {
path: path.join(__dirname, './dist'),
filename: '[name].js',
},
module: {
rules: [
{
test: /\.css$/,
exclude: /node_modules/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
query: {
sourceMap: true,
},
},
],
}),
},
],
},
plugins: [
new CleanWebpackPlugin('./dist'),
new webpack.optimize.CommonsChunkPlugin({
name: ['shared'],
minChunks: Infinity,
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime',
}),
new ExtractTextPlugin('[name].css'),
new CopyWebpackPlugin([{ from: 'index.html', to: '.' }]),
],
}
shared.js
// import shared dependencies & pollyfills
var vue = require('vue')
// import global site css file
require('./shared.css')
// initialize global defaults
// vue.setDefaults(...)
console.log('shared', { vue })
pageA.js
var vue = require('vue')
// only this page uses axios
var axios = require('axios')
console.log('pageA', { vue, axios })
shared.css
body {
background-color: aquamarine;
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<!-- included on every page-->
<link rel="stylesheet" href="shared.css">
</head>
<body>
<!-- included on every page-->
<script src="runtime.js"></script>
<script src="shared.js"></script>
<script src="pageA.js"></script>
</body>
</html>
With this setup
1) runtime.js contains the webpack loader, so any changes to shared.js don't cause pageA.js to be cache busted and vice versa
2) shared.js contains any shared dependencies (in this case vue) as well as any shared global initializion for every page (setting vue defaults etc). It is also the point we import our shared global css file.
3) pageA.js does not contain any dependencies imported in shared.js (vue in this case) but does contain dependicies it imports (axios in this case).
We have been unable to reproduce this setup using the SplitChunksPlugin
1) SplitChunksPlugin doesn't seem to allow an entry point as a split point.
2) All the examples have split out ALL node module dependices into a vendor chunk. This doesn't work for us as we have 100's of pages but only a few import a graphing library or moment etc... We don't want to have this graphing library or moment included in shared.js as it will then be load for all pages.
3) It wasn't clear how to split the runtime into its own file either
SplitChunksPlugin seems to be targeted at SPA's where javascript can be loaded on demand. Is the scenario we are trageting still supported?
Are you trying to migrate to webpack 4?
I find the optimisation cacheGroups test option works well to be specific about what goes where.
optimization: {
splitChunks: {
cacheGroups: {
shared: {
test: /node_modules[\\/](?!axios)/,
name: "shared",
enforce: true,
chunks: "all"
}
}
}
}
Will load everything from the node modules (except axios) and should therefore be included as part of your page entry point.
If you want webpack to chunk some component, you will need to import it asynchronously from your main entry file.
I have been using bundle-loader to do it, then I have:
In my webpack.config.js
optimization: {
splitChunks: {
chunks: 'all'
},
mergeDuplicateChunks: true,
}
module: {
rules: [
{
test: /\.bundle\.js$/, //yes my output file contains the bundle in its name
use: {
loader: 'bundle-loader', options: {lazy: true}
}
}
]
}
In my entry file.
//this code will replace the line where you are importing this component
let Login;
// this method will go inside your component
componentWillMount() {
require("bundle-loader!./ui/Login.jsx")((loginFile) => {
Login = loginFile.default;
this.setState({ loginLoaded: true });
});
}
If you don't want to use it, there are more ways of importing your file async.

Ember 1.10+grunt+EAK: "Could not find <template_name> template or view" after migration from 1.9.1

I'm using Ember App Kit with grunt and I'm trying to switch to Ember 1.10 and can't get HTMLBars working :/
TL;DR
After migration, I've got my HTMLBars templates lodaded in Ember.TEMPLATES but they're not visible either by Ember nor in App.__container.lookup.cache.
Details
The steps I did:
updated ember and ember-data
updated package.json ("grunt-ember-templates": "0.5.0")
updated my Gruntfile.js (grunt.loadNpmTasks('grunt-ember-templates') added a task emberTemplates)
passed the options to emberTemplates:
{
debug: [],
options: {
templateCompilerPath: 'vendor/ember/ember-template-compiler.js',
handlebarsPath: 'vendor/handlebars/handlebars.js',
templateNamespace: 'HTMLBars'
},
'public/assets/templates.js': [
'app/templates/**/*.hbs'
],
};
removed handlebars.js from index.html and replaced ember.js with ember.debug.js
Now, I've got my public/assets/templates.js file generated in a proper way, I had several compilation errors coming from ember-template-compiler, so this part, I assume, is working fine.
Lastly, in the app, I can see all my templates loaded in Ember.TEMPLATES variable but unfortunately, they're not accessible from App.__container__.lookup.cache or App.__container__.lookup('template:<template_name>').
The way I'm trying to render the template that throws an error is (and it's working with Ember 1.9):
export default AuthRoute.extend({
renderTemplate: function() {
this.render();
this.render('user-details', {
into: 'base',
outlet: 'profile',
controller: 'user-details'
});
}
});
What am I missing? Any help would be appreciated.
Bonus question: what is debug field in emberTemplates configuration? If I don't define it, it raises an error (Required config property "emberTemplates.debug" missing.) while compiling. Could that be a possible reason?
Bonus question 2: where should templates.js file go? The intuition tells me /tmp but then, even Ember.TEMPLATES is an empty object...
EDIT [SOLUTION]:
I missed templateBasePath: "app/templates" line in the emberTemplates options. Because of that, Ember.TEMPLATES object was sth similar to this:
{
"app/templates/base.hbs": {},
"app/templates/components/component.hbs": {}
}
instead of:
{
"base.hbs": {},
"components/component.hbs": {}
}
which is the format that Ember resolver (ember-application/system/resolver) in the resolveTemplate method expects.
EDIT: using grunt-ember-templates and this Gruntfile task, I got it working:
emberTemplates: {
options: {
precompile: true,
templateBasePath: "templates",
handlebarsPath: "node_modules/handlebars/dist/handlebars.js",
templateCompilerPath: "bower_components/ember/ember-template-compiler.js"
},
"dist/js/templates.js": ["templates/**/*.hbs"]
}
Differences seem to be precompile: true and point the handlebarsPath to the dependency in node_modules. Also the templateBasePath makes the ids like application instead of templates/application. Or in your case app/templates/application.
To answer your Bonus question 2, put templates.js after you load ember.js but before your app.js. Mine script includes look like this:
<script type="text/javascript" src="/bower_components/ember/ember.debug.js"></script>
<script type="text/javascript" src="/bower_components/ember/ember-template-compiler.js"></script>
<script type="text/javascript" src="/js/templates.js"></script>
<script type="text/javascript" src="/js/app.js"></script>
====================================
EDIT: Ignore this newbness...
It seems like the grunt-ember-templates task is outdated, or its dependencies are outdated. Remove it. I was able to hack together this solution:
Use grunt-contrib-concat instead. The money is with the process option.
concat: {
dist: {
// other concat tasks...
},
templates: {
options: {
banner: '',
process: function(src, filepath) {
var name = filepath.replace('app/templates/','').replace('.hbs','');
var Map = {
10: "n",
13: "r",
39: "'",
34: '"',
92: "\\"
};
src = '"' + src.replace(/[\n\r\"\\]/g, function(m) {
return "\\" + Map[m.charCodeAt(0)]
}) + '"';
return 'Ember.TEMPLATES["'+name+'"] = Ember.HTMLBars.template(Ember.HTMLBars.compile('+src+'));\n';
}
},
files: {
'public/assets/templates.js': 'app/templates/**/*.hbs'
}
}
},
So the whole solution is as follows:
module.exports = {
debug: {
src: "app/templates/**/*.{hbs,hjs,handlebars}",
dest: "tmp/result/assets/templates.js"
},
dist: {
src: "<%= emberTemplates.debug.src %>",
dest: "<%= emberTemplates.debug.dest %>"
},
options: {
templateCompilerPath: 'vendor/ember/ember-template-compiler.js',
handlebarsPath: 'vendor/handlebars/handlebars.js',
templateNamespace: 'HTMLBars',
templateBasePath: "app/templates"
}
};
where all my templates reside in app/templates/ directory.
I'm still using:
<script src="/assets/templates.js"></script>
in index.html.
Maybe somebody will find it useful ;)