Javascript library (Leaflet) not linking in production build, because of member renaming?

47 views
Skip to first unread message

Simon Brooke

unread,
Feb 25, 2020, 4:24:31 AM2/25/20
to ClojureScript
Hi all,

I've a bug which is baffling me. If you go to https://simon-brooke.github.io/geocsv-lite/ you will see three maps that all work. Excellent. But my guilty secret is that that is a dev build; the production build does not work.

If you clone the project and run

    lein cljsbuild once min

and load a page, you'll get

    TypeError: L.map(...).Wf is not a function

You'll get that even if you set the compile options to:

               {:id "min"
                :source-paths ["src"]
                :compiler {:output-to "resources/public/js/compiled/geocsv_lite.js"
                           :main geocsv-lite.core
                           :optimizations :none
                           :foreign-libs [{:file "./resources/node_modules/leaflet/dist/leaflet.js"}]
                           :externs ["leaflet.js"]
                           :stable-names true
                           :pretty-print true
                           :warnings true}}

If you look at the error, it happens here:

      switch(c = mk instanceof G ? mk.ib : null, c) {
        case "mapbox":
          c = L.map(a, Aj(new x(null, 1, [Xl, "false"], null))).Wf([55, -4], 10);
          L.Rg("http://{s}.tiles.mapbox.com/v3/FIXME/{z}/{x}/{y}.png", Aj(new x(null, 2, [Nl, "Map data \x26copy; [...]", gl, 18], null))).ef();
          break a;
        case "osm":
          c = L.map(a, Aj(new x(null, 1, [Xl, !1], null))).Wf([55, -4], 10); <<<< THIS LINE HERE
          L.Rg("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", Aj(new x(null, 2, [Nl, "Map data \x26copy; \x3ca href\x3d'http://openstreetmap.org'\x3eOpenStreetMap\x3c/a\x3e contributors", gl, 18], null))).ef(c);
          break a;
        default:
          throw Error(["No matching clause: ", B.a(c)].join(""));
      }

which is clearly compiled from:

(defn map-did-mount-osm
  "Did-mount function loading map tile data from Open Street Map.
  Arguments are:
  * `id` the element id of the HTML element to occupy (string);
  * `lat` the latitude of the centre of the view (real number);
  * `lng` the longitude of the centre of the view (real number);
  * `zoom` the initial zoom level of the view (real number)."
  [id lat lng zoom]
  (let [view (.setView
               (.map js/L
                     id
                     (clj->js {:zoomControl false}))
               #js [lat lng]
               zoom)]
    (.addTo (.tileLayer js/L osm-url
                        (clj->js {:attribution osm-attrib
                                  :maxZoom 18}))
            view)
    view))

(defn map-did-mount
  "Select the actual map provider to use. Arguments are:
  * `id` the element id of the HTML element to occupy (string);
  * `lat` the latitude of the centre of the view (real number);
  * `lng` the longitude of the centre of the view (real number);
  * `zoom` the initial zoom level of the view (real number)."
  [id lat lng zoom]
  (case *map-provider*
    :mapbox (map-did-mount-mapbox id lat lng zoom)
    :osm (map-did-mount-osm id lat lng zoom)
    ;; potentially others
    ))

So what is going wrong is that *some* (not all) Leaflet method names are being substituted, *despite* :stable-names being true.

For example, above, 'L.tileLayer' is being compiled to 'L.Rg', and, where it's actually breaking, 'setView' is being compiled to 'Wf'.

Now, this cannot be a problem unique to me, and I find it hard to believe that it's a problem unique to Leaflet. But I have googled and googled, and I cannot find how to tell the ClojureScript compiler that it does not own the 'L' global, and cannot rename its members. Anyone? Please help?

Thomas Heller

unread,
Feb 25, 2020, 5:48:27 AM2/25/20
to ClojureScript
Hey,

first of all :stable-names has nothing to do with this. It only attempts to keep the names stable between compiles, meaning that it will try to re-use the same shortened name on a recompile. It'll still shorten though.

The issue you need to google is externs and externs inference.


Your leaflet.js externs are likely just incomplete, which happens quite often if they are auto-generated.

If you enable :infer-externs you should get warnings for things that'll be renamed because the compiler cannot tell who they belong to.

You can also compile with :pseudo-names true which will make the names somewhat recognizable, meaning setView becomes $setView$. So you can add setView to your externs to make that error go away. That is a bit tedious but an alternative to infer-externs.

HTH,
/thomas

PS: shadow-cljs fixes a lot of those issues.

Simon Brooke

unread,
Feb 25, 2020, 10:00:56 AM2/25/20
to ClojureScript


On Tuesday, 25 February 2020 10:48:27 UTC, Thomas Heller wrote:
Hey,

first of all :stable-names has nothing to do with this. It only attempts to keep the names stable between compiles, meaning that it will try to re-use the same shortened name on a recompile. It'll still shorten though.

The issue you need to google is externs and externs inference.


Your leaflet.js externs are likely just incomplete, which happens quite often if they are auto-generated.

If you enable :infer-externs you should get warnings for things that'll be renamed because the compiler cannot tell who they belong to.

Thank you, that was the trick!

It's working now, but I need to do a wee bit more tidy-up before I push the fix to github.
Reply all
Reply to author
Forward
0 new messages