Skip to content
Advertisement

Importing leaflet into module from cdn with typescript support

I’m trying to import leaflet into a javascript module with typescript support but can’t get it to work.

I’ve installed @types/leaflet and have tried to following:
import 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.8.0/leaflet.js'
This works fine in the browser but typescript gives the following error:
'L' refers to a UMD global, but the current file is a module. Consider adding an import instead.ts(2686)
(I call L.map() in my code)

I’ve also tried: import * as L from 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.8.0/leaflet.js'
This works fine in typescript, but gives the following error in chrome:
Uncaught TypeError: L.map is not a function

Anyone know how to fix this?
I don’t want to use a bundler.

Edit As jsejcksn suggested in the comments, importing a ES module version of leaflet and adding a path alias to typescript fixed this. I’m now using the following import:
import * as L from 'https://unpkg.com/leaflet@1.8.0/dist/leaflet-src.esm.js'
The problem with this though, as stated on Leaflet’s Download page, is that this imports Leaflet’s source files including unit tests, files for debugging, build scripts, etc. This doesn’t seem very efficient to me.
Any other ideas?

Edit 2 It seems like Leaflet 1.9 will solve my problem when it will be released. This pull request adds an ES module entrypoint to Leaflet meaning you can do this:
import L from 'leaflet'
or in our case:
import L from 'cdn.com/link/to/leaflet/1.9/esm'

In the mean time, jsejcksn’s answer provides a good alternative so I will accept that answer.

Advertisement

Answer

Preface

I’ve already written an answer about how to do this with ES modules at the question How to include modules in client side in typescript projects?. Be sure to read through that question and answer for context here.

You said:

I don’t want to use a bundler.

and also:

The problem with this though, as stated on Leaflet’s Download page, is that this imports Leaflet’s source files including unit tests, files for debugging, build scripts, etc. This doesn’t seem very efficient to me.

These ideas seem at odds to me. Without more information, I can only assume that by “not very efficient” you mean that the byte size of the presumably not-tree-shaken ES module is larger than the UMD module. If that’s the case, why not just import Leaflet into your bundle and tree-shake away all of the parts that you don’t use during build, resulting in even fewer bytes than the UMD?

Ignoring that for now, and responding purely to your stated criteria, below I will provide a self-contained, reproducible example demonstrating how you can use a CDN-hosted UMD module that has a corresponding @types package in a TypeScript ES module without augmenting the definition of globalThis.window in the compiler and without bundling.

Note that some of what I show will be purely ceremonial, but the technique will allow for you to later swap out the shim module URL in the import map with a CDN ES module URL if/when you find one that suits you.

Download the files in this example

I’m going to provide a complete reproducible example here, including all the content of each file in a code block. If you want to use this example to reproduce it and don’t want to copy + paste every individual code block into a new file with a matching name on your device, then you can copy + paste the following script and run it in your browser console to download the repo as a zip archive file:

JavaScript

Example

This example will be served using a static file server from the ./public directory, and the entrypoint html file is here:

./public/index.html:

JavaScript

Nothing special there except maybe the import map if you aren’t familiar: WICG/import-maps: How to control the behavior of JavaScript imports

Next, there are two TypeScript source files in the ./src directory which are transpiled to JavaScript and copied into the ./public directory during compilation.

The first is a shim:

./src/leaflet_umd_shim.ts:

JavaScript

The "leaflet" import specifier in the import map is bound to the location of the JavaScript module produced by this file during compilation.

That specifier is used in the entrypoint module:

./src/main.ts:

JavaScript

When you serve this site on a local http server and access the HTML file, the main module will execute and print that information to the console using those methods from Leaflet to create the data that is logged, proving that this technique works.

Here’s my TSConfig:

./tsconfig.json:

JavaScript

And, lastly, here’s the npm package file:

./package.json:

JavaScript

You can ignore the volta property: it just shows that I was using Volta and the version of Node that I had installed when creating this repository example.

The node-static dependency is the package recommended by the official Node.js website for serving static files in the article How to serve static files | Node.js.

First use npm install to install the dependencies:

JavaScript

Then — to compile the TS files and emit them — use npm run compile:

JavaScript

There should now be two new files in the ./public directory after compilation:

./public/leaflet_umd_shim.js:

JavaScript

./public/main.js:

JavaScript

Now everything is ready to be served by the static file server, so use the command npm run serve:

JavaScript

If you navigate to http://127.0.0.1:8000 in your browser (this is the same as http://localhost:8000), you’ll see the index page with the text “See console output”, and if you look at your browser’s JS console, you’ll see a representation of the object data that was logged to the console in the main.js module.

When you’re ready to stop the server, just use ctrl + c in your terminal

And that’s it!

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement