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

* init

* improve symbol logging

* doh

* remove proxy path name when reassigned to a source

* try this

* fix

* oops

* fix

* "unown" proxy when in another source declaration

* fix

* tag proxy version

* proxy bindable props

* tag iterables used in destructuring

* add changeset, fix failing tests

* add comments, minor tweak

* lint

* somehow forgot to add support for class fields

* more class fields

* tag_source -> tag, since it applies to deriveds as well

* private class fields

* this condition is impossible

* explicit type narrowing lets us avoid coercion

* simplify

* unused

* tweak

* oops, never meant to commit that

* minor tweaks

* fix private field tagging, only get `declaration` once

* fix state declarations in constructors

* fix

* tag `svelte/reactivity`, `svelte/motion` sources in DEV

* try fixing lint

* fix intellisense formatting

* actually fix lint

* replace tag_if_necessary with conditional tagging

* avoid [[object Object]] in labels

* remove PROXY_REMOVE_PATH

* simplify a bit

* simplify

* tweak

* tweak implementation

* tweak implementation

* tweak implementation

* hoist

* tweak

* fix

* WIP (reduce number of with_parent calls, move towards possibility of combining tag and tag_proxy)

* DRY out

* tweak labels

* remove PROXY_REMOVE_PATH (#16126)

* remove PROXY_REMOVE_PATH

* simplify a bit

* simplify

* tweak

* tweak implementation

* tweak implementation

* tweak implementation

* hoist

* tweak

* fix

* WIP (reduce number of with_parent calls, move towards possibility of combining tag and tag_proxy)

* DRY out

* come on this was just lazy

* fix tests

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/16145/head
ComputerGuy 3 months ago committed by GitHub
parent 17629a60db
commit 91272d702b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': minor
---
feat: add source name logging to `$inspect.trace`

@ -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);
}
}

