Skip to content

Commit

Permalink
Merge pull request #3508 from jasongrout/requirepublic
Browse files Browse the repository at this point in the history
Better logic to set webpack public path in AMD modules
  • Loading branch information
ibdafna authored Jul 6, 2022
2 parents 22262e8 + 803cc66 commit 763dbb9
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 26 deletions.
24 changes: 24 additions & 0 deletions examples/embed-amd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Using the HTML widget manager as a RequireJS AMD module

## Description

This is an example project showing how to embed widgets in an HTML document using a RequireJS AMD module.

In order to test the current development repo, make a symbolic link from the `packages/html-manager` directory to this directory and uncomment the `html-manager` paths config in `index.html`.

The widget data in this example was generated from the following code:

```python
from ipywidgets import VBox, jsdlink, IntSlider, Button

s1, s2 = IntSlider(max=200, value=100), IntSlider(value=40)
b = Button(icon='legal')
jsdlink((s1, 'value'), (s2, 'max'))
VBox([s1, s2, b])
```

## Try it

1. Start with a repository checkout, and run `yarn install` in the root directory.
2. Run `yarn run build:examples` in the root directory.
3. Open the `index.html` file in this directory.
167 changes: 167 additions & 0 deletions examples/embed-amd/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF8">
<style>
.jupyter-widgetarea {
margin: 5px;
margin-left: auto;
margin-right: auto;
max-width: 900px;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.js"></script>
<script>
require.config({
bundles: {
'@jupyter-widgets/html-manager/dist/embed-amd': [
'@jupyter-widgets/html-manager',
'@jupyter-widgets/base',
'@jupyter-widgets/controls'
]
},
paths: {
'@jupyter-widgets/html-manager': [
// 'html-manager', // if a symbolic link is set up to the html-manager package for local dev
'https://cdn.jsdelivr.net/npm/@jupyter-widgets/html-manager',
]
},
})
require(["@jupyter-widgets/html-manager/dist/embed-amd"], () => {
console.log('Processing widgets on page');
});
</script>
<script type="application/vnd.jupyter.widget-state+json">
{
"version_major": 2,
"version_minor": 0,
"state": {
"1d915e54eff54fd89e505a46ccabdabd": {
"model_name": "LayoutModel",
"model_module": "@jupyter-widgets/base",
"model_module_version": "2.0.0",
"state": {}
},
"48a42260652f4b7eb7851c65cd155604": {
"model_name": "SliderStyleModel",
"model_module": "@jupyter-widgets/controls",
"model_module_version": "2.0.0",
"state": {
"description_width": ""
}
},
"105655a5e8dc4b7bb19d824cc3ff7770": {
"model_name": "IntSliderModel",
"model_module": "@jupyter-widgets/controls",
"model_module_version": "2.0.0",
"state": {
"value": 100,
"max": 200,
"style": "IPY_MODEL_48a42260652f4b7eb7851c65cd155604",
"behavior": "drag-tap",
"layout": "IPY_MODEL_1d915e54eff54fd89e505a46ccabdabd"
}
},
"cb13b25cf84542ba882ab2a9c6e57c6d": {
"model_name": "LayoutModel",
"model_module": "@jupyter-widgets/base",
"model_module_version": "2.0.0",
"state": {}
},
"f0479b348e2441cd87e1bd856fac5c22": {
"model_name": "SliderStyleModel",
"model_module": "@jupyter-widgets/controls",
"model_module_version": "2.0.0",
"state": {
"description_width": ""
}
},
"2182c1a3fe4a410f9b0a5306ae05c530": {
"model_name": "IntSliderModel",
"model_module": "@jupyter-widgets/controls",
"model_module_version": "2.0.0",
"state": {
"value": 40,
"style": "IPY_MODEL_f0479b348e2441cd87e1bd856fac5c22",
"behavior": "drag-tap",
"layout": "IPY_MODEL_cb13b25cf84542ba882ab2a9c6e57c6d"
}
},
"5f2da4ad981b467cb2d4f07efe5141f4": {
"model_name": "LayoutModel",
"model_module": "@jupyter-widgets/base",
"model_module_version": "2.0.0",
"state": {}
},
"e5f63e1e06af400aac8135ff3394b856": {
"model_name": "ButtonStyleModel",
"model_module": "@jupyter-widgets/controls",
"model_module_version": "2.0.0",
"state": {
"font_family": null,
"font_size": null,
"font_style": null,
"font_variant": null,
"font_weight": null,
"text_color": null,
"text_decoration": null
}
},
"891a12a9856949b4be2e520f732dcca9": {
"model_name": "ButtonModel",
"model_module": "@jupyter-widgets/controls",
"model_module_version": "2.0.0",
"state": {
"tooltip": null,
"icon": "legal",
"style": "IPY_MODEL_e5f63e1e06af400aac8135ff3394b856",
"layout": "IPY_MODEL_5f2da4ad981b467cb2d4f07efe5141f4"
}
},
"e8a6db8ff7bd4645b5b23ccb797dee9c": {
"model_name": "DirectionalLinkModel",
"model_module": "@jupyter-widgets/controls",
"model_module_version": "2.0.0",
"state": {
"target": [
"IPY_MODEL_2182c1a3fe4a410f9b0a5306ae05c530",
"max"
],
"source": [
"IPY_MODEL_105655a5e8dc4b7bb19d824cc3ff7770",
"value"
]
}
},
"b9445ea442bc4a5aae73c1e2241c3922": {
"model_name": "LayoutModel",
"model_module": "@jupyter-widgets/base",
"model_module_version": "2.0.0",
"state": {}
},
"a08a1974ba01461c8d9b91b8bfa0f6ce": {
"model_name": "VBoxModel",
"model_module": "@jupyter-widgets/controls",
"model_module_version": "2.0.0",
"state": {
"children": [
"IPY_MODEL_105655a5e8dc4b7bb19d824cc3ff7770",
"IPY_MODEL_2182c1a3fe4a410f9b0a5306ae05c530",
"IPY_MODEL_891a12a9856949b4be2e520f732dcca9"
],
"layout": "IPY_MODEL_b9445ea442bc4a5aae73c1e2241c3922"
}
}
}
}
</script>
<script type="application/vnd.jupyter.widget-view+json">
{
"version_major": 2,
"version_minor": 0,
"model_id": "a08a1974ba01461c8d9b91b8bfa0f6ce"
}
</script>
</body>
</html>
8 changes: 8 additions & 0 deletions packages/html-manager/amd-public-path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// In an AMD module, we set the public path using the magic requirejs 'module' dependency
// See https://github.com/requirejs/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#module
// Since 'module' is a requirejs magic module, we must include 'module' in the webpack externals configuration.
var module = require('module');
var url = new URL(module.uri, document.location);
// Using lastIndexOf('/')+1 gives us the empty string if there is no '/', so pathname becomes '/'
url.pathname = url.pathname.slice(0, url.pathname.lastIndexOf('/') + 1);
__webpack_public_path__ = `${url.origin}${url.pathname}`;
59 changes: 34 additions & 25 deletions packages/html-manager/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@

var path = require('path');

var rules = [
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
// required to load font-awesome
{ test: /\.(woff|woff2|eot|ttf|otf)$/i, type: 'asset/resource' },
{ test: /\.svg$/i, type: 'asset' },
];
var options = {
devtool: 'source-map',
mode: 'production',
module: {
rules: [
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
// required to load font-awesome
{ test: /\.(woff|woff2|eot|ttf|otf)$/i, type: 'asset/resource' },
{ test: /\.svg$/i, type: 'asset' },
],
},
};

module.exports = [
{
Expand All @@ -20,9 +26,7 @@ module.exports = [
filename: 'embed.js',
path: path.resolve(__dirname, 'dist'),
},
devtool: 'source-map',
module: { rules: rules },
mode: 'production',
...options,
},
{
// script that renders widgets using the amd embedding and can render third-party custom widgets
Expand All @@ -31,57 +35,62 @@ module.exports = [
filename: 'embed-amd-render.js',
path: path.resolve(__dirname, 'dist', 'amd'),
},
module: { rules: rules },
mode: 'production',
...options,
},
{
// embed library that depends on requirejs, and can load third-party widgets dynamically
entry: './lib/libembed-amd.js',
entry: ['./amd-public-path.js', './lib/libembed-amd.js'],
output: {
library: '@jupyter-widgets/html-manager/dist/libembed-amd',
filename: 'libembed-amd.js',
path: path.resolve(__dirname, 'dist', 'amd'),
libraryTarget: 'amd',
publicPath: '', // Set in amd-public-path.js
},
module: { rules: rules },
mode: 'production',
// 'module' is the magic requirejs dependency used to set the publicPath
externals: ['module'],
...options,
},
{
// @jupyter-widgets/html-manager
entry: './lib/index.js',
entry: ['./amd-public-path.js', './lib/index.js'],
output: {
library: '@jupyter-widgets/html-manager',
filename: 'index.js',
path: path.resolve(__dirname, 'dist', 'amd'),
libraryTarget: 'amd',
publicPath: '', // Set in amd-public-path.js
},
module: { rules: rules },
externals: ['@jupyter-widgets/base', '@jupyter-widgets/controls'],
mode: 'production',
// 'module' is the magic requirejs dependency used to set the publicPath
externals: ['@jupyter-widgets/base', '@jupyter-widgets/controls', 'module'],
...options,
},
{
// @jupyter-widgets/base
entry: '@jupyter-widgets/base/lib/index',
entry: ['./amd-public-path.js', '@jupyter-widgets/base/lib/index'],
output: {
library: '@jupyter-widgets/base',
filename: 'base.js',
path: path.resolve(__dirname, 'dist', 'amd'),
libraryTarget: 'amd',
publicPath: '', // Set in amd-public-path.js
},
module: { rules: rules },
mode: 'production',
// 'module' is the magic requirejs dependency used to set the publicPath
externals: ['module'],
...options,
},
{
// @jupyter-widgets/controls
entry: '@jupyter-widgets/controls/lib/index',
entry: ['./amd-public-path.js', '@jupyter-widgets/controls/lib/index'],
output: {
library: '@jupyter-widgets/controls',
filename: 'controls.js',
path: path.resolve(__dirname, 'dist', 'amd'),
libraryTarget: 'amd',
publicPath: '', // Set in amd-public-path.js
},
module: { rules: rules },
externals: ['@jupyter-widgets/base'],
mode: 'production',
// 'module' is the magic requirejs dependency used to set the publicPath
externals: ['@jupyter-widgets/base', 'module'],
...options,
},
];
8 changes: 8 additions & 0 deletions python/widgetsnbextension/amd-public-path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// In an AMD module, we set the public path using the magic requirejs 'module' dependency
// See https://github.com/requirejs/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#module
// Since 'module' is a requirejs magic module, we must include 'module' in the webpack externals configuration.
var module = require('module');
var url = new URL(module.uri, document.location);
// Using lastIndexOf('/')+1 gives us the empty string if there is no '/', so pathname becomes '/'
url.pathname = url.pathname.slice(0, url.pathname.lastIndexOf('/') + 1);
__webpack_public_path__ = `${url.origin}${url.pathname}`;
5 changes: 4 additions & 1 deletion python/widgetsnbextension/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
var path = require('path');
module.exports = {
entry: './src/extension.js',
entry: ['./amd-public-path.js', './src/extension.js'],
output: {
filename: 'extension.js',
path: path.resolve(__dirname, 'widgetsnbextension', 'static'),
libraryTarget: 'amd',
publicPath: '', // Set in amd-public-path.js
},
devtool: 'source-map',
module: {
Expand All @@ -15,4 +16,6 @@ module.exports = {
{ test: /\.svg$/i, type: 'asset' },
],
},
// 'module' is the magic requirejs dependency used to set the publicPath
externals: ['module'],
};

0 comments on commit 763dbb9

Please sign in to comment.