Skip to content

Commit 05856f3

Browse files
committed
Merge branch 'proxy-parent' of github.com:sveltejs/svelte into proxy-parent
2 parents 32183d1 + 47e35cf commit 05856f3

File tree

17 files changed

+254
-122
lines changed

17 files changed

+254
-122
lines changed

.changeset/poor-hats-design.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: return ast from `compile` (like Svelte 4 does)

.changeset/weak-drinks-speak.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: ensure bind:this unmount behavior for members is conditional

packages/svelte/src/compiler/index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export function compile(source, options) {
4444
const analysis = analyze_component(parsed, source, combined_options);
4545

4646
const result = transform_component(analysis, source, combined_options);
47+
result.ast = to_public_ast(source, parsed, options.modernAst);
4748
return result;
4849
} catch (e) {
4950
if (e instanceof CompileError) {
@@ -121,7 +122,16 @@ export function parse(source, options = {}) {
121122
throw e;
122123
}
123124

124-
if (options.modern) {
125+
return to_public_ast(source, ast, options.modern);
126+
}
127+
128+
/**
129+
* @param {string} source
130+
* @param {import('#compiler').Root} ast
131+
* @param {boolean | undefined} modern
132+
*/
133+
function to_public_ast(source, ast, modern) {
134+
if (modern) {
125135
// remove things that we don't want to treat as public API
126136
return walk(ast, null, {
127137
_(node, { next }) {

packages/svelte/src/compiler/legacy.js

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -141,29 +141,37 @@ export function convert(source, ast) {
141141
};
142142

143143
if (node.pending) {
144-
const first = /** @type {import('#compiler').BaseNode} */ (node.pending.nodes.at(0));
145-
const last = /** @type {import('#compiler').BaseNode} */ (node.pending.nodes.at(-1));
144+
const first = node.pending.nodes.at(0);
145+
const last = node.pending.nodes.at(-1);
146146

147-
pendingblock.start = first.start;
148-
pendingblock.end = last.end;
147+
pendingblock.start = first?.start ?? source.indexOf('}', node.expression.end) + 1;
148+
pendingblock.end = last?.end ?? pendingblock.start;
149149
pendingblock.skip = false;
150150
}
151151

152152
if (node.then) {
153-
const first = /** @type {import('#compiler').BaseNode} */ (node.then.nodes.at(0));
154-
const last = /** @type {import('#compiler').BaseNode} */ (node.then.nodes.at(-1));
153+
const first = node.then.nodes.at(0);
154+
const last = node.then.nodes.at(-1);
155155

156-
thenblock.start = pendingblock.end ?? first.start;
157-
thenblock.end = last.end;
156+
thenblock.start =
157+
pendingblock.end ?? first?.start ?? source.indexOf('}', node.expression.end) + 1;
158+
thenblock.end =
159+
last?.end ?? source.lastIndexOf('}', pendingblock.end ?? node.expression.end) + 1;
158160
thenblock.skip = false;
159161
}
160162

161163
if (node.catch) {
162-
const first = /** @type {import('#compiler').BaseNode} */ (node.catch.nodes.at(0));
163-
const last = /** @type {import('#compiler').BaseNode} */ (node.catch.nodes.at(-1));
164-
165-
catchblock.start = thenblock.end ?? pendingblock.end ?? first.start;
166-
catchblock.end = last.end;
164+
const first = node.catch.nodes.at(0);
165+
const last = node.catch.nodes.at(-1);
166+
167+
catchblock.start =
168+
thenblock.end ??
169+
pendingblock.end ??
170+
first?.start ??
171+
source.indexOf('}', node.expression.end) + 1;
172+
catchblock.end =
173+
last?.end ??
174+
source.lastIndexOf('}', thenblock.end ?? pendingblock.end ?? node.expression.end) + 1;
167175
catchblock.skip = false;
168176
}
169177

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,15 @@ function serialize_bind_this(bind_this, context, node) {
917917

918918
/** @type {import('estree').Expression[]} */
919919
const args = [node, b.arrow([b.id('$$value'), ...ids], update), b.arrow([...ids], bind_this_id)];
920+
// If we're mutating a property, then it might already be non-existent.
921+
// If we make all the object nodes optional, then it avoids any runtime exceptions.
922+
/** @type {import('estree').Expression | import('estree').Super} */
923+
let bind_node = bind_this_id;
924+
925+
while (bind_node?.type === 'MemberExpression') {
926+
bind_node.optional = true;
927+
bind_node = bind_node.object;
928+
}
920929
if (each_ids.size) {
921930
args.push(b.thunk(b.array(Array.from(each_ids.values()).map((id) => id[1]))));
922931
}

packages/svelte/src/compiler/phases/3-transform/index.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ export function transform_component(analysis, source, options) {
2020
warnings: transform_warnings(source, options.filename, analysis.warnings),
2121
metadata: {
2222
runes: analysis.runes
23-
}
23+
},
24+
ast: /** @type {any} */ (null) // set afterwards
2425
};
2526
}
2627

@@ -62,7 +63,8 @@ export function transform_component(analysis, source, options) {
6263
warnings: transform_warnings(source, options.filename, analysis.warnings), // TODO apply preprocessor sourcemap
6364
metadata: {
6465
runes: analysis.runes
65-
}
66+
},
67+
ast: /** @type {any} */ (null) // set afterwards
6668
};
6769
}
6870

@@ -80,7 +82,8 @@ export function transform_module(analysis, source, options) {
8082
warnings: transform_warnings(source, analysis.name, analysis.warnings),
8183
metadata: {
8284
runes: true
83-
}
85+
},
86+
ast: /** @type {any} */ (null) // set afterwards
8487
};
8588
}
8689

@@ -105,7 +108,8 @@ export function transform_module(analysis, source, options) {
105108
warnings: transform_warnings(source, analysis.name, analysis.warnings),
106109
metadata: {
107110
runes: true
108-
}
111+
},
112+
ast: /** @type {any} */ (null) // set afterwards
109113
};
110114
}
111115

