Skip to content

Commit b238152

Browse files
committed
stores -> runes for adapter state in order to better combine them; most usage locations untouched deliberately (they can be adjusted later on)
1 parent dccf277 commit b238152

File tree

11 files changed

+216
-206
lines changed

11 files changed

+216
-206
lines changed

apps/svelte.dev/src/lib/tutorial/adapters/rollup/index.ts renamed to apps/svelte.dev/src/lib/tutorial/adapters/rollup/index.svelte.ts

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,48 @@
11
import type { Adapter, FileStub, Stub, Warning } from '$lib/tutorial';
22
import Bundler from '@sveltejs/repl/bundler';
3-
import type { Writable } from 'svelte/store';
43
// @ts-ignore package exports don't have types
54
import * as yootils from 'yootils';
65

76
/** Rollup bundler singleton */
8-
let vm: Bundler;
7+
let bundler: Bundler;
8+
9+
export const state = new (class RollupState {
10+
progress = $state.raw({ value: 0, text: 'initialising' });
11+
bundle = $state.raw<any>(null);
12+
error = $state.raw<Error | null>(null); // TODO wire this up? Is this the correct place?
13+
logs = $state.raw<string[]>([]); // TODO wire this up?
14+
warnings = $state.raw<Record<string, Warning[]>>({});
15+
})();
916

