Merge branch 'fast-hydration' of github.com:avi/svelte into fast-hydration

pull/4309/head
Avi Marcus 6 years ago
commit a0127cad83

@ -2,9 +2,18 @@
## Unreleased
* Fix updating a `<slot>` inside an `{#if}` or other block ([#4292](https://github.com/sveltejs/svelte/issues/4292))
* Fix using RxJS observables in `derived` stores ([#4298](https://github.com/sveltejs/svelte/issues/4298))
* Add dev mode check to disallow duplicate keys in a keyed `{#each}` ([#4301](https://github.com/sveltejs/svelte/issues/4301))
## 3.17.2
* Fix removing attributes during hydration ([#1733](https://github.com/sveltejs/svelte/issues/1733))
* Disallow two-way binding to a variable declared by an `{#await}` block ([#4012](https://github.com/sveltejs/svelte/issues/4012))
* Allow access to `let:` variables in sibling attributes on slot root ([#4173](https://github.com/sveltejs/svelte/issues/4173))
* Fix `~=` and class selector matching against values separated by any whitespace characters ([#4242](https://github.com/sveltejs/svelte/issues/4242))
* Fix code generation for `await`ed expressions that need parentheses ([#4267](https://github.com/sveltejs/svelte/issues/4267))
* Preserve JavaScript comments from the original component source where possible ([#4268](https://github.com/sveltejs/svelte/issues/4268))
* Add some more known globals ([#4276](https://github.com/sveltejs/svelte/pull/4276))
* Correctly apply event modifiers to `<svelte:body>` events ([#4278](https://github.com/sveltejs/svelte/issues/4278))

8
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "svelte",
"version": "3.17.1",
"version": "3.17.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -597,9 +597,9 @@
"dev": true
},
"code-red": {
"version": "0.0.30",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.30.tgz",
"integrity": "sha512-nsScy3A59tbV5uzndcedIEGHmdWNEByJrC7DUyb0Wh7qZcoPfAlcYsr0ZINkAEhZofSI26F1mi+lRO0/6EV2/g==",
"version": "0.0.31",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-0.0.31.tgz",
"integrity": "sha512-7Gf3vm8pDbs+H/hKsaqOZe0xKlE9Neah12GCfs7qun3fBUaOXwexAMjn0Eo9cvJJvhRMaL0jgPiY9ZGLTWoe8A==",
"dev": true,
"requires": {
"acorn": "^7.1.0",

@ -1,6 +1,6 @@
{
"name": "svelte",
"version": "3.17.1",
"version": "3.17.2",
"description": "Cybernetically enhanced web apps",
"module": "index.mjs",
"main": "index",
@ -70,7 +70,7 @@
"acorn": "^7.1.0",
"agadoo": "^1.1.0",
"c8": "^5.0.1",
"code-red": "0.0.30",
"code-red": "0.0.31",
"codecov": "^3.5.0",
"css-tree": "1.0.0-alpha22",
"eslint": "^6.3.0",

@ -1,3 +1,6 @@
# IMPORTANT: Don't use this Dockerfile in your own Sapper projects without also looking at the .dockerignore file.
# Without an appropriate .dockerignore, this Dockerfile will copy a large number of unneeded files into your image.
FROM mhart/alpine-node:12
# install dependencies

@ -159,6 +159,6 @@ In Vue, meanwhile, we have a default export with a `data` function that returns
## Death to boilerplate
These are just some of the ways that Svelte helps you build user interfaces with a minimum of fuss. There are plenty of others — for example, [reactive declarations](https://svelte.dev/tutorial/reactive-declarations) essentially do the work of React's `useMemo`, `useCallback` and `useEffect` without the boilerplate (or indeed the garbage collection overhead of creating inline functions and arrays on each state change).
These are just some of the ways that Svelte helps you build user interfaces with a minimum of fuss. There are plenty of others — for example, [reactive declarations](tutorial/reactive-declarations) essentially do the work of React's `useMemo`, `useCallback` and `useEffect` without the boilerplate (or indeed the garbage collection overhead of creating inline functions and arrays on each state change).
How? By choosing a different set of constraints. Because [Svelte is a compiler](blog/frameworks-without-the-framework), we're not bound to the peculiarities of JavaScript: we can *design* a component authoring experience, rather than having to fit it around the semantics of the language. Paradoxically, this results in *more* idiomatic code — for example using variables naturally rather than via proxies or hooks — while delivering significantly more performant apps.

@ -197,7 +197,7 @@ You can `export` bindings from this block, and they will become exports of the c
You cannot `export default`, since the default export is the component itself.
> Variables defined in `module` scripts are not reactive — reassigning them will not trigger a rerender even though the variable itself will update. For values shared between multiple components, consider using a [store](https://svelte.dev/docs#svelte_store).
> Variables defined in `module` scripts are not reactive — reassigning them will not trigger a rerender even though the variable itself will update. For values shared between multiple components, consider using a [store](docs#svelte_store).
```html
<script context="module">

@ -256,7 +256,7 @@ function test_attribute(operator, expected_value, case_insensitive, value) {
}
switch (operator) {
case '=': return value === expected_value;
case '~=': return ` ${value} `.includes(` ${expected_value} `);
case '~=': return value.split(/\s/).includes(expected_value);
case '|=': return `${value}-`.startsWith(`${expected_value}-`);
case '^=': return value.startsWith(expected_value);
case '$=': return value.endsWith(expected_value);

@ -160,6 +160,9 @@ export default class Block {
});
this.has_update_method = true;
if (this.parent) {
this.parent.add_dependencies(dependencies);
}
}
add_element(

@ -374,6 +374,7 @@ export default class EachBlockWrapper extends Wrapper {
block.chunks.init.push(b`
const ${get_key} = #ctx => ${this.node.key.manipulate(block)};
${this.renderer.options.dev && b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`}
for (let #i = 0; #i < ${data_length}; #i += 1) {
let child_ctx = ${this.vars.get_each_context}(#ctx, ${this.vars.each_block_value}, #i);
let key = ${get_key}(child_ctx);
@ -416,6 +417,7 @@ export default class EachBlockWrapper extends Wrapper {
${this.block.has_outros && b`@group_outros();`}
${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].r();`}
${this.renderer.options.dev && b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`}
${iterations} = @update_keyed_each(${iterations}, #dirty, ${get_key}, ${dynamic ? 1 : 0}, #ctx, ${this.vars.each_block_value}, ${lookup}, ${update_mount_node}, ${destroy}, ${create_each_block}, ${update_anchor_node}, ${this.vars.get_each_context});
${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].a();`}
${this.block.has_outros && b`@check_outros();`}

@ -1,14 +1,13 @@
import * as acorn from 'acorn';
import { Node } from 'acorn';
import * as code_red from 'code-red';
const Parser = acorn.Parser;
export const parse = (source: string) => Parser.parse(source, {
export const parse = (source: string): Node => code_red.parse(source, {
sourceType: 'module',
ecmaVersion: 11,
locations: true
});
export const parse_expression_at = (source: string, index: number) => Parser.parseExpressionAt(source, index, {
export const parse_expression_at = (source: string, index: number): Node => code_red.parseExpressionAt(source, index, {
ecmaVersion: 11,
locations: true
});

@ -108,9 +108,13 @@ export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list
return new_blocks;
}
export function measure(blocks) {
const rects = {};
let i = blocks.length;
while (i--) rects[blocks[i].key] = blocks[i].node.getBoundingClientRect();
return rects;
export function validate_each_keys(ctx, list, get_context, get_key) {
const keys = new Set();
for (let i = 0; i < list.length; i++) {
const key = get_key(get_context(ctx, list, i));
if (keys.has(key)) {
throw new Error(`Cannot have duplicate keys in a keyed each`);
}
keys.add(key);
}
}

@ -48,8 +48,8 @@ export function validate_store(store, name) {
}
}
export function subscribe(store, callback) {
const unsub = store.subscribe(callback);
export function subscribe(store, ...callbacks) {
const unsub = store.subscribe(...callbacks);
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
}

@ -1,4 +1,4 @@
import { run_all, noop, safe_not_equal, is_function, get_store_value } from 'svelte/internal';
import { run_all, subscribe, noop, safe_not_equal, is_function, get_store_value } from 'svelte/internal';
/** Callback to inform of a value updates. */
type Subscriber<T> = (value: T) => void;
@ -173,7 +173,8 @@ export function derived<T>(stores: Stores, fn: Function, initial_value?: T): Rea
}
};
const unsubscribers = stores_array.map((store, i) => store.subscribe(
const unsubscribers = stores_array.map((store, i) => subscribe(
store,
(value) => {
values[i] = value;
pending &= ~(1 << i);

@ -0,0 +1 @@
.foo.svelte-xyz{color:red}[class~="bar"].svelte-xyz{background:blue}

@ -0,0 +1,13 @@
<div class="
foo
bar
"></div>
<style>
.foo {
color: red;
}
[class~="bar"] {
background: blue;
}
</style>

@ -1 +1 @@
<div class='foo'></div>
<div class='foo' title='bar'></div>

@ -43,7 +43,7 @@ function handleFoo(bar) {
function foo(node, callback) {
}
} // code goes here
function instance($$self, $$props, $$invalidate) {
let { bar } = $$props;

@ -63,11 +63,11 @@ function create_fragment(ctx) {
function handleTouchstart() {
}
} // ...
function handleClick() {
}
} // ...
class Component extends SvelteComponent {
constructor(options) {

@ -13,7 +13,7 @@ function foo() {
function swipe(node, callback) {
}
} // TODO implement
const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
onMount(() => {

@ -41,7 +41,15 @@
}
},
"body": [],
"sourceType": "module"
"sourceType": "module",
"trailingComments": [
{
"type": "Line",
"value": " TODO write some code",
"start": 10,
"end": 33
}
]
}
}
}

@ -134,7 +134,15 @@
"kind": "let"
}
],
"sourceType": "module"
"sourceType": "module",
"trailingComments": [
{
"type": "Block",
"value": "\n\ttrailing multiline comment\n",
"start": 32,
"end": 67
}
]
}
}
}

@ -134,7 +134,15 @@
"kind": "let"
}
],
"sourceType": "module"
"sourceType": "module",
"trailingComments": [
{
"type": "Line",
"value": " trailing line comment",
"start": 32,
"end": 56
}
]
}
}
}

@ -0,0 +1,6 @@
<script>
let val;
</script>
<input bind:value={val} />
<slot {val}></slot>

@ -0,0 +1,30 @@
export default {
html: `
<input>
`,
async test({ assert, target, snapshot, component, window }) {
const input = target.querySelector('input');
input.value = 'a';
await input.dispatchEvent(new window.Event('input'));
assert.htmlEqual(
target.innerHTML,
`
<input>
Display: a
`
);
input.value = 'abc';
await input.dispatchEvent(new window.Event('input'));
assert.htmlEqual(
target.innerHTML,
`
<input>
Display: abc
`
);
},
};

@ -0,0 +1,10 @@
<script>
import Input from "./Input.svelte";
import Display from "./Display.svelte";
</script>
<Input let:val={foo}>
{#if foo}
<Display>{foo}</Display>
{/if}
</Input>

@ -0,0 +1,7 @@
export default {
compileOptions: {
dev: true
},
error: `Cannot have duplicate keys in a keyed each`
};

@ -0,0 +1,7 @@
<script>
const array = [1, 2, 3, 1];
</script>
{#each array as item (item)}
{item}
{/each}

@ -116,6 +116,15 @@ describe('store', () => {
});
});
const fake_observable = {
subscribe(fn) {
fn(42);
return {
unsubscribe: () => {}
};
}
};
describe('derived', () => {
it('maps a single store', () => {
const a = writable(1);
@ -346,6 +355,11 @@ describe('store', () => {
b.set(2);
assert.deepEqual(get(c), 'two 2');
});
it('works with RxJS-style observables', () => {
const d = derived(fake_observable, _ => _);
assert.equal(get(d), 42);
});
});
describe('get', () => {
@ -355,16 +369,7 @@ describe('store', () => {
});
it('works with RxJS-style observables', () => {
const observable = {
subscribe(fn) {
fn(42);
return {
unsubscribe: () => {}
};
}
};
assert.equal(get(observable), 42);
assert.equal(get(fake_observable), 42);
});
});
});

Loading…
Cancel
Save