Skip to content

Commit 64d58bb

Browse files
committed
fix: require is not defined in widgets
1 parent b6be36f commit 64d58bb

6 files changed

Lines changed: 117 additions & 6 deletions

File tree

packages/docs/astro.config.mjs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,5 +134,16 @@ export default defineConfig({
134134
define: {
135135
__webpack_public_path__: '""',
136136
},
137+
build: {
138+
commonjsOptions: {
139+
// @jupyter-widgets/* (and some of their @jupyterlab transitives) ship
140+
// ESM modules that still call `require()` at runtime — Rollup leaves
141+
// those untouched by default, which surfaces in production as
142+
// `ReferenceError: require is not defined`. transformMixedEsModules
143+
// lets the CommonJS plugin rewrite those `require` calls into static
144+
// imports at build time.
145+
transformMixedEsModules: true,
146+
},
147+
},
137148
},
138149
});

packages/docs/src/content/docs/install-wizard.mdx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,14 +232,19 @@ import { createWidgetsPlugin } from '@jupyter-kit/widgets';
232232
import { createPyodideExecutor } from '@jupyter-kit/executor-pyodide';
233233
234234
<Notebook
235-
executor={createPyodideExecutor()}
235+
// Preloading `ipywidgets` is required for live mode — `autoloadImports`
236+
// doesn't reliably see `from ipywidgets import ...`, and the comm shim
237+
// must be wired before any cell runs. See the widgets reference for why.
238+
executor={createPyodideExecutor({ packages: ['ipywidgets'] })}
236239
plugins={[createWidgetsPlugin()]}
237240
...
238241
/>
239242
```
240243

241244
See the [widgets gallery demo](/demos/pyodide/) (select _widgets-gallery_
242-
in the fixture dropdown).
245+
in the fixture dropdown) and the
246+
[widgets reference](/reference/widgets/#live-pyodide) for the preload
247+
gotcha.
243248

244249
## Upgrading CodeMirror, React, etc.
245250

packages/docs/src/content/docs/reference/widgets.mdx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,65 @@ import { createWidgetsPlugin } from '@jupyter-kit/widgets';
4444

4545
The executor's `commProvider` is auto-detected — no extra wiring.
4646

47+
:::caution[Preload `ipywidgets`]
48+
`packages: ['ipywidgets']` is required, not just decorative. Pyodide's
49+
`autoloadImports` scanner walks the cell source for top-level `import X`
50+
statements but **misses `from ipywidgets import ...`** in many widget
51+
notebooks (and even when it sees them, the comm shim must already be
52+
wired before the first `.observe()` call). Preloading guarantees the
53+
package is ready and the JS-side comm handler is hooked up before any
54+
cell runs.
55+
:::
56+
57+
## Bundler config (Vite, Rollup, webpack)
58+
59+
`@jupyter-widgets/*` ship as ESM that still calls `require()` at runtime
60+
in a few spots — a webpack-era hybrid pattern. This is **upstream
61+
behaviour, not something `@jupyter-kit` can normalize from this side**
62+
the `require()` calls live inside the published `@jupyter-widgets` builds.
63+
Modern bundlers don't inject a CJS shim for ESM by default, so without
64+
the right config you'll see one of these in the browser console:
65+
66+
```
67+
Uncaught ReferenceError: require is not defined
68+
Uncaught ReferenceError: __webpack_public_path__ is not defined
69+
```
70+
71+
### Vite (and Astro)
72+
73+
```ts
74+
// vite.config.ts (or `vite:` block in astro.config)
75+
export default defineConfig({
76+
build: {
77+
commonjsOptions: {
78+
// Rewrites `require()` calls inside ESM-flagged files into static
79+
// imports at build time. Required for @jupyter-widgets/*.
80+
transformMixedEsModules: true,
81+
},
82+
},
83+
define: {
84+
// The widget bundle references `__webpack_public_path__` for chunk
85+
// URLs. Vite has no such global; replace with an empty string.
86+
__webpack_public_path__: '""',
87+
},
88+
});
89+
```
90+
91+
`@jupyter-kit/widgets@3.0.0-rc.5+` already sets
92+
`globalThis.__webpack_public_path__ = ''` at module init as a runtime
93+
shim, so the `define` is belt-and-suspenders for older versions or
94+
unusual bundle splits.
95+
96+
### webpack
97+
98+
webpack handles both natively — no extra config required. Just install
99+
`@jupyter-kit/widgets` and use the `<Notebook>` component.
100+
101+
### Rollup (without Vite)
102+
103+
`@rollup/plugin-commonjs` with `transformMixedEsModules: true`. Pair with
104+
`@rollup/plugin-replace` for `__webpack_public_path__: '""'`.
105+
47106
## Bundle cost
48107

49108
The plugin pulls in `@jupyter-widgets/{base,base-manager,controls,html-manager,output}`

packages/storybook/.examples/with-pyodide.stories.tsx

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,26 @@ import { Notebook } from '@jupyter-kit/react';
44
import { python as pythonHighlight } from '@jupyter-kit/core/langs/python';
55
import { createEditorPlugin } from '@jupyter-kit/editor-codemirror';
66
import { createKatexPlugin } from '@jupyter-kit/katex';
7+
import { createWidgetsPlugin } from '@jupyter-kit/widgets';
78
import {
89
createPyodideExecutor,
910
type PyodideStatus,
1011
} from '@jupyter-kit/executor-pyodide';
1112
import { python as pythonEditor } from '@codemirror/lang-python';
1213

1314
import showcase from '@jupyter-kit/fixtures/ipynb/showcase';
15+
import lorenz from '@jupyter-kit/fixtures/ipynb/Lorenz';
16+
import widgetsGallery from '@jupyter-kit/fixtures/ipynb/widgets-gallery';
1417
import '@jupyter-kit/theme-default/default.css';
1518
import '@jupyter-kit/theme-default/syntax/one-dark.css';
1619
import 'katex/dist/katex.min.css';
1720

21+
const FIXTURES: Record<string, unknown> = {
22+
showcase,
23+
Lorenz: lorenz,
24+
'widgets-gallery': widgetsGallery,
25+
};
26+
1827
const meta: Meta = {
1928
title: 'Executor / Pyodide',
2029
component: Notebook,
@@ -24,10 +33,16 @@ export default meta;
2433
const Template = (preinstall: string[] = []) =>
2534
function Story() {
2635
const [status, setStatus] = useState<PyodideStatus>('idle');
36+
const [fixture, setFixture] = useState<string>('showcase');
2737
const executor = useMemo(
2838
() =>
2939
createPyodideExecutor({
30-
packages: preinstall,
40+
// `ipywidgets` is a pyodide-bundled package but `loadPackagesFromImports`
41+
// only sees it when `import ipywidgets` appears literally in the cell
42+
// source (the widgets-gallery fixture writes `from ipywidgets import ...`,
43+
// and the scanner catches that — but we preload anyway so the comm shim
44+
// is wired up before any cell runs).
45+
packages: ['ipywidgets', ...preinstall],
3146
autoloadImports: true,
3247
onStatus: setStatus,
3348
}),
@@ -36,6 +51,7 @@ const Template = (preinstall: string[] = []) =>
3651
const plugins = useMemo(
3752
() => [
3853
createKatexPlugin(),
54+
createWidgetsPlugin(),
3955
createEditorPlugin({ languages: { python: pythonEditor() } }),
4056
],
4157
[],
@@ -44,16 +60,32 @@ const Template = (preinstall: string[] = []) =>
4460
<div>
4561
<div
4662
style={{
63+
display: 'flex',
64+
gap: 12,
65+
alignItems: 'center',
4766
padding: '8px 12px',
4867
fontFamily: 'monospace',
4968
fontSize: 12,
50-
opacity: 0.7,
5169
}}
5270
>
53-
pyodide: {status}
71+
<label>
72+
fixture:{' '}
73+
<select
74+
value={fixture}
75+
onChange={(e) => setFixture(e.target.value)}
76+
style={{ fontFamily: 'inherit', fontSize: 12 }}
77+
>
78+
{Object.keys(FIXTURES).map((k) => (
79+
<option key={k} value={k}>
80+
{k}
81+
</option>
82+
))}
83+
</select>
84+
</label>
85+
<span style={{ opacity: 0.7 }}>pyodide: {status}</span>
5486
</div>
5587
<Notebook
56-
ipynb={showcase as never}
88+
ipynb={FIXTURES[fixture] as never}
5789
language="python"
5890
languages={[pythonHighlight]}
5991
plugins={plugins}

packages/storybook/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"@jupyter-kit/mathjax-cdn": "workspace:*",
2727
"@jupyter-kit/react": "workspace:*",
2828
"@jupyter-kit/theme": "workspace:*",
29+
"@jupyter-kit/widgets": "workspace:*",
2930
"katex": "^0.16.9",
3031
"react": "^18.3.1",
3132
"react-dom": "^18.3.1"

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)