From ca2f02c23e44ad66b3ca24fae56eae58baee55d7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 21 Jul 2024 16:52:44 -0400 Subject: [PATCH 1/5] Version Packages (next) (#12515) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 2 ++ packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index 675c79f28c..5826fb7f66 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -185,6 +185,7 @@ "fluffy-dolls-share", "fluffy-humans-worry", "fluffy-ravens-juggle", + "forty-bikes-buy", "forty-comics-invent", "forty-dogs-divide", "forty-dolls-wave", @@ -193,6 +194,7 @@ "four-balloons-beam", "four-flies-hammer", "four-mice-hammer", + "four-papayas-turn", "four-pugs-listen", "fresh-beds-wash", "fresh-dots-destroy", diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 3c7523801d..b67d217c81 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.0.0-next.193 + +### Patch Changes + +- fix: improve validation error that occurs when using `{@render ...}` to render default slotted content ([#12521](https://github.com/sveltejs/svelte/pull/12521)) + +- fix: reset hydrate node after `hydrate(...)` ([#12512](https://github.com/sveltejs/svelte/pull/12512)) + ## 5.0.0-next.192 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 6484b8cb58..5067c4ad51 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.0.0-next.192", + "version": "5.0.0-next.193", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 90457f42f6..b947c0ce3f 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.0.0-next.192'; +export const VERSION = '5.0.0-next.193'; export const PUBLIC_VERSION = '5'; From a01f658f01f01e7347dd978ca0df3c8edd9d0572 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 21 Jul 2024 16:52:53 -0400 Subject: [PATCH 2/5] chore: tidy up each block compiler logic (#12513) * small tidy-up * each_type -> flags * tidy up --- .../3-transform/client/visitors/template.js | 49 +++++++++---------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 02a7699f0b..4683a945bd 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -2362,7 +2362,6 @@ export const template_visitors = { EachBlock(node, context) { const each_node_meta = node.metadata; const collection = /** @type {Expression} */ (context.visit(node.expression)); - let each_item_is_reactive = true; if (!each_node_meta.is_controlled) { context.state.template.push(''); @@ -2372,36 +2371,29 @@ export const template_visitors = { context.state.init.push(b.const(each_node_meta.array_name, b.thunk(collection))); } - // The runtime needs to know what kind of each block this is in order to optimize for the - // key === item (we avoid extra allocations). In that case, the item doesn't need to be reactive. - // We can guarantee this by knowing that in order for the item of the each block to change, they - // would need to mutate the key/item directly in the array. Given that in runes mode we use === - // equality, we can apply a fast-path (as long as the index isn't reactive). - let each_type = 0; + let flags = 0; if ( node.key && (node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index) ) { - each_type |= EACH_KEYED; - // If there's a destructuring, then we likely need the generated $$index - if (node.index || node.context.type !== 'Identifier') { - each_type |= EACH_INDEX_REACTIVE; + flags |= EACH_KEYED; + + if (node.index) { + flags |= EACH_INDEX_REACTIVE; } - if ( - context.state.analysis.runes && + + // In runes mode, if key === item, we don't need to wrap the item in a source + const key_is_item = node.key.type === 'Identifier' && node.context.type === 'Identifier' && - node.context.name === node.key.name && - (each_type & EACH_INDEX_REACTIVE) === 0 - ) { - // Fast-path for when the key === item - each_item_is_reactive = false; - } else { - each_type |= EACH_ITEM_REACTIVE; + node.context.name === node.key.name; + + if (!context.state.analysis.runes || !key_is_item) { + flags |= EACH_ITEM_REACTIVE; } } else { - each_type |= EACH_ITEM_REACTIVE; + flags |= EACH_ITEM_REACTIVE; } // Since `animate:` can only appear on elements that are the sole child of a keyed each block, @@ -2414,15 +2406,15 @@ export const template_visitors = { return child.attributes.some((attr) => attr.type === 'AnimateDirective'); }) ) { - each_type |= EACH_IS_ANIMATED; + flags |= EACH_IS_ANIMATED; } if (each_node_meta.is_controlled) { - each_type |= EACH_IS_CONTROLLED; + flags |= EACH_IS_CONTROLLED; } if (context.state.analysis.runes) { - each_type |= EACH_IS_STRICT_EQUALS; + flags |= EACH_IS_STRICT_EQUALS; } // If the array is a store expression, we need to invalidate it when the array is changed. @@ -2447,10 +2439,12 @@ export const template_visitors = { ); return [array, ...transitive_dependencies]; }); + if (each_node_meta.array_name) { indirect_dependencies.push(b.call(each_node_meta.array_name)); } else { indirect_dependencies.push(collection); + const transitive_dependencies = serialize_transitive_dependencies( each_node_meta.references, context @@ -2469,6 +2463,7 @@ export const template_visitors = { // into separate expressions, at which point this is called again with an identifier or member expression return serialize_set_binding(assignment, context, () => assignment); } + const left = object(assignment.left); const value = get_assignment_value(assignment, context); const invalidate = b.call( @@ -2509,10 +2504,12 @@ export const template_visitors = { const item_with_loc = with_loc(item, id); return b.call('$.unwrap', item_with_loc); }; + if (node.index) { const index_binding = /** @type {import('#compiler').Binding} */ ( context.state.scope.get(node.index) ); + index_binding.expression = (id) => { const index_with_loc = with_loc(index, id); return b.call('$.unwrap', index_with_loc); @@ -2575,7 +2572,7 @@ export const template_visitors = { declarations.push(b.let(node.index, index)); } - if (context.state.options.dev && (each_type & EACH_KEYED) !== 0) { + if (context.state.options.dev && (flags & EACH_KEYED) !== 0) { context.state.init.push( b.stmt(b.call('$.validate_each_keys', b.thunk(collection), key_function)) ); @@ -2584,7 +2581,7 @@ export const template_visitors = { /** @type {Expression[]} */ const args = [ context.state.node, - b.literal(each_type), + b.literal(flags), each_node_meta.array_name ? each_node_meta.array_name : b.thunk(collection), key_function, b.arrow([b.id('$$anchor'), item, index], b.block(declarations.concat(block.body))) From 0fb9fb9a9549a02d7ba8c3b9727ae6f0253dd843 Mon Sep 17 00:00:00 2001 From: Cevdet Arda Haran Date: Mon, 22 Jul 2024 14:20:05 +0200 Subject: [PATCH 3/5] docs: change meta element from "Description" to "description" (#12526) --- sites/svelte-5-preview/src/routes/docs/[slug]/+page.svelte | 2 +- sites/svelte.dev/src/routes/(authed)/repl/[id]/+page.svelte | 2 +- sites/svelte.dev/src/routes/(authed)/repl/embed/+page.svelte | 2 +- sites/svelte.dev/src/routes/+page.svelte | 2 +- sites/svelte.dev/src/routes/blog/+page.svelte | 2 +- sites/svelte.dev/src/routes/blog/[slug]/+page.svelte | 2 +- sites/svelte.dev/src/routes/docs/[slug]/+page.svelte | 2 +- sites/svelte.dev/src/routes/examples/[slug]/+page.svelte | 2 +- sites/svelte.dev/src/routes/tutorial/[slug]/+page.svelte | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sites/svelte-5-preview/src/routes/docs/[slug]/+page.svelte b/sites/svelte-5-preview/src/routes/docs/[slug]/+page.svelte index 7aa2ee78c6..bb5c166711 100644 --- a/sites/svelte-5-preview/src/routes/docs/[slug]/+page.svelte +++ b/sites/svelte-5-preview/src/routes/docs/[slug]/+page.svelte @@ -18,7 +18,7 @@ - +
diff --git a/sites/svelte.dev/src/routes/(authed)/repl/[id]/+page.svelte b/sites/svelte.dev/src/routes/(authed)/repl/[id]/+page.svelte index c867ff332f..2e4413de48 100644 --- a/sites/svelte.dev/src/routes/(authed)/repl/[id]/+page.svelte +++ b/sites/svelte.dev/src/routes/(authed)/repl/[id]/+page.svelte @@ -70,7 +70,7 @@ - +
diff --git a/sites/svelte.dev/src/routes/(authed)/repl/embed/+page.svelte b/sites/svelte.dev/src/routes/(authed)/repl/embed/+page.svelte index 43fed8a1cb..2833ca007c 100644 --- a/sites/svelte.dev/src/routes/(authed)/repl/embed/+page.svelte +++ b/sites/svelte.dev/src/routes/(authed)/repl/embed/+page.svelte @@ -10,7 +10,7 @@ - +
diff --git a/sites/svelte.dev/src/routes/+page.svelte b/sites/svelte.dev/src/routes/+page.svelte index 3a3463f712..1b6a59c1d7 100644 --- a/sites/svelte.dev/src/routes/+page.svelte +++ b/sites/svelte.dev/src/routes/+page.svelte @@ -11,7 +11,7 @@ - +

Svelte

diff --git a/sites/svelte.dev/src/routes/blog/+page.svelte b/sites/svelte.dev/src/routes/blog/+page.svelte index 39bd289603..d19a49a7e4 100644 --- a/sites/svelte.dev/src/routes/blog/+page.svelte +++ b/sites/svelte.dev/src/routes/blog/+page.svelte @@ -13,7 +13,7 @@ - +

Blog

diff --git a/sites/svelte.dev/src/routes/blog/[slug]/+page.svelte b/sites/svelte.dev/src/routes/blog/[slug]/+page.svelte index 158973aebe..b1962b35c4 100644 --- a/sites/svelte.dev/src/routes/blog/[slug]/+page.svelte +++ b/sites/svelte.dev/src/routes/blog/[slug]/+page.svelte @@ -14,7 +14,7 @@ - + diff --git a/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte b/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte index ddcdadf144..6142ff8dbf 100644 --- a/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte +++ b/sites/svelte.dev/src/routes/docs/[slug]/+page.svelte @@ -19,7 +19,7 @@ - +
diff --git a/sites/svelte.dev/src/routes/examples/[slug]/+page.svelte b/sites/svelte.dev/src/routes/examples/[slug]/+page.svelte index 22c6d4f81a..07fc075350 100644 --- a/sites/svelte.dev/src/routes/examples/[slug]/+page.svelte +++ b/sites/svelte.dev/src/routes/examples/[slug]/+page.svelte @@ -34,7 +34,7 @@ - +

Examples

diff --git a/sites/svelte.dev/src/routes/tutorial/[slug]/+page.svelte b/sites/svelte.dev/src/routes/tutorial/[slug]/+page.svelte index 2ec28418d4..5b8223b35b 100644 --- a/sites/svelte.dev/src/routes/tutorial/[slug]/+page.svelte +++ b/sites/svelte.dev/src/routes/tutorial/[slug]/+page.svelte @@ -102,7 +102,7 @@ - + From 2698716bdf7e40357180e463f49db935eaae5a05 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:36:16 +0200 Subject: [PATCH 4/5] docs: fine-tune $props types info (#12534) Show how to do optional props, add jsdoc example to non-preview-docs closes #12528 --- .../01-component-fundamentals.md | 26 ++++++++++++++++--- .../routes/docs/content/01-api/02-runes.md | 13 +++++++--- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/documentation/docs/02-template-syntax/01-component-fundamentals.md b/documentation/docs/02-template-syntax/01-component-fundamentals.md index 5e4d78e59a..debe4828d2 100644 --- a/documentation/docs/02-template-syntax/01-component-fundamentals.md +++ b/documentation/docs/02-template-syntax/01-component-fundamentals.md @@ -72,13 +72,31 @@ If you're using TypeScript, you can declare the prop types: ```svelte +``` + +If you're using JavaScript, you can declare the prop types using JSDoc: + +```svelte + ``` diff --git a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md index 879e39e5f0..5f0ee7c0ea 100644 --- a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md +++ b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md @@ -556,17 +556,22 @@ let props = $props(); If you're using TypeScript, you can declare the prop types: + ```ts -type MyProps = any; -// ---cut--- -let { a, b, c, ...everythingElse }: MyProps = $props(); +interface MyProps { + required: string; + optional?: number; + partOfEverythingElse?: boolean; +}; + +let { required, optional, ...everythingElse }: MyProps = $props(); ``` > In an earlier preview, `$props()` took a type argument. This caused bugs, since in a case like this... > > ```ts > // @errors: 2558 -> let { x = 42 } = $props<{ x: string }>(); +> let { x = 42 } = $props<{ x?: string }>(); > ``` > > ...TypeScript [widens the type](https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXwBIAHGHIgZwB4AVeAXnilQE8A+ACgEoAueagbgBQgiCAzwA3vAAe9eABYATPAC+c4qQqUp03uQwwsqAOaqOnIfCsB6a-AB6AfiA) of `x` to be `string | number`, instead of erroring. From 518d9854f5fa9826cdff4e8ebdecef2680f5fdfc Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 22 Jul 2024 14:55:33 +0100 Subject: [PATCH 5/5] chore: add warning for invalid render function of createRawSnippet (#12535) * chore: add warning for invalid render function of createRawSnippet * add test * warnings * Update packages/svelte/messages/client-warnings/warnings.md Co-authored-by: Rich Harris * build * build * build --------- Co-authored-by: Rich Harris --- .changeset/nervous-dolphins-allow.md | 5 +++++ .../svelte/messages/client-warnings/warnings.md | 4 ++++ .../src/internal/client/dom/blocks/snippet.js | 5 +++++ packages/svelte/src/internal/client/warnings.js | 12 ++++++++++++ .../create-raw-snippet-invalid-render/_config.js | 13 +++++++++++++ .../create-raw-snippet-invalid-render/main.svelte | 11 +++++++++++ 6 files changed, 50 insertions(+) create mode 100644 .changeset/nervous-dolphins-allow.md create mode 100644 packages/svelte/tests/runtime-runes/samples/create-raw-snippet-invalid-render/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/create-raw-snippet-invalid-render/main.svelte diff --git a/.changeset/nervous-dolphins-allow.md b/.changeset/nervous-dolphins-allow.md new file mode 100644 index 0000000000..c59dc94c06 --- /dev/null +++ b/.changeset/nervous-dolphins-allow.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: add warning for invalid render function of createRawSnippet diff --git a/packages/svelte/messages/client-warnings/warnings.md b/packages/svelte/messages/client-warnings/warnings.md index 9ffef25b01..7a5a495251 100644 --- a/packages/svelte/messages/client-warnings/warnings.md +++ b/packages/svelte/messages/client-warnings/warnings.md @@ -20,6 +20,10 @@ > Hydration failed because the initial UI does not match what was rendered on the server. The error occurred near %location% +## invalid_raw_snippet_render + +> The `render` function passed to `createRawSnippet` should return HTML for a single element + ## lifecycle_double_unmount > Tried to unmount a component that was not mounted diff --git a/packages/svelte/src/internal/client/dom/blocks/snippet.js b/packages/svelte/src/internal/client/dom/blocks/snippet.js index a920f6db3e..8a8b6af44a 100644 --- a/packages/svelte/src/internal/client/dom/blocks/snippet.js +++ b/packages/svelte/src/internal/client/dom/blocks/snippet.js @@ -11,6 +11,8 @@ import { import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; import { create_fragment_from_html } from '../reconciler.js'; import { assign_nodes } from '../template.js'; +import * as w from '../../warnings.js'; +import { DEV } from 'esm-env'; /** * @template {(node: TemplateNode, ...args: any[]) => void} SnippetFn @@ -89,6 +91,9 @@ export function createRawSnippet(fn) { var html = snippet.render().trim(); var fragment = create_fragment_from_html(html); element = /** @type {Element} */ (fragment.firstChild); + if (DEV && (element.nextSibling !== null || element.nodeType !== 3)) { + w.invalid_raw_snippet_render(); + } anchor.before(element); } diff --git a/packages/svelte/src/internal/client/warnings.js b/packages/svelte/src/internal/client/warnings.js index e1a6aa4325..31f67a725a 100644 --- a/packages/svelte/src/internal/client/warnings.js +++ b/packages/svelte/src/internal/client/warnings.js @@ -60,6 +60,18 @@ export function hydration_mismatch(location) { } } +/** + * The `render` function passed to `createRawSnippet` should return HTML for a single element + */ +export function invalid_raw_snippet_render() { + if (DEV) { + console.warn(`%c[svelte] invalid_raw_snippet_render\n%cThe \`render\` function passed to \`createRawSnippet\` should return HTML for a single element`, bold, normal); + } else { + // TODO print a link to the documentation + console.warn("invalid_raw_snippet_render"); + } +} + /** * Tried to unmount a component that was not mounted */ diff --git a/packages/svelte/tests/runtime-runes/samples/create-raw-snippet-invalid-render/_config.js b/packages/svelte/tests/runtime-runes/samples/create-raw-snippet-invalid-render/_config.js new file mode 100644 index 0000000000..fb05b78e98 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/create-raw-snippet-invalid-render/_config.js @@ -0,0 +1,13 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + + skip_mode: ['hydrate'], + + warnings: [ + 'The `render` function passed to `createRawSnippet` should return HTML for a single element' + ] +}); diff --git a/packages/svelte/tests/runtime-runes/samples/create-raw-snippet-invalid-render/main.svelte b/packages/svelte/tests/runtime-runes/samples/create-raw-snippet-invalid-render/main.svelte new file mode 100644 index 0000000000..e35f01800d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/create-raw-snippet-invalid-render/main.svelte @@ -0,0 +1,11 @@ + + +{@render snippet()}