ModuleFederationPlugin's remote property syntax - webpack-module-federation

What does this ui: "ui#http://some.external.host/remoteEntry.js" syntax mean in ModuleFederationPlugin's remotes property.
I understand that ui item is being loaded from an external host, but what does ui# before host definition mean ?
new ModuleFederationPlugin({
name: "myApp",
filename: "myAppEntry.js",
remotes: {
ui: "ui#http://some.external.host/remoteEntry.js",
},
shared: {
...,
},
}),

You can break this line of the config ui: "ui#http://some.external.host/remoteEntry.js" into three parts: LocalModuleName: "RemoteModuleName#Host". Let's assume myApp and ui have the following webpack configs for module federation:
// Config for myApp
new ModuleFederationPlugin({
name: "myApp",
filename: "myAppEntry.js",
remotes: {
ui: "ui#http://some.external.host/remoteEntry.js",
},
shared: {...},
}),
// Config for ui
new ModuleFederationPlugin({
name: "ui",
filename: "remoteEntry.js",
exposes: {
"./Button": "./src/Button",
},
shared: {...},
}),
LocalModuleName is the name under which you can import exposed features from the remote app in the local code, e.g.:
const RemoteButton = React.lazy(() => import("ui/Button"));
But you could also change the name to remoteUI: "ui#http://some.external.host/remoteEntry.js" and the import would have to look like this:
const RemoteButton = React.lazy(() => import("remoteUI/Button"));
This could be useful if two different remotes used the same name in their config.
RemoteModuleName refers to the name used in the remote configuration. This is necessary, so ModuleFederation can properly initialize the modules.
Host is the URL under which you find the remote container script.

Related

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>;
};

How can i load changes to my code in a Vue app?

I deployed a Django+VueJS app that uses django webpack loader in order to render Vue apps in my Django templates. I used Nginx and Gunicorn to deploy the app to a DigitalOcean VPS, everything works without any problem but i have some doubts on how to edit my components in production, since i'm fairly new to Vue
Here is my vue.config:
const BundleTracker = require("webpack-bundle-tracker");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const pages = {
'main': {
entry: './src/main.js',
chunks: ['chunk-vendors']
},
}
module.exports = {
pages: pages,
runtimeCompiler: true,
filenameHashing: false,
productionSourceMap: false,
publicPath: process.env.NODE_ENV === 'production'
? 'static/vue'
: 'http://localhost:8080/',
outputDir: '../django_vue_mpa/static/vue/',
chainWebpack: config => {
config.optimization
.splitChunks({
cacheGroups: {
moment: {
test: /[\\/]node_modules[\\/]moment/,
name: "chunk-moment",
chunks: "all",
priority: 5
},
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "chunk-vendors",
chunks: "all",
priority: 1
},
},
});
Object.keys(pages).forEach(page => {
config.plugins.delete(`html-${page}`);
config.plugins.delete(`preload-${page}`);
config.plugins.delete(`prefetch-${page}`);
})
config
.plugin('BundleTracker')
.use(BundleTracker, [{filename: '../vue_frontend/webpack-stats.json'}]);
// Uncomment below to analyze bundle sizes
// config.plugin("BundleAnalyzerPlugin").use(BundleAnalyzerPlugin);
config.resolve.alias
.set('__STATIC__', 'static')
config.devServer
.public('http://localhost:8080')
.host('localhost')
.port(8080)
.hotOnly(true)
.watchOptions({poll: 1000})
.https(false)
.headers({"Access-Control-Allow-Origin": ["*"]})
}
};
So in order to deploy the Vue part i did npm run build and npm created a bunch of files in my static directory. Now, every time i edit a component, in order to see the changes on the web, i do npm run build every time, which takes some time. Is this how am i supposed to do it? Or is there a shorter way?
I don't know about django, But I know about vue..
is this how am I supposed to do it?
For me, I don't suggest it, you can use your django as a backend for your frontend
that should mean you would have 2 servers running. 1 for your django and 1 for your vue app. use XHR request to access your django App, remember to handle CORS. IMHO I don't want vue to be used as a component based framework.
is there a shorter way.
YES, and this is how you do it.
add to package.json
{
...,
scripts: {
...,
'watch' : 'vue-cli-service build --watch --inline-vue',
...,
}
}
while using the following settings in vue.config.js
module.exports = {
'publicPath': '/django/path/to/public/folder',
'outputDir': '../dist',
'filenameHashing': false,
runtimeCompiler: true,
'css': {
extract: true,
},
}
i forgot about how publicPath and outputDir works..
but you can check it out here https://cli.vuejs.org/config/#publicpath
regarding the code on the package.json file..
you can check it here
https://github.com/vuejs/vue-cli/issues/1120#issuecomment-380902334

