Skip to content

Commit 2df261f

Browse files
authored
[fix] do comparison on input files (sveltejs#113)
simplifies code and makes it faster, because we don't need to go through Vite
1 parent d38e8b0 commit 2df261f

File tree

2 files changed

+61
-108
lines changed

2 files changed

+61
-108
lines changed

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

Lines changed: 53 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@
2222
/** @type {import('$lib/types').FileStub} */ (data.section.a[data.section.focus])
2323
);
2424
25-
/** @type {Map<string, string>} */
26-
let expected;
27-
2825
/** @type {import('$lib/types').Stub[]}*/
2926
let current_stubs = [];
3027
@@ -36,10 +33,13 @@
3633
/** @type {Error | null} */
3734
let error = null;
3835
36+
/** @type {Record<string, string>} */
37+
let expected = {};
3938
/** @type {Record<string, boolean>}*/
4039
let complete_states = {};
41-
let completed = false;
42-
let completing = false;
40+
$: completed =
41+
Object.keys(complete_states).length > 0 && Object.values(complete_states).every(Boolean);
42+
4343
let path = '/';
4444
4545
/** @type {Record<string, import('$lib/types').Stub>} */
@@ -129,20 +129,7 @@
129129
return destroy;
130130
});
131131
132-
afterNavigate(async () => {
133-
complete_states = {};
134-
current_stubs = Object.values(data.section.a);
135-
136-
select(
137-
/** @type {import('$lib/types').FileStub} */ (
138-
current_stubs.find((stub) => stub.name === data.section.focus)
139-
)
140-
);
141-
142-
completed = false;
143-
144-
load_exercise();
145-
});
132+
afterNavigate(load_exercise);
146133
147134
/**
148135
* Loads the adapter initially or resets it. This method can throw.
@@ -194,15 +181,26 @@
194181
195182
async function load_exercise() {
196183
try {
184+
current_stubs = Object.values(data.section.a);
185+
select(
186+
/** @type {import('$lib/types').FileStub} */ (
187+
current_stubs.find((stub) => stub.name === data.section.focus)
188+
)
189+
);
190+
197191
clearTimeout(timeout);
198192
loading = true;
199193
200-
// Load expected output first so we can compare it to the actual output to determine when it's completed
201-
await reset_adapter(Object.values(b));
202-
expected = await get_transformed_modules(data.section.scope.prefix, Object.values(b));
194+
expected = {};
195+
complete_states = {};
196+
for (const stub of Object.values(b)) {
197+
if (stub.type === 'file') {
198+
complete_states[stub.name] = false;
199+
expected[stub.name] = normalise(stub.contents);
200+
}
201+
}
203202
204-
const stubs = Object.values(data.section.a);
205-
await load_files(stubs);
203+
await load_files(current_stubs);
206204
207205
loading = false;
208206
initial = false;
@@ -218,13 +216,33 @@
218216
*/
219217
async function load_files(stubs) {
220218
adapter = await reset_adapter(stubs);
221-
const actual = await get_transformed_modules(data.section.scope.prefix, stubs);
219+
update_complete_states(stubs);
220+
set_iframe_src(adapter.base);
221+
}
222222
223-
for (const [name, transformed] of expected.entries()) {
224-
complete_states[name] = transformed === actual.get(name);
225-
}
223+
/**
224+
* @param {CustomEvent<import('$lib/types').FileStub>} event
225+
*/
226+
function update_stub(event) {
227+
const stub = event.detail;
228+
const index = current_stubs.findIndex((s) => s.name === stub.name);
229+
current_stubs[index] = stub;
230+
adapter?.update([stub]);
231+
update_complete_states([stub]);
232+
}
226233
227-
set_iframe_src(adapter.base);
234+
/**
235+
* @param {import('$lib/types').Stub[]} stubs
236+
*/
237+
function update_complete_states(stubs) {
238+
for (const stub of stubs) {
239+
if (stub.type === 'file' && stub.name in complete_states) {
240+
complete_states[stub.name] = expected[stub.name] === normalise(stub.contents);
241+
if (dev) {
242+
compare(stub.name, normalise(stub.contents), expected[stub.name]);
243+
}
244+
}
245+
}
228246
}
229247
230248
/** @type {NodeJS.Timeout} */
@@ -247,40 +265,9 @@
247265
set_iframe_src(adapter.base + path);
248266
loading = false;
249267
}, 500);
250-
} else if (e.data.type === 'hmr') {
251-
const transformed = await fetch_from_vite(e.data.data.map(({ path }) => path));
252-
253-
for (const { name, code } of transformed) {
254-
const normalised = normalise(code);
255-
complete_states[name] = normalised === expected.get(name);
256-
if (dev) compare(name, normalised, expected.get(name));
257-
}
258-
259-
completed = Object.values(complete_states).every((value) => value);
260268
}
261269
}
262270
263-
/**
264-
* @param {string[]} names
265-
* @return {Promise<Array<{ name: string, code: string }>>}
266-
*/
267-
async function fetch_from_vite(names) {
268-
/** @type {Window} */ (iframe.contentWindow).postMessage({ type: 'fetch', names }, '*');
269-
270-
return new Promise((fulfil, reject) => {
271-
window.addEventListener('message', function handler(e) {
272-
if (e.data.type === 'fetch-result') {
273-
fulfil(e.data.data);
274-
window.removeEventListener('message', handler);
275-
}
276-
});
277-
278-
setTimeout(() => {
279-
reject(new Error('Timed out fetching files from Vite'));
280-
}, 5000);
281-
});
282-
}
283-
284271
/**
285272
* @param {string} name
286273
* @param {string} actual
@@ -296,39 +283,10 @@
296283
console.groupEnd();
297284
}
298285
299-
/**
300-
* @param {string} prefix
301-
* @param {import('$lib/types').Stub[]} stubs
302-
* @returns {Promise<Map<string, string>>}
303-
*/
304-
async function get_transformed_modules(prefix, stubs) {
305-
const names = stubs
306-
.filter((stub) => {
307-
if (stub.name === '/src/__client.js') return;
308-
if (stub.type !== 'file') return;
309-
if (!/\.(js|ts|svelte)$/.test(stub.name)) return;
310-
311-
return stub.name.startsWith(prefix);
312-
})
313-
.map((stub) => stub.name);
314-
315-
const transformed = await fetch_from_vite(names);
316-
317-
const map = new Map();
318-
transformed.forEach(({ name, code }) => {
319-
map.set(name, normalise(code));
320-
});
321-
322-
return map;
323-
}
324-
325286
/** @param {string} code */
326287
function normalise(code) {
327-
return code
328-
.replace(/add_location\([^)]+\)/g, 'add_location(...)')
329-
.replace(/\?[tv]=[a-zA-Z0-9]+/g, '')
330-
.replace(/[&?]svelte&type=style&lang\.css/, '')
331-
.replace(/\/\/# sourceMappingURL=.+/, '');
288+
// TODO think about more sophisticated normalisation (e.g. truncate multiple newlines)
289+
return code.replace(/\s+/g, ' ').trim();
332290
}
333291
334292
/** @param {string} src */
@@ -381,14 +339,9 @@
381339
<button
382340
class:completed
383341
disabled={Object.keys(data.section.b).length === 0}
384-
on:click={async () => {
385-
completing = true;
386-
387-
completed = !completed;
388-
current_stubs = Object.values(completed ? b : data.section.a);
389-
adapter?.reset(current_stubs);
390-
391-
completing = false;
342+
on:click={() => {
343+
current_stubs = Object.values(completed ? data.section.a : b);
344+
load_files(current_stubs);
392345
}}
393346
>
394347
{#if completed && Object.keys(data.section.b).length > 0}
@@ -400,7 +353,7 @@
400353
</section>
401354
402355
<section class="editor-container" slot="b">
403-
<Editor stubs={current_stubs} selected={$selected} {adapter} />
356+
<Editor stubs={current_stubs} selected={$selected} on:change={update_stub} />
404357
<ImageViewer selected={$selected} />
405358
</section>
406359
</SplitPane>

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script>
22
import { dev } from '$app/environment';
3-
import { onMount } from 'svelte';
3+
import { createEventDispatcher, onMount } from 'svelte';
44
55
/**
66
* file extension -> monaco language
@@ -22,8 +22,8 @@
2222
export let stubs;
2323
/** @type {import('$lib/types').Stub | null} */
2424
export let selected = null;
25-
/** @type {import('$lib/types').Adapter | undefined} */
26-
export let adapter;
25+
26+
const dispatch = createEventDispatcher();
2727
2828
/** @type {HTMLDivElement} */
2929
let container;
@@ -95,14 +95,14 @@
9595
}
9696
});
9797
98-
let notify_adapter = true;
98+
let notify = true;
9999
100100
/**
101101
*
102102
* @param {import('$lib/types').Stub[]} stubs
103103
*/
104104
function update_files(stubs) {
105-
notify_adapter = false;
105+
notify = false;
106106
for (const stub of stubs) {
107107
if (stub.type === 'directory') {
108108
continue;
@@ -136,7 +136,7 @@
136136
models.delete(name);
137137
}
138138
}
139-
notify_adapter = true;
139+
notify = true;
140140
}
141141
142142
/**
@@ -159,9 +159,9 @@
159159
model.onDidChangeContent(() => {
160160
const contents = model.getValue();
161161
162-
if (notify_adapter) {
162+
if (notify) {
163163
stub.contents = contents;
164-
adapter?.update([stub]);
164+
dispatch('change', stub);
165165
}
166166
});
167167

0 commit comments

Comments
 (0)