Closed akx closed 7 years ago
Hi, and thanks, but our position on this is that it's a Webpack issue, and not something we will hack around.
See discussion in #139 and #145.
I understand your view there. Slippery slope and so on.
On the other hand this makes proj4leaflet
unusable with (increasingly popular) systems such as create-react-app
, which don't let you edit the Webpack configuration unless you "eject" the configuration and take full responsibility of it. 😥
Maybe I don't fully understand the situation, but I guess it must also mean that Proj4 is unusable from create-react-app
?
It seems that like a better solution to either fix Proj4, if their method of exporting isn't correct, or use a better Webpack configuration. Fixing it in Proj4Leaflet instead must be a hack?
I think that this has to be either a problem for create-react-app
or proj4
, we are too far down the chain to fix this problem with webpack
.
proj4
works fine using ES6 module syntax in modules that are transpiled by Babel.
The ES6 code
import proj4 from 'proj4';
console.log(proj4);
outputs function proj4(fromProj, toProj, coord) { ... }
in the browser console as expected.
The code is transpiled into
'use strict';
var _proj = require('proj4');
var _proj2 = _interopRequireDefault(_proj);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
console.log(_proj2.default);
by Babel -- that is, the object containing default
is peeled into, if it's tagged as an ES module (by the convention Babel uses).
I'll dig a little deeper into what's happening – looks like Webpack is treating proj4leaflet as an AMD module, so this might not have fixed anything after all . . .
Maybe a better solution would be to distribute Proj4Leaflet as a conventional CommonJS module, without UMD wrapper, and also include a "built" version with UMD wrapper for people who don't use a build tool and pull it in with a script
tag?
Okay, I think I got to the bottom of things here. The issue is in the interplay of Webpack (or any other ES6-compatible bundler really) and how Proj4 is packaged since the commit https://github.com/proj4js/proj4js/commit/7224e912ea0b7ca62bd0441946bed87c636169f4.
Webpack honors the module
field in package.json
.
main
field, and does not expect the module to be an ES6 module, but a plain old CommonJS module.Proj4's package.json
, referenced in the commit linked above, does contain a module
field, so Webpack bundles it as one, i.e. with the default object (Proj4
) exported as {default: Proj4}
. This means that users would import it as import Proj4 from 'proj4';
, and, well, see my earlier comment.
package.json
is patched to not contain the module
field, proj4leaflet
starts working in a bundle, as Webpack no longer bundles Proj4 in ES6 style, but uses the dist/proj4-src.js
UMD module.Because this issue stems from that "misunderstanding" in Webpack, distributing Proj4Leaflet as CJS would not really fix the issue (lest it is using ES6 import
for the dependencies, which can then be transpiled by Babel with the interop require code).
I have a better, improved fix here (though I figured I wouldn't make a PR before I got green light from you guys first): https://github.com/akx/Proj4Leaflet/commit/a1c61f9e5e57c72ab59a89dc39b78518b538f995
Basically, it does the same thing interopRequireDefault
would, and peels the default
away if it detects Proj4 has been bundled as an ES6 module.
@akx Hello, do you know if there is any progress about this issue? I've just run into it in my new app which uses
"leaflet": "1.2.0",
"proj4": "2.4.3", //2.3.x is not working either
"proj4leaflet": "1.0.1",
"webpack": "3.5.2"
and the proj4leaflet
is calling proj4.defs(code, def);
which does not exists because it is inside default
property.
All the related bug reports at webpack
/proj4
/proj4leaflet
are closed but none of them seems to provide solution apart from blaming webpack.
Feels to me like everyone who uses webpack/rollup must be stucked now like me. Your PR above looks promising but it's not released yet is it?
For some reason I haven't even seen @akx's comment until now. Thanks for the detailed and thorough examination of the problem!
I see two options for us:
module
field ourselves, which would be used by ES6 bundlers like Webpack, and we could point index
to a bundled version with UMD wrapper; we could do the bundling with Rollup, exactly like Leaflet itself does from version 1.1.0 and forwardOption 2 seem like the good long term solution, but option 1 is way quicker, and personally I don't think I even have the time for that one. So, probably go with option 1 for now and make a release as soon as possible.
@pthorin any chance you have the time to look at this?
For anxious people like myself, when can we expect the release?
Published version 1.0.2 that contains this fix.
Thanks again @akx for the detailed write-up, really made me understand this problem; I think your fix is sufficient for now.
Thanks @akx for this and @liedman for the release.
I will try to look into the option 2-solution after this week, when I'm back at work.
@pthorin I thought about it, but I ended up thinking it's not worth it. I don't think anyone wins on us building and bundling stuff. The current method works for everyone and is easy to understand, although not elegant.
For some reason, even after this fix usage in TS projects with import statements and Webpack build is broken. For instance:
import * as L from 'leaflet';
import * as MarkerCluster from 'leaflet.markercluster';
import * as proj4 from 'proj4';
import * as proj4leaflet from 'proj4leaflet';
Everything works fine except the proj4leaflet classes and functions. Apparently the Proj does not attach itself to the L and results in:
Cannot read property 'CRS' of undefined
at
mapOptions = {
crs: new L.Proj.CRS(mapSettings.crs, mapSettings.projection,
{ resolutions: mapSettings.resolutions, origin: mapSettings.origin /*, zoomAnimation: true*/ })
}
If I switch to require statements, it works but I lose intellisense/typings support and also wouldn't like to change the way we load dependencies just in one component and just for the sake of proj4leaftlet.
@brgrz
try this:
import * as proj4 from 'proj4leaflet';
mapOptions = {
crs: new proj4.CRS(mapSettings.crs, mapSettings.projection,
{ resolutions: mapSettings.resolutions, origin: mapSettings.origin /*, zoomAnimation: true*/ })
}
@GeorgeKnap nope.. Property 'CRS' does not exist on type 'typeof ... ' (compile time error)
@brgrz interesting. it works like this in my Angular app. Make sure you use proj4leaflet: 1.0.2
For anyone else who might run into this, I made it work by just using
import * as L from 'leaflet';
import 'proj4leaflet';
Hi, I'm trying to use proj4leaflet with webpack 4 and am running into very similar issue.
Using these versions
"proj4": "~2.4.3",
"proj4leaflet": "^1.0.2",
I keep running into
proj4leaflet.js:52 Uncaught TypeError: proj4.defs is not a function
at NewClass._projFromCodeDef (proj4leaflet.js:52)
at NewClass.initialize (proj4leaflet.js:36)
at new NewClass (leaflet-src.js:301)
at NewClass.initialize (proj4leaflet.js:90)
at new NewClass (leaflet-src.js:301)
at Object.registerLeafletSrid (SridProvider.js:29)
at app.js:132
at Object.invoke (angular.js:5097)
at angular.js:4894
at forEach (angular.js:408)
I've tested thoroughly I think, and only thing that helps is changing https://github.com/kartena/Proj4Leaflet/blob/master/src/proj4leaflet.js#L18 to
if (proj4.default) {
Why is that? Is it possible to solve this differently?
For anyone else who might run into this, I made it work by just using
import * as L from 'leaflet'; import 'proj4leaflet';
hello,I have tried ,but it does not work when I use "npm run build"
Note: The patch here is suboptimal; see https://github.com/akx/Proj4Leaflet/commit/a1c61f9e5e57c72ab59a89dc39b78518b538f995 for a better one!
When using Webpack,
leaflet
andproj4
may be exported as ES6 style modules that export the actual object as.default
; so, if the modules have.default
, use that.The symptom for this issue is "TypeError: proj4.defs is not a function" (for ease of googling).
This is related to
139