Merge branch 'main' into svelte-custom-renderer

pull/18317/head
Paolo Ricciuti 2 months ago committed by GitHub
commit 91563f0b04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: unlink errored and otherwise finished batch

@ -1,5 +0,0 @@
---
'svelte': patch
---
perf: walk composedPath() directly in delegated event propagation

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: transfer effects when merging batches

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: allow `$derived(await ...)` in disconnected effect roots

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: remove temporary raw-text hydration markers

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: propagate async `@const` blockers through closure references so template expressions like `{(() => host)()}` correctly wait for the awaited value

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: properly unlink batches

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: settle discarded batch

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: declare `let:` directives before `{@const}` declarations on slotted elements

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: resume outro-ed branches if they were kept around

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: avoid waterfall-warning when async resolves to same value

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: correctly coordinate component-level effects inside async blocks

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: make unnecessary commit work less likely

@ -1,5 +0,0 @@
---
"svelte": patch
---
chore: add tag name to `a11y_click_events_have_key_events` warning

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: catch rejected promises while merging/committing

@ -88,6 +88,10 @@ Effect cannot be created inside a `$derived` value that was not itself created i
`%rune%` can only be used inside an effect (e.g. during component initialisation)
```
Effects can only be created while a parent effect is running. This means that they cannot, for example, be created inside an event handler or after an `await` expression (unless the `await` occurs directly inside a component's `<script>` tag, and not inside an async function).
In very rare cases, it is appropriate to use [`$effect.root`]($effect#$effect.root) so that you can create effects outside the normal component lifecycle.
### effect_pending_outside_reaction
```

