diff --git a/packages/repl/src/lib/Repl.svelte b/packages/repl/src/lib/Repl.svelte index 0a279b84..b39162ca 100644 --- a/packages/repl/src/lib/Repl.svelte +++ b/packages/repl/src/lib/Repl.svelte @@ -12,7 +12,7 @@ import { set_repl_context } from './context.js'; import { get_full_filename } from './utils.js'; - export let packagesUrl = 'https://unpkg.com'; + export let packagesUrl = 'https://esm.run'; export let svelteUrl = `${packagesUrl}/svelte`; export let embedded = false; /** @type {'columns' | 'rows'} */ diff --git a/packages/repl/src/lib/workers/bundler/index.js b/packages/repl/src/lib/workers/bundler/index.js index 5887f6e7..69bd00ba 100644 --- a/packages/repl/src/lib/workers/bundler/index.js +++ b/packages/repl/src/lib/workers/bundler/index.js @@ -1,10 +1,11 @@ /// import '../patch_window.js'; -import { sleep } from '$lib/utils.js'; + import { rollup } from '@rollup/browser'; import { DEV } from 'esm-env'; import * as resolve from 'resolve.exports'; +import { get_svelte_package_json, load_compiler } from '../worker-helpers.js'; import commonjs from './plugins/commonjs.js'; import glsl from './plugins/glsl.js'; import json from './plugins/json.js'; @@ -22,7 +23,7 @@ let svelte_url; /** @type {number} */ let current_id; -/** @type {(...arg: never) => void} */ +/** @type {(...arg: any) => void} */ let fulfil_ready; const ready = new Promise((f) => { fulfil_ready = f; @@ -35,21 +36,10 @@ self.addEventListener( case 'init': { ({ packages_url, svelte_url } = event.data); - const { version } = await fetch(`${svelte_url}/package.json`).then((r) => r.json()); + const { version } = await get_svelte_package_json(svelte_url); console.log(`Using Svelte compiler version ${version}`); - 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 { - try { - importScripts(`${svelte_url}/compiler.js`); - } catch { - self.svelte = await import(/* @vite-ignore */ `${svelte_url}/compiler.mjs`); - } - } + await load_compiler(svelte_url, version); fulfil_ready(); break; @@ -63,7 +53,7 @@ self.addEventListener( current_id = uid; - setTimeout(async () => { + Promise.resolve().then(async () => { if (current_id !== uid) return; const result = await bundle({ uid, files }); @@ -99,12 +89,13 @@ async function fetch_if_uncached(url, uid) { } // TODO: investigate whether this is necessary - await sleep(50); + // await sleep(50); if (uid !== current_id) throw ABORT; const promise = fetch(url) .then(async (r) => { if (!r.ok) throw new Error(await r.text()); + if (r.headers.get('content-type')?.includes('text/html')) throw new Error('HTML!'); return { url: r.url, @@ -125,7 +116,24 @@ async function fetch_if_uncached(url, uid) { * @param {number} uid */ async function follow_redirects(url, uid) { - const res = await fetch_if_uncached(url, uid); + /** @type {{ + * url: string; + * body: string; + * } | undefined} */ + let res; + console.log(url); + + const paths = ['', '.js', '/index.js', '.mjs', '/index.mjs', '.cjs', '/index.cjs']; + + for (const path of paths) { + try { + res = await fetch_if_uncached(url.replace(/\/$/, '') + path, uid); + break; + } catch { + // maybe the next option will be successful + } + } + return res?.url; } @@ -314,7 +322,12 @@ async function get_bundle(uid, mode, cache, local_files_lookup) { const fetch_package_info = async () => { try { - const pkg_url = await follow_redirects(`${packages_url}/${pkg_name}/package.json`, uid); + const pkg_url = await follow_redirects( + `${ + packages_url.includes('esm.run') ? 'https://cdn.jsdelivr.net/npm' : packages_url + }/${pkg_name}/package.json`, + uid + ); if (!pkg_url) throw new Error(); @@ -328,7 +341,7 @@ async function get_bundle(uid, mode, cache, local_files_lookup) { pkg_url_base }; } catch (_e) { - throw new Error(`Error fetching "${pkg_name}" from unpkg. Does the package exist?`); + throw new Error(`Error fetching "${pkg_name}". Does the package exist?`); } }; @@ -365,6 +378,7 @@ async function get_bundle(uid, mode, cache, local_files_lookup) { const name = id.split('/').pop()?.split('.')[0]; const cached_id = cache.get(id); + const result = cached_id && cached_id.code === code ? cached_id.result @@ -377,17 +391,19 @@ async function get_bundle(uid, mode, cache, local_files_lookup) { }) }); + console.log(code); + new_cache.set(id, { code, result }); // @ts-expect-error - (result.warnings || result.stats.warnings)?.forEach((warning) => { + for (const warning of (result.warnings || result.stats.warnings) ?? []) { // This is required, otherwise postMessage won't work // @ts-ignore delete warning.toString; // TODO remove stats post-launch // @ts-ignore warnings.push(warning); - }); + } return result.js; } @@ -402,7 +418,10 @@ async function get_bundle(uid, mode, cache, local_files_lookup) { json, glsl, replace({ - 'process.env.NODE_ENV': JSON.stringify('production') + 'process.env.NODE_ENV': JSON.stringify('production'), + 'import.meta.env.PROD': JSON.stringify(true), + 'import.meta.env.DEV': JSON.stringify(false), + 'import.meta.env.SSR': JSON.stringify(mode === 'ssr') }) ], inlineDynamicImports: true, @@ -464,6 +483,8 @@ async function bundle({ uid, files }) { }) )?.output[0]; + console.log(dom_result?.code); + const ssr = false // TODO how can we do SSR? ? await get_bundle(uid, 'ssr', cached.ssr, lookup) : null; @@ -495,7 +516,7 @@ async function bundle({ uid, files }) { error: null }; } catch (err) { - console.error(err); + console.trace(err); /** @type {Error} */ // @ts-ignore diff --git a/packages/repl/src/lib/workers/compiler/index.js b/packages/repl/src/lib/workers/compiler/index.js index 330e29c7..652c8796 100644 --- a/packages/repl/src/lib/workers/compiler/index.js +++ b/packages/repl/src/lib/workers/compiler/index.js @@ -1,11 +1,10 @@ /// -self.window = self; //TODO: still need?: egregious hack to get magic-string to work in a worker -// This is just for type-safety -/** @type {import('svelte/compiler')} */ -var svelte; +import { get_svelte_package_json, load_compiler } from '../worker-helpers'; + +self.window = self; //TODO: still need?: egregious hack to get magic-string to work in a worker -/** @type {(...val: never) => void} */ +/** @type {(...val: never[]) => void} */ let fulfil_ready; const ready = new Promise((f) => { fulfil_ready = f; @@ -17,21 +16,10 @@ self.addEventListener( async (event) => { switch (event.data.type) { case 'init': - const { svelte_url } = event.data; - const { version } = await fetch(`${svelte_url}/package.json`).then((r) => r.json()); + const { svelte_url = '' } = event.data; + const { version } = await get_svelte_package_json(svelte_url); - 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 { - try { - importScripts(`${svelte_url}/compiler.js`); - } catch { - self.svelte = await import(/* @vite-ignore */ `${svelte_url}/compiler.mjs`); - } - } + await load_compiler(svelte_url, version); fulfil_ready(); break; diff --git a/packages/repl/src/lib/workers/worker-helpers.js b/packages/repl/src/lib/workers/worker-helpers.js new file mode 100644 index 00000000..73b9d33b --- /dev/null +++ b/packages/repl/src/lib/workers/worker-helpers.js @@ -0,0 +1,65 @@ +/** + * Replaces export {} with svelte.EXPORT = EXPORT + * @param {string} input + */ +function remove_exports(input) { + const pattern = /export\{(.*?)\};/g; + + return input.replace( + pattern, + /** + @param {string} _ + @param {string} exports + */ (_, exports) => { + return exports + .split(',') + .map((e) => { + const [original, alias] = e.split(' as ').map((s) => s.trim()); + return `svelte.${alias} = ${original};`; + }) + .join(''); + } + ); +} + +/** + * @param {string} url + */ +export async function get_svelte_package_json(url) { + if (url.includes('https://esm.run')) { + // This will be an import, rather manually get the package.json + url = url.replace('https://esm.run', 'https://cdn.jsdelivr.net/npm'); + } + + return await fetch(`${url}/package.json`).then((r) => r.json()); +} + +/** + * Loads the compiler from the specified version + * @param {string} version + * @param {string} svelte_url + */ +export async function load_compiler(svelte_url, version) { + if (version.startsWith('4')) { + let compiler = await fetch( + `${ + svelte_url.includes('esm.run') + ? `https://cdn.jsdelivr.net/npm/svelte@${version}` + : svelte_url + }/compiler.cjs` + ).then((r) => r.text()); + + if (svelte_url.includes('esm.run')) { + // Remove all the exports + compiler = remove_exports(compiler); + } + + (0, eval)('var svelte = {};' + compiler + '\n//# sourceURL=compiler.js@' + version); + } else { + try { + importScripts(`${svelte_url}/compiler.js`); + } catch { + self.svelte = await import(/* @vite-ignore */ `${svelte_url}/compiler.mjs`); + } + } +} diff --git a/packages/repl/src/routes/+page.svelte b/packages/repl/src/routes/+page.svelte index b64ffd03..8702de23 100644 --- a/packages/repl/src/routes/+page.svelte +++ b/packages/repl/src/routes/+page.svelte @@ -13,22 +13,52 @@ name: 'App', type: 'svelte', source: - '\n\timport Timeline from './Timeline.svelte'\n\timport Sequence from './Sequence.svelte'\n\t\n\timport { tweened } from 'svelte/motion';\n\n\n\n\t\n\t\t
\n\t\t\t

\n\t\t\t\tHello Svelte\n\t\t\t

\n\t\t\t

\n\t\t\t\tThis is a test.\n\t\t\t

\n\t\t
\n\t
\n
\n' - }, - { - name: 'Sequence', - type: 'svelte', - source: - '\n\timport { onMount, onDestroy, getContext } from 'svelte';\n\t\n\tconst timeline = getContext('x:timeline');\n\t\n\t$: ({ width, height, fps } = $timeline);\n\t\n\texport let duration = fps * 10;\n\texport let start = 0;\n\texport let track = 1;\n\t\n\t$: frame = $timeline.frame - start;\n\n\n{#if timeline}\n\t
\n\t\t\n\t
\n{/if}\n' - }, - { - name: 'Timeline', - type: 'svelte', - source: '' + ` + import { ConfettiExplosion } from 'svelte-confetti-explosion' + import { tick } from 'svelte' + + let x, y; + let isVisible = false; + + const handleClick = async e => { + x = e.clientX; + y = e.clientY; + + isVisible = false; + await tick(); + isVisible = true + } + + + + +{#if isVisible} + +{/if} + +
+ Click anywhere for confetti +
+ +` } ] }); @@ -36,7 +66,7 @@
- +