From 0bc81ebaca77f95b9b95a759209b47f22a3c04e3 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 21 Oct 2025 08:51:34 -0700 Subject: [PATCH 01/11] fix: make `$inspect` logs come from the callsite --- .changeset/silly-penguins-sleep.md | 5 +++ .../client/visitors/CallExpression.js | 34 +++++++++++++++++-- .../server/visitors/CallExpression.js | 23 ++++++++++--- .../src/compiler/phases/3-transform/utils.js | 29 ---------------- .../svelte/src/internal/client/dev/inspect.js | 5 ++- packages/svelte/src/internal/server/index.js | 9 ----- 6 files changed, 57 insertions(+), 48 deletions(-) create mode 100644 .changeset/silly-penguins-sleep.md diff --git a/.changeset/silly-penguins-sleep.md b/.changeset/silly-penguins-sleep.md new file mode 100644 index 000000000000..f397f1e8ba17 --- /dev/null +++ b/.changeset/silly-penguins-sleep.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: make `$inspect` logs come from the callsite diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js index bf9a09bb74b1..54c2b97a0b23 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js @@ -1,9 +1,8 @@ -/** @import { CallExpression, Expression } from 'estree' */ +/** @import { CallExpression, Expression, MemberExpression } from 'estree' */ /** @import { Context } from '../types' */ import { dev, is_ignored } from '../../../../state.js'; import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; -import { transform_inspect_rune } from '../../utils.js'; import { should_proxy } from '../utils.js'; /** @@ -73,7 +72,7 @@ export function CallExpression(node, context) { case '$inspect': case '$inspect().with': - return transform_inspect_rune(node, context); + return transform_inspect_rune(rune, node, context); } if ( @@ -104,3 +103,32 @@ export function CallExpression(node, context) { context.next(); } + +/** + * @param {'$inspect' | '$inspect().with'} rune + * @param {CallExpression} node + * @param {Context} context + */ +function transform_inspect_rune(rune, node, context) { + if (!dev) return b.empty; + + const { visit } = context; + + const call = + rune === '$inspect' + ? node + : /** @type {CallExpression} */ (/** @type {MemberExpression} */ (node.callee).object); + + const args = call.arguments.map((arg) => /** @type {Expression} */ (context.visit(arg))); + + const inspector = + rune === '$inspect' + ? 'console.log' + : /** @type {Expression} */ (context.visit(node.arguments[0])); + + // by passing an arrow function, the log appears to come from the `$inspect` callsite + // rather than the `inspect.js` file containing the utility + const fn = b.arrow([b.rest(b.id('$$args'))], b.call(inspector, b.spread(b.id('$$args')))); + + return b.call('$.inspect', b.thunk(b.array(args)), fn); +} diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js index d53b631aa5ec..76d7efbf2178 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js @@ -1,9 +1,8 @@ -/** @import { CallExpression, Expression } from 'estree' */ +/** @import { CallExpression, Expression, MemberExpression } from 'estree' */ /** @import { Context } from '../types.js' */ -import { is_ignored } from '../../../../state.js'; +import { dev, is_ignored } from '../../../../state.js'; import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; -import { transform_inspect_rune } from '../../utils.js'; /** * @param {CallExpression} node @@ -51,7 +50,23 @@ export function CallExpression(node, context) { } if (rune === '$inspect' || rune === '$inspect().with') { - return transform_inspect_rune(node, context); + if (!dev) return b.empty; + + const call = + rune === '$inspect' + ? node + : /** @type {CallExpression} */ (/** @type {MemberExpression} */ (node.callee).object); + + const args = call.arguments.map((arg) => /** @type {Expression} */ (context.visit(arg))); + + const inspector = + rune === '$inspect' + ? 'console.log' + : /** @type {Expression} */ (context.visit(node.arguments[0])); + + // TODO is the `init` doing any work on the server? should it be `$inspect` instead? + // should we include a stack trace? + return b.call(inspector, b.literal('init'), ...args); } context.next(); diff --git a/packages/svelte/src/compiler/phases/3-transform/utils.js b/packages/svelte/src/compiler/phases/3-transform/utils.js index 1445ce3aa6b1..fcdd35fd63f2 100644 --- a/packages/svelte/src/compiler/phases/3-transform/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/utils.js @@ -450,32 +450,3 @@ export function determine_namespace_for_children(node, namespace) { return node.metadata.mathml ? 'mathml' : 'html'; } - -/** - * @template {TransformState} T - * @param {CallExpression} node - * @param {Context} context - */ -export function transform_inspect_rune(node, context) { - const { state, visit } = context; - const as_fn = state.options.generate === 'client'; - - if (!dev) return b.empty; - - if (node.callee.type === 'MemberExpression') { - const raw_inspect_args = /** @type {CallExpression} */ (node.callee.object).arguments; - const inspect_args = - /** @type {Array} */ - (raw_inspect_args.map((arg) => visit(arg))); - const with_arg = /** @type {Expression} */ (visit(node.arguments[0])); - - return b.call( - '$.inspect', - as_fn ? b.thunk(b.array(inspect_args)) : b.array(inspect_args), - with_arg - ); - } else { - const arg = node.arguments.map((arg) => /** @type {Expression} */ (visit(arg))); - return b.call('$.inspect', as_fn ? b.thunk(b.array(arg)) : b.array(arg)); - } -} diff --git a/packages/svelte/src/internal/client/dev/inspect.js b/packages/svelte/src/internal/client/dev/inspect.js index f79cf472991a..14ce1e70111f 100644 --- a/packages/svelte/src/internal/client/dev/inspect.js +++ b/packages/svelte/src/internal/client/dev/inspect.js @@ -5,10 +5,9 @@ import { untrack } from '../runtime.js'; /** * @param {() => any[]} get_value - * @param {Function} [inspector] + * @param {Function} inspector */ -// eslint-disable-next-line no-console -export function inspect(get_value, inspector = console.log) { +export function inspect(get_value, inspector) { validate_effect('$inspect'); let initial = true; diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 50bb629c4d5a..74a90a8600f5 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -418,15 +418,6 @@ export function ensure_array_like(array_like_or_iterator) { return []; } -/** - * @param {any[]} args - * @param {Function} [inspect] - */ -// eslint-disable-next-line no-console -export function inspect(args, inspect = console.log) { - inspect('init', ...args); -} - /** * @template V * @param {() => V} get_value From 1b0f0500afd723fa701919550b19046fab8f6e21 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 21 Oct 2025 11:15:19 -0700 Subject: [PATCH 02/11] default to showing stack trace --- .../3-transform/client/visitors/CallExpression.js | 2 +- .../svelte/src/internal/client/dev/inspect.js | 15 +++++++++++++-- .../svelte/src/internal/client/dev/tracing.js | 4 ++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js index 54c2b97a0b23..dfc5128b2736 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js @@ -130,5 +130,5 @@ function transform_inspect_rune(rune, node, context) { // rather than the `inspect.js` file containing the utility const fn = b.arrow([b.rest(b.id('$$args'))], b.call(inspector, b.spread(b.id('$$args')))); - return b.call('$.inspect', b.thunk(b.array(args)), fn); + return b.call('$.inspect', b.thunk(b.array(args)), fn, rune === '$inspect' && b.true); } diff --git a/packages/svelte/src/internal/client/dev/inspect.js b/packages/svelte/src/internal/client/dev/inspect.js index 14ce1e70111f..db7ab0d976b7 100644 --- a/packages/svelte/src/internal/client/dev/inspect.js +++ b/packages/svelte/src/internal/client/dev/inspect.js @@ -2,12 +2,14 @@ import { UNINITIALIZED } from '../../../constants.js'; import { snapshot } from '../../shared/clone.js'; import { inspect_effect, render_effect, validate_effect } from '../reactivity/effects.js'; import { untrack } from '../runtime.js'; +import { get_stack } from './tracing.js'; /** * @param {() => any[]} get_value * @param {Function} inspector + * @param {boolean} show_stack */ -export function inspect(get_value, inspector) { +export function inspect(get_value, inspector, show_stack = false) { validate_effect('$inspect'); let initial = true; @@ -27,7 +29,16 @@ export function inspect(get_value, inspector) { var snap = snapshot(value, true, true); untrack(() => { - inspector(initial ? 'init' : 'update', ...snap); + if (show_stack) { + inspector(...snap); + + if (!initial) { + // eslint-disable-next-line no-console + console.log(get_stack('UpdatedAt')); + } + } else { + inspector(initial ? 'init' : 'update', ...snap); + } }); initial = false; diff --git a/packages/svelte/src/internal/client/dev/tracing.js b/packages/svelte/src/internal/client/dev/tracing.js index 673a710faca6..b44c39a631f2 100644 --- a/packages/svelte/src/internal/client/dev/tracing.js +++ b/packages/svelte/src/internal/client/dev/tracing.js @@ -134,7 +134,11 @@ export function trace(label, fn) { * @returns {Error & { stack: string } | null} */ export function get_stack(label) { + const limit = Error.stackTraceLimit; + Error.stackTraceLimit = Infinity; let error = Error(); + Error.stackTraceLimit = limit; + const stack = error.stack; if (!stack) return null; From 4839f0ca8aa236f77a6e5517c0eb9b04502827a3 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 21 Oct 2025 11:16:34 -0700 Subject: [PATCH 03/11] reuse id --- .../phases/3-transform/client/visitors/CallExpression.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js index dfc5128b2736..bdc8fc3043a5 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js @@ -112,8 +112,6 @@ export function CallExpression(node, context) { function transform_inspect_rune(rune, node, context) { if (!dev) return b.empty; - const { visit } = context; - const call = rune === '$inspect' ? node @@ -128,7 +126,8 @@ function transform_inspect_rune(rune, node, context) { // by passing an arrow function, the log appears to come from the `$inspect` callsite // rather than the `inspect.js` file containing the utility - const fn = b.arrow([b.rest(b.id('$$args'))], b.call(inspector, b.spread(b.id('$$args')))); + const id = b.id('$$args'); + const fn = b.arrow([b.rest(id)], b.call(inspector, b.spread(id))); return b.call('$.inspect', b.thunk(b.array(args)), fn, rune === '$inspect' && b.true); } From 47d2f2c7ada33961ca0b9e03e2d8a5f34eb22178 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 21 Oct 2025 11:38:57 -0700 Subject: [PATCH 04/11] DRY out, improve server-side inspect --- .../client/visitors/CallExpression.js | 13 ++---------- .../server/visitors/CallExpression.js | 19 +++++------------- .../src/compiler/phases/3-transform/utils.js | 20 ++++++++++++++++++- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js index bdc8fc3043a5..ae60f3be4070 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js @@ -4,6 +4,7 @@ import { dev, is_ignored } from '../../../../state.js'; import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; import { should_proxy } from '../utils.js'; +import { get_inspect_args } from '../../utils.js'; /** * @param {CallExpression} node @@ -112,17 +113,7 @@ export function CallExpression(node, context) { function transform_inspect_rune(rune, node, context) { if (!dev) return b.empty; - const call = - rune === '$inspect' - ? node - : /** @type {CallExpression} */ (/** @type {MemberExpression} */ (node.callee).object); - - const args = call.arguments.map((arg) => /** @type {Expression} */ (context.visit(arg))); - - const inspector = - rune === '$inspect' - ? 'console.log' - : /** @type {Expression} */ (context.visit(node.arguments[0])); + const { args, inspector } = get_inspect_args(rune, node, context.visit); // by passing an arrow function, the log appears to come from the `$inspect` callsite // rather than the `inspect.js` file containing the utility diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js index 76d7efbf2178..bba6511eec8d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js @@ -3,6 +3,7 @@ import { dev, is_ignored } from '../../../../state.js'; import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; +import { get_inspect_args } from '../../utils.js'; /** * @param {CallExpression} node @@ -52,21 +53,11 @@ export function CallExpression(node, context) { if (rune === '$inspect' || rune === '$inspect().with') { if (!dev) return b.empty; - const call = - rune === '$inspect' - ? node - : /** @type {CallExpression} */ (/** @type {MemberExpression} */ (node.callee).object); + const { args, inspector } = get_inspect_args(rune, node, context.visit); - const args = call.arguments.map((arg) => /** @type {Expression} */ (context.visit(arg))); - - const inspector = - rune === '$inspect' - ? 'console.log' - : /** @type {Expression} */ (context.visit(node.arguments[0])); - - // TODO is the `init` doing any work on the server? should it be `$inspect` instead? - // should we include a stack trace? - return b.call(inspector, b.literal('init'), ...args); + return rune === '$inspect' + ? b.call(inspector, b.literal('$inspect('), ...args, b.literal(')')) + : b.call(inspector, b.literal('init'), ...args); } context.next(); diff --git a/packages/svelte/src/compiler/phases/3-transform/utils.js b/packages/svelte/src/compiler/phases/3-transform/utils.js index fcdd35fd63f2..dfc2ab1de140 100644 --- a/packages/svelte/src/compiler/phases/3-transform/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/utils.js @@ -1,7 +1,7 @@ /** @import { Context } from 'zimmerframe' */ /** @import { TransformState } from './types.js' */ /** @import { AST, Binding, Namespace, ValidatedCompileOptions } from '#compiler' */ -/** @import { Node, Expression, CallExpression } from 'estree' */ +/** @import { Node, Expression, CallExpression, MemberExpression } from 'estree' */ import { regex_ends_with_whitespaces, regex_not_whitespace, @@ -450,3 +450,21 @@ export function determine_namespace_for_children(node, namespace) { return node.metadata.mathml ? 'mathml' : 'html'; } + +/** + * @param {'$inspect' | '$inspect().with'} rune + * @param {CallExpression} node + * @param {(node: AST.SvelteNode) => AST.SvelteNode} visit + */ +export function get_inspect_args(rune, node, visit) { + const call = + rune === '$inspect' + ? node + : /** @type {CallExpression} */ (/** @type {MemberExpression} */ (node.callee).object); + + return { + args: call.arguments.map((arg) => /** @type {Expression} */ (visit(arg))), + inspector: + rune === '$inspect' ? 'console.log' : /** @type {Expression} */ (visit(node.arguments[0])) + }; +} From 2c2891aeff3dc5ac0d863748cc5a222c9ca1b637 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 21 Oct 2025 11:46:50 -0700 Subject: [PATCH 05/11] update docs --- documentation/docs/02-runes/07-$inspect.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/documentation/docs/02-runes/07-$inspect.md b/documentation/docs/02-runes/07-$inspect.md index 13ac8b79a33a..6d47e30e2735 100644 --- a/documentation/docs/02-runes/07-$inspect.md +++ b/documentation/docs/02-runes/07-$inspect.md @@ -18,6 +18,8 @@ The `$inspect` rune is roughly equivalent to `console.log`, with the exception t ``` +On updates, a stack trace will be printed, making it easy to find the origin of a state change (unless you're in the playground, due to technical limitations). + ## $inspect(...).with `$inspect` returns a property `with`, which you can invoke with a callback, which will then be invoked instead of `console.log`. The first argument to the callback is either `"init"` or `"update"`; subsequent arguments are the values passed to `$inspect` ([demo](/playground/untitled#H4sIAAAAAAAACkVQ24qDMBD9lSEUqlTqPlsj7ON-w7pQG8c2VCchmVSK-O-bKMs-DefKYRYx6BG9qL4XQd2EohKf1opC8Nsm4F84MkbsTXAqMbVXTltuWmp5RAZlAjFIOHjuGLOP_BKVqB00eYuKs82Qn2fNjyxLtcWeyUE2sCRry3qATQIpJRyD7WPVMf9TW-7xFu53dBcoSzAOrsqQNyOe2XUKr0Xi5kcMvdDB2wSYO-I9vKazplV1-T-d6ltgNgSG1KjVUy7ZtmdbdjqtzRcphxMS1-XubOITJtPrQWMvKnYB15_1F7KKadA_AQAA)): @@ -36,13 +38,6 @@ The `$inspect` rune is roughly equivalent to `console.log`, with the exception t ``` -A convenient way to find the origin of some change is to pass `console.trace` to `with`: - -```js -// @errors: 2304 -$inspect(stuff).with(console.trace); -``` - ## $inspect.trace(...) This rune, added in 5.14, causes the surrounding function to be _traced_ in development. Any time the function re-runs as part of an [effect]($effect) or a [derived]($derived), information will be printed to the console about which pieces of reactive state caused the effect to fire. From cbcee734c6a1f3c02434d6035e904fcc43131151 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 21 Oct 2025 11:47:38 -0700 Subject: [PATCH 06/11] ugh --- packages/svelte/src/internal/client/dev/tracing.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/svelte/src/internal/client/dev/tracing.js b/packages/svelte/src/internal/client/dev/tracing.js index b44c39a631f2..3b8eeec9cccc 100644 --- a/packages/svelte/src/internal/client/dev/tracing.js +++ b/packages/svelte/src/internal/client/dev/tracing.js @@ -134,9 +134,14 @@ export function trace(label, fn) { * @returns {Error & { stack: string } | null} */ export function get_stack(label) { + // @ts-ignore stackTraceLimit doesn't exist everywhere const limit = Error.stackTraceLimit; + + // @ts-ignore Error.stackTraceLimit = Infinity; let error = Error(); + + // @ts-ignore Error.stackTraceLimit = limit; const stack = error.stack; From 723d9eb5c8bd832cb6bb38d15e3f822361585647 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 21 Oct 2025 19:54:58 -0700 Subject: [PATCH 07/11] fix the dang tests --- packages/svelte/tests/helpers.js | 16 ++++++++++++++++ .../_config.js | 2 +- .../samples/inspect-deep-array/_config.js | 3 ++- .../samples/inspect-deep/_config.js | 3 ++- .../samples/inspect-derived-2/_config.js | 8 ++++---- .../samples/inspect-derived-3/_config.js | 12 +++++------- .../samples/inspect-map-set/_config.js | 11 +++++------ .../samples/inspect-multiple/_config.js | 12 +++++++++++- .../samples/inspect-nested-effect/_config.js | 3 ++- .../samples/inspect-nested-state/_config.js | 8 ++++---- .../samples/inspect-new-property/_config.js | 10 +++++++++- .../samples/inspect-recursive-2/_config.js | 2 +- .../samples/inspect-recursive/_config.js | 9 ++++++++- .../runtime-runes/samples/inspect/_config.js | 3 ++- 14 files changed, 72 insertions(+), 30 deletions(-) diff --git a/packages/svelte/tests/helpers.js b/packages/svelte/tests/helpers.js index 7a9640636c40..1dab4e05ca38 100644 --- a/packages/svelte/tests/helpers.js +++ b/packages/svelte/tests/helpers.js @@ -197,6 +197,22 @@ export const fragments = /** @type {'html' | 'tree'} */ (process.env.FRAGMENTS) export const async_mode = process.env.SVELTE_NO_ASYNC !== 'true'; +/** + * @param {any[]} logs + */ +export function normalise_inspect_logs(logs) { + return logs.map((log) => { + if (log instanceof Error) { + const last_line = log.stack?.trim().split('\n')[1]; + const match = last_line && /(at .+) /.exec(last_line); + + return match && match[1]; + } + + return log; + }); +} + /** * @param {any[]} logs */ diff --git a/packages/svelte/tests/runtime-runes/samples/class-private-fields-reassigned-this/_config.js b/packages/svelte/tests/runtime-runes/samples/class-private-fields-reassigned-this/_config.js index 88b806c0f085..0e6bd73220e2 100644 --- a/packages/svelte/tests/runtime-runes/samples/class-private-fields-reassigned-this/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/class-private-fields-reassigned-this/_config.js @@ -5,6 +5,6 @@ export default test({ dev: true }, async test({ assert, logs }) { - assert.deepEqual(logs, ['init', 1, 'init', 1]); + assert.deepEqual(logs, [1, 1]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-deep-array/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-deep-array/_config.js index 49f1b5de4170..1b331f5b4014 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-deep-array/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-deep-array/_config.js @@ -1,5 +1,6 @@ import { flushSync } from 'svelte'; import { test } from '../../test'; +import { normalise_inspect_logs } from '../../../helpers.js'; export default test({ compileOptions: { @@ -13,6 +14,6 @@ export default test({ button?.click(); }); - assert.deepEqual(logs, ['init', [1, 2, 3, 7], 'update', [2, 3, 7]]); + assert.deepEqual(normalise_inspect_logs(logs), [[1, 2, 3, 7], [2, 3, 7], 'at Object.doSplice']); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-deep/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-deep/_config.js index f7480b0e7bbb..89b01da499fb 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-deep/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-deep/_config.js @@ -1,3 +1,4 @@ +import { normalise_inspect_logs } from '../../../helpers.js'; import { test } from '../../test'; export default test({ @@ -6,6 +7,6 @@ export default test({ }, async test({ assert, logs }) { - assert.deepEqual(logs, ['init', undefined, 'update', [{}]]); + assert.deepEqual(normalise_inspect_logs(logs), [undefined, [{}], 'at $effect']); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-derived-2/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-derived-2/_config.js index 9474397f7f45..374238275902 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-derived-2/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-derived-2/_config.js @@ -1,5 +1,6 @@ import { flushSync } from 'svelte'; import { test } from '../../test'; +import { normalise_inspect_logs } from '../../../helpers.js'; export default test({ compileOptions: { @@ -14,8 +15,7 @@ export default test({ }); assert.htmlEqual(target.innerHTML, `\n1`); - assert.deepEqual(logs, [ - 'init', + assert.deepEqual(normalise_inspect_logs(logs), [ { data: { derived: 0, @@ -23,14 +23,14 @@ export default test({ }, derived: [] }, - 'update', { data: { derived: 0, list: [1] }, derived: [1] - } + }, + 'at HTMLButtonElement.Main.button.__click' ]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-derived-3/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-derived-3/_config.js index d2226f433ea0..017de6c0c72d 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-derived-3/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-derived-3/_config.js @@ -1,5 +1,6 @@ import { flushSync } from 'svelte'; import { test } from '../../test'; +import { normalise_inspect_logs } from '../../../helpers.js'; export default test({ compileOptions: { @@ -13,22 +14,19 @@ export default test({ button?.click(); }); - assert.deepEqual(logs, [ - 'init', + assert.deepEqual(normalise_inspect_logs(logs), [ '0', true, - 'init', '1', false, - 'init', '2', false, - 'update', '0', false, - 'update', + 'at $effect', '1', - true + true, + 'at $effect' ]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-map-set/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-map-set/_config.js index 2052cb7f135d..2f91e842880d 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-map-set/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-map-set/_config.js @@ -1,5 +1,6 @@ import { flushSync } from 'svelte'; import { test } from '../../test'; +import { normalise_inspect_logs } from '../../../helpers.js'; export default test({ compileOptions: { @@ -12,15 +13,13 @@ export default test({ btn2.click(); flushSync(); - assert.deepEqual(logs, [ - 'init', + assert.deepEqual(normalise_inspect_logs(logs), [ new Map(), - 'init', new Set(), - 'update', new Map([['a', 'a']]), - 'update', - new Set(['a']) + 'at SvelteMap.set', + new Set(['a']), + 'at SvelteSet.add' ]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-multiple/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-multiple/_config.js index fc9a0cda9a74..6886f5e53e6e 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-multiple/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-multiple/_config.js @@ -1,3 +1,4 @@ +import { normalise_inspect_logs } from '../../../helpers.js'; import { test } from '../../test'; export default test({ @@ -11,6 +12,15 @@ export default test({ b2.click(); await Promise.resolve(); - assert.deepEqual(logs, ['init', 0, 0, 'update', 1, 0, 'update', 1, 1]); + assert.deepEqual(normalise_inspect_logs(logs), [ + 0, + 0, + 1, + 0, + 'at HTMLButtonElement.', + 1, + 1, + 'at HTMLButtonElement.' + ]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-nested-effect/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-nested-effect/_config.js index 82429e5e361b..86e65d50443f 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-nested-effect/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-nested-effect/_config.js @@ -1,3 +1,4 @@ +import { normalise_inspect_logs } from '../../../helpers.js'; import { test } from '../../test'; export default test({ @@ -6,6 +7,6 @@ export default test({ }, async test({ assert, logs }) { - assert.deepEqual(logs, ['init', 0, 'update', 1]); + assert.deepEqual(normalise_inspect_logs(logs), [0, 1, 'at $effect']); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-nested-state/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-nested-state/_config.js index e4d9fb501378..34cd74d780b2 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-nested-state/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-nested-state/_config.js @@ -1,3 +1,4 @@ +import { normalise_inspect_logs } from '../../../helpers.js'; import { test } from '../../test'; export default test({ @@ -10,13 +11,12 @@ export default test({ b1.click(); await Promise.resolve(); - assert.deepEqual(logs, [ - 'init', + assert.deepEqual(normalise_inspect_logs(logs), [ { x: { count: 0 } }, [{ count: 0 }], - 'update', { x: { count: 1 } }, - [{ count: 1 }] + [{ count: 1 }], + 'at HTMLButtonElement.' ]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-new-property/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-new-property/_config.js index a85972a0f990..8134044b16fc 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-new-property/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-new-property/_config.js @@ -1,3 +1,4 @@ +import { normalise_inspect_logs } from '../../../helpers.js'; import { test } from '../../test'; export default test({ @@ -10,6 +11,13 @@ export default test({ btn.click(); await Promise.resolve(); - assert.deepEqual(logs, ['init', {}, 'init', [], 'update', { x: 'hello' }, 'update', ['hello']]); + assert.deepEqual(normalise_inspect_logs(logs), [ + {}, + [], + { x: 'hello' }, + 'at HTMLButtonElement.on_click', + ['hello'], + 'at HTMLButtonElement.on_click' + ]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js index ab496971955f..1bfc2dc68fc0 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-recursive-2/_config.js @@ -18,6 +18,6 @@ export default test({ }; b.a.b = b; - assert.deepEqual(logs, ['init', a, 'init', b]); + assert.deepEqual(logs, [a, b]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-recursive/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-recursive/_config.js index e35917b1f3c5..9d95956e7d06 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-recursive/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-recursive/_config.js @@ -1,3 +1,4 @@ +import { normalise_inspect_logs } from '../../../helpers.js'; import { test } from '../../test'; export default test({ @@ -11,6 +12,12 @@ export default test({ btn.click(); await Promise.resolve(); - assert.deepEqual(logs, ['init', [], 'update', [{}], 'update', [{}, {}]]); + assert.deepEqual(normalise_inspect_logs(logs), [ + [], + [{}], + 'at HTMLButtonElement.on_click', + [{}, {}], + 'at HTMLButtonElement.on_click' + ]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect/_config.js index 09a921abee06..c05c4b15c4f1 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect/_config.js @@ -1,3 +1,4 @@ +import { normalise_inspect_logs } from '../../../helpers.js'; import { test } from '../../test'; export default test({ @@ -11,6 +12,6 @@ export default test({ b2.click(); await Promise.resolve(); - assert.deepEqual(logs, ['init', 0, 'update', 1]); + assert.deepEqual(normalise_inspect_logs(logs), [0, 1, 'at HTMLButtonElement.']); } }); From 39255689c2babdd061886f8f68014123d567d3de Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 21 Oct 2025 20:05:20 -0700 Subject: [PATCH 08/11] ugh windows is there no punch bowl you won't poop in? --- packages/svelte/tests/helpers.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/svelte/tests/helpers.js b/packages/svelte/tests/helpers.js index 1dab4e05ca38..bf708878a325 100644 --- a/packages/svelte/tests/helpers.js +++ b/packages/svelte/tests/helpers.js @@ -203,7 +203,11 @@ export const async_mode = process.env.SVELTE_NO_ASYNC !== 'true'; export function normalise_inspect_logs(logs) { return logs.map((log) => { if (log instanceof Error) { - const last_line = log.stack?.trim().split('\n')[1]; + const last_line = log.stack + ?.trim() + .split('\n') + .filter((line) => !line.includes('at Module.get_stack'))[1]; + const match = last_line && /(at .+) /.exec(last_line); return match && match[1]; From 3d4f3e1353ddf51dfa4d7819551c0189fb1a5589 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 21 Oct 2025 20:14:59 -0700 Subject: [PATCH 09/11] argh --- packages/svelte/tests/helpers.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/svelte/tests/helpers.js b/packages/svelte/tests/helpers.js index bf708878a325..f2e81532f964 100644 --- a/packages/svelte/tests/helpers.js +++ b/packages/svelte/tests/helpers.js @@ -210,6 +210,9 @@ export function normalise_inspect_logs(logs) { const match = last_line && /(at .+) /.exec(last_line); + // fucking windows jfc + console.error({ stack: log.stack, last_line, match }); + return match && match[1]; } From 271d7f2a4e14127a72a30d981cd727e367e6507c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 21 Oct 2025 20:25:15 -0700 Subject: [PATCH 10/11] how about this --- packages/svelte/src/internal/client/dev/tracing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/dev/tracing.js b/packages/svelte/src/internal/client/dev/tracing.js index 3b8eeec9cccc..95baefc64aa3 100644 --- a/packages/svelte/src/internal/client/dev/tracing.js +++ b/packages/svelte/src/internal/client/dev/tracing.js @@ -160,7 +160,7 @@ export function get_stack(label) { if (line.includes('validate_each_keys')) { return null; } - if (line.includes('svelte/src/internal')) { + if (line.includes('svelte/src/internal') || line.includes('svelte\\src\\internal')) { continue; } new_lines.push(line); From e6242e68fff1d689fa1e58e136bfe0c1e1d5cdb7 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 21 Oct 2025 20:29:23 -0700 Subject: [PATCH 11/11] alright finally --- packages/svelte/tests/helpers.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/svelte/tests/helpers.js b/packages/svelte/tests/helpers.js index f2e81532f964..bf708878a325 100644 --- a/packages/svelte/tests/helpers.js +++ b/packages/svelte/tests/helpers.js @@ -210,9 +210,6 @@ export function normalise_inspect_logs(logs) { const match = last_line && /(at .+) /.exec(last_line); - // fucking windows jfc - console.error({ stack: log.stack, last_line, match }); - return match && match[1]; }