pull/1890/head
Rich Harris 7 years ago
parent bb41b8626f
commit 5aeaafd096

@ -1154,6 +1154,11 @@
"tiny-emitter": "^2.0.0" "tiny-emitter": "^2.0.0"
} }
}, },
"codemirror": {
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.42.0.tgz",
"integrity": "sha512-pbApC8zDzItP3HRphD6kQVwS976qB5Qi0hU3MZMixLk+AyugOW1RF+8XJEjeyl5yWsHNe88tDUxzeRh5AOxPRw=="
},
"collection-visit": { "collection-visit": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@ -1353,6 +1358,11 @@
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
}, },
"do-not-zip": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/do-not-zip/-/do-not-zip-1.0.0.tgz",
"integrity": "sha512-Pgd81ET43bhAGaN2Hq1zluSX1FmD7kl7KcV9ER/lawiLsRUB9pRA5y8r6us29Xk6BrINZETO8TjhYwtwafWUww=="
},
"ee-first": { "ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",

@ -12,7 +12,9 @@
"test": "run-p --race dev cy:run" "test": "run-p --race dev cy:run"
}, },
"dependencies": { "dependencies": {
"codemirror": "^5.42.0",
"compression": "^1.7.1", "compression": "^1.7.1",
"do-not-zip": "^1.0.0",
"express": "^4.16.4", "express": "^4.16.4",
"golden-fleece": "^1.0.9", "golden-fleece": "^1.0.9",
"marked": "^0.5.2", "marked": "^0.5.2",

@ -14,15 +14,10 @@
<script> <script>
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { observe } from 'svelte-extras';
import { locate } from 'locate-character'; import { locate } from 'locate-character';
import * as fleece from 'golden-fleece'; import * as fleece from 'golden-fleece';
import CodeMirror from './_components/CodeMirror.html';
import ModuleEditor from './_components/ModuleEditor.html';
import Viewer from './_components/Viewer.html';
import ComponentSelector from './_components/ComponentSelector.html';
import AppControls from './_components/AppControls.html'; import AppControls from './_components/AppControls.html';
import SplitPane from './_components/SplitPane.html'; import Repl from './_components/Repl.html';
import examples from '../../../content/examples/manifest.json'; import examples from '../../../content/examples/manifest.json';
export let query; export let query;
@ -31,31 +26,15 @@
const version = query.version || 'latest'; const version = query.version || 'latest';
let components = []; let components = [];
let selectedComponent;
let data = {}; let data = {};
let name = 'loading...'; let name = 'loading...';
let json5 = '{}'; let json5 = '{}';
let gist = null; let gist = null;
let uid = 0; let zen_mode = false;
let bundle;
let selectedComponent = null; $: if (typeof history !== 'undefined') {
let sourceError = null;
let runtimeError = null;
let sourceErrorLoc, runtimeErrorLoc, url;
$: if (sourceError && selectedComponent) {
sourceErrorLoc = sourceError.filename === `${selectedComponent.name}.${selectedComponent.type}`
? sourceError.start
: null;
}
$: if (runtimeError && selectedComponent) {
runtimeErrorLoc = runtimeError.filename === `${selectedComponent.name}.${selectedComponent.type}`
? runtimeError.start
: null;
}
$: {
const params = []; const params = [];
if (version !== 'latest') params.push(`version=${version}`); if (version !== 'latest') params.push(`version=${version}`);
@ -63,9 +42,11 @@
else if (query.data) params.push(`data=${query.data}`); else if (query.data) params.push(`data=${query.data}`);
else if (query.demo) params.push(`demo=${query.demo}`); else if (query.demo) params.push(`demo=${query.demo}`);
url = params.length > 0 const url = params.length > 0
? `repl?${params.join('&')}` ? `repl?${params.join('&')}`
: 'repl'; : 'repl';
history.replaceState({}, 'x', url);
} }
const slugs = new Set(); const slugs = new Set();
@ -86,66 +67,6 @@
} }
} }
function createComponent() {
const newComponent = {
name: uid++ ? `Component${uid}` : 'Component1',
type: 'html',
source: '',
edit: true
};
components = components.concat(newComponent);
// TODO need an intermediate flush here?
selectedComponent = null;
selectedComponent = newComponent;
document.getElementById(newComponent.name).scrollIntoView(false);
refs.selector.focusLast();
}
function removeComponent() {
const component = selectedComponent;
if (component.name === 'App') {
// App.html can't be removed
component.source = '';
selectedComponent = component;
} else {
const index = components.indexOf(component);
if (~index) {
components = components.slice(0, index).concat(components.slice(index + 1));
} else {
console.error(`Could not find component! That's... odd`);
}
selectedComponent = components[index] || components[components.length - 1];
}
}
function resizePanes(panes) {
panes.forEach(pane => {
refs[pane].resize();
});
}
function updateData(current) {
const data = fleece.evaluate(json5);
for (const key in data) {
data[key] = current[key];
}
json5 = fleece.patch(json5, data);
}
function navigate(filename) {
const name = filename.replace(/\.html$/, '');
if (selectedComponent.name === name) return;
selectedComponent = components.find(c => c.name === name);
}
function stringifyComponents(components) { function stringifyComponents(components) {
return JSON.stringify( return JSON.stringify(
components.map(component => { components.map(component => {
@ -168,9 +89,9 @@
version = event.data.version; version = event.data.version;
break; break;
case 'result': case 'bundled':
// TODO // TODO
// const { bundle, dom, ssr, warningCount, error } = event.data; // const { bundle, dom, ssr, warningCount, error } = event.data.result;
// if (error) { // if (error) {
// this.set({ // this.set({
@ -193,6 +114,9 @@
// compilerReady: true // compilerReady: true
// }); // });
// } // }
case 'compiled':
// TODO
} }
} }
@ -240,8 +164,8 @@
selectedComponent = components[0]; selectedComponent = components[0];
}); });
} else { } else if (!query.demo) {
if (!query.demo) query.demo = 'hello-world'; query.demo = 'hello-world';
} }
}); });
@ -272,10 +196,10 @@
last_selected_component = selectedComponent; last_selected_component = selectedComponent;
} }
$: if (demo) { function load_demo(slug) {
fetch(`api/${slugs.has(demo) ? 'examples' : 'guide/demo'}examples/${demo}`).then(async response => { fetch(`api/${slugs.has(query.demo) ? 'examples' : 'guide/demo'}/${query.demo}`).then(async response => {
if (response.ok) { if (response.ok) {
const demo = await r.json(); const demo = await response.json();
name = demo.title; name = demo.title;
json5 = demo.json5 || '{}'; json5 = demo.json5 || '{}';
@ -285,101 +209,16 @@
}); });
} }
$: try { $: if (process.browser && query.demo) {
data= fleece.evaluate(json5); load_demo(query.demo);
dataError = null;
dataErrorLoc = null;
} catch (err) {
dataError = err;
dataErrorLoc = err && err.loc;
}
$: {
history.replaceState({}, 'x', url);
} }
</script> </script>
<svelte:head>
<title>Svelte REPL</title>
</svelte:head>
<div class="repl-outer {zen_mode ? 'zen-mode' : ''}">
<AppControls
{examples}
{bundle}
{components}
{data}
{json5}
{gist}
bind:name
bind:zen_mode
on:select="{e => (demo = e.detail.slug, gist = null)}"
on:forked="{e => gist = e.detail.gist}"
/>
<div class='repl-inner'>
<SplitPane type=horizontal on:resize="{() => resizePanes(['editor', 'data'])}">
<section slot=a class="input">
<ComponentSelector
bind:this={refs.selector}
{components}
bind:selectedComponent
on:create={createComponent}
on:remove={removeComponent}
/>
<ModuleEditor
bind:this={refs.editor}
bind:selectedComponent
{compiled}
error={sourceError}
errorLoc="{sourceErrorLoc || runtimeErrorLoc}"
{warningCount}
on:navigate={navigate}
/>
</section>
<div slot=b style='height: 100%;'>
<SplitPane type=vertical on:resize="{() => resizePanes(['data'])}">
<div class=pane slot=a>
<h3 class='show-if-mobile'>Rendered component</h3>
{#if compilerReady}
<Viewer
{bundle}
{dom}
{ssr}
{data}
{sourceError}
bind:error={runtimeError}
on:data="{e => updateData(e.detail.current)}"
on:navigate={navigate}
/>
{:else}
<p class='loading'>loading Svelte compiler...</p>
{/if}
</div>
<div class=pane slot=b>
<h3 class='show-if-mobile'>data.json5</h3>
<CodeMirror
bind:this={refs.data}
mode="json"
bind:code={json5}
error={dataError}
errorLoc={dataErrorLoc}
/>
</div>
</SplitPane>
</div>
</SplitPane>
</div>
</div>
<style> <style>
.repl-outer { .repl-outer {
min-height: calc(100vh - var(--nav-h)); min-height: calc(100vh - var(--nav-h));
} }
.repl-inner { height: 100% }
.pane { width: 100%; height: 100% } .pane { width: 100%; height: 100% }
h3 { h3 {
@ -389,9 +228,9 @@
} }
@media (min-width: 768px) { @media (min-width: 768px) {
.show-if-mobile { display: none }
.repl-outer { .repl-outer {
display: grid;
grid-template-rows: 5.6rem 1fr;
min-height: auto; min-height: auto;
min-height: calc(100vh - var(--nav-h)); min-height: calc(100vh - var(--nav-h));
background-color: var(--back); background-color: var(--back);
@ -406,8 +245,6 @@
left: 0; left: 0;
z-index: 11; z-index: 11;
} }
section { height: 100% }
} }
.loading { .loading {
@ -428,55 +265,31 @@
.input { .input {
padding: 2.4em 0 0 0; padding: 2.4em 0 0 0;
perspective: 1800px;
perspective-origin: 50% 0%;
}
@media (min-width: 768px) {
.input {
perspective-origin: 50% 50%;
}
}
.repl-inner :global(.message) {
position: relative;
border-radius: var(--border-r);
margin: 0;
padding: 1.2rem 1.6rem 1.2rem 4.4rem;
vertical-align: middle;
font: 300 1.2rem/1.7 var(--font-ui);
color: white;
}
.repl-inner :global(.message::before) {
content: '!';
position: absolute;
left: 1.2rem;
top: 1.1rem;
width: 1rem;
height: 1rem;
text-align: center;
line-height: 1;
padding: .4rem;
border-radius: 50%;
color: white;
border: .2rem solid white;
}
.repl-inner :global(.error.message) {
background-color: #da106e;
} }
</style>
.repl-inner :global(.warning.message) { <svelte:head>
background-color: #e47e0a; <title>Svelte REPL</title>
} </svelte:head>
.repl-inner :global(.info.message) { <div class="repl-outer {zen_mode ? 'zen-mode' : ''}">
background-color: var(--second); <AppControls
animation: fade-in .4s .2s both; {examples}
} {bundle}
{components}
{data}
{json5}
{gist}
bind:name
bind:zen_mode
on:select="{e => (query.demo = e.detail.slug, gist = null)}"
on:forked="{e => gist = e.detail.gist}"
/>
.repl-inner :global(.error) :global(.filename) { <Repl
cursor: pointer; bind:selectedComponent
} {components}
</style> {data}
{json5}
/>
</div>

@ -11,6 +11,8 @@
<link rel='manifest' href='manifest.json'> <link rel='manifest' href='manifest.json'>
<link rel='icon' type='image/png' href='favicon.png'> <link rel='icon' type='image/png' href='favicon.png'>
<link href="https://fonts.googleapis.com/css?family=Overpass|Overpass+Mono" rel="stylesheet">
<!-- Sapper generates a <style> tag containing critical CSS <!-- Sapper generates a <style> tag containing critical CSS
for the current page. CSS for the rest of the app is for the current page. CSS for the rest of the app is
lazily loaded when it precaches secondary pages --> lazily loaded when it precaches secondary pages -->

@ -26,9 +26,11 @@
} }
body { body {
--font: aller, sans-serif; /* --font: aller, sans-serif; */
--font-alt: asap-bold, sans-serif; --font: 'Overpass', sans-serif;
/* --font-mono: overpass, monospace; */ /* --font-alt: asap-bold, sans-serif; */
--font-alt: 'Overpass', sans-serif;
--font-mono: 'Overpass Mono', monospace;
--font-ui: var(--font-mono); --font-ui: var(--font-mono);
/* ui-elements: buttons, forms etc. */ /* ui-elements: buttons, forms etc. */
} }
@ -212,9 +214,9 @@ button {
--bttn-outline: var(--border-w); --bttn-outline: var(--border-w);
--bttn-font: var(--font-ui); --bttn-font: var(--font-ui);
--bttn-calc-h: calc(var(--bttn-h) - var(--bttn-outline) * 2); --bttn-calc-h: calc(var(--bttn-h) - var(--bttn-outline) * 2);
--bttn-hover: linear-gradient( /* --bttn-hover: linear-gradient(
to top, rgba(0,0,0,.05), rgba(0,0,0,.05) to top, rgba(0,0,0,.05), rgba(0,0,0,.05)
); ); */
--bttn-active: linear-gradient( --bttn-active: linear-gradient(
to top, rgba(255,255,255,.1), rgba(255,255,255,.1) to top, rgba(255,255,255,.1), rgba(255,255,255,.1)
); );

@ -1,4 +1,42 @@
global.window = self; // egregious hack to get magic-string to work in a worker self.window = self; // egregious hack to get magic-string to work in a worker
let ready = false;
let pending_components;
let pending_component;
self.addEventListener('message', async event => {
switch (event.data.type) {
case 'version':
self.postMessage({
type: 'version',
version: await init(event.data.version)
}, '*');
break;
case 'bundle':
if (ready) {
self.postMessage({
type: 'bundled',
result: await bundle(event.data.components)
}, '*');
} else {
pending_components = event.data.components;
}
break;
case 'compile':
if (ready) {
self.postMessage({
type: 'compiled',
result: await compile(event.data.component)
}, '*');
} else {
pending_component = event.data.component;
}
break;
}
});
const commonCompilerOptions = { const commonCompilerOptions = {
cascade: false, cascade: false,
@ -8,34 +46,48 @@ const commonCompilerOptions = {
dev: true, dev: true,
}; };
const svelteCache = new Map(); async function init(version) {
// TODO use local versions
function loadSvelte(version) { importScripts(
if (!svelteCache.has(version)) { `https://unpkg.com/svelte@${version}/compiler/svelte.js`,
if (version === 'local') { `https://unpkg.com/rollup/dist/rollup.browser.js`
svelteCache.set(version, import(/* webpackChunkName: "svelte" */ 'svelte')); );
} else {
svelteCache.set(version, new Promise((fulfil => { console.log(1);
importScripts(`https://unpkg.com/svelte@${version}/compiler/svelte.js`); console.log({
fulfil(global.svelte); svelte: self.svelte,
}))) rollup: self.rollup
}
}
return svelteCache.get(version).then(svelte => {
global.svelte = svelte;
}); });
} console.log({
svelte,
rollup
});
console.log(2);
self.postMessage({
type: 'version',
version: version === 'local' ? version : svelte.VERSION
}, '*');
export async function init(version) { ready = true;
await Promise.all([
import(/* webpackChunkName: "rollup" */ 'rollup/dist/rollup.browser.js').then(r => {
global.rollup = r;
}),
loadSvelte(version)
]);
return version === 'local' ? version : svelte.VERSION; if (pending_components) {
self.postMessage({
type: 'bundled',
result: await bundle(pending_components)
}, '*');
pending_components = null;
}
if (pending_component) {
self.postMessage({
type: 'compiled',
result: await compile(pending_component)
}, '*');
pending_component = null;
}
} }
let cached = { let cached = {
@ -104,8 +156,8 @@ async function getBundle(mode, cache, lookup) {
return { bundle, info, error: null, warningCount }; return { bundle, info, error: null, warningCount };
} }
export async function bundle(components) { async function bundle(components) {
console.clear(); // console.clear();
console.log(`running Svelte compiler version %c${svelte.VERSION}`, 'font-weight: bold'); console.log(`running Svelte compiler version %c${svelte.VERSION}`, 'font-weight: bold');
const token = currentToken = {}; const token = currentToken = {};
@ -194,7 +246,7 @@ export async function bundle(components) {
} }
} }
export function compile(component) { function compile(component) {
try { try {
const { js } = svelte.compile(component.source, Object.assign({ const { js } = svelte.compile(component.source, Object.assign({
// TODO make options configurable // TODO make options configurable

Loading…
Cancel
Save