Skip to content

Commit 1377c40

Browse files
committed
Merge branch 'main' into print
2 parents a39ab49 + e896c55 commit 1377c40

File tree

16 files changed

+149
-121
lines changed

16 files changed

+149
-121
lines changed

.prettierignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ packages/**/config/*.js
77

88
# packages/svelte
99
packages/svelte/messages/**/*.md
10+
packages/svelte/scripts/_bundle.js
1011
packages/svelte/src/compiler/errors.js
1112
packages/svelte/src/compiler/warnings.js
1213
packages/svelte/src/internal/client/errors.js
@@ -25,8 +26,7 @@ packages/svelte/tests/hydration/samples/*/_expected.html
2526
packages/svelte/tests/hydration/samples/*/_override.html
2627
packages/svelte/types
2728
packages/svelte/compiler/index.js
28-
playgrounds/sandbox/input/**.svelte
29-
playgrounds/sandbox/output
29+
playgrounds/sandbox/src/*
3030

3131
# sites/svelte.dev
3232
sites/svelte.dev/static/svelte-app.json

documentation/docs/07-misc/03-typescript.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ If you're using tools like Rollup or Webpack instead, install their respective S
8383

8484
When using TypeScript, make sure your `tsconfig.json` is setup correctly.
8585

86-
- 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
86+
- Use a [`target`](https://www.typescriptlang.org/tsconfig/#target) of at least `ES2015` so classes are not compiled to functions
8787
- Set [`verbatimModuleSyntax`](https://www.typescriptlang.org/tsconfig/#verbatimModuleSyntax) to `true` so that imports are left as-is
8888
- 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.
8989

eslint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export default [
8787
'**/*.d.ts',
8888
'**/tests',
8989
'packages/svelte/scripts/process-messages/templates/*.js',
90+
'packages/svelte/scripts/_bundle.js',
9091
'packages/svelte/src/compiler/errors.js',
9192
'packages/svelte/src/internal/client/errors.js',
9293
'packages/svelte/src/internal/client/warnings.js',

packages/svelte/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# svelte
22

