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 `` element, since we need to
// set the value of `hydrate_node` to `node.content`
@@ -387,7 +385,7 @@ export function RegularElement(node, context) {
arg = b.member(arg, 'content');
}
- process_children(trimmed, (is_text) => b.call('$.child', arg, is_text && b.true), node, {
+ process_children(trimmed, (is_text) => b.call('$.child', arg, is_text && b.true), true, {
...context,
state: child_state
});
@@ -587,44 +585,10 @@ function build_element_attribute_update_assignment(element, node_id, attribute,
state.update.push(update);
}
return true;
- }
-
- // we need to special case textarea value because it's not an actual attribute
- const can_inline =
- (attribute.name !== 'value' || element.name !== 'textarea') &&
- attribute.metadata.expression.can_inline;
-
- if (can_inline) {
- /** @type {Literal | undefined} */
- let literal = undefined;
-
- if (value.type === 'Literal') {
- literal = value;
- } else if (value.type === 'Identifier') {
- const binding = context.state.scope.get(value.name);
- if (binding && binding.initial?.type === 'Literal' && !binding.reassigned) {
- literal = binding.initial;
- }
- }
-
- if (literal && escape_html(literal.value, true) === String(literal.value)) {
- if (is_boolean_attribute(name)) {
- if (literal.value) {
- context.state.template.push(` ${name}`);
- }
- } else {
- context.state.template.push(` ${name}="`, value, '"');
- }
- } else {
- context.state.template.push(
- b.call('$.attr', b.literal(name), value, is_boolean_attribute(name) && b.true)
- );
- }
} else {
state.init.push(update);
+ return false;
}
-
- return false;
}
/**
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 102988781aca..af336733bcb4 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
@@ -1,10 +1,8 @@
/** @import { Expression } from 'estree' */
/** @import { AST, SvelteNode } from '#compiler' */
-/** @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 { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js';
import * as b from '../../../../../utils/builders.js';
import { build_template_chunk, build_update } from './utils.js';
@@ -14,10 +12,10 @@ import { build_template_chunk, build_update } from './utils.js';
* corresponding template node references these updates are applied to.
* @param {SvelteNode[]} nodes
* @param {(is_text: boolean) => Expression} initial
- * @param {AST.RegularElement | null} element
+ * @param {boolean} is_element
* @param {ComponentContext} context
*/
-export function process_children(nodes, initial, element, { visit, state }) {
+export function process_children(nodes, initial, is_element, { visit, state }) {
const within_bound_contenteditable = state.metadata.bound_contenteditable;
let prev = initial;
let skipped = 0;
@@ -63,17 +61,16 @@ export function process_children(nodes, initial, element, { visit, state }) {
* @param {Sequence} sequence
*/
function flush_sequence(sequence) {
- const { has_state, has_call, value, can_inline } = build_template_chunk(sequence, visit, state);
-
- if (can_inline) {
+ if (sequence.every((node) => node.type === 'Text')) {
skipped += 1;
- const raw = element?.name === 'script' || element?.name === 'style';
- state.template.push(raw ? value : escape_inline_expression(value, state.scope));
+ state.template.push(sequence.map((node) => node.raw).join(''));
return;
}
state.template.push(' ');
+ const { has_state, has_call, value } = build_template_chunk(sequence, visit, state);
+
// if this is a standalone `{expression}`, make sure we handle the case where
// no text node was created because the expression was empty during SSR
const is_text = sequence.length === 1;
@@ -101,9 +98,9 @@ export function process_children(nodes, initial, element, { visit, state }) {
let child_state = state;
- if (is_static_element(node)) {
+ if (is_static_element(node, state)) {
skipped += 1;
- } else if (node.type === 'EachBlock' && nodes.length === 1 && element) {
+ } else if (node.type === 'EachBlock' && nodes.length === 1 && is_element) {
node.metadata.is_controlled = true;
} else {
const id = flush_node(false, node.type === 'RegularElement' ? node.name : 'node');
@@ -128,8 +125,9 @@ export function process_children(nodes, initial, element, { visit, state }) {
/**
* @param {SvelteNode} node
+ * @param {ComponentContext["state"]} state
*/
-function is_static_element(node) {
+function is_static_element(node, state) {
if (node.type !== 'RegularElement') return false;
if (node.fragment.metadata.dynamic) return false;
if (node.name.includes('-')) return false; // we're setting all attributes on custom elements through properties
@@ -156,49 +154,10 @@ function is_static_element(node) {
return false;
}
- if (!attribute.metadata.expression.can_inline) {
+ if (attribute.value !== true && !is_text_attribute(attribute)) {
return false;
}
}
return true;
}
-
-/**
- * @param {Expression} node
- * @param {Scope} scope
- * @returns {Expression}
- */
-function escape_inline_expression(node, scope) {
- if (node.type === 'Literal') {
- if (typeof node.value === 'string') {
- return b.literal(escape_html(node.value));
- }
-
- return node;
- }
-
- if (node.type === 'TemplateLiteral') {
- return b.template(
- node.quasis.map((q) => b.quasi(escape_html(q.value.cooked))),
- node.expressions.map((expression) => escape_inline_expression(expression, scope))
- );
- }
-
- /**
- * If we can't determine the range of possible values statically, wrap in
- * `$.escape(...)`. TODO expand this to cover more cases
- */
- let needs_escape = true;
-
- if (node.type === 'Identifier') {
- const binding = scope.get(node.name);
-
- // TODO handle more cases
- if (binding?.initial?.type === 'Literal' && !binding.reassigned) {
- needs_escape = escape_html(binding.initial.value) !== String(binding.initial.value);
- }
- }
-
- return needs_escape ? b.call('$.escape', node) : node;
-}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js
index b74d2cc9c08e..59beacbb0c30 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js
@@ -1,4 +1,4 @@
-/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, Statement, Super, TemplateLiteral, Node } from 'estree' */
+/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, Statement, Super } from 'estree' */
/** @import { AST, SvelteNode } from '#compiler' */
/** @import { ComponentClientTransformState } from '../../types' */
import { walk } from 'zimmerframe';
@@ -14,7 +14,7 @@ import { locator } from '../../../../../state.js';
* @param {Array} values
* @param {(node: SvelteNode, state: any) => any} visit
* @param {ComponentClientTransformState} state
- * @returns {{ value: Expression, has_state: boolean, has_call: boolean, can_inline: boolean }}
+ * @returns {{ value: Expression, has_state: boolean, has_call: boolean }}
*/
export function build_template_chunk(values, visit, state) {
/** @type {Expression[]} */
@@ -25,23 +25,15 @@ export function build_template_chunk(values, visit, state) {
let has_call = false;
let has_state = false;
- let can_inline = true;
let contains_multiple_call_expression = false;
for (const node of values) {
if (node.type === 'ExpressionTag') {
- if (node.metadata.expression.has_call) {
- if (has_call) contains_multiple_call_expression = true;
- has_call = true;
- }
-
- if (node.metadata.expression.has_state) {
- has_state = true;
- }
+ const metadata = node.metadata.expression;
- if (!node.metadata.expression.can_inline) {
- can_inline = false;
- }
+ contains_multiple_call_expression ||= has_call && metadata.has_call;
+ has_call ||= metadata.has_call;
+ has_state ||= metadata.has_state;
}
}
@@ -50,49 +42,39 @@ export function build_template_chunk(values, visit, state) {
if (node.type === 'Text') {
quasi.value.cooked += node.data;
+ } else if (node.type === 'ExpressionTag' && node.expression.type === 'Literal') {
+ if (node.expression.value != null) {
+ quasi.value.cooked += node.expression.value + '';
+ }
} else {
- const expression = /** @type {Expression} */ (visit(node.expression, state));
-
- if (expression.type === 'Literal') {
- if (expression.value != null) {
- quasi.value.cooked += expression.value + '';
- }
+ if (contains_multiple_call_expression) {
+ const id = b.id(state.scope.generate('stringified_text'));
+ state.init.push(
+ b.const(
+ id,
+ create_derived(
+ state,
+ b.thunk(
+ b.logical(
+ '??',
+ /** @type {Expression} */ (visit(node.expression, state)),
+ b.literal('')
+ )
+ )
+ )
+ )
+ );
+ expressions.push(b.call('$.get', id));
+ } else if (values.length === 1) {
+ // If we have a single expression, then pass that in directly to possibly avoid doing
+ // extra work in the template_effect (instead we do the work in set_text).
+ return { value: visit(node.expression, state), has_state, has_call };
} else {
- let value = expression;
-
- // if we don't know the value, we need to add `?? ''` to replace
- // `null` and `undefined` with the empty string
- let needs_fallback = true;
-
- if (value.type === 'Identifier') {
- const binding = state.scope.get(value.name);
-
- if (binding && binding.initial?.type === 'Literal' && !binding.reassigned) {
- needs_fallback = binding.initial.value === null;
- }
- }
-
- if (needs_fallback) {
- value = b.logical('??', expression, b.literal(''));
- }
-
- if (contains_multiple_call_expression) {
- const id = b.id(state.scope.generate('stringified_text'));
-
- state.init.push(b.const(id, create_derived(state, b.thunk(value))));
-
- expressions.push(b.call('$.get', id));
- } else if (values.length === 1) {
- // If we have a single expression, then pass that in directly to possibly avoid doing
- // extra work in the template_effect (instead we do the work in set_text).
- return { value: visit(node.expression, state), has_state, has_call, can_inline };
- } else {
- expressions.push(value);
- }
-
- quasi = b.quasi('', i + 1 === values.length);
- quasis.push(quasi);
+ expressions.push(b.logical('??', visit(node.expression, state), b.literal('')));
}
+
+ quasi = b.quasi('', i + 1 === values.length);
+ quasis.push(quasi);
}
}
@@ -102,7 +84,7 @@ export function build_template_chunk(values, visit, state) {
const value = b.template(quasis, expressions);
- return { value, has_state, has_call, can_inline };
+ return { value, has_state, has_call };
}
/**
diff --git a/packages/svelte/src/compiler/phases/nodes.js b/packages/svelte/src/compiler/phases/nodes.js
index 13a54730eb0c..ead525aaa149 100644
--- a/packages/svelte/src/compiler/phases/nodes.js
+++ b/packages/svelte/src/compiler/phases/nodes.js
@@ -58,7 +58,6 @@ export function create_expression_metadata() {
return {
dependencies: new Set(),
has_state: false,
- has_call: false,
- can_inline: true
+ has_call: false
};
}
diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts
index bd4749db001c..8a45f2d4fd84 100644
--- a/packages/svelte/src/compiler/types/index.d.ts
+++ b/packages/svelte/src/compiler/types/index.d.ts
@@ -317,8 +317,6 @@ export interface ExpressionMetadata {
has_state: boolean;
/** True if the expression involves a call expression (often, it will need to be wrapped in a derived) */
has_call: boolean;
- /** True if the expression can be inlined into a template */
- can_inline: boolean;
}
export * from './template.js';
diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js
index bfc0a6aac93d..6fd749b42cfa 100644
--- a/packages/svelte/src/internal/client/index.js
+++ b/packages/svelte/src/internal/client/index.js
@@ -1,5 +1,4 @@
export { FILENAME, HMR, NAMESPACE_SVG } from '../../constants.js';
-export { escape_html as escape } from '../../escaping.js';
export { cleanup_styles } from './dev/css.js';
export { add_locations } from './dev/elements.js';
export { hmr } from './dev/hmr.js';
diff --git a/packages/svelte/tests/migrate/samples/slot-non-identifier/output.svelte b/packages/svelte/tests/migrate/samples/slot-non-identifier/output.svelte
index fb262e8c2532..b9a324801244 100644
--- a/packages/svelte/tests/migrate/samples/slot-non-identifier/output.svelte
+++ b/packages/svelte/tests/migrate/samples/slot-non-identifier/output.svelte
@@ -82,4 +82,4 @@
cool
-
+
\ No newline at end of file
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
deleted file mode 100644
index 462262442437..000000000000
--- a/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/client/index.svelte.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import "svelte/internal/disclose-version";
-import * as $ from "svelte/internal/client";
-
-const __DECLARED_ASSET_0__ = "__VITE_ASSET__2AM7_y_a__ 1440w, __VITE_ASSET__2AM7_y_b__ 960w";
-const __DECLARED_ASSET_1__ = "__VITE_ASSET__2AM7_y_c__ 1440w, __VITE_ASSET__2AM7_y_d__ 960w";
-const __DECLARED_ASSET_2__ = "__VITE_ASSET__2AM7_y_e__ 1440w, __VITE_ASSET__2AM7_y_f__ 960w";
-const __DECLARED_ASSET_3__ = "__VITE_ASSET__2AM7_y_g__";
-const a = 1;
-const b = 2;
-var root = $.template(` ${a} + ${b} = ${$.escape(a + b ?? "")}
`, 1);
-
-export default function Inline_module_vars($$anchor) {
- var fragment = root();
-
- $.next(2);
- $.append($$anchor, fragment);
-}
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/server/index.svelte.js
deleted file mode 100644
index ca98cd952b72..000000000000
--- a/packages/svelte/tests/snapshot/samples/inline-module-vars/_expected/server/index.svelte.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import * as $ from "svelte/internal/server";
-
-const __DECLARED_ASSET_0__ = "__VITE_ASSET__2AM7_y_a__ 1440w, __VITE_ASSET__2AM7_y_b__ 960w";
-const __DECLARED_ASSET_1__ = "__VITE_ASSET__2AM7_y_c__ 1440w, __VITE_ASSET__2AM7_y_d__ 960w";
-const __DECLARED_ASSET_2__ = "__VITE_ASSET__2AM7_y_e__ 1440w, __VITE_ASSET__2AM7_y_f__ 960w";
-const __DECLARED_ASSET_3__ = "__VITE_ASSET__2AM7_y_g__";
-const a = 1;
-const b = 2;
-
-export default function Inline_module_vars($$payload) {
- $$payload.out += ` ${$.escape(a)} + ${$.escape(b)} = ${$.escape(a + b)}
`;
-}
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/inline-module-vars/index.svelte b/packages/svelte/tests/snapshot/samples/inline-module-vars/index.svelte
deleted file mode 100644
index a7b737e59612..000000000000
--- a/packages/svelte/tests/snapshot/samples/inline-module-vars/index.svelte
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-{a} + {b} = {a + b}
From 85ec6fa276e1cf3202ccadf751b7c3c387debb75 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 20 Nov 2024 19:54:56 +0000
Subject: [PATCH 25/29] Version Packages (#14377)
Co-authored-by: github-actions[bot]
---
.changeset/calm-mice-perform.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/calm-mice-perform.md
diff --git a/.changeset/calm-mice-perform.md b/.changeset/calm-mice-perform.md
deleted file mode 100644
index f43c7fcf0e22..000000000000
--- a/.changeset/calm-mice-perform.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'svelte': patch
----
-
-fix: remove template expression inlining
diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md
index f5b581ab0e03..b69c0dd3e505 100644
--- a/packages/svelte/CHANGELOG.md
+++ b/packages/svelte/CHANGELOG.md
@@ -1,5 +1,11 @@
# svelte
+## 5.2.6
+
+### Patch Changes
+
+- fix: remove template expression inlining ([#14374](https://github.com/sveltejs/svelte/pull/14374))
+
## 5.2.5
### Patch Changes
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index 2205990fff87..9c11b4cdd9bd 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.5",
+ "version": "5.2.6",
"type": "module",
"types": "./types/index.d.ts",
"engines": {
diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js
index 5014c8870288..1db41e6c2161 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.5';
+export const VERSION = '5.2.6';
export const PUBLIC_VERSION = '5';
From 520055cf5c6635e44aa2cb04fdabc6ecb776d527 Mon Sep 17 00:00:00 2001
From: Paolo Ricciuti
Date: Wed, 20 Nov 2024 21:03:05 +0100
Subject: [PATCH 26/29] fix: always use set for private identifiers (#14378)
* fix: always use set for private identifiers
* we can simplify this further - no need to check the value was transformed, since the outcome of not returning immediately is the same but with extra steps
* add explanatory note
---------
Co-authored-by: Rich Harris
---
.changeset/tiny-trainers-sleep.md | 5 +++
.../client/visitors/AssignmentExpression.js | 41 ++++++++-----------
.../private-identifiers-not-this/_config.js | 17 ++++++++
.../private-identifiers-not-this/main.svelte | 26 ++++++++++++
4 files changed, 66 insertions(+), 23 deletions(-)
create mode 100644 .changeset/tiny-trainers-sleep.md
create mode 100644 packages/svelte/tests/runtime-runes/samples/private-identifiers-not-this/_config.js
create mode 100644 packages/svelte/tests/runtime-runes/samples/private-identifiers-not-this/main.svelte
diff --git a/.changeset/tiny-trainers-sleep.md b/.changeset/tiny-trainers-sleep.md
new file mode 100644
index 000000000000..4cb0b05e6651
--- /dev/null
+++ b/.changeset/tiny-trainers-sleep.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: always use set for private identifiers
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js
index cf54dba1b32a..ee909ede91bf 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js
@@ -32,31 +32,26 @@ function build_assignment(operator, left, right, context) {
if (
context.state.analysis.runes &&
left.type === 'MemberExpression' &&
- left.object.type === 'ThisExpression'
+ left.property.type === 'PrivateIdentifier'
) {
- if (left.property.type === 'PrivateIdentifier') {
- const private_state = context.state.private_state.get(left.property.name);
-
- if (private_state !== undefined) {
- let transformed = false;
- let value = /** @type {Expression} */ (
- context.visit(build_assignment_value(operator, left, right))
- );
-
- if (should_proxy(value, context.state.scope)) {
- transformed = true;
- value =
- private_state.kind === 'raw_state'
- ? value
- : build_proxy_reassignment(value, b.member(b.this, private_state.id));
- }
-
- if (!context.state.in_constructor) {
- return b.call('$.set', left, value);
- } else if (transformed) {
- return b.assignment(operator, /** @type {Pattern} */ (context.visit(left)), value);
- }
+ const private_state = context.state.private_state.get(left.property.name);
+
+ if (private_state !== undefined) {
+ let value = /** @type {Expression} */ (
+ context.visit(build_assignment_value(operator, left, right))
+ );
+
+ if (private_state.kind !== 'raw_state' && should_proxy(value, context.state.scope)) {
+ value = build_proxy_reassignment(value, b.member(b.this, private_state.id));
}
+
+ if (context.state.in_constructor) {
+ // inside the constructor, we can assign to `this.#foo.v` rather than using `$.set`,
+ // since nothing is tracking the signal at this point
+ return b.assignment(operator, /** @type {Pattern} */ (context.visit(left)), value);
+ }
+
+ return b.call('$.set', left, value);
}
}
diff --git a/packages/svelte/tests/runtime-runes/samples/private-identifiers-not-this/_config.js b/packages/svelte/tests/runtime-runes/samples/private-identifiers-not-this/_config.js
new file mode 100644
index 000000000000..0294b930c20e
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/private-identifiers-not-this/_config.js
@@ -0,0 +1,17 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ html: `42
1337
`,
+ async test({ assert, target, instance }) {
+ const [a, b] = target.querySelectorAll('p');
+ const btn = target.querySelector('button');
+
+ flushSync(() => {
+ btn?.click();
+ });
+
+ assert.equal(a.textContent, '1337');
+ assert.equal(b.textContent, '42');
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/private-identifiers-not-this/main.svelte b/packages/svelte/tests/runtime-runes/samples/private-identifiers-not-this/main.svelte
new file mode 100644
index 000000000000..384795f60d12
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/private-identifiers-not-this/main.svelte
@@ -0,0 +1,26 @@
+
+
+{a.value}
+{b.value}
+{a.swap(b)}}>
\ No newline at end of file
From 97185eb3d5e839d6da70edd3e1ebb97396c6197f Mon Sep 17 00:00:00 2001
From: Rob B <2623102+rob-balfre@users.noreply.github.com>
Date: Thu, 21 Nov 2024 09:14:21 +1100
Subject: [PATCH 27/29] docs Update 03-typescript.md (#14382)
* Update 03-typescript.md
Fix TS error: Cannot invoke an object which is possibly 'undefined'.
* Update 03-typescript.md
---------
Co-authored-by: Rich Harris
---
documentation/docs/07-misc/03-typescript.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/documentation/docs/07-misc/03-typescript.md b/documentation/docs/07-misc/03-typescript.md
index e02ac7b6b105..f4513a9a2dce 100644
--- a/documentation/docs/07-misc/03-typescript.md
+++ b/documentation/docs/07-misc/03-typescript.md
@@ -142,7 +142,7 @@ In case you're writing a component that wraps a native element, you may want to
- {@render children()}
+ {@render children?.()}
```
@@ -156,7 +156,7 @@ Not all elements have a dedicated type definition. For those without one, use `S
- {@render children()}
+ {@render children?.()}
```
From 6e391fce59c5c79cd42b0b214a54143e05e01db3 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 20 Nov 2024 17:14:43 -0500
Subject: [PATCH 28/29] Version Packages (#14380)
Co-authored-by: github-actions[bot]
---
.changeset/tiny-trainers-sleep.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/tiny-trainers-sleep.md
diff --git a/.changeset/tiny-trainers-sleep.md b/.changeset/tiny-trainers-sleep.md
deleted file mode 100644
index 4cb0b05e6651..000000000000
--- a/.changeset/tiny-trainers-sleep.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'svelte': patch
----
-
-fix: always use set for private identifiers
diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md
index b69c0dd3e505..47b620b40220 100644
--- a/packages/svelte/CHANGELOG.md
+++ b/packages/svelte/CHANGELOG.md
@@ -1,5 +1,11 @@
# svelte
+## 5.2.7
+
+### Patch Changes
+
+- fix: always use set for private identifiers ([#14378](https://github.com/sveltejs/svelte/pull/14378))
+
## 5.2.6
### Patch Changes
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index 9c11b4cdd9bd..130fe64ba001 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.6",
+ "version": "5.2.7",
"type": "module",
"types": "./types/index.d.ts",
"engines": {
diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js
index 1db41e6c2161..3cd065be8e73 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.6';
+export const VERSION = '5.2.7';
export const PUBLIC_VERSION = '5';
From 6a5f30b290dbce34afdd1f5d743044e904d024a6 Mon Sep 17 00:00:00 2001
From: Simon H <5968653+dummdidumm@users.noreply.github.com>
Date: Thu, 21 Nov 2024 13:48:36 +0100
Subject: [PATCH 29/29] chore: add changeset checkbox to PR template (#14388)
too many people forget it
---
.github/PULL_REQUEST_TEMPLATE.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 6d1fead58713..aa5f9732b6d3 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -4,6 +4,7 @@
- [ ] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [ ] This message body should clearly illustrate what problems it solves.
- [ ] Ideally, include a test that fails without this PR but passes with it.
+- [ ] If this PR changes code within `packages/svelte/src`, add a changeset (`npx changeset`).
### Tests and linting