@ -1,5 +1,39 @@
# svelte
## 5.55.10
### Patch Changes
- fix: unlink errored and otherwise finished batch ([#18264](https://github.com/sveltejs/svelte/pull/18264))
- perf: walk composedPath() directly in delegated event propagation ([#18268](https://github.com/sveltejs/svelte/pull/18268))
- fix: transfer effects when merging batches ([#18254](https://github.com/sveltejs/svelte/pull/18254))
- fix: allow `$derived(await ...)` in disconnected effect roots ([#18273](https://github.com/sveltejs/svelte/pull/18273))
- fix: remove temporary raw-text hydration markers ([#18269](https://github.com/sveltejs/svelte/pull/18269))
- fix: propagate async `@const` blockers through closure references so template expressions like `{(() => host)()}` correctly wait for the awaited value ([#18309](https://github.com/sveltejs/svelte/pull/18309))
- fix: properly unlink batches ([#18298](https://github.com/sveltejs/svelte/pull/18298))
- fix: settle discarded batch ([#18290](https://github.com/sveltejs/svelte/pull/18290))
- fix: declare `let:` directives before `{@const}` declarations on slotted elements ([#18271](https://github.com/sveltejs/svelte/pull/18271))
- fix: resume outro-ed branches if they were kept around ([#18291](https://github.com/sveltejs/svelte/pull/18291))
- fix: avoid waterfall-warning when async resolves to same value ([#18297](https://github.com/sveltejs/svelte/pull/18297))
- fix: correctly coordinate component-level effects inside async blocks ([#18260](https://github.com/sveltejs/svelte/pull/18260))
- fix: make unnecessary commit work less likely ([#18263](https://github.com/sveltejs/svelte/pull/18263))
- chore: add tag name to `a11y_click_events_have_key_events` warning ([#18272](https://github.com/sveltejs/svelte/pull/18272))
- fix: catch rejected promises while merging/committing ([#18266](https://github.com/sveltejs/svelte/pull/18266))
## 5.55.9
### Patch Changes

@ -60,6 +60,10 @@ The key expression in a keyed each block must return the same value when called
> `%rune%` can only be used inside an effect (e.g. during component initialisation)
Effects can only be created while a parent effect is running. This means that they cannot, for example, be created inside an event handler or after an `await` expression (unless the `await` occurs directly inside a component's `<script>` tag, and not inside an async function).
In very rare cases, it is appropriate to use [`$effect.root`]($effect#$effect.root) so that you can create effects outside the normal component lifecycle.
## effect_pending_outside_reaction
> `$effect.pending()` can only be called inside an effect or derived

@ -2,7 +2,7 @@
"name": "svelte",
"description": "Cybernetically enhanced web apps",
"license": "MIT",
"version": "5.55.9",
"version": "5.55.10",
"type": "module",
"types": "./types/index.d.ts",
"engines": {
@ -145,7 +145,7 @@
"templating"
],
"scripts": {
"build": "rollup -c && pnpm generate",
"build": "rollup -c && pnpm generate && node scripts/check-treeshakeability.js",
"dev": "node scripts/process-messages -w & rollup -cw",
"check": "tsc --project tsconfig.runtime.json && tsc && cd ./tests/types && tsc",
"check:tsgo": "tsgo --project tsconfig.runtime.json --skipLibCheck && tsgo --skipLibCheck",
@ -153,7 +153,7 @@
"generate": "node scripts/process-messages && node ./scripts/generate-types.js",
"generate:version": "node ./scripts/generate-version.js",
"generate:types": "node ./scripts/generate-types.js && tsc -p tsconfig.generated.json",
"prepublishOnly": "pnpm build && node scripts/check-treeshakeability.js",
"prepublishOnly": "pnpm build",
"knip": "pnpm dlx knip"
},
"devDependencies": {

@ -115,8 +115,7 @@ function is_last_evaluated_expression(path, node) {
break;
case 'MemberExpression':
if (parent.computed && node === parent.object) return false;
break;
return false;
case 'ObjectExpression':
if (node !== parent.properties.at(-1)) return false;

@ -194,11 +194,6 @@ export function VariableDeclaration(node, context) {
/** @type {CallExpression} */ (init)
);
// for now, only wrap async derived in $.save if it's not
// a top-level instance derived. TODO in future maybe we
// can dewaterfall all of them?
const should_save = context.state.is_instance && context.state.scope.function_depth > 1;
if (declarator.id.type === 'Identifier') {
let expression = /** @type {Expression} */ (context.visit(value));
@ -213,9 +208,7 @@ export function VariableDeclaration(node, context) {
location ? b.literal(location) : undefined
);
call = should_save ? save(call, true) : b.await(call);
declarations.push(b.declarator(declarator.id, call));
declarations.push(b.declarator(declarator.id, b.await(call)));
} else {
if (rune === '$derived') expression = b.thunk(expression);
@ -251,7 +244,7 @@ export function VariableDeclaration(node, context) {
location ? b.literal(location) : undefined
);
call = should_save ? save(call, true) : b.await(call);
call = b.await(call);
}
declarations.push(b.declarator(id, call));

@ -633,8 +633,7 @@ export function has_await_expression(node) {
/**
* Turns `await ...` to `(await $.save(...))()`
* @param {ESTree.Expression} expression
* @param {boolean} unset
*/
export function save(expression, unset = false) {
return b.call(b.await(b.call('$.save', expression, unset && b.true)));
export function save(expression) {
return b.call(b.await(b.call('$.save', expression)));
}

@ -26,7 +26,6 @@ import {
set_reactivity_loss_tracker
} from './deriveds.js';
import { aborted } from './effects.js';
import { queue_micro_task } from '../dom/task.js';
/**
* @param {Blocker[]} blockers
@ -153,25 +152,13 @@ export function capture() {
* `await a + b` becomes `(await $.save(a))() + b`
* @template T
* @param {Promise<T>} promise
* @param {boolean} unset
* @returns {Promise<() => T>}
*/
export async function save(promise, unset) {
var batch = current_batch;
export async function save(promise) {
var restore = capture();
var value = await promise;
return () => {
if (unset) {
// If this is happening outside the context of an async derived,
// context will not automatically be unset
queue_micro_task(() => {
if (batch === current_batch) {
unset_context();
}
});
}
restore();
return value;
};

@ -127,13 +127,6 @@ export class Batch {
*/
previous = new Map();
/**
* Async effects which this batch doesn't take into account anymore when calculating blockers,
* as it has a value for it already.
* @type {Set<Effect>}
*/
unblocked = new Set();
/**
* When the batch is committed (and the DOM is updated), we need to remove old branches
* and append new ones by calling the functions added inside (if/each/key/etc) blocks
@ -214,6 +207,18 @@ export class Batch {
#decrement_queued = false;
constructor() {
// link batch
if (last_batch === null) {
first_batch = last_batch = this;
} else {
last_batch.#next = this;
this.#prev = last_batch;
}
last_batch = this;
}
#is_deferred() {
if (this.is_fork) return true;
@ -327,11 +332,11 @@ export class Batch {
} catch (e) {
reset_all(root);
// If there's no async work left, this branch is now dead and needs
// to be unlinked to not become a zombie that is never cleaned up.
// to be discarded to not become a zombie that is never cleaned up.
// See https://github.com/sveltejs/svelte/issues/18221#issuecomment-4497918414
// for a (non-minimal) reproduction that demonstrates a case where this is necessary
// to not get follow-up false-positives via "batch has scheduled roots" invariant errors.
if (!this.#is_deferred()) this.#unlink();
if (!this.#is_deferred()) this.discard();
throw e;
}
}
@ -852,7 +857,6 @@ export class Batch {
static ensure() {
if (current_batch === null) {
const batch = (current_batch = new Batch());
batch.#link();
if (!is_processing && !is_flushing_sync) {
queue_micro_task(() => {
@ -972,18 +976,11 @@ export class Batch {
this.#roots.push(e);
}
#link() {
if (last_batch === null) {
first_batch = last_batch = this;
} else {
last_batch.#next = this;
this.#prev = last_batch;
}
last_batch = this;
}
#unlink() {
// #merge calls #unlink, discard later on does it again - prevent
// running it multiple times to not corrupt the linked list
if (!this.linked) return;
var prev = this.#prev;
var next = this.#next;

@ -4,5 +4,5 @@
* The current version, as set in package.json.
* @type {string}
*/
export const VERSION = '5.55.9';
export const VERSION = '5.55.10';
export const PUBLIC_VERSION = '5';

@ -6,14 +6,13 @@
async function fn() {
let count = $state(1);
increment = () => { count++; };
$effect.pre(() => console.log(count))
const value = $derived(await count);
$effect.pre(() => console.log(value))
return { get value() { return value } };
}
fn().then(r => console.log(r.value));
})
})
</script>
<button onclick={() => increment()}>increment</button>

@ -0,0 +1,12 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, logs }) {
await tick();
assert.deepEqual(logs, [
`effect_orphan\n\`$effect\` can only be used inside an effect (e.g. during component initialisation)\nhttps://svelte.dev/e/effect_orphan`
]);
}
});

@ -0,0 +1,14 @@
<script>
async function foo() {
await 0;
$effect(() => {
console.log('this effect is orphaned');
});
}
try {
await foo();
} catch (e) {
console.log(e.message)
}
</script>

@ -1,14 +0,0 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, logs }) {
await new Promise((resolve) => setTimeout(resolve, 10));
assert.deepEqual(logs, [1, 1]);
const [button] = document.querySelectorAll('button');
button.click();
await tick();
assert.deepEqual(logs, [1, 1, 2]);
}
});

@ -1,15 +0,0 @@
<script>
let increment;
async function fn() {
let count = $state(1);
increment = () => { count++; };
const value = $derived(await count);
$effect.pre(() => console.log(value))
return { get value() { return value } };
}
fn().then(r => console.log(r.value));
</script>
<button onclick={() => increment()}>increment</button>

@ -59,19 +59,21 @@ for (const generate of /** @type {const} */ (['client', 'server'])) {
mkdirp(path.dirname(output_js));
if (generate === 'client') {
if (AST) {
if (AST || PRINT) {
const ast = parse(source, {
modern: true
});
write(
`${cwd}/output/ast/${file}.json`,
JSON.stringify(
ast,
(key, value) => (typeof value === 'bigint' ? ['BigInt', value.toString()] : value),
'\t'
)
);
if (AST) {
write(
`${cwd}/output/ast/${file}.json`,
JSON.stringify(
ast,
(key, value) => (typeof value === 'bigint' ? ['BigInt', value.toString()] : value),
'\t'
)
);
}
if (PRINT) {
const printed = print(ast);

Loading…
Cancel
Save