Merge branch 'master' into v3-readme

pull/1953/head
Conduitry 7 years ago
commit c92df84e64

@ -33,9 +33,8 @@
"plugin:import/errors",
"plugin:import/warnings"
],
"plugins": ["svelte3"],
"parserOptions": {
"ecmaVersion": 6,
"ecmaVersion": 9,
"sourceType": "module"
},
"settings": {

1
.gitignore vendored

@ -2,6 +2,7 @@
.nyc_output
node_modules
*.map
/src/compile/internal-exports.ts
/cli/
/compiler.js
/index.js

@ -3,5 +3,8 @@ export {
onDestroy,
beforeUpdate,
afterUpdate,
setContext,
getContext,
tick,
createEventDispatcher
} from './internal';

3955
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,9 +1,9 @@
{
"name": "svelte",
"version": "3.0.0-alpha15",
"version": "3.0.0-beta.3",
"description": "The magical disappearing UI framework",
"module": "index.mjs",
"main": "index.js",
"main": "index",
"bin": {
"svelte": "svelte"
},
@ -23,18 +23,17 @@
"scripts": {
"test": "mocha --opts mocha.opts",
"quicktest": "mocha --opts mocha.opts",
"precoverage": "export COVERAGE=true && nyc mocha --opts mocha.coverage.opts",
"coverage": "nyc report --reporter=text-lcov > coverage.lcov",
"precoverage": "c8 mocha --opts mocha.coverage.opts",
"coverage": "c8 report --reporter=text-lcov > coverage.lcov && c8 report --reporter=html",
"codecov": "codecov",
"precodecov": "npm run coverage",
"lint": "eslint src test/*.js",
"build": "rollup -c",
"prepare": "npm run build",
"dev": "rollup -c -w",
"dev": "rollup -cw",
"pretest": "npm run build",
"posttest": "agadoo src/internal/index.js",
"prepublishOnly": "npm run lint && npm test",
"prettier": "prettier --write \"src/**/*.ts\""
"prepublishOnly": "export PUBLISH=true && npm run lint && npm test"
},
"repository": {
"type": "git",
@ -55,31 +54,31 @@
"devDependencies": {
"@types/mocha": "^5.2.0",
"@types/node": "^10.5.5",
"acorn": "^5.4.1",
"acorn-dynamic-import": "^3.0.0",
"acorn": "^6.0.5",
"acorn-dynamic-import": "^4.0.0",
"agadoo": "^1.0.1",
"c8": "^3.4.0",
"codecov": "^3.0.0",
"css-tree": "1.0.0-alpha22",
"eslint": "^5.3.0",
"eslint-plugin-html": "^4.0.3",
"eslint-plugin-html": "^5.0.0",
"eslint-plugin-import": "^2.11.0",
"estree-walker": "^0.6.0",
"is-reference": "^1.1.1",
"jsdom": "^11.8.0",
"jsdom": "^12.2.0",
"kleur": "^3.0.0",
"locate-character": "^2.0.5",
"magic-string": "^0.25.0",
"magic-string": "^0.25.2",
"mocha": "^5.2.0",
"nightmare": "^3.0.1",
"node-resolve": "^1.3.3",
"nyc": "^12.0.2",
"prettier": "^1.12.1",
"rollup": "^0.63.5",
"rollup": "^1.1.2",
"rollup-plugin-commonjs": "^9.1.0",
"rollup-plugin-json": "^3.0.0",
"rollup-plugin-node-resolve": "^3.3.0",
"rollup-plugin-node-resolve": "^4.0.0",
"rollup-plugin-replace": "^2.0.0",
"rollup-plugin-typescript": "^0.8.1",
"rollup-plugin-sucrase": "^2.1.0",
"rollup-plugin-typescript": "^1.0.0",
"rollup-plugin-virtual": "^1.0.1",
"rollup-watch": "^4.3.1",
"sade": "^1.4.0",
@ -88,7 +87,7 @@
"source-map": "0.6",
"source-map-support": "^0.5.4",
"tiny-glob": "^0.2.1",
"ts-node": "^7.0.0",
"ts-node": "^8.0.2",
"tslib": "^1.8.0",
"typescript": "^3.0.1"
},

@ -2,21 +2,21 @@ const fs = require('fs');
const path = require('path');
const { compile } = require('./compiler.js');
let compileOptions = {
extensions: ['.html']
};
let extensions = ['.svelte', '.html'];
let compileOptions = {};
function capitalise(name) {
return name[0].toUpperCase() + name.slice(1);
}
function register(options) {
function register(options = {}) {
if (options.extensions) {
compileOptions.extensions.forEach(deregisterExtension);
extensions.forEach(deregisterExtension);
options.extensions.forEach(registerExtension);
}
compileOptions = options;
compileOptions = Object.assign({}, options);
delete compileOptions.extensions;
}
function deregisterExtension(extension) {
@ -43,6 +43,7 @@ function registerExtension(extension) {
};
}
registerExtension('.svelte');
registerExtension('.html');
module.exports = register;

@ -1,11 +1,41 @@
import fs from 'fs';
import replace from 'rollup-plugin-replace';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import json from 'rollup-plugin-json';
import sucrase from 'rollup-plugin-sucrase';
import typescript from 'rollup-plugin-typescript';
import pkg from './package.json';
const is_publish = !!process.env.PUBLISH;
export default [
/* internal.[m]js */
{
input: `src/internal/index.js`,
output: [
{
file: `internal.mjs`,
format: 'esm',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
},
{
file: `internal.js`,
format: 'cjs',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
}
],
external: id => id.startsWith('svelte/'),
plugins: [{
generateBundle(options, bundle) {
const mod = bundle['internal.mjs'];
if (mod) {
fs.writeFileSync('src/compile/internal-exports.ts', `// This file is automatically generated\nexport default new Set(${JSON.stringify(mod.exports)});`);
}
}
}]
},
/* compiler.js */
{
input: 'src/index.ts',
@ -14,20 +44,29 @@ export default [
__VERSION__: pkg.version
}),
resolve(),
commonjs(),
commonjs({
include: ['node_modules/**']
}),
json(),
typescript({
include: 'src/**',
exclude: 'src/internal/**',
typescript: require('typescript')
})
is_publish
? typescript({
include: 'src/**',
exclude: 'src/internal/**',
typescript: require('typescript')
})
: sucrase({
transforms: ['typescript']
})
],
output: {
file: 'compiler.js',
format: 'umd',
format: is_publish ? 'umd' : 'cjs',
name: 'svelte',
sourcemap: true
}
sourcemap: true,
},
external: is_publish
? []
: id => id === 'acorn' || id === 'magic-string' || id.startsWith('css-tree')
},
/* cli/*.js */
@ -48,27 +87,26 @@ export default [
typescript({
typescript: require('typescript')
})
],
experimentalCodeSplitting: true
]
},
/* internal.[m]js, motion.mjs */
...['internal', 'motion'].map(name => ({
input: `src/${name}/index.js`,
/* motion.mjs */
{
input: `src/motion/index.js`,
output: [
{
file: `${name}.mjs`,
file: `motion.mjs`,
format: 'esm',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
},
{
file: `${name}.js`,
file: `motion.js`,
format: 'cjs',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
}
],
external: id => id.startsWith('svelte/')
})),
},
// everything else
...['index', 'store', 'easing', 'transition'].map(name => ({

@ -35,7 +35,7 @@
],
"plugins": ["svelte3"],
"parserOptions": {
"ecmaVersion": 6,
"ecmaVersion": 8,
"sourceType": "module"
},
"settings": {

@ -1,75 +1,26 @@
# sapper-template-rollup
## Running locally
A version of the default [Sapper](https://github.com/sveltejs/sapper) template that uses Rollup instead of webpack. To clone it and get started:
Set up the project:
```bash
npx degit sveltejs/sapper-template#rollup my-app
cd my-app
npm install # or yarn!
npm run dev
git clone https://github.com/sveltejs/svelte.git
cd svelte/site
npm ci
npm run update
```
Open up [localhost:3000](http://localhost:3000) and start clicking around.
Start the server with `npm run dev`, and navigate to [localhost:3000](http://localhost:3000).
Consult [sapper.svelte.technology](https://sapper.svelte.technology) for help getting started.
## Using a local copy of Svelte
*[Click here for the webpack version of this template](https://github.com/sveltejs/sapper-template)*
By default, the REPL will fetch the most recent version of Svelte from https://unpkg.com/svelte. To use the local copy of the compiler and runtime from this repo, you can navigate to [localhost:3000/repl?version=local](http://localhost:3000/repl?version=local).
## Structure
## REPL GitHub integration
Sapper expects to find three directories in the root of your project — `app`, `assets` and `routes`.
In order for the REPL's GitHub integration to work properly when running locally, you will need to create a GitHub OAuth app. Set its authorization callback URL to `http://localhost:3000/auth/callback`, and in this project, create `site/.env` containing:
### app
The [app](app) directory contains the entry points for your app — `client.js`, `server.js` and (optionally) a `service-worker.js` — along with a `template.html` file.
### assets
The [assets](assets) directory contains any static assets that should be available. These are served using [sirv](https://github.com/lukeed/sirv).
In your [service-worker.js](app/service-worker.js) file, you can import these as `assets` from the generated manifest...
```js
import { assets } from './manifest/service-worker.js';
```
...so that you can cache them (though you can choose not to, for example if you don't want to cache very large files).
### routes
This is the heart of your Sapper app. There are two kinds of routes — *pages*, and *server routes*.
**Pages** are Svelte components written in `.html` files. When a user first visits the application, they will be served a server-rendered version of the route in question, plus some JavaScript that 'hydrates' the page and initialises a client-side router. From that point forward, navigating to other pages is handled entirely on the client for a fast, app-like feel. (Sapper will preload and cache the code for these subsequent pages, so that navigation is instantaneous.)
**Server routes** are modules written in `.js` files, that export functions corresponding to HTTP methods. Each function receives Express `request` and `response` objects as arguments, plus a `next` function. This is useful for creating a JSON API, for example.
There are three simple rules for naming the files that define your routes:
* A file called `routes/about.html` corresponds to the `/about` route. A file called `routes/blog/[slug].html` corresponds to the `/blog/:slug` route, in which case `params.slug` is available to the route
* The file `routes/index.html` (or `routes/index.js`) corresponds to the root of your app. `routes/about/index.html` is treated the same as `routes/about.html`.
* Files and directories with a leading underscore do *not* create routes. This allows you to colocate helper modules and components with the routes that depend on them — for example you could have a file called `routes/_helpers/datetime.js` and it would *not* create a `/_helpers/datetime` route
## Rollup config
Sapper uses Rollup to provide code-splitting and dynamic imports, as well as compiling your Svelte components. As long as you don't do anything daft, you can edit the configuration files to add whatever plugins you'd like.
## Production mode and deployment
To start a production version of your app, run `npm run build && npm start`.
You can deploy your application to any environment that supports Node 8 or above. As an example, to deploy to [Now](https://zeit.co/now), run these commands:
```bash
npm install -g now
now
GITHUB_CLIENT_ID=[your app's client id]
GITHUB_CLIENT_SECRET=[your app's client secret]
BASEURL=http://localhost:3000
```
## Bugs and feedback
Sapper is in early development, and may have the odd rough edge here and there. Please be vocal over on the [Sapper issue tracker](https://github.com/sveltejs/sapper/issues).

@ -0,0 +1,23 @@
---
title: Svelte on The Changelog
description: Listen to the interview here
pubdate: 2019-01-31
author: Rich Harris
authorURL: https://twitter.com/Rich_Harris
---
Earlier this month, I had the privilege of appearing on [The Changelog](https://changelog.com/podcast), a podcast about software development. We had a fun (for me) and wide-ranging conversation:
* life as a coder inside a newsroom
* the big compilers-as-frameworks trend
* scalability
* the [Great Divide](https://css-tricks.com/the-great-divide/)
...and, most importantly, Svelte 3.
Unless you hang out in our [Discord server](https://discord.gg/yy75DKs) or follow [@sveltejs](https://twitter.com/sveltejs) on Twitter, you might not know that Svelte 3 is just around the corner, and it's going to be a huge release. We've rethought the developer experience from the ground up, and while it *will* be a nuisance if you need to upgrade a Svelte 2 app (more on that soon) we think you're going to love it.
On the podcast [Adam](https://twitter.com/adamstac), [Jerod](https://twitter.com/jerodsanto) and I talk about some of the changes and why we're making them. You can listen here or on the [podcast page](https://changelog.com/podcast/332).
<audio data-theme="night" style="width: 100%" data-src="https://changelog.com/podcast/332/embed" src="https://cdn.changelog.com/uploads/podcast/332/the-changelog-332.mp3" preload="none" class="changelog-episode" controls></audio><p><a href="https://changelog.com/podcast/332">The Changelog 332: A UI framework without the framework</a> Listen on <a href="https://changelog.com/">Changelog.com</a></p><script async src="//cdn.changelog.com/embed.js"></script>

@ -1,17 +1,24 @@
<!-- https://eugenkiss.github.io/7guis/tasks#crud -->
<script>
import { beforeUpdate } from 'svelte';
export let people = [];
let filteredPeople;
let selected;
let prefix = '';
let first = '';
let last = '';
let i = 0;
$: filteredPeople = prefix
? people.filter(person => {
const name = `${person.last}, ${person.first}`;
return name.toLowerCase().startsWith(prefix.toLowerCase());
})
: people;
$: selected = filteredPeople[i];
$: reset_inputs(selected);
function create() {
people = people.concat({ first, last });
i = people.length - 1;
@ -29,18 +36,8 @@
i = Math.min(i, people.length - 1);
}
$: filteredPeople = prefix
? people.filter(person => {
const name = `${person.last}, ${person.first}`;
return name.toLowerCase().startsWith(prefix.toLowerCase());
})
: people;
$: selected = filteredPeople[i];
$: if (selected) {
first = selected.first;
last = selected.last;
function reset_inputs(person) {
({ first, last } = person);
}
</script>
@ -81,4 +78,4 @@
<button on:click={create} disabled="{!first || !last}">create</button>
<button on:click={update} disabled="{!first || !last || !selected}">update</button>
<button on:click={remove} disabled="{!selected}">delete</button>
</div>
</div>

@ -1,33 +1,31 @@
<script>
const tomorrow = new Date(Date.now() + 86400000);
const tomorrowAsString = [
let start = [
tomorrow.getFullYear(),
pad(tomorrow.getMonth() + 1, 2),
pad(tomorrow.getDate(), 2)
].join('-');
let start = tomorrowAsString;
let end = tomorrowAsString;
let end = start;
let isReturn = false;
const startDate = () => convertToDate(start);
const endDate = () => convertToDate(end);
$: startDate = convertToDate(start);
$: endDate = convertToDate(end);
function bookFlight() {
const type = isReturn ? 'return' : 'one-way';
let message = `You have booked a ${type} flight, leaving ${startDate().toDateString()}`;
let message = `You have booked a ${type} flight, leaving ${startDate.toDateString()}`;
if (type === 'return') {
message += ` and returning ${endDate().toDateString()}`;
message += ` and returning ${endDate.toDateString()}`;
}
alert(message);
}
function convertToDate(str) {
var split = str.split('-');
const split = str.split('-');
return new Date(+split[0], +split[1] - 1, +split[2]);
}
@ -57,5 +55,5 @@
<button
on:click={bookFlight}
disabled="{isReturn && (startDate() >= endDate())}"
>book</button>
disabled="{isReturn && (startDate >= endDate)}"
>book</button>

@ -1,13 +1,11 @@
<script>
export let promise;
let promise;
// [svelte-upgrade suggestion]
// review these functions and remove unnecessary 'export' keywords
export function findAnswer() {
function findAnswer() {
promise = new Promise(fulfil => {
const delay = 1000 + Math.random() * 3000;
setTimeout(() => fulfil(42), delay);
});
const delay = 1000 + Math.random() * 3000;
setTimeout(() => fulfil(42), delay);
});
}
</script>
@ -21,4 +19,4 @@
{:catch error}
<p>well that's odd</p>
{/await}
{/if}
{/if}

@ -1,5 +1,4 @@
<script>
import { onMount } from 'svelte';
import { scaleLinear } from 'd3-scale';
export let points;
@ -109,4 +108,4 @@
{/each}
</g>
</svg>
</div>
</div>

@ -1,16 +1,16 @@
<script>
export let paused = true;
export let t = 0;
export let d;
let paused = true;
let t = 0;
let d;
let icon, bg;
$: icon = `https://icon.now.sh/${paused ? 'play' : 'pause'}_circle_filled`;
$: {
var p = d ? t / d : 0;
var h = 90 + 90 * p;
var l = 10 + p * 30;
const p = d ? t / d : 0;
const h = 90 + 90 * p;
const l = 10 + p * 30;
bg = `hsl(${h},50%,${l}%)`;
}
@ -20,8 +20,8 @@
const format = time => {
if (isNaN(time)) return '--:--.-';
var minutes = Math.floor(time / 60);
var seconds = (time % 60).toFixed(1);
const minutes = Math.floor(time / 60);
const seconds = (time % 60).toFixed(1);
return minutes + ':' + pad(seconds)
};
@ -29,7 +29,7 @@
function seek(event) {
if (event.buttons === 1) {
event.preventDefault();
var p = event.clientX / window.innerWidth;
const p = event.clientX / window.innerWidth;
t = p * d;
}
}

@ -1,7 +1,7 @@
<script>
import { onMount } from 'svelte';
import List from './List.html';
import Item from './Item.html';
import List from './List.svelte';
import Item from './Item.svelte';
let item;
let page;

@ -1,5 +1,5 @@
<script>
import Foo from './Foo.html';
import Foo from './Foo.svelte';
</script>
<style>

@ -1,8 +1,8 @@
<svelte:meta immutable/>
<svelte:options immutable/>
<script>
import ImmutableTodo from './ImmutableTodo.html';
import MutableTodo from './MutableTodo.html';
import ImmutableTodo from './ImmutableTodo.svelte';
import MutableTodo from './MutableTodo.svelte';
export let todos;

@ -1,4 +1,4 @@
<svelte:meta immutable/>
<svelte:options immutable/>
<script>
import { afterUpdate } from 'svelte';

@ -8,7 +8,6 @@
const xTicks = [1980, 1990, 2000, 2010];
const padding = { top: 20, right: 15, bottom: 20, left: 25 };
let svg;
let width = 500;
let height = 200;
@ -36,10 +35,9 @@
return "'" + tick % 100;
}
let svg;
function resize() {
const bcr = svg.getBoundingClientRect();
width = bcr.width;
height = bcr.height;
({ width, height } = svg.getBoundingClientRect());
}
onMount(resize);
@ -126,4 +124,4 @@
.path-area {
fill: rgba(0,100,100,0.2);
}
</style>
</style>

@ -1,5 +1,5 @@
<script>
import Modal from './Modal.html';
import Modal from './Modal.svelte';
export let showModal;
</script>

@ -1,5 +1,5 @@
<script>
import Nested from './Nested.html';
import Nested from './Nested.svelte';
</script>
<p>This is a top-level element.</p>

@ -1,5 +1,5 @@
<script>
import Scatterplot from './Scatterplot.html';
import Scatterplot from './Scatterplot.svelte';
export let a;
export let b;

@ -1,111 +0,0 @@
<script>
// TODO this example needs updating
import { onMount } from 'svelte';
import { scaleLinear } from 'd3-scale';
export let svg;
const xScale = scaleLinear();
const yScale = scaleLinear();
export let width = 500;
export let height = 200;
export let padding = { top: 20, right: 40, bottom: 40, left: 25 };
export let points;
export let xTicks = [0, 4, 8, 12, 16, 20];
export let yTicks = [0, 2, 4, 6, 8, 10, 12];
function xTicks() {
return width > 180 ?
[0, 4, 8, 12, 16, 20] :
[0, 10, 20];
}
function yTicks() {
return height > 180 ?
[0, 2, 4, 6, 8, 10, 12] :
[0, 4, 8, 12];
}
function xScale() {
return xScale()
.domain([0, 20])
.range([padding.left, width - padding.right]);
}
function yScale() {
return yScale()
.domain([0, 12])
.range([height - padding.bottom, padding.top]);
}
onMount(() => {
resize();
});
function resize() {
const { width, height } = svg.getBoundingClientRect();
width = width, height = height;
}
</script>
<svelte:window on:resize='{resize}'/>
<svg ref:svg>
<!-- y axis -->
<g class='axis y-axis'>
{#each yTicks as tick}
<g class='tick tick-{tick}' transform='translate(0, {yScale()(tick)})'>
<line x1='{padding.left}' x2='{xScale()(22)}'/>
<text x='{padding.left - 8}' y='+4'>{tick}</text>
</g>
{/each}
</g>
<!-- x axis -->
<g class='axis x-axis'>
{#each xTicks as tick}
<g class='tick' transform='translate({xScale()(tick)},0)'>
<line y1='{yScale()(0)}' y2='{yScale()(13)}'/>
<text y='{height - padding.bottom + 16}'>{tick}</text>
</g>
{/each}
</g>
<!-- data -->
{#each points as point}
<circle cx='{xScale()(point.x)}' cy='{yScale()(point.y)}' r='5'/>
{/each}
</svg>
<style>
svg {
width: 50%;
height: 50%;
float: left;
}
circle {
fill: orange;
fill-opacity: 0.6;
stroke: rgba(0,0,0,0.5);
}
.tick line {
stroke: #ddd;
stroke-dasharray: 2;
}
text {
font-size: 12px;
fill: #999;
}
.x-axis text {
text-anchor: middle;
}
.y-axis text {
text-anchor: end;
}
</style>

@ -0,0 +1,96 @@
<script>
import { onMount } from 'svelte';
import { scaleLinear } from 'd3-scale';
export let points;
const padding = { top: 20, right: 40, bottom: 40, left: 25 };
let xScale;
$: xScale = scaleLinear()
.domain([0, 20])
.range([padding.left, width - padding.right]);
let yScale;
$: yScale = scaleLinear()
.domain([0, 12])
.range([height - padding.bottom, padding.top]);
let width = 500;
let height = 200;
let xTicks;
$: xTicks = width > 180 ?
[0, 4, 8, 12, 16, 20] :
[0, 10, 20];
let yTicks;
$: yTicks = height > 180 ?
[0, 2, 4, 6, 8, 10, 12] :
[0, 4, 8, 12];
onMount(resize);
let svg;
function resize() {
({ width, height } = svg.getBoundingClientRect());
}
</script>
<svelte:window on:resize='{resize}'/>
<svg bind:this={svg}>
<!-- y axis -->
<g class='axis y-axis'>
{#each yTicks as tick}
<g class='tick tick-{tick}' transform='translate(0, {yScale(tick)})'>
<line x1='{padding.left}' x2='{xScale(22)}'/>
<text x='{padding.left - 8}' y='+4'>{tick}</text>
</g>
{/each}
</g>
<!-- x axis -->
<g class='axis x-axis'>
{#each xTicks as tick}
<g class='tick' transform='translate({xScale(tick)},0)'>
<line y1='{yScale(0)}' y2='{yScale(13)}'/>
<text y='{height - padding.bottom + 16}'>{tick}</text>
</g>
{/each}
</g>
<!-- data -->
{#each points as point}
<circle cx='{xScale(point.x)}' cy='{yScale(point.y)}' r='5'/>
{/each}
</svg>
<style>
svg {
width: 50%;
height: 50%;
float: left;
}
circle {
fill: orange;
fill-opacity: 0.6;
stroke: rgba(0,0,0,0.5);
}
.tick line {
stroke: #ddd;
stroke-dasharray: 2;
}
text {
font-size: 12px;
fill: #999;
}
.x-axis text {
text-anchor: middle;
}
.y-axis text {
text-anchor: end;
}
</style>

@ -2,7 +2,7 @@
import * as eases from 'svelte/easing';
import { fade } from 'svelte/transition';
export let visible;
let visible;
const wheee = (node, params) => {
return {

@ -1,11 +1,11 @@
<script>
import { fade } from 'svelte/transition';
export let visible;
let visible;
</script>
<input type=checkbox bind:checked={visible}> visible
{#if visible}
<p transition:fade>fades in and out</p>
{/if}
{/if}

@ -1,11 +1,11 @@
<script>
import { fly } from 'svelte/transition';
export let visible;
let visible;
</script>
<input type=checkbox bind:checked={visible}> visible
{#if visible}
<p transition:fly="{{y: 200, duration: 1000}}">flies 200 pixels up, slowly</p>
{/if}
{/if}

@ -1,11 +1,11 @@
<script>
import { fade, fly } from 'svelte/transition';
export let visible;
let visible;
</script>
<input type=checkbox bind:checked={visible}> visible
{#if visible}
<p in:fly="{{y: 50}}" out:fade>flies up, fades out</p>
{/if}
{/if}

@ -114,11 +114,9 @@ You can also bind to certain values — so far `innerWidth`, `outerWidth`, `inne
```
### `<svelte:document>`
### `<svelte:body>`
TODO REPLACE THIS WITH svelte:body
The `<svelte:document>` tag, just like `<svelte:window>`, gives you a convenient way to declaratively add event listeners to the `document` object. This is useful for listening to events that don't fire on `window`, such as `mouseenter` and `mouseleave`.
The `<svelte:body>` tag, just like `<svelte:window>`, gives you a convenient way to declaratively add event listeners to the `document.body` object. This is useful for listening to events that don't fire on `window`, such as `mouseenter` and `mouseleave`.
### `<svelte:head>`

@ -36,7 +36,7 @@ Element bindings make it easy to respond to user interactions:
```html
<!-- { title: 'Element bindings' } -->
<h1>Hello {name}!</h1>
<input bind:value=name>
<input bind:value={name}>
```
```json
@ -158,4 +158,4 @@ export default function createRenderer(canvas, ctx) {
ctx.putImageData(imageData, 0, 0);
}
}
```
```

@ -18,7 +18,7 @@ It's easier to show the effect of this than to describe it. Open the following e
```html
<!-- { title: 'Keyed each blocks' } -->
<button on:click="update()">update</button>
<button on:click="{update}">update</button>
<section>
<h2>Keyed</h2>
@ -46,29 +46,21 @@ It's easier to show the effect of this than to describe it. Open the following e
</style>
<script>
import { slide } from 'svelte-transitions';
var people = ['Alice', 'Barry', 'Cecilia', 'Douglas', 'Eleanor', 'Felix', 'Grace', 'Horatio', 'Isabelle'];
import { slide } from 'svelte/transition';
const names = ['Alice', 'Barry', 'Cecilia', 'Douglas', 'Eleanor', 'Felix', 'Grace', 'Horatio', 'Isabelle'];
function random() {
return people
return names
.filter(() => Math.random() < 0.5)
.map(name => ({ name }))
.map(name => ({ name }));
}
let people = random();
function update() {
people = random();
}
export default {
data() {
return { people: random() };
},
methods: {
update() {
this.set({ people: random() });
}
},
transitions: { slide }
};
</script>
```

1024
site/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,57 +1,58 @@
{
"name": "TODO",
"description": "TODO",
"version": "0.0.1",
"name": "svelte.technology",
"version": "1.0.0",
"description": "Docs and examples for Svelte",
"scripts": {
"dev": "sapper dev",
"sapper": "sapper build --legacy",
"update_template": "sh ./scripts/update_template.sh",
"update": "node scripts/update_template.js && node scripts/get-contributors.js",
"start": "node __sapper__/build",
"cy:run": "cypress run",
"cy:open": "cypress open",
"test": "run-p --race dev cy:run",
"deploy": "npm run stage && now alias",
"prestage": "npm run update_template && node scripts/get-contributors.js && npm run sapper",
"prestage": "npm run update && npm run sapper",
"stage": "now"
},
"dependencies": {
"codemirror": "^5.42.0",
"compression": "^1.7.1",
"codemirror": "^5.43.0",
"compression": "^1.7.3",
"devalue": "^1.1.0",
"do-not-zip": "^1.0.0",
"dotenv": "^6.2.0",
"express": "^4.16.4",
"express-session": "^1.15.6",
"golden-fleece": "^1.0.9",
"marked": "^0.5.2",
"marked": "^0.6.0",
"node-fetch": "^2.3.0",
"passport": "^0.4.0",
"passport-github": "^1.1.0",
"prismjs": "^1.15.0",
"session-file-store": "^1.2.0",
"sirv": "^0.2.0",
"shelljs": "^0.8.3",
"sirv": "^0.2.2",
"yootils": "0.0.14"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/runtime": "^7.0.0",
"chokidar": "^2.0.4",
"@babel/core": "^7.2.2",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-transform-runtime": "^7.2.0",
"@babel/preset-env": "^7.3.1",
"@babel/runtime": "^7.3.1",
"chokidar": "^2.1.0",
"degit": "^2.1.3",
"eslint-plugin-svelte3": "git+https://github.com/sveltejs/eslint-plugin-svelte3.git",
"now": "^12.1.14",
"eslint-plugin-svelte3": "git+https://github.com/sveltejs/eslint-plugin-svelte3.git#semver:*",
"now": "^13.1.3",
"npm-run-all": "^4.1.5",
"rollup": "^0.68.0",
"rollup-plugin-babel": "^4.0.2",
"rollup-plugin-commonjs": "^9.1.6",
"rollup": "^1.1.2",
"rollup-plugin-babel": "^4.3.2",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-json": "^3.1.0",
"rollup-plugin-node-resolve": "^3.3.0",
"rollup-plugin-replace": "^2.0.0",
"rollup-plugin-svelte": "^4.2.1",
"rollup-plugin-terser": "^1.0.1",
"sapper": "^0.25.0-alpha2",
"svelte": "^3.0.0-alpha10"
"rollup-plugin-node-resolve": "^4.0.0",
"rollup-plugin-replace": "^2.1.0",
"rollup-plugin-svelte": "^5.0.1",
"rollup-plugin-terser": "^4.0.4",
"sapper": "^0.26.0-alpha.8",
"svelte": "^3.0.0-beta.3"
}
}

@ -0,0 +1,26 @@
const sh = require('shelljs');
const fs = require('fs')
sh.cd(__dirname+'/../')
// fetch svelte app
sh.rm('-rf','scripts/svelte-app')
sh.exec('npx degit sveltejs/template scripts/svelte-app')
// update repl-viewer.css based on template
sh.cp('scripts/svelte-app/public/global.css', 'static/repl-viewer.css')
// remove src (will be recreated client-side) and node_modules
sh.rm('-rf', 'scripts/svelte-app/src')
sh.rm('-rf', 'scripts/svelte-app/node_modules')
// build svelte-app.json
const appPath = 'scripts/svelte-app'
let files = []
for (const path of sh.find(appPath).filter(p => fs.lstatSync(p).isFile()) ) {
files.push({ path: path.slice(appPath.length + 1), data: fs.readFileSync(path).toString() });
}
fs.writeFileSync('static/svelte-app.json', JSON.stringify(files));

@ -1,15 +0,0 @@
cd `dirname $0`/..
# fetch svelte-app
rm -rf scripts/svelte-app
node_modules/.bin/degit sveltejs/template scripts/svelte-app
# update repl-viewer.css based on template
cp scripts/svelte-app/public/global.css static/repl-viewer.css
# remove src (will be recreated client-side) and node_modules
rm -rf scripts/svelte-app/src
rm -rf scripts/svelte-app/node_modules
# build svelte-app.json
node scripts/build-svelte-app-json.js `find scripts/svelte-app -type f`

@ -1,4 +1,4 @@
import * as sapper from '../__sapper__/client.js';
import * as sapper from '@sapper/app';
sapper.start({
target: document.querySelector('#sapper')

Before

Width:  |  Height:  |  Size: 828 B

After

Width:  |  Height:  |  Size: 828 B

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

@ -1,7 +1,7 @@
<script>
import { onMount } from 'svelte';
import Icon from './Icon.html';
import Logo from './Logo.html';
import Icon from './Icon.svelte';
import Logo from './Logo.svelte';
export let segment;

@ -1,6 +1,6 @@
<script>
import InlineSvg from '../components/InlineSvg.html';
import Nav from '../components/TopNav.html';
import InlineSvg from '../components/InlineSvg.svelte';
import Nav from '../components/TopNav.svelte';
export let child;
export let path;

@ -3,58 +3,53 @@ import path from 'path';
import process_markdown from '../../../utils/_process_markdown.js';
import marked from 'marked';
// import hljs from 'highlight.js';
import prismjs from 'prismjs'; // prism-highlighter smaller footprint [hljs: 192.5k]
require('prismjs/components/prism-bash');
import PrismJS from 'prismjs';
import 'prismjs/components/prism-bash';
// map lang to prism-language-attr
const prismLang = {
bash: 'bash',
html: 'markup',
js: 'javascript',
css: 'css',
bash: 'bash',
html: 'markup',
js: 'javascript',
css: 'css',
};
export default function() {
return fs
.readdirSync('content/blog')
.map(file => {
if (path.extname(file) !== '.md') return;
const markdown = fs.readFileSync(`content/blog/${file}`, 'utf-8');
const { content, metadata } = process_markdown(markdown);
const date = new Date(`${metadata.pubdate} EDT`); // cheeky hack
metadata.dateString = date.toDateString();
const renderer = new marked.Renderer();
renderer.code = (source, lang) => {
let plang = prismLang[lang];
const highlighted = Prism.highlight(
source,
Prism.languages[plang],
lang,
);
return `<pre class='language-${plang}'><code>${highlighted}</code></pre>`;
};
const html = marked(
content.replace(/^\t+/gm, match => match.split('\t').join(' ')),
{
renderer,
},
);
return {
html,
metadata,
slug: file.replace(/^[\d-]+/, '').replace(/\.md$/, ''),
};
})
.sort((a, b) => {
return a.metadata.pubdate < b.metadata.pubdate ? 1 : -1;
});
return fs
.readdirSync('content/blog')
.map(file => {
if (path.extname(file) !== '.md') return;
const markdown = fs.readFileSync(`content/blog/${file}`, 'utf-8');
const { content, metadata } = process_markdown(markdown);
const date = new Date(`${metadata.pubdate} EDT`); // cheeky hack
metadata.dateString = date.toDateString();
const renderer = new marked.Renderer();
renderer.code = (source, lang) => {
const plang = prismLang[lang];
const highlighted = PrismJS.highlight(
source,
PrismJS.languages[plang],
lang,
);
return `<pre class='language-${plang}'><code>${highlighted}</code></pre>`;
};
const html = marked(
content.replace(/^\t+/gm, match => match.split('\t').join(' ')),
{ renderer }
);
return {
html,
metadata,
slug: file.replace(/^[\d-]+/, '').replace(/\.md$/, ''),
};
})
.sort((a, b) => a.metadata.pubdate < b.metadata.pubdate ? 1 : -1);
}

@ -19,7 +19,7 @@ function createExample(slug) {
const components = files
.map(file => {
const ext = path.extname(file);
if (ext !== '.html' && ext !== '.js') return null;
if (ext !== '.svelte' && ext !== '.js') return null;
const source = fs.readFileSync(`content/examples/${slug}/${file}`, 'utf-8');
@ -31,10 +31,10 @@ function createExample(slug) {
})
.filter(Boolean)
.sort((a, b) => {
if (a.name === 'App' && a.type === 'html') return -1;
if (b.name === 'App' && b.type === 'html') return 1;
if (a.name === 'App' && a.type === 'svelte') return -1;
if (b.name === 'App' && b.type === 'svelte') return 1;
if (a.type !== b.type) return a.type === 'html' ? -1 : 1;
if (a.type !== b.type) return a.type === 'svelte' ? -1 : 1;
return a.name < b.name ? -1 : 1;
});

@ -1,6 +1,6 @@
export function get(req, res) {
if (!req.session.passport || !req.session.passport.user) {
res.send(null);
if (!req.session || !req.session.passport || !req.session.passport.user) {
res.send('null');
return;
}

@ -11,6 +11,7 @@
<svelte:head>
<title>Svelte • The magical disappearing UI framework</title>
<link rel="alternate" type="application/rss+xml" title="Svelte blog" href="blog/rss.xml">
</svelte:head>
<div class='posts stretch'>

@ -1,10 +1,10 @@
import get_posts from '../api/blog/_posts.js';
const months = ',Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split( ',' );
const months = ',Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(',');
function formatPubdate ( str ) {
const [ y, m, d ] = str.split( '-' );
return `${d} ${months[+m]} ${y}`;
function formatPubdate(str) {
const [y, m, d] = str.split('-');
return `${d} ${months[+m]} ${y} 12:00 +0000`;
}
const rss = `
@ -20,18 +20,18 @@ const rss = `
<title>Svelte</title>
<link>https://svelte.technology/blog</link>
</image>
${get_posts().map( post => `
${get_posts().map(post => `
<item>
<title>${post.metadata.title}</title>
<link>https://svelte.technology/blog/${post.slug}</link>
<description>${post.metadata.description}</description>
<pubDate>${formatPubdate(post.metadata.pubdate)}</pubDate>
</item>
` )}
`).join('')}
</channel>
</rss>
`.replace( />[^\S]+/gm, '>' ).replace( /[^\S]+</gm, '<' ).trim();
`.replace(/>[^\S]+/gm, '>').replace(/[^\S]+</gm, '<').trim();
export function get(req, res) {
res.set({
@ -39,4 +39,4 @@ export function get(req, res) {
'Content-Type': 'application/rss+xml'
});
res.end(rss);
}
}

@ -5,7 +5,7 @@ export async function get(req, res) {
const { id } = req.params;
const headers = {};
const user = req.session.passport && req.session.passport.user;
const user = req.session && req.session.passport && req.session.passport.user;
if (user) {
headers.Authorization = `token ${user.token}`;
}
@ -34,7 +34,7 @@ export async function get(req, res) {
}
export async function patch(req, res) {
const user = req.session.passport && req.session.passport.user;
const user = req.session && req.session.passport && req.session.passport.user;
if (!user) {
res.writeHead(403, {

@ -1,6 +1,6 @@
<script>
import { onMount, afterUpdate } from 'svelte';
import Icon from '../../components/Icon.html';
import Icon from '../../components/Icon.svelte';
export let sections = [];
export let active_section = null;

@ -4,13 +4,8 @@ import * as fleece from 'golden-fleece';
import process_markdown from '../../utils/_process_markdown.js';
import marked from 'marked';
import Prism from 'prismjs'; // prism-highlighter smaller footprint [hljs: 192.5k]
require('prismjs/components/prism-bash');
const langs = {
'hidden-data': 'json',
'html-no-repl': 'html',
};
import PrismJS from 'prismjs';
import 'prismjs/components/prism-bash';
// map lang to prism-language-attr
const prismLang = {
@ -20,10 +15,6 @@ const prismLang = {
css: 'css',
};
function btoa(str) {
return new Buffer(str).toString('base64');
}
const escaped = {
'"': '&quot;',
"'": '&#39;',
@ -112,13 +103,6 @@ export default function() {
let className = 'code-block';
if (lang === 'html' && !group) {
/* prettier-ignore
-----------------------------------------------
edit vedam
don't know how to access components here
so i hardcoded icon here
-----------------------------------------------
*/
if (!meta || meta.repl !== false) {
prefix = `<a class='open-in-repl' href='repl?demo=@@${uid}' title='open in REPL'><svg class='icon'><use xlink:href='#maximize-2' /></svg></a>`;
}
@ -129,7 +113,7 @@ export default function() {
if (meta) {
source = lines.slice(1).join('\n');
const filename = meta.filename || (lang === 'html' && 'App.html');
const filename = meta.filename || (lang === 'html' && 'App.svelte');
if (filename) {
prefix = `<span class='filename'>${prefix} ${filename}</span>`;
className += ' named';
@ -140,16 +124,10 @@ export default function() {
if (meta && meta.hidden) return '';
/* prettier-ignore
-----------------------------------------------
design-edit vedam
insert prism-highlighter
-----------------------------------------------
*/
let plang = prismLang[lang];
const highlighted = Prism.highlight(
const plang = prismLang[lang];
const highlighted = PrismJS.highlight(
source,
Prism.languages[plang],
PrismJS.languages[plang],
lang
);

@ -7,8 +7,8 @@
<script>
import { onMount, afterUpdate } from 'svelte';
import GuideContents from './_GuideContents.html';
import Icon from '../../components/Icon.html';
import GuideContents from './_GuideContents.svelte';
import Icon from '../../components/Icon.svelte';
export let sections;
let active_section;

@ -1,6 +1,6 @@
<script>
import Icon from '../components/Icon.html';
import Logo from '../components/Logo.html';
import Icon from '../components/Icon.svelte';
import Logo from '../components/Logo.svelte';
import contributors from './_contributors.js';
let sy = 0;

@ -1,8 +1,8 @@
<script>
import * as fleece from 'golden-fleece';
import { createEventDispatcher } from 'svelte';
import UserMenu from './UserMenu.html';
import Icon from '../../../../components/Icon.html';
import UserMenu from './UserMenu.svelte';
import Icon from '../../../../components/Icon.svelte';
import * as doNotZip from 'do-not-zip';
import downloadBlob from '../../_utils/downloadBlob.js';
import { user } from '../../../../user.js';
@ -115,7 +115,7 @@
// null out any deleted files
const set = new Set(components.map(m => `${m.name}.${m.type}`));
Object.keys(gist.files).forEach(file => {
if (/\.(html|js)$/.test(file)) {
if (/\.(svelte|html|js)$/.test(file)) {
if (!set.has(file)) files[file] = null;
}
});
@ -126,7 +126,7 @@
throw new Error(`GitHub does not allow saving gists with empty files - ${file}`);
}
if (!gist.files[files] || module.source !== gist.files[file].content) {
if (!gist.files[file] || module.source !== gist.files[file].content) {
files[file] = { content: module.source };
}
});
@ -189,7 +189,7 @@
files.push(...components.map(component => ({ path: `src/${component.name}.${component.type}`, data: component.source })));
files.push({
path: `src/main.js`, data: `import App from './App.html';
path: `src/main.js`, data: `import App from './App.svelte';
var app = new App({
target: document.body,

@ -1,6 +1,6 @@
<script>
import { createEventDispatcher } from 'svelte';
import Icon from '../../../../components/Icon.html';
import Icon from '../../../../components/Icon.svelte';
import { enter } from '../events.js';
const dispatch = createEventDispatcher();
@ -25,7 +25,7 @@
}
function closeEdit() {
const match = /(.+)\.(html|js)$/.exec($selected_store.name);
const match = /(.+)\.(svelte|js)$/.exec($selected_store.name);
$selected_store.name = match ? match[1] : $selected_store.name;
if (match && match[2]) $selected_store.type = match[2];
editing = null;
@ -49,7 +49,7 @@
function addNew() {
const component = {
name: uid++ ? `Component${uid}` : 'Component1',
type: 'html',
type: 'svelte',
source: ''
};
@ -181,7 +181,7 @@
>
{#if component.name == 'App'}
<div class="uneditable">
App.html
App.svelte
</div>
{:else}
{#if component === editing}

@ -1,5 +1,5 @@
<script>
import CodeMirror from '../CodeMirror.html';
import CodeMirror from '../CodeMirror.svelte';
export let component;
export let error;

@ -1,6 +1,6 @@
<script>
import ComponentSelector from './ComponentSelector.html';
import ModuleEditor from './ModuleEditor.html';
import ComponentSelector from './ComponentSelector.svelte';
import ModuleEditor from './ModuleEditor.svelte';
export let component_store;
export let selected_store;

@ -1,7 +1,7 @@
<script>
import { createEventDispatcher } from 'svelte';
import * as fleece from 'golden-fleece';
import CodeMirror from '../CodeMirror.html';
import CodeMirror from '../CodeMirror.svelte';
const dispatch = createEventDispatcher();

@ -1,6 +1,7 @@
<script>
import { onMount, createEventDispatcher } from 'svelte';
import { onMount, onDestroy, createEventDispatcher } from 'svelte';
import getLocationFromStack from '../../_utils/getLocationFromStack.js';
import ReplProxy from '../../_utils/replProxy.js';
import { decode } from 'sourcemap-codec';
const dispatch = createEventDispatcher();
@ -14,29 +15,18 @@
export let error;
export function setProp(prop, value) {
if (component) {
component[prop] = value;
}
}
if (!replProxy) return;
replProxy.setProp(prop, value);
}
let component;
let hasComponent = false;
const refs = {};
const importCache = {};
let pendingImports = 0;
let pending = false;
function fetchImport(id, curl) {
return new Promise((fulfil, reject) => {
curl([`https://bundle.run/${id}`]).then(module => {
importCache[id] = module;
fulfil(module);
}, err => {
console.error(err.stack);
reject(new Error(`Error loading ${id} from bundle.run`));
});
});
}
let replProxy = null;
const namespaceSpecifier = /\*\s+as\s+(\w+)/;
const namedSpecifiers = /\{(.+)\}/;
@ -77,39 +67,29 @@
let createComponent;
let init;
onDestroy(() => {
if (replProxy) {
replProxy.destroy();
}
});
onMount(() => {
replProxy = new ReplProxy(refs.child);
refs.child.addEventListener('load', () => {
const iframe = refs.child;
const body = iframe.contentDocument.body;
const evalInIframe = iframe.contentWindow.eval;
// intercept links, so that we can use #hashes inside the iframe
body.addEventListener('click', event => {
if (event.which !== 1) return;
if (event.metaKey || event.ctrlKey || event.shiftKey) return;
if (event.defaultPrevented) return;
// ensure target is a link
let el = event.target;
while (el && el.nodeName !== 'A') el = el.parentNode;
if (!el || el.nodeName !== 'A') return;
if (el.hasAttribute('download') || el.getAttribute('rel') === 'external' || el.target) return;
event.preventDefault();
if (el.href.startsWith(top.location.origin)) {
const url = new URL(el.href);
if (url.hash[0] === '#') {
iframe.contentWindow.location.hash = url.hash;
return;
}
}
window.open(el.href, '_blank');
});
replProxy.onPropUpdate = (prop, value) => {
dispatch('binding', { prop, value });
values_store.update(values => Object.assign({}, values, {
[prop]: value
}));
};
replProxy.onFetchProgress = (progress) => {
pendingImports = progress
}
replProxy.handleLinks();
let promise = null;
let updating = false;
@ -118,14 +98,18 @@
const init = () => {
if (sourceError) return;
const imports = [];
const missingImports = bundle.imports.filter(x => !importCache[x]);
const removeStyles = () => {
const styles = iframe.contentDocument.querySelectorAll('style.svelte');
let i = styles.length;
while (i--) styles[i].parentNode.removeChild(styles[i]);
replProxy.eval(`
const styles = document.querySelectorAll('style.svelte');
let i = styles.length;
while (i--) styles[i].parentNode.removeChild(styles[i]);
`)
};
const destroyComponent = () => {
replProxy.eval(`if (window.component)
window.component.\$destroy();
window.component = null`);
};
const ready = () => {
@ -133,18 +117,10 @@
if (toDestroy) {
removeStyles();
toDestroy.$destroy();
destroyComponent();
toDestroy = null;
}
bundle.imports.forEach(x => {
const module = importCache[x];
const name = bundle.importMap.get(x);
iframe.contentWindow[name] = module;
});
if (ssr) { // this only gets generated if component uses lifecycle hooks
pending = true;
createHtml();
@ -152,23 +128,22 @@
pending = false;
createComponent();
}
};
}
const createHtml = () => {
try {
evalInIframe(`${ssr.code}
var rendered = SvelteComponent.render(${JSON.stringify($values_store)});
if (rendered.css.code) {
var style = document.createElement('style');
style.className = 'svelte';
style.textContent = rendered.css.code;
document.head.appendChild(style);
}
document.body.innerHTML = rendered.html;
`)
} catch (e) {
replProxy.eval(`${ssr.code}
var rendered = SvelteComponent.render(${JSON.stringify($values_store)});
if (rendered.css.code) {
var style = document.createElement('style');
style.className = 'svelte';
style.textContent = rendered.css.code;
document.head.appendChild(style);
}
document.body.innerHTML = rendered.html;
`)
.catch( e => {
const loc = getLocationFromStack(e.stack, ssr.map);
if (loc) {
e.filename = loc.source;
@ -176,35 +151,29 @@
}
error = e;
}
});
};
const createComponent = () => {
// remove leftover styles from SSR renderer
if (ssr) removeStyles();
try {
evalInIframe(`${dom.code}
document.body.innerHTML = '';
window.location.hash = '';
window._svelteTransitionManager = null;
var component = new SvelteComponent({
target: document.body,
props: ${JSON.stringify($values_store)}
});`);
component = window.app = window.component = iframe.contentWindow.component;
// component.on('state', ({ current }) => {
// if (updating) return;
// updating = true;
// this.fire('data', { current });
// updating = false;
// });
} catch (e) {
replProxy.eval(`${dom.code}
document.body.innerHTML = '';
window.location.hash = '';
window._svelteTransitionManager = null;
window.component = new SvelteComponent({
target: document.body,
props: ${JSON.stringify($values_store)}
});
`)
.then(()=> {
replProxy.bindProps(props);
})
.catch(e => {
// TODO show in UI
component = null;
hasComponent = false;
const loc = getLocationFromStack(e.stack, dom.map);
if (loc) {
@ -213,40 +182,32 @@
}
error = e;
}
});
};
pendingImports = missingImports.length;
if (missingImports.length) {
// Download the imports (sets them on iframe window when complete)
{
let cancelled = false;
promise = Promise.all(
missingImports.map(id => fetchImport(id, iframe.contentWindow.curl).then(module => {
pendingImports -= 1;
return module;
}))
);
promise.cancel = () => cancelled = true;
promise
.then(() => {
promise = replProxy.fetchImports(bundle);
promise.cancel = () => { cancelled = true };
promise.then(() => {
if (cancelled) return;
ready();
})
.catch(e => {
ready()
}).catch(e => {
if (cancelled) return;
error = e;
});
} else {
ready();
}
run = () => {
pending = false;
// TODO do we need to clear out SSR HTML?
createComponent();
props_handler = props => {
replProxy.bindProps(props)
};
replProxy.bindProps(props);
};
}
@ -254,31 +215,13 @@
if (!bundle) return; // TODO can this ever happen?
if (promise) promise.cancel();
toDestroy = component;
component = null;
toDestroy = hasComponent;
hasComponent = false;
init();
};
props_handler = props => {
if (!component) {
// TODO can this happen?
console.error(`no component to bind to`);
return;
}
props.forEach(prop => {
// TODO should there be a public API for binding?
// e.g. `component.$watch(prop, handler)`?
// (answer: probably)
component.$$.bound[prop] = value => {
dispatch('binding', { prop, value });
values_store.update(values => Object.assign({}, values, {
[prop]: value
}));
};
});
};
});
});
@ -346,7 +289,7 @@
</style>
<div class="iframe-container">
<iframe title="Result" bind:this={refs.child} class="{error || pending || pendingImports ? 'greyed-out' : ''}" srcdoc='
<iframe title="Result" bind:this={refs.child} sandbox="allow-scripts allow-popups allow-forms allow-pointer-lock allow-top-navigation allow-modals" class="{error || pending || pendingImports ? 'greyed-out' : ''}" srcdoc='
<!doctype html>
<html>
<head>
@ -355,6 +298,7 @@
<body>
<script src="/curl.js"></script>
<script>curl.config(&#123; dontAddFileExt: /./ });</script>
<script src="/repl-runner.js"></script>
</body>
</html>
'></iframe>

@ -1,9 +1,9 @@
<script>
import SplitPane from '../SplitPane.html';
import Viewer from './Viewer.html';
import CompilerOptions from './CompilerOptions.html';
import PropEditor from './PropEditor.html';
import CodeMirror from '../CodeMirror.html';
import SplitPane from '../SplitPane.svelte';
import Viewer from './Viewer.svelte';
import CompilerOptions from './CompilerOptions.svelte';
import PropEditor from './PropEditor.svelte';
import CodeMirror from '../CodeMirror.svelte';
export let bundle;
export let js;

@ -2,13 +2,13 @@
import { onMount } from 'svelte';
import { writable } from 'svelte/store';
import * as fleece from 'golden-fleece';
import SplitPane from './SplitPane.html';
import CodeMirror from './CodeMirror.html';
import Input from './Input/index.html';
import Output from './Output/index.html';
import InputOutputToggle from './InputOutputToggle.html';
import SplitPane from './SplitPane.svelte';
import CodeMirror from './CodeMirror.svelte';
import Input from './Input/index.svelte';
import Output from './Output/index.svelte';
import InputOutputToggle from './InputOutputToggle.svelte';
export let version = 'alpha'; // TODO change this to latest when the time comes
export let version = 'beta'; // TODO change this to latest when the time comes
export let app;
export let embedded = false;
@ -53,7 +53,7 @@
let js = '';
let css = '';
let uid = 0;
let props = null;
let props = [];
let sourceErrorLoc;
let runtimeErrorLoc;
@ -100,7 +100,7 @@
const selected = $selected_store;
if (selected.name === 'App') {
// App.html can't be removed
// App.svelte can't be removed
selected.source = '';
// $selected_store.set(selected);
} else {
@ -118,13 +118,13 @@
}
function compile(component, options) {
if (component.type === 'html') {
if (component.type === 'svelte') {
workers.compiler.postMessage({
type: 'compile',
source: component.source,
options: Object.assign({
name: component.name,
filename: `${component.name}.html`
filename: `${component.name}.svelte`
}, options),
entry: component === $component_store[0]
});
@ -154,7 +154,7 @@
}
function navigate(filename) {
const name = filename.replace(/\.html$/, '');
const name = filename.replace(/\.svelte$/, '');
console.error(`TODO navigate`);

@ -0,0 +1,90 @@
export default class ReplProxy {
constructor(iframe) {
this.iframe = iframe;
this.cmdId = 1;
this.pendingCmds = new Map();
this.onPropUpdate = null;
this.onFetchProgress = null;
this.handle_event = (ev) => this.handleReplMessage(ev);
window.addEventListener("message", this.handle_event, false);
}
destroy() {
window.removeEventListener("message", this.handle_event);
}
iframeCommand(command, args) {
return new Promise( (resolve, reject) => {
this.cmdId = this.cmdId + 1;
this.pendingCmds.set(this.cmdId, { resolve: resolve, reject: reject });
this.iframe.contentWindow.postMessage({
action: command,
cmdId: this.cmdId,
args: args
}, '*')
});
}
handleCommandMessage(cmdData) {
let action = cmdData.action;
let id = cmdData.cmdId;
let handler = this.pendingCmds.get(id);
if (handler) {
this.pendingCmds.delete(id);
if (action == "cmdError") {
let { message, stack } = cmdData;
let e = new Error(message);
e.stack = stack;
console.log("repl cmd fail");
handler.reject(e)
}
if (action == "cmdOk") {
handler.resolve(cmdData.args)
}
} else {
console.error("command not found", id, cmdData, [...this.pendingCmds.keys()]);
}
}
handleReplMessage(ev) {
let action = ev.data.action;
if ( action == "cmdError" || action == "cmdOk" ) {
this.handleCommandMessage(ev.data);
}
if (action == "prop_update") {
let { prop, value } = ev.data.args;
if (this.onPropUpdate)
this.onPropUpdate(prop, value)
}
if (action == "fetch_progress") {
if (this.onFetchProgress)
this.onFetchProgress(ev.data.args.remaining)
}
}
eval(script) {
return this.iframeCommand("eval", { script: script });
}
setProp(prop, value) {
return this.iframeCommand("set_prop", {prop, value})
}
bindProps(props) {
return this.iframeCommand("bind_props", { props: props })
}
handleLinks() {
return this.iframeCommand("catch_clicks", {});
}
fetchImports(bundle) {
return this.iframeCommand("fetch_imports", { bundle })
}
}

@ -1,10 +1,19 @@
<script context="module">
export function preload({ query }) {
return {
version: query.version || 'beta',
gist: query.gist,
demo: query.demo
};
}
</script>
<script>
import { onMount } from 'svelte';
import * as fleece from 'golden-fleece';
import Repl from './_components/Repl.html';
import Repl from './_components/Repl.svelte';
export let query;
let version = query.version || 'alpha';
export let version, gist, demo;
let app = {
components: [],
@ -14,14 +23,16 @@
let name = 'loading...';
onMount(() => {
fetch(`https://unpkg.com/svelte@${version}/package.json`)
.then(r => r.json())
.then(pkg => {
version = pkg.version;
});
if (version !== 'local') {
fetch(`https://unpkg.com/svelte@${version}/package.json`)
.then(r => r.json())
.then(pkg => {
version = pkg.version;
});
}
if (query.gist) {
fetch(`gist/${query.gist}`).then(r => r.json()).then(data => {
if (gist) {
fetch(`gist/${gist}`).then(r => r.json()).then(data => {
const { id, description, files } = data;
name = description;
@ -46,20 +57,20 @@
source
};
})
.filter(x => x.type === 'html' || x.type === 'js')
.filter(x => x.type === 'svelte' || x.type === 'js')
.sort((a, b) => {
if (a.name === 'App' && a.type === 'html') return -1;
if (b.name === 'App' && b.type === 'html') return 1;
if (a.name === 'App' && a.type === 'svelte') return -1;
if (b.name === 'App' && b.type === 'svelte') return 1;
if (a.type !== b.type) return a.type === 'html' ? -1 : 1;
if (a.type !== b.type) return a.type === 'svelte' ? -1 : 1;
return a.name < b.name ? -1 : 1;
});
app = { components, values };
});
} else if (query.demo) {
const url = `api/examples/${query.demo}`;
} else if (demo) {
const url = `api/examples/${demo}`;
fetch(url).then(async response => {
if (response.ok) {

@ -1,16 +1,22 @@
<script context="module">
export function preload({ query }) {
return {
version: query.version || 'beta',
gist_id: query.gist,
demo: query.demo || 'hello-world'
};
}
</script>
<script>
import { onMount } from 'svelte';
import { locate } from 'locate-character';
import * as fleece from 'golden-fleece';
import AppControls from './_components/AppControls/index.html';
import Repl from './_components/Repl.html';
import AppControls from './_components/AppControls/index.svelte';
import Repl from './_components/Repl.svelte';
import examples from '../../../content/examples/manifest.json';
export let query;
let version = query.version;
let demo = query.demo || 'hello-world';
let gist_id = query.gist;
export let version, demo, gist_id;
let gist;
@ -45,11 +51,13 @@
});
onMount(() => {
fetch(`https://unpkg.com/svelte@${version || 'alpha'}/package.json`)
.then(r => r.json())
.then(pkg => {
version = pkg.version;
});
if (version !== 'local') {
fetch(`https://unpkg.com/svelte@${version || 'beta'}/package.json`)
.then(r => r.json())
.then(pkg => {
version = pkg.version;
});
}
if (gist_id) {
fetch(`gist/${gist_id}`).then(r => r.json()).then(data => {
@ -72,18 +80,21 @@
values = tryParseData(source) || {};
}
let type = file.slice(dot + 1);
if (type === 'html') type = 'svelte';
return {
name: file.slice(0, dot),
type: file.slice(dot + 1),
type,
source
};
})
.filter(x => x.type === 'html' || x.type === 'js')
.filter(x => x.type === 'svelte' || x.type === 'js')
.sort((a, b) => {
if (a.name === 'App' && a.type === 'html') return -1;
if (b.name === 'App' && b.type === 'html') return 1;
if (a.name === 'App' && a.type === 'svelte') return -1;
if (b.name === 'App' && b.type === 'svelte') return 1;
if (a.type !== b.type) return a.type === 'html' ? -1 : 1;
if (a.type !== b.type) return a.type === 'svelte' ? -1 : 1;
return a.name < b.name ? -1 : 1;
});

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save