Skip to content

Commit 926b033

Browse files
paoloricciutioscard0mRich-Harris
authored
feat: allow version to be a pr-[prnumber] or commit-[commithash] (#745)
* feat: allow version to be a `pkg.pr.new` url Co-authored-by: Oscar Dominguez <[email protected]> * fix: move from full url to `ref:[pr|hash]` * fix: typescript * chore: mode tarparser.d.ts * chore: swap `ref:` for `pr-` * feat: allow version to be `commit-[commithash] * feat: show error if fetching the `tar` fails * fix: lint * bump tarparser * DRY out * fix: only use fetched package json if it's available * move logic into component * simplify with regex * trim down a bit * fix * more robust error handling * separate onstatus from onerror * simplify with regex * no need to thunkify * DRY out * avoid overloading svelteUrl, just use the verison string directly * use Promise.withResolvers, simplify error handling * turns out this code doesn't actually do anything - very old versions are already broken and this doesn't fix them. we can deal with that another time if we want to * use existing FETCH_CACHE * tidy up * remove some unused code * lint * ugh --------- Co-authored-by: Oscar Dominguez <[email protected]> Co-authored-by: Rich Harris <[email protected]>
1 parent e830fe9 commit 926b033

File tree

13 files changed

+181
-152
lines changed

13 files changed

+181
-152
lines changed

apps/svelte.dev/src/config.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// REPL props
22

3-
export const svelteUrl = `https://unpkg.com/svelte@4`;
43
export const mapbox_setup = `window.MAPBOX_ACCESS_TOKEN = '${
54
import.meta.env.VITE_MAPBOX_ACCESS_TOKEN
65
}';`;

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ export async function create(): Promise<Adapter> {
2424

2525
bundler = new Bundler({
2626
packages_url: 'https://unpkg.com',
27-
svelte_url: `https://unpkg.com/svelte`,
28-
// svelte_url: `${browser ? location.origin : ''}/svelte`, // TODO think about bringing back main-build for Playground?
27+
svelte_version: 'latest',
2928
onstatus(val) {
3029
if (!done && val === null) {
3130
done = true;

apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.server.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { error } from '@sveltejs/kit';
22
import type { Examples } from '../api/examples/all.json/+server.js';
33

4-
export async function load({ fetch, params, url }) {
4+
export async function load({ fetch, params }) {
55
const examples_res = fetch('/playground/api/examples/all.json').then((r) => r.json());
66
const res = await fetch(`/playground/api/${params.id}.json`);
77

@@ -21,7 +21,6 @@ export async function load({ fetch, params, url }) {
2121
title: example.title,
2222
slug: example.slug
2323
}))
24-
})),
25-
version: url.searchParams.get('version') || 'latest'
24+
}))
2625
};
2726
}

apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import type { Gist } from '$lib/db/types';
55
import { Repl } from '@sveltejs/repl';
66
import { theme } from '@sveltejs/site-kit/stores';
7-
import { onMount } from 'svelte';
87
import { mapbox_setup } from '../../../../config.js';
98
import AppControls from './AppControls.svelte';
109
import { compress_and_encode_text, decode_and_decompress_text } from './gzip.js';
@@ -18,15 +17,18 @@
1817
let repl = $state() as ReturnType<typeof Repl>;
1918
let name = $state(data.gist.name);
2019
let modified = $state(false);
21-
let version = data.version;
2220
let setting_hash: any = null;
2321
22+
// svelte-ignore non_reactive_update
23+
let version = $page.url.searchParams.get('version') || 'latest';
24+
let is_pr_or_commit_version = version.startsWith('pr-') || version.startsWith('commit-');
25+
2426
// Hashed URLs are less safe (we can't delete malicious REPLs), therefore
2527
// don't allow links to escape the sandbox restrictions
2628
const can_escape = browser && !$page.url.hash;
2729
28-
onMount(() => {
29-
if (version !== 'local') {
30+
if (version !== 'local' && !is_pr_or_commit_version) {
31+
$effect(() => {
3032
fetch(`https://unpkg.com/svelte@${version}/package.json`)
3133
.then((r) => r.json())
3234
.then((pkg) => {
@@ -40,8 +42,8 @@
4042
replaceState(url, {});
4143
}
4244
});
43-
}
44-
});
45+
});
46+
}
4547
4648
afterNavigate(() => {
4749
name = data.gist.name;
@@ -148,11 +150,6 @@
148150
}
149151
}
150152
151-
const svelteUrl =
152-
browser && version === 'local'
153-
? `${location.origin}/playground/local`
154-
: `https://unpkg.com/svelte@${version}`;
155-
156153
const relaxed = $derived(data.gist.relaxed || (data.user && data.user.id === data.gist.owner));
157154
</script>
158155