@ -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';
/**
@ -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') {
@ -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))]),

@ -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)) {
@ -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;
};
@ -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));
@ -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);
@ -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);
@ -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
)
);
}
}

@ -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');

@ -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';
@ -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
@ -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);
}

@ -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';

@ -9,12 +9,15 @@ import {
object_prototype
} from '../shared/utils.js';
import { state as source, set } from './reactivity/sources.js';
import { STATE_SYMBOL } from '#client/constants';
import { PROXY_PATH_SYMBOL, STATE_SYMBOL } from '#client/constants';
import { UNINITIALIZED } from '../../constants.js';
import * as e from './errors.js';
import { get_stack } from './dev/tracing.js';
import { get_stack, tag } from './dev/tracing.js';
import { tracing_mode_flag } from '../flags/index.js';
// TODO move all regexes into shared module?
const regex_is_valid_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
/**
* @template T
* @param {T} value
@ -61,6 +64,21 @@ export function proxy(value) {
sources.set('length', source(/** @type {any[]} */ (value).length, stack));
}
/** Used in dev for $inspect.trace() */
var path = '';
/** @param {string} new_path */
function update_path(new_path) {
path = new_path;
tag(version, `${path} version`);
// rename all child sources and child proxies
for (const [prop, source] of sources) {
tag(source, get_label(path, prop));
}
}
return new Proxy(/** @type {any} */ (value), {
defineProperty(_, prop, descriptor) {
if (
@ -76,17 +94,20 @@ export function proxy(value) {
e.state_descriptors_fixed();
}
var s = sources.get(prop);
with_parent(() => {
var s = sources.get(prop);
if (s === undefined) {
s = with_parent(() => source(descriptor.value, stack));
sources.set(prop, s);
} else {
set(
s,
with_parent(() => proxy(descriptor.value))
);
}
if (s === undefined) {
s = source(descriptor.value, stack);
sources.set(prop, s);
if (DEV && typeof prop === 'string') {
tag(s, get_label(path, prop));
}
} else {
set(s, descriptor.value, true);
}
});
return true;
},
@ -96,11 +117,13 @@ export function proxy(value) {
if (s === undefined) {
if (prop in target) {
sources.set(
prop,
with_parent(() => source(UNINITIALIZED, stack))
);
const s = with_parent(() => source(UNINITIALIZED, stack));
sources.set(prop, s);
update_version(version);
if (DEV) {
tag(s, get_label(path, prop));
}
}
} else {
// When working with arrays, we need to also ensure we update the length when removing
@ -125,12 +148,26 @@ export function proxy(value) {
return value;
}
if (DEV && prop === PROXY_PATH_SYMBOL) {
return update_path;
}
var s = sources.get(prop);
var exists = prop in target;
// create a source, but only if it's an own property and not a prototype property
if (s === undefined && (!exists || get_descriptor(target, prop)?.writable)) {
s = with_parent(() => source(proxy(exists ? target[prop] : UNINITIALIZED), stack));
s = with_parent(() => {
var p = proxy(exists ? target[prop] : UNINITIALIZED);
var s = source(p, stack);
if (DEV) {
tag(s, get_label(path, prop));
}
return s;
});
sources.set(prop, s);
}
@ -178,7 +215,17 @@ export function proxy(value) {
(active_effect !== null && (!has || get_descriptor(target, prop)?.writable))
) {
if (s === undefined) {
s = with_parent(() => source(has ? proxy(target[prop]) : UNINITIALIZED, stack));
s = with_parent(() => {
var p = has ? proxy(target[prop]) : UNINITIALIZED;
var s = source(p, stack);
if (DEV) {
tag(s, get_label(path, prop));
}
return s;
});
sources.set(prop, s);
}
@ -207,6 +254,10 @@ export function proxy(value) {
// the value of the original item at that index.
other_s = with_parent(() => source(UNINITIALIZED, stack));
sources.set(i + '', other_s);
if (DEV) {
tag(other_s, get_label(path, i));
}
}
}
}
@ -217,19 +268,23 @@ export function proxy(value) {
// object property before writing to that property.
if (s === undefined) {
if (!has || get_descriptor(target, prop)?.writable) {
s = with_parent(() => source(undefined, stack));
set(
s,
with_parent(() => proxy(value))
);
s = with_parent(() => {
var s = source(undefined, stack);
set(s, proxy(value));
return s;
});
sources.set(prop, s);
if (DEV) {
tag(s, get_label(path, prop));
}
}
} else {
has = s.v !== UNINITIALIZED;
set(
s,
with_parent(() => proxy(value))
);
var p = with_parent(() => proxy(value));
set(s, p);
}
var descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
@ -282,6 +337,16 @@ export function proxy(value) {
});
}
/**
* @param {string} path
* @param {string | symbol} prop
*/
function get_label(path, prop) {
if (typeof prop === 'symbol') return `${path}[Symbol(${prop.description ?? ''})]`;
if (regex_is_valid_identifier.test(prop)) return `${path}.${prop}`;
return /^\d+$/.test(prop) ? `${path}[${prop}]` : `${path}['${prop}']`;
}
/**
* @param {Source<number>} signal
* @param {1 | -1} [d]

@ -31,7 +31,7 @@ import {
} from '#client/constants';
import * as e from '../errors.js';
import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js';
import { get_stack } from '../dev/tracing.js';
import { get_stack, tag_proxy } from '../dev/tracing.js';
import { component_context, is_runes } from '../context.js';
import { proxy } from '../proxy.js';
import { execute_derived } from './deriveds.js';
@ -141,6 +141,10 @@ export function set(source, value, should_proxy = false) {
let new_value = should_proxy ? proxy(value) : value;
if (DEV) {
tag_proxy(new_value, /** @type {string} */ (source.label));
}
return internal_set(source, new_value);
}

@ -21,6 +21,7 @@ export interface Value<V = unknown> extends Signal {
updated?: Error | null;
trace_need_increase?: boolean;
trace_v?: V;
label?: string;
debug?: null | (() => void);
}