URL management with Django, GraphQL, Apollo and VueJS

As said in the title, I'm using Django, GraphQL, Apollo and VueJS in my project.
I'm developping it as a SPA (Single Page Application).
Everything works fine, until I hit the F5 button and refresh the page. Indeed, it shows an unknown page. The thing is it is VueRouter that is managing the SPA and it works fine. But when I press F5, that is Django that tries to serve a page for the current URL and since it doesn't know it, it can't serve the appropriate page.
I know I can set the VueRouter 'history' mode, which I did, and add a URL to Django that serves index.html whatever the URL is.
My problem is the following :
When I'm on a particular form view (i.e : a User form view) my URL is the following :
http://localhost:8000/user
Since I'm using GraphQL for my API, the retrieved data is not based on the URL. In fact, that is my VueJS component that says : Hey Apollo, run that GraphQL to retrieve the User I want.
So when I refresh, yes it serves the User form view..but empty.
The question is : How could I solve this ?
For clarification purposes, here are some code samples :
My Django URLs :
# (... other imports here ...)
from .schema import schema
urlpatterns = [
path('admin/', admin.site.urls),
path('graphql', csrf_exempt(GraphQLView.as_view(graphiql=True, schema=schema))), # 'schema' is the main GraphQL schema
path('', TemplateView.as_view(template_name='index.html')),
re_path(r'^.*$', TemplateView.as_view(template_name='index.html')) # I saw that many times to serve the page whatever the URL is when refreshing the page
]
My Vue Router :
export default new Router({
mode: 'history',
routes: [
{ path: '/', name: 'MainApp' },
// ...
{ path: '/users', name: 'UserList', component: UserList },
{ path: '/user/create', name: 'UserFormCreate', component: UserForm, props: true },
{ path: '/user', name: 'UserFormView', component: UserForm, props: true },
{ path: '/user/edit', name: 'UserFormEdit', component: UserForm, props: true },
// Same pattern for other models like 'Group' ...
]
My Example VueJS Component :
<script>
import {
// ...
USER_QUERY,
// ...
} from '../../graphql/base/user.js'
export default {
name: 'UserForm',
props: {
userId: Number,
editing: {
type: Boolean,
default: false
}
},
apollo: {
user: {
query: USER_QUERY,
variables () { return { id: this.userId } },
skip () { return this.userId === undefined },
result ({ data }) {
this.form.username = data.user.username
this.form.firstName = data.user.firstName
this.form.lastName = data.user.lastName
}
}
},
data () {
return {
form: {
username: '',
password: '',
firstName: '',
lastName: ''
},
// ...
}
},
methods: {
// ...
}
I have to mention that I've seen more or less related topics but that doesn't solve my problem.
Thanks in advance for your help !
Edit your route paths to use params. For example:
{ path: '/user/:userId', name: 'UserFormView', component: UserForm, props: true }
Now, the app will interpret any number following the user/ path as a prop called userId. (props: true is important here for using the params as props.)
The only other change you need to make is adjusting your router-links to include the id as well (Ex.: http://localhost:8000/user/1) so that when the page is refreshed, there will be a param to read.

Access window object in ember-cli environment

Hi I am trying to use torii in a cordova application. My environment.js file looks as below. I an not able to access window document object to setup redirectUri. getting error undefined variable. how can I access window document object.
module.exports = function (environment) {
var ENV = {
environment: environment,
baseURL: '/',
locationType: 'hash',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. 'with-controller': true
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
},
torii: {
providers: {
'facebook-oauth2': {
apiKey: '2xxxxxxxxxx',
redirectUri: document.location.href
},
}
},
cordova: {
rebuildOnChange: false,
rebuildAsync: false,
emulate: false
}
};
in my .jshintrc
"predef": {
"document": true,
"window": true,
"AuthENV": true
}
so I assume document should be globally available but it is not
You are able to access the window and document object through most of your Ember.js code as global variables.
In this particular file, you are trying to access it in use for the app config. The problem is that the config is generated during the Node.js build process - meaning that the context is not the same.
You will see in the page source of your app that the config is static and serialized into a meta property in the page:
<meta name="[your-app]/config/environment" content="your-config-here" />
As the redirect url will change dynamically depending on the user's location, it would be easier to pull in this data "just in time" for the OAuth flow.

Why is my intern test failing with "document is not defined"

I am new to Intern and struggling with trying to get a simple test to run in my environment. I was able to get the tutorial test to run but I've tried to set up a test where the test file is located inside my app directory hierarchy. The module being tested is located here:
sandbox/web/libs/ev/grids/FilterGrid.js
The test file is located here:
sandbox/web/libs/ev/tests/FilterGrid.js
My intern config file is located here:
sandbox/tests/intern.js
My loader and suites objects looks like this:
loader: {
packages: [
{ name: 'dojo', location: 'web/libs/dojo' },
{ name: 'dijit', location: 'web/libs/dijit },
{ name: 'dgrid', location: 'web/libs/dgrid' },
{ name: 'put-selector', location: 'web/libs/put-selector' },
{ name: 'xstyle', location: 'web/libs/xstyle' },
{ name: 'ev', location: 'web/libs/ev' }
]
},
suites: ['ev/tests/FilterGrid'],
When the loader tries to load this, I get:
Defaulting to "console" reporter
ReferenceError: document is not defined
at /home/bholm/Projects/src/sandbox/web/libs/dojo/selector/_loader.js:5:15
at execModule (/home/bholm/Projects/src/sandbox/node_modules/intern/node_module
/dojo/dojo.js:512:54)
at /home/bholm/Projects/src/sandbox/node_modules/intern/node_modules/dojo/dojo.js:579:7
at guardCheckComplete (/home/bholm/Projects/src/sandbox/node_modules/intern/node_modules/dojo/dojo.js:563:4)
at checkComplete (/home/bholm/Projects/src/sandbox/node_modules/intern/node_modules/dojo/dojo.js:571:27)
at onLoadCallback (/home/bholm/Projects/src/sandbox/node_modules/intern/node_modules/dojo/dojo.js:653:7)
at /home/bholm/Projects/src/sandbox/node_modules/intern/node_modules/dojo/dojo.js:746:5
at fs.js:266:14
at Object.oncomplete (fs.js:107:15)
Does the unit tests using Intern need a DOM document defined??
I also notice that Intern lists dojo2_core as it's dependency. So it's using unreleased code?
Any help with this would be appreciated!
It looks like you’re trying to load some code using the Node.js client that requires a browser environment. This won’t work. You should only load the ev/tests/FilterGrid test suite in a browser. You can do this by modifying your Intern configuration file to look something like this:
define([ 'intern/node_modules/dojo/has' ], function (has) {
var suites = [];
if (has('host-browser')) {
suites.push('ev/tests/FilterGrid');
}
return {
// ...your existing configuration...
suites: suites,
// ...
};
});