1017
/**
11-
* @param {import('svelte/store').Writable<any>} bundle
12-
* @param {import('svelte/store').Writable<Error | null>} error
13-
* @param {import('svelte/store').Writable<{ value: number, text: string }>} progress
14-
* @param {import('svelte/store').Writable<string[]>} logs
15-
* @param {import('svelte/store').Writable<Record<string, import('$lib/tutorial').Warning[]>>} warnings
1618
* @returns {Promise<import('$lib/tutorial').Adapter>}
1719
*/
18-
export async function create(
19-
bundle: Writable<any>,
20-
error: Writable<Error | null>, // TODO wire this up? Is this the correct place?
21-
progress: Writable<{ value: number; text: string }>,
22-
logs: Writable<string[]>, // TODO write to this somehow instead of the console viewer?
23-
warnings: Writable<Record<string, Warning[]>>
24-
): Promise<Adapter> {
25-
progress.set({ value: 0, text: 'loading files' });
20+
export async function create(): Promise<Adapter> {
21+
bundler?.destroy();
22+
23+
state.progress = { value: 0, text: 'loading files' };
2624

2725
let done = false;
2826

29-
vm = new Bundler({
27+
bundler = new Bundler({
3028
packages_url: 'https://unpkg.com',
3129
svelte_url: `https://unpkg.com/svelte@next`, // TODO remove @next once 5.0 is released
3230
// svelte_url: `${browser ? location.origin : ''}/svelte`, // TODO think about bringing back main-build for Playground?
3331
onstatus(val) {
3432
if (!done && val === null) {
3533
done = true;
36-
progress.set({ value: 1, text: 'ready' });
34+
state.progress = { value: 1, text: 'ready' };
3735
}
3836
}
3937
});
4038

41-
progress.set({ value: 0.5, text: 'loading svelte compiler' });
39+
state.progress = { value: 0.5, text: 'loading svelte compiler' };
4240

4341
/** Paths and contents of the currently loaded file stubs */
4442
let current_stubs = stubs_to_map([]);
4543

4644
async function compile() {
47-
const result = await vm.bundle(
45+
const result = await bundler.bundle(
4846
[...current_stubs.values()]
4947
// TODO we can probably remove all the SvelteKit specific stuff from the tutorial content once this settles down
5048
.filter((f): f is FileStub => f.name.startsWith('/src/lib/') && f.type === 'file')
@@ -54,15 +52,15 @@ export async function create(
5452
type: f.name.split('.').pop() ?? 'svelte'
5553
}))
5654
);
57-
bundle.set(result);
55+
state.bundle = result;
5856

5957
const _warnings: Record<string, any> = {};
6058
for (const warning of result?.warnings ?? []) {
6159
const file = '/src/lib/' + warning.filename;
6260
_warnings[file] = _warnings[file] || [];
6361
_warnings[file].push(warning);
6462
}
65-
warnings.set(_warnings);
63+
state.warnings = _warnings;
6664
}
6765

6866
const q = yootils.queue(1);

apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.js renamed to apps/svelte.dev/src/lib/tutorial/adapters/webcontainer/index.svelte.js

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,23 @@ const converter = new AnsiToHtml({
1414
/** @type {import('@webcontainer/api').WebContainer} Web container singleton */
1515
let vm;
1616

17+
export const state = new (class WCState {
18+
progress = $state.raw({ value: 0, text: 'initialising' });
19+
/** @type {string | null} */
20+
base = $state.raw(null);
21+
/** @type {Error | null} */
22+
error = $state.raw(null);
23+
/** @type {string[]} */
24+
logs = $state.raw([]);
25+
/** @type {Record<string, import('$lib/tutorial').Warning[]>} */
26+
warnings = $state.raw({});
27+
})();
28+
1729
/**
18-
* @param {import('svelte/store').Writable<string | null>} base
19-
* @param {import('svelte/store').Writable<Error | null>} error
20-
* @param {import('svelte/store').Writable<{ value: number, text: string }>} progress
21-
* @param {import('svelte/store').Writable<string[]>} logs
22-
* @param {import('svelte/store').Writable<Record<string, import('$lib/tutorial').Warning[]>>} warnings
2330
* @returns {Promise<import('$lib/tutorial').Adapter>}
2431
*/
25-
export async function create(base, error, progress, logs, warnings) {
26-
progress.set({ value: 0, text: 'loading files' });
32+
export async function create() {
33+
state.progress = { value: 0, text: 'loading files' };
2734

2835
const q = yootils.queue(1);
2936
/** @type {Map<string, Array<import('$lib/tutorial').FileStub>>} */
@@ -32,10 +39,10 @@ export async function create(base, error, progress, logs, warnings) {
3239
/** Paths and contents of the currently loaded file stubs */
3340
let current_stubs = stubs_to_map([]);
3441

35-
progress.set({ value: 1 / 5, text: 'booting webcontainer' });
42+
state.progress = { value: 1 / 5, text: 'booting webcontainer' };
3643
vm = await WebContainer.boot();
3744

38-
progress.set({ value: 2 / 5, text: 'writing virtual files' });
45+
state.progress = { value: 2 / 5, text: 'writing virtual files' };
3946
const common = await ready;
4047
await vm.mount({
4148
'common.zip': {
@@ -47,32 +54,31 @@ export async function create(base, error, progress, logs, warnings) {
4754
});
4855

4956
/** @type {Record<string, import('$lib/tutorial').Warning[]>} */
50-
let $warnings;
51-
warnings.subscribe((value) => ($warnings = value));
57+
let warnings = {};
5258

5359
/** @type {any} */
5460
let timeout;
5561

5662
/** @param {number} msec */
5763
function schedule_to_update_warning(msec) {
5864
clearTimeout(timeout);
59-
timeout = setTimeout(() => warnings.set($warnings), msec);
65+
timeout = setTimeout(() => (state.warnings = { ...warnings }), msec);
6066
}
6167

6268
const log_stream = () =>
6369
new WritableStream({
6470
write(chunk) {
6571
if (chunk === '\x1B[1;1H') {
6672
// clear screen
67-
logs.set([]);
73+
state.logs = [];
6874
} else if (chunk?.startsWith('svelte:warnings:')) {
6975
/** @type {import('$lib/tutorial').Warning} */
7076
const warn = JSON.parse(chunk.slice(16));
7177
const filename = warn.filename.startsWith('/') ? warn.filename : '/' + warn.filename;
72-
const current = $warnings[filename];
78+
const current = warnings[filename];
7379

7480
if (!current) {
75-
$warnings[filename] = [warn];
81+
warnings[filename] = [warn];
7682
// the exact same warning may be given multiple times in a row
7783
} else if (!current.some((s) => s.code === warn.code && s.pos === warn.pos)) {
7884
current.push(warn);
@@ -81,12 +87,12 @@ export async function create(base, error, progress, logs, warnings) {
8187
schedule_to_update_warning(100);
8288
} else {
8389
const log = converter.toHtml(escape_html(chunk)).replace(/\n/g, '<br>');
84-
logs.update(($logs) => [...$logs, log]);
90+
state.logs = [...state.logs, log];
8591
}
8692
}
8793
});
8894

89-
progress.set({ value: 3 / 5, text: 'unzipping files' });
95+
state.progress = { value: 3 / 5, text: 'unzipping files' };
9096
const unzip = await vm.spawn('node', ['unzip.cjs']);
9197
unzip.output.pipeTo(log_stream());
9298
const code = await unzip.exit;
@@ -98,11 +104,11 @@ export async function create(base, error, progress, logs, warnings) {
98104
await vm.spawn('chmod', ['a+x', 'node_modules/vite/bin/vite.js']);
99105

100106
vm.on('server-ready', (_port, url) => {
101-
base.set(url);
107+
state.base = url;
102108
});
103109

104110
vm.on('error', ({ message }) => {
105-
error.set(new Error(message));
111+
state.error = new Error(message);
106112
});
107113

108114
let launched = false;
@@ -111,7 +117,7 @@ export async function create(base, error, progress, logs, warnings) {
111117
if (launched) return;
112118
launched = true;
113119

114-
progress.set({ value: 4 / 5, text: 'starting dev server' });
120+
state.progress = { value: 4 / 5, text: 'starting dev server' };
115121

116122
await new Promise(async (fulfil, reject) => {
117123
const error_unsub = vm.on('error', (error) => {
@@ -121,7 +127,7 @@ export async function create(base, error, progress, logs, warnings) {
121127

122128
const ready_unsub = vm.on('server-ready', (_port, base) => {
123129
ready_unsub();
124-
progress.set({ value: 5 / 5, text: 'ready' });
130+
state.progress = { value: 5 / 5, text: 'ready' };
125131
fulfil(base); // this will be the last thing that happens if everything goes well
126132
});
127133

@@ -182,14 +188,14 @@ export async function create(base, error, progress, logs, warnings) {
182188

183189
// initialize warnings of written files
184190
to_write
185-
.filter((stub) => stub.type === 'file' && $warnings[stub.name])
186-
.forEach((stub) => ($warnings[stub.name] = []));
191+
.filter((stub) => stub.type === 'file' && warnings[stub.name])
192+
.forEach((stub) => (warnings[stub.name] = []));
187193
// remove warnings of deleted files
188194
to_delete
189-
.filter((stubname) => $warnings[stubname])
190-
.forEach((stubname) => delete $warnings[stubname]);
195+
.filter((stubname) => warnings[stubname])
196+
.forEach((stubname) => delete warnings[stubname]);
191197

192-
warnings.set($warnings);
198+
state.warnings = { ...warnings };
193199

194200
current_stubs = stubs_to_map(stubs);
195201

@@ -260,7 +266,7 @@ export async function create(base, error, progress, logs, warnings) {
260266
tree[basename] = to_file(file);
261267

262268
// initialize warnings of this file
263-
$warnings[file.name] = [];
269+
warnings[file.name] = [];
264270
schedule_to_update_warning(100);
265271

266272
await vm.mount(root);

apps/svelte.dev/src/routes/tutorial/[slug]/+page.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { afterNavigate, beforeNavigate } from '$app/navigation';
33
import { SplitPane } from '@rich_harris/svelte-split-pane';
44
import { Icon } from '@sveltejs/site-kit/components';
5-
import { reset } from './adapter.js';
5+
import { reset } from './adapter.svelte';
66
import Editor from './Editor.svelte';
77
import ContextMenu from './filetree/ContextMenu.svelte';
88
import Filetree from './filetree/Filetree.svelte';

apps/svelte.dev/src/routes/tutorial/[slug]/Editor.svelte

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
import { svelteTheme } from '@sveltejs/repl/theme';
1414
import { basicSetup } from 'codemirror';
1515
import { onMount, tick } from 'svelte';
16-
import { warnings } from './adapter.js';
16+
import { adapter_state } from './adapter.svelte';
1717
import { autocomplete_for_svelte } from './autocompletion.js';
1818
import './codemirror.css';
1919
import { files, selected_file, selected_name, update_file } from './state.js';
20+
import { toStore } from 'svelte/store';
2021
2122
/** @type {HTMLDivElement} */
2223
let container;
@@ -33,6 +34,8 @@
3334
/** @type {import('@codemirror/view').EditorView} */
3435
let editor_view;
3536
37+
const warnings = toStore(() => adapter_state.warnings);
38+
3639
const extensions = [
3740
basicSetup,
3841
EditorState.tabSize.of(2),

apps/svelte.dev/src/routes/tutorial/[slug]/Loading.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import { load_webcontainer, reset } from './adapter.js';
2+
import { load_webcontainer, reset } from './adapter.svelte';
33
import { files } from './state.js';
44
55
interface Props {

apps/svelte.dev/src/routes/tutorial/[slug]/Output.svelte

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@
55
import { onMount } from 'svelte';
66
import Chrome from './Chrome.svelte';
77
import Loading from './Loading.svelte';
8-
import { base, error, logs, progress, subscribe } from './adapter.js';
8+
import { adapter_state, subscribe } from './adapter.svelte';
9+
import { toStore } from 'svelte/store';
10+
11+
const base = toStore(() => adapter_state.base);
12+
const error = toStore(() => adapter_state.error);
13+
const logs = toStore(() => adapter_state.logs);
14+
const progress = toStore(() => adapter_state.progress);
915
1016
/** @type {import('$lib/tutorial').Exercise} */
1117
export let exercise;

apps/svelte.dev/src/routes/tutorial/[slug]/OutputRollup.svelte

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
import { theme } from '@sveltejs/site-kit/stores';
66
import Chrome from './Chrome.svelte';
77
import Loading from './Loading.svelte';
8-
import { bundle, logs, progress } from './adapter.js';
8+
import { adapter_state } from './adapter.svelte';
9+
import { toStore } from 'svelte/store';
10+
11+
const bundle = toStore(() => adapter_state.bundle);
912
1013
let initial = $state(true);
1114
let terminal_visible = $state(false);
@@ -19,13 +22,17 @@
1922
<Viewer {bundle} theme={$theme.current} />
2023
{/if}
2124

22-
{#if $progress.value !== 1}
25+
{#if adapter_state.progress.value !== 1}
2326
<!-- TODO is there any startup error we should worry about and forward to the Loading component? -->
24-
<Loading {initial} progress={$progress.value} status={$progress.text} />
27+
<Loading
28+
{initial}
29+
progress={adapter_state.progress.value}
30+
status={adapter_state.progress.text}
31+
/>
2532
{/if}
2633

2734
<div class="terminal" class:visible={terminal_visible}>
28-
{#each $logs as log}
35+
{#each adapter_state.logs as log}
2936
<div>{@html log}</div>
3037
{/each}
3138
</div>

0 commit comments

Comments
 (0)