diff --git a/.prettierignore b/.prettierignore
index d5c124353c..72cd10aca8 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -7,6 +7,7 @@ packages/**/config/*.js
# packages/svelte
packages/svelte/messages/**/*.md
+packages/svelte/scripts/_bundle.js
packages/svelte/src/compiler/errors.js
packages/svelte/src/compiler/warnings.js
packages/svelte/src/internal/client/errors.js
@@ -25,8 +26,7 @@ packages/svelte/tests/hydration/samples/*/_expected.html
packages/svelte/tests/hydration/samples/*/_override.html
packages/svelte/types
packages/svelte/compiler/index.js
-playgrounds/sandbox/input/**.svelte
-playgrounds/sandbox/output
+playgrounds/sandbox/src/*
# sites/svelte.dev
sites/svelte.dev/static/svelte-app.json
diff --git a/documentation/docs/07-misc/03-typescript.md b/documentation/docs/07-misc/03-typescript.md
index fbf8817069..ff33885fb8 100644
--- a/documentation/docs/07-misc/03-typescript.md
+++ b/documentation/docs/07-misc/03-typescript.md
@@ -83,7 +83,7 @@ If you're using tools like Rollup or Webpack instead, install their respective S
When using TypeScript, make sure your `tsconfig.json` is setup correctly.
-- Use a [`target`](https://www.typescriptlang.org/tsconfig/#target) of at least `ES2022`, or a `target` of at least `ES2015` alongside [`useDefineForClassFields`](https://www.typescriptlang.org/tsconfig/#useDefineForClassFields). This ensures that rune declarations on class fields are not messed with, which would break the Svelte compiler
+- Use a [`target`](https://www.typescriptlang.org/tsconfig/#target) of at least `ES2015` so classes are not compiled to functions
- Set [`verbatimModuleSyntax`](https://www.typescriptlang.org/tsconfig/#verbatimModuleSyntax) to `true` so that imports are left as-is
- Set [`isolatedModules`](https://www.typescriptlang.org/tsconfig/#isolatedModules) to `true` so that each file is looked at in isolation. TypeScript has a few features which require cross-file analysis and compilation, which the Svelte compiler and tooling like Vite don't do.
diff --git a/eslint.config.js b/eslint.config.js
index d6c977a36a..d7044fc9f1 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -87,6 +87,7 @@ export default [
'**/*.d.ts',
'**/tests',
'packages/svelte/scripts/process-messages/templates/*.js',
+ 'packages/svelte/scripts/_bundle.js',
'packages/svelte/src/compiler/errors.js',
'packages/svelte/src/internal/client/errors.js',
'packages/svelte/src/internal/client/warnings.js',
diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md
index e254be754f..ef64091ca3 100644
--- a/packages/svelte/CHANGELOG.md
+++ b/packages/svelte/CHANGELOG.md
@@ -1,5 +1,11 @@
# svelte
+## 5.34.9
+
+### Patch Changes
+
+- fix: ensure unowned deriveds can add themselves as reactions while connected ([#16249](https://github.com/sveltejs/svelte/pull/16249))
+
## 5.34.8
### Patch Changes
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index 6933ecafbe..2d88d2a051 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.34.8",
+ "version": "5.34.9",
"type": "module",
"types": "./types/index.d.ts",
"engines": {
diff --git a/packages/svelte/scripts/check-treeshakeability.js b/packages/svelte/scripts/check-treeshakeability.js
index 1501ee6954..e883496fe2 100644
--- a/packages/svelte/scripts/check-treeshakeability.js
+++ b/packages/svelte/scripts/check-treeshakeability.js
@@ -118,36 +118,40 @@ const bundle = await bundle_code(
).js.code
);
-if (!bundle.includes('hydrate_node') && !bundle.includes('hydrate_next')) {
- // eslint-disable-next-line no-console
- console.error(`✅ Hydration code treeshakeable`);
-} else {
- failed = true;
- // eslint-disable-next-line no-console
- console.error(`❌ Hydration code not treeshakeable`);
-}
+/**
+ * @param {string} case_name
+ * @param {string[]} strings
+ */
+function check_bundle(case_name, ...strings) {
+ for (const string of strings) {
+ const index = bundle.indexOf(string);
+ if (index >= 0) {
+ // eslint-disable-next-line no-console
+ console.error(`❌ ${case_name} not treeshakeable`);
+ failed = true;
-if (!bundle.includes('component_context.l')) {
- // eslint-disable-next-line no-console
- console.error(`✅ Legacy code treeshakeable`);
-} else {
- failed = true;
+ let lines = bundle.slice(index - 500, index + 500).split('\n');
+ const target_line = lines.findIndex((line) => line.includes(string));
+ // mark the failed line
+ lines = lines
+ .map((line, i) => (i === target_line ? `> ${line}` : `| ${line}`))
+ .slice(target_line - 5, target_line + 6);
+ // eslint-disable-next-line no-console
+ console.error('The first failed line:\n' + lines.join('\n'));
+ return;
+ }
+ }
// eslint-disable-next-line no-console
- console.error(`❌ Legacy code not treeshakeable`);
+ console.error(`✅ ${case_name} treeshakeable`);
}
-if (!bundle.includes(`'CreatedAt'`)) {
- // eslint-disable-next-line no-console
- console.error(`✅ $inspect.trace code treeshakeable`);
-} else {
- failed = true;
- // eslint-disable-next-line no-console
- console.error(`❌ $inspect.trace code not treeshakeable`);
-}
+check_bundle('Hydration code', 'hydrate_node', 'hydrate_next');
+check_bundle('Legacy code', 'component_context.l');
+check_bundle('$inspect.trace', `'CreatedAt'`);
if (failed) {
// eslint-disable-next-line no-console
- console.error(bundle);
+ console.error('Full bundle at', path.resolve('scripts/_bundle.js'));
fs.writeFileSync('scripts/_bundle.js', bundle);
}
diff --git a/packages/svelte/src/compiler/index.js b/packages/svelte/src/compiler/index.js
index 11db091936..2aa9a820d2 100644
--- a/packages/svelte/src/compiler/index.js
+++ b/packages/svelte/src/compiler/index.js
@@ -3,7 +3,6 @@
/** @import { AST } from './public.js' */
import { walk as zimmerframe_walk } from 'zimmerframe';
import { convert } from './legacy.js';
-import { parse as parse_acorn } from './phases/1-parse/acorn.js';
import { parse as _parse } from './phases/1-parse/index.js';
import { remove_typescript_nodes } from './phases/1-parse/remove_typescript_nodes.js';
import { analyze_component, analyze_module } from './phases/2-analyze/index.js';
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 1aefff0db0..ae8680f594 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
@@ -22,13 +22,7 @@ import {
build_set_style
} from './shared/element.js';
import { process_children } from './shared/fragment.js';
-import {
- build_render_statement,
- build_template_chunk,
- build_update_assignment,
- get_expression_id,
- memoize_expression
-} from './shared/utils.js';
+import { build_render_statement, build_template_chunk, get_expression_id } from './shared/utils.js';
import { visit_event_attribute } from './shared/events.js';
/**
@@ -200,16 +194,16 @@ export function RegularElement(node, context) {
const node_id = context.state.node;
+ /** If true, needs `__value` for inputs */
+ const needs_special_value_handling =
+ node.name === 'option' ||
+ node.name === 'select' ||
+ bindings.has('group') ||
+ bindings.has('checked');
+
if (has_spread) {
build_attribute_effect(attributes, class_directives, style_directives, context, node, node_id);
} else {
- /** If true, needs `__value` for inputs */
- const needs_special_value_handling =
- node.name === 'option' ||
- node.name === 'select' ||
- bindings.has('group') ||
- bindings.has('checked');
-
for (const attribute of /** @type {AST.Attribute[]} */ (attributes)) {
if (is_event_attribute(attribute)) {
visit_event_attribute(attribute, context);
@@ -217,7 +211,6 @@ export function RegularElement(node, context) {
}
if (needs_special_value_handling && attribute.name === 'value') {
- build_element_special_value_attribute(node.name, node_id, attribute, context);
continue;
}
@@ -392,6 +385,15 @@ export function RegularElement(node, context) {
context.state.update.push(b.stmt(b.assignment('=', dir, dir)));
}
+ if (!has_spread && needs_special_value_handling) {
+ for (const attribute of /** @type {AST.Attribute[]} */ (attributes)) {
+ if (attribute.name === 'value') {
+ build_element_special_value_attribute(node.name, node_id, attribute, context);
+ break;
+ }
+ }
+ }
+
context.state.template.pop_element();
}
@@ -622,12 +624,7 @@ function build_element_special_value_attribute(element, node_id, attribute, cont
element === 'select' && attribute.value !== true && !is_text_attribute(attribute);
const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
- metadata.has_call
- ? // if is a select with value we will also invoke `init_select` which need a reference before the template effect so we memoize separately
- is_select_with_value
- ? memoize_expression(state, value)
- : get_expression_id(state.expressions, value)
- : value
+ metadata.has_call ? get_expression_id(state.expressions, value) : value
);
const evaluated = context.state.scope.evaluate(value);
@@ -652,23 +649,21 @@ function build_element_special_value_attribute(element, node_id, attribute, cont
: inner_assignment
);
- if (is_select_with_value) {
- state.init.push(b.stmt(b.call('$.init_select', node_id, b.thunk(value))));
- }
-
if (has_state) {
- const id = state.scope.generate(`${node_id.name}_value`);
- build_update_assignment(
- state,
- id,
- // `