Minimal plugin "provides" not working?

Hi All,

I have an extremely simple setup with two projects, myextension and myextension2 generated from the cookiecutter template (GitHub - jupyterlab/extension-cookiecutter-ts: A cookiecutter recipe for JupyterLab extensions in Typescript).

I have a clean conda environment where I run pip install . in the folder for both projects and both are installed and enabled:

jupyter labextension list                                        
JupyterLab v3.2.9
/Users/dleen/miniconda3/envs/myextension/share/jupyter/labextensions
        myextension2 v0.1.0 enabled OK (python, myextension2)
        myextension v0.1.0 enabled OK (python, myextension)

and versions:

jupyter --version                             
Selected Jupyter core packages...
IPython          : 8.0.1
ipykernel        : 6.9.1
ipywidgets       : not installed
jupyter_client   : 7.1.2
jupyter_core     : 4.9.2
jupyter_server   : 1.13.5
jupyterlab       : 3.2.9
nbclient         : 0.5.11
nbconvert        : 6.4.2
nbformat         : 5.1.3
notebook         : 6.4.8
qtconsole        : not installed
traitlets        : 5.1.1

When I open JupyterLab in the browser I see:

JupyterLab extension myextension is activated!
JupyterLab extension myextension2 is activated!

All is good so far.

In myextension I add a token for provides:

import { Token } from '@lumino/coreutils';

export interface Foo { }

export const Foo = new Token<Foo>("myId");

const plugin: JupyterFrontEndPlugin<Foo> = {
  id: "myextension:plugin",
  autoStart: true,
  requires: [],
  provides: Foo,
  activate: () => {
    return {} as Foo;
  },
}

and I consume it in myextension2:

import { Foo } from 'myextension';

const plugin: JupyterFrontEndPlugin<void> = {
  id: 'myextension2:plugin',
  autoStart: true,
  requires: [Foo],
  activate: (app: JupyterFrontEnd, foo: Foo) => {
  }
};

In order to get import { Foo } from 'myextension'; working in myextension2 I did the following:

In myextension: yarn link.
In myextension2: yarn link myextension.

I’m not 100% sure if this was the correct way to do it for JupyterLab, but it allowed the build to succeed.

Finally loading JupyterLab I unfortunately see:

Plugin 'myextension2:plugin' failed to activate.
Error: No provider for: myId.

I’m all out of ideas for why this isn’t working.

If I just have a single package where both plugins are defined, one being the producer and the other being the consumer it works fine. Something goes wrong when I split it into two packages. Is there something wrong with my setup?

Thanks,
David.

I think I figured it out after staring at the webpacked javascript for a while…

In the generated output of myextension2 it was bundling the code from myextension. Clearly having two versions of the same extension was not right. I recalled seeing some mention of sharedPackages somewhere.

Long story short the solution is to add the following to myextension2's package.json:

  "jupyterlab": {
    "extension": true,
    "outputDir": "myextension2/labextension",
    "sharedPackages": {
      "myextension": {
        "bundled": false,
        "singleton": true
      }
    }
  },

The sharedPackages section lets the bundler know that myextension will already be loaded by JupyterLab so don’t include a copy of it.

After making this change everything seems to work ok!

For more info, please see deduplication of dependencies, and the requiring a service pattern in particular.