diff --git a/src/utils.ts b/src/utils.ts index 00e2387..1915419 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -45,239 +45,21 @@ export async function setUpMuiFixModule() { return await importShim(url); } +export function expose(module: any) { + const id = "_ipyreact_" + (Math.random()).toString(36); + // @ts-ignore + window[id] = module; + const names = Object.keys(module).filter(n => n !== "default").join(", ") + return toModuleUrl(` + const { ${names} } = window["${id}"]; + export default window["${id}"].default; + delete window["${id}"]; + export { ${names} };`) +} - - -export async function setUpReact16ESM() { - let urlReact = URL.createObjectURL( - new Blob([` - let r = window.React16FromIpyReact; - const Children = r.Children; - const Component = r.Component; - const Fragment = r.Fragment; - const Profiler = r.Profiler; - const PureComponent = r.PureComponent; - const StrictMode = r.StrictMode; - const Suspense = r.Suspense; - const cloneElement = r.cloneElement; - const createContext = r.createContext; - const createElement = r.createElement; - const createFactory = r.createFactory; - const createRef = r.createRef; - const forwardRef = r.forwardRef; - const isValidElement = r.isValidElement; - const lazy = r.lazy; - const memo = r.memo; - const startTransition = r.startTransition; - const unstable_act = r.unstable_act; - const useCallback = r.useCallback; - const useContext = r.useContext; - const useDebugValue = r.useDebugValue; - const useDeferredValue = r.useDeferredValue; - const useEffect = r.useEffect; - const useId = r.useId; - const useImperativeHandle = r.useImperativeHandle; - const useInsertionEffect = r.useInsertionEffect; - const useLayoutEffect = r.useLayoutEffect; - const useMemo = r.useMemo; - const useReducer = r.useReducer; - const useRef = r.useRef; - const useState = r.useState; - const useSyncExternalStore = r.useSyncExternalStore; - const useTransition = r.useTransition; - const version = r.version; - const __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; - - export default r.default; - export { - Children, - Component, - Fragment, - Profiler, - PureComponent, - StrictMode, - Suspense, - cloneElement, - createContext, - createElement, - createFactory, - createRef, - forwardRef, - isValidElement, - lazy, - memo, - startTransition, - unstable_act, - useCallback, - useContext, - useDebugValue, - useDeferredValue, - useEffect, - useId, - useImperativeHandle, - useInsertionEffect, - useLayoutEffect, - useMemo, - useReducer, - useRef, - useState, - useSyncExternalStore, - useTransition, - version, - __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, - };` - ], { type: "text/javascript" }), - ); - let urlReactDom = URL.createObjectURL( - new Blob([` - let r = window.ReactDOM16FromIpyReact; - const createPortal = r.createPortal; - const createRoot = r.createRoot; - const findDOMNode = r.findDOMNode; - const flushSync = r.flushSync; - const hydrate = r.hydrate; - const hydrateRoot = r.hydrateRoot; - const render = r.render; - const unmountComponentAtNode = r.unmountComponentAtNode; - const unstable_batchedUpdates = r.unstable_batchedUpdates; - const unstable_renderSubtreeIntoContainer = r.unstable_renderSubtreeIntoContainer; - const version = r.version; - const __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; - export default r.default; - export { - createPortal, - createRoot, - findDOMNode, - flushSync, - hydrate, - hydrateRoot, - render, - unmountComponentAtNode, - unstable_batchedUpdates, - unstable_renderSubtreeIntoContainer, - version, - __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, - };` - ], { type: "text/javascript" }), - ); - return {urlReact, urlReactDom}; - } - - -export async function setUpReact18ESM() { - let urlReact = URL.createObjectURL( - new Blob([` - let r = window.React18FromIpyReact; - const Children = r.Children; - const Component = r.Component; - const Fragment = r.Fragment; - const Profiler = r.Profiler; - const PureComponent = r.PureComponent; - const StrictMode = r.StrictMode; - const Suspense = r.Suspense; - const cloneElement = r.cloneElement; - const createContext = r.createContext; - const createElement = r.createElement; - const createFactory = r.createFactory; - const createRef = r.createRef; - const forwardRef = r.forwardRef; - const isValidElement = r.isValidElement; - const lazy = r.lazy; - const memo = r.memo; - const startTransition = r.startTransition; - const unstable_act = r.unstable_act; - const useCallback = r.useCallback; - const useContext = r.useContext; - const useDebugValue = r.useDebugValue; - const useDeferredValue = r.useDeferredValue; - const useEffect = r.useEffect; - const useId = r.useId; - const useImperativeHandle = r.useImperativeHandle; - const useInsertionEffect = r.useInsertionEffect; - const useLayoutEffect = r.useLayoutEffect; - const useMemo = r.useMemo; - const useReducer = r.useReducer; - const useRef = r.useRef; - const useState = r.useState; - const useSyncExternalStore = r.useSyncExternalStore; - const useTransition = r.useTransition; - const version = r.version; - const __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; - - export default r.default; - export { - Children, - Component, - Fragment, - Profiler, - PureComponent, - StrictMode, - Suspense, - cloneElement, - createContext, - createElement, - createFactory, - createRef, - forwardRef, - isValidElement, - lazy, - memo, - startTransition, - unstable_act, - useCallback, - useContext, - useDebugValue, - useDeferredValue, - useEffect, - useId, - useImperativeHandle, - useInsertionEffect, - useLayoutEffect, - useMemo, - useReducer, - useRef, - useState, - useSyncExternalStore, - useTransition, - version, - __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, - };` - ], { type: "text/javascript" }), - ); - let urlReactDom = URL.createObjectURL( - new Blob([` - let r = window.ReactDOM18FromIpyReact; - const createPortal = r.createPortal; - const createRoot = r.createRoot; - const findDOMNode = r.findDOMNode; - const flushSync = r.flushSync; - const hydrate = r.hydrate; - const hydrateRoot = r.hydrateRoot; - const render = r.render; - const unmountComponentAtNode = r.unmountComponentAtNode; - const unstable_batchedUpdates = r.unstable_batchedUpdates; - const unstable_renderSubtreeIntoContainer = r.unstable_renderSubtreeIntoContainer; - const version = r.version; - const __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; - export default r.default; - export { - createPortal, - createRoot, - findDOMNode, - flushSync, - hydrate, - hydrateRoot, - render, - unmountComponentAtNode, - unstable_batchedUpdates, - unstable_renderSubtreeIntoContainer, - version, - __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, - };` - ], { type: "text/javascript" }), - ); - return {urlReact, urlReactDom}; - } +export function toModuleUrl(code: string) { + return URL.createObjectURL(new Blob([code], { type: "text/javascript" })); +} export async function loadScript(type: string, src: string) { const script = document.createElement("script") diff --git a/src/widget.tsx b/src/widget.tsx index 44a58f7..da75757 100644 --- a/src/widget.tsx +++ b/src/widget.tsx @@ -14,7 +14,7 @@ import * as ReactDOM from 'react-dom'; import * as ReactDOMClient from 'react-dom/client'; // @ts-ignore import '../css/widget.css'; -import { loadScript, setUpMuiFixModule, setUpReact16ESM, setUpReact18ESM } from './utils'; +import { expose, loadScript, setUpMuiFixModule } from './utils'; import { MODULE_NAME, MODULE_VERSION } from './version'; // import * as Babel from '@babel/standalone'; // TODO: find a way to ship es-module-shims with the widget @@ -62,17 +62,17 @@ window.esmsInitOptions = { shimMode: true, let react18ESMUrls : any = null; let react16ESMUrls : any = null; -async function ensureReactSetup(version: number) { +function ensureReactSetup(version: number) { if(version == 18) { if(react18ESMUrls == null) { - react18ESMUrls = setUpReact18ESM(); + react18ESMUrls = {urlReact: expose(React), urlReactDom: expose(ReactDOM)}; } - return await react18ESMUrls; + return react18ESMUrls; } else if(version == 16) { if(react16ESMUrls == null) { - react16ESMUrls = setUpReact16ESM(); + // react16ESMUrls = {urlReact: expose(React16), urlReactDom: expose(ReactDOM16)}; } - return await react16ESMUrls; + return react16ESMUrls; } } @@ -109,14 +109,6 @@ export class ReactModel extends DOMWidgetModel { export class ReactView extends DOMWidgetView { render() { - // @ts-ignore - window.React18FromIpyReact = React; - // @ts-ignore - // window.React16FromIpyReact = React16; - // @ts-ignore - window.ReactDOM18FromIpyReact = ReactDOM; - // @ts-ignore - // window.ReactDOM16FromIpyReact = ReactDOM16; this.el.classList.add('jupyter-react-widget'); // using babel is a bit of an art, so leaving this code for if we // want to switch back to babel. However, babel is very large compared @@ -207,7 +199,7 @@ export class ReactView extends DOMWidgetView { setScope(compiledCode); return; } - const {urlReact, urlReactDom} = await ensureReactSetup(this.model.get("react_version")); + const {urlReact, urlReactDom} = ensureReactSetup(this.model.get("react_version")); await ensureImportShimLoaded(); let finalCode = compiledCode; // @ts-ignore