Skip to content

Commit 57636a5

Browse files
authored
refactor: Better utilize suspense in REPL (#1150)
* fix: Html router middleware * refactor: Switch REPL to utilize suspense better * chore: Drop linkstate * chore: Add impl note to REPL * refactor: Simplify code editor import now that repl is skipped during SSR * revert: Simplify repl runner worker now that repl is skipped during SSR
1 parent 68d51c7 commit 57636a5

File tree

9 files changed

+275
-304
lines changed

9 files changed

+275
-304
lines changed

package-lock.json

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

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@
7676
"comlink": "^4.4.1",
7777
"decko": "^1.2.0",
7878
"htm": "^3.1.1",
79-
"linkstate": "^1.1.1",
8079
"magic-string": "^0.25.7",
8180
"marked": "^0.8.0",
8281
"preact": "10.15.1",

plugins/html-routing-middleware.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function htmlRoutingMiddlewarePlugin() {
3333

3434
try {
3535
await fs.access(file);
36-
req.url += '/index.html';
36+
req.url = url.pathname + '/index.html' + url.search;
3737
} catch {
3838
req.url = '/404/index.html';
3939
}

src/components/code-editor/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export default class CodeEditor extends Component {
6262

6363
this.value = this.editor.getValue();
6464
let { onInput } = this.props;
65-
if (onInput) onInput({ value: this.value });
65+
if (onInput) onInput(this.value);
6666
});
6767
}
6868

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { useRoute } from 'preact-iso';
22
import { Repl } from './repl';
3-
import { useContent } from '../../lib/use-resource';
3+
import { useExample } from './repl/examples';
4+
import { useContent, useResource } from '../../lib/use-resource';
45
import { useTitle, useDescription } from './utils';
56
import { useLanguage } from '../../lib/i18n';
67

8+
import style from './repl/style.module.css';
9+
710
export default function ReplPage() {
811
const { query } = useRoute();
912
const [lang] = useLanguage();
@@ -12,5 +15,74 @@ export default function ReplPage() {
1215
useTitle(meta.title);
1316
useDescription(meta.description);
1417

15-
return <Repl query={query} />;
18+
const [code, slug] = initialCode(query);
19+
20+
return (
21+
<div class={style.repl}>
22+
<style>{`
23+
main {
24+
height: 100% !important;
25+
overflow: hidden !important;
26+
}
27+
footer {
28+
display: none !important;
29+
}
30+
`}</style>
31+
<Repl code={code} slug={slug} />
32+
</div>
33+
);
34+
}
35+
36+
/**
37+
* Go down the list of fallbacks to get initial code
38+
*
39+
* ?code -> ?example -> localStorage -> simple counter example
40+
*/
41+
function initialCode(query) {
42+
let code, slug;
43+
if (query.code) {
44+
try {
45+
code = useResource(() => querySafetyCheck(atob(query.code)), [query.code]);
46+
} catch (e) {}
47+
} else if (query.example) {
48+
code = useExample([query.example]);
49+
if (code) {
50+
slug = query.example;
51+
history.replaceState(
52+
null,
53+
null,
54+
`/repl?example=${encodeURIComponent(slug)}`
55+
);
56+
}
57+
else history.replaceState(null, null, '/repl');
58+
}
59+
60+
if (!code) {
61+
if (typeof window !== 'undefined' && localStorage.getItem('preact-www-repl-code')) {
62+
code = localStorage.getItem('preact-www-repl-code');
63+
} else {
64+
slug = 'counter';
65+
if (typeof window !== 'undefined') {
66+
history.replaceState(
67+
null,
68+
null,
69+
`/repl?example=${encodeURIComponent(slug)}`
70+
);
71+
}
72+
code = useExample([slug]);
73+
}
74+
}
75+
76+
return [code, slug];
77+
}
78+
79+
async function querySafetyCheck(code) {
80+
return (
81+
!document.referrer ||
82+
document.referrer.indexOf(location.origin) === 0 ||
83+
// eslint-disable-next-line no-alert
84+
confirm('Are you sure you want to run the code contained in this link?')
85+
)
86+
? code
87+
: undefined;
1688
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { useResource } from '../../../lib/use-resource';
2+
3+
import simpleCounterExample from './examples/simple-counter.txt?url';
4+
import counterWithHtmExample from './examples/counter-with-htm.txt?url';
5+
import todoExample from './examples/todo-list.txt?url';
6+
import todoExampleSignal from './examples/todo-list-signal.txt?url';
7+
import repoListExample from './examples/github-repo-list.txt?url';
8+
import contextExample from './examples/context.txt?url';
9+
import spiralExample from './examples/spiral.txt?url';
10+
11+
export const EXAMPLES = [
12+
{
13+
name: 'Simple Counter',
14+
slug: 'counter',
15+
url: simpleCounterExample
16+
},
17+
{
18+
name: 'Todo List',
19+
slug: 'todo',
20+
url: todoExample
21+
},
22+
{
23+
name: 'Todo List (Signals)',
24+
slug: 'todo-list-signals',
25+
url: todoExampleSignal
26+
},
27+
{
28+
name: 'Github Repo List',
29+
slug: 'github-repo-list',
30+
url: repoListExample
31+
},
32+
{
33+
group: 'Advanced',
34+
items: [
35+
{
36+
name: 'Counter using HTM',
37+
slug: 'counter-htm',
38+
url: counterWithHtmExample
39+
},
40+
{
41+
name: 'Context',
42+
slug: 'context',
43+
url: contextExample
44+
}
45+
]
46+
},
47+
{
48+
group: 'Animation',
49+
items: [
50+
{
51+
name: 'Spiral',
52+
slug: 'spiral',
53+
url: spiralExample
54+
}
55+
]
56+
}
57+
];
58+
59+
export function getExample(slug, list = EXAMPLES) {
60+
for (let i = 0; i < list.length; i++) {
61+
let item = list[i];
62+
if (item.group) {
63+
let found = getExample(slug, item.items);
64+
if (found) return found;
65+
} else if (item.slug.toLowerCase() === slug.toLowerCase()) {
66+
return item;
67+
}
68+
}
69+
}
70+
71+
/**
72+
* @param {[ slug: string ]} args
73+
* @returns {string | undefined}
74+
*/
75+
export function useExample([slug]) {
76+
const example = getExample(slug);
77+
if (!example) return;
78+
return useResource(() => loadExample(example.url), ['example', slug]);
79+
}
80+
81+
export async function loadExample(exampleUrl) {
82+
return await fetch(exampleUrl).then(r => r.text());
83+
}

0 commit comments

Comments
 (0)