Require + notebook runtime

I’ve been running into an issue where require'd modules intermittently throw a mysterious Pe: invalid module error if I’ve imported Observable’s runtime library and embedded a notebook in another cell. The following notebook is my attempt to give a minimal example:

The two cells of interest are:

  • req, which requires vega-lite (can be replaced with any other module of your choice, in my original case it was d3)
  • loaded, which embeds an almost-blank notebook using required Observable runtime and inspector libraries.

In my browser (Chrome, macOS), about ~2/3 of the time loaded shows an error “RuntimeError: md could not be resolved”. The other ~1/3 of the time the req cell fails with Pe: invalid module.

If I comment out req, then loaded never seems to get an error. If I comment out loaded then req always seems to be fine. If I comment out the dependencies in req to change the evaluation order then I can sometimes get no error in either of the cells.

(I also see this in Firefox when I use a polyfill for dynamic import, but I stripped that out to make the example simpler.)

Ultimately I’d like to be able to require and embed notebooks without fear. I’d appreciate any pointers or advice on how to debug this sort of thing.

Thanks!

After some digging, I think the issue may be that both the imported observable.Library and the “usual” stdlib instance (that is, the one provided by the Observable runtime in the editor) are adding define to the global namespace (from their respective copies of d3-require) and these are overriding each other (?).

One possibly (ugly) solution might be to use the copy of require given by @observablehq/runtime whenever possible:

More explicitly, the idea is that the notebook should first import the runtime using the built-in require, and then import all other libraries using a “new” require coming from a Library instance.

edit: One remaining issue is that the hljs instance (for syntax highlighting in markdown) provided by an imported runtime can still conflict with the “usual” one. I’ve updated the example notebook in this post to illustrate this. After refreshing a bunch of times, I see that sometimes the code in the markdown is properly highlighted, but sometimes not (and then Uncaught (in promise) Pe: invalid module errors appear in the console).

I’m still curious if there are better ways to deal with all this, but I think I’ve figured out enough to be able to work around these problems. Anyways, I guess the bottom line is that one has to be very careful with imports (such as @observablehq/runtime) that attach stuff to window.

Yep, this is what I was going to mention:

You can’t use more than one copy of d3-require (which is used by Observable’s standard library), because they will compete to set the define global. (And define has to be a global according to the design of AMD.)

It would be possible to change d3-require to wrap an existing define global, if present, rather than overwriting it. Though I worry that might cause different problems.

I’m curious why you’re using the Observable runtime to load a notebook within another notebook, rather than using Observable’s native imports? Mind giving a little more detail on what you are trying to accomplish?

1 Like

Here’s a clean way to embed an Observable notebook within another Observable notebook using an iframe:

1 Like

Thanks! The iframe approach looks like it might be the best option, all things considered.

Sure! It’s kind of open-ended. I’ve been trying to better understand “how Observable works”, and so I’ve been messing around with the runtime / inspector / parser libraries using Observable itself, since I find the live environment pleasant to work and think in.

For example, I’ve been hacking on a crude re-implementation of how Observable transforms the raw source of notebooks to importable notebook runtime modules, and it’s been helpful to display / check the results in the same notebook:

(This is where I noticed a d3-fetch import failing).