Select to view content in your preferred language

Problem using ES modules in webpack module federation remote entry

3256
6
12-27-2021 10:14 PM
matuskajba
Emerging Contributor

I am having serious trouble running angular application that uses webpack module federation remote entries with esri JS api ES modules.

The idea is that there is a shell application that consumes remote entries, shared library that provides some business logic and is used by multiple components, and finally there is plugins application that provides some remote entries (plugins) for shell. In shared library there is service for creating a map view. In plugins application there is map-component (plugin) that provides UI components for created map view (home, scale, locate).

When I run the plugins application on its own everything works as expected (home, scale, locate).

But when I consume map-component from remote entry in shell app, nothing works at all (scale is frozen, locate zooms to other place and home does nothing) and console is filled with errors like this one:

„Logger.js:5 [esri.core.Accessor] Accessor#set Assigning an instance of 'esri.geometry.SpatialReference' which is not a subclass of 'esri.geometry.SpatialReference“

The extent created after clicking on home button seems all right exanimating it in debugger, but when used in mapView.goTo, it does nothing.

The app is using esri JS api via ES modules from npm package @arcgis/core.  

Fully reproducible example with readme.me to reproduce issues can be found here:

https://github.com/bigend/esri-webpack-module-federation-issue

If anyone could just shed some light on this, or at least explain to me what that error is supposed to mean, I would be really very grateful.

0 Kudos
6 Replies
ReneRubalcava
Honored Contributor

This is odd. That error means the instance of the SpatialReference is not from the same install of the API as the rest of the classes. This can happen when you mix CDN API classes with local ESM API classes for instance. I'm able to build your app and see the error, but in my case, the map and buttons still all work. You don't seem to use arcgis/core in the shell, just the component stuff.

This error could happen if your shared component, at host :3001 uses the API, then your app at host :3002 use the API. Even though these are the same version of the API, they come from different sources via the ports, so they don't match up.

I've done some work with webpack module federation before, but not with Angulars tooling for it. If you are not loading API code from your main app shell, and only the component, it might be with the angular/webpack tooling.

I made a vanilla version of your map service and home button here.

https://github.com/odoe/arcgis-jsapi-federated

I don't see the same error though.

0 Kudos
matuskajba
Emerging Contributor

Thank you very much for the reply.

Can you just please confirm, that “but in my case, the map and buttons still all work“. You mean buttons in app on port 4200 (shell app)? Because when I run it, the scale bar is not changing values when you zoom the map, locate button drives you to some nonsense location and doesn’t show a point graphic on the map, and the home button does nothing at all. In app on port 4201 (plugins app) everything works as expected. But if it works for you same way in both apps, that would really be a breakthrough, because I have spent eternity with it and couldn’t make it work.

You are right, that I am not using arcgis/core in the shell app, just in the plugin app. But I use „shared“ library in both, and this shared library uses arcgis/core. Could this be an issue?

You also mentioned that api on remote entry point and the shell app may not match because their source has different port and the versions don’t match up. However I could not confirm that this is the issue, because I have made builds in a way, that both apps are in same port (different folder) and after deploying it to the web server I got exactly the same error. You can check it, I have added those builds to the repo.

But if different ports can cause such a trouble, is even module federation with arcgis/core possible in a way that your shell app uses arcgis/core as well, or you have multiple remote entries that uses arcgis/core on different ports?

I have checked you repo, but that case is much simpler, it doesn’t have shared library, and it seems that you don’t use arcgis/core in shell app as well. I agree with you though, that it may be an issue in Angular tooling for module federation, especially in that shared library mappings. And I would ask on that side for help, but there seems to be no issue with other libraries, and I am afraid that that mentioned error would make no sense to them.

Now I have clue, that it may be caused by version mismatch, but still the question is how is that mismatch determinated? Or maybe even what causes it in this case? Mixing CDN api with ESM api is out of question in this case I think, but if it may be caused with different ports, that already breaks the concept of module federation or microfrontends and I am already done with such an architecture :-(.

I would be really grateful, if you could help me to determine what causes this version mismatch in this case, because I think that understanding it may help to fix it.

Thank you.

0 Kudos
ReneRubalcava
Honored Contributor

Ah you're right. I was able to pan around and stuff, but the Locate breaks things. I still think it's weird that it breaks like in the shared lib, even though it's not instantiating classes in the main app.

As to the why...

This boils own the prototype chain. Most classes in the API are subclasses of the Accessor. So what happens in the case of federated modules, if the shell app creates a Graphic instance, and passes that to the shared lib, the Accessor in the chain is a different origin, which causes the error. Federation is working as expected, the shared lib has a build of the API, the shell has its own build, but the two don't share the same prototype chain.

My recommendations so far when using federated modules is to create a shared lib that exposes factory methods to do the API specific work, exposes components with the API, and the shell doesn't need to worry about instantiating API stuff. We are currently doing some internal testing in the area, but nothing concrete at the moment.

0 Kudos
matuskajba
Emerging Contributor

Thank you very much for clarification.

I have to admit, that I don't quite understand why the same origin is so important. I can imagine that the same version or build is important for objects compatibility, but I can't see an importance of the place from where the classes are loaded. But surely, I don't know what is behind the scenes, so it's not up to me to judge it.

Finally, I would like to suggest, that it may be a good idea to share this limitation in the API documentation. The module federation seems like hot topic these days, and I think it's very likely that other developers may stumble upon this and a note in documentation can spare them from some desperation. And of course it will be very interesting to read somewhere about your internal testing in this area.

One more time, thank you very much for you insight, I am really appreciating it.

0 Kudos
ReneRubalcava
Honored Contributor

I know it can seem a little odd. There are checks in Accessor for when properties are set where we do something like this; target instanceof Clazz. When the origin of the modules is different, this is false and fails. It's part of the internal API.

One solution would be to treat shared libs from the shell like workers. You can't pass class instances to workers, only data objects, like objects, arrays, buffers, etc. So you could use some of the toJSON() and fromJSON() methods of classes to pass data back and forth. But, I'll try to keep a mental note for this thread as we do more research in federated modules. Thanks!

0 Kudos
by Anonymous User
Not applicable

Hi there,

I am getting the same issues as this while working with federated modules and React. Any idea when this might be easily resolved out of the box?

My set up is:

  • main app that has components such as a custom sketch tool
    • custom sketch tool sets up a SketchViewModel and GraphicsLayer on mount and handles event listeners and so on. On complete or update of a sketch, an onComplete or onUpdate prop can be called, if passed into the React component.
  • plugin app that can render the custom sketch tool, and pass it props such as onComplete, onUpdate, various other settings

I get a lot of errors similar to the above, even though the plugin app never directly affects the sketch tool or the map, but I'm assuming it's because the custom sketch component is being rendered in a separate app and this a separate origin? 

Is there any way on setting up a MapView we can specify accepted origins or something that might get this working more easily?

 

Thanks in advance.

Tags (1)
0 Kudos