maplibre-gl-js icon indicating copy to clipboard operation
maplibre-gl-js copied to clipboard

Install RTL plugin "locally" from node_modules

Open neodescis opened this issue 1 year ago • 9 comments

User Story

Currently the RTL text plugin is provided to MapLibre as a URL. Depending on the environment, this can be cumbersome. It would be much more convenient if the plugin could simply be installed into a project with npm and provided directly.

Rationale

Many of my projects run in a disconnected environment. Downloading the RTL plugin on-the-fly from a CDN is not an option. One possible solution is to serve the file locally as part of the web application, but this results in an extra HTTP request, and can be less than obvious to get working in some build environments. In Angular, for example, the file needs to be included in the application's "assets" for this to work. This is further complicated if building an Angular library that provides a map component that uses MapLibre, as the consuming application must then have a similar "assets" setup as well. Another option is to copy the file into a string and create a blob URL from that. This is the solution I'm using presently, but it is less than ideal for other (hopefully obvious) reasons. It would be nice to at least have the option of including the plugin directly from an import and using it from there.

Impact

In my opinion, the current method of providing the RTL plugin is convoluted. Allowing it to be provided directly would make it much easier to drop in the RTL plugin.

neodescis avatar Oct 13 '23 23:10 neodescis

Sounds like a good idea to me @neodescis. If this should require creating a new repo in the MapLibre github organization, then the main question for me would be if you would be willing to maintain it?

wipfli avatar Oct 14 '23 15:10 wipfli

This is generally possible I believe, I'm doing it already: https://github.com/IsraelHikingMap/Site/blob/645b0544889446f35de97a0fb30a89c60ec9c4aa/IsraelHiking.Web/src/application/components/map/main-map.component.ts#L113

HarelM avatar Oct 14 '23 15:10 HarelM

This is generally possible I believe, I'm doing it already: https://github.com/IsraelHikingMap/Site/blob/645b0544889446f35de97a0fb30a89c60ec9c4aa/IsraelHiking.Web/src/application/components/map/main-map.component.ts#L113

Yes, this is one of the alternatives I mentioned above. For that solution to work, you have to deliver the .js file as part of the consuming application. There's a glob in angular.json' assets section to do so.

neodescis avatar Oct 15 '23 16:10 neodescis

Sounds like a good idea to me @neodescis. If this should require creating a new repo in the MapLibre github organization, then the main question for me would be if you would be willing to maintain it?

I hope that a fork would not be needed! I know very little about how the plugin itself actually works. Ultimately I will need to play with it to see what's possible.

neodescis avatar Oct 15 '23 16:10 neodescis

I must have missed read the description, sorry. I would recommend allowing to place the content of the exported variable (assuming this is what the plugin does) and not just the url when setting rtl plugin. It doesn't sound too complicated, hopefully... I haven't looked through the code to fully understand what it takes to implement this. Sounds like a good simplification in general.

HarelM avatar Oct 15 '23 16:10 HarelM

I've spent some time looking at this, and there are a few things I see as blockers:

  1. The RTL "plugin" is built to support multiple environments, including node. There is a require('fs') and a require('path') in there that, even if not being used when the plugin is imported, still get processed by webpack (assuming that's being used to build the consuming application). As of webpack 5, these node dependencies are not automatically polyfilled, so either a) it would be up to the consuming application to polyfill them, or b) a fork of the plugin would be necessary to clean this up.
  2. Assuming # 1 is resolved, the plugin still has to make its way to the worker thread. Either a) the plugin would have to be transferred to the worker thread somehow, or b) it could be imported there directly to begin with. I'm not really sure how a) could be made to work, given that the plugin is essentially a collection of three functions, which are not self-contained (so .toString() won't work). I was actually very close to getting b) to work with a dynamic import(), by configuring '@mapbox/mapbox-gl-rtl-text' in rollup as an "external" module and getting rollup to not transpile import() to require(). Unfortunately, even in doing that, rollup was still wanting require to be passed into the encompassing define() call, even though it wasn't used. I think this could be worked around by passing undefined in for require, but I stopped investigating at this point.

Overall, I think this could be achieved, but ultimately I don't think it's worth all of the trouble it would require. However, if anyone has any further insight on the above, I'm all ears and would be happy to keep fiddling.

neodescis avatar Oct 20 '23 17:10 neodescis

I'm working on the communication layer between the workers and the main thread now, and there's a full section to help with the RTL plugin, so stating that this is a plugin is a bit of an outreach... But it allows reducing bundle size, which I guess is the main reason around it...

HarelM avatar Oct 20 '23 17:10 HarelM

In a webpack/create-react-app5 app I just switched to

import { setRTLTextPlugin } from 'maplibre-gl';
const mapboxGlRtlUrl = new URL(
  '@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.min.js',
  import.meta.url
);
setRTLTextPlugin(mapboxGlRtlUrl, () => {}, true);

So now it's bundled through the regular build process. The only funny thing is it's processed as a regular file like an image would be, not JavaScript. But since it's already built/minified it's not a big deal.

msbarry avatar Oct 21 '23 15:10 msbarry

Unfortunately for me, the Angular overlords have decided to make that impossible (well, without a custom webpack config anyway) by disabling webpack's import.meta.url parsing:

https://github.com/angular/angular-cli/issues/24617

Maybe I'll try again when the Angular esbuild-based builder is something I can switch to.

neodescis avatar Oct 23 '23 18:10 neodescis