@ -7,8 +7,10 @@ import { raf } from '../internal/client/timing.js';
import { is_date } from './utils.js';
import { set, source } from '../internal/client/reactivity/sources.js';
import { render_effect } from '../internal/client/reactivity/effects.js';
import { tag } from '../internal/client/dev/tracing.js';
import { get } from '../internal/client/runtime.js';
import { deferred, noop } from '../internal/shared/utils.js';
import { DEV } from 'esm-env';
/**
* @template T
@ -172,8 +174,8 @@ export class Spring {
#damping = source(0.8);
#precision = source(0.01);
#current = source(/** @type {T} */ (undefined));
#target = source(/** @type {T} */ (undefined));
#current;
#target;
#last_value = /** @type {T} */ (undefined);
#last_time = 0;
@ -192,11 +194,20 @@ export class Spring {
* @param {SpringOpts} [options]
*/
constructor(value, options = {}) {
this.#current.v = this.#target.v = value;
this.#current = DEV ? tag(source(value), 'Spring.current') : source(value);
this.#target = DEV ? tag(source(value), 'Spring.target') : source(value);
if (typeof options.stiffness === 'number') this.#stiffness.v = clamp(options.stiffness, 0, 1);
if (typeof options.damping === 'number') this.#damping.v = clamp(options.damping, 0, 1);
if (typeof options.precision === 'number') this.#precision.v = options.precision;
if (DEV) {
tag(this.#stiffness, 'Spring.stiffness');
tag(this.#damping, 'Spring.damping');
tag(this.#precision, 'Spring.precision');
tag(this.#current, 'Spring.current');
tag(this.#target, 'Spring.target');
}
}
/**

@ -7,7 +7,9 @@ import { loop } from '../internal/client/loop.js';
import { linear } from '../easing/index.js';
import { is_date } from './utils.js';
import { set, source } from '../internal/client/reactivity/sources.js';
import { tag } from '../internal/client/dev/tracing.js';
import { get, render_effect } from 'svelte/internal/client';
import { DEV } from 'esm-env';
/**
* @template T
@ -175,8 +177,8 @@ export function tweened(value, defaults = {}) {
* @since 5.8.0
*/
export class Tween {
#current = source(/** @type {T} */ (undefined));
#target = source(/** @type {T} */ (undefined));
#current;
#target;
/** @type {TweenedOptions<T>} */
#defaults;
@ -189,8 +191,14 @@ export class Tween {
* @param {TweenedOptions<T>} options
*/
constructor(value, options = {}) {
this.#current.v = this.#target.v = value;
this.#current = source(value);
this.#target = source(value);
this.#defaults = options;
if (DEV) {
tag(this.#current, 'Tween.current');
tag(this.#target, 'Tween.target');
}
}
/**

@ -1,7 +1,9 @@
import { get, tick, untrack } from '../internal/client/runtime.js';
import { effect_tracking, render_effect } from '../internal/client/reactivity/effects.js';
import { source } from '../internal/client/reactivity/sources.js';
import { tag } from '../internal/client/dev/tracing.js';
import { increment } from './utils.js';
import { DEV } from 'esm-env';
/**
* Returns a `subscribe` function that, if called in an effect (including expressions in the template),
@ -51,6 +53,10 @@ export function createSubscriber(start) {
/** @type {(() => void) | void} */
let stop;
if (DEV) {
tag(version, 'createSubscriber version');
}
return () => {
if (effect_tracking()) {
get(version);

@ -1,7 +1,9 @@
/** @import { Source } from '#client' */
import { derived } from '../internal/client/index.js';
import { source, set } from '../internal/client/reactivity/sources.js';
import { tag } from '../internal/client/dev/tracing.js';
import { active_reaction, get, set_active_reaction } from '../internal/client/runtime.js';
import { DEV } from 'esm-env';
var inited = false;
@ -49,6 +51,11 @@ export class SvelteDate extends Date {
constructor(...params) {
// @ts-ignore
super(...params);
if (DEV) {
tag(this.#time, 'SvelteDate.#time');
}
if (!inited) this.#init();
}

@ -1,6 +1,7 @@
/** @import { Source } from '#client' */
import { DEV } from 'esm-env';
import { set, source } from '../internal/client/reactivity/sources.js';
import { label, tag } from '../internal/client/dev/tracing.js';
import { get } from '../internal/client/runtime.js';
import { increment } from './utils.js';
@ -62,8 +63,13 @@ export class SvelteMap extends Map {
constructor(value) {
super();
// If the value is invalid then the native exception will fire here
if (DEV) value = new Map(value);
if (DEV) {
// If the value is invalid then the native exception will fire here
value = new Map(value);
tag(this.#version, 'SvelteMap version');
tag(this.#size, 'SvelteMap.size');
}
if (value) {
for (var [key, v] of value) {
@ -82,6 +88,11 @@ export class SvelteMap extends Map {
var ret = super.get(key);
if (ret !== undefined) {
s = source(0);
if (DEV) {
tag(s, `SvelteMap get(${label(key)})`);
}
sources.set(key, s);
} else {
// We should always track the version in case
@ -113,6 +124,11 @@ export class SvelteMap extends Map {
var ret = super.get(key);
if (ret !== undefined) {
s = source(0);
if (DEV) {
tag(s, `SvelteMap get(${label(key)})`);
}
sources.set(key, s);
} else {
// We should always track the version in case
@ -138,7 +154,13 @@ export class SvelteMap extends Map {
var version = this.#version;
if (s === undefined) {
sources.set(key, source(0));
s = source(0);
if (DEV) {
tag(s, `SvelteMap get(${label(key)})`);
}
sources.set(key, s);
set(this.#size, super.size);
increment(version);
} else if (prev_res !== value) {
@ -197,12 +219,18 @@ export class SvelteMap extends Map {
if (this.#size.v !== sources.size) {
for (var key of super.keys()) {
if (!sources.has(key)) {
sources.set(key, source(0));
var s = source(0);
if (DEV) {
tag(s, `SvelteMap get(${label(key)})`);
}
sources.set(key, s);
}
}
}
for (var [, s] of this.#sources) {
for ([, s] of this.#sources) {
get(s);
}
}

@ -1,6 +1,7 @@
/** @import { Source } from '#client' */
import { DEV } from 'esm-env';
import { source, set } from '../internal/client/reactivity/sources.js';
import { label, tag } from '../internal/client/dev/tracing.js';
import { get } from '../internal/client/runtime.js';
import { increment } from './utils.js';
@ -56,8 +57,13 @@ export class SvelteSet extends Set {
constructor(value) {
super();
// If the value is invalid then the native exception will fire here
if (DEV) value = new Set(value);
if (DEV) {
// If the value is invalid then the native exception will fire here
value = new Set(value);
tag(this.#version, 'SvelteSet version');
tag(this.#size, 'SvelteSet.size');
}
if (value) {
for (var element of value) {
@ -111,6 +117,11 @@ export class SvelteSet extends Set {
}
s = source(true);
if (DEV) {
tag(s, `SvelteSet has(${label(value)})`);
}
sources.set(value, s);
}

@ -1,4 +1,6 @@
import { DEV } from 'esm-env';
import { source } from '../internal/client/reactivity/sources.js';
import { tag } from '../internal/client/dev/tracing.js';
import { get } from '../internal/client/runtime.js';
import { get_current_url } from './url.js';
import { increment } from './utils.js';
@ -32,7 +34,7 @@ export const REPLACE = Symbol();
* ```
*/
export class SvelteURLSearchParams extends URLSearchParams {
#version = source(0);
#version = DEV ? tag(source(0), 'SvelteURLSearchParams version') : source(0);
#url = get_current_url();
#updating = false;

@ -1,4 +1,6 @@
import { DEV } from 'esm-env';
import { source, set } from '../internal/client/reactivity/sources.js';
import { tag } from '../internal/client/dev/tracing.js';
import { get } from '../internal/client/runtime.js';
import { REPLACE, SvelteURLSearchParams } from './url-search-params.js';
@ -56,6 +58,17 @@ export class SvelteURL extends URL {
url = new URL(url, base);
super(url);
if (DEV) {
tag(this.#protocol, 'SvelteURL.protocol');
tag(this.#username, 'SvelteURL.username');
tag(this.#password, 'SvelteURL.password');
tag(this.#hostname, 'SvelteURL.hostname');
tag(this.#port, 'SvelteURL.port');
tag(this.#pathname, 'SvelteURL.pathname');
tag(this.#hash, 'SvelteURL.hash');
tag(this.#search, 'SvelteURL.search');
}
current_url = this;
this.#searchParams = new SvelteURLSearchParams(url.searchParams);
current_url = null;

@ -1,8 +1,9 @@
import { BROWSER } from 'esm-env';
import { BROWSER, DEV } from 'esm-env';
import { on } from '../../events/index.js';
import { ReactiveValue } from '../reactive-value.js';
import { get } from '../../internal/client/index.js';
import { set, source } from '../../internal/client/reactivity/sources.js';
import { tag } from '../../internal/client/dev/tracing.js';
/**
* `scrollX.current` is a reactive view of `window.scrollX`. On the server it is `undefined`.
@ -147,6 +148,10 @@ export const devicePixelRatio = /* @__PURE__ */ new (class DevicePixelRatio {
if (BROWSER) {
this.#update();
}
if (DEV) {
tag(this.#dpr, 'window.devicePixelRatio');
}
}
get current() {

@ -192,3 +192,44 @@ if (typeof window !== 'undefined') {
}
export const fragments = /** @type {'html' | 'tree'} */ (process.env.FRAGMENTS) ?? 'html';
/**
* @param {any[]} logs
*/
export function normalise_trace_logs(logs) {
let normalised = [];
logs = logs.slice();
while (logs.length > 0) {
const log = logs.shift();
if (log instanceof Error) {
continue;
}
if (typeof log === 'string' && log.includes('%c')) {
const split = log.split('%c');
const first = /** @type {string} */ (split.shift()).trim();
if (first) normalised.push({ log: first });
while (split.length > 0) {
const log = /** @type {string} */ (split.shift()).trim();
const highlighted = logs.shift() === 'color: CornflowerBlue; font-weight: bold';
// omit timings, as they will differ between runs
if (/\(.+ms\)/.test(log)) continue;
normalised.push({
log,
highlighted
});
}
} else {
normalised.push({ log });
}
}
return normalised;
}

@ -1,29 +1,6 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
/**
* @param {any[]} logs
*/
function normalise_trace_logs(logs) {
let normalised = [];
for (let i = 0; i < logs.length; i++) {
const log = logs[i];
if (typeof log === 'string' && log.includes('%c')) {
const split = log.split('%c');
normalised.push({
log: (split[0].length !== 0 ? split[0] : split[1]).trim(),
highlighted: logs[i + 1] === 'color: CornflowerBlue; font-weight: bold'
});
i++;
} else if (log instanceof Error) {
continue;
} else {
normalised.push({ log });
}
}
return normalised;
}
import { normalise_trace_logs } from '../../../helpers.js';
export default test({
compileOptions: {
@ -34,10 +11,11 @@ export default test({
// initial log, everything is highlighted
assert.deepEqual(normalise_trace_logs(logs), [
{ log: 'iife', highlighted: false },
{ log: 'iife' },
{ log: '$state', highlighted: true },
{ log: 'count', highlighted: false },
{ log: 0 },
{ log: 'effect', highlighted: false }
{ log: 'effect' }
]);
logs.length = 0;
@ -47,10 +25,11 @@ export default test({
flushSync();
assert.deepEqual(normalise_trace_logs(logs), [
{ log: 'iife', highlighted: false },
{ log: 'iife' },
{ log: '$state', highlighted: true },
{ log: 'count', highlighted: false },
{ log: 1 },
{ log: 'effect', highlighted: false }
{ log: 'effect' }
]);
}
});

@ -1,29 +1,6 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
/**
* @param {any[]} logs
*/
function normalise_trace_logs(logs) {
let normalised = [];
for (let i = 0; i < logs.length; i++) {
const log = logs[i];
if (typeof log === 'string' && log.includes('%c')) {
const split = log.split('%c');
normalised.push({
log: (split[0].length !== 0 ? split[0] : split[1]).trim(),
highlighted: logs[i + 1] === 'color: CornflowerBlue; font-weight: bold'
});
i++;
} else if (log instanceof Error) {
continue;
} else {
normalised.push({ log });
}
}
return normalised;
}
import { normalise_trace_logs } from '../../../helpers.js';
export default test({
compileOptions: {
@ -34,8 +11,9 @@ export default test({
// initial log, everything is highlighted
assert.deepEqual(normalise_trace_logs(logs), [
{ log: 'effect', highlighted: false },
{ log: 'effect' },
{ log: '$state', highlighted: true },
{ log: 'checked', highlighted: false },
{ log: false }
]);
@ -52,20 +30,26 @@ export default test({
// checked changed, effect reassign state, values should be correct and be correctly highlighted
assert.deepEqual(normalise_trace_logs(logs), [
{ log: 'effect', highlighted: false },
{ log: 'effect' },
{ log: '$state', highlighted: true },
{ log: 'checked', highlighted: false },
{ log: true },
{ log: '$state', highlighted: true },
{ log: 'count', highlighted: false },
{ log: 1 },
{ log: 'effect', highlighted: false },
{ log: 'effect' },
{ log: '$state', highlighted: false },
{ log: 'checked', highlighted: false },
{ log: true },
{ log: '$state', highlighted: true },
{ log: 'count', highlighted: false },
{ log: 2 },
{ log: 'effect', highlighted: false },
{ log: 'effect' },
{ log: '$state', highlighted: false },
{ log: 'checked', highlighted: false },
{ log: true },
{ log: '$state', highlighted: true },
{ log: 'count', highlighted: false },
{ log: 3 }
]);
}

@ -1,29 +1,6 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
/**
* @param {any[]} logs
*/
function normalise_trace_logs(logs) {
let normalised = [];
for (let i = 0; i < logs.length; i++) {
const log = logs[i];
if (typeof log === 'string' && log.includes('%c')) {
const split = log.split('%c');
normalised.push({
log: (split[0].length !== 0 ? split[0] : split[1]).trim(),
highlighted: logs[i + 1] === 'color: CornflowerBlue; font-weight: bold'
});
i++;
} else if (log instanceof Error) {
continue;
} else {
normalised.push({ log });
}
}
return normalised;
}
import { normalise_trace_logs } from '../../../helpers.js';
export default test({
compileOptions: {
@ -34,12 +11,15 @@ export default test({
// initial log, everything is highlighted
assert.deepEqual(normalise_trace_logs(logs), [
{ log: 'effect', highlighted: false },
{ log: 'effect' },
{ log: '$derived', highlighted: true },
{ log: 'double', highlighted: false },
{ log: 0 },
{ log: '$state', highlighted: true },
{ log: 'count', highlighted: false },
{ log: 0 },
{ log: '$state', highlighted: true },
{ log: 'checked', highlighted: false },
{ log: false }
]);
@ -52,12 +32,15 @@ export default test({
// count changed, derived and state are highlighted, last state is not
assert.deepEqual(normalise_trace_logs(logs), [
{ log: 'effect', highlighted: false },
{ log: 'effect' },
{ log: '$derived', highlighted: true },
{ log: 'double', highlighted: false },
{ log: 2 },
{ log: '$state', highlighted: true },
{ log: 'count', highlighted: false },
{ log: 1 },
{ log: '$state', highlighted: false },
{ log: 'checked', highlighted: false },
{ log: false }
]);
@ -70,12 +53,15 @@ export default test({
// checked changed, last state is highlighted, first two are not
assert.deepEqual(normalise_trace_logs(logs), [
{ log: 'effect', highlighted: false },
{ log: 'effect' },
{ log: '$derived', highlighted: false },
{ log: 'double', highlighted: false },
{ log: 2 },
{ log: '$state', highlighted: false },
{ log: 'count', highlighted: false },
{ log: 1 },
{ log: '$state', highlighted: true },
{ log: 'checked', highlighted: false },
{ log: true }
]);
@ -87,10 +73,12 @@ export default test({
// count change and derived it's >=4, checked is not in the dependencies anymore
assert.deepEqual(normalise_trace_logs(logs), [
{ log: 'effect', highlighted: false },
{ log: 'effect' },
{ log: '$derived', highlighted: true },
{ log: 'double', highlighted: false },
{ log: 4 },
{ log: '$state', highlighted: true },
{ log: 'count', highlighted: false },
{ log: 2 }
]);
}

Loading…
Cancel
Save