Merge branch 'master' into pr/3822

pull/3822/head
Conduitry 6 years ago
commit 67bafeaf40

@ -1,5 +1,14 @@
# Svelte changelog
## Unreleased
* Fix unused export warning for props used as stores ([#4021](https://github.com/sveltejs/svelte/issues/4021))
* Fix `{:then}` without resolved value containing `{#each}` ([#4022](https://github.com/sveltejs/svelte/issues/4022))
* Fix incorrect code generated with `loopGuardTimeout` ([#4034](https://github.com/sveltejs/svelte/issues/4034))
* Fix `{:then}` containing `{#if}` ([#4044](https://github.com/sveltejs/svelte/issues/4044))
* Fix bare `import`s in `format: 'cjs'` output mode ([#4055](https://github.com/sveltejs/svelte/issues/4050))
* Warn when using a known global as a component name ([#4070](https://github.com/sveltejs/svelte/issues/4070))
## 3.16.0
* Use bitmasks to track changes ([#3945](https://github.com/sveltejs/svelte/pull/3945))

@ -53,7 +53,7 @@ What happens if we use the new model as a starting point?
The same 'hello world' app that took 204kb with React and Next weighs just 7kb with Sapper. That number is likely to fall further in the future as we explore the space of optimisation possibilities, such as not shipping any JavaScript *at all* for pages that aren't interactive, beyond the tiny Sapper runtime that handles client-side routing.
What about a more 'real world' example? Conveniently, the [RealWorld](https://github.com/gothinkster/realworld) project, which challenges frameworks to develop an implementation of a Medium clone, gives us a way to find out. The [Sapper implementation](http://svelte-realworld.now.sh/) takes 39.6kb (11.8kb zipped) to render an interactive homepage.
What about a more 'real world' example? Conveniently, the [RealWorld](https://github.com/gothinkster/realworld) project, which challenges frameworks to develop an implementation of a Medium clone, gives us a way to find out. The [Sapper implementation](https://github.com/sveltejs/realworld) takes 39.6kb (11.8kb zipped) to render an interactive homepage.
<aside><p>Code-splitting isn't free — if the reference implementation used code-splitting, it would be larger still</p></aside>

@ -45,9 +45,11 @@
</style>
<div class="logos">
<a target="_blank" rel="noopener" href="https://absoluteweb.com"><img src="organisations/absoluteweb.svg" alt="Absolute Web logo" loading="lazy"></a>
<a target="_blank" rel="noopener" href="https://bekchy.com"><img src="organisations/bekchy.png" alt="Bekchy logo" loading="lazy"></a>
<a target="_blank" rel="noopener" href="https://beyonk.com"><img src="organisations/beyonk.svg" alt="Beyonk logo" loading="lazy"></a>
<a target="_blank" rel="noopener" href="https://buydotstar.com"><img src="organisations/buydotstar.svg" alt="buy.* logo" loading="lazy"></a>
<a target="_blank" rel="noopener" href="https://cashfree.com/"><img src="organisations/cashfree.svg" alt="Cashfree logo" loading="lazy"></a>
<a target="_blank" rel="noopener" href="https://chess.com" style="background-color: rgb(49,46,43);"><img src="organisations/chess.svg" alt="Chess.com logo" loading="lazy"></a>
<a target="_blank" rel="noopener" href="https://comigosaude.com.br"><img src="organisations/comigo.svg" alt="Comigo logo" loading="lazy"></a>
<a target="_blank" rel="noopener" href="https://datawrapper.de"><img src="organisations/datawrapper.svg" alt="Datawrapper logo" loading="lazy"></a>

@ -187,7 +187,7 @@
<svelte:head>
<title>{name} • REPL • Svelte</title>
<meta name="twitter:title" content="Svelte REPL">
<meta name="twitter:title" content="{name} | Svelte REPL">
<meta name="twitter:description" content="Cybernetically enhanced web apps">
<meta name="Description" content="Interactive Svelte playground">
</svelte:head>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.4 KiB

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2156 462"><defs><linearGradient id="linear-gradient" x1="212.75" y1="160.13" x2="257.3" y2="176.47" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f18221"/><stop offset="1" stop-color="#f2b81a"/></linearGradient><linearGradient id="linear-gradient-2" x1="255.57" y1="98.18" x2="450.73" y2="92.36" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#215f70"/><stop offset="1" stop-color="#00b7b1"/></linearGradient><style>.cls-2{fill:#fff}</style></defs><path fill="#4d3475" d="M1 0h2155v462H1z"/><path class="cls-2" d="M819.83 318.07a53.65 53.65 0 01-47.75 53.19 215.5 215.5 0 01-24.54 1.34q-56 0-89.69-35.34t-33.72-92.74q0-61.71 37.93-99.73t95.88-38q6.31 0 12.26.3a52.54 52.54 0 0149.63 52.61 110.48 110.48 0 00-57.4-15.42q-41.8 0-67.72 26.72t-25.92 71.4q0 42.51 24.22 67.71t63.68 25.21q36.4 0 63.14-17.22zM1005 368.29h-40.41v-28.7h-.72q-19 33-55.78 33-27.09 0-42.43-14.71T850.32 319q0-52 59.92-60.64l54.53-7.71q0-39.28-37.31-39.28-32.84 0-59.2 22.6c0-25.22 17.75-47.61 42.64-51.71a149.89 149.89 0 0124.45-1.93q69.6 0 69.6 68.53zm-40.18-90.22l-38.57 5.38q-17.94 2.32-27 8.7t-9.06 22.33a24.45 24.45 0 008.35 19.1q8.34 7.46 22.33 7.45 19 0 31.48-13.37T964.77 294zM1044.23 324q23.32 17.76 51.49 17.76 37.66 0 37.66-22.25a17.54 17.54 0 00-3.22-10.67 31 31 0 00-8.7-7.8 68.51 68.51 0 00-12.92-6.1q-7.44-2.7-16.59-6.1a175.83 175.83 0 01-20.36-9.51 61.5 61.5 0 01-15.07-11.39 42.85 42.85 0 01-9.06-14.44 53.23 53.23 0 01-3-18.83 43.48 43.48 0 016.28-23.41 54.58 54.58 0 0116.86-17 80.72 80.72 0 0124-10.41 110.09 110.09 0 0127.8-3.5h2.66c23.89.39 42.91 20.21 42.91 44.11q-19.38-13.27-44.49-13.27a58.38 58.38 0 00-14.26 1.61 36.82 36.82 0 00-10.85 4.48 22.12 22.12 0 00-7 6.91 16.51 16.51 0 00-2.51 8.88 19 19 0 002.51 10.05 22.65 22.65 0 007.45 7.26 63.78 63.78 0 0011.84 5.74q6.9 2.61 15.87 5.65a232.44 232.44 0 0121.26 9.87 72 72 0 0116 11.39 44.74 44.74 0 0110.22 14.71q3.6 8.34 3.59 19.82 0 14-6.45 24.39a53.84 53.84 0 01-17.15 17.19 80.48 80.48 0 01-24.85 10.14 129.19 129.19 0 01-29.69 3.32c-1.65 0-3.29 0-4.91-.07a48.58 48.58 0 01-47.32-48.53zM1377.34 368.29a41.61 41.61 0 01-41.61-41.62v-58.83q0-54.52-36.42-54.53-18.3 0-30.85 15.78t-12.56 40.37v98.83h-41.79V138.15a41.8 41.8 0 0141.79-41.8V215.1h.72q20.81-34.78 59.55-34.8 61.17 0 61.17 74.8zM1531.79 130.26a37.82 37.82 0 00-19.19-4.85q-30.33 0-30.32 34.26v24.94H1525a32.64 32.64 0 01-32.64 32.64h-9.87v151h-41.62v-151h-31.39V216a31.39 31.39 0 0131.39-31.39v-29.78q0-29.06 19-45.83t47.54-16.77q15.42 0 24.39 3.41zM1661.12 224.25q-7.53-5.91-21.7-5.92-18.48 0-30.86 16.68t-12.38 45.39v87.89h-41.61V226.23a41.61 41.61 0 0141.61-41.62v37.85h.72q6.11-19.38 18.75-30.23t28.25-10.85q11.31 0 17.22 3.41zM1842.84 287.75h-125.21q.7 25.47 15.69 39.29t41.17 13.81q29.41 0 54-17.58v33.54q-25.12 15.79-66.37 15.79-40.55 0-63.59-25t-23.06-70.41q0-42.87 25.39-69.87t63.05-27q37.66 0 58.3 24.22t20.63 67.27zm-40.19-29.42q-.18-22.41-10.58-34.88t-28.7-12.45a40.37 40.37 0 00-30.4 13.09q-12.48 13.1-15.34 34.26zM2038 287.75h-125.21q.72 25.47 15.7 39.29t41.17 13.81q29.41 0 54-17.58v33.54q-25.11 15.79-66.37 15.79-40.54 0-63.59-25t-23-70.41q0-42.87 25.38-69.87t63-27q37.68 0 58.3 24.22t20.62 67.25zm-40.18-29.42q-.18-22.41-10.59-34.88t-28.7-12.45a40.37 40.37 0 00-30.4 13.09q-12.47 13.1-15.34 34.26z"/><path d="M220.69 154.26c-16.76 2.15-16 34.7-18.34 61.86l-13.54 138.76a64.73 64.73 0 01-70.7 58.12l20.82-213.39a53.09 53.09 0 0158-47.68z" fill="#f2b81a"/><path d="M410.32 172.76a63.36 63.36 0 01-69.21 56.9l-138.76-13.54c2.34-27.16 1.58-59.71 18.34-61.86z" fill="url(#linear-gradient)"/><path d="M540.09 73.67A65.07 65.07 0 01469 132.12l-142.5-13.91c2.4-27.9 1.62-61.34 18.84-63.54z" fill="url(#linear-gradient-2)"/><path d="M345.61 54.06c-17.49 2.81-16.71 36.25-19.11 64.15l-13.91 142.5a66.45 66.45 0 01-72.6 59.7l21.38-219.16c2.92-30 29.59-51.89 59.83-49.57z" fill="#00b7b1"/></svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

@ -344,7 +344,7 @@ export default class Component {
};
}
get_unique_name(name: string): Identifier {
get_unique_name(name: string, scope?: Scope): Identifier {
if (test) name = `${name}$`;
let alias = name;
for (
@ -352,7 +352,8 @@ export default class Component {
reserved.has(alias) ||
this.var_lookup.has(alias) ||
this.used_names.has(alias) ||
this.globally_used_names.has(alias);
this.globally_used_names.has(alias) ||
(scope && scope.has(alias));
alias = `${name}_${i++}`
);
this.used_names.add(alias);
@ -465,7 +466,7 @@ export default class Component {
extract_names(declarator.id).forEach(name => {
const variable = this.var_lookup.get(name);
variable.export_name = name;
if (variable.writable && !(variable.referenced || variable.referenced_from_script)) {
if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) {
this.warn(declarator, {
code: `unused-export-let`,
message: `${this.name.name} has unused export property '${name}'. If it is for external reference only, please consider using \`export const '${name}'\``
@ -488,7 +489,7 @@ export default class Component {
if (variable) {
variable.export_name = specifier.exported.name;
if (variable.writable && !(variable.referenced || variable.referenced_from_script)) {
if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) {
this.warn(specifier, {
code: `unused-export-let`,
message: `${this.name.name} has unused export property '${specifier.exported.name}'. If it is for external reference only, please consider using \`export const '${specifier.exported.name}'\``
@ -707,8 +708,7 @@ export default class Component {
const remove = (parent, prop, index) => {
to_remove.unshift([parent, prop, index]);
};
const to_insert = new Map();
let scope_updated = false;
walk(content, {
enter(node, parent, prop, index) {
@ -735,37 +735,21 @@ export default class Component {
}
component.warn_on_undefined_store_value_references(node, parent, scope);
},
leave(node) {
// do it on leave, to prevent infinite loop
if (component.compile_options.dev && component.compile_options.loopGuardTimeout > 0) {
const to_insert_for_loop_protect = component.loop_protect(node, prop, index, component.compile_options.loopGuardTimeout);
if (to_insert_for_loop_protect) {
if (!Array.isArray(parent[prop])) {
parent[prop] = {
type: 'BlockStatement',
body: [to_insert_for_loop_protect.node, node],
};
} else {
// can't insert directly, will screw up the index in the for-loop of estree-walker
if (!to_insert.has(parent)) {
to_insert.set(parent, []);
}
to_insert.get(parent).push(to_insert_for_loop_protect);
}
const to_replace_for_loop_protect = component.loop_protect(node, scope, component.compile_options.loopGuardTimeout);
if (to_replace_for_loop_protect) {
this.replace(to_replace_for_loop_protect);
scope_updated = true;
}
}
},
leave(node) {
if (map.has(node)) {
scope = scope.parent;
}
if (to_insert.has(node)) {
const nodes_to_insert = to_insert.get(node);
for (const { index, prop, node: node_to_insert } of nodes_to_insert.reverse()) {
node[prop].splice(index, 0, node_to_insert);
}
to_insert.delete(node);
}
},
});
@ -778,6 +762,12 @@ export default class Component {
}
}
}
if (scope_updated) {
const { scope, map } = create_scopes(script.content);
this.instance_scope = scope;
this.instance_scope_map = map;
}
}
track_references_and_mutations() {
@ -849,15 +839,12 @@ export default class Component {
}
}
loop_protect(node, prop, index, timeout) {
loop_protect(node, scope: Scope, timeout: number): Node | null {
if (node.type === 'WhileStatement' ||
node.type === 'ForStatement' ||
node.type === 'DoWhileStatement') {
const guard = this.get_unique_name('guard');
this.add_var({
name: guard.name,
internal: true,
});
const guard = this.get_unique_name('guard', scope);
this.used_names.add(guard.name);
const before = b`const ${guard} = @loop_guard(${timeout})`;
const inside = b`${guard}();`;
@ -870,7 +857,14 @@ export default class Component {
};
}
node.body.body.push(inside[0]);
return { index, prop, node: before[0] };
return {
type: 'BlockStatement',
body: [
before[0],
node,
],
};
}
return null;
}
@ -1289,7 +1283,7 @@ export default class Component {
if (this.var_lookup.has(name) && !this.var_lookup.get(name).global) return;
if (template_scope && template_scope.names.has(name)) return;
if (globals.has(name)) return;
if (globals.has(name) && node.type !== 'InlineComponent') return;
let message = `'${name}' is not defined`;
if (!this.ast.instance)

@ -152,28 +152,34 @@ function cjs(
const internal_globals = get_internal_globals(globals, helpers);
const user_requires = imports.map(node => ({
type: 'VariableDeclaration',
kind: 'const',
declarations: [{
type: 'VariableDeclarator',
id: node.specifiers[0].type === 'ImportNamespaceSpecifier'
? { type: 'Identifier', name: node.specifiers[0].local.name }
: {
type: 'ObjectPattern',
properties: node.specifiers.map(s => ({
type: 'Property',
method: false,
shorthand: false,
computed: false,
key: s.type === 'ImportSpecifier' ? s.imported : { type: 'Identifier', name: 'default' },
value: s.local,
kind: 'init'
}))
},
init: x`require("${edit_source(node.source.value, sveltePath)}")`
}]
}));
const user_requires = imports.map(node => {
const init = x`require("${edit_source(node.source.value, sveltePath)}")`;
if (node.specifiers.length === 0) {
return b`${init};`;
}
return {
type: 'VariableDeclaration',
kind: 'const',
declarations: [{
type: 'VariableDeclarator',
id: node.specifiers[0].type === 'ImportNamespaceSpecifier'
? { type: 'Identifier', name: node.specifiers[0].local.name }
: {
type: 'ObjectPattern',
properties: node.specifiers.map(s => ({
type: 'Property',
method: false,
shorthand: false,
computed: false,
key: s.type === 'ImportSpecifier' ? s.imported : { type: 'Identifier', name: 'default' },
value: s.local,
kind: 'init'
}))
},
init
}]
};
});
const exports = module_exports.map(x => b`exports.${{ type: 'Identifier', name: x.as }} = ${{ type: 'Identifier', name: x.name }};`);

@ -71,6 +71,8 @@ export default class AwaitBlockWrapper extends Wrapper {
this.not_static_content();
block.add_dependencies(this.node.expression.dependencies);
if (this.node.value) block.renderer.add_to_context(this.node.value, true);
if (this.node.error) block.renderer.add_to_context(this.node.error, true);
let is_dynamic = false;
let has_intros = false;
@ -118,9 +120,6 @@ export default class AwaitBlockWrapper extends Wrapper {
if (has_outros) {
block.add_outro();
}
if (this.node.value) block.renderer.add_to_context(this.node.value, true);
if (this.node.error) block.renderer.add_to_context(this.node.error, true);
}
render(
@ -206,7 +205,7 @@ export default class AwaitBlockWrapper extends Wrapper {
} else {
const #child_ctx = #ctx.slice();
#child_ctx[${value_index}] = ${info}.resolved;
${this.node.value && x`#child_ctx[${value_index}] = ${info}.resolved;`}
${info}.block.p(#child_ctx, #dirty);
}
`);
@ -220,7 +219,7 @@ export default class AwaitBlockWrapper extends Wrapper {
block.chunks.update.push(b`
{
const #child_ctx = #ctx.slice();
#child_ctx[${value_index}] = ${info}.resolved;
${this.node.value && x`#child_ctx[${value_index}] = ${info}.resolved;`}
${info}.block.p(#child_ctx, #dirty);
}
`);

@ -1,8 +1,13 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponentDev,
add_location,
binding_callbacks,
detach_dev,
dispatch_dev,
element,
init,
insert_dev,
loop_guard,
noop,
safe_not_equal
@ -11,16 +16,27 @@ import {
const file = undefined;
function create_fragment(ctx) {
let div;
const block = {
c: noop,
c: function create() {
div = element("div");
add_location(div, file, 22, 0, 288);
},
l: function claim(nodes) {
throw new Error("options.hydrate only works if the component was compiled with the `hydratable: true` option");
},
m: noop,
m: function mount(target, anchor) {
insert_dev(target, div, anchor);
/*div_binding*/ ctx[1](div);
},
p: noop,
i: noop,
o: noop,
d: noop
d: function destroy(detaching) {
if (detaching) detach_dev(div);
/*div_binding*/ ctx[1](null);
}
};
dispatch_dev("SvelteRegisterBlock", {
@ -34,57 +50,93 @@ function create_fragment(ctx) {
return block;
}
function foo() {
const guard = "foo";
{
const guard_1 = loop_guard(100);
while (true) {
console.log(guard);
guard_1();
}
}
}
function instance($$self, $$props, $$invalidate) {
const guard = loop_guard(100);
let node;
{
const guard = loop_guard(100);
while (true) {
foo();
guard();
while (true) {
foo();
guard();
}
}
const guard_1 = loop_guard(100);
{
const guard_2 = loop_guard(100);
for (; ; ) {
foo();
guard_1();
for (; ; ) {
foo();
guard_2();
}
}
const guard_2 = loop_guard(100);
{
const guard_3 = loop_guard(100);
while (true) {
foo();
guard_2();
while (true) {
foo();
guard_3();
}
}
const guard_4 = loop_guard(100);
{
const guard_5 = loop_guard(100);
do {
foo();
guard_4();
} while (true);
do {
foo();
guard_5();
} while (true);
}
$$self.$capture_state = () => ({ foo });
$$self.$inject_state = noop;
function div_binding($$value) {
binding_callbacks[$$value ? "unshift" : "push"](() => {
$$invalidate(0, node = $$value);
});
}
$$self.$capture_state = () => ({ node, foo, console });
$$self.$inject_state = $$props => {
if ("node" in $$props) $$invalidate(0, node = $$props.node);
};
if ($$props && "$$inject" in $$props) {
$$self.$inject_state($$props.$$inject);
}
$: {
const guard_3 = loop_guard(100);
const guard_4 = loop_guard(100);
while (true) {
foo();
guard_3();
guard_4();
}
}
$: {
const guard_5 = loop_guard(100);
const guard_6 = loop_guard(100);
do {
foo();
guard_5();
guard_6();
} while (true);
}
return [];
return [node, div_binding];
}
class Component extends SvelteComponentDev {

@ -1,4 +1,13 @@
<script>
let node;
function foo() {
const guard = 'foo';
while(true) {
console.log(guard);
}
}
while(true) {
foo();
}
@ -10,3 +19,5 @@
do foo(); while(true);
$: do foo(); while(true);
</script>
<div bind:this={node}></div>

@ -0,0 +1,25 @@
let fulfil;
const thePromise = new Promise(f => {
fulfil = f;
});
export default {
props: {
thePromise
},
html: `
loading...
`,
async test({ assert, component, target }) {
fulfil([]);
await thePromise;
assert.htmlEqual(target.innerHTML, `
<p>promise array is empty</p>
`);
}
};

@ -0,0 +1,13 @@
<script>
export let thePromise;
</script>
{#await thePromise}
loading...
{:then r}
{#if r.length < 1}
<p>promise array is empty</p>
{:else}
<p>promise array is not empty</p>
{/if}
{/await}

@ -0,0 +1,12 @@
<script>
const promise = new Promise(() => {});
const test = [1, 2, 3];
</script>
{#await promise}
<div>waiting</div>
{:then}
{#each test as t}
<div>t</div>
{/each}
{/await}

@ -0,0 +1,7 @@
export default {
html: '<div></div>',
compileOptions: {
dev: true,
loopGuardTimeout: 100,
}
};

@ -0,0 +1,7 @@
<script>
let node;
(function () {
while(false) ;
}());
</script>
<div bind:this={node}></div>

@ -0,0 +1,15 @@
[{
"code": "missing-declaration",
"message": "'String' is not defined. Consider adding a <script> block with 'export let String' to declare a prop",
"start": {
"line": 2,
"column": 1,
"character": 7
},
"end": {
"line": 2,
"column": 10,
"character": 16
},
"pos": 7
}]

@ -18,4 +18,8 @@
function foo() {
return m + n + o;
}
export let p;
export let q;
$p;
</script>
{$q}

Loading…
Cancel
Save