Skip to content

Commit 6b99d15

Browse files
authored
refactor: Use standard lazy loading for repl runner & editor (#1223)
1 parent b4f3adc commit 6b99d15

File tree

7 files changed

+138
-131
lines changed

7 files changed

+138
-131
lines changed

src/components/content-region/index.jsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import style from './style.module.css';
55
import { useTranslation } from '../../lib/i18n';
66
import { TocContext } from '../table-of-contents';
77
import { prefetchContent } from '../../lib/use-content';
8-
import { preloadRepl } from '../../lib/use-repl';
9-
import { Repl, TutorialPage } from '../routes';
8+
import { preloadRepl } from '../../lib/repl';
9+
import { ReplPage, TutorialPage } from '../routes';
1010

1111
const COMPONENTS = {
1212
...widgets,
@@ -16,7 +16,7 @@ const COMPONENTS = {
1616

1717
props.onMouseOver = () => {
1818
if (props.href.startsWith('/repl?code')) {
19-
Repl.preload();
19+
ReplPage.preload();
2020
preloadRepl();
2121
} else if (props.href.startsWith('/tutorial')) {
2222
TutorialPage.preload();

src/components/controllers/repl/index.jsx

+32-32
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { useState, useEffect } from 'preact/hooks';
2-
import { useLocation, useRoute } from 'preact-iso';
2+
import { useLocation, useRoute, ErrorBoundary } from 'preact-iso';
33
import { Splitter } from '../../splitter';
44
import { textToBase64 } from './query-encode.js';
55
import { ErrorOverlay } from './error-overlay';
66
import { EXAMPLES, fetchExample } from './examples';
77
import { useStoredValue } from '../../../lib/localstorage';
8-
import { useRepl } from '../../../lib/use-repl';
8+
import { Repl as _Repl } from '../../../lib/repl.js';
99
import { parseStackTrace } from './errors';
1010
import style from './style.module.css';
1111
import REPL_CSS from './examples/style.css?raw';
@@ -26,8 +26,6 @@ export function Repl({ code }) {
2626
// TODO: Needs some work for prerendering to not cause pop-in
2727
if (typeof window === 'undefined') return null;
2828

29-
const { CodeEditor, Runner } = useRepl();
30-
3129
const applyExample = (e) => {
3230
const slug = e.target.value;
3331
fetchExample(slug)
@@ -112,35 +110,37 @@ export function Repl({ code }) {
112110
</button>
113111
</header>
114112
<div class={style.replWrapper}>
115-
<Splitter
116-
orientation="horizontal"
117-
other={
118-
<div class={style.output}>
119-
{error && (
120-
<ErrorOverlay
121-
name={error.name}
122-
message={error.message}
123-
stack={parseStackTrace(error)}
113+
<ErrorBoundary>
114+
<Splitter
115+
orientation="horizontal"
116+
other={
117+
<div class={style.output}>
118+
{error && (
119+
<ErrorOverlay
120+
name={error.name}
121+
message={error.message}
122+
stack={parseStackTrace(error)}
123+
/>
124+
)}
125+
<_Repl.Runner
126+
onRealm={onRealm}
127+
onError={setError}
128+
onSuccess={() => setError(null)}
129+
css={REPL_CSS}
130+
code={runnerCode}
124131
/>
125-
)}
126-
<Runner
127-
onRealm={onRealm}
128-
onError={setError}
129-
onSuccess={() => setError(null)}
130-
css={REPL_CSS}
131-
code={runnerCode}
132-
/>
133-
</div>
134-
}
135-
>
136-
<CodeEditor
137-
class={style.code}
138-
value={editorCode}
139-
error={error}
140-
slug={query.example}
141-
onInput={onEditorInput}
142-
/>
143-
</Splitter>
132+
</div>
133+
}
134+
>
135+
<_Repl.CodeEditor
136+
class={style.code}
137+
value={editorCode}
138+
error={error}
139+
slug={query.example}
140+
onInput={onEditorInput}
141+
/>
142+
</Splitter>
143+
</ErrorBoundary>
144144
</div>
145145
</>
146146
);

src/components/controllers/tutorial/index.jsx

+68-68
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import {
88
useMemo,
99
useCallback
1010
} from 'preact/hooks';
11-
import { useLocation } from 'preact-iso';
11+
import { ErrorBoundary, useLocation } from 'preact-iso';
1212
import { TutorialContext, SolutionContext } from './contexts';
1313
import { ErrorOverlay } from '../repl/error-overlay';
1414
import { parseStackTrace } from '../repl/errors';
1515
import cx from '../../../lib/cx';
16-
import { useRepl } from '../../../lib/use-repl';
16+
import { Repl } from '../../../lib/repl';
1717
import { useLanguage } from '../../../lib/i18n';
1818
import { Splitter } from '../../splitter';
1919
import config from '../../../config.json';
@@ -61,8 +61,6 @@ export function Tutorial({ html, meta }) {
6161
// TODO: Needs some work for prerendering to not cause pop-in
6262
if (typeof window === 'undefined') return null;
6363

64-
const { CodeEditor, Runner } = useRepl();
65-
6664
useEffect(() => {
6765
if (meta.tutorial?.initial && editorCode !== meta.tutorial.initial) {
6866
setEditorCode(meta.tutorial.initial);
@@ -145,74 +143,76 @@ export function Tutorial({ html, meta }) {
145143
showCode && style.showCode
146144
)}
147145
>
148-
<Splitter
149-
orientation="horizontal"
150-
force={!showCode ? '100%' : undefined}
151-
other={
152-
<Splitter
153-
orientation="vertical"
154-
other={
155-
<>
156-
<div class={style.output}>
157-
{error && (
158-
<ErrorOverlay
159-
name={error.name}
160-
message={error.message}
161-
stack={parseStackTrace(error)}
146+
<ErrorBoundary>
147+
<Splitter
148+
orientation="horizontal"
149+
force={!showCode ? '100%' : undefined}
150+
other={
151+
<Splitter
152+
orientation="vertical"
153+
other={
154+
<>
155+
<div class={style.output}>
156+
{error && (
157+
<ErrorOverlay
158+
name={error.name}
159+
message={error.message}
160+
stack={parseStackTrace(error)}
161+
/>
162+
)}
163+
<Repl.Runner
164+
ref={runner}
165+
onSuccess={onSuccess}
166+
onRealm={onRealm}
167+
onError={onError}
168+
code={runnerCode}
162169
/>
170+
</div>
171+
{hasCode && (
172+
<button
173+
class={style.toggleCode}
174+
title="Toggle Code"
175+
onClick={toggleCode}
176+
>
177+
<span>Toggle Code</span>
178+
</button>
163179
)}
164-
<Runner
165-
ref={runner}
166-
onSuccess={onSuccess}
167-
onRealm={onRealm}
168-
onError={onError}
169-
code={runnerCode}
170-
/>
171-
</div>
172-
{hasCode && (
173-
<button
174-
class={style.toggleCode}
175-
title="Toggle Code"
176-
onClick={toggleCode}
177-
>
178-
<span>Toggle Code</span>
179-
</button>
180-
)}
181-
</>
182-
}
183-
>
184-
<div class={style.codeWindow}>
185-
<CodeEditor
186-
class={style.code}
187-
value={editorCode}
188-
error={error}
189-
slug={url}
190-
onInput={setEditorCode}
191-
/>
192-
</div>
193-
</Splitter>
194-
}
195-
>
196-
<div class={style.tutorialWindow} ref={content}>
197-
<MarkdownRegion
198-
html={html}
199-
meta={meta}
200-
components={TUTORIAL_COMPONENTS}
201-
/>
202-
203-
{meta.tutorial?.setup &&
204-
<TutorialSetupBlock
205-
code={meta.tutorial.setup}
206-
runner={runner}
207-
useResult={useResult}
208-
useRealm={useRealm}
209-
useError={useError}
210-
/>
180+
</>
181+
}
182+
>
183+
<div class={style.codeWindow}>
184+
<Repl.CodeEditor
185+
class={style.code}
186+
value={editorCode}
187+
error={error}
188+
slug={url}
189+
onInput={setEditorCode}
190+
/>
191+
</div>
192+
</Splitter>
211193
}
194+
>
195+
<div class={style.tutorialWindow} ref={content}>
196+
<MarkdownRegion
197+
html={html}
198+
meta={meta}
199+
components={TUTORIAL_COMPONENTS}
200+
/>
201+
202+
{meta.tutorial?.setup &&
203+
<TutorialSetupBlock
204+
code={meta.tutorial.setup}
205+
runner={runner}
206+
useResult={useResult}
207+
useRealm={useRealm}
208+
useError={useError}
209+
/>
210+
}
212211

213-
<ButtonContainer meta={meta} showCode={showCode} help={help} />
214-
</div>
215-
</Splitter>
212+
<ButtonContainer meta={meta} showCode={showCode} help={help} />
213+
</div>
214+
</Splitter>
215+
</ErrorBoundary>
216216
</div>
217217
</TutorialContext.Provider>
218218
);

src/components/header/index.jsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import { useOverlayToggle } from '../../lib/toggle-overlay';
1111
import { useLocation } from 'preact-iso';
1212
import { useLanguage } from '../../lib/i18n';
1313
import { prefetchContent } from '../../lib/use-content';
14-
import { preloadRepl } from '../../lib/use-repl';
15-
import { Repl, TutorialPage } from '../routes';
14+
import { preloadRepl } from '../../lib/repl';
15+
import { ReplPage, TutorialPage } from '../routes';
1616

1717
const LINK_FLAIR = {
1818
logo: InvertedLogo
@@ -224,7 +224,7 @@ const NavLink = ({ to, isOpen, route, ...props }) => {
224224

225225
const onMouseOver = () => {
226226
if (prefetchHref.startsWith('/repl')) {
227-
Repl.preload();
227+
ReplPage.preload();
228228
preloadRepl();
229229
} else if (prefetchHref.startsWith('/tutorial')) {
230230
TutorialPage.preload();

src/components/routes.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { DocPage } from './controllers/doc-page';
55
import { NotFound } from './controllers/not-found';
66
import { navRoutes } from '../lib/route-utils';
77

8-
export const Repl = lazy(() => import('./controllers/repl-page'));
8+
export const ReplPage = lazy(() => import('./controllers/repl-page'));
99
export const BlogPage = lazy(() => import('./controllers/blog-page'));
1010
export const TutorialPage = lazy(() => import('./controllers/tutorial-page'));
1111

@@ -26,7 +26,7 @@ export default function Routes() {
2626
.filter(route => !route.startsWith('/guide'))
2727
.filter(route => !route.startsWith('/tutorial'))
2828
.map(route => {
29-
const component = route === '/repl' ? Repl : Page;
29+
const component = route === '/repl' ? ReplPage : Page;
3030
return <Route key={route} path={route} component={component} />;
3131
})}
3232
<Route path="/tutorial/:step?" component={TutorialPage} />

src/lib/repl.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { options } from 'preact';
2+
import { lazy } from 'preact-iso';
3+
4+
const CodeEditor = lazy(() => import('../components/code-editor'));
5+
const Runner = lazy(() => import('../components/controllers/repl/runner'));
6+
7+
// `preact-iso` doesn't forward refs to lazy-loaded components (should we?)
8+
// so we need to manually do it here to support the tutorial which reads the runner ref
9+
const oldDiff = options.__b;
10+
options.__b = (vnode) => {
11+
if (vnode.type === Runner && vnode.ref) {
12+
vnode.props.ref = vnode.ref;
13+
vnode.ref = null;
14+
}
15+
16+
if (oldDiff) oldDiff(vnode);
17+
};
18+
19+
/**
20+
* @returns {void}
21+
*/
22+
export function preloadRepl() {
23+
CodeEditor.preload();
24+
Runner.preload();
25+
}
26+
27+
export const Repl = {
28+
CodeEditor,
29+
Runner
30+
};

src/lib/use-repl.js

-23
This file was deleted.

0 commit comments

Comments
 (0)