Skip to content

feat: add source name logging to $inspect.trace #16060

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 55 commits into from
Jun 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
26173de
init
Ocean-OS Jun 1, 2025
45caf0b
improve symbol logging
Ocean-OS Jun 1, 2025
fdfc6ac
doh
Ocean-OS Jun 1, 2025
e62c167
remove proxy path name when reassigned to a source
Ocean-OS Jun 1, 2025
2f189ec
try this
Ocean-OS Jun 1, 2025
db9b0b5
fix
Ocean-OS Jun 1, 2025
4d5a28f
oops
Ocean-OS Jun 1, 2025
58820a6
fix
Ocean-OS Jun 1, 2025
61116b4
Merge branch 'main' into $inspect.trace-source-tagging
Ocean-OS Jun 1, 2025
07952e1
"unown" proxy when in another source declaration
Ocean-OS Jun 1, 2025
fe2072e
fix
Ocean-OS Jun 1, 2025
6872c19
tag proxy version
Ocean-OS Jun 1, 2025
7a3a349
proxy bindable props
Ocean-OS Jun 1, 2025
ce2d1b3
tag iterables used in destructuring
Ocean-OS Jun 1, 2025
115384c
add changeset, fix failing tests
Ocean-OS Jun 2, 2025
3bbabaf
add comments, minor tweak
Ocean-OS Jun 2, 2025
039d2c8
lint
Ocean-OS Jun 2, 2025
896dfc9
somehow forgot to add support for class fields
Ocean-OS Jun 2, 2025
245283d
more class fields
Ocean-OS Jun 2, 2025
d4ac394
tag_source -> tag, since it applies to deriveds as well
Rich-Harris Jun 2, 2025
e7a001a
private class fields
Rich-Harris Jun 2, 2025
3f402c6
this condition is impossible
Rich-Harris Jun 2, 2025
2add7b3
explicit type narrowing lets us avoid coercion
Rich-Harris Jun 2, 2025
dc46df7
simplify
Rich-Harris Jun 2, 2025
ca3330c
unused
Rich-Harris Jun 2, 2025
520d3fc
tweak
Rich-Harris Jun 2, 2025
209d147
oops, never meant to commit that
Rich-Harris Jun 2, 2025
88c50e5
minor tweaks
Ocean-OS Jun 2, 2025
437e663
fix private field tagging, only get `declaration` once
Ocean-OS Jun 2, 2025
dd4b8b9
fix state declarations in constructors
Ocean-OS Jun 3, 2025
0901a1b
fix
Ocean-OS Jun 3, 2025
e22a60f
tag `svelte/reactivity`, `svelte/motion` sources in DEV
Ocean-OS Jun 10, 2025
90e52ce
try fixing lint
Ocean-OS Jun 10, 2025
d03b111
fix intellisense formatting
Ocean-OS Jun 10, 2025
3480a35
actually fix lint
Ocean-OS Jun 10, 2025
c6d0db4
Merge branch 'main' into $inspect.trace-source-tagging
Ocean-OS Jun 10, 2025
8a416a8
replace tag_if_necessary with conditional tagging
Rich-Harris Jun 10, 2025
3d161ee
avoid [[object Object]] in labels
Rich-Harris Jun 10, 2025
75c448c
remove PROXY_REMOVE_PATH
Rich-Harris Jun 10, 2025
4e7f261
simplify a bit
Rich-Harris Jun 10, 2025
a7a08d5
simplify
Rich-Harris Jun 10, 2025
35cbd04
tweak
Rich-Harris Jun 10, 2025
9307ad5
tweak implementation
Rich-Harris Jun 10, 2025
016f02b
tweak implementation
Rich-Harris Jun 10, 2025
7f4debe
tweak implementation
Rich-Harris Jun 10, 2025
1d64512
hoist
Rich-Harris Jun 10, 2025
4e6e8a4
tweak
Rich-Harris Jun 10, 2025
d2487f8
fix
Rich-Harris Jun 10, 2025
b1f761c
WIP (reduce number of with_parent calls, move towards possibility of …
Rich-Harris Jun 10, 2025
4105d05
DRY out
Rich-Harris Jun 10, 2025
94d0b08
tweak labels
Rich-Harris Jun 10, 2025
197cfbe
remove PROXY_REMOVE_PATH (#16126)
Rich-Harris Jun 10, 2025
1a78177
merge
Rich-Harris Jun 10, 2025
798a495
come on this was just lazy
Rich-Harris Jun 10, 2025
4047913
fix tests
Rich-Harris Jun 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/big-teachers-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': minor
---

feat: add source name logging to `$inspect.trace`
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,20 @@ function build_assignment(operator, left, right, context) {
in_constructor: rune !== '$derived' && rune !== '$derived.by'
};

return b.assignment(
operator,
b.member(b.this, field.key),
/** @type {Expression} */ (context.visit(right, child_state))
);
let value = /** @type {Expression} */ (context.visit(right, child_state));

if (dev) {
const declaration = context.path.findLast(
(parent) => parent.type === 'ClassDeclaration' || parent.type === 'ClassExpression'
);
value = b.call(
'$.tag',
value,
b.literal(`${declaration?.id?.name ?? '[class]'}.${name}`)
);
}

return b.assignment(operator, b.member(b.this, field.key), value);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/** @import { CallExpression, ClassBody, MethodDefinition, PropertyDefinition, StaticBlock } from 'estree' */
/** @import { CallExpression, ClassBody, ClassDeclaration, ClassExpression, MethodDefinition, PropertyDefinition, StaticBlock } from 'estree' */
/** @import { StateField } from '#compiler' */
/** @import { Context } from '../types' */
import * as b from '#compiler/builders';
import { dev } from '../../../../state.js';
import { get_parent } from '../../../../utils/ast.js';
import { get_name } from '../../../nodes.js';

/**
Expand Down Expand Up @@ -50,6 +52,10 @@ export function ClassBody(node, context) {
}
}

const declaration = /** @type {ClassDeclaration | ClassExpression} */ (
get_parent(context.path, -1)
);

// Replace parts of the class body
for (const definition of node.body) {
if (definition.type !== 'PropertyDefinition') {
Expand All @@ -68,17 +74,26 @@ export function ClassBody(node, context) {
}

if (name[0] === '#') {
body.push(/** @type {PropertyDefinition} */ (context.visit(definition, child_state)));
let value = definition.value
? /** @type {CallExpression} */ (context.visit(definition.value, child_state))
: undefined;

if (dev) {
value = b.call('$.tag', value, b.literal(`${declaration.id?.name ?? '[class]'}.${name}`));
}

body.push(b.prop_def(definition.key, value));
} else if (field.node === definition) {
const member = b.member(b.this, field.key);
let call = /** @type {CallExpression} */ (context.visit(field.value, child_state));

if (dev) {
call = b.call('$.tag', call, b.literal(`${declaration.id?.name ?? '[class]'}.${name}`));
}
const member = b.member(b.this, field.key);
const should_proxy = field.type === '$state' && true; // TODO

body.push(
b.prop_def(
field.key,
/** @type {CallExpression} */ (context.visit(field.value, child_state))
),
b.prop_def(field.key, call),

b.method('get', definition.key, [], [b.return(b.call('$.get', member))]),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ export function VariableDeclaration(node, context) {
should_proxy(initial, context.state.scope)
) {
initial = b.call('$.proxy', initial);

if (dev) {
initial = b.call('$.tag_proxy', initial, b.literal(id.name));
}
}

if (is_prop_source(binding, context.state)) {
Expand Down Expand Up @@ -128,12 +132,25 @@ export function VariableDeclaration(node, context) {
const binding = /** @type {import('#compiler').Binding} */ (
context.state.scope.get(id.name)
);
if (rune === '$state' && should_proxy(value, context.state.scope)) {
const is_state = is_state_source(binding, context.state.analysis);
const is_proxy = should_proxy(value, context.state.scope);

if (rune === '$state' && is_proxy) {
value = b.call('$.proxy', value);

if (dev && !is_state) {
value = b.call('$.tag_proxy', value, b.literal(id.name));
}
}
if (is_state_source(binding, context.state.analysis)) {

if (is_state) {
value = b.call('$.state', value);

if (dev) {
value = b.call('$.tag', value, b.literal(id.name));
}
}

return value;
};

Expand All @@ -154,7 +171,11 @@ export function VariableDeclaration(node, context) {
context.state.transform[id.name] = { read: get_value };

const expression = /** @type {Expression} */ (context.visit(b.thunk(value)));
return b.declarator(id, b.call('$.derived', expression));
const call = b.call('$.derived', expression);
return b.declarator(
id,
dev ? b.call('$.tag', call, b.literal('[$state iterable]')) : call
);
}),
...paths.map((path) => {
const value = /** @type {Expression} */ (context.visit(path.expression));
Expand All @@ -176,8 +197,13 @@ export function VariableDeclaration(node, context) {
if (declarator.id.type === 'Identifier') {
let expression = /** @type {Expression} */ (context.visit(value));
if (rune === '$derived') expression = b.thunk(expression);

declarations.push(b.declarator(declarator.id, b.call('$.derived', expression)));
const call = b.call('$.derived', expression);
declarations.push(
b.declarator(
declarator.id,
dev ? b.call('$.tag', call, b.literal(declarator.id.name)) : call
)
);
} else {
const init = /** @type {CallExpression} */ (declarator.init);

Expand All @@ -189,8 +215,10 @@ export function VariableDeclaration(node, context) {

let expression = /** @type {Expression} */ (context.visit(value));
if (rune === '$derived') expression = b.thunk(expression);

declarations.push(b.declarator(id, b.call('$.derived', expression)));
const call = b.call('$.derived', expression);
declarations.push(
b.declarator(id, dev ? b.call('$.tag', call, b.literal('[$derived iterable]')) : call)
);
}

const { inserts, paths } = extract_paths(declarator.id, rhs);
Expand All @@ -200,12 +228,23 @@ export function VariableDeclaration(node, context) {
context.state.transform[id.name] = { read: get_value };

const expression = /** @type {Expression} */ (context.visit(b.thunk(value)));
declarations.push(b.declarator(id, b.call('$.derived', expression)));
const call = b.call('$.derived', expression);
declarations.push(
b.declarator(id, dev ? b.call('$.tag', call, b.literal('[$derived iterable]')) : call)
);
}

for (const path of paths) {
const expression = /** @type {Expression} */ (context.visit(path.expression));
declarations.push(b.declarator(path.node, b.call('$.derived', b.thunk(expression))));
const call = b.call('$.derived', b.thunk(expression));
declarations.push(
b.declarator(
path.node,
dev
? b.call('$.tag', call, b.literal(/** @type {Identifier} */ (path.node).name))
: call
)
);
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/svelte/src/internal/client/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ export const EFFECT_IS_UPDATING = 1 << 21;
export const STATE_SYMBOL = Symbol('$state');
export const LEGACY_PROPS = Symbol('legacy props');
export const LOADING_ATTR_SYMBOL = Symbol('');
export const PROXY_PATH_SYMBOL = Symbol('proxy path');
41 changes: 38 additions & 3 deletions packages/svelte/src/internal/client/dev/tracing.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { UNINITIALIZED } from '../../../constants.js';
import { snapshot } from '../../shared/clone.js';
import { define_property } from '../../shared/utils.js';
import { DERIVED, STATE_SYMBOL } from '#client/constants';
import { DERIVED, PROXY_PATH_SYMBOL, STATE_SYMBOL } from '#client/constants';
import { effect_tracking } from '../reactivity/effects.js';
import { active_reaction, captured_signals, set_captured_signals, untrack } from '../runtime.js';

Expand Down Expand Up @@ -43,11 +43,15 @@ function log_entry(signal, entry) {
const type = (signal.f & DERIVED) !== 0 ? '$derived' : '$state';
const current_reaction = /** @type {Reaction} */ (active_reaction);
const dirty = signal.wv > current_reaction.wv || current_reaction.wv === 0;
const style = dirty
? 'color: CornflowerBlue; font-weight: bold'
: 'color: grey; font-weight: normal';

// eslint-disable-next-line no-console
console.groupCollapsed(
`%c${type}`,
dirty ? 'color: CornflowerBlue; font-weight: bold' : 'color: grey; font-weight: bold',
signal.label ? `%c${type}%c ${signal.label}` : `%c${type}%c`,
style,
dirty ? 'font-weight: normal' : style,
typeof value === 'object' && value !== null && STATE_SYMBOL in value
? snapshot(value, true)
: value
Expand Down Expand Up @@ -177,3 +181,34 @@ export function get_stack(label) {
}
return error;
}

/**
* @param {Value} source
* @param {string} label
*/
export function tag(source, label) {
source.label = label;
tag_proxy(source.v, label);

return source;
}

/**
* @param {unknown} value
* @param {string} label
*/
export function tag_proxy(value, label) {
// @ts-expect-error
value?.[PROXY_PATH_SYMBOL]?.(label);
return value;
}

/**
* @param {unknown} value
*/
export function label(value) {
if (typeof value === 'symbol') return `Symbol(${value.description})`;
if (typeof value === 'function') return '<function>';
if (typeof value === 'object' && value) return '<object>';
return String(value);
}
2 changes: 1 addition & 1 deletion packages/svelte/src/internal/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export { add_locations } from './dev/elements.js';
export { hmr } from './dev/hmr.js';
export { create_ownership_validator } from './dev/ownership.js';
export { check_target, legacy_api } from './dev/legacy.js';
export { trace } from './dev/tracing.js';
export { trace, tag, tag_proxy } from './dev/tracing.js';
export { inspect } from './dev/inspect.js';
export { validate_snippet_args } from './dev/validation.js';
export { await_block as await } from './dom/blocks/await.js';
Expand Down
Loading