# How to keep a notebook working

**URL:** <https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464>\
**Category:** Help\
**Created:** [April 19, 2022, 9:25am UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464 "2022-04-19T09:25:50Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![PBrockmann](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/pbrockmann/32/1552_2.png) [@PBrockmann](https://talk.observablehq.com/u/PBrockmann)\
**Post date:** [April 19, 2022, 9:25am UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/1 "2022-04-19T09:25:50Z")

</div>

Hi,  
I have a simple notebook that was running perfectly and today no more.  
It is really annoying. Demo time.  
Resources (from require) seems available yet.

> **[Leaflet Synchro](https://observablehq.com/@pbrockmann/leaflet-synchro)**
>
> All maps are synchronized, their size can be controled from the first map (pass mouse over south-east corner)

Any help would be welcomed.

Tested load of 2 ressources. ganja is ok, leaflet.sync not.  
Both available.  
What do I have missed ?  
From [Introduction to require / Observable | Observable](https://observablehq.com/@observablehq/require)

 ![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/f/ffd6f5d9cb05b6164cadd2fd9b77552e931c3266.png)

> a = require(“[https://cdn.jsdelivr.net/npm/leaflet.sync@0.2.4/L.Map.Sync.min.js](https://cdn.jsdelivr.net/npm/leaflet.sync@0.2.4/L.Map.Sync.min.js)”)

Both exist and available from [https://www.jsdelivr.com](https://www.jsdelivr.com)

---

<div class="post-metadata">

**Author:** ![Cobus](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/cobus/32/9670_2.png) [@Cobus](https://talk.observablehq.com/u/Cobus)\
**Post date:** [April 19, 2022, 3:12pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/2 "2022-04-19T15:12:49Z")

</div>

Sorry to hear that! Just so I understand… are you saying that the leaflet.sync require statement worked before and now it is not working? I see the same behavior atm, but wondering if it actually worked before…

---

<div class="post-metadata">

**Author:** ![PBrockmann](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/pbrockmann/32/1552_2.png) [@PBrockmann](https://talk.observablehq.com/u/PBrockmann)\
**Post date:** [April 19, 2022, 3:27pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/3 "2022-04-19T15:27:26Z")

</div>

Yes Sure , it was working.  
This notebook is a simple test for more complicated ones that do not work anymore.

> **[WMS Leaflet map GCA](https://observablehq.com/@pbrockmann/wms-leaflet-gca)**
>
> An interface to produce a slippy map using WMS (Web Map Service) An interface to produce a slippy map using WMS (Web Map Service) layer from netCDF files available
> from :...

for example  
or

> **[WMS Leaflet map IPSL](https://observablehq.com/@pbrockmann/wms-leaflet-ipsl)**
>
> An interface to produce a slippy map using WMS (Web Map Service) An interface to produce a slippy map using WMS (Web Map Service) layer from netCDF files available
> from :...

Same issue: map = TypeError: L.control.resizer is not a function  
because require does not load the js ressource anymore.

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [April 19, 2022, 5:12pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/4 "2022-04-19T17:12:28Z")

</div>

In your `L` cell, change

```auto
return L;

```

to

```auto
return window.L;

```

---

<div class="post-metadata">

**Author:** ![PBrockmann](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/pbrockmann/32/1552_2.png) [@PBrockmann](https://talk.observablehq.com/u/PBrockmann)\
**Post date:** [April 19, 2022, 5:16pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/5 "2022-04-19T17:16:05Z")

</div>

Indeed. Thanks…

Why this change now ?

And why the require on leaflet.sync@0.2.4/L.Map.Sync.min.js does not work .

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [April 19, 2022, 5:23pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/6 "2022-04-19T17:23:35Z")

</div>

> [@PBrockmann](#):
>
> And why the require on leaflet.sync@0.2.4/L.Map.Sync.min.js does not work

It never did, you just forgot the `.catch()`. Take a look at [https://cdn.jsdelivr.net/npm/leaflet.sync@0.2.4/L.Map.Sync.js](https://cdn.jsdelivr.net/npm/leaflet.sync@0.2.4/L.Map.Sync.js) and you’ll see that there’s no UMD support. The file is meant to be included directly in the browser and expects the global variable `L` to be present.

By calling

```auto
require('https://cdn.jsdelivr.net/npm/leaflet.sync@0.2.4/L.Map.Sync.min.js').catch(()=>{})

```

you tell d3-require to _attempt_ to load the script, and suppress any errors. d3-require creates a script element with the script URL as src, attaches it to the document, waits for the UMD module to register itself (via `window.define()`), fails, and throws the error “invalid module”. `.catch()` suppresses the error.

This pattern is often used in combination with, e.g., `.catch(() => window.Foo)`, where `Foo` is a global variable created by the loaded script. This allows other cells to 1. wait for the library to load, and 2. reference it via its loading cell, instead of via `window`.

---

<div class="post-metadata">

**Author:** ![PBrockmann](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/pbrockmann/32/1552_2.png) [@PBrockmann](https://talk.observablehq.com/u/PBrockmann)\
**Post date:** [April 19, 2022, 5:30pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/7 "2022-04-19T17:30:06Z")

</div>

Ok I will try to make those things clearer.

But according to the doc from [Introduction to require / Observable / Observable](https://observablehq.com/@observablehq/require), I understand that wrongly.

What about the change now needed window.L  
Because I can assure you that those notebooks were running correctly.

---

<div class="post-metadata">

**Author:** ![PBrockmann](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/pbrockmann/32/1552_2.png) [@PBrockmann](https://talk.observablehq.com/u/PBrockmann)\
**Post date:** [April 19, 2022, 5:34pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/8 "2022-04-19T17:34:09Z")

</div>

Could you help me to correct loading of ressources needed from [Leaflet Synchro / Patrick Brockmann / Observable](https://observablehq.com/@pbrockmann/leaflet-synchro) ?

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [April 19, 2022, 5:37pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/9 "2022-04-19T17:37:58Z")

</div>

> [@PBrockmann](#):
>
> What about the change now needed window.L

No idea, tbh. For this to work in the past, cells would have to have received the global variable instead of the cell value whenever a global var of the same name was defined.

@mbostock might be able to provide an answer.

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [April 19, 2022, 6:02pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/10 "2022-04-19T18:02:19Z")

</div>

> [@PBrockmann](#):
>
> Could you help me to correct loading of ressources needed

@PBrockmann This seems to do the trick:

```javascript
L = {
  const BASE = `https://cdn.jsdelivr.net/npm/`;
  const style = document.head.appendChild(html`<style>
    @import url(${BASE}leaflet@1.7.1/dist/leaflet.css);
    @import url(${BASE}leaflet-fullscreen@1.0.2/dist/leaflet.fullscreen.css);
    @import url(${BASE}leaflet-timedimension@1.1.1/dist/leaflet.timedimension.control.min.css);
    @import url(${BASE}leaflet.control.resizer@0.0.1/L.Control.Resizer.css);
  `);
  invalidation.then(() => style.remove());

  delete window.L;
  const L = window.L = await require(`${BASE}leaflet@1.7.1/dist/leaflet-src.min.js`);
  await Promise.all([
    `${BASE}leaflet-fullscreen@1.0.2`,
    `${BASE}leaflet-timedimension@1.1.1`,
    `${BASE}leaflet.sync@0.2.4`,
    `${BASE}leaflet.control.resizer@0.0.1`
  ].map(url => require(url).catch(() => {})));
  
  return L;
}

```

Unfortunately there doesn’t seem to be a cleaner way, as leaflet cannot be extended by the plugins if it’s loaded as an ES module.

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [April 19, 2022, 7:24pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/11 "2022-04-19T19:24:38Z")

</div>

One of the problems here is that multiple versions of Leaflet are being loaded. At the top-level, you’ve requested version 1.7.1, but one of the other libraries you’re loading has a transitive dependency on leaflet, too, resulting in the [latest version 1.8.0](https://github.com/Leaflet/Leaflet/blob/main/CHANGELOG.md#180-2022-04-18) also being loaded. I put your libraries into the package dependency visualizer here:

 ![Screen Shot 2022-04-19 at 12.05.05 PM](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/8/84d79ccaada0511499b9fa5253b6bfa652b4b597.png)

> **[Package Dependencies](https://observablehq.com/@mbostock/package-dependencies?name=leaflet@1.7.1,%20leaflet-fullscreen@1.0.2,%20leaflet-timedimension@1.1.1,%20leaflet.sync@0.2.4,%20leaflet.control.resizer@0.0.1)**
>
> This notebook visualizes the transitive dependencies of any package in npm. Enter one or more package names (such as \\\`d3\\\` or \\\`react\\\`) separated by commas into the input below. You can also specify a version range for each package, such as...

If you want to load a complicated network of dependencies like this, your best bet is to use [require.alias](https://github.com/d3/d3-require/blob/main/README.md#require_alias) to define all of the versions you want to load explicitly. As an added bonus, if you define the dependencies explicitly, require won’t have to load the package.json files to resolve the versions, so everything will load faster. And you can also load things faster by using Promise.all to load in parallel.

```nohighlight
L = {
  const r = require.alias({
    "leaflet": "leaflet@1.7.1/dist/leaflet-src.js",
    "leaflet/dist/leaflet.css": "leaflet@1.7.1/dist/leaflet.css",
    "leaflet-fullscreen": "leaflet-fullscreen@1.0.2/dist/Leaflet.fullscreen.min.js",
    "leaflet-fullscreen/dist/leaflet.fullscreen.css": "leaflet-fullscreen@1.0.2/dist/leaflet.fullscreen.css",
    "leaflet-timedimension": "leaflet-timedimension@1.1.1/dist/leaflet.timedimension.min.js",
    "leaflet-timedimension/dist/leaflet.timedimension.control.min.css": "leaflet-timedimension@1.1.1/dist/leaflet.timedimension.control.min.css",
    "iso8601-js-period": "iso8601-js-period@0.2.1/iso8601.js",
    "leaflet.sync": "leaflet.sync@0.2.4/L.Map.Sync.js",
    "leaflet.control.resizer": "leaflet.control.resizer@0.0.1/L.Control.Resizer.js",
    "leaflet.control.resizer/L.Control.Resizer.css": "leaflet.control.resizer@0.0.1/L.Control.Resizer.css"
  });
  const L = await r("leaflet");
  await Promise.all([
    r("leaflet-fullscreen").catch(() => {}),
    r("leaflet-timedimension").catch(() => {}),
    r("leaflet.sync").catch(() => {}),
    r("leaflet.control.resizer").catch(() => {})
  ]);
  if (!L._style) { // apply stylesheets (only once on load)
    L._style = true;
    document.head.append(htl.html`
      <link href=${await r.resolve("leaflet/dist/leaflet.css")} rel=stylesheet>
      <link href=${await r.resolve("leaflet-fullscreen/dist/leaflet.fullscreen.css")} rel=stylesheet>
      <link href=${await r.resolve("leaflet-timedimension/dist/leaflet.timedimension.control.min.css")} rel=stylesheet>
      <link href=${await r.resolve("leaflet.control.resizer/L.Control.Resizer.css")} rel=stylesheet>
    `);
  }
  return L;
}

```

[https://observablehq.com/compare/4cbfcfc2d8cd3222...c43119f41bf860c0](https://observablehq.com/compare/4cbfcfc2d8cd3222...c43119f41bf860c0)

There are lots of tricky things here:

1. The `leaflet` module is UMD, so it works great with require. But the various plugins do not; they are simply vanilla JavaScript, pre-ES modules, and they expect the `leaflet` module to define the `L` global. This is why you must `await r("leaflet")` before loading the other modules, rather than being able to load them in parallel.
2. The leaflet plugins mutate the `L` global, so you don’t need the return value resulting from loading them. An empty catch is enough to ignore the error (since these modules don’t implement AMD/UMD).
3. Leaflet and its plugins also use global stylesheets, but do not insert them into the document automatically, so you need to do it. Also, you want to avoid inserting them multiple times, ideally, if the `L` cell is re-run. I used a flag `L._style` to check whether I’ve already inserted the stylesheets to avoid this, but it also means that you might need to reload the notebook if you change the definition of `L`.
4. When resolving the stylesheets, you want to be consistent with the version of the module you are using, so I included them in require.alias, too. I think that means that the subsequent require.resolve calls resolve synchronously, so it’s fine to await them in series; if these were really asynchronous, you’d want to include them in the Promise.all so they can be done in parallel, too.

---

<div class="post-metadata">

**Author:** ![mcmcclur](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mcmcclur/32/2364_2.png) [@mcmcclur](https://talk.observablehq.com/u/mcmcclur)\
**Post date:** [April 19, 2022, 7:43pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/12 "2022-04-19T19:43:29Z")

</div>

> [@mbostock](#):
>
> At the top-level, you’ve requested version 1.7.1, but one of the other libraries you’re loading has a transitive dependency on leaflet, too, resulting in the [latest version 1.8.0](https://github.com/Leaflet/Leaflet/blob/main/CHANGELOG.md#180-2022-04-18) also being loaded.

Version 1.8.0 was just released a couple of days ago, which probably explains the observed change in behavior.

---

<div class="post-metadata">

**Author:** ![PBrockmann](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/pbrockmann/32/1552_2.png) [@PBrockmann](https://talk.observablehq.com/u/PBrockmann)\
**Post date:** [April 20, 2022, 4:28pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/13 "2022-04-20T16:28:53Z")

</div>

First of all. Many thanks for having taken the time to make this really good support on this tricky problem when loading resources.

I have finally used leaflet 1.8.0 and have noticed no problem with it.

A problem still persists when I use the Export button (after having added 2 maps).

![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/5/53a198b5a37dc3dd752802075fb9a6b5a4c5405f.png)

I get this message in the new thumbnail. If I reload the page, then the maps appear.  
Sometimes also after a reload of the notebook, the problem is visible.

![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/7/70b1a7657d5ba6a717652b326188219ce7ab38e0.png)

Do not know how to solve this problem again. Sorry.

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [April 21, 2022, 1:44pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/14 "2022-04-21T13:44:55Z")

</div>

Can you share a notebook with the latest problem? Thanks.

---

<div class="post-metadata">

**Author:** ![PBrockmann](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/pbrockmann/32/1552_2.png) [@PBrockmann](https://talk.observablehq.com/u/PBrockmann)\
**Post date:** [April 21, 2022, 2:01pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/15 "2022-04-21T14:01:18Z")

</div>

No, no this is me that feel bitterly in request.

> **[WMS Leaflet map GCA (DEV)](https://observablehq.com/@pbrockmann/wms-leaflet-gca-dev)**
>
> An interface to produce a slippy map using WMS (Web Map Service) An interface to produce a slippy map using WMS (Web Map Service) layer from netCDF files available
> from :...

To run properly you must first access to the thredds server from a click to [https://www.globalcarbonatlas.org:8443/thredds/catalog/Atlas/Flux\_Transcom/catalog.html](https://www.globalcarbonatlas.org:8443/thredds/catalog/Atlas/Flux_Transcom/catalog.html).

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [April 21, 2022, 9:39pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/16 "2022-04-21T21:39:32Z")

</div>

Yeah, so the problem is that leaflet-timedimension implements UMD, which is good!, but means you can’t use the same catch pattern you’re using for the other Leaflet plugins that don’t implement UMD. Instead of this:

```nohighlight
    r("leaflet-timedimension").catch(() => {}),

```

You need this:

```nohighlight
    r("leaflet-timedimension").then((o) => (L.TimeDimension = o)),

```

Otherwise, you’re loading leaflet-timedimension, but you’re not assigning it to the L object, so the subsequent L.TimeDimension call fails.

I discovered this by looking at the source:

[https://cdn.jsdelivr.net/npm/leaflet-timedimension@1.1.1/dist/leaflet.timedimension.src.js](https://cdn.jsdelivr.net/npm/leaflet-timedimension@1.1.1/dist/leaflet.timedimension.src.js)

---

<div class="post-metadata">

**Author:** ![tomlarkworthy](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/tomlarkworthy/32/5940_2.png) [@tomlarkworthy](https://talk.observablehq.com/u/tomlarkworthy)\
**Post date:** [April 22, 2022, 9:55am UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/17 "2022-04-22T09:55:43Z")

</div>

On the more general subject of keeping notebooks working (the thread title), I use a healthcheck notebook

> **[Generic Notebook Health Check](https://observablehq.com/@endpointservices/healthcheck)**
>
> This notebook executes other notebooks through an embedded runtime and looks for errors thrown by cells. You do not need to modify the original notebook in any way! It exposes its functionality as an endpoint, which can be connected to uptime...

That notebook can be called via a URL which when called runs the healthcheck on an arbitrary notebook.

So then I automate calling the healtcheck using a monitoring service (uptime robot).

The entire technique is explained:

> **[Productionizing Observable Notebooks with 3rd party Active Monitoring](https://observablehq.com/@tomlarkworthy/howto-monitoring)**
>
> I keep my 200+ public notebooks on the observablehq.com platform running by actively monitoring them with a 3rd party monitoring tool uptimerobot. Here is how I got it connected, and how I did it unobtrusively without altering the original notebooks....

Here is my personal status dashboard:

 ![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/d/df6b8901d93c57101f25119b5aac575a1ccef06d.jpeg)

I am half thinking of streamlining this process to create a subscription monitoring service for like $10 a month and it will auto monitor all public notebooks for errors, would you be interested?

---

<div class="post-metadata">

**Author:** ![PBrockmann](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/pbrockmann/32/1552_2.png) [@PBrockmann](https://talk.observablehq.com/u/PBrockmann)\
**Post date:** [April 22, 2022, 12:02pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/18 "2022-04-22T12:02:33Z")

</div>

> [@mbostock](#):
>
> `r("leaflet-timedimension").then((o) => (L.TimeDimension = o)),`

I have tried this but get sometimes:

![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/f/fc7b34916ff3507c5fd3a88cc509afacd4c59ac4.png)

That is tricky and puzzling…

---

<div class="post-metadata">

**Author:** ![PBrockmann](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/pbrockmann/32/1552_2.png) [@PBrockmann](https://talk.observablehq.com/u/PBrockmann)\
**Post date:** [May 6, 2022, 4:10pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/19 "2022-05-06T16:10:09Z")

</div>

Any news on this issue.  
The “export map” produces this error.  
If I insist by reloading and reloading. it works.  
So a problem again when loading ressources.

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [May 7, 2022, 3:38pm UTC](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464/20 "2022-05-07T15:38:26Z")

</div>

You could try ignoring errors on the leaflet-timedimension require:

```nohighlight
    r("leaflet-timedimension").then((o) => (L.TimeDimension = o), () => {}),

```

But I think the real answer is probably that you should transpile and bundle Leaflet and all the plugins into a modern ES module so that you can load it without all these race conditions. 😬 Given that each of these Leaflet plugins uses a different style of export (which is perfectly representative of the insanity solved by the ES modules standard), I’m not sure it’s possible to load this set of libraries reliably using require.

[Next page](https://talk.observablehq.com/t/how-to-keep-a-notebook-working/6464.md?page=2)