@@ -198,7 +195,7 @@
198195
<div style="display: contents" onfocusout={update_hash}>
199196
<Repl
200197
bind:this={repl}
201-
{svelteUrl}
198+
svelteVersion={version}
202199
{relaxed}
203200
{can_escape}
204201
injectedJS={mapbox_setup}

apps/svelte.dev/src/routes/(authed)/playground/[id]/embed/+page.svelte

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,34 @@
44
import { theme } from '@sveltejs/site-kit/stores';
55
import { Repl } from '@sveltejs/repl';
66
import { mapbox_setup } from '../../../../../config.js';
7-
import { onMount } from 'svelte';
7+
import { page } from '$app/stores';
88
99
let { data } = $props();
1010
1111
let repl = $state() as ReturnType<typeof Repl>;
1212
13-
onMount(() => {
14-
if (data.version !== 'local') {
15-
fetch(`https://unpkg.com/svelte@${data.version}/package.json`)
13+
// svelte-ignore non_reactive_update
14+
let version = $page.url.searchParams.get('version') || 'latest';
15+
let is_pr_or_commit_version = version.startsWith('pr-') || version.startsWith('commit-');
16+
17+
if (version !== 'local' && !is_pr_or_commit_version) {
18+
$effect(() => {
19+
fetch(`https://unpkg.com/svelte@${version}/package.json`)
1620
.then((r) => r.json())
1721
.then((pkg) => {
1822
if (pkg.version !== data.version) {
1923
replaceState(`/playground/${data.gist.id}/embed?version=${pkg.version}`, {});
2024
}
2125
});
22-
}
23-
});
26+
});
27+
}
2428
2529
afterNavigate(() => {
2630
repl?.set({
2731
files: data.gist.components
2832
});
2933
});
3034
31-
const svelteUrl =
32-
browser && data.version === 'local'
33-
? `${location.origin}/playground/local`
34-
: `https://unpkg.com/svelte@${data.version}`;
35-
3635
const relaxed = $derived(data.gist.relaxed || (data.user && data.user.id === data.gist.owner));
3736
</script>
3837

@@ -48,7 +47,7 @@
4847
{#if browser}
4948
<Repl
5049
bind:this={repl}
51-
{svelteUrl}
50+
svelteVersion={version}
5251
{relaxed}
5352
can_escape
5453
injectedJS={mapbox_setup}

packages/editor/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"type": "module",
6464
"dependencies": {
6565
"@lezer/highlight": "^1.2.1",
66-
"esm-env": "^1.0.0"
66+
"esm-env": "^1.0.0",
67+
"tarparser": "^0.0.4"
6768
}
6869
}

packages/editor/src/lib/compile-worker/worker.ts

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,68 @@
1+
import { parseTar } from 'tarparser';
12
import type { CompileResult } from 'svelte/compiler';
23
import type { ExposedCompilerOptions, File } from '../Workspace.svelte';
4+
import type { FileDescription } from 'tarparser';
35

46
// hack for magic-string and Svelte 4 compiler
57
// do not put this into a separate module and import it, would be treeshaken in prod
68
self.window = self;
79

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

10-
let inited = false;
11-
let fulfil_ready: (arg?: never) => void;
12-
const ready = new Promise((f) => {
13-
fulfil_ready = f;
14-
});
12+
let inited: PromiseWithResolvers<typeof self.svelte>;
1513

