# How does nbconvert executes Javacript ? Can I see the JS console output?

**URL:** <https://discourse.jupyter.org/t/how-does-nbconvert-executes-javacript-can-i-see-the-js-console-output/21700>\
**Category:** nbconvert\
**Tags:** help-wanted\
**Created:** [October 1, 2023, 6:45am UTC](https://discourse.jupyter.org/t/how-does-nbconvert-executes-javacript-can-i-see-the-js-console-output/21700 "2023-10-01T06:45:19Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jan\_Pfeifer](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/jan_pfeifer/32/13014_2.png) [@Jan\_Pfeifer](https://discourse.jupyter.org/u/Jan_Pfeifer)\
**Post date:** [October 1, 2023, 6:45am UTC](https://discourse.jupyter.org/t/how-does-nbconvert-executes-javacript-can-i-see-the-js-console-output/21700/1 "2023-10-01T06:45:19Z")

</div>

hi all,

I have a notebook that uses Javascript to build some dynamic HTML in the output, and it works fine in JupyterLab – even the export to HTML is working perfectly, when used from the browser.

Now when executing the same notebook from nbconvert the Javascript seems to fail – I’m not sure why yet.

What does `nbconvert` uses as a front-end ? I saw some references to chromium, but when I run it, I don’t see any chromium processes running. Is there a way to force it to use it ? And would it be possible to capture/display the console output ?

I do have installed `playwright` – some posts in the internet seem to indicate that nbconvert uses that, but I’m not sure.

I tried `--allow-chromium-download`, but it didn’t make a difference – also it never complained it couldn’t find chromium …

Any pointers would be most welcome!  
cheers

---

<div class="post-metadata">

**Author:** ![krassowski](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/krassowski/32/1685_2.png) [@krassowski](https://discourse.jupyter.org/u/krassowski)\
**Post date:** [October 1, 2023, 11:19am UTC](https://discourse.jupyter.org/t/how-does-nbconvert-executes-javacript-can-i-see-the-js-console-output/21700/2 "2023-10-01T11:19:59Z")

</div>

This would depend on the output format. Chromium/playwright is only used for `webpdf` output.

---

<div class="post-metadata">

**Author:** ![Jan\_Pfeifer](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/jan_pfeifer/32/13014_2.png) [@Jan\_Pfeifer](https://discourse.jupyter.org/u/Jan_Pfeifer)\
**Post date:** [October 1, 2023, 11:24am UTC](https://discourse.jupyter.org/t/how-does-nbconvert-executes-javacript-can-i-see-the-js-console-output/21700/3 "2023-10-01T11:24:37Z")

</div>

Thx @krassowski , Yes, I saw that in the doc, so I tried `webpdf`. But I also tried `asciidoc` and `html` … all the same issue.

Now, `nbconvert` converts a saved notebook correctly. If I execute the notebook in the browser, save it, and then use `nbcovert` without the `--execute`.

So I suppose an alternative is to write a small program to do that using [`chrome --headless`](https://developer.chrome.com/blog/headless-chrome/) and remote control it with [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) – there are some nice libraries to do that, I hope I can get `jupyter notebook` to execute all cells and save.

Any thoughts ?

---

<div class="post-metadata">

**Author:** ![Jan\_Pfeifer](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/jan_pfeifer/32/13014_2.png) [@Jan\_Pfeifer](https://discourse.jupyter.org/u/Jan_Pfeifer)\
**Post date:** [October 4, 2023, 4:21am UTC](https://discourse.jupyter.org/t/how-does-nbconvert-executes-javacript-can-i-see-the-js-console-output/21700/4 "2023-10-04T04:21:58Z")

</div>

[`nbexec`](https://github.com/janpfeifer/gonb/tree/main/cmd/nbexec), fresh from the oven, will execute a notebook on a headless chromium browser, and save it with the output contents.

It can then be converted to PDF/text/etc with `nbconvert`.

It offer more debugging options – e.g.: it also optionally captures Javascript console messages, which is great to debug javascript executed for a cell output.

It’s documented, and the [code](https://github.com/janpfeifer/gonb/blob/fcffe3a44045b66bee35ff1b85698c5a768b74e3/cmd/nbexec/nbexec.go) (in Go) is small and simple to read, if anyone is curious.

---

<div class="post-metadata">

**Author:** ![krassowski](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/krassowski/32/1685_2.png) [@krassowski](https://discourse.jupyter.org/u/krassowski)\
**Post date:** [October 4, 2023, 12:02pm UTC](https://discourse.jupyter.org/t/how-does-nbconvert-executes-javacript-can-i-see-the-js-console-output/21700/5 "2023-10-04T12:02:04Z")

</div>

This could be also implemented using [`galata`](https://github.com/jupyterlab/jupyterlab/tree/main/galata) (a jupyterlab wrapper around `playwright`) but that requires a node runtime.

---

<div class="post-metadata">

**Author:** ![Jan\_Pfeifer](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/jan_pfeifer/32/13014_2.png) [@Jan\_Pfeifer](https://discourse.jupyter.org/u/Jan_Pfeifer)\
**Post date:** [October 5, 2023, 6:17am UTC](https://discourse.jupyter.org/t/how-does-nbconvert-executes-javacript-can-i-see-the-js-console-output/21700/6 "2023-10-05T06:17:34Z")

</div>

Oh, I missed that one, I could have used it! Thanks for the link.  
(_edit_: [  
bollwyvl](https://discourse.jupyter.org/u/bollwyvl) had pointed it out to me in another thread, I should have seen it)

Fortunately, github.com/go-rod/rod made it easy – and I’m more familiar with Go than node.

The harder part was figuring out how to use `jupyterapp` Javascript object. Eventually, I found out one can use `jupyterapp.commandLinker._commands.execute` to execute commands – the rest was simple. Btw, is there an easier way to drive `jupyterapp` programmatically ?

---

<div class="post-metadata">

**Author:** ![krassowski](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/krassowski/32/1685_2.png) [@krassowski](https://discourse.jupyter.org/u/krassowski)\
**Post date:** [October 5, 2023, 1:47pm UTC](https://discourse.jupyter.org/t/how-does-nbconvert-executes-javacript-can-i-see-the-js-console-output/21700/7 "2023-10-05T13:47:39Z")

</div>

Would not `jupyterapp.commands.execute` work too? This is what we use in `galata` when we want to drive the test using commands, e.g.:

> <https://github.com/jupyterlab/jupyterlab/blob/86c9d5d2bc546fac2de1433f9497a3a6f2b80069/galata/test/jupyterlab/settings.test.ts#L206-L208>

It is using [`JupyterFrontEnd`](https://jupyterlab.readthedocs.io/en/latest/api/classes/application.JupyterFrontEnd-1.html) which inherits from lumino’s [`Application`](https://lumino.readthedocs.io/en/latest/api/classes/application.Application-1.html).

---

<div class="post-metadata">

**Author:** ![bollwyvl](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/bollwyvl/32/904_2.png) [@bollwyvl](https://discourse.jupyter.org/u/bollwyvl)\
**Post date:** [October 5, 2023, 2:20pm UTC](https://discourse.jupyter.org/t/how-does-nbconvert-executes-javacript-can-i-see-the-js-console-output/21700/8 "2023-10-05T14:20:27Z")

</div>

There’s also this weird little thing:

> **[GitHub - deathbeds/jyg: run Jupyter browser client commands from a CLI, REST...](https://github.com/deathbeds/jyg)**
>
> run Jupyter browser client commands from a CLI, REST API, or other browser windows - GitHub - deathbeds/jyg: run Jupyter browser client commands from a CLI, REST API, or other browser windows

But if in a headless testing/debug situation where `expose-app-in-browser` is enabled, and you have control of the JS interpreter, there’s no particular use to it.

---

<div class="post-metadata">

**Author:** ![Jan\_Pfeifer](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.jupyter.org/jan_pfeifer/32/13014_2.png) [@Jan\_Pfeifer](https://discourse.jupyter.org/u/Jan_Pfeifer)\
**Post date:** [October 9, 2023, 7:43am UTC](https://discourse.jupyter.org/t/how-does-nbconvert-executes-javacript-can-i-see-the-js-console-output/21700/9 "2023-10-09T07:43:47Z")

</div>

Thanks for the pointer, I suspected there should exist an easier way. I just randomly browsed code around and tried, and found out that `jupyterapp.commandLinker._commands.execute()` did the job 🙂

I’m changing it to use the public `jupyterapp.commands.execute` instead.
