From 4e51d35dd8abb8f71602c151e428589ce5e98d10 Mon Sep 17 00:00:00 2001 From: Sebastian Stephan Date: Wed, 4 Dec 2019 18:57:23 +0100 Subject: [PATCH 01/12] site: fix link to realworld demo in blogpost (#4028) * Fix link to realworld demo in blogpost * link to repo instead --- .../2017-12-31-sapper-towards-the-ideal-web-app-framework.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/blog/2017-12-31-sapper-towards-the-ideal-web-app-framework.md b/site/content/blog/2017-12-31-sapper-towards-the-ideal-web-app-framework.md index 8799fdacce6e..cca200e966f6 100644 --- a/site/content/blog/2017-12-31-sapper-towards-the-ideal-web-app-framework.md +++ b/site/content/blog/2017-12-31-sapper-towards-the-ideal-web-app-framework.md @@ -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. From 185ff4ae041a6fcc1cd1f2be7114a3d1492d022b Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sat, 30 Nov 2019 15:18:29 -0500 Subject: [PATCH 02/12] fix child context in await blocks with no `then` variable (#4022) --- .../compile/render_dom/wrappers/AwaitBlock.ts | 4 ++-- .../samples/await-then-no-context/main.svelte | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 test/runtime/samples/await-then-no-context/main.svelte diff --git a/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts b/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts index 8ce7495fd7bf..01153b108378 100644 --- a/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/AwaitBlock.ts @@ -206,7 +206,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 +220,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); } `); diff --git a/test/runtime/samples/await-then-no-context/main.svelte b/test/runtime/samples/await-then-no-context/main.svelte new file mode 100644 index 000000000000..c72951214921 --- /dev/null +++ b/test/runtime/samples/await-then-no-context/main.svelte @@ -0,0 +1,12 @@ + + +{#await promise} +
waiting
+{:then} + {#each test as t} +
t
+ {/each} +{/await} From e996ef750d31ada1ef2dfefd4aae06010dbd2ea6 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Sat, 30 Nov 2019 19:55:49 -0500 Subject: [PATCH 03/12] fix warnings for props that are only used as stores (#4021) --- src/compiler/compile/Component.ts | 4 ++-- test/validator/samples/unreferenced-variables/input.svelte | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index cd97c8dd8efa..de5400367da3 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -465,7 +465,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 +488,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}'\`` diff --git a/test/validator/samples/unreferenced-variables/input.svelte b/test/validator/samples/unreferenced-variables/input.svelte index 1180f6ef1a43..8c9e0b15f34f 100644 --- a/test/validator/samples/unreferenced-variables/input.svelte +++ b/test/validator/samples/unreferenced-variables/input.svelte @@ -18,4 +18,8 @@ function foo() { return m + n + o; } + export let p; + export let q; + $p; +{$q} From aa2b4e72f7f0991cb7ec89c533c9ebd6dfe2e7cf Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 4 Dec 2019 08:50:44 -0500 Subject: [PATCH 04/12] fix bare imports in `format: 'cjs'` mode (#4050) --- src/compiler/compile/create_module.ts | 50 +++++++++++++++------------ 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/compiler/compile/create_module.ts b/src/compiler/compile/create_module.ts index 632206652a27..80e6308263ee 100644 --- a/src/compiler/compile/create_module.ts +++ b/src/compiler/compile/create_module.ts @@ -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 }};`); From 6a4956b4031fc5ec2bb31a9a4e41c0950fcbe814 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 4 Dec 2019 13:11:23 -0500 Subject: [PATCH 05/12] update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 634f85fb3ecb..4c7f34dfeb21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # 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 bare `import`s in `format: 'cjs'` output mode ([#4055](https://github.com/sveltejs/svelte/issues/4050)) + ## 3.16.0 * Use bitmasks to track changes ([#3945](https://github.com/sveltejs/svelte/pull/3945)) From 2c9d864e33feba60d5f0b47d7b71f098815814a0 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Sat, 7 Dec 2019 14:42:36 +0800 Subject: [PATCH 06/12] fix: loop-guard scope leak --- src/compiler/compile/Component.ts | 64 ++++++------ test/js/samples/loop-protect/expected.js | 99 ++++++++++++++----- test/js/samples/loop-protect/input.svelte | 13 ++- .../loop-protect-inner-function/_config.js | 7 ++ .../loop-protect-inner-function/main.svelte | 7 ++ 5 files changed, 127 insertions(+), 63 deletions(-) create mode 100644 test/runtime/samples/loop-protect-inner-function/_config.js create mode 100644 test/runtime/samples/loop-protect-inner-function/main.svelte diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index de5400367da3..1c4b36360cfb 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -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); @@ -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; } diff --git a/test/js/samples/loop-protect/expected.js b/test/js/samples/loop-protect/expected.js index 093cccf63c66..554ccf23b15f 100644 --- a/test/js/samples/loop-protect/expected.js +++ b/test/js/samples/loop-protect/expected.js @@ -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,62 +50,91 @@ function create_fragment(ctx) { return block; } -function instance($$self) { - const guard = loop_guard(100); +function foo() { + const guard = "foo"; + + { + const guard_1 = loop_guard(100); - while (true) { - foo(); - guard(); + while (true) { + console.log(guard); + guard_1(); + } } +} + +function instance($$self, $$props, $$invalidate) { + let node; - const guard_1 = loop_guard(100); + { + const guard = loop_guard(100); - for (; ; ) { - foo(); - guard_1(); + while (true) { + foo(); + guard(); + } } - const guard_2 = loop_guard(100); + { + const guard_2 = loop_guard(100); - while (true) { - foo(); - guard_2(); + for (; ; ) { + foo(); + guard_2(); + } } - const guard_4 = loop_guard(100); + { + const guard_3 = loop_guard(100); - do { - foo(); - guard_4(); - } while (true); + while (true) { + foo(); + guard_3(); + } + } + + { + const guard_5 = loop_guard(100); + + do { + foo(); + guard_5(); + } while (true); + } + + function div_binding($$value) { + binding_callbacks[$$value ? "unshift" : "push"](() => { + $$invalidate(0, node = $$value); + }); + } $$self.$capture_state = () => { return {}; }; $$self.$inject_state = $$props => { - + if ("node" in $$props) $$invalidate(0, node = $$props.node); }; $: { - 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 { diff --git a/test/js/samples/loop-protect/input.svelte b/test/js/samples/loop-protect/input.svelte index c39ea75f7216..daac6ab1c4aa 100644 --- a/test/js/samples/loop-protect/input.svelte +++ b/test/js/samples/loop-protect/input.svelte @@ -1,4 +1,13 @@ \ No newline at end of file + + +
\ No newline at end of file diff --git a/test/runtime/samples/loop-protect-inner-function/_config.js b/test/runtime/samples/loop-protect-inner-function/_config.js new file mode 100644 index 000000000000..862d4f4c0f44 --- /dev/null +++ b/test/runtime/samples/loop-protect-inner-function/_config.js @@ -0,0 +1,7 @@ +export default { + html: '
', + compileOptions: { + dev: true, + loopGuardTimeout: 100, + } +}; diff --git a/test/runtime/samples/loop-protect-inner-function/main.svelte b/test/runtime/samples/loop-protect-inner-function/main.svelte new file mode 100644 index 000000000000..cf5350efde49 --- /dev/null +++ b/test/runtime/samples/loop-protect-inner-function/main.svelte @@ -0,0 +1,7 @@ + +
\ No newline at end of file From 3433418dceeae56ffef0cd963e4a8d7bb6358525 Mon Sep 17 00:00:00 2001 From: "Benjamin W. Broersma" Date: Mon, 9 Dec 2019 15:15:49 +0100 Subject: [PATCH 07/12] Skip JS globals for InlineComponent nodes in warn_if_undefined. (#4071) --- src/compiler/compile/Component.ts | 2 +- .../samples/missing-component-global/input.svelte | 3 +++ .../missing-component-global/warnings.json | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 test/validator/samples/missing-component-global/input.svelte create mode 100644 test/validator/samples/missing-component-global/warnings.json diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 1c4b36360cfb..4402a4a94749 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -1283,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) diff --git a/test/validator/samples/missing-component-global/input.svelte b/test/validator/samples/missing-component-global/input.svelte new file mode 100644 index 000000000000..5d17448bd408 --- /dev/null +++ b/test/validator/samples/missing-component-global/input.svelte @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/test/validator/samples/missing-component-global/warnings.json b/test/validator/samples/missing-component-global/warnings.json new file mode 100644 index 000000000000..c9de56619a07 --- /dev/null +++ b/test/validator/samples/missing-component-global/warnings.json @@ -0,0 +1,15 @@ +[{ + "code": "missing-declaration", + "message": "'String' is not defined. Consider adding a + +{#await thePromise} + loading... +{:then r} + {#if r.length < 1} +

promise array is empty

+ {:else} +

promise array is not empty

+ {/if} +{/await} \ No newline at end of file From ad1f15df4117971cde0db4db50acda79a4e0fb88 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Sun, 8 Dec 2019 12:43:31 +0800 Subject: [PATCH 09/12] feat repl twitter title use repl title --- site/src/routes/repl/[id]/index.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/routes/repl/[id]/index.svelte b/site/src/routes/repl/[id]/index.svelte index 4d05ac28ea35..59b8e6359ab4 100644 --- a/site/src/routes/repl/[id]/index.svelte +++ b/site/src/routes/repl/[id]/index.svelte @@ -187,7 +187,7 @@ {name} • REPL • Svelte - + From 0f2c7d2872d89a4049253cf10982458f95a94b3d Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 9 Dec 2019 09:27:42 -0500 Subject: [PATCH 10/12] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c7f34dfeb21..c286390f8ae9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,10 @@ * 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 From 7768110d272baf8330f451a590f29bd1fe9b53d1 Mon Sep 17 00:00:00 2001 From: Raj Nandan Sharma Date: Mon, 9 Dec 2019 20:03:06 +0530 Subject: [PATCH 11/12] added Cashfree to WhoUsingSvelte (#4054) --- site/src/routes/_components/WhosUsingSvelte.svelte | 1 + site/static/organisations/cashfree.svg | 1 + 2 files changed, 2 insertions(+) create mode 100644 site/static/organisations/cashfree.svg diff --git a/site/src/routes/_components/WhosUsingSvelte.svelte b/site/src/routes/_components/WhosUsingSvelte.svelte index c1b80c0fe159..8ea0b0e18809 100644 --- a/site/src/routes/_components/WhosUsingSvelte.svelte +++ b/site/src/routes/_components/WhosUsingSvelte.svelte @@ -48,6 +48,7 @@ Bekchy logo Beyonk logo buy.* logo + Cashfree logo Chess.com logo Comigo logo Datawrapper logo diff --git a/site/static/organisations/cashfree.svg b/site/static/organisations/cashfree.svg new file mode 100644 index 000000000000..d0952dd71c8f --- /dev/null +++ b/site/static/organisations/cashfree.svg @@ -0,0 +1 @@ + \ No newline at end of file From 3e1e6cbd787a9ff434e8df077af48d8af1bdd217 Mon Sep 17 00:00:00 2001 From: rykiplov Date: Mon, 9 Dec 2019 16:34:42 +0200 Subject: [PATCH 12/12] Added Absolute Web logo to WhosUsingSvelte (#4057) --- site/src/routes/_components/WhosUsingSvelte.svelte | 1 + site/static/organisations/absoluteweb.svg | 1 + 2 files changed, 2 insertions(+) create mode 100644 site/static/organisations/absoluteweb.svg diff --git a/site/src/routes/_components/WhosUsingSvelte.svelte b/site/src/routes/_components/WhosUsingSvelte.svelte index 8ea0b0e18809..f27f1684e60a 100644 --- a/site/src/routes/_components/WhosUsingSvelte.svelte +++ b/site/src/routes/_components/WhosUsingSvelte.svelte @@ -45,6 +45,7 @@
+ Absolute Web logo Bekchy logo Beyonk logo buy.* logo diff --git a/site/static/organisations/absoluteweb.svg b/site/static/organisations/absoluteweb.svg new file mode 100644 index 000000000000..e2aeb9421e69 --- /dev/null +++ b/site/static/organisations/absoluteweb.svg @@ -0,0 +1 @@ + \ No newline at end of file