Skip to content

Commit 763dbb9

Browse files
authored
Merge pull request #3508 from jasongrout/requirepublic
Better logic to set webpack public path in AMD modules
2 parents 22262e8 + 803cc66 commit 763dbb9

File tree

6 files changed

+245
-26
lines changed

6 files changed

+245
-26
lines changed

examples/embed-amd/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Using the HTML widget manager as a RequireJS AMD module
2+
3+
## Description
4+
5+
This is an example project showing how to embed widgets in an HTML document using a RequireJS AMD module.
6+
7+
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`.
8+
9+
The widget data in this example was generated from the following code:
10+
11+
```python
12+
from ipywidgets import VBox, jsdlink, IntSlider, Button
13+
14+
s1, s2 = IntSlider(max=200, value=100), IntSlider(value=40)
15+
b = Button(icon='legal')
16+
jsdlink((s1, 'value'), (s2, 'max'))
17+
VBox([s1, s2, b])
18+
```
19+
20+
## Try it
21+
22+
1. Start with a repository checkout, and run `yarn install` in the root directory.
23+
2. Run `yarn run build:examples` in the root directory.
24+
3. Open the `index.html` file in this directory.

examples/embed-amd/index.html

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<html>
2+
<head>
3+
<meta http-equiv="content-type" content="text/html; charset=UTF8">
4+
<style>
5+
.jupyter-widgetarea {
6+
margin: 5px;
7+
margin-left: auto;
8+
margin-right: auto;
9+
max-width: 900px;
10+
}
11+
</style>
12+
</head>
13+
<body>
14+
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.js"></script>
15+
<script>
16+
require.config({
17+
bundles: {
18+
'@jupyter-widgets/html-manager/dist/embed-amd': [
19+
'@jupyter-widgets/html-manager',
20+
'@jupyter-widgets/base',
21+
'@jupyter-widgets/controls'
22+
]
23+
},
24+
paths: {
25+
'@jupyter-widgets/html-manager': [
26+
// 'html-manager', // if a symbolic link is set up to the html-manager package for local dev
27+
'https://cdn.jsdelivr.net/npm/@jupyter-widgets/html-manager',
28+
]
29+
},
30+
})
31+
require(["@jupyter-widgets/html-manager/dist/embed-amd"], () => {
32+
console.log('Processing widgets on page');
33+
});
34+
</script>
35+
<script type="application/vnd.jupyter.widget-state+json">
36+
{
37+
"version_major": 2,
38+
"version_minor": 0,
39+
"state": {
40+
"1d915e54eff54fd89e505a46ccabdabd": {
41+
"model_name": "LayoutModel",
42+
"model_module": "@jupyter-widgets/base",
43+
"model_module_version": "2.0.0",
44+
"state": {}
45+
},
46+
"48a42260652f4b7eb7851c65cd155604": {
47+
"model_name": "SliderStyleModel",
48+
"model_module": "@jupyter-widgets/controls",
49+
"model_module_version": "2.0.0",
50+
"state": {
51+
"description_width": ""
52+
}
53+
},
54+
"105655a5e8dc4b7bb19d824cc3ff7770": {
55+
"model_name": "IntSliderModel",
56+
"model_module": "@jupyter-widgets/controls",
57+
"model_module_version": "2.0.0",
58+
"state": {
59+
"value": 100,
60+
"max": 200,
61+
"style": "IPY_MODEL_48a42260652f4b7eb7851c65cd155604",
62+
"behavior": "drag-tap",
63+
"layout": "IPY_MODEL_1d915e54eff54fd89e505a46ccabdabd"
64+
}
65+
},
66+
"cb13b25cf84542ba882ab2a9c6e57c6d": {
67+
"model_name": "LayoutModel",
68+
"model_module": "@jupyter-widgets/base",
69+
"model_module_version": "2.0.0",
70+
"state": {}
71+
},
72+
"f0479b348e2441cd87e1bd856fac5c22": {
73+
"model_name": "SliderStyleModel",
74+
"model_module": "@jupyter-widgets/controls",
75+
"model_module_version": "2.0.0",
76+
"state": {
77+
"description_width": ""
78+
}
79+
},
80+
"2182c1a3fe4a410f9b0a5306ae05c530": {
81+
"model_name": "IntSliderModel",
82+
"model_module": "@jupyter-widgets/controls",
83+
"model_module_version": "2.0.0",
84+
"state": {
85+
"value": 40,
86+
"style": "IPY_MODEL_f0479b348e2441cd87e1bd856fac5c22",
87+
"behavior": "drag-tap",
88+
"layout": "IPY_MODEL_cb13b25cf84542ba882ab2a9c6e57c6d"
89+
}
90+
},
91+
"5f2da4ad981b467cb2d4f07efe5141f4": {
92+
"model_name": "LayoutModel",
93+
"model_module": "@jupyter-widgets/base",
94+
"model_module_version": "2.0.0",
95+
"state": {}
96+
},
97+
"e5f63e1e06af400aac8135ff3394b856": {
98+
"model_name": "ButtonStyleModel",
99+
"model_module": "@jupyter-widgets/controls",
100+
"model_module_version": "2.0.0",
101+
"state": {
102+
"font_family": null,
103+
"font_size": null,
104+
"font_style": null,
105+
"font_variant": null,
106+
"font_weight": null,
107+
"text_color": null,
108+
"text_decoration": null
109+
}
110+
},
111+
"891a12a9856949b4be2e520f732dcca9": {
112+
"model_name": "ButtonModel",
113+
"model_module": "@jupyter-widgets/controls",
114+
"model_module_version": "2.0.0",
115+
"state": {
116+
"tooltip": null,
117+
"icon": "legal",
118+
"style": "IPY_MODEL_e5f63e1e06af400aac8135ff3394b856",
119+
"layout": "IPY_MODEL_5f2da4ad981b467cb2d4f07efe5141f4"
120+
}
121+
},
122+
"e8a6db8ff7bd4645b5b23ccb797dee9c": {
123+
"model_name": "DirectionalLinkModel",
124+
"model_module": "@jupyter-widgets/controls",
125+
"model_module_version": "2.0.0",
126+
"state": {
127+
"target": [
128+
"IPY_MODEL_2182c1a3fe4a410f9b0a5306ae05c530",
129+
"max"
130+
],
131+
"source": [
132+
"IPY_MODEL_105655a5e8dc4b7bb19d824cc3ff7770",
133+
"value"
134+
]
135+
}
136+
},
137+
"b9445ea442bc4a5aae73c1e2241c3922": {
138+
"model_name": "LayoutModel",
139+
"model_module": "@jupyter-widgets/base",
140+
"model_module_version": "2.0.0",
141+
"state": {}
142+
},
143+
"a08a1974ba01461c8d9b91b8bfa0f6ce": {
144+
"model_name": "VBoxModel",
145+
"model_module": "@jupyter-widgets/controls",
146+
"model_module_version": "2.0.0",
147+
"state": {
148+
"children": [
149+
"IPY_MODEL_105655a5e8dc4b7bb19d824cc3ff7770",
150+
"IPY_MODEL_2182c1a3fe4a410f9b0a5306ae05c530",
151+
"IPY_MODEL_891a12a9856949b4be2e520f732dcca9"
152+
],
153+
"layout": "IPY_MODEL_b9445ea442bc4a5aae73c1e2241c3922"
154+
}
155+
}
156+
}
157+
}
158+
</script>
159+
<script type="application/vnd.jupyter.widget-view+json">
160+
{
161+
"version_major": 2,
162+
"version_minor": 0,
163+
"model_id": "a08a1974ba01461c8d9b91b8bfa0f6ce"
164+
}
165+
</script>
166+
</body>
167+
</html>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// In an AMD module, we set the public path using the magic requirejs 'module' dependency
2+
// See https://github.com/requirejs/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#module
3+
// Since 'module' is a requirejs magic module, we must include 'module' in the webpack externals configuration.
4+
var module = require('module');
5+
var url = new URL(module.uri, document.location);
6+
// Using lastIndexOf('/')+1 gives us the empty string if there is no '/', so pathname becomes '/'
7+
url.pathname = url.pathname.slice(0, url.pathname.lastIndexOf('/') + 1);
8+
__webpack_public_path__ = `${url.origin}${url.pathname}`;

packages/html-manager/webpack.config.js

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,18 @@
55

66
var path = require('path');
77

8-
var rules = [
9-
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
10-
// required to load font-awesome
11-
{ test: /\.(woff|woff2|eot|ttf|otf)$/i, type: 'asset/resource' },
12-
{ test: /\.svg$/i, type: 'asset' },
13-
];
8+
var options = {
9+
devtool: 'source-map',
10+
mode: 'production',
11+
module: {
12+
rules: [
13+
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
14+
// required to load font-awesome
15+
{ test: /\.(woff|woff2|eot|ttf|otf)$/i, type: 'asset/resource' },
16+
{ test: /\.svg$/i, type: 'asset' },
17+
],
18+
},
19+
};
1420

1521
module.exports = [
1622
{
@@ -20,9 +26,7 @@ module.exports = [
2026
filename: 'embed.js',
2127
path: path.resolve(__dirname, 'dist'),
2228
},
23-
devtool: 'source-map',
24-
module: { rules: rules },
25-
mode: 'production',
29+
...options,
2630
},
2731
{
2832
// script that renders widgets using the amd embedding and can render third-party custom widgets
@@ -31,57 +35,62 @@ module.exports = [
3135
filename: 'embed-amd-render.js',
3236
path: path.resolve(__dirname, 'dist', 'amd'),
3337
},
34-
module: { rules: rules },
35-
mode: 'production',
38+
...options,
3639
},
3740
{
3841
// embed library that depends on requirejs, and can load third-party widgets dynamically
39-
entry: './lib/libembed-amd.js',
42+
entry: ['./amd-public-path.js', './lib/libembed-amd.js'],
4043
output: {
4144
library: '@jupyter-widgets/html-manager/dist/libembed-amd',
4245
filename: 'libembed-amd.js',
4346
path: path.resolve(__dirname, 'dist', 'amd'),
4447
libraryTarget: 'amd',
48+
publicPath: '', // Set in amd-public-path.js
4549
},
46-
module: { rules: rules },
47-
mode: 'production',
50+
// 'module' is the magic requirejs dependency used to set the publicPath
51+
externals: ['module'],
52+
...options,
4853
},
4954
{
5055
// @jupyter-widgets/html-manager
51-
entry: './lib/index.js',
56+
entry: ['./amd-public-path.js', './lib/index.js'],
5257
output: {
5358
library: '@jupyter-widgets/html-manager',
5459
filename: 'index.js',
5560
path: path.resolve(__dirname, 'dist', 'amd'),
5661
libraryTarget: 'amd',
62+
publicPath: '', // Set in amd-public-path.js
5763
},
58-
module: { rules: rules },
59-
externals: ['@jupyter-widgets/base', '@jupyter-widgets/controls'],
60-
mode: 'production',
64+
// 'module' is the magic requirejs dependency used to set the publicPath
65+
externals: ['@jupyter-widgets/base', '@jupyter-widgets/controls', 'module'],
66+
...options,
6167
},
6268
{
6369
// @jupyter-widgets/base
64-
entry: '@jupyter-widgets/base/lib/index',
70+
entry: ['./amd-public-path.js', '@jupyter-widgets/base/lib/index'],
6571
output: {
6672
library: '@jupyter-widgets/base',
6773
filename: 'base.js',
6874
path: path.resolve(__dirname, 'dist', 'amd'),
6975
libraryTarget: 'amd',
76+
publicPath: '', // Set in amd-public-path.js
7077
},
71-
module: { rules: rules },
72-
mode: 'production',
78+
// 'module' is the magic requirejs dependency used to set the publicPath
79+
externals: ['module'],
80+
...options,
7381
},
7482
{
7583
// @jupyter-widgets/controls
76-
entry: '@jupyter-widgets/controls/lib/index',
84+
entry: ['./amd-public-path.js', '@jupyter-widgets/controls/lib/index'],
7785
output: {
7886
library: '@jupyter-widgets/controls',
7987
filename: 'controls.js',
8088
path: path.resolve(__dirname, 'dist', 'amd'),
8189
libraryTarget: 'amd',
90+
publicPath: '', // Set in amd-public-path.js
8291
},
83-
module: { rules: rules },
84-
externals: ['@jupyter-widgets/base'],
85-
mode: 'production',
92+
// 'module' is the magic requirejs dependency used to set the publicPath
93+
externals: ['@jupyter-widgets/base', 'module'],
94+
...options,
8695
},
8796
];
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// In an AMD module, we set the public path using the magic requirejs 'module' dependency
2+
// See https://github.com/requirejs/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#module
3+
// Since 'module' is a requirejs magic module, we must include 'module' in the webpack externals configuration.
4+
var module = require('module');
5+
var url = new URL(module.uri, document.location);
6+
// Using lastIndexOf('/')+1 gives us the empty string if there is no '/', so pathname becomes '/'
7+
url.pathname = url.pathname.slice(0, url.pathname.lastIndexOf('/') + 1);
8+
__webpack_public_path__ = `${url.origin}${url.pathname}`;

python/widgetsnbextension/webpack.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
var path = require('path');
22
module.exports = {
3-
entry: './src/extension.js',
3+
entry: ['./amd-public-path.js', './src/extension.js'],
44
output: {
55
filename: 'extension.js',
66
path: path.resolve(__dirname, 'widgetsnbextension', 'static'),
77
libraryTarget: 'amd',
8+
publicPath: '', // Set in amd-public-path.js
89
},
910
devtool: 'source-map',
1011
module: {
@@ -15,4 +16,6 @@ module.exports = {
1516
{ test: /\.svg$/i, type: 'asset' },
1617
],
1718
},
19+
// 'module' is the magic requirejs dependency used to set the publicPath
20+
externals: ['module'],
1821
};

0 commit comments

Comments
 (0)