Repository: plotly/plotly.py

Issue:
Effect on JupyterLab bundle size
Hi!

I've been looking at the effect of the plotly jupyterlab extensions on the final bundle size, and I've made these observations:
- The plotly.js part for a prod build is roughly 17 MB.
- Both `plotly.js/dist/plotly.js` and `plotly.js/dist/plotly.min.js` gets included in the final lab bundle (one from plotlywidget and one from jupyterlab-plotly). Both end up taking roughly equal amount of space (6-7 MB) when the lab build is run in prod mode.
- Also some parts of the `plotly.js/src` dir gets included (from plotlywidget).

Would it be possible to consolidate the imports in the jupyterlab packages such that only one of the dists gets imported from (and if possible also the `src` import)?

load plotly.js from unminified dist bundle in plotlywidget
With this PR, both `jupyterlab-plotly` and `plotlywidget` now load plotly.js from the unminified dist bundle, so jupyterlab should be able to de-duplicate plotly.js when both extensions are installed.

Closes https://github.com/plotly/plotly.py/issues/1873

cc @vidartf


Discussion:
Yikes, we should be using the `min` version everywhere for sure. The duplication is a bummer. 

@jonmmease could we imagine merging these extensions someday?
> could we imagine merging these extensions someday?

Yes, and we almost did for v4.  But the distinct plotlywidget package is needed in order for nbviewer to find the extension, and I didn't spend enough time on it to figure out how to combine things in such a way that wouldn't break something :slightly_smiling_face: 
Ah right. Does JLab have a way for extensions share dependencies yet that we know of?
The packages doesn't need to be merged to resolve this, they just need to import from the same dist. If the dependency versions are compatible, jupyterlab will de-duplicate them.
Note: even now the dependency *package* is already de-duplicated, but the *code* is duplicated in the final bundle since it is imported from two different dists.
Ah nice. So if we changed https://github.com/plotly/plotly.py/blob/master/packages/javascript/plotlywidget/src/Figure.js#L5 to load the unminified bundle and not from `src` then in principle webpack should dedupe and minify all of Plotly. We'll try! Probably for 4.4 :)
Sounds like a plan. Please ping this issue with any progress, and I'll be happy to verify if it works :)
So I'm trying to tackle this and having some problems... I've uninstalled and reinstalled both extensions from NPM and right now my vendor bundle sits at 11MB which is smaller that I would expect. I'm also having trouble analyzing it to understand what's in it... @vidartf how were you able to determine that Plotly is in there twice?
Is there a way to `jupyter lab build` such that it outputs the webpack bundle analyzer stats?
Some data from `ls ~/miniconda2/envs/dash/share/jupyter/lab/static`

### Base install - 4.4 MB vendor file

```
JupyterLab v1.2.0
Known labextensions:
   app dir: /Users/nicolas/miniconda2/envs/dash/share/jupyter/lab
        @jupyter-widgets/jupyterlab-manager v1.1.0  enabled  OK
        jupyterlab-jupytext v1.1.1  enabled  OK
```

### With `jupyterlab-plotly` from NPM  - 7.7MB

```
JupyterLab v1.2.0
Known labextensions:
   app dir: /Users/nicolas/miniconda2/envs/dash/share/jupyter/lab
        @jupyter-widgets/jupyterlab-manager v1.1.0  enabled  OK
        jupyterlab-jupytext v1.1.1  enabled  OK
        jupyterlab-plotly v1.4.0  enabled  OK
```

### With `jupyterlab-plotly` and `plotlywidget` from NPM - 11MB

```
JupyterLab v1.2.0
Known labextensions:
   app dir: /Users/nicolas/miniconda2/envs/dash/share/jupyter/lab
        @jupyter-widgets/jupyterlab-manager v1.1.0  enabled  OK
        jupyterlab-jupytext v1.1.1  enabled  OK
        jupyterlab-plotly v1.4.0  enabled  OK
        plotlywidget v1.4.0  enabled  OK
```

... OK so definitely there's less deduplication happening here than we'd like ;)
So the `index.js` file is now larger (7 Mo instead of 4). Is this a problem (for other environments, eg Dash applications etc.). Probably a naive question :-)
@nicolaskruchten any thoughts on this. We could use the `min` bundle in both extensions instead of the regular one. My thinking was that this should be getting compressed everywhere it matters, but I don't have a strong opinion here.
Perhaps when making this change, please also consider registering the exports in the lab plugin with a [lazy load](https://webpack.js.org/guides/code-splitting/#dynamic-imports): this will keep it from 
- hitting the browser until it's actually requested
- getting compiled in with the already-titanic `vendor~main`

Here's how it interacts with the crucial `loadClass`: https://github.com/jupyter-widgets/ipywidgets/blob/master/packages/jupyterlab-manager/src/manager.ts#L383

Since it appears to be commonjs, you may have to use the vendor-specific `webpack.ensure` (or `require.ensure`?) rather than the standards-based `await import`. But doing it at the widget load level is far more convenient than trying to do it within the backbone-related code, which doesn't async much. 
Here's an example with `jupyter-threejs`:

https://github.com/jupyter-widgets/pythreejs/pull/297
@nrbgt While I agree with your point, I believe it should be made as a separate issue.
@vidartf made issue #1913

Resolving patch:
diff --git a/packages/python/plotly/setup.py b/packages/python/plotly/setup.py
--- a/packages/python/plotly/setup.py
+++ b/packages/python/plotly/setup.py
@@ -137,6 +137,12 @@ def run(self):
                 stdout=sys.stdout,
                 stderr=sys.stderr,
             )
+            check_call(
+                [npmName, "run", "build"],
+                cwd=node_root,
+                stdout=sys.stdout,
+                stderr=sys.stderr,
+            )
             os.utime(self.node_modules, None)
 
         for t in self.targets:


Test patch: