Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: more playground fixes #500

Merged
merged 9 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions packages/editor/src/lib/Workspace.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ export type Item = File | Directory;

export interface Compiled {
error: CompileError | null;
result: CompileResult;
result: CompileResult | null;
migration: {
code: string;
};
} | null;
}

function is_file(item: Item): item is File {
Expand Down Expand Up @@ -85,6 +85,7 @@ export class Workspace {
});
compiled = $state<Record<string, Compiled>>({});

#svelte_version: string;
#readonly = false; // TODO do we need workspaces for readonly stuff?
#files = $state.raw<Item[]>([]);
#current = $state.raw() as File;
Expand All @@ -99,17 +100,20 @@ export class Workspace {
constructor(
files: Item[],
{
svelte_version = 'latest',
initial,
readonly = false,
onupdate,
onreset
}: {
svelte_version?: string;
initial?: string;
readonly?: boolean;
onupdate?: (file: File) => void;
onreset?: (items: Item[]) => void;
} = {}
) {
this.#svelte_version = svelte_version;
this.#readonly = readonly;

this.set(files, initial);
Expand Down Expand Up @@ -315,7 +319,7 @@ export class Workspace {
this.modified[file.name] = true;

if (BROWSER && is_svelte_file(file)) {
compile_file(file, this.compiler_options).then((compiled) => {
compile_file(file, this.#svelte_version, this.compiler_options).then((compiled) => {
this.compiled[file.name] = compiled;
});
}
Expand Down Expand Up @@ -430,7 +434,7 @@ export class Workspace {

seen.push(file.name);

compile_file(file, this.compiler_options).then((compiled) => {
compile_file(file, this.#svelte_version, this.compiler_options).then((compiled) => {
this.compiled[file.name] = compiled;
});
}
Expand Down
40 changes: 31 additions & 9 deletions packages/editor/src/lib/compile-worker/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { BROWSER } from 'esm-env';
import CompileWorker from './worker?worker';
import type { Compiled, File } from '../Workspace.svelte';
import type { CompileOptions } from 'svelte/compiler';

const callbacks = new Map<number, (compiled: Compiled) => void>();
const callbacks = new Map<string, Map<number, (compiled: Compiled) => void>>();

let worker: Worker;

Expand All @@ -13,27 +12,50 @@ if (BROWSER) {
worker = new CompileWorker();

worker.addEventListener('message', (event) => {
const callback = callbacks.get(event.data.id);

if (callback) {
callback(event.data.payload);
callbacks.delete(event.data.id);
const { filename, id, payload } = event.data;
const file_callbacks = callbacks.get(filename);

if (file_callbacks) {
const callback = file_callbacks.get(id);
if (callback) {
callback(payload);
file_callbacks.delete(id);

for (const [other_id, callback] of file_callbacks) {
if (id > other_id) {
callback(payload);
file_callbacks.delete(other_id);
}
}

if (file_callbacks.size === 0) {
callbacks.delete(filename);
}
}
}
});
}

export function compile_file(
file: File,
version: string,
options: { generate: 'client' | 'server'; dev: boolean }
): Promise<Compiled> {
// @ts-ignore
if (!BROWSER) return;

let id = uid++;
const filename = file.name;

if (!callbacks.has(filename)) {
callbacks.set(filename, new Map());
}

const file_callbacks = callbacks.get(filename)!;

worker.postMessage({ id, file, options });
worker.postMessage({ id, file, version, options });

return new Promise((fulfil) => {
callbacks.set(id, fulfil);
file_callbacks.set(id, fulfil);
});
}
76 changes: 66 additions & 10 deletions packages/editor/src/lib/compile-worker/worker.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,99 @@
import { compile, compileModule, migrate } from 'svelte/compiler';
import type { File } from '../Workspace.svelte';

// TODO need to handle Svelte 3/4 for playground
// hack for magic-string and Svelte 4 compiler
// do not put this into a separate module and import it, would be treeshaken in prod
self.window = self;

declare var self: Window & typeof globalThis & { svelte: typeof import('svelte/compiler') };

let inited = false;
let fulfil_ready: (arg?: never) => void;
const ready = new Promise((f) => {
fulfil_ready = f;
});

addEventListener('message', async (event) => {
if (!inited) {
inited = true;
const svelte_url = `https://unpkg.com/svelte@${event.data.version}`;
const { version } = await fetch(`${svelte_url}/package.json`).then((r) => r.json());

if (version.startsWith('4.')) {
// unpkg doesn't set the correct MIME type for .cjs files
// https://github.com/mjackson/unpkg/issues/355
const compiler = await fetch(`${svelte_url}/compiler.cjs`).then((r) => r.text());
(0, eval)(compiler + '\n//# sourceURL=compiler.cjs@' + version);
} else if (version.startsWith('3.')) {
const compiler = await fetch(`${svelte_url}/compiler.js`).then((r) => r.text());
(0, eval)(compiler + '\n//# sourceURL=compiler.js@' + version);
} else {
const compiler = await fetch(`${svelte_url}/compiler/index.js`).then((r) => r.text());
(0, eval)(compiler + '\n//# sourceURL=compiler/index.js@' + version);
}

fulfil_ready();
}

await ready;

addEventListener('message', (event) => {
const { id, file, options } = event.data as {
id: number;
file: File;
options: { generate: 'client' | 'server'; dev: boolean };
};

const fn = file.name.endsWith('.svelte') ? compile : compileModule;
const fn = file.name.endsWith('.svelte') ? self.svelte.compile : self.svelte.compileModule;

if (!fn) {
// .svelte.js file compiled with Svelte 3/4 compiler
postMessage({
id,
filename: file.name,
payload: {
error: null,
result: null,
migration: null
}
});
return;
}

let migration = null;

try {
migration = migrate(file.contents, { filename: file.name });
} catch (e) {
// can this happen?
if (self.svelte.migrate) {
try {
migration = self.svelte.migrate(file.contents, { filename: file.name });
} catch (e) {
// can this happen?
}
}

try {
const result = fn(file.contents, { ...options, filename: file.name });

postMessage({
id,
filename: file.name,
payload: {
error: null,
result: {
// @ts-expect-error Svelte 3/4 doesn't contain this field
metadata: { runes: false },
...result,
// @ts-expect-error https://github.com/sveltejs/svelte/issues/13628
warnings: result.warnings.map((w) => ({ message: w.message, ...w }))
warnings: result.warnings.map((w) => {
// @ts-expect-error This exists on Svelte 3/4 and is required to be deleted, otherwise postMessage won't work
delete w.toString;
// @ts-expect-error https://github.com/sveltejs/svelte/issues/13628 (fixed in 5.0, but was like that for most of the preview phase)
return { message: w.message, ...w };
})
},
migration
}
});
} catch (e) {
postMessage({
id,
filename: file.name,
payload: {
// @ts-expect-error
error: { message: e.message, ...e },
Expand Down
14 changes: 6 additions & 8 deletions packages/repl/src/lib/Output/Output.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,12 @@

// TODO this effect is a bit of a code smell
$effect(() => {
if (current) {
if (current.error) {
js.contents = css.contents = `/* ${current.error.message} */`;
} else {
js.contents = current.result.js.code;
css.contents =
current.result.css?.code ?? `/* Add a <st` + `yle> tag to see the CSS output */`;
}
if (current?.error) {
js.contents = css.contents = `/* ${current.error.message} */`;
} else if (current?.result) {
js.contents = current.result.js.code;
css.contents =
current.result.css?.code ?? `/* Add a <st` + `yle> tag to see the CSS output */`;
} else {
js.contents = css.contents = `/* Select a component to see its compiled code */`;
}
Expand Down
25 changes: 12 additions & 13 deletions packages/repl/src/lib/Repl.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,17 @@
text: true
};

const workspace = $state(
new Workspace([dummy], {
initial: 'App.svelte',
onupdate() {
rebundle();
onchange?.();
},
onreset() {
rebundle();
}
})
);
const workspace = new Workspace([dummy], {
initial: 'App.svelte',
svelte_version: svelteUrl.split('@')[1],
onupdate() {
rebundle();
onchange?.();
},
onreset() {
rebundle();
}
});

// TODO get rid
export function toJSON() {
Expand Down Expand Up @@ -105,7 +104,7 @@

workspace.update_file({
...workspace.current!,
contents: migration.code
contents: migration!.code
});

rebundle();
Expand Down
9 changes: 8 additions & 1 deletion packages/repl/src/lib/workers/bundler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import type { Warning } from '../../types';
import type { CompileError, CompileOptions, CompileResult } from 'svelte/compiler';
import type { File } from 'editor';

// hack for magic-string and rollup inline sourcemaps
// do not put this into a separate module and import it, would be treeshaken in prod
self.window = self;

let packages_url: string;
let svelte_url: string;
let version: string;
Expand All @@ -40,6 +44,9 @@ self.addEventListener('message', async (event: MessageEvent<BundleMessageData>)
// https://github.com/mjackson/unpkg/issues/355
const compiler = await fetch(`${svelte_url}/compiler.cjs`).then((r) => r.text());
(0, eval)(compiler + '\n//# sourceURL=compiler.cjs@' + version);
} else if (version.startsWith('3.')) {
const compiler = await fetch(`${svelte_url}/compiler.js`).then((r) => r.text());
(0, eval)(compiler + '\n//# sourceURL=compiler.js@' + version);
} else {
const compiler = await fetch(`${svelte_url}/compiler/index.js`).then((r) => r.text());
(0, eval)(compiler + '\n//# sourceURL=compiler/index.js@' + version);
Expand Down Expand Up @@ -395,7 +402,7 @@ async function get_bundle(
`.replace(/\t/g, '');
}
} else if (id.endsWith('.svelte.js')) {
result = svelte.compileModule(code, {
result = svelte.compileModule?.(code, {
filename: name + '.js',
generate: 'client',
dev: true
Expand Down
Loading