From 637364104538cdac26e3807fd71e4be6dd2958f8 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:32:47 +0100 Subject: [PATCH 01/29] docs: add example to "state instead of stores" (#14310) closes #13879 --- documentation/docs/06-runtime/01-stores.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/documentation/docs/06-runtime/01-stores.md b/documentation/docs/06-runtime/01-stores.md index 8ea01e53149d..9cff5a754f87 100644 --- a/documentation/docs/06-runtime/01-stores.md +++ b/documentation/docs/06-runtime/01-stores.md @@ -38,6 +38,28 @@ Prior to Svelte 5, stores were the go-to solution for creating cross-component r - when extracting logic, it's better to take advantage of runes' universal reactivity: You can use runes outside the top level of components and even place them into JavaScript or TypeScript files (using a `.svelte.js` or `.svelte.ts` file ending) - when creating shared state, you can create a `$state` object containing the values you need and then manipulate said state +```ts +/// file: state.svelte.js +export const userState = $state({ + name: 'name', + /* ... */ +}); +``` + +```svelte + + + +

User name: {userState.name}

+ +``` + Stores are still a good solution when you have complex asynchronous data streams or it's important to have more manual control over updating values or listening to changes. If you're familiar with RxJs and want to reuse that knowledge, the `$` also comes in handy for you. ## svelte/store From efc65d4e0c185cd597a6eeef013f6641b8e25d50 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:33:10 +0100 Subject: [PATCH 02/29] fix: mark pseudo classes nested inside `:not` as used (#14303) fixes the css bug part of #14299 --- .changeset/chatty-singers-sin.md | 5 +++++ .../src/compiler/phases/2-analyze/css/css-analyze.js | 3 ++- .../svelte/src/compiler/phases/2-analyze/css/css-prune.js | 7 ++++++- .../svelte/tests/css/samples/not-selector/expected.css | 4 ++++ .../svelte/tests/css/samples/not-selector/input.svelte | 4 ++++ 5 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 .changeset/chatty-singers-sin.md diff --git a/.changeset/chatty-singers-sin.md b/.changeset/chatty-singers-sin.md new file mode 100644 index 000000000000..f69bc974543d --- /dev/null +++ b/.changeset/chatty-singers-sin.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: mark pseudo classes nested inside `:not` as used diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js index 38551f328f37..1dd2d9ae7c36 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js @@ -150,8 +150,9 @@ const css_visitors = { // So that nested selectors like `:root:not(.x)` are not marked as unused for (const child of node.selectors) { walk(/** @type {Css.Node} */ (child), null, { - ComplexSelector(node) { + ComplexSelector(node, context) { node.metadata.used = true; + context.next(); } }); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index bf0fd275662c..bfb83d3e024d 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -531,7 +531,12 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element, // with descendants, in which case we scope them all. if (name === 'not' && selector.args) { for (const complex_selector of selector.args.children) { - complex_selector.metadata.used = true; + walk(complex_selector, null, { + ComplexSelector(node, context) { + node.metadata.used = true; + context.next(); + } + }); const relative = truncate(complex_selector); if (complex_selector.children.length > 1) { diff --git a/packages/svelte/tests/css/samples/not-selector/expected.css b/packages/svelte/tests/css/samples/not-selector/expected.css index ea0b3aacacd9..1b0db0ff4b63 100644 --- a/packages/svelte/tests/css/samples/not-selector/expected.css +++ b/packages/svelte/tests/css/samples/not-selector/expected.css @@ -29,3 +29,7 @@ span.svelte-xyz:not(:focus) { color: green; } + + p.svelte-xyz:not(:has(span)) { + color: green; + } diff --git a/packages/svelte/tests/css/samples/not-selector/input.svelte b/packages/svelte/tests/css/samples/not-selector/input.svelte index 3a786530a2ee..da8f4579c233 100644 --- a/packages/svelte/tests/css/samples/not-selector/input.svelte +++ b/packages/svelte/tests/css/samples/not-selector/input.svelte @@ -36,4 +36,8 @@ span:not(:focus) { color: green; } + + p:not(:has(span)) { + color: green; + } \ No newline at end of file From 1f0700f5c597174e51d6e07407b078c8314486fc Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Fri, 15 Nov 2024 23:40:08 +0100 Subject: [PATCH 03/29] fix: mark subtree dynamic for img with loading attribute (#14317) * fix: mark subtree dynamic for img with loading attribute * chore: unify conditions * chore: change conditions --- .changeset/rotten-turkeys-sniff.md | 5 +++++ .../compiler/phases/2-analyze/visitors/Attribute.js | 11 ++++++++--- .../_expected/client/index.svelte.js | 6 +++++- .../_expected/server/index.svelte.js | 2 +- .../snapshot/samples/skip-static-subtree/index.svelte | 5 ++++- 5 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 .changeset/rotten-turkeys-sniff.md diff --git a/.changeset/rotten-turkeys-sniff.md b/.changeset/rotten-turkeys-sniff.md new file mode 100644 index 000000000000..53fb5bd66327 --- /dev/null +++ b/.changeset/rotten-turkeys-sniff.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: mark subtree dynamic for img with loading attribute diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index 6931f873fbf7..ca0ea55d1fb0 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -18,9 +18,14 @@ export function Attribute(node, context) { const parent = /** @type {SvelteNode} */ (context.path.at(-1)); - // special case - if (node.name === 'value') { - if (parent.type === 'RegularElement' && parent.name === 'option') { + if (parent.type === 'RegularElement') { + // special case - \ No newline at end of file + +
+ +
\ No newline at end of file From 94471ca38e9752c08dde1620916b76ff517b369b Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 15 Nov 2024 23:50:47 +0100 Subject: [PATCH 04/29] fix: avoid relying on Node specifics within compiler (#14314) fixes #14294 --- .changeset/strange-adults-visit.md | 5 +++++ packages/svelte/src/compiler/validate-options.js | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 .changeset/strange-adults-visit.md diff --git a/.changeset/strange-adults-visit.md b/.changeset/strange-adults-visit.md new file mode 100644 index 000000000000..06cc667dacbe --- /dev/null +++ b/.changeset/strange-adults-visit.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: avoid relying on Node specifics within compiler diff --git a/packages/svelte/src/compiler/validate-options.js b/packages/svelte/src/compiler/validate-options.js index 9465cba77a06..ab932ed5bca1 100644 --- a/packages/svelte/src/compiler/validate-options.js +++ b/packages/svelte/src/compiler/validate-options.js @@ -1,5 +1,3 @@ -import process from 'node:process'; - /** @import { ModuleCompileOptions, ValidatedModuleCompileOptions, CompileOptions, ValidatedCompileOptions } from '#compiler' */ import * as e from './errors.js'; import * as w from './warnings.js'; @@ -13,9 +11,19 @@ import * as w from './warnings.js'; const common = { filename: string('(unknown)'), - // default to process.cwd() where it exists to replicate svelte4 behavior + // default to process.cwd() where it exists to replicate svelte4 behavior (and make Deno work with this as well) // see https://github.com/sveltejs/svelte/blob/b62fc8c8fd2640c9b99168f01b9d958cb2f7574f/packages/svelte/src/compiler/compile/Component.js#L211 - rootDir: string(typeof process !== 'undefined' ? process.cwd?.() : undefined), + /* eslint-disable */ + rootDir: string( + typeof process !== 'undefined' + ? process.cwd?.() + : // @ts-expect-error + typeof Deno !== 'undefined' + ? // @ts-expect-error + Deno.cwd() + : undefined + ), + /* eslint-enable */ dev: boolean(false), From 95ab85fad742d9911bbe87561dfaf9f868f75eaf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 16 Nov 2024 00:07:08 +0000 Subject: [PATCH 05/29] Version Packages (#14304) Co-authored-by: github-actions[bot] --- .changeset/chatty-singers-sin.md | 5 ----- .changeset/fast-ligers-repeat.md | 5 ----- .changeset/fresh-pigs-divide.md | 5 ----- .changeset/rotten-turkeys-sniff.md | 5 ----- .changeset/strange-adults-visit.md | 5 ----- packages/svelte/CHANGELOG.md | 14 ++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 8 files changed, 16 insertions(+), 27 deletions(-) delete mode 100644 .changeset/chatty-singers-sin.md delete mode 100644 .changeset/fast-ligers-repeat.md delete mode 100644 .changeset/fresh-pigs-divide.md delete mode 100644 .changeset/rotten-turkeys-sniff.md delete mode 100644 .changeset/strange-adults-visit.md diff --git a/.changeset/chatty-singers-sin.md b/.changeset/chatty-singers-sin.md deleted file mode 100644 index f69bc974543d..000000000000 --- a/.changeset/chatty-singers-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: mark pseudo classes nested inside `:not` as used diff --git a/.changeset/fast-ligers-repeat.md b/.changeset/fast-ligers-repeat.md deleted file mode 100644 index c6b35c437367..000000000000 --- a/.changeset/fast-ligers-repeat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: disallow invalid attributes for `` and `` diff --git a/.changeset/fresh-pigs-divide.md b/.changeset/fresh-pigs-divide.md deleted file mode 100644 index 8aa3d74ce9ee..000000000000 --- a/.changeset/fresh-pigs-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure props passed to components via mount are updateable diff --git a/.changeset/rotten-turkeys-sniff.md b/.changeset/rotten-turkeys-sniff.md deleted file mode 100644 index 53fb5bd66327..000000000000 --- a/.changeset/rotten-turkeys-sniff.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: mark subtree dynamic for img with loading attribute diff --git a/.changeset/strange-adults-visit.md b/.changeset/strange-adults-visit.md deleted file mode 100644 index 06cc667dacbe..000000000000 --- a/.changeset/strange-adults-visit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: avoid relying on Node specifics within compiler diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index f7e17f14f994..58883484099c 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,19 @@ # svelte +## 5.2.1 + +### Patch Changes + +- fix: mark pseudo classes nested inside `:not` as used ([#14303](https://github.com/sveltejs/svelte/pull/14303)) + +- fix: disallow invalid attributes for `` and `` ([#14228](https://github.com/sveltejs/svelte/pull/14228)) + +- fix: ensure props passed to components via mount are updateable ([#14210](https://github.com/sveltejs/svelte/pull/14210)) + +- fix: mark subtree dynamic for img with loading attribute ([#14317](https://github.com/sveltejs/svelte/pull/14317)) + +- fix: avoid relying on Node specifics within compiler ([#14314](https://github.com/sveltejs/svelte/pull/14314)) + ## 5.2.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index b23d031a17f4..9989394873bd 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.2.0", + "version": "5.2.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index e3d34edc4eec..1f6874e75e7f 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.2.0'; +export const VERSION = '5.2.1'; export const PUBLIC_VERSION = '5'; From 3a69b4c41533757ea0e3c7e293bd41818a833ead Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Sat, 16 Nov 2024 17:10:45 +0000 Subject: [PATCH 06/29] fix: ensure inline object literals are correctly serialised (#14325) * fix: ensure inline object literals are correctly serialised * Apply suggestions from code review * address feedback * address feedback --------- Co-authored-by: Rich Harris --- .changeset/wicked-readers-knock.md | 5 ++++ .../phases/2-analyze/visitors/shared/utils.js | 27 ++++++++++++++++--- .../samples/inline-expressions-2/_config.js | 11 ++++++++ .../samples/inline-expressions-2/main.svelte | 6 +++++ 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 .changeset/wicked-readers-knock.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte diff --git a/.changeset/wicked-readers-knock.md b/.changeset/wicked-readers-knock.md new file mode 100644 index 000000000000..4ec417402f20 --- /dev/null +++ b/.changeset/wicked-readers-knock.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: treat property accesses of literals as pure diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js index 82356ea619c1..2dec4361c8a0 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js @@ -1,4 +1,4 @@ -/** @import { AssignmentExpression, Expression, Identifier, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */ +/** @import { AssignmentExpression, Expression, Literal, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */ /** @import { AST, Binding } from '#compiler' */ /** @import { AnalysisState, Context } from '../../types' */ /** @import { Scope } from '../../../scope' */ @@ -176,21 +176,42 @@ export function is_safe_identifier(expression, scope) { } /** - * @param {Expression | Super} node + * @param {Expression | Literal | Super} node * @param {Context} context * @returns {boolean} */ export function is_pure(node, context) { + if (node.type === 'Literal') { + return true; + } + if (node.type === 'CallExpression') { + if (!is_pure(node.callee, context)) { + return false; + } + for (let arg of node.arguments) { + if (!is_pure(arg.type === 'SpreadElement' ? arg.argument : arg, context)) { + return false; + } + } + return true; + } if (node.type !== 'Identifier' && node.type !== 'MemberExpression') { return false; } - const left = object(node); + /** @type {Expression | Super | null} */ + let left = node; + while (left.type === 'MemberExpression') { + left = left.object; + } + if (!left) return false; if (left.type === 'Identifier') { const binding = context.state.scope.get(left.name); if (binding === null) return true; // globals are assumed to be safe + } else if (is_pure(left, context)) { + return true; } // TODO add more cases (safe Svelte imports, etc) diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js new file mode 100644 index 000000000000..1869540b2c55 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; + +export default test({ + html: ` +

Without text expression: 7.36

+

With text expression: 7.36

+

With text expression and function call: 7.36

+

With text expression and property access: 4

+

Hello name!

+

4

` +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte new file mode 100644 index 000000000000..78a325dcc420 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte @@ -0,0 +1,6 @@ +

Without text expression: 7.36

+

With text expression: {7.36}

+

With text expression and function call: {(7.36).toLocaleString()}

+

With text expression and property access: {"test".length}

+

Hello {('name').toUpperCase().toLowerCase()}!

+

{"test".length}

From 396ea2ef370e7ea5b5d4571c4e5e14384bac3ca6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 16 Nov 2024 17:13:21 +0000 Subject: [PATCH 07/29] Version Packages (#14328) Co-authored-by: github-actions[bot] --- .changeset/wicked-readers-knock.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/wicked-readers-knock.md diff --git a/.changeset/wicked-readers-knock.md b/.changeset/wicked-readers-knock.md deleted file mode 100644 index 4ec417402f20..000000000000 --- a/.changeset/wicked-readers-knock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: treat property accesses of literals as pure diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 58883484099c..64b04e0fc2c5 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.2.2 + +### Patch Changes + +- fix: treat property accesses of literals as pure ([#14325](https://github.com/sveltejs/svelte/pull/14325)) + ## 5.2.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 9989394873bd..8c9efb00de50 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.2.1", + "version": "5.2.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 1f6874e75e7f..db950a129a3d 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.2.1'; +export const VERSION = '5.2.2'; export const PUBLIC_VERSION = '5'; From 24ebbcbbc19701987d7560c4fe3952e8c24432ef Mon Sep 17 00:00:00 2001 From: Gwen Le Bihan Date: Sun, 17 Nov 2024 23:27:34 +0100 Subject: [PATCH 08/29] docs: use e.detail instead of e.details in migration examples (#14340) --- documentation/docs/07-misc/07-v5-migration-guide.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/docs/07-misc/07-v5-migration-guide.md b/documentation/docs/07-misc/07-v5-migration-guide.md index 4ebfb5f905cc..687f8e93b11f 100644 --- a/documentation/docs/07-misc/07-v5-migration-guide.md +++ b/documentation/docs/07-misc/07-v5-migration-guide.md @@ -169,11 +169,11 @@ This function is deprecated in Svelte 5. Instead, components should accept _call { - size += power---.details---; + size += power---.detail---; if (size > 75) burst = true; }} ---on:---deflate={(power) => { - if (size > 0) size -= power---.details---; + if (size > 0) size -= power---.detail---; }} /> @@ -317,7 +317,7 @@ When spreading props, local event handlers must go _after_ the spread, or they r > - import the function > - call the function to get a dispatch function > - call said dispatch function with a string and possibly a payload -> - retrieve said payload on the other end through a `.details` property, because the event itself was always a `CustomEvent` +> - retrieve said payload on the other end through a `.detail` property, because the event itself was always a `CustomEvent` > > It was always possible to use component callback props, but because you had to listen to DOM events using `on:`, it made sense to use `createEventDispatcher` for component events due to syntactical consistency. Now that we have event attributes (`onclick`), it's the other way around: Callback props are now the more sensible thing to do. > From 1c454c236eeb00fab04007b4c8b9ae0f475e9cf6 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 18 Nov 2024 14:24:55 +0000 Subject: [PATCH 09/29] fix: ensure dynamic call expressions correctly generate output (#14345) --- .changeset/moody-bears-change.md | 5 +++++ .../src/compiler/phases/2-analyze/visitors/CallExpression.js | 2 ++ .../samples/inline-expressions-subtree/_config.js | 5 +++++ .../samples/inline-expressions-subtree/main.svelte | 4 ++++ 4 files changed, 16 insertions(+) create mode 100644 .changeset/moody-bears-change.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/main.svelte diff --git a/.changeset/moody-bears-change.md b/.changeset/moody-bears-change.md new file mode 100644 index 000000000000..0bf32026d2ed --- /dev/null +++ b/.changeset/moody-bears-change.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure dynamic call expressions correctly generate output diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index 2ae32e80e1ba..fe3f638a69f9 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -5,6 +5,7 @@ import { get_rune } from '../../scope.js'; import * as e from '../../../errors.js'; import { get_parent, unwrap_optional } from '../../../utils/ast.js'; import { is_pure, is_safe_identifier } from './shared/utils.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {CallExpression} node @@ -179,6 +180,7 @@ export function CallExpression(node, context) { context.state.expression.has_call = true; context.state.expression.has_state = true; context.state.expression.can_inline = false; + mark_subtree_dynamic(context.path); } } } diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/_config.js b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/_config.js new file mode 100644 index 000000000000..17c573a5105c --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: 'A\n
B
' +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/main.svelte b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/main.svelte new file mode 100644 index 000000000000..555dc8af23a1 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-subtree/main.svelte @@ -0,0 +1,4 @@ + 'red')()}>A +
+ 'red')()}>B +
From b145035a00525770abe9116e3887c050e19340ea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:23:13 +0000 Subject: [PATCH 10/29] Version Packages (#14348) Co-authored-by: github-actions[bot] --- .changeset/moody-bears-change.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/moody-bears-change.md diff --git a/.changeset/moody-bears-change.md b/.changeset/moody-bears-change.md deleted file mode 100644 index 0bf32026d2ed..000000000000 --- a/.changeset/moody-bears-change.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure dynamic call expressions correctly generate output diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 64b04e0fc2c5..d36307c80cac 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.2.3 + +### Patch Changes + +- fix: ensure dynamic call expressions correctly generate output ([#14345](https://github.com/sveltejs/svelte/pull/14345)) + ## 5.2.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 8c9efb00de50..50319c77aa2f 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.2.2", + "version": "5.2.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index db950a129a3d..b79e9a2891e2 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.2.2'; +export const VERSION = '5.2.3'; export const PUBLIC_VERSION = '5'; From 012166ec3c2a4f199d0314444519096bc21d3715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santiago=20C=C3=A9zar?= <48573316+santiagocezar@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:48:01 -0300 Subject: [PATCH 11/29] fix: convert input value to number on hydration (#14349) * convert input value to number on hydration * add test * changeset --------- Co-authored-by: Rich Harris --- .changeset/wicked-grapes-flash.md | 5 +++++ .../client/dom/elements/bindings/input.js | 2 +- .../hydrate-modified-input-numeric/_config.js | 17 +++++++++++++++++ .../hydrate-modified-input-numeric/main.svelte | 6 ++++++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .changeset/wicked-grapes-flash.md create mode 100644 packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/main.svelte diff --git a/.changeset/wicked-grapes-flash.md b/.changeset/wicked-grapes-flash.md new file mode 100644 index 000000000000..75ff68a728b9 --- /dev/null +++ b/.changeset/wicked-grapes-flash.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: coerce value to number when hydrating range/number input with changed value diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/input.js b/packages/svelte/src/internal/client/dom/elements/bindings/input.js index e528d1699e48..aec6f815a012 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/input.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/input.js @@ -45,7 +45,7 @@ export function bind_value(input, get, set = get) { // If we are hydrating and the value has since changed, then use the update value // from the input instead. if (hydrating && input.defaultValue !== input.value) { - set(input.value); + set(is_numberlike_input(input) ? to_number(input.value) : input.value); return; } diff --git a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/_config.js b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/_config.js new file mode 100644 index 000000000000..f4b5037890f0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/_config.js @@ -0,0 +1,17 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + skip_mode: ['client'], + + test({ assert, target, hydrate }) { + const input = /** @type {HTMLInputElement} */ (target.querySelector('input')); + input.value = '1'; + input.dispatchEvent(new window.Event('input')); + // Hydration shouldn't reset the value to empty + hydrate(); + flushSync(); + + assert.htmlEqual(target.innerHTML, '\n1 (number)'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/main.svelte b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/main.svelte new file mode 100644 index 000000000000..a10f3cec1e12 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-numeric/main.svelte @@ -0,0 +1,6 @@ + + + +{value} ({typeof value}) From 6e8267f46277592f6f6afb6f77d62d3d9d740605 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 19 Nov 2024 16:48:30 +0000 Subject: [PATCH 12/29] fix: correctly update dynamic member expressions (#14359) * fix: output template effect for svg xlink attribute * mark subtree dynamic in MemberExpression visitor * don't treat attributes and text nodes differently * Update .changeset/serious-spiders-bake.md --------- Co-authored-by: Rich Harris --- .changeset/serious-spiders-bake.md | 5 +++++ .../phases/2-analyze/visitors/Identifier.js | 13 ++++--------- .../phases/2-analyze/visitors/MemberExpression.js | 3 +++ .../samples/inline-expressions-3/_config.js | 5 +++++ .../samples/inline-expressions-3/main.svelte | 9 +++++++++ .../samples/inline-expressions-3/sprites.js | 3 +++ .../_expected/client/index.svelte.js | 2 +- 7 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 .changeset/serious-spiders-bake.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/main.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/sprites.js diff --git a/.changeset/serious-spiders-bake.md b/.changeset/serious-spiders-bake.md new file mode 100644 index 000000000000..b1f9d84a3f28 --- /dev/null +++ b/.changeset/serious-spiders-bake.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly update dynamic member expressions diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js index 635f939c7520..042c467df1ed 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js @@ -126,16 +126,11 @@ export function Identifier(node, context) { } } - if (!can_inline && context.state.expression) { - context.state.expression.can_inline = false; - } + if (!can_inline) { + if (context.state.expression) { + context.state.expression.can_inline = false; + } - /** - * if the identifier is part of an expression tag of an attribute we want to check if it's inlinable - * before marking the subtree as dynamic. This is because if it's inlinable it will be inlined in the template - * directly making the whole thing actually static. - */ - if (!can_inline || !context.path.find((node) => node.type === 'Attribute')) { mark_subtree_dynamic(context.path); } } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js index adcc2da4226e..88adecbd359a 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js @@ -4,6 +4,7 @@ import * as e from '../../../errors.js'; import * as w from '../../../warnings.js'; import { object } from '../../../utils/ast.js'; import { is_pure, is_safe_identifier } from './shared/utils.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {MemberExpression} node @@ -20,6 +21,8 @@ export function MemberExpression(node, context) { if (context.state.expression && !is_pure(node, context)) { context.state.expression.has_state = true; context.state.expression.can_inline = false; + + mark_subtree_dynamic(context.path); } if (!is_safe_identifier(node, context.state.scope)) { diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/_config.js b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/_config.js new file mode 100644 index 000000000000..c229f97e0709 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: `
+ import { sprites } from './sprites.js' + + +
+ +
diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/sprites.js b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/sprites.js new file mode 100644 index 000000000000..055fdbd59a64 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-3/sprites.js @@ -0,0 +1,3 @@ +export const sprites = { + a: 'test' +}; diff --git a/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js index 75e632239e0b..462262442437 100644 --- a/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js @@ -11,7 +11,7 @@ var root = $.template(` + {#each tiles as { x, y }} +
+ {/each} +
diff --git a/benchmarking/benchmarks/ssr/wrapper/wrapper_bench.js b/benchmarking/benchmarks/ssr/wrapper/wrapper_bench.js new file mode 100644 index 000000000000..ba0457b80ea7 --- /dev/null +++ b/benchmarking/benchmarks/ssr/wrapper/wrapper_bench.js @@ -0,0 +1,36 @@ +import { render } from 'svelte/server'; +import { fastest_test, read_file, write } from '../../../utils.js'; +import { compile } from 'svelte/compiler'; + +const dir = `${process.cwd()}/benchmarking/benchmarks/ssr/wrapper`; + +async function compile_svelte() { + const output = compile(read_file(`${dir}/App.svelte`), { + generate: 'server' + }); + write(`${dir}/output/App.js`, output.js.code); + + const module = await import(`${dir}/output/App.js`); + + return module.default; +} + +export async function wrapper_bench() { + const App = await compile_svelte(); + // Do 3 loops to warm up JIT + for (let i = 0; i < 3; i++) { + render(App); + } + + const { timing } = await fastest_test(10, () => { + for (let i = 0; i < 100; i++) { + render(App); + } + }); + + return { + benchmark: 'wrapper_bench', + time: timing.time.toFixed(2), + gc_time: timing.gc_time.toFixed(2) + }; +} diff --git a/benchmarking/run.js b/benchmarking/run.js index 35a6ac307fd8..bd96b9c2dc5a 100644 --- a/benchmarking/run.js +++ b/benchmarking/run.js @@ -1,30 +1,53 @@ import * as $ from '../packages/svelte/src/internal/client/index.js'; -import { benchmarks } from './benchmarks.js'; +import { reactivity_benchmarks } from './benchmarks/reactivity/index.js'; +import { ssr_benchmarks } from './benchmarks/ssr/index.js'; let total_time = 0; let total_gc_time = 0; +const suites = [ + { benchmarks: reactivity_benchmarks, name: 'reactivity benchmarks' }, + { benchmarks: ssr_benchmarks, name: 'server-side rendering benchmarks' } +]; + // eslint-disable-next-line no-console -console.log('-- Benchmarking Started --'); +console.log('\x1b[1m', '-- Benchmarking Started --', '\x1b[0m'); $.push({}, true); try { - for (const benchmark of benchmarks) { - const results = await benchmark(); + for (const { benchmarks, name } of suites) { + let suite_time = 0; + let suite_gc_time = 0; + // eslint-disable-next-line no-console + console.log(`\nRunning ${name}...\n`); + + for (const benchmark of benchmarks) { + const results = await benchmark(); + // eslint-disable-next-line no-console + console.log(results); + total_time += Number(results.time); + total_gc_time += Number(results.gc_time); + suite_time += Number(results.time); + suite_gc_time += Number(results.gc_time); + } + + console.log(`\nFinished ${name}.\n`); + // eslint-disable-next-line no-console - console.log(results); - total_time += Number(results.time); - total_gc_time += Number(results.gc_time); + console.log({ + suite_time: suite_time.toFixed(2), + suite_gc_time: suite_gc_time.toFixed(2) + }); } } catch (e) { // eslint-disable-next-line no-console - console.error('-- Benchmarking Failed --'); + console.log('\x1b[1m', '\n-- Benchmarking Failed --\n', '\x1b[0m'); // eslint-disable-next-line no-console console.error(e); process.exit(1); } $.pop(); // eslint-disable-next-line no-console -console.log('-- Benchmarking Complete --'); +console.log('\x1b[1m', '\n-- Benchmarking Complete --\n', '\x1b[0m'); // eslint-disable-next-line no-console console.log({ total_time: total_time.toFixed(2), diff --git a/benchmarking/utils.js b/benchmarking/utils.js index db2fa753ee64..684d2ee02b4d 100644 --- a/benchmarking/utils.js +++ b/benchmarking/utils.js @@ -1,5 +1,7 @@ import { performance, PerformanceObserver } from 'node:perf_hooks'; import v8 from 'v8-natives'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; // Credit to https://github.com/milomg/js-reactivity-benchmark for the logic for timing + GC tracking. @@ -96,3 +98,22 @@ export function assert(a) { throw new Error('Assertion failed'); } } + +/** + * @param {string} file + */ +export function read_file(file) { + return fs.readFileSync(file, 'utf-8').replace(/\r\n/g, '\n'); +} + +/** + * @param {string} file + * @param {string} contents + */ +export function write(file, contents) { + try { + fs.mkdirSync(path.dirname(file), { recursive: true }); + } catch {} + + fs.writeFileSync(file, contents); +} From 741106879bc60bb8b4e5daf3884032a04c803dd2 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 19 Nov 2024 18:11:30 +0000 Subject: [PATCH 14/29] fix: ensure internal cloning can work circular values (#14347) * fix: ensure internal cloning can work circular values * better fixc * 'original' feels slightly clearer than 'json_instance' * use an optional parameter, so we can omit it in most cases * Update packages/svelte/src/internal/shared/clone.js Co-authored-by: Rich Harris --------- Co-authored-by: Rich Harris --- .changeset/cool-trains-yawn.md | 5 ++++ packages/svelte/src/internal/shared/clone.js | 15 ++++++++++-- .../samples/inspect-recursive-2/_config.js | 23 +++++++++++++++++++ .../samples/inspect-recursive-2/main.svelte | 23 +++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 .changeset/cool-trains-yawn.md create mode 100644 packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/main.svelte diff --git a/.changeset/cool-trains-yawn.md b/.changeset/cool-trains-yawn.md new file mode 100644 index 000000000000..d5198d118c52 --- /dev/null +++ b/.changeset/cool-trains-yawn.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure internal cloning can work circular values diff --git a/packages/svelte/src/internal/shared/clone.js b/packages/svelte/src/internal/shared/clone.js index bfdc9af26390..cef14bc14f9e 100644 --- a/packages/svelte/src/internal/shared/clone.js +++ b/packages/svelte/src/internal/shared/clone.js @@ -49,9 +49,10 @@ export function snapshot(value, skip_warning = false) { * @param {Map>} cloned * @param {string} path * @param {string[]} paths + * @param {null | T} original The original value, if `value` was produced from a `toJSON` call * @returns {Snapshot} */ -function clone(value, cloned, path, paths) { +function clone(value, cloned, path, paths, original = null) { if (typeof value === 'object' && value !== null) { const unwrapped = cloned.get(value); if (unwrapped !== undefined) return unwrapped; @@ -63,6 +64,10 @@ function clone(value, cloned, path, paths) { const copy = /** @type {Snapshot} */ ([]); cloned.set(value, copy); + if (original !== null) { + cloned.set(original, copy); + } + for (let i = 0; i < value.length; i += 1) { copy.push(clone(value[i], cloned, DEV ? `${path}[${i}]` : path, paths)); } @@ -75,6 +80,10 @@ function clone(value, cloned, path, paths) { const copy = {}; cloned.set(value, copy); + if (original !== null) { + cloned.set(original, copy); + } + for (var key in value) { // @ts-expect-error copy[key] = clone(value[key], cloned, DEV ? `${path}.${key}` : path, paths); @@ -92,7 +101,9 @@ function clone(value, cloned, path, paths) { /** @type {T & { toJSON(): any } } */ (value).toJSON(), cloned, DEV ? `${path}.toJSON()` : path, - paths + paths, + // Associate the instance with the toJSON clone + value ); } } diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js new file mode 100644 index 000000000000..ab496971955f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js @@ -0,0 +1,23 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + + async test({ assert, logs }) { + var a = { + a: {} + }; + a.a = a; + + var b = { + a: { + b: {} + } + }; + b.a.b = b; + + assert.deepEqual(logs, ['init', a, 'init', b]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/main.svelte new file mode 100644 index 000000000000..f7874d2192ce --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/main.svelte @@ -0,0 +1,23 @@ + From 747d40833b024d2b5ea5cf0de8e6198cbd3af23c Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 19 Nov 2024 19:04:40 +0000 Subject: [PATCH 15/29] fix: ensure is_pure takes into account runes (#14333) * fix: ensure is_pure takes into account runes * feedback --- .changeset/slimy-islands-cry.md | 5 +++++ .../phases/2-analyze/visitors/shared/utils.js | 10 ++++++++- .../samples/inline-expressions-2/_config.js | 11 ---------- .../samples/inline-expressions/_config.js | 21 +++++++++++++++++++ .../samples/inline-expressions}/main.svelte | 1 + 5 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 .changeset/slimy-islands-cry.md delete mode 100644 packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/inline-expressions/_config.js rename packages/svelte/tests/{runtime-legacy/samples/inline-expressions-2 => runtime-runes/samples/inline-expressions}/main.svelte (87%) diff --git a/.changeset/slimy-islands-cry.md b/.changeset/slimy-islands-cry.md new file mode 100644 index 000000000000..f7f34b60e171 --- /dev/null +++ b/.changeset/slimy-islands-cry.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure is_pure takes into account $effect.tracking() diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js index 2dec4361c8a0..8698174c6b53 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js @@ -4,8 +4,10 @@ /** @import { Scope } from '../../../scope' */ /** @import { NodeLike } from '../../../../errors.js' */ import * as e from '../../../../errors.js'; -import { extract_identifiers, object } from '../../../../utils/ast.js'; +import { extract_identifiers } from '../../../../utils/ast.js'; import * as w from '../../../../warnings.js'; +import * as b from '../../../../utils/builders.js'; +import { get_rune } from '../../../scope.js'; /** * @param {AssignmentExpression | UpdateExpression} node @@ -184,6 +186,7 @@ export function is_pure(node, context) { if (node.type === 'Literal') { return true; } + if (node.type === 'CallExpression') { if (!is_pure(node.callee, context)) { return false; @@ -195,10 +198,15 @@ export function is_pure(node, context) { } return true; } + if (node.type !== 'Identifier' && node.type !== 'MemberExpression') { return false; } + if (get_rune(b.call(node), context.state.scope) === '$effect.tracking') { + return false; + } + /** @type {Expression | Super | null} */ let left = node; while (left.type === 'MemberExpression') { diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js deleted file mode 100644 index 1869540b2c55..000000000000 --- a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/_config.js +++ /dev/null @@ -1,11 +0,0 @@ -import { test } from '../../test'; - -export default test({ - html: ` -

Without text expression: 7.36

-

With text expression: 7.36

-

With text expression and function call: 7.36

-

With text expression and property access: 4

-

Hello name!

-

4

` -}); diff --git a/packages/svelte/tests/runtime-runes/samples/inline-expressions/_config.js b/packages/svelte/tests/runtime-runes/samples/inline-expressions/_config.js new file mode 100644 index 000000000000..c5d4a75379a5 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inline-expressions/_config.js @@ -0,0 +1,21 @@ +import { test } from '../../test'; + +export default test({ + html: ` +

Without text expression: 7.36

+

With text expression: 7.36

+

With text expression and function call: 7.36

+

With text expression and property access: 4

+

Hello name!

+

4

+

Tracking: true

`, + + ssrHtml: ` +

Without text expression: 7.36

+

With text expression: 7.36

+

With text expression and function call: 7.36

+

With text expression and property access: 4

+

Hello name!

+

4

+

Tracking: false

` +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/inline-expressions/main.svelte similarity index 87% rename from packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte rename to packages/svelte/tests/runtime-runes/samples/inline-expressions/main.svelte index 78a325dcc420..38a97415d2c1 100644 --- a/packages/svelte/tests/runtime-legacy/samples/inline-expressions-2/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/inline-expressions/main.svelte @@ -4,3 +4,4 @@

With text expression and property access: {"test".length}

Hello {('name').toUpperCase().toLowerCase()}!

{"test".length}

+

Tracking: {$effect.tracking()}

From 53cc60085e099352c04732f0653af03b63af624c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 14:05:50 -0500 Subject: [PATCH 16/29] Version Packages (#14362) Co-authored-by: github-actions[bot] --- .changeset/cool-trains-yawn.md | 5 ----- .changeset/serious-spiders-bake.md | 5 ----- .changeset/slimy-islands-cry.md | 5 ----- .changeset/wicked-grapes-flash.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 7 files changed, 14 insertions(+), 22 deletions(-) delete mode 100644 .changeset/cool-trains-yawn.md delete mode 100644 .changeset/serious-spiders-bake.md delete mode 100644 .changeset/slimy-islands-cry.md delete mode 100644 .changeset/wicked-grapes-flash.md diff --git a/.changeset/cool-trains-yawn.md b/.changeset/cool-trains-yawn.md deleted file mode 100644 index d5198d118c52..000000000000 --- a/.changeset/cool-trains-yawn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure internal cloning can work circular values diff --git a/.changeset/serious-spiders-bake.md b/.changeset/serious-spiders-bake.md deleted file mode 100644 index b1f9d84a3f28..000000000000 --- a/.changeset/serious-spiders-bake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly update dynamic member expressions diff --git a/.changeset/slimy-islands-cry.md b/.changeset/slimy-islands-cry.md deleted file mode 100644 index f7f34b60e171..000000000000 --- a/.changeset/slimy-islands-cry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure is_pure takes into account $effect.tracking() diff --git a/.changeset/wicked-grapes-flash.md b/.changeset/wicked-grapes-flash.md deleted file mode 100644 index 75ff68a728b9..000000000000 --- a/.changeset/wicked-grapes-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: coerce value to number when hydrating range/number input with changed value diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index d36307c80cac..f01f519150f4 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.2.4 + +### Patch Changes + +- fix: ensure internal cloning can work circular values ([#14347](https://github.com/sveltejs/svelte/pull/14347)) + +- fix: correctly update dynamic member expressions ([#14359](https://github.com/sveltejs/svelte/pull/14359)) + +- fix: ensure is_pure takes into account $effect.tracking() ([#14333](https://github.com/sveltejs/svelte/pull/14333)) + +- fix: coerce value to number when hydrating range/number input with changed value ([#14349](https://github.com/sveltejs/svelte/pull/14349)) + ## 5.2.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 50319c77aa2f..f6ba05339800 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.2.3", + "version": "5.2.4", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b79e9a2891e2..3a728f3d14c6 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.2.3'; +export const VERSION = '5.2.4'; export const PUBLIC_VERSION = '5'; From ce471310c49695550f8422ade46025f9818106c2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 19 Nov 2024 16:31:22 -0500 Subject: [PATCH 17/29] chore: remove some unused code (#14363) --- .../visitors/ExportNamedDeclaration.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js index 784d2fdd881e..547f6ab9c73e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js @@ -57,23 +57,5 @@ export function ExportNamedDeclaration(node, context) { } } } - - if (!context.state.ast_type /* .svelte.js module */ || context.state.ast_type === 'module') { - for (const specified of node.specifiers) { - if (specified.local.type !== 'Identifier') continue; - - const binding = context.state.scope.get(specified.local.name); - - if (!binding) continue; - - if (binding.kind === 'derived') { - e.derived_invalid_export(node); - } - - if ((binding.kind === 'state' || binding.kind === 'raw_state') && binding.reassigned) { - e.state_invalid_export(node); - } - } - } } } From 32a14538051a9ab382b467d4c5f772114809b7d1 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 19 Nov 2024 22:38:36 +0100 Subject: [PATCH 18/29] fix: include method definitions in class private fields (#14365) * fix: include method definitions in class private fields * Update packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/_config.js --------- Co-authored-by: Rich Harris --- .changeset/unlucky-icons-sit.md | 5 +++++ .../phases/3-transform/client/visitors/ClassBody.js | 2 +- .../_config.js | 3 +++ .../main.svelte | 8 ++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 .changeset/unlucky-icons-sit.md create mode 100644 packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/main.svelte diff --git a/.changeset/unlucky-icons-sit.md b/.changeset/unlucky-icons-sit.md new file mode 100644 index 000000000000..7fc14cf43524 --- /dev/null +++ b/.changeset/unlucky-icons-sit.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: include method definitions in class private fields diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js index 2d832b9df168..11a524d33c55 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js @@ -28,7 +28,7 @@ export function ClassBody(node, context) { for (const definition of node.body) { if ( - definition.type === 'PropertyDefinition' && + (definition.type === 'PropertyDefinition' || definition.type === 'MethodDefinition') && (definition.key.type === 'Identifier' || definition.key.type === 'PrivateIdentifier' || definition.key.type === 'Literal') diff --git a/packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/_config.js b/packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/_config.js new file mode 100644 index 000000000000..f47bee71df87 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/main.svelte new file mode 100644 index 000000000000..92f254049857 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-disabinguate-private-method-definition/main.svelte @@ -0,0 +1,8 @@ + From 4c98c2e4a6aaa54650cc834e109a0e2dabd5a574 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:52:03 +0100 Subject: [PATCH 19/29] chore: consolidate checks for never-static attributes (#14372) We're using string checks in various places, better to have it encapsulated in one function --- .../phases/2-analyze/visitors/RegularElement.js | 6 ++---- .../3-transform/client/visitors/RegularElement.js | 4 ++-- .../3-transform/client/visitors/shared/fragment.js | 3 ++- packages/svelte/src/utils.js | 11 +++++++++++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js index 60bd1dd0c5c7..d5964f9ae12a 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js @@ -1,6 +1,6 @@ /** @import { AST } from '#compiler' */ /** @import { Context } from '../types' */ -import { is_mathml, is_svg, is_void } from '../../../../utils.js'; +import { cannot_be_set_statically, is_mathml, is_svg, is_void } from '../../../../utils.js'; import { is_tag_valid_with_ancestor, is_tag_valid_with_parent @@ -77,9 +77,7 @@ export function RegularElement(node, context) { if ( node.attributes.some( - (attribute) => - attribute.type === 'Attribute' && - (attribute.name === 'autofocus' || attribute.name === 'muted') + (attribute) => attribute.type === 'Attribute' && cannot_be_set_statically(attribute.name) ) ) { mark_subtree_dynamic(context.path); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 455327a71219..19948464d2f0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -5,6 +5,7 @@ /** @import { Scope } from '../../../scope' */ import { escape_html } from '../../../../../escaping.js'; import { + cannot_be_set_statically, is_boolean_attribute, is_dom_property, is_load_error_element, @@ -262,8 +263,7 @@ export function RegularElement(node, context) { if ( !is_custom_element && - attribute.name !== 'autofocus' && - attribute.name !== 'muted' && + !cannot_be_set_statically(attribute.name) && (attribute.value === true || is_text_attribute(attribute)) ) { const name = get_attribute_name(node, attribute); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js index ac6e0f8f9fdb..102988781aca 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js @@ -3,6 +3,7 @@ /** @import { Scope } from '../../../../scope.js' */ /** @import { ComponentContext } from '../../types' */ import { escape_html } from '../../../../../../escaping.js'; +import { cannot_be_set_statically } from '../../../../../../utils.js'; import { is_event_attribute } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; import { build_template_chunk, build_update } from './utils.js'; @@ -142,7 +143,7 @@ function is_static_element(node) { return false; } - if (attribute.name === 'autofocus' || attribute.name === 'muted') { + if (cannot_be_set_statically(attribute.name)) { return false; } diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index 60ec364e6fdd..84c7ca1efb17 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -222,6 +222,17 @@ export function is_dom_property(name) { return DOM_PROPERTIES.includes(name); } +const NON_STATIC_PROPERTIES = ['autofocus', 'muted']; + +/** + * Returns `true` if the given attribute cannot be set through the template + * string, i.e. needs some kind of JavaScript handling to work. + * @param {string} name + */ +export function cannot_be_set_statically(name) { + return NON_STATIC_PROPERTIES.includes(name); +} + /** * Subset of delegated events which should be passive by default. * These two are already passive via browser defaults on window, document and body. From 4dfa0e31fe70174538ca47efe1b276ca13b2cd80 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:14:33 +0100 Subject: [PATCH 20/29] fix: tighten up `export default` validation (#14368) through #14363 I noticed our `export default` validation wasn't quite right: - missed checking for derived/state exports - the "cannot have a default export" error was only thrown if you did `export default` from the instance script, but it shouldn't matter from which component part you export it; it's never ok --- .changeset/new-houses-roll.md | 5 ++++ .../visitors/ExportDefaultDeclaration.js | 9 +++++-- .../2-analyze/visitors/ExportSpecifier.js | 25 ++----------------- .../phases/2-analyze/visitors/shared/utils.js | 21 +++++++++++++++- .../_config.js | 10 ++++++++ .../main.svelte.js | 5 ++++ .../export-default-state-indirect/_config.js | 10 ++++++++ .../main.svelte.js | 7 ++++++ .../samples/default-export-module/errors.json | 14 +++++++++++ .../default-export-module/input.svelte | 3 +++ 10 files changed, 83 insertions(+), 26 deletions(-) create mode 100644 .changeset/new-houses-roll.md create mode 100644 packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/main.svelte.js create mode 100644 packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/main.svelte.js create mode 100644 packages/svelte/tests/validator/samples/default-export-module/errors.json create mode 100644 packages/svelte/tests/validator/samples/default-export-module/input.svelte diff --git a/.changeset/new-houses-roll.md b/.changeset/new-houses-roll.md new file mode 100644 index 000000000000..a2b8370e065f --- /dev/null +++ b/.changeset/new-houses-roll.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: tighten up `export default` validation diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportDefaultDeclaration.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportDefaultDeclaration.js index 0a7461f15589..768f1c6305a4 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportDefaultDeclaration.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportDefaultDeclaration.js @@ -1,13 +1,18 @@ -/** @import { ExportDefaultDeclaration, Node } from 'estree' */ +/** @import { ExportDefaultDeclaration } from 'estree' */ /** @import { Context } from '../types' */ import * as e from '../../../errors.js'; +import { validate_export } from './shared/utils.js'; /** * @param {ExportDefaultDeclaration} node * @param {Context} context */ export function ExportDefaultDeclaration(node, context) { - if (context.state.ast_type === 'instance') { + if (!context.state.ast_type /* .svelte.js module */) { + if (node.declaration.type === 'Identifier') { + validate_export(node, context.state.scope, node.declaration.name); + } + } else { e.module_illegal_default_export(node); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js index d0d1ccf93211..cfb24970de21 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js @@ -1,8 +1,6 @@ -/** @import { ExportSpecifier, Node } from 'estree' */ -/** @import { Binding } from '#compiler' */ +/** @import { ExportSpecifier } from 'estree' */ /** @import { Context } from '../types' */ -/** @import { Scope } from '../../scope' */ -import * as e from '../../../errors.js'; +import { validate_export } from './shared/utils.js'; /** * @param {ExportSpecifier} node @@ -30,22 +28,3 @@ export function ExportSpecifier(node, context) { validate_export(node, context.state.scope, local_name); } } - -/** - * - * @param {Node} node - * @param {Scope} scope - * @param {string} name - */ -function validate_export(node, scope, name) { - const binding = scope.get(name); - if (!binding) return; - - if (binding.kind === 'derived') { - e.derived_invalid_export(node); - } - - if ((binding.kind === 'state' || binding.kind === 'raw_state') && binding.reassigned) { - e.state_invalid_export(node); - } -} diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js index 8698174c6b53..e265637c404d 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js @@ -1,4 +1,4 @@ -/** @import { AssignmentExpression, Expression, Literal, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */ +/** @import { AssignmentExpression, Expression, Literal, Node, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */ /** @import { AST, Binding } from '#compiler' */ /** @import { AnalysisState, Context } from '../../types' */ /** @import { Scope } from '../../../scope' */ @@ -263,3 +263,22 @@ export function validate_identifier_name(binding, function_depth) { } } } + +/** + * Checks that the exported name is not a derived or reassigned state variable. + * @param {Node} node + * @param {Scope} scope + * @param {string} name + */ +export function validate_export(node, scope, name) { + const binding = scope.get(name); + if (!binding) return; + + if (binding.kind === 'derived') { + e.derived_invalid_export(node); + } + + if ((binding.kind === 'state' || binding.kind === 'raw_state') && binding.reassigned) { + e.state_invalid_export(node); + } +} diff --git a/packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/_config.js b/packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/_config.js new file mode 100644 index 000000000000..4a362117699f --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'derived_invalid_export', + message: + 'Cannot export derived state from a module. To expose the current derived value, export a function returning its value', + position: [61, 83] + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/main.svelte.js b/packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/main.svelte.js new file mode 100644 index 000000000000..64a794492b0d --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/export-default-derived-state-indirect/main.svelte.js @@ -0,0 +1,5 @@ +let count = $state(0); + +const double = $derived(count * 2); + +export default double; diff --git a/packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/_config.js b/packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/_config.js new file mode 100644 index 000000000000..99e4faec25d4 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'state_invalid_export', + message: + "Cannot export state from a module if it is reassigned. Either export a function returning the state value or only mutate the state value's properties", + position: [93, 118] + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/main.svelte.js b/packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/main.svelte.js new file mode 100644 index 000000000000..8e52f76533d4 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/export-default-state-indirect/main.svelte.js @@ -0,0 +1,7 @@ +let primitive = $state('nope'); + +export function update_primitive() { + primitive = 'yep'; +} + +export default primitive; diff --git a/packages/svelte/tests/validator/samples/default-export-module/errors.json b/packages/svelte/tests/validator/samples/default-export-module/errors.json new file mode 100644 index 000000000000..9fd2bb7df5ea --- /dev/null +++ b/packages/svelte/tests/validator/samples/default-export-module/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "module_illegal_default_export", + "message": "A component cannot have a default export", + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 19 + } + } +] diff --git a/packages/svelte/tests/validator/samples/default-export-module/input.svelte b/packages/svelte/tests/validator/samples/default-export-module/input.svelte new file mode 100644 index 000000000000..2aeeabb2e154 --- /dev/null +++ b/packages/svelte/tests/validator/samples/default-export-module/input.svelte @@ -0,0 +1,3 @@ + From 7bd1cdf4271078c977cc39497cbe545a9de4dd62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Crozatier?= <48696601+fcrozatier@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:14:56 +0100 Subject: [PATCH 21/29] feat: add content-visibility: auto state change event (#14373) * add contentvisibilityautostatechange event * changeset * Update .changeset/kind-horses-lay.md * Update .changeset/kind-horses-lay.md --------- Co-authored-by: Rich Harris --- .changeset/kind-horses-lay.md | 5 +++++ packages/svelte/elements.d.ts | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 .changeset/kind-horses-lay.md diff --git a/.changeset/kind-horses-lay.md b/.changeset/kind-horses-lay.md new file mode 100644 index 000000000000..d08bdde6c415 --- /dev/null +++ b/.changeset/kind-horses-lay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +add `contentvisibilityautostatechange` event to element definitions diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts index 8746b29e250a..8b2d388bf81c 100644 --- a/packages/svelte/elements.d.ts +++ b/packages/svelte/elements.d.ts @@ -60,6 +60,10 @@ export type AnimationEventHandler = EventHandler = EventHandler; export type MessageEventHandler = EventHandler; export type ToggleEventHandler = EventHandler; +export type ContentVisibilityAutoStateChangeEventHandler = EventHandler< + ContentVisibilityAutoStateChangeEvent, + T +>; export type FullAutoFill = | AutoFill @@ -157,6 +161,20 @@ export interface DOMAttributes { ontoggle?: ToggleEventHandler | undefined | null; ontogglecapture?: ToggleEventHandler | undefined | null; + // Content visibility Events + 'on:contentvisibilityautostatechange'?: + | ContentVisibilityAutoStateChangeEventHandler + | undefined + | null; + oncontentvisibilityautostatechange?: + | ContentVisibilityAutoStateChangeEventHandler + | undefined + | null; + oncontentvisibilityautostatechangecapture?: + | ContentVisibilityAutoStateChangeEventHandler + | undefined + | null; + // Keyboard Events 'on:keydown'?: KeyboardEventHandler | undefined | null; onkeydown?: KeyboardEventHandler | undefined | null; From 811c8d32ebcd38501ec5acf1eabe461693bc24d7 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 20 Nov 2024 15:19:17 +0000 Subject: [PATCH 22/29] fix: correctly handle srcObject attribute on video elements (#14369) * fix: correctly handle srcObject attribute on video elements * remove side-effect * side-effects agin * side-effects agin * better fix --- .changeset/bright-jokes-bow.md | 5 +++++ packages/svelte/src/utils.js | 6 ++++-- .../samples/video-src-object/_config.js | 12 ++++++++++++ .../samples/video-src-object/main.svelte | 10 ++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 .changeset/bright-jokes-bow.md create mode 100644 packages/svelte/tests/runtime-runes/samples/video-src-object/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/video-src-object/main.svelte diff --git a/.changeset/bright-jokes-bow.md b/.changeset/bright-jokes-bow.md new file mode 100644 index 000000000000..f70076b8692d --- /dev/null +++ b/.changeset/bright-jokes-bow.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly handle srcObject attribute on video elements diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index 84c7ca1efb17..919660fd6a0a 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -192,7 +192,8 @@ const ATTRIBUTE_ALIASES = { ismap: 'isMap', nomodule: 'noModule', playsinline: 'playsInline', - readonly: 'readOnly' + readonly: 'readOnly', + srcobject: 'srcObject' }; /** @@ -212,7 +213,8 @@ const DOM_PROPERTIES = [ 'readOnly', 'value', 'inert', - 'volume' + 'volume', + 'srcObject' ]; /** diff --git a/packages/svelte/tests/runtime-runes/samples/video-src-object/_config.js b/packages/svelte/tests/runtime-runes/samples/video-src-object/_config.js new file mode 100644 index 000000000000..a31b98cac65b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/video-src-object/_config.js @@ -0,0 +1,12 @@ +import { test } from '../../test'; + +export default test({ + html: ``, + + test({ assert, target }) { + const video = target.querySelector('video'); + + // @ts-ignore + assert.deepEqual(video?.srcObject, {}); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/video-src-object/main.svelte b/packages/svelte/tests/runtime-runes/samples/video-src-object/main.svelte new file mode 100644 index 000000000000..f4059b028abe --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/video-src-object/main.svelte @@ -0,0 +1,10 @@ + + + + From f616c2205399090965d6cf11e9e9b7d5c542fafc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:30:25 -0500 Subject: [PATCH 23/29] Version Packages (#14366) Co-authored-by: github-actions[bot] --- .changeset/bright-jokes-bow.md | 5 ----- .changeset/kind-horses-lay.md | 5 ----- .changeset/new-houses-roll.md | 5 ----- .changeset/unlucky-icons-sit.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 7 files changed, 14 insertions(+), 22 deletions(-) delete mode 100644 .changeset/bright-jokes-bow.md delete mode 100644 .changeset/kind-horses-lay.md delete mode 100644 .changeset/new-houses-roll.md delete mode 100644 .changeset/unlucky-icons-sit.md diff --git a/.changeset/bright-jokes-bow.md b/.changeset/bright-jokes-bow.md deleted file mode 100644 index f70076b8692d..000000000000 --- a/.changeset/bright-jokes-bow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly handle srcObject attribute on video elements diff --git a/.changeset/kind-horses-lay.md b/.changeset/kind-horses-lay.md deleted file mode 100644 index d08bdde6c415..000000000000 --- a/.changeset/kind-horses-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -add `contentvisibilityautostatechange` event to element definitions diff --git a/.changeset/new-houses-roll.md b/.changeset/new-houses-roll.md deleted file mode 100644 index a2b8370e065f..000000000000 --- a/.changeset/new-houses-roll.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: tighten up `export default` validation diff --git a/.changeset/unlucky-icons-sit.md b/.changeset/unlucky-icons-sit.md deleted file mode 100644 index 7fc14cf43524..000000000000 --- a/.changeset/unlucky-icons-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: include method definitions in class private fields diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index f01f519150f4..f5b581ab0e03 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.2.5 + +### Patch Changes + +- fix: correctly handle srcObject attribute on video elements ([#14369](https://github.com/sveltejs/svelte/pull/14369)) + +- add `contentvisibilityautostatechange` event to element definitions ([#14373](https://github.com/sveltejs/svelte/pull/14373)) + +- fix: tighten up `export default` validation ([#14368](https://github.com/sveltejs/svelte/pull/14368)) + +- fix: include method definitions in class private fields ([#14365](https://github.com/sveltejs/svelte/pull/14365)) + ## 5.2.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index f6ba05339800..2205990fff87 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.2.4", + "version": "5.2.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 3a728f3d14c6..5014c8870288 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.2.4'; +export const VERSION = '5.2.5'; export const PUBLIC_VERSION = '5'; From 9d12fd1a01d7020855122a8bfb5a35a3dd4e1c8f Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 20 Nov 2024 17:46:53 +0000 Subject: [PATCH 24/29] chore: remove template expression inlining (#14374) * chore: remove template expression inlining * missed some * fix * feedback * feedback * Update packages/svelte/src/compiler/phases/3-transform/client/utils.js Co-authored-by: Rich Harris * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js Co-authored-by: Rich Harris * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js Co-authored-by: Rich Harris * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js Co-authored-by: Rich Harris * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js Co-authored-by: Rich Harris * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js Co-authored-by: Rich Harris * fix * Update .changeset/calm-mice-perform.md Co-authored-by: Rich Harris --------- Co-authored-by: Rich Harris --- .changeset/calm-mice-perform.md | 5 + .../phases/2-analyze/visitors/Attribute.js | 9 +- .../2-analyze/visitors/CallExpression.js | 2 - .../2-analyze/visitors/ExpressionTag.js | 5 + .../phases/2-analyze/visitors/Identifier.js | 17 +--- .../2-analyze/visitors/MemberExpression.js | 3 - .../2-analyze/visitors/RegularElement.js | 10 +- .../visitors/TaggedTemplateExpression.js | 1 - .../phases/3-transform/client/utils.js | 10 +- .../3-transform/client/visitors/Fragment.js | 10 +- .../client/visitors/RegularElement.js | 72 ++++----------- .../client/visitors/shared/fragment.js | 65 +++---------- .../client/visitors/shared/utils.js | 92 ++++++++----------- packages/svelte/src/compiler/phases/nodes.js | 3 +- packages/svelte/src/compiler/types/index.d.ts | 2 - packages/svelte/src/internal/client/index.js | 1 - .../samples/slot-non-identifier/output.svelte | 2 +- .../_expected/client/index.svelte.js | 17 ---- .../_expected/server/index.svelte.js | 12 --- .../samples/inline-module-vars/index.svelte | 20 ---- 20 files changed, 97 insertions(+), 261 deletions(-) create mode 100644 .changeset/calm-mice-perform.md delete mode 100644 packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js delete mode 100644 packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/server/index.svelte.js delete mode 100644 packages/svelte/tests/snapshot/samples/inline-module-vars/index.svelte diff --git a/.changeset/calm-mice-perform.md b/.changeset/calm-mice-perform.md new file mode 100644 index 000000000000..f43c7fcf0e22 --- /dev/null +++ b/.changeset/calm-mice-perform.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: remove template expression inlining diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index ca0ea55d1fb0..6c050d966a22 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -1,7 +1,7 @@ /** @import { ArrowFunctionExpression, Expression, FunctionDeclaration, FunctionExpression } from 'estree' */ /** @import { AST, DelegatedEvent, SvelteNode } from '#compiler' */ /** @import { Context } from '../types' */ -import { is_boolean_attribute, is_capture_event, is_delegated } from '../../../../utils.js'; +import { cannot_be_set_statically, is_capture_event, is_delegated } from '../../../../utils.js'; import { get_attribute_chunks, get_attribute_expression, @@ -30,12 +30,12 @@ export function Attribute(node, context) { } } - if (node.name.startsWith('on')) { + if (is_event_attribute(node)) { mark_subtree_dynamic(context.path); } - if (parent.type === 'RegularElement' && is_boolean_attribute(node.name.toLowerCase())) { - node.metadata.expression.can_inline = false; + if (cannot_be_set_statically(node.name)) { + mark_subtree_dynamic(context.path); } if (node.value !== true) { @@ -51,7 +51,6 @@ export function Attribute(node, context) { node.metadata.expression.has_state ||= chunk.metadata.expression.has_state; node.metadata.expression.has_call ||= chunk.metadata.expression.has_call; - node.metadata.expression.can_inline &&= chunk.metadata.expression.can_inline; } if (is_event_attribute(node)) { diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index fe3f638a69f9..c7ade4856bcb 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -179,8 +179,6 @@ export function CallExpression(node, context) { if (!is_pure(node.callee, context) || context.state.expression.dependencies.size > 0) { context.state.expression.has_call = true; context.state.expression.has_state = true; - context.state.expression.can_inline = false; - mark_subtree_dynamic(context.path); } } } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js index f59b7fc5692b..32c8d2ca3671 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js @@ -2,6 +2,7 @@ /** @import { Context } from '../types' */ import { is_tag_valid_with_parent } from '../../../../html-tree-validation.js'; import * as e from '../../../errors.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {AST.ExpressionTag} node @@ -14,5 +15,9 @@ export function ExpressionTag(node, context) { } } + // TODO ideally we wouldn't do this here, we'd just do it on encountering + // an `Identifier` within the tag. But we currently need to handle `{42}` etc + mark_subtree_dynamic(context.path); + context.next({ ...context.state, expression: node.metadata.expression }); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js index 042c467df1ed..79dccd5a7cf5 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js @@ -1,4 +1,5 @@ /** @import { Expression, Identifier } from 'estree' */ +/** @import { EachBlock } from '#compiler' */ /** @import { Context } from '../types' */ import is_reference from 'is-reference'; import { should_proxy } from '../../3-transform/client/utils.js'; @@ -19,6 +20,8 @@ export function Identifier(node, context) { return; } + mark_subtree_dynamic(context.path); + // If we are using arguments outside of a function, then throw an error if ( node.name === 'arguments' && @@ -84,12 +87,6 @@ export function Identifier(node, context) { } } - // no binding means global, and we can't inline e.g. `{location}` - // because it could change between component renders. if there _is_ a - // binding and it is outside module scope, the expression cannot - // be inlined (TODO allow inlining in more cases - e.g. primitive consts) - let can_inline = !!binding && !binding.scope.parent && binding.kind === 'normal'; - if (binding) { if (context.state.expression) { context.state.expression.dependencies.add(binding); @@ -125,12 +122,4 @@ export function Identifier(node, context) { w.reactive_declaration_module_script_dependency(node); } } - - if (!can_inline) { - if (context.state.expression) { - context.state.expression.can_inline = false; - } - - mark_subtree_dynamic(context.path); - } } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js index 88adecbd359a..1cc20c96dac8 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js @@ -20,9 +20,6 @@ export function MemberExpression(node, context) { if (context.state.expression && !is_pure(node, context)) { context.state.expression.has_state = true; - context.state.expression.can_inline = false; - - mark_subtree_dynamic(context.path); } if (!is_safe_identifier(node, context.state.scope)) { diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js index d5964f9ae12a..7d1c4aaeaaf0 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js @@ -1,6 +1,6 @@ /** @import { AST } from '#compiler' */ /** @import { Context } from '../types' */ -import { cannot_be_set_statically, is_mathml, is_svg, is_void } from '../../../../utils.js'; +import { is_mathml, is_svg, is_void } from '../../../../utils.js'; import { is_tag_valid_with_ancestor, is_tag_valid_with_parent @@ -75,14 +75,6 @@ export function RegularElement(node, context) { node.attributes.push(create_attribute('value', child.start, child.end, [child])); } - if ( - node.attributes.some( - (attribute) => attribute.type === 'Attribute' && cannot_be_set_statically(attribute.name) - ) - ) { - mark_subtree_dynamic(context.path); - } - const binding = context.state.scope.get(node.name); if ( binding !== null && diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js index 724b9af31185..eacb8a342ac2 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js @@ -10,7 +10,6 @@ export function TaggedTemplateExpression(node, context) { if (context.state.expression && !is_pure(node.tag, context)) { context.state.expression.has_call = true; context.state.expression.has_state = true; - context.state.expression.can_inline = false; } if (node.tag.type === 'Identifier') { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index 910f173f79ae..0b49d18ee922 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -3,16 +3,16 @@ /** @import { ClientTransformState, ComponentClientTransformState, ComponentContext } from './types.js' */ /** @import { Analysis } from '../../types.js' */ /** @import { Scope } from '../../scope.js' */ +import * as b from '../../../utils/builders.js'; +import { extract_identifiers, is_simple_expression } from '../../../utils/ast.js'; import { - PROPS_IS_BINDABLE, - PROPS_IS_IMMUTABLE, PROPS_IS_LAZY_INITIAL, + PROPS_IS_IMMUTABLE, PROPS_IS_RUNES, - PROPS_IS_UPDATED + PROPS_IS_UPDATED, + PROPS_IS_BINDABLE } from '../../../../constants.js'; import { dev } from '../../../state.js'; -import { extract_identifiers, is_simple_expression } from '../../../utils/ast.js'; -import * as b from '../../../utils/builders.js'; import { get_value } from './visitors/shared/declarations.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js index 111516ce0ae0..0e6ea29614ff 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js @@ -141,14 +141,14 @@ export function Fragment(node, context) { const id = b.id(context.state.scope.generate('fragment')); const use_space_template = - trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag') && - trimmed.some((node) => node.type === 'ExpressionTag' && !node.metadata.expression.can_inline); + trimmed.some((node) => node.type === 'ExpressionTag') && + trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag'); if (use_space_template) { // special case — we can use `$.text` instead of creating a unique template const id = b.id(context.state.scope.generate('text')); - process_children(trimmed, () => id, null, { + process_children(trimmed, () => id, false, { ...context, state }); @@ -158,12 +158,12 @@ export function Fragment(node, context) { } else { if (is_standalone) { // no need to create a template, we can just use the existing block's anchor - process_children(trimmed, () => b.id('$$anchor'), null, { ...context, state }); + process_children(trimmed, () => b.id('$$anchor'), false, { ...context, state }); } else { /** @type {(is_text: boolean) => Expression} */ const expression = (is_text) => b.call('$.first_child', id, is_text && b.true); - process_children(trimmed, expression, null, { ...context, state }); + process_children(trimmed, expression, false, { ...context, state }); let flags = TEMPLATE_FRAGMENT; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 19948464d2f0..85df92e8bfd0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -1,9 +1,8 @@ -/** @import { Expression, ExpressionStatement, Identifier, Literal, MemberExpression, ObjectExpression, Statement } from 'estree' */ +/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { SourceLocation } from '#shared' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */ /** @import { Scope } from '../../../scope' */ -import { escape_html } from '../../../../../escaping.js'; import { cannot_be_set_statically, is_boolean_attribute, @@ -11,6 +10,7 @@ import { is_load_error_element, is_void } from '../../../../../utils.js'; +import { escape_html } from '../../../../../escaping.js'; import { dev, is_ignored, locator } from '../../../../state.js'; import { is_event_attribute, is_text_attribute } from '../../../../utils/ast.js'; import * as b from '../../../../utils/builders.js'; @@ -18,13 +18,12 @@ import { is_custom_element_node } from '../../../nodes.js'; import { clean_nodes, determine_namespace_for_children } from '../../utils.js'; import { build_getter, create_derived } from '../utils.js'; import { + get_attribute_name, build_attribute_value, build_class_directives, - build_set_attributes, build_style_directives, - get_attribute_name + build_set_attributes } from './shared/element.js'; -import { visit_event_attribute } from './shared/events.js'; import { process_children } from './shared/fragment.js'; import { build_render_statement, @@ -32,6 +31,7 @@ import { build_update, build_update_assignment } from './shared/utils.js'; +import { visit_event_attribute } from './shared/events.js'; /** * @param {AST.RegularElement} node @@ -352,32 +352,30 @@ export function RegularElement(node, context) { // special case — if an element that only contains text, we don't need // to descend into it if the text is non-reactive - const is_text = trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag'); - // in the rare case that we have static text that can't be inlined // (e.g. `{location}`), set `textContent` programmatically const use_text_content = - is_text && + trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag') && trimmed.every((node) => node.type === 'Text' || !node.metadata.expression.has_state) && - trimmed.some((node) => node.type === 'ExpressionTag' && !node.metadata.expression.can_inline); + trimmed.some((node) => node.type === 'ExpressionTag'); if (use_text_content) { - let { value } = build_template_chunk(trimmed, context.visit, child_state); - child_state.init.push( - b.stmt(b.assignment('=', b.member(context.state.node, 'textContent'), value)) + b.stmt( + b.assignment( + '=', + b.member(context.state.node, 'textContent'), + build_template_chunk(trimmed, context.visit, child_state).value + ) + ) ); } else { /** @type {Expression} */ let arg = context.state.node; // If `hydrate_node` is set inside the element, we need to reset it - // after the element has been hydrated (we don't need to reset if it's been inlined) - let needs_reset = !trimmed.every( - (node) => - node.type === 'Text' || - (node.type === 'ExpressionTag' && node.metadata.expression.can_inline) - ); + // after the element has been hydrated + let needs_reset = trimmed.some((node) => node.type !== 'Text'); // The same applies if it's a `