|
|
@ -1,5 +1,6 @@
|
|
|
|
<script>
|
|
|
|
<script>
|
|
|
|
import { onMount } from 'svelte';
|
|
|
|
import { onMount } from 'svelte';
|
|
|
|
|
|
|
|
import { writable } from 'svelte/store';
|
|
|
|
import * as fleece from 'golden-fleece';
|
|
|
|
import * as fleece from 'golden-fleece';
|
|
|
|
import SplitPane from './SplitPane.html';
|
|
|
|
import SplitPane from './SplitPane.html';
|
|
|
|
import CodeMirror from './CodeMirror.html';
|
|
|
|
import CodeMirror from './CodeMirror.html';
|
|
|
@ -7,151 +8,146 @@
|
|
|
|
import Output from './Output/index.html';
|
|
|
|
import Output from './Output/index.html';
|
|
|
|
|
|
|
|
|
|
|
|
export let version;
|
|
|
|
export let version;
|
|
|
|
export let components;
|
|
|
|
export let app;
|
|
|
|
export let selectedComponent;
|
|
|
|
|
|
|
|
export let values;
|
|
|
|
let component_store = writable([]);
|
|
|
|
|
|
|
|
let values_store = writable({});
|
|
|
|
|
|
|
|
let selected_store = writable(null);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$: {
|
|
|
|
|
|
|
|
component_store.set(app.components);
|
|
|
|
|
|
|
|
values_store.set(app.values);
|
|
|
|
|
|
|
|
selected_store.set(app.components[0]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$: {
|
|
|
|
|
|
|
|
// necessary pending https://github.com/sveltejs/svelte/issues/1889
|
|
|
|
|
|
|
|
$component_store;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let workers;
|
|
|
|
|
|
|
|
|
|
|
|
let bundle = null;
|
|
|
|
let bundle = null;
|
|
|
|
let dom;
|
|
|
|
let dom;
|
|
|
|
let ssr;
|
|
|
|
let ssr;
|
|
|
|
let sourceError = null;
|
|
|
|
let sourceError = null;
|
|
|
|
let runtimeError = null;
|
|
|
|
let runtimeError = null;
|
|
|
|
let dataError = null;
|
|
|
|
|
|
|
|
let dataErrorLoc = null;
|
|
|
|
|
|
|
|
let warningCount = 0;
|
|
|
|
let warningCount = 0;
|
|
|
|
let code = '';
|
|
|
|
let code = '';
|
|
|
|
let uid = 0;
|
|
|
|
let uid = 0;
|
|
|
|
let props = [];
|
|
|
|
let props = null;
|
|
|
|
let sourceErrorLoc;
|
|
|
|
let sourceErrorLoc;
|
|
|
|
let runtimeErrorLoc;
|
|
|
|
let runtimeErrorLoc;
|
|
|
|
|
|
|
|
|
|
|
|
let worker;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onMount(async () => {
|
|
|
|
onMount(async () => {
|
|
|
|
worker = new Worker('/repl-worker.js');
|
|
|
|
workers = {
|
|
|
|
|
|
|
|
compiler: new Worker('/workers/compiler.js'),
|
|
|
|
function listener(event) {
|
|
|
|
bundler: new Worker('/workers/bundler.js')
|
|
|
|
switch (event.data.type) {
|
|
|
|
};
|
|
|
|
case 'version':
|
|
|
|
|
|
|
|
version = event.data.version;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'bundled':
|
|
|
|
|
|
|
|
({ bundle, dom, ssr, warningCount, error: sourceError } = event.data.result);
|
|
|
|
|
|
|
|
console.log(dom, sourceError);
|
|
|
|
|
|
|
|
runtimeError = null;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'compiled':
|
|
|
|
|
|
|
|
code = event.data.result.code;
|
|
|
|
|
|
|
|
if (event.data.result.props) props = event.data.result.props;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
worker.addEventListener('message', listener);
|
|
|
|
workers.compiler.postMessage({ type: 'init', version });
|
|
|
|
|
|
|
|
workers.compiler.onmessage = event => {
|
|
|
|
|
|
|
|
code = event.data.code;
|
|
|
|
|
|
|
|
if (event.data.props) props = event.data.props;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
worker.postMessage({ type: 'init', version });
|
|
|
|
workers.bundler.postMessage({ type: 'init', version });
|
|
|
|
|
|
|
|
workers.bundler.onmessage = event => {
|
|
|
|
|
|
|
|
({ bundle, dom, ssr, warningCount, error: sourceError } = event.data);
|
|
|
|
|
|
|
|
if (sourceError) console.error(sourceError);
|
|
|
|
|
|
|
|
runtimeError = null;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
return () => {
|
|
|
|
worker.removeEventListener('message', listener);
|
|
|
|
workers.compiler.terminate();
|
|
|
|
worker.terminate();
|
|
|
|
workers.bundler.terminate();
|
|
|
|
};
|
|
|
|
};
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
function removeComponent() {
|
|
|
|
function removeComponent() {
|
|
|
|
const component = selectedComponent;
|
|
|
|
const selected = $selected_store;
|
|
|
|
|
|
|
|
|
|
|
|
if (component.name === 'App') {
|
|
|
|
if (selected.name === 'App') {
|
|
|
|
// App.html can't be removed
|
|
|
|
// App.html can't be removed
|
|
|
|
component.source = '';
|
|
|
|
selected.source = '';
|
|
|
|
selectedComponent = component;
|
|
|
|
// $selected_store.set(selected);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
const index = components.indexOf(component);
|
|
|
|
const components = $component_store;
|
|
|
|
|
|
|
|
const index = components.indexOf(selected);
|
|
|
|
|
|
|
|
|
|
|
|
if (~index) {
|
|
|
|
if (~index) {
|
|
|
|
components = components.slice(0, index).concat(components.slice(index + 1));
|
|
|
|
component_store.set(components.slice(0, index).concat(components.slice(index + 1)));
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
console.error(`Could not find component! That's... odd`);
|
|
|
|
console.error(`Could not find component! That's... odd`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
selectedComponent = components[index] || components[components.length - 1];
|
|
|
|
selected_store.set(components[index] || components[components.length - 1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function compile(component) {
|
|
|
|
function compile(component) {
|
|
|
|
if (component.type === 'html') {
|
|
|
|
if (component.type === 'html') {
|
|
|
|
worker.postMessage({
|
|
|
|
workers.compiler.postMessage({
|
|
|
|
type: 'compile',
|
|
|
|
type: 'compile',
|
|
|
|
component,
|
|
|
|
source: component.source,
|
|
|
|
entry: component === components[0]
|
|
|
|
options: {
|
|
|
|
|
|
|
|
name: component.name,
|
|
|
|
|
|
|
|
filename: `${component.name}.html`
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
entry: component === $component_store[0]
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
code = component.source;
|
|
|
|
code = component.source;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleSelect(event) {
|
|
|
|
function handleChange(event) {
|
|
|
|
console.log(`handleSelect`);
|
|
|
|
selected_store.update(component => {
|
|
|
|
|
|
|
|
// TODO this is a bit hacky — we're relying on mutability
|
|
|
|
selectedComponent = event.detail.component;
|
|
|
|
// so that updating component_store works... might be better
|
|
|
|
// compile(selectedComponent);
|
|
|
|
// if a) components had unique IDs, b) we tracked selected
|
|
|
|
}
|
|
|
|
// *index* rather than component, and c) `selected` was
|
|
|
|
|
|
|
|
// derived from `components` and `index`
|
|
|
|
|
|
|
|
component.source = event.detail.value;
|
|
|
|
|
|
|
|
return component;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
function handleChange() {
|
|
|
|
component_store.update(c => c);
|
|
|
|
console.log(`handleChange`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// recompile selected component
|
|
|
|
// recompile selected component
|
|
|
|
compile(selectedComponent);
|
|
|
|
compile($selected_store);
|
|
|
|
|
|
|
|
|
|
|
|
// regenerate bundle (TODO do this in a separate worker?)
|
|
|
|
// regenerate bundle (TODO do this in a separate worker?)
|
|
|
|
worker.postMessage({ type: 'bundle', components });
|
|
|
|
workers.bundler.postMessage({ type: 'bundle', components: $component_store });
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
function navigate(filename) {
|
|
|
|
const name = filename.replace(/\.html$/, '');
|
|
|
|
const name = filename.replace(/\.html$/, '');
|
|
|
|
|
|
|
|
|
|
|
|
if (selectedComponent.name === name) return;
|
|
|
|
console.error(`TODO navigate`);
|
|
|
|
selectedComponent = components.find(c => c.name === name);
|
|
|
|
|
|
|
|
|
|
|
|
// if (selected.name === name) return;
|
|
|
|
|
|
|
|
// selected = components.find(c => c.name === name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$: if (sourceError && selectedComponent) {
|
|
|
|
$: if (sourceError && $selected_store) {
|
|
|
|
sourceErrorLoc = sourceError.filename === `${selectedComponent.name}.${selectedComponent.type}`
|
|
|
|
sourceErrorLoc = sourceError.filename === `${$selected_store.name}.${$selected_store.type}`
|
|
|
|
? sourceError.start
|
|
|
|
? sourceError.start
|
|
|
|
: null;
|
|
|
|
: null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$: if (runtimeError && selectedComponent) {
|
|
|
|
$: if (runtimeError && $selected_store) {
|
|
|
|
runtimeErrorLoc = runtimeError.filename === `${selectedComponent.name}.${selectedComponent.type}`
|
|
|
|
runtimeErrorLoc = runtimeError.filename === `${$selected_store.name}.${$selected_store.type}`
|
|
|
|
? runtimeError.start
|
|
|
|
? runtimeError.start
|
|
|
|
: null;
|
|
|
|
: null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$: if (worker && components) {
|
|
|
|
$: if (workers && app.components) {
|
|
|
|
console.log(`posting`, worker, components);
|
|
|
|
workers.bundler.postMessage({ type: 'bundle', components: app.components });
|
|
|
|
worker.postMessage({ type: 'bundle', components });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$: if (worker && selectedComponent) {
|
|
|
|
|
|
|
|
compile(selectedComponent);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$: try {
|
|
|
|
$: if (workers && $selected_store) {
|
|
|
|
data= fleece.evaluate(json5);
|
|
|
|
compile($selected_store);
|
|
|
|
dataError = null;
|
|
|
|
|
|
|
|
dataErrorLoc = null;
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
|
|
dataError = err;
|
|
|
|
|
|
|
|
dataErrorLoc = err && err.loc;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
@ -239,13 +235,12 @@
|
|
|
|
<SplitPane type="horizontal">
|
|
|
|
<SplitPane type="horizontal">
|
|
|
|
<section slot=a>
|
|
|
|
<section slot=a>
|
|
|
|
<Input
|
|
|
|
<Input
|
|
|
|
bind:components
|
|
|
|
{component_store}
|
|
|
|
{selectedComponent}
|
|
|
|
{selected_store}
|
|
|
|
|
|
|
|
{values_store}
|
|
|
|
error={sourceError}
|
|
|
|
error={sourceError}
|
|
|
|
errorLoc="{sourceErrorLoc || runtimeErrorLoc}"
|
|
|
|
errorLoc="{sourceErrorLoc || runtimeErrorLoc}"
|
|
|
|
{warningCount}
|
|
|
|
{warningCount}
|
|
|
|
on:create="{e => components = components.concat(e.detail.component)}"
|
|
|
|
|
|
|
|
on:select="{e => selectedComponent = e.detail.component}"
|
|
|
|
|
|
|
|
on:remove={removeComponent}
|
|
|
|
on:remove={removeComponent}
|
|
|
|
on:change="{handleChange}"
|
|
|
|
on:change="{handleChange}"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
@ -253,16 +248,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
<section slot=b style='height: 100%;'>
|
|
|
|
<section slot=b style='height: 100%;'>
|
|
|
|
<Output
|
|
|
|
<Output
|
|
|
|
|
|
|
|
{version}
|
|
|
|
|
|
|
|
{selected_store}
|
|
|
|
{code}
|
|
|
|
{code}
|
|
|
|
{bundle}
|
|
|
|
{bundle}
|
|
|
|
{ssr}
|
|
|
|
{ssr}
|
|
|
|
{dom}
|
|
|
|
{dom}
|
|
|
|
{props}
|
|
|
|
{props}
|
|
|
|
{values}
|
|
|
|
{values_store}
|
|
|
|
{sourceError}
|
|
|
|
{sourceError}
|
|
|
|
{runtimeError}
|
|
|
|
{runtimeError}
|
|
|
|
{dataError}
|
|
|
|
|
|
|
|
{dataErrorLoc}
|
|
|
|
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
</SplitPane>
|
|
|
|
</SplitPane>
|
|
|
|