packages/svelte/src/compiler/types/index.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ export interface CompileResult {
4646
*/
4747
runes: boolean;
4848
};
49+
/** The AST */
50+
ast: any;
4951
}
5052

5153
export interface Warning {
@@ -184,6 +186,13 @@ export interface CompileOptions extends ModuleCompileOptions {
184186
* @default false
185187
*/
186188
hmr?: boolean;
189+
/**
190+
* If `true`, returns the modern version of the AST.
191+
* Will become `true` by default in Svelte 6, and the option will be removed in Svelte 7.
192+
*
193+
* @default false
194+
*/
195+
modernAst?: boolean;
187196
}
188197

189198
export interface ModuleCompileOptions {

packages/svelte/src/internal/client/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export {
137137
$window as window,
138138
$document as document
139139
} from './dom/operations.js';
140-
export { noop, call_once } from '../shared/utils.js';
140+
export { noop } from '../shared/utils.js';
141141
export {
142142
add_snippet_symbol,
143143
validate_component,

packages/svelte/src/internal/shared/utils.js

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,3 @@ export function run_all(arr) {
2323
arr[i]();
2424
}
2525
}
26-
27-
/**
28-
* @param {Function} fn
29-
*/
30-
export function call_once(fn) {
31-
let called = false;
32-
/** @type {unknown} */
33-
let result;
34-
return function () {
35-
if (!called) {
36-
called = true;
37-
result = fn();
38-
}
39-
return result;
40-
};
41-
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
let { item = $bindable() } = $props();
3+
</script>
4+
5+
<div bind:this={item.dom}>
6+
{item.text}
7+
</div>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
async test({ assert, target, component }) {
6+
const [b1, b2] = target.querySelectorAll('button');
7+
8+
flushSync(() => {
9+
b1.click();
10+
b1.click();
11+
b1.click();
12+
});
13+
14+
assert.htmlEqual(
15+
target.innerHTML,
16+
`<button>add item</button><button>clear</button><div>Item 1</div><div>Item 2</div><div>Item 3</div>`
17+
);
18+
19+
flushSync(() => {
20+
b2.click();
21+
});
22+
23+
assert.htmlEqual(target.innerHTML, `<button>add item</button><button>clear</button>`);
24+
}
25+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script>
2+
import Child from './Child.svelte';
3+
let items = $state([]);
4+
5+
function add_item() {
6+
items.push({
7+
id: items.length,
8+
text: 'Item ' + (items.length + 1),
9+
dom: null,
10+
})
11+
}
12+
13+
function clear() {
14+
items = [];
15+
}
16+
</script>
17+
18+
<button on:click={add_item}>add item</button>
19+
<button on:click={clear}>clear</button>
20+
21+
{#each items as item, index (item.id)}
22+
<Child bind:item={items[index]} />
23+
{/each}

packages/svelte/types/index.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,8 @@ declare module 'svelte/compiler' {
541541
*/
542542
runes: boolean;
543543
};
544+
/** The AST */
545+
ast: any;
544546
}
545547

546548
interface Warning {
@@ -675,6 +677,13 @@ declare module 'svelte/compiler' {
675677
* @default false
676678
*/
677679
hmr?: boolean;
680+
/**
681+
* If `true`, returns the modern version of the AST.
682+
* Will become `true` by default in Svelte 6, and the option will be removed in Svelte 7.
683+
*
684+
* @default false
685+
*/
686+
modernAst?: boolean;
678687
}
679688

680689
interface ModuleCompileOptions {
@@ -2476,6 +2485,13 @@ declare module 'svelte/types/compiler/interfaces' {
24762485
* @default false
24772486
*/
24782487
hmr?: boolean;
2488+
/**
2489+
* If `true`, returns the modern version of the AST.
2490+
* Will become `true` by default in Svelte 6, and the option will be removed in Svelte 7.
2491+
*
2492+
* @default false
2493+
*/
2494+
modernAst?: boolean;
24792495
}
24802496

24812497
interface ModuleCompileOptions {

sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -93,25 +93,6 @@ This can improve performance with large arrays and objects that you weren't plan
9393

9494
> Objects and arrays passed to `$state.frozen` will be shallowly frozen using `Object.freeze()`. If you don't want this, pass in a clone of the object or array instead.
9595
96-
### Reactive Map, Set and Date
97-
98-
Svelte provides reactive `Map`, `Set` and `Date` classes. These can be imported from `svelte/reactivity` and used just like their native counterparts.
99-
100-
```svelte
101-
<script>
102-
import { Map } from 'svelte/reactivity';
103-
104-
const map = new Map();
105-
map.set('message', 'hello');
106-
107-
function update_message() {
108-
map.set('message', 'goodbye');
109-
}
110-
</script>
111-
112-
<p>{map.get('message')}</p>
113-
```
114-
11596
## `$state.snapshot`
11697

11798
To take a static snapshot of a deeply reactive `$state` proxy, use `$state.snapshot`:

sites/svelte-5-preview/src/routes/docs/content/01-api/05-functions.md

Lines changed: 0 additions & 68 deletions
This file was deleted.

0 commit comments

Comments
 (0)