From a8c35daa9a97c1f12c4edb26fca0bac4ebc6f545 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 14 Jul 2021 09:54:15 +0200 Subject: [PATCH 01/69] [fix] be able to silence more warnings (#6504) including css-unused-selector, unused-export-let, module-script-reactive-declaration Fixes #5954 Related to #5281 --- CHANGELOG.md | 1 + src/compiler/compile/Component.ts | 19 +++++++++++ src/compiler/compile/css/Stylesheet.ts | 4 +++ src/compiler/compile/nodes/Comment.ts | 6 +--- src/compiler/compile/render_dom/index.ts | 16 ++------- src/compiler/interfaces.ts | 9 ++++- src/compiler/parse/state/tag.ts | 4 ++- src/compiler/utils/extract_svelte_ignore.ts | 34 +++++++++++++++++++ src/compiler/utils/flatten.ts | 14 ++++++++ .../samples/comment-with-ignores/input.svelte | 1 + .../samples/comment-with-ignores/output.json | 16 +++++++++ test/parser/samples/comment/output.json | 3 +- .../samples/silence-warnings-2/input.svelte | 9 +++++ .../samples/silence-warnings-2/warnings.json | 1 + .../samples/silence-warnings/input.svelte | 15 ++++++++ .../samples/silence-warnings/warnings.json | 1 + 16 files changed, 131 insertions(+), 22 deletions(-) create mode 100644 src/compiler/utils/extract_svelte_ignore.ts create mode 100644 src/compiler/utils/flatten.ts create mode 100644 test/parser/samples/comment-with-ignores/input.svelte create mode 100644 test/parser/samples/comment-with-ignores/output.json create mode 100644 test/validator/samples/silence-warnings-2/input.svelte create mode 100644 test/validator/samples/silence-warnings-2/warnings.json create mode 100644 test/validator/samples/silence-warnings/input.svelte create mode 100644 test/validator/samples/silence-warnings/warnings.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 93e1539093..79e5ee0624 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ * Support `bind:group` in SSR ([#4621](https://github.com/sveltejs/svelte/pull/4621)) * Add sourcemaps to html elements ([#6427](https://github.com/sveltejs/svelte/pull/6427)) * Make ` + + + diff --git a/test/validator/samples/silence-warnings-2/warnings.json b/test/validator/samples/silence-warnings-2/warnings.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/test/validator/samples/silence-warnings-2/warnings.json @@ -0,0 +1 @@ +[] diff --git a/test/validator/samples/silence-warnings/input.svelte b/test/validator/samples/silence-warnings/input.svelte new file mode 100644 index 0000000000..0e473f51db --- /dev/null +++ b/test/validator/samples/silence-warnings/input.svelte @@ -0,0 +1,15 @@ + + + + + + diff --git a/test/validator/samples/silence-warnings/warnings.json b/test/validator/samples/silence-warnings/warnings.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/test/validator/samples/silence-warnings/warnings.json @@ -0,0 +1 @@ +[] From fd031105aa864f832ee0f7874187ce3881455af3 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Wed, 14 Jul 2021 16:01:05 +0800 Subject: [PATCH 02/69] [fix] do not warn if module variables are not the only dependencies in reactive statements (#6510) The warning was too strict, since there are valid use cases for having non-reactive variables inside reactive statements Fixes #5954 --- src/compiler/compile/Component.ts | 7 ++++++- src/compiler/compile/compiler_warnings.ts | 4 ++-- .../samples/reactive-module-variable-2/input.svelte | 7 +++++++ .../samples/reactive-module-variable-2/warnings.json | 1 + .../samples/reactive-module-variable/warnings.json | 10 +++++----- 5 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 test/validator/samples/reactive-module-variable-2/input.svelte create mode 100644 test/validator/samples/reactive-module-variable-2/warnings.json diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index f3a012341d..4ca111f7ea 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -1192,6 +1192,7 @@ export default class Component { const assignees = new Set(); const assignee_nodes = new Set(); const dependencies = new Set(); + const module_dependencies = new Set(); let scope = this.instance_scope; const map = this.instance_scope_map; @@ -1228,7 +1229,7 @@ export default class Component { variable.is_reactive_dependency = true; if (variable.module) { should_add_as_dependency = false; - component.warn(node as any, compiler_warnings.module_script_variable_reactive_declaration(name)); + module_dependencies.add(name); } } const is_writable_or_mutated = @@ -1253,6 +1254,10 @@ export default class Component { } }); + if (module_dependencies.size > 0 && dependencies.size === 0) { + component.warn(node.body as any, compiler_warnings.module_script_variable_reactive_declaration(Array.from(module_dependencies))); + } + const { expression } = node.body as ExpressionStatement; const declaration = expression && (expression as AssignmentExpression).left; diff --git a/src/compiler/compile/compiler_warnings.ts b/src/compiler/compile/compiler_warnings.ts index 3b8ba929fa..bfcd779fc7 100644 --- a/src/compiler/compile/compiler_warnings.ts +++ b/src/compiler/compile/compiler_warnings.ts @@ -20,9 +20,9 @@ export default { code: 'non-top-level-reactive-declaration', message: '$: has no effect outside of the top-level' }, - module_script_variable_reactive_declaration: (name: string) => ({ + module_script_variable_reactive_declaration: (names: string[]) => ({ code: 'module-script-reactive-declaration', - message: `"${name}" is declared in a module script and will not be reactive` + message: `${names.map(name => `"${name}"`).join(', ')} ${names.length > 1 ? 'are' : 'is'} declared in a module script and will not be reactive` }), missing_declaration: (name: string, has_script: boolean) => ({ code: 'missing-declaration', diff --git a/test/validator/samples/reactive-module-variable-2/input.svelte b/test/validator/samples/reactive-module-variable-2/input.svelte new file mode 100644 index 0000000000..d36d9cf210 --- /dev/null +++ b/test/validator/samples/reactive-module-variable-2/input.svelte @@ -0,0 +1,7 @@ + + diff --git a/test/validator/samples/reactive-module-variable-2/warnings.json b/test/validator/samples/reactive-module-variable-2/warnings.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/test/validator/samples/reactive-module-variable-2/warnings.json @@ -0,0 +1 @@ +[] diff --git a/test/validator/samples/reactive-module-variable/warnings.json b/test/validator/samples/reactive-module-variable/warnings.json index c3c52cb479..d08fc1309f 100644 --- a/test/validator/samples/reactive-module-variable/warnings.json +++ b/test/validator/samples/reactive-module-variable/warnings.json @@ -2,15 +2,15 @@ { "code": "module-script-reactive-declaration", "message": "\"foo\" is declared in a module script and will not be reactive", - "pos": 65, + "pos": 59, "start": { - "character": 65, - "column": 10, + "character": 59, + "column": 4, "line": 5 }, "end": { - "character": 68, - "column": 13, + "character": 69, + "column": 14, "line": 5 } } From 5dd1e6e232a1d599b722286f7bb8629a927450a2 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 14 Jul 2021 10:01:57 +0200 Subject: [PATCH 03/69] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79e5ee0624..6b6336b85b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ * Add sourcemaps to html elements ([#6427](https://github.com/sveltejs/svelte/pull/6427)) * Make ` + + + diff --git a/test/validator/samples/error-mode-warn/options.json b/test/validator/samples/error-mode-warn/options.json new file mode 100644 index 0000000000..8d48d2f34e --- /dev/null +++ b/test/validator/samples/error-mode-warn/options.json @@ -0,0 +1,3 @@ +{ + "errorMode": "warn" +} \ No newline at end of file diff --git a/test/validator/samples/error-mode-warn/warnings.json b/test/validator/samples/error-mode-warn/warnings.json new file mode 100644 index 0000000000..cce6324df5 --- /dev/null +++ b/test/validator/samples/error-mode-warn/warnings.json @@ -0,0 +1,47 @@ +[ + { + "code": "invalid-binding", + "message": "Cannot bind to a variable which is not writable", + "pos": 61, + "start": { + "line": 5, + "column": 19, + "character": 61 + }, + "end": { + "line": 5, + "column": 24, + "character": 66 + } + }, + { + "code": "missing-declaration", + "message": "'undeclared' is not defined", + "pos": 88, + "start": { + "character": 88, + "column": 19, + "line": 6 + }, + "end": { + "character": 98, + "column": 29, + "line": 6 + } + }, + { + "code": "binding-undeclared", + "message": "undeclared is not declared", + "pos": 88, + "end": { + "character": 98, + "column": 29, + "line": 6 + }, + "start": { + "character": 88, + "column": 19, + "line": 6 + } + } +] \ No newline at end of file From 5534b911ea9da16682d6052e641461087661c689 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sat, 17 Jul 2021 13:38:48 +0200 Subject: [PATCH 07/69] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b6336b85b..2f7252114a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Expose `svelte/ssr` which exports lifecycle methods as no-ops ([#6416](https://github.com/sveltejs/svelte/pull/6416)) * Add `|trusted` event modifier ([#6137](https://github.com/sveltejs/svelte/issues/6137)) * Add `varsReport` compiler option to include all variables reference in the component in the `variables` report ([#6192](https://github.com/sveltejs/svelte/pull/6192)) +* Add `errorMode` compiler option to try to continue compiling when an error is detected ([#6194](https://github.com/sveltejs/svelte/pull/6194)) * Throw compiler error when passing empty directive names ([#6299](https://github.com/sveltejs/svelte/issues/6299)) * Throw proper error for `export default function() {}` and `export default class {}` rather than crashing the compiler ([#3275](https://github.com/sveltejs/svelte/issues/3275)) * Fix usage of falsy `input` values ([#6458](https://github.com/sveltejs/svelte/pull/6458)) From f6a9804275f48ce788708b1bba4f88dcccf31e9a Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 20 Jul 2021 20:31:55 -0500 Subject: [PATCH 08/69] Upgrade Periscopic (#6549) --- package-lock.json | 41 +++++++++++++++---- package.json | 2 +- src/compiler/compile/Component.ts | 3 +- .../compile/nodes/shared/Expression.ts | 3 +- src/compiler/compile/render_dom/index.ts | 4 +- src/compiler/compile/render_ssr/index.ts | 4 +- src/compiler/compile/utils/scope.ts | 4 +- 7 files changed, 43 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index d58fdac957..d90c33bcb2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -974,6 +974,33 @@ "requires": { "@types/estree": "*" } + }, + "periscopic": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-2.0.3.tgz", + "integrity": "sha512-FuCZe61mWxQOJAQFEfmt9FjzebRlcpFz8sFPbyaCKtdusPkMEbA9ey0eARnRav5zAhmXznhaQkKGFAPn7X9NUw==", + "dev": true, + "requires": { + "estree-walker": "^2.0.2", + "is-reference": "^1.1.4" + }, + "dependencies": { + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "requires": { + "@types/estree": "*" + } + } + } } } }, @@ -3473,19 +3500,19 @@ "dev": true }, "periscopic": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-2.0.3.tgz", - "integrity": "sha512-FuCZe61mWxQOJAQFEfmt9FjzebRlcpFz8sFPbyaCKtdusPkMEbA9ey0eARnRav5zAhmXznhaQkKGFAPn7X9NUw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.0.3.tgz", + "integrity": "sha512-8wX0FcHlCndaln004f0tLr3tHLLpSQQHqVAiunx859Fz1jZ75OG5Hpt5F9DLmM0YnfGuDZPJC3Yh0w9eCngj/g==", "dev": true, "requires": { - "estree-walker": "^2.0.2", + "estree-walker": "^3.0.0", "is-reference": "^1.1.4" }, "dependencies": { "estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.0.tgz", + "integrity": "sha512-s6ceX0NFiU/vKPiKvFdR83U1Zffu7upwZsGwpoqfg5rbbq1l50WQ5hCeIvM6E6oD4shUHCYMsiFPns4Jk0YfMQ==", "dev": true } } diff --git a/package.json b/package.json index 14cb172b14..495893a91d 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "locate-character": "^2.0.5", "magic-string": "^0.25.3", "mocha": "^7.0.0", - "periscopic": "^2.0.3", + "periscopic": "^3.0.4", "puppeteer": "^2.1.1", "rollup": "^1.27.14", "source-map": "^0.7.3", diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 8067e29828..c7fd3481dd 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -10,7 +10,6 @@ import { Scope, extract_identifiers } from './utils/scope'; -import { Node as PeriscopicNode } from 'periscopic'; import Stylesheet from './css/Stylesheet'; import { test } from '../config'; import Fragment from './nodes/Fragment'; @@ -820,7 +819,7 @@ export default class Component { if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') { const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument; - const names = extract_names(assignee as PeriscopicNode); + const names = extract_names(assignee as Node); const deep = assignee.type === 'MemberExpression'; diff --git a/src/compiler/compile/nodes/shared/Expression.ts b/src/compiler/compile/nodes/shared/Expression.ts index afe326e831..7c6fc39185 100644 --- a/src/compiler/compile/nodes/shared/Expression.ts +++ b/src/compiler/compile/nodes/shared/Expression.ts @@ -17,7 +17,6 @@ import replace_object from '../../utils/replace_object'; import is_contextual from './is_contextual'; import EachBlock from '../EachBlock'; import { clone } from '../../../utils/clone'; -import { Node as PeriscopicNode } from 'periscopic'; import compiler_errors from '../../compiler_errors'; type Owner = INode; @@ -355,7 +354,7 @@ export default class Expression { // (a or b). In destructuring cases (`[d, e] = [e, d]`) there // may be more, in which case we need to tack the extra ones // onto the initial function call - const names = new Set(extract_names(assignee as PeriscopicNode)); + const names = new Set(extract_names(assignee as Node)); const traced: Set = new Set(); names.forEach(name => { diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index 6a02a00949..7894c42115 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -3,7 +3,7 @@ import Component from '../Component'; import Renderer from './Renderer'; import { CompileOptions, CssResult } from '../../interfaces'; import { walk } from 'estree-walker'; -import { extract_names, Node as PeriscopicNode, Scope } from 'periscopic'; +import { extract_names, Scope } from 'periscopic'; import { invalidate } from './invalidate'; import Block from './Block'; import { ClassDeclaration, FunctionExpression, Node, Statement, ObjectExpression, Expression } from 'estree'; @@ -253,7 +253,7 @@ export default function dom( // (a or b). In destructuring cases (`[d, e] = [e, d]`) there // may be more, in which case we need to tack the extra ones // onto the initial function call - const names = new Set(extract_names(assignee as PeriscopicNode)); + const names = new Set(extract_names(assignee as Node)); this.replace(invalidate(renderer, scope, node, names, execution_context === null)); } diff --git a/src/compiler/compile/render_ssr/index.ts b/src/compiler/compile/render_ssr/index.ts index baafe40251..b35a6ce6ff 100644 --- a/src/compiler/compile/render_ssr/index.ts +++ b/src/compiler/compile/render_ssr/index.ts @@ -6,7 +6,7 @@ import Renderer from './Renderer'; import { INode as TemplateNode } from '../nodes/interfaces'; // TODO import Text from '../nodes/Text'; import { LabeledStatement, Statement, Node } from 'estree'; -import { Node as PeriscopicNode , extract_names } from 'periscopic'; +import { extract_names } from 'periscopic'; import { walk } from 'estree-walker'; import { invalidate } from '../render_dom/invalidate'; @@ -90,7 +90,7 @@ export default function ssr( if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') { const assignee = node.type === 'AssignmentExpression' ? node.left : node.argument; - const names = new Set(extract_names(assignee as PeriscopicNode)); + const names = new Set(extract_names(assignee as Node)); const to_invalidate = new Set(); for (const name of names) { diff --git a/src/compiler/compile/utils/scope.ts b/src/compiler/compile/utils/scope.ts index 033baf32c0..e103defa4d 100644 --- a/src/compiler/compile/utils/scope.ts +++ b/src/compiler/compile/utils/scope.ts @@ -1,8 +1,8 @@ import { Node } from 'estree'; -import { Node as PeriscopicNode, analyze, Scope, extract_names, extract_identifiers } from 'periscopic'; +import { analyze, Scope, extract_names, extract_identifiers } from 'periscopic'; export function create_scopes(expression: Node) { - return analyze(expression as PeriscopicNode); + return analyze(expression); } export { Scope, extract_names, extract_identifiers }; From 222a9dd2c6cd341135c3e8c16c89774eeb06d4f8 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Wed, 21 Jul 2021 12:59:00 +0800 Subject: [PATCH 09/69] [feat] get all contexts (#6528) * get all contexts * docs * explicit return type * allow specifying return type through generic parameter * Update site/content/docs/03-run-time.md Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- site/content/docs/03-run-time.md | 18 ++++++++++++++++++ src/runtime/index.ts | 1 + src/runtime/internal/lifecycle.ts | 4 ++++ src/runtime/ssr.ts | 1 + test/runtime/samples/context-api-d/Leaf.svelte | 9 +++++++++ .../samples/context-api-d/Nested.svelte | 8 ++++++++ test/runtime/samples/context-api-d/_config.js | 7 +++++++ test/runtime/samples/context-api-d/main.svelte | 8 ++++++++ 8 files changed, 56 insertions(+) create mode 100644 test/runtime/samples/context-api-d/Leaf.svelte create mode 100644 test/runtime/samples/context-api-d/Nested.svelte create mode 100644 test/runtime/samples/context-api-d/_config.js create mode 100644 test/runtime/samples/context-api-d/main.svelte diff --git a/site/content/docs/03-run-time.md b/site/content/docs/03-run-time.md index da65ac2488..d7bf3757df 100644 --- a/site/content/docs/03-run-time.md +++ b/site/content/docs/03-run-time.md @@ -200,6 +200,24 @@ Checks whether a given `key` has been set in the context of a parent component. ``` +#### `getAllContexts` + +```js +contexts: Map = getAllContexts() +``` + +--- + +Retrieves the whole context map that belongs to the closest parent component. Must be called during component initialisation. Useful, for example, if you programmatically create a component and want to pass the existing context to it. + +```sv + +``` + #### `createEventDispatcher` ```js diff --git a/src/runtime/index.ts b/src/runtime/index.ts index b3451ed5cb..8e12f9f0ee 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -7,6 +7,7 @@ export { afterUpdate, setContext, getContext, + getAllContexts, hasContext, tick, createEventDispatcher, diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index a84bcc689b..bb3df3d295 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -54,6 +54,10 @@ export function getContext(key): T { return get_current_component().$$.context.get(key); } +export function getAllContexts = Map>(): T { + return get_current_component().$$.context; +} + export function hasContext(key): boolean { return get_current_component().$$.context.has(key); } diff --git a/src/runtime/ssr.ts b/src/runtime/ssr.ts index c75bb30349..69fe841e5c 100644 --- a/src/runtime/ssr.ts +++ b/src/runtime/ssr.ts @@ -1,6 +1,7 @@ export { setContext, getContext, + getAllContexts, hasContext, tick, createEventDispatcher, diff --git a/test/runtime/samples/context-api-d/Leaf.svelte b/test/runtime/samples/context-api-d/Leaf.svelte new file mode 100644 index 0000000000..02b6b26e00 --- /dev/null +++ b/test/runtime/samples/context-api-d/Leaf.svelte @@ -0,0 +1,9 @@ + + +{#each [...context.keys()] as key} +
{key}: {context.get(key)}
+{/each} diff --git a/test/runtime/samples/context-api-d/Nested.svelte b/test/runtime/samples/context-api-d/Nested.svelte new file mode 100644 index 0000000000..e8a7e083c3 --- /dev/null +++ b/test/runtime/samples/context-api-d/Nested.svelte @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/test/runtime/samples/context-api-d/_config.js b/test/runtime/samples/context-api-d/_config.js new file mode 100644 index 0000000000..a5b0769f8b --- /dev/null +++ b/test/runtime/samples/context-api-d/_config.js @@ -0,0 +1,7 @@ +export default { + html: ` +
a: 1
+
b: 2
+
c: 3
+ ` +}; diff --git a/test/runtime/samples/context-api-d/main.svelte b/test/runtime/samples/context-api-d/main.svelte new file mode 100644 index 0000000000..82d1021574 --- /dev/null +++ b/test/runtime/samples/context-api-d/main.svelte @@ -0,0 +1,8 @@ + + + + + From c78a3af01930db3cc3a9c8806d69e11a13fa4792 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Wed, 21 Jul 2021 13:00:09 +0800 Subject: [PATCH 10/69] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f7252114a..63df8e21bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ * Make ` + + \ No newline at end of file diff --git a/test/runtime/samples/component-binding-accessors/_config.js b/test/runtime/samples/component-binding-accessors/_config.js new file mode 100644 index 0000000000..0fb83df870 --- /dev/null +++ b/test/runtime/samples/component-binding-accessors/_config.js @@ -0,0 +1,18 @@ +export default { + async test({ assert, component, target, window }) { + const [input1, input2] = target.querySelectorAll('input'); + assert.equal(input1.value, 'something'); + assert.equal(input2.value, 'something'); + + input1.value = 'abc'; + + await input1.dispatchEvent(new window.Event('input')); + assert.equal(input1.value, 'abc'); + assert.equal(input2.value, 'abc'); + + await target.querySelector('button').dispatchEvent(new window.MouseEvent('click')); + + assert.equal(input1.value, 'Reset'); + assert.equal(input2.value, 'Reset'); + } +}; diff --git a/test/runtime/samples/component-binding-accessors/main.svelte b/test/runtime/samples/component-binding-accessors/main.svelte new file mode 100644 index 0000000000..1d8576643d --- /dev/null +++ b/test/runtime/samples/component-binding-accessors/main.svelte @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file From 68a1b4977c1e3cf37b076933b3620bcff37338a4 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 21 Jul 2021 16:27:22 -0400 Subject: [PATCH 24/69] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf8c1b34d4..a24e23ab4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Fix `.end` in AST for expressions inside attributes ([#6258](https://github.com/sveltejs/svelte/issues/6258)) * Various hydration improvements and fixes ([#6449](https://github.com/sveltejs/svelte/pull/6449)) * Use smaller versions of internal helpers when compiling without hydration support ([#6462](https://github.com/sveltejs/svelte/issues/6462)) +* Fix two-way binding of values when updating through synchronous component accessors ([#6502](https://github.com/sveltejs/svelte/issues/6502)) ## 3.39.0 From c54d57b0d92e464471f00453842c885c361b27a0 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 22 Jul 2021 04:33:49 +0800 Subject: [PATCH 25/69] don't scope :root selector (#6514) --- src/compiler/compile/css/Selector.ts | 8 ++++++-- test/css/samples/root/_config.js | 1 + test/css/samples/root/expected.css | 1 + test/css/samples/root/expected.html | 1 + test/css/samples/root/input.svelte | 13 +++++++++++++ 5 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 test/css/samples/root/_config.js create mode 100644 test/css/samples/root/expected.css create mode 100644 test/css/samples/root/expected.html create mode 100644 test/css/samples/root/input.svelte diff --git a/src/compiler/compile/css/Selector.ts b/src/compiler/compile/css/Selector.ts index 071a18b637..7b3dba375a 100644 --- a/src/compiler/compile/css/Selector.ts +++ b/src/compiler/compile/css/Selector.ts @@ -47,8 +47,9 @@ export default class Selector { this.local_blocks = this.blocks.slice(0, i); const host_only = this.blocks.length === 1 && this.blocks[0].host; + const root_only = this.blocks.length === 1 && this.blocks[0].root; - this.used = this.local_blocks.length === 0 || host_only; + this.used = this.local_blocks.length === 0 || host_only || root_only; } apply(node: Element) { @@ -273,7 +274,7 @@ function block_might_apply_to_node(block: Block, node: Element): BlockAppliesToN const selector = block.selectors[i]; const name = typeof selector.name === 'string' && selector.name.replace(/\\(.)/g, '$1'); - if (selector.type === 'PseudoClassSelector' && name === 'host') { + if (selector.type === 'PseudoClassSelector' && (name === 'host' || name === 'root')) { return BlockAppliesToNode.NotPossible; } @@ -582,6 +583,7 @@ function loop_child(children: INode[], adjacent_only: boolean) { class Block { host: boolean; + root: boolean; combinator: CssNode; selectors: CssNode[] start: number; @@ -591,6 +593,7 @@ class Block { constructor(combinator: CssNode) { this.combinator = combinator; this.host = false; + this.root = false; this.selectors = []; this.start = null; @@ -604,6 +607,7 @@ class Block { this.start = selector.start; this.host = selector.type === 'PseudoClassSelector' && selector.name === 'host'; } + this.root = this.root || selector.type === 'PseudoClassSelector' && selector.name === 'root'; this.selectors.push(selector); this.end = selector.end; diff --git a/test/css/samples/root/_config.js b/test/css/samples/root/_config.js new file mode 100644 index 0000000000..ff8b4c5632 --- /dev/null +++ b/test/css/samples/root/_config.js @@ -0,0 +1 @@ +export default {}; diff --git a/test/css/samples/root/expected.css b/test/css/samples/root/expected.css new file mode 100644 index 0000000000..ce35d8835f --- /dev/null +++ b/test/css/samples/root/expected.css @@ -0,0 +1 @@ +:root{color:red}.foo:root{color:blue}:root.foo{color:green} \ No newline at end of file diff --git a/test/css/samples/root/expected.html b/test/css/samples/root/expected.html new file mode 100644 index 0000000000..1d90ab5df7 --- /dev/null +++ b/test/css/samples/root/expected.html @@ -0,0 +1 @@ +

Hello!

\ No newline at end of file diff --git a/test/css/samples/root/input.svelte b/test/css/samples/root/input.svelte new file mode 100644 index 0000000000..979c9d4f0a --- /dev/null +++ b/test/css/samples/root/input.svelte @@ -0,0 +1,13 @@ + + +

Hello!

From 0d3e105915270fc75e38e2bf25235e38c40c5e34 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 21 Jul 2021 16:34:47 -0400 Subject: [PATCH 26/69] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a24e23ab4d..861a798c85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Support rendering a component in a shadow DOM ([#5869](https://github.com/sveltejs/svelte/issues/5869)) +* Fix `:root` selector being erroneously scoped to component ([#4767](https://github.com/sveltejs/svelte/issues/4767)) * Fix `.end` in AST for expressions inside attributes ([#6258](https://github.com/sveltejs/svelte/issues/6258)) * Various hydration improvements and fixes ([#6449](https://github.com/sveltejs/svelte/pull/6449)) * Use smaller versions of internal helpers when compiling without hydration support ([#6462](https://github.com/sveltejs/svelte/issues/6462)) From 8c66acfa920323f81335a13d7a3ffe23a261ffc6 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Thu, 22 Jul 2021 04:36:16 +0800 Subject: [PATCH 27/69] fix one-way + + + From 6d9c2ea057b9596f53c3897fd9e50528ed8e58de Mon Sep 17 00:00:00 2001 From: Conduitry Date: Wed, 21 Jul 2021 16:37:17 -0400 Subject: [PATCH 28/69] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 861a798c85..67bbd44938 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Support rendering a component in a shadow DOM ([#5869](https://github.com/sveltejs/svelte/issues/5869)) * Fix `:root` selector being erroneously scoped to component ([#4767](https://github.com/sveltejs/svelte/issues/4767)) * Fix `.end` in AST for expressions inside attributes ([#6258](https://github.com/sveltejs/svelte/issues/6258)) +* Fix one-way `` binding when it has a spread attribute ([#6433](https://github.com/sveltejs/svelte/issues/)) +* Fix one-way ` +{#if active === 'default'} + +{:else if active === 'dynamic-false'} + +{:else if active === 'dynamic-true'} + +{:else if active === 'spread'} + +{:else if active === 'spread-override'} + {/if} \ No newline at end of file From 923088f5f8460875852a4678daab6c25a21e5f05 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Fri, 23 Jul 2021 14:20:40 -0400 Subject: [PATCH 43/69] update changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ba49f9e9f..0a4a3ea13c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,10 @@ ## Unreleased +* Fix dynamic `autofocus={...}` attribute handling ([#4995](https://github.com/sveltejs/svelte/issues/4995)) +* Add filename to combined source map if needed ([#6089](https://github.com/sveltejs/svelte/pull/6089)) +* Fix tracking whether transition has started ([#6399](https://github.com/sveltejs/svelte/pull/6399)) * Fix incorrect scoping of `:global()` selectors ([#6550](https://github.com/sveltejs/svelte/issues/6550)) -* Add filename to combined source map if needed ([#6089](https://github.com/sveltejs/svelte/issues/6089)) -* Fix tracking whether transition has started ([#6399](https://github.com/sveltejs/svelte/issues/6399)) ## 3.40.1 From fd8c5885c869fe8ab3e1fcdbe2245d93ccf6f838 Mon Sep 17 00:00:00 2001 From: Yuichiro Yamashita Date: Sat, 24 Jul 2021 03:57:47 +0900 Subject: [PATCH 44/69] [fix] create AST node of empty string for empty attribute values (#6539) Co-authored-by: Conduitry --- src/compiler/parse/state/tag.ts | 9 +++ .../input.svelte | 2 + .../output.json | 64 +++++++++++++++++++ .../element-with-attribute/input.svelte | 2 + .../element-with-attribute/output.json | 64 +++++++++++++++++++ 5 files changed, 141 insertions(+) create mode 100644 test/parser/samples/element-with-attribute-empty-string/input.svelte create mode 100644 test/parser/samples/element-with-attribute-empty-string/output.json create mode 100644 test/parser/samples/element-with-attribute/input.svelte create mode 100644 test/parser/samples/element-with-attribute/output.json diff --git a/src/compiler/parse/state/tag.ts b/src/compiler/parse/state/tag.ts index 7999c34f84..28821edb01 100644 --- a/src/compiler/parse/state/tag.ts +++ b/src/compiler/parse/state/tag.ts @@ -422,6 +422,15 @@ function get_directive_type(name: string): DirectiveType { function read_attribute_value(parser: Parser) { const quote_mark = parser.eat("'") ? "'" : parser.eat('"') ? '"' : null; + if (quote_mark && parser.eat(quote_mark)) { + return [{ + start: parser.index - 1, + end: parser.index - 1, + type: 'Text', + raw: '', + data: '' + }]; + } const regex = ( quote_mark === "'" ? /'/ : diff --git a/test/parser/samples/element-with-attribute-empty-string/input.svelte b/test/parser/samples/element-with-attribute-empty-string/input.svelte new file mode 100644 index 0000000000..685c0995d5 --- /dev/null +++ b/test/parser/samples/element-with-attribute-empty-string/input.svelte @@ -0,0 +1,2 @@ + + diff --git a/test/parser/samples/element-with-attribute-empty-string/output.json b/test/parser/samples/element-with-attribute-empty-string/output.json new file mode 100644 index 0000000000..8e8768eb62 --- /dev/null +++ b/test/parser/samples/element-with-attribute-empty-string/output.json @@ -0,0 +1,64 @@ +{ + "html": { + "start": 0, + "end": 43, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 21, + "type": "Element", + "name": "span", + "attributes": [ + { + "start": 6, + "end": 13, + "type": "Attribute", + "name": "attr", + "value": [ + { + "start": 12, + "end": 12, + "type": "Text", + "raw": "", + "data": "" + } + ] + } + ], + "children": [] + }, + { + "start": 21, + "end": 22, + "type": "Text", + "raw": "\n", + "data": "\n" + }, + { + "start": 22, + "end": 43, + "type": "Element", + "name": "span", + "attributes": [ + { + "start": 28, + "end": 35, + "type": "Attribute", + "name": "attr", + "value": [ + { + "start": 34, + "end": 34, + "type": "Text", + "raw": "", + "data": "" + } + ] + } + ], + "children": [] + } + ] + } +} diff --git a/test/parser/samples/element-with-attribute/input.svelte b/test/parser/samples/element-with-attribute/input.svelte new file mode 100644 index 0000000000..f564fd0541 --- /dev/null +++ b/test/parser/samples/element-with-attribute/input.svelte @@ -0,0 +1,2 @@ + + diff --git a/test/parser/samples/element-with-attribute/output.json b/test/parser/samples/element-with-attribute/output.json new file mode 100644 index 0000000000..a80344101e --- /dev/null +++ b/test/parser/samples/element-with-attribute/output.json @@ -0,0 +1,64 @@ +{ + "html": { + "start": 0, + "end": 49, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 24, + "type": "Element", + "name": "span", + "attributes": [ + { + "start": 6, + "end": 16, + "type": "Attribute", + "name": "attr", + "value": [ + { + "start": 12, + "end": 15, + "type": "Text", + "raw": "foo", + "data": "foo" + } + ] + } + ], + "children": [] + }, + { + "start": 24, + "end": 25, + "type": "Text", + "raw": "\n", + "data": "\n" + }, + { + "start": 25, + "end": 49, + "type": "Element", + "name": "span", + "attributes": [ + { + "start": 31, + "end": 41, + "type": "Attribute", + "name": "attr", + "value": [ + { + "start": 37, + "end": 40, + "type": "Text", + "raw": "bar", + "data": "bar" + } + ] + } + ], + "children": [] + } + ] + } +} From 7eeb4dfa6859564d2ce26404a711db3c12346660 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Fri, 23 Jul 2021 14:59:32 -0400 Subject: [PATCH 45/69] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a4a3ea13c..b821a6568f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Fix dynamic `autofocus={...}` attribute handling ([#4995](https://github.com/sveltejs/svelte/issues/4995)) * Add filename to combined source map if needed ([#6089](https://github.com/sveltejs/svelte/pull/6089)) +* In AST, parse empty attribute values as an empty string ([#6286](https://github.com/sveltejs/svelte/issues/6286)) * Fix tracking whether transition has started ([#6399](https://github.com/sveltejs/svelte/pull/6399)) * Fix incorrect scoping of `:global()` selectors ([#6550](https://github.com/sveltejs/svelte/issues/6550)) From 3b97329b82e51b277eb483cc3bd30f2933a91f17 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Fri, 23 Jul 2021 15:02:06 -0400 Subject: [PATCH 46/69] -> v3.40.2 --- CHANGELOG.md | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b821a6568f..56390f7b25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Svelte changelog -## Unreleased +## 3.40.2 * Fix dynamic `autofocus={...}` attribute handling ([#4995](https://github.com/sveltejs/svelte/issues/4995)) * Add filename to combined source map if needed ([#6089](https://github.com/sveltejs/svelte/pull/6089)) diff --git a/package-lock.json b/package-lock.json index 6c45aed82b..d1d890e4a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.40.1", + "version": "3.40.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 295a0cbe87..9b927795a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.40.1", + "version": "3.40.2", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index", From 63f592e7133bbb5730ceb61ca8184854fed52edb Mon Sep 17 00:00:00 2001 From: Stephane Date: Fri, 23 Jul 2021 22:21:37 +0200 Subject: [PATCH 47/69] [docs] Add clarification about reactivity and arrays (#6547) --- site/content/docs/01-component-format.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/site/content/docs/01-component-format.md b/site/content/docs/01-component-format.md index 0d8cc6a43e..546715eb56 100644 --- a/site/content/docs/01-component-format.md +++ b/site/content/docs/01-component-format.md @@ -95,8 +95,6 @@ To change component state and trigger a re-render, just assign to a locally decl Update expressions (`count += 1`) and property assignments (`obj.x = y`) have the same effect. -Because Svelte's reactivity is based on assignments, using array methods like `.push()` and `.splice()` won't automatically trigger updates. Options for getting around this can be found in the [tutorial](tutorial/updating-arrays-and-objects). - ```sv ``` +--- + +Because Svelte's reactivity is based on assignments, using array methods like `.push()` and `.splice()` won't automatically trigger updates. A subsequent assignment is required to trigger the update. This and more details can also be found in the [tutorial](tutorial/updating-arrays-and-objects). + +```sv + +``` + #### 3. `$:` marks a statement as reactive --- From 18780fac00ea21d7a21fbf815ffd0cd5048e5185 Mon Sep 17 00:00:00 2001 From: Konstantin BIFERT Date: Mon, 26 Jul 2021 15:46:27 +0200 Subject: [PATCH 48/69] [docs] replace shift by the correct key to hold: control (#6573) --- .../tutorial/06-bindings/07-multiple-select-bindings/text.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/tutorial/06-bindings/07-multiple-select-bindings/text.md b/site/content/tutorial/06-bindings/07-multiple-select-bindings/text.md index 69de470f34..e7b625c6d6 100644 --- a/site/content/tutorial/06-bindings/07-multiple-select-bindings/text.md +++ b/site/content/tutorial/06-bindings/07-multiple-select-bindings/text.md @@ -18,4 +18,4 @@ Returning to our [earlier ice cream example](tutorial/group-inputs), we can repl ``` -> Press and hold the `shift` key for selecting multiple options. +> Press and hold the `control` key for selecting multiple options. From a3fb765d6fe517fd2bf9d44ae4caa4f6cba2b7d5 Mon Sep 17 00:00:00 2001 From: Dennis Dudek Date: Mon, 26 Jul 2021 18:36:46 +0200 Subject: [PATCH 49/69] [docs] add trusted modifier to list of modifiers in tutorial and docs (#6566) --- site/content/docs/02-template-syntax.md | 1 + site/content/tutorial/05-events/03-event-modifiers/text.md | 1 + 2 files changed, 2 insertions(+) diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md index ca0c1d4993..bbf3071583 100644 --- a/site/content/docs/02-template-syntax.md +++ b/site/content/docs/02-template-syntax.md @@ -516,6 +516,7 @@ The following modifiers are available: * `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase * `once` — remove the handler after the first time it runs * `self` — only trigger handler if event.target is the element itself +* `trusted` — only trigger handler if `event.trusted` is `true`. I.e. if the event is triggered by a user action. Modifiers can be chained together, e.g. `on:click|once|capture={...}`. diff --git a/site/content/tutorial/05-events/03-event-modifiers/text.md b/site/content/tutorial/05-events/03-event-modifiers/text.md index 2b2d6e6b31..f6a4b5784d 100644 --- a/site/content/tutorial/05-events/03-event-modifiers/text.md +++ b/site/content/tutorial/05-events/03-event-modifiers/text.md @@ -25,5 +25,6 @@ The full list of modifiers: * `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase ([MDN docs](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture)) * `once` — remove the handler after the first time it runs * `self` — only trigger handler if event.target is the element itself +* `trusted` — only trigger handler if `event.trusted` is `true`. I.e. if the event is triggered by a user action. You can chain modifiers together, e.g. `on:click|once|capture={...}`. From 1952ea22de02e65552a6261bee431b62760f3b93 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 27 Jul 2021 01:35:12 +0800 Subject: [PATCH 50/69] [fix] no root node for detached dom node (#6570) --- src/runtime/internal/dom.ts | 19 +++++++++---------- src/runtime/internal/style_manager.ts | 4 ++-- .../samples/target-dom-detached/App.svelte | 11 +++++++++++ .../samples/target-dom-detached/_config.js | 16 ++++++++++++++++ .../samples/target-dom-detached/main.svelte | 18 ++++++++++++++++++ test/runtime/samples/target-dom/App.svelte | 11 +++++++++++ test/runtime/samples/target-dom/_config.js | 16 ++++++++++++++++ test/runtime/samples/target-dom/main.svelte | 18 ++++++++++++++++++ .../samples/target-shadow-dom/App.svelte | 11 +++++++++++ .../samples/target-shadow-dom/_config.js | 13 +++++++++++++ .../samples/target-shadow-dom/main.svelte | 19 +++++++++++++++++++ 11 files changed, 144 insertions(+), 12 deletions(-) create mode 100644 test/runtime/samples/target-dom-detached/App.svelte create mode 100644 test/runtime/samples/target-dom-detached/_config.js create mode 100644 test/runtime/samples/target-dom-detached/main.svelte create mode 100644 test/runtime/samples/target-dom/App.svelte create mode 100644 test/runtime/samples/target-dom/_config.js create mode 100644 test/runtime/samples/target-dom/main.svelte create mode 100644 test/runtime/samples/target-shadow-dom/App.svelte create mode 100644 test/runtime/samples/target-shadow-dom/_config.js create mode 100644 test/runtime/samples/target-shadow-dom/main.svelte diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index e9976a7fa6..f563f6d099 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -134,9 +134,9 @@ export function append_styles( style_sheet_id: string, styles: string ) { - const append_styles_to = get_root_for_styles(target); + const append_styles_to = get_root_for_style(target); - if (!append_styles_to?.getElementById(style_sheet_id)) { + if (!append_styles_to.getElementById(style_sheet_id)) { const style = element('style'); style.id = style_sheet_id; style.textContent = styles; @@ -144,20 +144,19 @@ export function append_styles( } } -export function get_root_for_node(node: Node) { +export function get_root_for_style(node: Node): ShadowRoot | Document { if (!node) return document; - return (node.getRootNode ? node.getRootNode() : node.ownerDocument); // check for getRootNode because IE is still supported -} - -function get_root_for_styles(node: Node) { - const root = get_root_for_node(node); - return (root as ShadowRoot).host ? root as ShadowRoot : root as Document; + const root = node.getRootNode ? node.getRootNode() : node.ownerDocument; + if ((root as ShadowRoot).host) { + return root as ShadowRoot; + } + return document; } export function append_empty_stylesheet(node: Node) { const style_element = element('style') as HTMLStyleElement; - append_stylesheet(get_root_for_styles(node), style_element); + append_stylesheet(get_root_for_style(node), style_element); return style_element; } diff --git a/src/runtime/internal/style_manager.ts b/src/runtime/internal/style_manager.ts index a646c9b916..0993b3bf18 100644 --- a/src/runtime/internal/style_manager.ts +++ b/src/runtime/internal/style_manager.ts @@ -1,4 +1,4 @@ -import { append_empty_stylesheet, get_root_for_node } from './dom'; +import { append_empty_stylesheet, get_root_for_style } from './dom'; import { raf } from './environment'; interface ExtendedDoc extends Document { @@ -29,7 +29,7 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`; const name = `__svelte_${hash(rule)}_${uid}`; - const doc = get_root_for_node(node) as unknown as ExtendedDoc; + const doc = get_root_for_style(node) as ExtendedDoc; active_docs.add(doc); const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = append_empty_stylesheet(node).sheet as CSSStyleSheet); const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {}); diff --git a/test/runtime/samples/target-dom-detached/App.svelte b/test/runtime/samples/target-dom-detached/App.svelte new file mode 100644 index 0000000000..251e480307 --- /dev/null +++ b/test/runtime/samples/target-dom-detached/App.svelte @@ -0,0 +1,11 @@ + + +
Hello {name}
+ + \ No newline at end of file diff --git a/test/runtime/samples/target-dom-detached/_config.js b/test/runtime/samples/target-dom-detached/_config.js new file mode 100644 index 0000000000..b63656530c --- /dev/null +++ b/test/runtime/samples/target-dom-detached/_config.js @@ -0,0 +1,16 @@ +export default { + skip_if_ssr: true, + compileOptions: { + cssHash: () => 'svelte-xyz' + }, + async test({ assert, component, target, window }) { + assert.htmlEqual( + window.document.head.innerHTML, + '' + ); + assert.htmlEqual( + component.div.innerHTML, + '
Hello World
' + ); + } +}; diff --git a/test/runtime/samples/target-dom-detached/main.svelte b/test/runtime/samples/target-dom-detached/main.svelte new file mode 100644 index 0000000000..42e7dffee9 --- /dev/null +++ b/test/runtime/samples/target-dom-detached/main.svelte @@ -0,0 +1,18 @@ + diff --git a/test/runtime/samples/target-dom/App.svelte b/test/runtime/samples/target-dom/App.svelte new file mode 100644 index 0000000000..251e480307 --- /dev/null +++ b/test/runtime/samples/target-dom/App.svelte @@ -0,0 +1,11 @@ + + +
Hello {name}
+ + \ No newline at end of file diff --git a/test/runtime/samples/target-dom/_config.js b/test/runtime/samples/target-dom/_config.js new file mode 100644 index 0000000000..b63656530c --- /dev/null +++ b/test/runtime/samples/target-dom/_config.js @@ -0,0 +1,16 @@ +export default { + skip_if_ssr: true, + compileOptions: { + cssHash: () => 'svelte-xyz' + }, + async test({ assert, component, target, window }) { + assert.htmlEqual( + window.document.head.innerHTML, + '' + ); + assert.htmlEqual( + component.div.innerHTML, + '
Hello World
' + ); + } +}; diff --git a/test/runtime/samples/target-dom/main.svelte b/test/runtime/samples/target-dom/main.svelte new file mode 100644 index 0000000000..68d2990552 --- /dev/null +++ b/test/runtime/samples/target-dom/main.svelte @@ -0,0 +1,18 @@ + + +
\ No newline at end of file diff --git a/test/runtime/samples/target-shadow-dom/App.svelte b/test/runtime/samples/target-shadow-dom/App.svelte new file mode 100644 index 0000000000..251e480307 --- /dev/null +++ b/test/runtime/samples/target-shadow-dom/App.svelte @@ -0,0 +1,11 @@ + + +
Hello {name}
+ + \ No newline at end of file diff --git a/test/runtime/samples/target-shadow-dom/_config.js b/test/runtime/samples/target-shadow-dom/_config.js new file mode 100644 index 0000000000..cec383afd0 --- /dev/null +++ b/test/runtime/samples/target-shadow-dom/_config.js @@ -0,0 +1,13 @@ +export default { + skip_if_ssr: true, + compileOptions: { + cssHash: () => 'svelte-xyz' + }, + async test({ assert, component, target, window }) { + assert.htmlEqual(window.document.head.innerHTML, ''); + assert.htmlEqual(component.div.shadowRoot.innerHTML, ` + +
Hello World
+ `); + } +}; diff --git a/test/runtime/samples/target-shadow-dom/main.svelte b/test/runtime/samples/target-shadow-dom/main.svelte new file mode 100644 index 0000000000..cb47ad01f0 --- /dev/null +++ b/test/runtime/samples/target-shadow-dom/main.svelte @@ -0,0 +1,19 @@ + + +
\ No newline at end of file From 69e3c0fe7f1eacef4ac7be572a39c33f513b73ba Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 26 Jul 2021 13:36:16 -0400 Subject: [PATCH 51/69] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56390f7b25..013357fa12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Svelte changelog +## Unreleased + +* Fix mounting component at detached DOM node ([#6567](https://github.com/sveltejs/svelte/issues/6567)) + ## 3.40.2 * Fix dynamic `autofocus={...}` attribute handling ([#4995](https://github.com/sveltejs/svelte/issues/4995)) From ee769101fc95688c3045f0fd4f47d09b15bc70c3 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 27 Jul 2021 01:42:10 +0800 Subject: [PATCH 52/69] [fix] applying :global for > combinator (#6563) --- src/compiler/compile/css/Selector.ts | 3 ++- .../global-with-child-combinator-2/_config.js | 27 +++++++++++++++++++ .../expected.css | 1 + .../expected.html | 3 +++ .../input.svelte | 15 +++++++++++ .../global-with-child-combinator-3/_config.js | 3 +++ .../expected.css | 1 + .../expected.html | 3 +++ .../input.svelte | 9 +++++++ .../expected.html | 3 +++ 10 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 test/css/samples/global-with-child-combinator-2/_config.js create mode 100644 test/css/samples/global-with-child-combinator-2/expected.css create mode 100644 test/css/samples/global-with-child-combinator-2/expected.html create mode 100644 test/css/samples/global-with-child-combinator-2/input.svelte create mode 100644 test/css/samples/global-with-child-combinator-3/_config.js create mode 100644 test/css/samples/global-with-child-combinator-3/expected.css create mode 100644 test/css/samples/global-with-child-combinator-3/expected.html create mode 100644 test/css/samples/global-with-child-combinator-3/input.svelte create mode 100644 test/css/samples/global-with-child-combinator/expected.html diff --git a/src/compiler/compile/css/Selector.ts b/src/compiler/compile/css/Selector.ts index c88a244a13..d9868f4530 100644 --- a/src/compiler/compile/css/Selector.ts +++ b/src/compiler/compile/css/Selector.ts @@ -227,7 +227,8 @@ function apply_selector(blocks: Block[], node: Element, to_encapsulate: Array<{ return false; } else if (block.combinator.name === '>') { - if (apply_selector(blocks, get_element_parent(node), to_encapsulate)) { + const has_global_parent = blocks.every(block => block.global); + if (has_global_parent || apply_selector(blocks, get_element_parent(node), to_encapsulate)) { to_encapsulate.push({ node, block }); return true; } diff --git a/test/css/samples/global-with-child-combinator-2/_config.js b/test/css/samples/global-with-child-combinator-2/_config.js new file mode 100644 index 0000000000..2286f4fe3c --- /dev/null +++ b/test/css/samples/global-with-child-combinator-2/_config.js @@ -0,0 +1,27 @@ +export default { + warnings: [ + { + code: 'css-unused-selector', + end: { + character: 111, + column: 21, + line: 8 + }, + frame: ` + 6: color: red; + 7: } + 8: a:global(.foo) > div { + ^ + 9: color: red; + 10: } + `, + message: 'Unused CSS selector "a:global(.foo) > div"', + pos: 91, + start: { + character: 91, + column: 1, + line: 8 + } + } + ] +}; diff --git a/test/css/samples/global-with-child-combinator-2/expected.css b/test/css/samples/global-with-child-combinator-2/expected.css new file mode 100644 index 0000000000..3f406244a3 --- /dev/null +++ b/test/css/samples/global-with-child-combinator-2/expected.css @@ -0,0 +1 @@ +div>div.svelte-xyz.svelte-xyz{color:red}div.svelte-xyz.foo>div.svelte-xyz{color:red} \ No newline at end of file diff --git a/test/css/samples/global-with-child-combinator-2/expected.html b/test/css/samples/global-with-child-combinator-2/expected.html new file mode 100644 index 0000000000..32ff99e34f --- /dev/null +++ b/test/css/samples/global-with-child-combinator-2/expected.html @@ -0,0 +1,3 @@ +
+
+
\ No newline at end of file diff --git a/test/css/samples/global-with-child-combinator-2/input.svelte b/test/css/samples/global-with-child-combinator-2/input.svelte new file mode 100644 index 0000000000..caf7c5869a --- /dev/null +++ b/test/css/samples/global-with-child-combinator-2/input.svelte @@ -0,0 +1,15 @@ + + +
+
+
\ No newline at end of file diff --git a/test/css/samples/global-with-child-combinator-3/_config.js b/test/css/samples/global-with-child-combinator-3/_config.js new file mode 100644 index 0000000000..c81f1a9f82 --- /dev/null +++ b/test/css/samples/global-with-child-combinator-3/_config.js @@ -0,0 +1,3 @@ +export default { + warnings: [] +}; diff --git a/test/css/samples/global-with-child-combinator-3/expected.css b/test/css/samples/global-with-child-combinator-3/expected.css new file mode 100644 index 0000000000..11c60c7147 --- /dev/null +++ b/test/css/samples/global-with-child-combinator-3/expected.css @@ -0,0 +1 @@ +a>b>div.svelte-xyz{color:red} \ No newline at end of file diff --git a/test/css/samples/global-with-child-combinator-3/expected.html b/test/css/samples/global-with-child-combinator-3/expected.html new file mode 100644 index 0000000000..32ff99e34f --- /dev/null +++ b/test/css/samples/global-with-child-combinator-3/expected.html @@ -0,0 +1,3 @@ +
+
+
\ No newline at end of file diff --git a/test/css/samples/global-with-child-combinator-3/input.svelte b/test/css/samples/global-with-child-combinator-3/input.svelte new file mode 100644 index 0000000000..146f302633 --- /dev/null +++ b/test/css/samples/global-with-child-combinator-3/input.svelte @@ -0,0 +1,9 @@ + + +
+
+
\ No newline at end of file diff --git a/test/css/samples/global-with-child-combinator/expected.html b/test/css/samples/global-with-child-combinator/expected.html new file mode 100644 index 0000000000..32ff99e34f --- /dev/null +++ b/test/css/samples/global-with-child-combinator/expected.html @@ -0,0 +1,3 @@ +
+
+
\ No newline at end of file From 1750feb8f61a0884532b07a3f93fcb4bf4d59472 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 26 Jul 2021 13:42:56 -0400 Subject: [PATCH 53/69] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 013357fa12..f7355d950d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* Fix applying `:global()` for `>` selector combinator ([#6550](https://github.com/sveltejs/svelte/issues/6550)) * Fix mounting component at detached DOM node ([#6567](https://github.com/sveltejs/svelte/issues/6567)) ## 3.40.2 From 9501ac62573b716577ff13471d4e506ddc42c469 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 27 Jul 2021 01:44:15 +0800 Subject: [PATCH 54/69] [fix] destructuring store assignment (#6529) --- src/runtime/internal/utils.ts | 2 +- .../_config.js | 11 ++++++ .../main.svelte | 34 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 test/runtime/samples/store-assignment-updates-destructure/_config.js create mode 100644 test/runtime/samples/store-assignment-updates-destructure/main.svelte diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index f487732b77..646851d0a5 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -169,7 +169,7 @@ export function null_to_empty(value) { return value == null ? '' : value; } -export function set_store_value(store, ret, value = ret) { +export function set_store_value(store, ret, value) { store.set(value); return ret; } diff --git a/test/runtime/samples/store-assignment-updates-destructure/_config.js b/test/runtime/samples/store-assignment-updates-destructure/_config.js new file mode 100644 index 0000000000..7a1023614c --- /dev/null +++ b/test/runtime/samples/store-assignment-updates-destructure/_config.js @@ -0,0 +1,11 @@ +export default { + html: ` +
$userName1: user1
+
$userName2: undefined
+
$userName3: undefined
+
$userName4: user4
+
$userName5: undefined
+
$userName6: user6
+
$userName7: undefined
+ ` +}; diff --git a/test/runtime/samples/store-assignment-updates-destructure/main.svelte b/test/runtime/samples/store-assignment-updates-destructure/main.svelte new file mode 100644 index 0000000000..71e02fe16d --- /dev/null +++ b/test/runtime/samples/store-assignment-updates-destructure/main.svelte @@ -0,0 +1,34 @@ + + +
$userName1: {$userName1}
+
$userName2: {$userName2}
+
$userName3: {$userName3}
+
$userName4: {$userName4}
+
$userName5: {$userName5}
+
$userName6: {$userName6}
+
$userName7: {$userName7}
From 0d9dd153924aa0a5fab6cbf9a6b24de3776d83f3 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 26 Jul 2021 13:50:23 -0400 Subject: [PATCH 55/69] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7355d950d..72fc88ff44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* Fix destructuring into variables beginning with `$` so that they result in store updates ([#5653](https://github.com/sveltejs/svelte/issues/5653)) * Fix applying `:global()` for `>` selector combinator ([#6550](https://github.com/sveltejs/svelte/issues/6550)) * Fix mounting component at detached DOM node ([#6567](https://github.com/sveltejs/svelte/issues/6567)) From c8732c8a276725025f263575c5b6f1f5dd8d75b0 Mon Sep 17 00:00:00 2001 From: Yuichiro Yamashita Date: Tue, 27 Jul 2021 02:56:38 +0900 Subject: [PATCH 56/69] [fix] create in transition even if intro is initialized (#6516) --- .../render_dom/wrappers/Element/index.ts | 2 +- .../_config.js | 21 +++++++++++++++ .../main.svelte | 26 +++++++++++++++++++ .../transition-css-in-out-in/_config.js | 2 +- 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 test/runtime/samples/transition-css-in-out-in-with-param/_config.js create mode 100644 test/runtime/samples/transition-css-in-out-in-with-param/main.svelte diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 62c45c093d..db26b6673c 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -760,7 +760,7 @@ export default class ElementWrapper extends Wrapper { intro_block = b` @add_render_callback(() => { if (${outro_name}) ${outro_name}.end(1); - if (!${intro_name}) ${intro_name} = @create_in_transition(${this.var}, ${fn}, ${snippet}); + ${intro_name} = @create_in_transition(${this.var}, ${fn}, ${snippet}); ${intro_name}.start(); }); `; diff --git a/test/runtime/samples/transition-css-in-out-in-with-param/_config.js b/test/runtime/samples/transition-css-in-out-in-with-param/_config.js new file mode 100644 index 0000000000..c5dfb63225 --- /dev/null +++ b/test/runtime/samples/transition-css-in-out-in-with-param/_config.js @@ -0,0 +1,21 @@ +export default { + test({ assert, component, target, window, raf }) { + component.visible = true; + const div = target.querySelector('div'); + + // animation duration of `in` should be 10ms. + assert.equal(div.style.animation, '__svelte_1670736059_0 10ms linear 0ms 1 both'); + + // animation duration of `out` should be 5ms. + component.visible = false; + assert.equal(div.style.animation, '__svelte_1670736059_0 10ms linear 0ms 1 both, __svelte_1998461463_0 5ms linear 0ms 1 both'); + + // change param + raf.tick(1); + component.param = true; + component.visible = true; + + // animation duration of `in` should be 20ms. + assert.equal(div.style.animation, '__svelte_722598827_0 20ms linear 0ms 1 both'); + } +}; diff --git a/test/runtime/samples/transition-css-in-out-in-with-param/main.svelte b/test/runtime/samples/transition-css-in-out-in-with-param/main.svelte new file mode 100644 index 0000000000..616e2e0e8b --- /dev/null +++ b/test/runtime/samples/transition-css-in-out-in-with-param/main.svelte @@ -0,0 +1,26 @@ + + +{#if visible} +
+{/if} \ No newline at end of file diff --git a/test/runtime/samples/transition-css-in-out-in/_config.js b/test/runtime/samples/transition-css-in-out-in/_config.js index cd7ae14ce8..6d93c0e8c3 100644 --- a/test/runtime/samples/transition-css-in-out-in/_config.js +++ b/test/runtime/samples/transition-css-in-out-in/_config.js @@ -15,6 +15,6 @@ export default { component.visible = true; // reset original styles - assert.equal(div.style.animation, '__svelte_3809512021_1 100ms linear 0ms 1 both'); + assert.equal(div.style.animation, '__svelte_3809512021_0 100ms linear 0ms 1 both'); } }; From 588b37f81060a9a68e6d7f3fe95d13a0fc5fa53b Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 26 Jul 2021 13:58:36 -0400 Subject: [PATCH 57/69] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72fc88ff44..fb0f04019b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Fix destructuring into variables beginning with `$` so that they result in store updates ([#5653](https://github.com/sveltejs/svelte/issues/5653)) +* Fix `in:` transition configuration not properly updating when it's changed after its initial creation ([#6505](https://github.com/sveltejs/svelte/issues/6505)) * Fix applying `:global()` for `>` selector combinator ([#6550](https://github.com/sveltejs/svelte/issues/6550)) * Fix mounting component at detached DOM node ([#6567](https://github.com/sveltejs/svelte/issues/6567)) From d75ed6a00302da71015c86b603c2feca7e601a8c Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 27 Jul 2021 02:03:35 +0800 Subject: [PATCH 58/69] [fix] slot data for cancelled transition (#6314) --- .../compile/render_dom/wrappers/Slot.ts | 42 ++++++++++++------- src/runtime/internal/utils.ts | 22 ++++++---- .../samples/transition-js-slot-2/_config.js | 1 + .../Nested.svelte | 19 +++++++++ .../transition-js-slot-4-cancelled/_config.js | 35 ++++++++++++++++ .../main.svelte | 22 ++++++++++ .../Nested.svelte | 19 +++++++++ .../_config.js | 40 ++++++++++++++++++ .../main.svelte | 27 ++++++++++++ .../Nested.svelte | 19 +++++++++ .../Nested2.svelte | 19 +++++++++ .../_config.js | 40 ++++++++++++++++++ .../main.svelte | 26 ++++++++++++ .../Nested.svelte | 19 +++++++++ .../_config.js | 40 ++++++++++++++++++ .../main.svelte | 27 ++++++++++++ 16 files changed, 396 insertions(+), 21 deletions(-) create mode 100644 test/runtime/samples/transition-js-slot-4-cancelled/Nested.svelte create mode 100644 test/runtime/samples/transition-js-slot-4-cancelled/_config.js create mode 100644 test/runtime/samples/transition-js-slot-4-cancelled/main.svelte create mode 100644 test/runtime/samples/transition-js-slot-5-cancelled-overflow/Nested.svelte create mode 100644 test/runtime/samples/transition-js-slot-5-cancelled-overflow/_config.js create mode 100644 test/runtime/samples/transition-js-slot-5-cancelled-overflow/main.svelte create mode 100644 test/runtime/samples/transition-js-slot-6-spread-cancelled/Nested.svelte create mode 100644 test/runtime/samples/transition-js-slot-6-spread-cancelled/Nested2.svelte create mode 100644 test/runtime/samples/transition-js-slot-6-spread-cancelled/_config.js create mode 100644 test/runtime/samples/transition-js-slot-6-spread-cancelled/main.svelte create mode 100644 test/runtime/samples/transition-js-slot-7-spread-cancelled-overflow/Nested.svelte create mode 100644 test/runtime/samples/transition-js-slot-7-spread-cancelled-overflow/_config.js create mode 100644 test/runtime/samples/transition-js-slot-7-spread-cancelled-overflow/main.svelte diff --git a/src/compiler/compile/render_dom/wrappers/Slot.ts b/src/compiler/compile/render_dom/wrappers/Slot.ts index 937a75b0aa..09366dcaaf 100644 --- a/src/compiler/compile/render_dom/wrappers/Slot.ts +++ b/src/compiler/compile/render_dom/wrappers/Slot.ts @@ -107,7 +107,7 @@ export default class SlotWrapper extends Wrapper { if (spread_dynamic_dependencies.size) { get_slot_spread_changes_fn = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_spread_changes`); renderer.blocks.push(b` - const ${get_slot_spread_changes_fn} = #dirty => ${renderer.dirty(Array.from(spread_dynamic_dependencies))} > 0 ? -1 : 0; + const ${get_slot_spread_changes_fn} = #dirty => ${renderer.dirty(Array.from(spread_dynamic_dependencies))}; `); } } else { @@ -168,27 +168,41 @@ export default class SlotWrapper extends Wrapper { if (block.has_outros) { condition = x`!#current || ${condition}`; } - let dirty = x`#dirty`; - if (block.has_outros) { - dirty = x`!#current ? ${renderer.get_initial_dirty()} : ${dirty}`; + + // conditions to treat everything as dirty + const all_dirty_conditions = [ + get_slot_spread_changes_fn ? x`${get_slot_spread_changes_fn}(#dirty)` : null, + block.has_outros ? x`!#current` : null + ].filter(Boolean); + const all_dirty_condition = all_dirty_conditions.length ? all_dirty_conditions.reduce((condition1, condition2) => x`${condition1} || ${condition2}`): null; + + let slot_update; + if (all_dirty_condition) { + const dirty = x`${all_dirty_condition} ? @get_all_dirty_from_scope(${renderer.reference('$$scope')}) : @get_slot_changes(${slot_definition}, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn})`; + + slot_update = b` + if (${slot}.p && ${condition}) { + @update_slot_base(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${dirty}, ${get_slot_context_fn}); + } + `; + } else { + slot_update = b` + if (${slot}.p && ${condition}) { + @update_slot(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn}, ${get_slot_context_fn}); + } + `; } - const slot_update = get_slot_spread_changes_fn ? b` - if (${slot}.p && ${condition}) { - @update_slot_spread(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${dirty}, ${get_slot_changes_fn}, ${get_slot_spread_changes_fn}, ${get_slot_context_fn}); - } - ` : b` - if (${slot}.p && ${condition}) { - @update_slot(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${dirty}, ${get_slot_changes_fn}, ${get_slot_context_fn}); - } - `; let fallback_condition = renderer.dirty(fallback_dynamic_dependencies); + let fallback_dirty = x`#dirty`; if (block.has_outros) { fallback_condition = x`!#current || ${fallback_condition}`; + fallback_dirty = x`!#current ? ${renderer.get_initial_dirty()} : ${fallback_dirty}`; } + const fallback_update = has_fallback && fallback_dynamic_dependencies.length > 0 && b` if (${slot_or_fallback} && ${slot_or_fallback}.p && ${fallback_condition}) { - ${slot_or_fallback}.p(#ctx, ${dirty}); + ${slot_or_fallback}.p(#ctx, ${fallback_dirty}); } `; diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index 646851d0a5..8868e38ee2 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -119,20 +119,28 @@ export function get_slot_changes(definition, $$scope, dirty, fn) { return $$scope.dirty; } -export function update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_context_fn) { - const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn); +export function update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn) { if (slot_changes) { const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn); slot.p(slot_context, slot_changes); } } -export function update_slot_spread(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_spread_changes_fn, get_slot_context_fn) { - const slot_changes = get_slot_spread_changes_fn(dirty) | get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn); - if (slot_changes) { - const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn); - slot.p(slot_context, slot_changes); +export function update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_context_fn) { + const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn); + update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn); +} + +export function get_all_dirty_from_scope($$scope) { + if ($$scope.ctx.length > 32) { + const dirty = []; + const length = $$scope.ctx.length / 32; + for (let i = 0; i < length; i++) { + dirty[i] = -1; + } + return dirty; } + return -1; } export function exclude_internal_props(props) { diff --git a/test/runtime/samples/transition-js-slot-2/_config.js b/test/runtime/samples/transition-js-slot-2/_config.js index 67cc0b46d2..0f0cad5e41 100644 --- a/test/runtime/samples/transition-js-slot-2/_config.js +++ b/test/runtime/samples/transition-js-slot-2/_config.js @@ -1,3 +1,4 @@ +// cancelled the transition halfway export default { html: `
Foo
diff --git a/test/runtime/samples/transition-js-slot-4-cancelled/Nested.svelte b/test/runtime/samples/transition-js-slot-4-cancelled/Nested.svelte new file mode 100644 index 0000000000..04eea750fd --- /dev/null +++ b/test/runtime/samples/transition-js-slot-4-cancelled/Nested.svelte @@ -0,0 +1,19 @@ + + +{#if visible} +
+ +
+{/if} \ No newline at end of file diff --git a/test/runtime/samples/transition-js-slot-4-cancelled/_config.js b/test/runtime/samples/transition-js-slot-4-cancelled/_config.js new file mode 100644 index 0000000000..d6b7c31132 --- /dev/null +++ b/test/runtime/samples/transition-js-slot-4-cancelled/_config.js @@ -0,0 +1,35 @@ +// updated props in the middle of transitions +// and cancelled the transition halfway +export default { + html: ` +
outside Foo Foo Foo
+
inside Foo Foo Foo
+ `, + props: { + props: 'Foo' + }, + + async test({ assert, component, target, window, raf }) { + await component.hide(); + const [, div] = target.querySelectorAll('div'); + + raf.tick(50); + assert.equal(div.foo, 0.5); + + component.props = 'Bar'; + assert.htmlEqual(target.innerHTML, ` +
outside Bar Bar Bar
+
inside Foo Foo Foo
+ `); + + await component.show(); + + assert.htmlEqual(target.innerHTML, ` +
outside Bar Bar Bar
+
inside Bar Bar Bar
+ `); + + raf.tick(100); + assert.equal(div.foo, 1); + } +}; diff --git a/test/runtime/samples/transition-js-slot-4-cancelled/main.svelte b/test/runtime/samples/transition-js-slot-4-cancelled/main.svelte new file mode 100644 index 0000000000..1003419244 --- /dev/null +++ b/test/runtime/samples/transition-js-slot-4-cancelled/main.svelte @@ -0,0 +1,22 @@ + + +
outside {state} {props} {slotProps}
+ + inside {state} {props} {slotProps} + diff --git a/test/runtime/samples/transition-js-slot-5-cancelled-overflow/Nested.svelte b/test/runtime/samples/transition-js-slot-5-cancelled-overflow/Nested.svelte new file mode 100644 index 0000000000..10050b6a10 --- /dev/null +++ b/test/runtime/samples/transition-js-slot-5-cancelled-overflow/Nested.svelte @@ -0,0 +1,19 @@ + + +{#if visible} +
+ +
+{/if} diff --git a/test/runtime/samples/transition-js-slot-5-cancelled-overflow/_config.js b/test/runtime/samples/transition-js-slot-5-cancelled-overflow/_config.js new file mode 100644 index 0000000000..aedc87f0b6 --- /dev/null +++ b/test/runtime/samples/transition-js-slot-5-cancelled-overflow/_config.js @@ -0,0 +1,40 @@ +// updated props in the middle of transitions +// and cancelled the transition halfway +// + spreaded props + overflow context + +export default { + html: ` +
outside Foo Foo Foo
+
inside Foo Foo Foo
+ 0 + `, + props: { + props: 'Foo' + }, + + async test({ assert, component, target, window, raf }) { + await component.hide(); + const [, div] = target.querySelectorAll('div'); + + raf.tick(50); + assert.equal(div.foo, 0.5); + + component.props = 'Bar'; + assert.htmlEqual(target.innerHTML, ` +
outside Bar Bar Bar
+
inside Foo Foo Foo
+ 0 + `); + + await component.show(); + + assert.htmlEqual(target.innerHTML, ` +
outside Bar Bar Bar
+
inside Bar Bar Bar
+ 0 + `); + + raf.tick(100); + assert.equal(div.foo, 1); + } +}; diff --git a/test/runtime/samples/transition-js-slot-5-cancelled-overflow/main.svelte b/test/runtime/samples/transition-js-slot-5-cancelled-overflow/main.svelte new file mode 100644 index 0000000000..9c46fd0521 --- /dev/null +++ b/test/runtime/samples/transition-js-slot-5-cancelled-overflow/main.svelte @@ -0,0 +1,27 @@ + + +
outside {state} {props} {slotProps}
+ + + inside {state} {props} {slotProps} + + +{a1+a2+a3+a4+a5+a6+a7+a8+a9+a10+a11+a12+a13+a14+a15+a16+a17+a18+a19+a20+a21+a22+a23+a24+a25+a26+a27+a28+a29+a30+a31+a32+a33} \ No newline at end of file diff --git a/test/runtime/samples/transition-js-slot-6-spread-cancelled/Nested.svelte b/test/runtime/samples/transition-js-slot-6-spread-cancelled/Nested.svelte new file mode 100644 index 0000000000..b1853993d3 --- /dev/null +++ b/test/runtime/samples/transition-js-slot-6-spread-cancelled/Nested.svelte @@ -0,0 +1,19 @@ + + +{#if visible} +
+ +
+{/if} \ No newline at end of file diff --git a/test/runtime/samples/transition-js-slot-6-spread-cancelled/Nested2.svelte b/test/runtime/samples/transition-js-slot-6-spread-cancelled/Nested2.svelte new file mode 100644 index 0000000000..52f89858a0 --- /dev/null +++ b/test/runtime/samples/transition-js-slot-6-spread-cancelled/Nested2.svelte @@ -0,0 +1,19 @@ + + +{#if visible} +
+ +
+{/if} \ No newline at end of file diff --git a/test/runtime/samples/transition-js-slot-6-spread-cancelled/_config.js b/test/runtime/samples/transition-js-slot-6-spread-cancelled/_config.js new file mode 100644 index 0000000000..1d42c9cf71 --- /dev/null +++ b/test/runtime/samples/transition-js-slot-6-spread-cancelled/_config.js @@ -0,0 +1,40 @@ +// updated props in the middle of transitions +// and cancelled the transition halfway +// with spreaded props + +export default { + html: ` +
outside Foo Foo Foo
+
inside Foo Foo Foo
+
inside Foo Foo XXX
+ `, + props: { + props: 'Foo' + }, + + async test({ assert, component, target, window, raf }) { + await component.hide(); + const [, div] = target.querySelectorAll('div'); + + raf.tick(50); + assert.equal(div.foo, 0.5); + + component.props = 'Bar'; + assert.htmlEqual(target.innerHTML, ` +
outside Bar Bar Bar
+
inside Foo Foo Foo
+
inside Foo Foo XXX
+ `); + + await component.show(); + + assert.htmlEqual(target.innerHTML, ` +
outside Bar Bar Bar
+
inside Bar Bar Bar
+
inside Bar Bar XXX
+ `); + + raf.tick(100); + assert.equal(div.foo, 1); + } +}; diff --git a/test/runtime/samples/transition-js-slot-6-spread-cancelled/main.svelte b/test/runtime/samples/transition-js-slot-6-spread-cancelled/main.svelte new file mode 100644 index 0000000000..5dd8ce348e --- /dev/null +++ b/test/runtime/samples/transition-js-slot-6-spread-cancelled/main.svelte @@ -0,0 +1,26 @@ + + +
outside {state} {props} {slotProps.slotProps}
+ + inside {state} {props} {slotProps} + + + inside {state} {props} {slotProps} + diff --git a/test/runtime/samples/transition-js-slot-7-spread-cancelled-overflow/Nested.svelte b/test/runtime/samples/transition-js-slot-7-spread-cancelled-overflow/Nested.svelte new file mode 100644 index 0000000000..b01200fd9f --- /dev/null +++ b/test/runtime/samples/transition-js-slot-7-spread-cancelled-overflow/Nested.svelte @@ -0,0 +1,19 @@ + + +{#if visible} +
+ +
+{/if} diff --git a/test/runtime/samples/transition-js-slot-7-spread-cancelled-overflow/_config.js b/test/runtime/samples/transition-js-slot-7-spread-cancelled-overflow/_config.js new file mode 100644 index 0000000000..aedc87f0b6 --- /dev/null +++ b/test/runtime/samples/transition-js-slot-7-spread-cancelled-overflow/_config.js @@ -0,0 +1,40 @@ +// updated props in the middle of transitions +// and cancelled the transition halfway +// + spreaded props + overflow context + +export default { + html: ` +
outside Foo Foo Foo
+
inside Foo Foo Foo
+ 0 + `, + props: { + props: 'Foo' + }, + + async test({ assert, component, target, window, raf }) { + await component.hide(); + const [, div] = target.querySelectorAll('div'); + + raf.tick(50); + assert.equal(div.foo, 0.5); + + component.props = 'Bar'; + assert.htmlEqual(target.innerHTML, ` +
outside Bar Bar Bar
+
inside Foo Foo Foo
+ 0 + `); + + await component.show(); + + assert.htmlEqual(target.innerHTML, ` +
outside Bar Bar Bar
+
inside Bar Bar Bar
+ 0 + `); + + raf.tick(100); + assert.equal(div.foo, 1); + } +}; diff --git a/test/runtime/samples/transition-js-slot-7-spread-cancelled-overflow/main.svelte b/test/runtime/samples/transition-js-slot-7-spread-cancelled-overflow/main.svelte new file mode 100644 index 0000000000..000b29ee7f --- /dev/null +++ b/test/runtime/samples/transition-js-slot-7-spread-cancelled-overflow/main.svelte @@ -0,0 +1,27 @@ + + +
outside {state} {props} {slotProps.slotProps}
+ + + inside {state} {props} {slotProps} + + +{a1+a2+a3+a4+a5+a6+a7+a8+a9+a10+a11+a12+a13+a14+a15+a16+a17+a18+a19+a20+a21+a22+a23+a24+a25+a26+a27+a28+a29+a30+a31+a32+a33} \ No newline at end of file From dd20623cb32ea1a258470284bdbb7300f7802dce Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 26 Jul 2021 14:07:57 -0400 Subject: [PATCH 59/69] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb0f04019b..b7f0bf7ab3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* Fix `` data when a transition is cancelled before completing ([#5394](https://github.com/sveltejs/svelte/issues/5394)) * Fix destructuring into variables beginning with `$` so that they result in store updates ([#5653](https://github.com/sveltejs/svelte/issues/5653)) * Fix `in:` transition configuration not properly updating when it's changed after its initial creation ([#6505](https://github.com/sveltejs/svelte/issues/6505)) * Fix applying `:global()` for `>` selector combinator ([#6550](https://github.com/sveltejs/svelte/issues/6550)) From bc1556fff33c8a09e85aa77f6fc0dc0870e38606 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 26 Jul 2021 14:16:52 -0400 Subject: [PATCH 60/69] fix lint --- src/compiler/compile/render_dom/wrappers/Slot.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/compile/render_dom/wrappers/Slot.ts b/src/compiler/compile/render_dom/wrappers/Slot.ts index 09366dcaaf..5a778ba5fb 100644 --- a/src/compiler/compile/render_dom/wrappers/Slot.ts +++ b/src/compiler/compile/render_dom/wrappers/Slot.ts @@ -92,7 +92,7 @@ export default class SlotWrapper extends Wrapper { add_to_set(spread_dynamic_dependencies, Array.from(attribute.dependencies).filter((name) => this.is_dependency_dynamic(name))); } else { const dynamic_dependencies = Array.from(attribute.dependencies).filter((name) => this.is_dependency_dynamic(name)); - + if (dynamic_dependencies.length > 0) { changes.properties.push(p`${attribute.name}: ${renderer.dirty(dynamic_dependencies)}`); } @@ -174,7 +174,7 @@ export default class SlotWrapper extends Wrapper { get_slot_spread_changes_fn ? x`${get_slot_spread_changes_fn}(#dirty)` : null, block.has_outros ? x`!#current` : null ].filter(Boolean); - const all_dirty_condition = all_dirty_conditions.length ? all_dirty_conditions.reduce((condition1, condition2) => x`${condition1} || ${condition2}`): null; + const all_dirty_condition = all_dirty_conditions.length ? all_dirty_conditions.reduce((condition1, condition2) => x`${condition1} || ${condition2}`) : null; let slot_update; if (all_dirty_condition) { @@ -199,7 +199,7 @@ export default class SlotWrapper extends Wrapper { fallback_condition = x`!#current || ${fallback_condition}`; fallback_dirty = x`!#current ? ${renderer.get_initial_dirty()} : ${fallback_dirty}`; } - + const fallback_update = has_fallback && fallback_dynamic_dependencies.length > 0 && b` if (${slot_or_fallback} && ${slot_or_fallback}.p && ${fallback_condition}) { ${slot_or_fallback}.p(#ctx, ${fallback_dirty}); From 5cb4101fe6a4ea37962ccc93f04f4774950abd90 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Mon, 26 Jul 2021 14:17:01 -0400 Subject: [PATCH 61/69] -> v3.40.3 --- CHANGELOG.md | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7f0bf7ab3..7aa192fef4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Svelte changelog -## Unreleased +## 3.40.3 * Fix `` data when a transition is cancelled before completing ([#5394](https://github.com/sveltejs/svelte/issues/5394)) * Fix destructuring into variables beginning with `$` so that they result in store updates ([#5653](https://github.com/sveltejs/svelte/issues/5653)) diff --git a/package-lock.json b/package-lock.json index d1d890e4a4..e059174ea6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.40.2", + "version": "3.40.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 9b927795a7..6f6303348f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.40.2", + "version": "3.40.3", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index", From 4d677d56433b3ebb6d032aed86201f86323bfdcd Mon Sep 17 00:00:00 2001 From: Luke Rhoads <51463884+lukerhoads@users.noreply.github.com> Date: Mon, 26 Jul 2021 23:56:16 -0500 Subject: [PATCH 62/69] [chore] add space to improve formatting in type definitions file (#6577) --- src/compiler/interfaces.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts index c9eb9e0236..73be74bef9 100644 --- a/src/compiler/interfaces.ts +++ b/src/compiler/interfaces.ts @@ -46,7 +46,7 @@ interface BaseDirective extends BaseNode { modifiers: string[]; } -export interface Transition extends BaseDirective{ +export interface Transition extends BaseDirective { type: 'Transition'; intro: boolean; outro: boolean; From c550f604f28354031be4aa40f4b5461ce2989b4b Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 27 Jul 2021 21:37:37 +0800 Subject: [PATCH 63/69] [feat] enable export ... from (#6574) --- src/compiler/compile/Component.ts | 28 ++++++++----- src/compiler/compile/compiler_errors.ts | 4 -- src/compiler/compile/create_module.ts | 33 +++++++++++---- src/compiler/compile/render_dom/index.ts | 42 ++++++++++++++++++- .../samples/export-from-accessors/_config.js | 5 +++ .../samples/export-from-accessors/expected.js | 34 +++++++++++++++ .../export-from-accessors/input.svelte | 11 +++++ test/js/samples/export-from-cjs/_config.js | 6 +++ test/js/samples/export-from-cjs/expected.js | 36 ++++++++++++++++ test/js/samples/export-from-cjs/input.svelte | 11 +++++ test/js/samples/export-from/expected.js | 18 ++++++++ test/js/samples/export-from/input.svelte | 11 +++++ test/runtime/samples/export-from/A.svelte | 23 ++++++++++ test/runtime/samples/export-from/B.svelte | 8 ++++ test/runtime/samples/export-from/_config.js | 28 +++++++++++++ test/runtime/samples/export-from/main.svelte | 21 ++++++++++ 16 files changed, 295 insertions(+), 24 deletions(-) create mode 100644 test/js/samples/export-from-accessors/_config.js create mode 100644 test/js/samples/export-from-accessors/expected.js create mode 100644 test/js/samples/export-from-accessors/input.svelte create mode 100644 test/js/samples/export-from-cjs/_config.js create mode 100644 test/js/samples/export-from-cjs/expected.js create mode 100644 test/js/samples/export-from-cjs/input.svelte create mode 100644 test/js/samples/export-from/expected.js create mode 100644 test/js/samples/export-from/input.svelte create mode 100644 test/runtime/samples/export-from/A.svelte create mode 100644 test/runtime/samples/export-from/B.svelte create mode 100644 test/runtime/samples/export-from/_config.js create mode 100644 test/runtime/samples/export-from/main.svelte diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index b94b108f43..fa5a8d555e 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -24,7 +24,7 @@ import TemplateScope from './nodes/shared/TemplateScope'; import fuzzymatch from '../utils/fuzzymatch'; import get_object from './utils/get_object'; import Slot from './nodes/Slot'; -import { Node, ImportDeclaration, Identifier, Program, ExpressionStatement, AssignmentExpression, Literal } from 'estree'; +import { Node, ImportDeclaration, ExportNamedDeclaration, Identifier, Program, ExpressionStatement, AssignmentExpression, Literal, ExportDefaultDeclaration, ExportAllDeclaration } from 'estree'; import add_to_set from './utils/add_to_set'; import check_graph_for_cycles from './utils/check_graph_for_cycles'; import { print, x, b } from 'code-red'; @@ -70,6 +70,8 @@ export default class Component { var_lookup: Map = new Map(); imports: ImportDeclaration[] = []; + exports_from: ExportNamedDeclaration[] = []; + instance_exports_from: ExportNamedDeclaration[] = []; hoistable_nodes: Set = new Set(); node_for_declaration: Map = new Map(); @@ -333,7 +335,8 @@ export default class Component { .map(variable => ({ name: variable.name, as: variable.export_name - })) + })), + this.exports_from ); css = compile_options.customElement @@ -492,22 +495,27 @@ export default class Component { this.imports.push(node); } - extract_exports(node) { + extract_exports(node, module_script = false) { const ignores = extract_svelte_ignore_from_comments(node); if (ignores.length) this.push_ignores(ignores); - const result = this._extract_exports(node); + const result = this._extract_exports(node, module_script); if (ignores.length) this.pop_ignores(); return result; } - private _extract_exports(node) { + private _extract_exports(node: ExportDefaultDeclaration | ExportNamedDeclaration | ExportAllDeclaration, module_script) { if (node.type === 'ExportDefaultDeclaration') { - return this.error(node, compiler_errors.default_export); + return this.error(node as any, compiler_errors.default_export); } if (node.type === 'ExportNamedDeclaration') { if (node.source) { - return this.error(node, compiler_errors.not_implemented); + if (module_script) { + this.exports_from.push(node); + } else { + this.instance_exports_from.push(node); + } + return null; } if (node.declaration) { if (node.declaration.type === 'VariableDeclaration') { @@ -516,7 +524,7 @@ export default class Component { const variable = this.var_lookup.get(name); variable.export_name = name; if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) { - this.warn(declarator, compiler_warnings.unused_export_let(this.name.name, name)); + this.warn(declarator as any, compiler_warnings.unused_export_let(this.name.name, name)); } }); }); @@ -536,7 +544,7 @@ export default class Component { variable.export_name = specifier.exported.name; if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) { - this.warn(specifier, compiler_warnings.unused_export_let(this.name.name, specifier.exported.name)); + this.warn(specifier as any, compiler_warnings.unused_export_let(this.name.name, specifier.exported.name)); } } }); @@ -612,7 +620,7 @@ export default class Component { } if (/^Export/.test(node.type)) { - const replacement = this.extract_exports(node); + const replacement = this.extract_exports(node, true); if (replacement) { body[i] = replacement; } else { diff --git a/src/compiler/compile/compiler_errors.ts b/src/compiler/compile/compiler_errors.ts index 7ef06c0bbb..fac7029c80 100644 --- a/src/compiler/compile/compiler_errors.ts +++ b/src/compiler/compile/compiler_errors.ts @@ -174,10 +174,6 @@ export default { code: 'default-export', message: 'A component cannot have a default export' }, - not_implemented: { - code: 'not-implemented', - message: 'A component currently cannot have an export ... from' - }, illegal_declaration: { code: 'illegal-declaration', message: 'The $ prefix is reserved, and cannot be used for variable and import names' diff --git a/src/compiler/compile/create_module.ts b/src/compiler/compile/create_module.ts index 80e6308263..037b2b396e 100644 --- a/src/compiler/compile/create_module.ts +++ b/src/compiler/compile/create_module.ts @@ -1,7 +1,7 @@ import list from '../utils/list'; import { ModuleFormat } from '../interfaces'; import { b, x } from 'code-red'; -import { Identifier, ImportDeclaration } from 'estree'; +import { Identifier, ImportDeclaration, ExportNamedDeclaration } from 'estree'; const wrappers = { esm, cjs }; @@ -19,20 +19,21 @@ export default function create_module( helpers: Array<{ name: string; alias: Identifier }>, globals: Array<{ name: string; alias: Identifier }>, imports: ImportDeclaration[], - module_exports: Export[] + module_exports: Export[], + exports_from: ExportNamedDeclaration[] ) { const internal_path = `${sveltePath}/internal`; helpers.sort((a, b) => (a.name < b.name) ? -1 : 1); globals.sort((a, b) => (a.name < b.name) ? -1 : 1); + + const formatter = wrappers[format]; - if (format === 'esm') { - return esm(program, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports); + if (!formatter) { + throw new Error(`options.format is invalid (must be ${list(Object.keys(wrappers))})`); } - if (format === 'cjs') return cjs(program, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports); - - throw new Error(`options.format is invalid (must be ${list(Object.keys(wrappers))})`); + return formatter(program, name, banner, sveltePath, internal_path, helpers, globals, imports, module_exports, exports_from); } function edit_source(source, sveltePath) { @@ -76,7 +77,8 @@ function esm( helpers: Array<{ name: string; alias: Identifier }>, globals: Array<{ name: string; alias: Identifier }>, imports: ImportDeclaration[], - module_exports: Export[] + module_exports: Export[], + exports_from: ExportNamedDeclaration[] ) { const import_declaration = { type: 'ImportDeclaration', @@ -94,6 +96,9 @@ function esm( imports.forEach(node => { node.source.value = edit_source(node.source.value, sveltePath); }); + exports_from.forEach(node => { + node.source!.value = edit_source(node.source!.value, sveltePath); + }); const exports = module_exports.length > 0 && { type: 'ExportNamedDeclaration', @@ -110,6 +115,7 @@ function esm( ${import_declaration} ${internal_globals} ${imports} + ${exports_from} ${program.body} @@ -127,7 +133,8 @@ function cjs( helpers: Array<{ name: string; alias: Identifier }>, globals: Array<{ name: string; alias: Identifier }>, imports: ImportDeclaration[], - module_exports: Export[] + module_exports: Export[], + exports_from: ExportNamedDeclaration[] ) { const internal_requires = { type: 'VariableDeclaration', @@ -183,6 +190,13 @@ function cjs( const exports = module_exports.map(x => b`exports.${{ type: 'Identifier', name: x.as }} = ${{ type: 'Identifier', name: x.name }};`); + const user_exports_from = exports_from.map(node => { + const init = x`require("${edit_source(node.source.value, sveltePath)}")`; + return node.specifiers.map(specifier => { + return b`exports.${specifier.exported} = ${init}.${specifier.local};`; + }); + }); + program.body = b` /* ${banner} */ @@ -190,6 +204,7 @@ function cjs( ${internal_requires} ${internal_globals} ${user_requires} + ${user_exports_from} ${program.body} diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index 535983cfd0..f74f4cdf1c 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -6,7 +6,7 @@ import { walk } from 'estree-walker'; import { extract_names, Scope } from 'periscopic'; import { invalidate } from './invalidate'; import Block from './Block'; -import { ClassDeclaration, FunctionExpression, Node, Statement, ObjectExpression, Expression } from 'estree'; +import { ImportDeclaration, ClassDeclaration, FunctionExpression, Node, Statement, ObjectExpression, Expression } from 'estree'; import { apply_preprocessor_sourcemap } from '../../utils/mapped_code'; import { RawSourceMap, DecodedSourceMap } from '@ampproject/remapping/dist/types/types'; import { flatten } from '../../utils/flatten'; @@ -174,6 +174,46 @@ export default function dom( } }); + component.instance_exports_from.forEach(exports_from => { + const import_declaration = { + ...exports_from, + type: 'ImportDeclaration', + specifiers: [], + source: exports_from.source + }; + component.imports.push(import_declaration as ImportDeclaration); + + exports_from.specifiers.forEach(specifier => { + if (component.component_options.accessors) { + const name = component.get_unique_name(specifier.exported.name); + import_declaration.specifiers.push({ + ...specifier, + type: 'ImportSpecifier', + imported: specifier.local, + local: name + }); + + accessors.push({ + type: 'MethodDefinition', + kind: 'get', + key: { type: 'Identifier', name: specifier.exported.name }, + value: x`function() { + return ${name} + }` + }); + } else if (component.compile_options.dev) { + accessors.push({ + type: 'MethodDefinition', + kind: 'get', + key: { type: 'Identifier', name: specifier.exported.name }, + value: x`function() { + throw new @_Error("<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ''"); + }` + }); + } + }); + }); + if (component.compile_options.dev) { // checking that expected ones were passed const expected = props.filter(prop => prop.writable && !prop.initialised); diff --git a/test/js/samples/export-from-accessors/_config.js b/test/js/samples/export-from-accessors/_config.js new file mode 100644 index 0000000000..7f9293a560 --- /dev/null +++ b/test/js/samples/export-from-accessors/_config.js @@ -0,0 +1,5 @@ +export default { + options: { + accessors: true + } +}; diff --git a/test/js/samples/export-from-accessors/expected.js b/test/js/samples/export-from-accessors/expected.js new file mode 100644 index 0000000000..20b0524ca7 --- /dev/null +++ b/test/js/samples/export-from-accessors/expected.js @@ -0,0 +1,34 @@ +/* generated by Svelte vX.Y.Z */ +import { SvelteComponent, init, safe_not_equal } from "svelte/internal"; + +import { f as f_1, g as g_1 } from './d'; +import { h as h_1 } from './e'; +import { i as j } from './f'; +export { d as e } from './c'; +export { c } from './b'; +export { a, b } from './a'; + +class Component extends SvelteComponent { + constructor(options) { + super(); + init(this, options, null, null, safe_not_equal, {}); + } + + get f() { + return f_1; + } + + get g() { + return g_1; + } + + get h() { + return h_1; + } + + get j() { + return j; + } +} + +export default Component; \ No newline at end of file diff --git a/test/js/samples/export-from-accessors/input.svelte b/test/js/samples/export-from-accessors/input.svelte new file mode 100644 index 0000000000..66957806a2 --- /dev/null +++ b/test/js/samples/export-from-accessors/input.svelte @@ -0,0 +1,11 @@ + + + diff --git a/test/js/samples/export-from-cjs/_config.js b/test/js/samples/export-from-cjs/_config.js new file mode 100644 index 0000000000..2506f1d5fc --- /dev/null +++ b/test/js/samples/export-from-cjs/_config.js @@ -0,0 +1,6 @@ +export default { + options: { + accessors: true, + format: 'cjs' + } +}; diff --git a/test/js/samples/export-from-cjs/expected.js b/test/js/samples/export-from-cjs/expected.js new file mode 100644 index 0000000000..d40f986635 --- /dev/null +++ b/test/js/samples/export-from-cjs/expected.js @@ -0,0 +1,36 @@ +/* generated by Svelte vX.Y.Z */ +"use strict"; + +const { SvelteComponent, init, safe_not_equal } = require("svelte/internal"); +const { f: f_1, g: g_1 } = require("./d"); +const { h: h_1 } = require("./e"); +const { i: j } = require("./f"); +exports.e = require("./c").d; +exports.c = require("./b").c; +exports.a = require("./a").a; +exports.b = require("./a").b; + +class Component extends SvelteComponent { + constructor(options) { + super(); + init(this, options, null, null, safe_not_equal, {}); + } + + get f() { + return f_1; + } + + get g() { + return g_1; + } + + get h() { + return h_1; + } + + get j() { + return j; + } +} + +exports.default = Component; \ No newline at end of file diff --git a/test/js/samples/export-from-cjs/input.svelte b/test/js/samples/export-from-cjs/input.svelte new file mode 100644 index 0000000000..66957806a2 --- /dev/null +++ b/test/js/samples/export-from-cjs/input.svelte @@ -0,0 +1,11 @@ + + + diff --git a/test/js/samples/export-from/expected.js b/test/js/samples/export-from/expected.js new file mode 100644 index 0000000000..04fb47605f --- /dev/null +++ b/test/js/samples/export-from/expected.js @@ -0,0 +1,18 @@ +/* generated by Svelte vX.Y.Z */ +import { SvelteComponent, init, safe_not_equal } from "svelte/internal"; + +import './d'; +import './e'; +import './f'; +export { d as e } from './c'; +export { c } from './b'; +export { a, b } from './a'; + +class Component extends SvelteComponent { + constructor(options) { + super(); + init(this, options, null, null, safe_not_equal, {}); + } +} + +export default Component; \ No newline at end of file diff --git a/test/js/samples/export-from/input.svelte b/test/js/samples/export-from/input.svelte new file mode 100644 index 0000000000..66957806a2 --- /dev/null +++ b/test/js/samples/export-from/input.svelte @@ -0,0 +1,11 @@ + + + diff --git a/test/runtime/samples/export-from/A.svelte b/test/runtime/samples/export-from/A.svelte new file mode 100644 index 0000000000..7773727704 --- /dev/null +++ b/test/runtime/samples/export-from/A.svelte @@ -0,0 +1,23 @@ + + + + +a: {typeof a}
+b: {typeof b}
+c: {typeof c}
+d: {typeof d}
+e: {typeof e}
+f: {typeof f}
+g: {typeof g}
\ No newline at end of file diff --git a/test/runtime/samples/export-from/B.svelte b/test/runtime/samples/export-from/B.svelte new file mode 100644 index 0000000000..0cc1070cb5 --- /dev/null +++ b/test/runtime/samples/export-from/B.svelte @@ -0,0 +1,8 @@ + diff --git a/test/runtime/samples/export-from/_config.js b/test/runtime/samples/export-from/_config.js new file mode 100644 index 0000000000..9e65b7501d --- /dev/null +++ b/test/runtime/samples/export-from/_config.js @@ -0,0 +1,28 @@ +export default { + html: ` + a,b,undefined,c +
+ a: undefined
+ b: number
+ c: undefined
+ d: undefined
+ e: number
+ f: undefined
+ g: undefined
+
+ {"d":"d","e":"e","g":"f"} + `, + ssrHtml: ` + a,b,undefined,c +
+ a: undefined
+ b: number
+ c: undefined
+ d: undefined
+ e: number
+ f: undefined
+ g: undefined
+
+ {} + ` +}; diff --git a/test/runtime/samples/export-from/main.svelte b/test/runtime/samples/export-from/main.svelte new file mode 100644 index 0000000000..18cc066c1f --- /dev/null +++ b/test/runtime/samples/export-from/main.svelte @@ -0,0 +1,21 @@ + + +{a},{b},{c},{d} +
+ +
+{JSON.stringify(props)} \ No newline at end of file From 520b4751dcda44626fa35d062ed603f8b3394717 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Tue, 27 Jul 2021 09:38:52 -0400 Subject: [PATCH 64/69] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7aa192fef4..effa1e821c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Svelte changelog +## Unreleased + +* Support `export { ... } from` syntax in components ([#2214](https://github.com/sveltejs/svelte/issues/2214)) + ## 3.40.3 * Fix `` data when a transition is cancelled before completing ([#5394](https://github.com/sveltejs/svelte/issues/5394)) From b720f0e6202f57cc6e4a16106aa16c620db86dfa Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 27 Jul 2021 21:53:55 +0800 Subject: [PATCH 65/69] [feat] support destructured declaration of props (#6578) --- src/compiler/compile/Component.ts | 159 ++++++++++++------ src/compiler/compile/compiler_errors.ts | 4 - .../samples/destructured-props-1/A.svelte | 24 +++ .../samples/destructured-props-1/_config.js | 9 + .../samples/destructured-props-1/main.svelte | 7 + .../samples/destructured-props-2/A.svelte | 21 +++ .../samples/destructured-props-2/_config.js | 19 +++ .../samples/destructured-props-2/main.svelte | 30 ++++ .../samples/destructured-props-3/A.svelte | 10 ++ .../samples/destructured-props-3/_config.js | 15 ++ .../samples/destructured-props-3/main.svelte | 29 ++++ 11 files changed, 271 insertions(+), 56 deletions(-) create mode 100644 test/runtime/samples/destructured-props-1/A.svelte create mode 100644 test/runtime/samples/destructured-props-1/_config.js create mode 100644 test/runtime/samples/destructured-props-1/main.svelte create mode 100644 test/runtime/samples/destructured-props-2/A.svelte create mode 100644 test/runtime/samples/destructured-props-2/_config.js create mode 100644 test/runtime/samples/destructured-props-2/main.svelte create mode 100644 test/runtime/samples/destructured-props-3/A.svelte create mode 100644 test/runtime/samples/destructured-props-3/_config.js create mode 100644 test/runtime/samples/destructured-props-3/main.svelte diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index fa5a8d555e..6a8d5b178e 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -24,10 +24,10 @@ import TemplateScope from './nodes/shared/TemplateScope'; import fuzzymatch from '../utils/fuzzymatch'; import get_object from './utils/get_object'; import Slot from './nodes/Slot'; -import { Node, ImportDeclaration, ExportNamedDeclaration, Identifier, Program, ExpressionStatement, AssignmentExpression, Literal, ExportDefaultDeclaration, ExportAllDeclaration } from 'estree'; +import { Node, ImportDeclaration, ExportNamedDeclaration, Identifier, ExpressionStatement, AssignmentExpression, Literal, Property, RestElement, ExportDefaultDeclaration, ExportAllDeclaration } from 'estree'; import add_to_set from './utils/add_to_set'; import check_graph_for_cycles from './utils/check_graph_for_cycles'; -import { print, x, b } from 'code-red'; +import { print, b } from 'code-red'; import { is_reserved_keyword } from './utils/reserved_keywords'; import { apply_preprocessor_sourcemap } from '../utils/mapped_code'; import Element from './nodes/Element'; @@ -943,7 +943,7 @@ export default class Component { let scope = instance_scope; walk(this.ast.instance.content, { - enter(node: Node, parent, key, index) { + enter(node: Node) { if (/Function/.test(node.type)) { return this.skip(); } @@ -952,75 +952,130 @@ export default class Component { scope = map.get(node); } + if (node.type === 'ExportNamedDeclaration' && node.declaration) { + return this.replace(node.declaration); + } + if (node.type === 'VariableDeclaration') { + // NOTE: `var` does not follow block scoping if (node.kind === 'var' || scope === instance_scope) { - node.declarations.forEach(declarator => { - if (declarator.id.type !== 'Identifier') { - const inserts = []; - - extract_names(declarator.id).forEach(name => { - const variable = component.var_lookup.get(name); - - if (variable.export_name) { - // TODO is this still true post-#3539? - return component.error(declarator as any, compiler_errors.destructured_prop); + const inserts = []; + const props = []; + + function add_new_props(exported, local, default_value) { + props.push({ + type: 'Property', + method: false, + shorthand: false, + computed: false, + kind: 'init', + key: exported, + value: default_value + ? { + type: 'AssignmentPattern', + left: local, + right: default_value } + : local + }); + } + // transform + // ``` + // export let { x, y = 123 } = OBJ, z = 456 + // ``` + // into + // ``` + // let { x: x$, y: y$ = 123 } = OBJ; + // let { x = x$, y = y$, z = 456 } = $$props; + // ``` + for (let index = 0; index < node.declarations.length; index++) { + const declarator = node.declarations[index]; + if (declarator.id.type !== 'Identifier') { + function get_new_name(local) { + const variable = component.var_lookup.get(local.name); if (variable.subscribable) { inserts.push(get_insert(variable)); } - }); - if (inserts.length) { - parent[key].splice(index + 1, 0, ...inserts); + if (variable.export_name && variable.writable) { + const alias_name = component.get_unique_name(local.name); + add_new_props({ type: 'Identifier', name: variable.export_name }, local, alias_name); + return alias_name; + } + return local; } - return; - } - - const { name } = declarator.id; - const variable = component.var_lookup.get(name); + function rename_identifiers(param: Node) { + switch (param.type) { + case 'ObjectPattern': { + const handle_prop = (prop: Property | RestElement) => { + if (prop.type === 'RestElement') { + rename_identifiers(prop); + } else if (prop.value.type === 'Identifier') { + prop.value = get_new_name(prop.value); + } else { + rename_identifiers(prop.value); + } + }; + + param.properties.forEach(handle_prop); + break; + } + case 'ArrayPattern': { + const handle_element = (element: Node, index: number, array: Node[]) => { + if (element) { + if (element.type === 'Identifier') { + array[index] = get_new_name(element); + } else { + rename_identifiers(element); + } + } + }; + + param.elements.forEach(handle_element); + break; + } + + case 'RestElement': + param.argument = get_new_name(param.argument); + break; + + case 'AssignmentPattern': + param.left = get_new_name(param.left); + break; + } + } - if (variable.export_name && variable.writable) { - declarator.id = { - type: 'ObjectPattern', - properties: [{ - type: 'Property', - method: false, - shorthand: false, - computed: false, - kind: 'init', - key: { type: 'Identifier', name: variable.export_name }, - value: declarator.init - ? { - type: 'AssignmentPattern', - left: declarator.id, - right: declarator.init - } - : declarator.id - }] - }; - - declarator.init = x`$$props`; + rename_identifiers(declarator.id); + } else { + const { name } = declarator.id; + const variable = component.var_lookup.get(name); + const is_props = variable.export_name && variable.writable; + if (is_props) { + add_new_props({ type: 'Identifier', name: variable.export_name }, declarator.id, declarator.init); + node.declarations.splice(index--, 1); + } + if (variable.subscribable && (is_props || declarator.init)) { + inserts.push(get_insert(variable)); + } } + } - if (variable.subscribable && declarator.init) { - const insert = get_insert(variable); - parent[key].splice(index + 1, 0, ...insert); - } - }); + this.replace(b` + ${node.declarations.length ? node : null} + ${ props.length > 0 && b`let { ${ props } } = $$props;`} + ${inserts} + ` as any); + return this.skip(); } } }, - leave(node: Node, parent, _key, index) { + leave(node: Node) { if (map.has(node)) { scope = scope.parent; } - - if (node.type === 'ExportNamedDeclaration' && node.declaration) { - (parent as Program).body[index] = node.declaration; - } } }); } diff --git a/src/compiler/compile/compiler_errors.ts b/src/compiler/compile/compiler_errors.ts index fac7029c80..54263c3eb9 100644 --- a/src/compiler/compile/compiler_errors.ts +++ b/src/compiler/compile/compiler_errors.ts @@ -186,10 +186,6 @@ export default { code: 'illegal-global', message: `${name} is an illegal variable name` }), - destructured_prop: { - code: 'destructured-prop', - message: 'Cannot declare props in destructured declaration' - }, cyclical_reactive_declaration: (cycle: string[]) => ({ code: 'cyclical-reactive-declaration', message: `Cyclical dependency detected: ${cycle.join(' → ')}` diff --git a/test/runtime/samples/destructured-props-1/A.svelte b/test/runtime/samples/destructured-props-1/A.svelte new file mode 100644 index 0000000000..d1b392ef34 --- /dev/null +++ b/test/runtime/samples/destructured-props-1/A.svelte @@ -0,0 +1,24 @@ + + +
+a: {a}, +b: {typeof b}, +c: {c}, +d_one: {d_one}, +d_three: {$d_three}, +f: {f}, +g: {g}, +e: {typeof e}, +e_one: {e_one}, +A: {A}, +C: {C} +
+
{JSON.stringify(THING)}
\ No newline at end of file diff --git a/test/runtime/samples/destructured-props-1/_config.js b/test/runtime/samples/destructured-props-1/_config.js new file mode 100644 index 0000000000..29feaaabab --- /dev/null +++ b/test/runtime/samples/destructured-props-1/_config.js @@ -0,0 +1,9 @@ +export default { + html: ` +
a: 1, b: undefined, c: 2, d_one: 3, d_three: 5, f: undefined, g: 9, e: undefined, e_one: 6, A: 1, C: 2
+
{"a":1,"b":{"c":2,"d":[3,4,{}]},"e":[6],"h":8}
+
+
a: a, b: undefined, c: 2, d_one: d_one, d_three: 5, f: f, g: g, e: undefined, e_one: 6, A: 1, C: 2
+
{"a":1,"b":{"c":2,"d":[3,4,{}]},"e":[6],"h":8}
+ ` +}; diff --git a/test/runtime/samples/destructured-props-1/main.svelte b/test/runtime/samples/destructured-props-1/main.svelte new file mode 100644 index 0000000000..dbe7c88d33 --- /dev/null +++ b/test/runtime/samples/destructured-props-1/main.svelte @@ -0,0 +1,7 @@ + + +
+
+
diff --git a/test/runtime/samples/destructured-props-2/A.svelte b/test/runtime/samples/destructured-props-2/A.svelte new file mode 100644 index 0000000000..51167edbc4 --- /dev/null +++ b/test/runtime/samples/destructured-props-2/A.svelte @@ -0,0 +1,21 @@ + + +
+ x: {x}, + list_two_a: {list_two_a}, + list_two_b: {list_two_b}, + y: {$y}, + m: {m}, + n: {n}, + o: {o}, + p: {p}, + q: {$q} +
+
{JSON.stringify(LIST)}
\ No newline at end of file diff --git a/test/runtime/samples/destructured-props-2/_config.js b/test/runtime/samples/destructured-props-2/_config.js new file mode 100644 index 0000000000..c647f31be1 --- /dev/null +++ b/test/runtime/samples/destructured-props-2/_config.js @@ -0,0 +1,19 @@ +export default { + html: ` +
x: 1, list_two_a: 2, list_two_b: 5, y: 4, m: 1, n: 2, o: 5, p: 3, q: 4
+
[1,{"a":2},[3,{}]]
+
x: 1, list_two_a: 2, list_two_b: 5, y: 4, m: m, n: n, o: o, p: p, q: q
+
[1,{"a":2},[3,{}]]
+ `, + + async test({ component, assert, target }) { + await component.update(); + + assert.htmlEqual(target.innerHTML, ` +
x: 1, list_two_a: 2, list_two_b: 5, y: 4, m: 1, n: 2, o: 5, p: 3, q: 4
+
[1,{"a":2},[3,{}]]
+
x: 1, list_two_a: 2, list_two_b: 5, y: 4, m: MM, n: NN, o: OO, p: PP, q: QQ
+
[1,{"a":2},[3,{}]]
+ `); + } +}; diff --git a/test/runtime/samples/destructured-props-2/main.svelte b/test/runtime/samples/destructured-props-2/main.svelte new file mode 100644 index 0000000000..f0044bb567 --- /dev/null +++ b/test/runtime/samples/destructured-props-2/main.svelte @@ -0,0 +1,30 @@ + + +
+
+
diff --git a/test/runtime/samples/destructured-props-3/A.svelte b/test/runtime/samples/destructured-props-3/A.svelte new file mode 100644 index 0000000000..6c5aca05b0 --- /dev/null +++ b/test/runtime/samples/destructured-props-3/A.svelte @@ -0,0 +1,10 @@ + + +
i: {i}, j: {j}, k: {$k}, l: {l}, m: {m}, n: {$n}, a: {a}, b: {b}, c: {$c}, d: {d}, e: {e}, f: {$f}
\ No newline at end of file diff --git a/test/runtime/samples/destructured-props-3/_config.js b/test/runtime/samples/destructured-props-3/_config.js new file mode 100644 index 0000000000..6d2e516e0e --- /dev/null +++ b/test/runtime/samples/destructured-props-3/_config.js @@ -0,0 +1,15 @@ +export default { + html: ` +
i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, a: 9, b: 10, c: 11, d: 12, e: 13, f: 14
+
+
i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, a: a, b: 10, c: c, d: d, e: 13, f: f
+ `, + async test({ component, target, assert }) { + await component.update(); + assert.htmlEqual(target.innerHTML, ` +
i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, a: 9, b: 10, c: 11, d: 12, e: 13, f: 14
+
+
i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, a: aa, b: 10, c: cc, d: dd, e: 13, f: ff
+ `); + } +}; diff --git a/test/runtime/samples/destructured-props-3/main.svelte b/test/runtime/samples/destructured-props-3/main.svelte new file mode 100644 index 0000000000..8152d61aab --- /dev/null +++ b/test/runtime/samples/destructured-props-3/main.svelte @@ -0,0 +1,29 @@ + + +
+
+
From 32b376b472c5fdd9e0de97c9c2a435e6c94af54f Mon Sep 17 00:00:00 2001 From: Conduitry Date: Tue, 27 Jul 2021 09:54:54 -0400 Subject: [PATCH 66/69] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index effa1e821c..703bfac047 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Support `export { ... } from` syntax in components ([#2214](https://github.com/sveltejs/svelte/issues/2214)) +* Support `export let { ... } =` syntax in components ([#5612](https://github.com/sveltejs/svelte/issues/5612)) ## 3.40.3 From e1d0d00ebb9a926822bb413f042b7784662b2355 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Tue, 27 Jul 2021 21:58:32 +0800 Subject: [PATCH 67/69] [feat] allow shorthand {#await ... then/catch} (#6564) --- src/compiler/parse/state/mustache.ts | 20 +++++-- .../await-catch-no-expression/_config.js | 47 ++++++++++++++++ .../await-catch-no-expression/main.svelte | 15 +++++ .../await-then-no-expression/_config.js | 55 +++++++++++++++++++ .../await-then-no-expression/main.svelte | 23 ++++++++ 5 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 test/runtime/samples/await-catch-no-expression/_config.js create mode 100644 test/runtime/samples/await-catch-no-expression/main.svelte create mode 100644 test/runtime/samples/await-then-no-expression/_config.js create mode 100644 test/runtime/samples/await-then-no-expression/main.svelte diff --git a/src/compiler/parse/state/mustache.ts b/src/compiler/parse/state/mustache.ts index cc3c24349c..584f9d6e9a 100644 --- a/src/compiler/parse/state/mustache.ts +++ b/src/compiler/parse/state/mustache.ts @@ -290,16 +290,24 @@ export default function mustache(parser: Parser) { const await_block_shorthand = type === 'AwaitBlock' && parser.eat('then'); if (await_block_shorthand) { - parser.require_whitespace(); - block.value = read_context(parser); - parser.allow_whitespace(); + if (parser.match_regex(/\s*}/)) { + parser.allow_whitespace(); + } else { + parser.require_whitespace(); + block.value = read_context(parser); + parser.allow_whitespace(); + } } const await_block_catch_shorthand = !await_block_shorthand && type === 'AwaitBlock' && parser.eat('catch'); if (await_block_catch_shorthand) { - parser.require_whitespace(); - block.error = read_context(parser); - parser.allow_whitespace(); + if (parser.match_regex(/\s*}/)) { + parser.allow_whitespace(); + } else { + parser.require_whitespace(); + block.error = read_context(parser); + parser.allow_whitespace(); + } } parser.eat('}', true); diff --git a/test/runtime/samples/await-catch-no-expression/_config.js b/test/runtime/samples/await-catch-no-expression/_config.js new file mode 100644 index 0000000000..5274e60e2d --- /dev/null +++ b/test/runtime/samples/await-catch-no-expression/_config.js @@ -0,0 +1,47 @@ +let fulfil; + +let thePromise = new Promise(f => { + fulfil = f; +}); + +export default { + props: { + thePromise + }, + + html: ` +
+

the promise is pending

+ `, + + async test({ assert, component, target }) { + fulfil(42); + + await thePromise; + + assert.htmlEqual(target.innerHTML, '
'); + + let reject; + + thePromise = new Promise((f, r) => { + reject = r; + }); + + component.thePromise = thePromise; + + assert.htmlEqual(target.innerHTML, ` +
+

the promise is pending

+ `); + + reject(new Error()); + + await thePromise.catch(() => {}); + + assert.htmlEqual(target.innerHTML, ` +

oh no! Something broke!

+
+

oh no! Something broke!

+ `); + } +}; diff --git a/test/runtime/samples/await-catch-no-expression/main.svelte b/test/runtime/samples/await-catch-no-expression/main.svelte new file mode 100644 index 0000000000..0da0d12092 --- /dev/null +++ b/test/runtime/samples/await-catch-no-expression/main.svelte @@ -0,0 +1,15 @@ + + +{#await thePromise catch} +

oh no! Something broke!

+{/await} + +
+ +{#await thePromise} +

the promise is pending

+{:catch} +

oh no! Something broke!

+{/await} diff --git a/test/runtime/samples/await-then-no-expression/_config.js b/test/runtime/samples/await-then-no-expression/_config.js new file mode 100644 index 0000000000..f684da52ed --- /dev/null +++ b/test/runtime/samples/await-then-no-expression/_config.js @@ -0,0 +1,55 @@ +let fulfil; + +let thePromise = new Promise(f => { + fulfil = f; +}); + +export default { + props: { + thePromise + }, + + html: ` +
+
+

the promise is pending

+ `, + + async test({ assert, component, target }) { + fulfil(); + + await thePromise; + + assert.htmlEqual(target.innerHTML, ` +

the promise is resolved

+
+

the promise is resolved

+
+

the promise is resolved

+ `); + + let reject; + + thePromise = new Promise((f, r) => { + reject = r; + }); + + component.thePromise = thePromise; + + assert.htmlEqual(target.innerHTML, ` +
+
+

the promise is pending

+ `); + + reject(new Error('something broke')); + + await thePromise.catch(() => {}); + + assert.htmlEqual(target.innerHTML, ` +

oh no! something broke

+
+
+ `); + } +}; diff --git a/test/runtime/samples/await-then-no-expression/main.svelte b/test/runtime/samples/await-then-no-expression/main.svelte new file mode 100644 index 0000000000..fedc7cd2b7 --- /dev/null +++ b/test/runtime/samples/await-then-no-expression/main.svelte @@ -0,0 +1,23 @@ + + +{#await thePromise then} +

the promise is resolved

+{:catch theError} +

oh no! {theError.message}

+{/await} + +
+ +{#await thePromise then} +

the promise is resolved

+{/await} + +
+ +{#await thePromise} +

the promise is pending

+{:then} +

the promise is resolved

+{/await} \ No newline at end of file From 95b40f2492f2f44fc043a389a7a43b0653544f47 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Tue, 27 Jul 2021 09:59:40 -0400 Subject: [PATCH 68/69] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 703bfac047..91a2b55765 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Support `export { ... } from` syntax in components ([#2214](https://github.com/sveltejs/svelte/issues/2214)) * Support `export let { ... } =` syntax in components ([#5612](https://github.com/sveltejs/svelte/issues/5612)) +* Support `{#await ... then/catch}` without a variable for the resolved/rejected value ([#6270](https://github.com/sveltejs/svelte/issues/6270)) ## 3.40.3 From ff6ce725bfdabba908690f4bae0b06a3f26da881 Mon Sep 17 00:00:00 2001 From: Conduitry Date: Tue, 27 Jul 2021 10:01:49 -0400 Subject: [PATCH 69/69] -> v3.41.0 --- CHANGELOG.md | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91a2b55765..dadb5cd406 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Svelte changelog -## Unreleased +## 3.41.0 * Support `export { ... } from` syntax in components ([#2214](https://github.com/sveltejs/svelte/issues/2214)) * Support `export let { ... } =` syntax in components ([#5612](https://github.com/sveltejs/svelte/issues/5612)) diff --git a/package-lock.json b/package-lock.json index e059174ea6..b4f16c00cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.40.3", + "version": "3.41.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 6f6303348f..0d39beb8f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.40.3", + "version": "3.41.0", "description": "Cybernetically enhanced web apps", "module": "index.mjs", "main": "index",