16-
addEventListener('message', async (event) => {
17-
if (!inited) {
18-
inited = true;
19-
const svelte_url = `https://unpkg.com/svelte@${event.data.version}`;
20-
const { version } = await fetch(`${svelte_url}/package.json`).then((r) => r.json());
21-
22-
if (version.startsWith('4.')) {
23-
// unpkg doesn't set the correct MIME type for .cjs files
24-
// https://github.com/mjackson/unpkg/issues/355
25-
const compiler = await fetch(`${svelte_url}/compiler.cjs`).then((r) => r.text());
26-
(0, eval)(compiler + '\n//# sourceURL=compiler.cjs@' + version);
27-
} else if (version.startsWith('3.')) {
28-
const compiler = await fetch(`${svelte_url}/compiler.js`).then((r) => r.text());
29-
(0, eval)(compiler + '\n//# sourceURL=compiler.js@' + version);
30-
} else {
31-
const compiler = await fetch(`${svelte_url}/compiler/index.js`).then((r) => r.text());
32-
(0, eval)(compiler + '\n//# sourceURL=compiler/index.js@' + version);
14+
async function init(v: string) {
15+
const svelte_url = `https://unpkg.com/svelte@${v}`;
16+
const match = /^(?:pr|commit)-(.+)/.exec(v);
17+
18+
let tarball: FileDescription[] | undefined;
19+
let version: string;
20+
21+
if (match) {
22+
const response = await fetch(`https://pkg.pr.new/svelte@${match[1]}`);
23+
24+
if (!response.ok) {
25+
throw new Error('Could not fetch tarball');
3326
}
3427

35-
fulfil_ready();
28+
tarball = await parseTar(await response.arrayBuffer());
29+
30+
const json = tarball.find((file) => file.name === 'package/package.json')!.text;
31+
version = JSON.parse(json).version;
32+
} else {
33+
version = (await fetch(`${svelte_url}/package.json`).then((r) => r.json())).version;
34+
}
35+
36+
const entry = version.startsWith('3.')
37+
? 'compiler.js'
38+
: version.startsWith('4.')
39+
? 'compiler.cjs'
40+
: 'compiler/index.js';
41+
42+
const compiler = tarball
43+
? tarball.find((file) => file.name === `package/${entry}`)!.text
44+
: await fetch(`${svelte_url}/${entry}`).then((r) => r.text());
45+
46+
(0, eval)(compiler + `\n//# sourceURL=${entry}@` + version);
47+
48+
return self.svelte;
49+
}
50+
51+
addEventListener('message', async (event) => {
52+
if (!inited) {
53+
inited = Promise.withResolvers();
54+
init(event.data.version).then(inited.resolve, inited.reject);
3655
}
3756

38-
await ready;
57+
const svelte = await inited.promise;
3958

4059
const { id, file, options } = event.data as {
4160
id: number;
4261
file: File;
4362
options: ExposedCompilerOptions;
4463
};
4564

46-
if (!file.name.endsWith('.svelte') && !self.svelte.compileModule) {
65+
if (!file.name.endsWith('.svelte') && !svelte.compileModule) {
4766
// .svelte.js file compiled with Svelte 3/4 compiler
4867
postMessage({
4968
id,
@@ -59,9 +78,9 @@ addEventListener('message', async (event) => {
5978

6079
let migration = null;
6180

62-
if (self.svelte.migrate) {
81+
if (svelte.migrate) {
6382
try {
64-
migration = self.svelte.migrate(file.contents, { filename: file.name });
83+
migration = svelte.migrate(file.contents, { filename: file.name });
6584
} catch (e) {
6685
// can this happen?
6786
}
@@ -71,7 +90,7 @@ addEventListener('message', async (event) => {
7190
let result: CompileResult;
7291

7392
if (file.name.endsWith('.svelte')) {
74-
const is_svelte_3_or_4 = !self.svelte.compileModule;
93+
const is_svelte_3_or_4 = !svelte.compileModule;
7594
const compilerOptions: any = {
7695
generate: is_svelte_3_or_4
7796
? options.generate === 'client'
@@ -84,9 +103,9 @@ addEventListener('message', async (event) => {
84103
if (!is_svelte_3_or_4) {
85104
compilerOptions.modernAst = options.modernAst; // else Svelte 3/4 will throw an "unknown option" error
86105
}
87-
result = self.svelte.compile(file.contents, compilerOptions);
106+
result = svelte.compile(file.contents, compilerOptions);
88107
} else {
89-
result = self.svelte.compileModule(file.contents, {
108+
result = svelte.compileModule(file.contents, {
90109
generate: options.generate,
91110
dev: options.dev,
92111
filename: file.name

packages/repl/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"marked": "^14.1.2",
8787
"resolve.exports": "^2.0.2",
8888
"svelte": "5.0.1",
89+
"tarparser": "^0.0.4",
8990
"zimmerframe": "^1.1.2"
9091
}
9192
}

packages/repl/src/lib/Bundler.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,20 @@ export default class Bundler {
1515

1616
constructor({
1717
packages_url,
18-
svelte_url,
19-
onstatus
18+
svelte_version,
19+
onstatus,
20+
onerror
2021
}: {
2122
packages_url: string;
22-
svelte_url: string;
23+
svelte_version: string;
2324
onstatus: (val: string | null) => void;
25+
onerror?: (message: string) => void;
2426
}) {
25-
this.hash = `${packages_url}:${svelte_url}`;
27+
this.hash = `${packages_url}:${svelte_version}`;
2628

2729
if (!workers.has(this.hash)) {
2830
const worker = new Worker();
29-
worker.postMessage({ type: 'init', packages_url, svelte_url });
31+
worker.postMessage({ type: 'init', packages_url, svelte_version });
3032
workers.set(this.hash, worker);
3133
}
3234

@@ -44,6 +46,11 @@ export default class Bundler {
4446
return;
4547
}
4648

49+
if (event.data.type === 'error') {
50+
onerror?.(event.data.message);
51+
return;
52+
}
53+
4754
onstatus(null);
4855
handler(event.data);
4956
this.handlers.delete(event.data.uid);
@@ -54,7 +61,6 @@ export default class Bundler {
5461
bundle(files: File[], options: CompileOptions = {}): Promise<BundleResult> {
5562
return new Promise<any>((fulfil) => {
5663
this.handlers.set(uid, fulfil);
57-
5864
this.worker.postMessage({
5965
uid,
6066
type: 'bundle',

0 commit comments

Comments
 (0)