3+
## 5.34.9
4+
5+
### Patch Changes
6+
7+
- fix: ensure unowned deriveds can add themselves as reactions while connected ([#16249](https://github.com/sveltejs/svelte/pull/16249))
8+
39
## 5.34.8
410

511
### Patch Changes

packages/svelte/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "svelte",
33
"description": "Cybernetically enhanced web apps",
44
"license": "MIT",
5-
"version": "5.34.8",
5+
"version": "5.34.9",
66
"type": "module",
77
"types": "./types/index.d.ts",
88
"engines": {

packages/svelte/scripts/check-treeshakeability.js

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -118,36 +118,40 @@ const bundle = await bundle_code(
118118
).js.code
119119
);
120120

121-
if (!bundle.includes('hydrate_node') && !bundle.includes('hydrate_next')) {
122-
// eslint-disable-next-line no-console
123-
console.error(`✅ Hydration code treeshakeable`);
124-
} else {
125-
failed = true;
126-
// eslint-disable-next-line no-console
127-
console.error(`❌ Hydration code not treeshakeable`);
128-
}
121+
/**
122+
* @param {string} case_name
123+
* @param {string[]} strings
124+
*/
125+
function check_bundle(case_name, ...strings) {
126+
for (const string of strings) {
127+
const index = bundle.indexOf(string);
128+
if (index >= 0) {
129+
// eslint-disable-next-line no-console
130+
console.error(`❌ ${case_name} not treeshakeable`);
131+
failed = true;
129132

130-
if (!bundle.includes('component_context.l')) {
131-
// eslint-disable-next-line no-console
132-
console.error(`✅ Legacy code treeshakeable`);
133-
} else {
134-
failed = true;
133+
let lines = bundle.slice(index - 500, index + 500).split('\n');
134+
const target_line = lines.findIndex((line) => line.includes(string));
135+
// mark the failed line
136+
lines = lines
137+
.map((line, i) => (i === target_line ? `> ${line}` : `| ${line}`))
138+
.slice(target_line - 5, target_line + 6);
139+
// eslint-disable-next-line no-console
140+
console.error('The first failed line:\n' + lines.join('\n'));
141+
return;
142+
}
143+
}
135144
// eslint-disable-next-line no-console
136-
console.error(`❌ Legacy code not treeshakeable`);
145+
console.error(`${case_name} treeshakeable`);
137146
}
138147

139-
if (!bundle.includes(`'CreatedAt'`)) {
140-
// eslint-disable-next-line no-console
141-
console.error(`✅ $inspect.trace code treeshakeable`);
142-
} else {
143-
failed = true;
144-
// eslint-disable-next-line no-console
145-
console.error(`❌ $inspect.trace code not treeshakeable`);
146-
}
148+
check_bundle('Hydration code', 'hydrate_node', 'hydrate_next');
149+
check_bundle('Legacy code', 'component_context.l');
150+
check_bundle('$inspect.trace', `'CreatedAt'`);
147151

148152
if (failed) {
149153
// eslint-disable-next-line no-console
150-
console.error(bundle);
154+
console.error('Full bundle at', path.resolve('scripts/_bundle.js'));
151155
fs.writeFileSync('scripts/_bundle.js', bundle);
152156
}
153157

packages/svelte/src/compiler/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
/** @import { AST } from './public.js' */
44
import { walk as zimmerframe_walk } from 'zimmerframe';
55
import { convert } from './legacy.js';
6-
import { parse as parse_acorn } from './phases/1-parse/acorn.js';
76
import { parse as _parse } from './phases/1-parse/index.js';
87
import { remove_typescript_nodes } from './phases/1-parse/remove_typescript_nodes.js';
98
import { analyze_component, analyze_module } from './phases/2-analyze/index.js';

packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,7 @@ import {
2222
build_set_style
2323
} from './shared/element.js';
2424
import { process_children } from './shared/fragment.js';
25-
import {
26-
build_render_statement,
27-
build_template_chunk,
28-
build_update_assignment,
29-
get_expression_id,
30-
memoize_expression
31-
} from './shared/utils.js';
25+
import { build_render_statement, build_template_chunk, get_expression_id } from './shared/utils.js';
3226
import { visit_event_attribute } from './shared/events.js';
3327

3428
/**
@@ -200,24 +194,23 @@ export function RegularElement(node, context) {
200194

201195
const node_id = context.state.node;
202196

197+
/** If true, needs `__value` for inputs */
198+
const needs_special_value_handling =
199+
node.name === 'option' ||
200+
node.name === 'select' ||
201+
bindings.has('group') ||
202+
bindings.has('checked');
203+
203204
if (has_spread) {
204205
build_attribute_effect(attributes, class_directives, style_directives, context, node, node_id);
205206
} else {
206-
/** If true, needs `__value` for inputs */
207-
const needs_special_value_handling =
208-
node.name === 'option' ||
209-
node.name === 'select' ||
210-
bindings.has('group') ||
211-
bindings.has('checked');
212-
213207
for (const attribute of /** @type {AST.Attribute[]} */ (attributes)) {
214208
if (is_event_attribute(attribute)) {
215209
visit_event_attribute(attribute, context);
216210
continue;
217211
}
218212

219213
if (needs_special_value_handling && attribute.name === 'value') {
220-
build_element_special_value_attribute(node.name, node_id, attribute, context);
221214
continue;
222215
}
223216

@@ -392,6 +385,15 @@ export function RegularElement(node, context) {
392385
context.state.update.push(b.stmt(b.assignment('=', dir, dir)));
393386
}
394387

388+
if (!has_spread && needs_special_value_handling) {
389+
for (const attribute of /** @type {AST.Attribute[]} */ (attributes)) {
390+
if (attribute.name === 'value') {
391+
build_element_special_value_attribute(node.name, node_id, attribute, context);
392+
break;
393+
}
394+
}
395+
}
396+
395397
context.state.template.pop_element();
396398
}
397399

@@ -622,12 +624,7 @@ function build_element_special_value_attribute(element, node_id, attribute, cont
622624
element === 'select' && attribute.value !== true && !is_text_attribute(attribute);
623625

624626
const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
625-
metadata.has_call
626-
? // if is a select with value we will also invoke `init_select` which need a reference before the template effect so we memoize separately
627-
is_select_with_value
628-
? memoize_expression(state, value)
629-
: get_expression_id(state.expressions, value)
630-
: value
627+
metadata.has_call ? get_expression_id(state.expressions, value) : value
631628
);
632629

633630
const evaluated = context.state.scope.evaluate(value);
@@ -652,23 +649,21 @@ function build_element_special_value_attribute(element, node_id, attribute, cont
652649
: inner_assignment
653650
);
654651

655-
if (is_select_with_value) {
656-
state.init.push(b.stmt(b.call('$.init_select', node_id, b.thunk(value))));
657-
}
658-
659652
if (has_state) {
660-
const id = state.scope.generate(`${node_id.name}_value`);
661-
build_update_assignment(
662-
state,
663-
id,
664-
// `<option>` is a special case: The value property reflects to the DOM. If the value is set to undefined,
665-
// that means the value should be set to the empty string. To be able to do that when the value is
666-
// initially undefined, we need to set a value that is guaranteed to be different.
667-
element === 'option' ? b.object([]) : undefined,
668-
value,
669-
update
670-
);
653+
const id = b.id(state.scope.generate(`${node_id.name}_value`));
654+
655+
// `<option>` is a special case: The value property reflects to the DOM. If the value is set to undefined,
656+
// that means the value should be set to the empty string. To be able to do that when the value is
657+
// initially undefined, we need to set a value that is guaranteed to be different.
658+
const init = element === 'option' ? b.object([]) : undefined;
659+
660+
state.init.push(b.var(id, init));
661+
state.update.push(b.if(b.binary('!==', id, b.assignment('=', id, value)), b.block([update])));
671662
} else {
672663
state.init.push(update);
673664
}
665+
666+
if (is_select_with_value) {
667+
state.init.push(b.stmt(b.call('$.init_select', node_id)));
668+
}
674669
}

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -165,20 +165,6 @@ export function parse_directive_name(name) {
165165
return expression;
166166
}
167167

168-
/**
169-
* @param {ComponentClientTransformState} state
170-
* @param {string} id
171-
* @param {Expression | undefined} init
172-
* @param {Expression} value
173-
* @param {ExpressionStatement} update
174-
*/
175-
export function build_update_assignment(state, id, init, value, update) {
176-
state.init.push(b.var(id, init));
177-
state.update.push(
178-
b.if(b.binary('!==', b.id(id), b.assignment('=', b.id(id), value)), b.block([update]))
179-
);
180-
}
181-
182168
/**
183169
* Serializes `bind:this` for components and elements.
184170
* @param {Identifier | MemberExpression | SequenceExpression} expression

packages/svelte/src/internal/client/dom/elements/attributes.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { clsx } from '../../../shared/attributes.js';
2020
import { set_class } from './class.js';
2121
import { set_style } from './style.js';
2222
import { ATTACHMENT_KEY, NAMESPACE_HTML } from '../../../../constants.js';
23-
import { block, branch, destroy_effect } from '../../reactivity/effects.js';
23+
import { block, branch, destroy_effect, effect } from '../../reactivity/effects.js';
2424
import { derived } from '../../reactivity/deriveds.js';
2525
import { init_select, select_option } from './bindings/select.js';
2626

@@ -513,10 +513,12 @@ export function attribute_effect(
513513
});
514514

515515
if (is_select) {
516-
init_select(
517-
/** @type {HTMLSelectElement} */ (element),
518-
() => /** @type {Record<string | symbol, any>} */ (prev).value
519-
);
516+
var select = /** @type {HTMLSelectElement} */ (element);
517+
518+
effect(() => {
519+
select_option(select, /** @type {Record<string | symbol, any>} */ (prev).value);
520+
init_select(select);
521+
});
520522
}
521523

522524
inited = true;

0 commit comments

Comments
 (0)