Can an ipywidget source its JavaScript from a URL, rather than local disk?

I’m building an ipywidget to be shared with some coworkers on a private repo. I’d like to have it source JavaScript from a server, rather than requiring my coworkers to build the JS bundle locally, and I’m wondering if this is possible.

Here’s my setup. I’ve created a directory (test-extension) and enabled it for development with Jupyter:

jupyter nbextension install test-extension --sys-prefix --symlink
jupyter nbextension enable test-extension/main --sys-prefix

Following the Custom Widget Tutorial, I can create a custom widget with some Python:

from traitlets import Unicode, Bool, validate, TraitError
from ipywidgets import DOMWidget, register

@register
class Email(DOMWidget):
    _view_name = Unicode('EmailView').tag(sync=True)
    _view_module = Unicode('email_widget').tag(sync=True)
    _view_module_version = Unicode('0.1.0').tag(sync=True)

    value = Unicode('example@example.com', help="The email value.").tag(sync=True)

and some JavaScript, in test-extension/main.js:

define('email_widget', ["@jupyter-widgets/base"], function(widgets) {
    var EmailView = widgets.DOMWidgetView.extend({

        // Render the view.
        render: function() {
            this.email_input = document.createElement('input');
            this.email_input.type = 'email';
            this.email_input.value = this.model.get('value');
            this.el.appendChild(this.email_input);
        },
    });

    return {
        EmailView: EmailView
    };
});

In my real application, this main.js file is generated by webpack and is 10-20MB. I’d rather not require my coworkers to rebuild the widget JS whenever anything changes. But since it’s so large, I’d prefer not to commit it to GitHub. It’s for a private repo, so I don’t want to publish it on PyPI. One idea is to host the JS on an internal server and have the widget source it from there.

Is this possible with ipywidgets? Can a widget source the JS for its _view_module from a URL, rather than from local disk? ipywidgets uses RequireJS as a module loader, so a solution involving that would work, too. Or is there a better solution that I’ve overlooked?

Answer

I’m no expert of ipywidgets, but I’m quite experienced with JS. Allow me to make an educated guess.

My theory is python part of widget is only loosely coupled with js part, or more precisely put, the dependency resolution is lazy. Thus it wouldn’t break anything even if you asynchronously load the js part of code.

So my proposed solution is dead simple, two steps:

  1. Put your bulky main.js on a remote server, let’s say the URL would be http://my-server.com/test-extension/main.js
  2. You still need to keep a main.js in your repo, but it’s just a tiny loader that fetches the real main.js from remote server. Here’s the loader code:
var url = "http://my-server.com/test-extension/main.js";
var head = document.head;
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;

Everything else remains the same. Please try it out to see if my theory is right. Would love to know the result!