Comparing version 5.13.0 to 5.14.0
@@ -5,3 +5,3 @@ { | ||
"license": "MIT", | ||
"version": "5.13.0", | ||
"version": "5.14.0", | ||
"type": "module", | ||
@@ -8,0 +8,0 @@ "types": "./types/index.d.ts", |
@@ -374,2 +374,21 @@ declare module '*.svelte' { | ||
declare namespace $inspect { | ||
/** | ||
* Tracks which reactive state changes caused an effect to re-run. Must be the first | ||
* statement of a function body. Example: | ||
* | ||
* ```svelte | ||
* <script> | ||
* let count = $state(0); | ||
* | ||
* $effect(() => { | ||
* $inspect.trace('my effect'); | ||
* | ||
* count; | ||
* }); | ||
* </script> | ||
*/ | ||
export function trace(name: string): void; | ||
} | ||
/** | ||
@@ -376,0 +395,0 @@ * Retrieves the `this` reference of the custom element that contains this component. Example: |
@@ -210,2 +210,20 @@ /* This file is generated by scripts/process-messages/index.js. Do not edit! */ | ||
/** | ||
* `$inspect.trace(...)` cannot be used inside a generator function | ||
* @param {null | number | NodeLike} node | ||
* @returns {never} | ||
*/ | ||
export function inspect_trace_generator(node) { | ||
e(node, "inspect_trace_generator", `\`$inspect.trace(...)\` cannot be used inside a generator function\nhttps://svelte.dev/e/inspect_trace_generator`); | ||
} | ||
/** | ||
* `$inspect.trace(...)` must be the first statement of a function body | ||
* @param {null | number | NodeLike} node | ||
* @returns {never} | ||
*/ | ||
export function inspect_trace_invalid_placement(node) { | ||
e(node, "inspect_trace_invalid_placement", `\`$inspect.trace(...)\` must be the first statement of a function body\nhttps://svelte.dev/e/inspect_trace_invalid_placement`); | ||
} | ||
/** | ||
* The arguments keyword cannot be used within the template or at the top level of a component | ||
@@ -212,0 +230,0 @@ * @param {null | number | NodeLike} node |
@@ -1,2 +0,2 @@ | ||
/** @import { CallExpression, VariableDeclarator } from 'estree' */ | ||
/** @import { ArrowFunctionExpression, CallExpression, Expression, FunctionDeclaration, FunctionExpression, Identifier, VariableDeclarator } from 'estree' */ | ||
/** @import { AST } from '#compiler' */ | ||
@@ -8,3 +8,4 @@ /** @import { Context } from '../types' */ | ||
import { is_pure, is_safe_identifier } from './shared/utils.js'; | ||
import { mark_subtree_dynamic } from './shared/fragment.js'; | ||
import { dev, locate_node, source } from '../../../state.js'; | ||
import * as b from '../../../utils/builders.js'; | ||
@@ -140,2 +141,41 @@ /** | ||
case '$inspect.trace': { | ||
if (node.arguments.length > 1) { | ||
e.rune_invalid_arguments_length(node, rune, 'zero or one arguments'); | ||
} | ||
const grand_parent = context.path.at(-2); | ||
const fn = context.path.at(-3); | ||
if ( | ||
parent.type !== 'ExpressionStatement' || | ||
grand_parent?.type !== 'BlockStatement' || | ||
!( | ||
fn?.type === 'FunctionDeclaration' || | ||
fn?.type === 'FunctionExpression' || | ||
fn?.type === 'ArrowFunctionExpression' | ||
) || | ||
grand_parent.body[0] !== parent | ||
) { | ||
e.inspect_trace_invalid_placement(node); | ||
} | ||
if (fn.generator) { | ||
e.inspect_trace_generator(node); | ||
} | ||
if (dev) { | ||
if (node.arguments[0]) { | ||
context.state.scope.tracing = b.thunk(/** @type {Expression} */ (node.arguments[0])); | ||
} else { | ||
const label = get_function_label(context.path.slice(0, -2)) ?? 'trace'; | ||
const loc = `(${locate_node(fn)})`; | ||
context.state.scope.tracing = b.thunk(b.literal(label + ' ' + loc)); | ||
} | ||
} | ||
break; | ||
} | ||
case '$state.snapshot': | ||
@@ -187,1 +227,29 @@ if (node.arguments.length !== 1) { | ||
} | ||
/** | ||
* @param {AST.SvelteNode[]} nodes | ||
*/ | ||
function get_function_label(nodes) { | ||
const fn = /** @type {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} */ ( | ||
nodes.at(-1) | ||
); | ||
if ((fn.type === 'FunctionDeclaration' || fn.type === 'FunctionExpression') && fn.id != null) { | ||
return fn.id.name; | ||
} | ||
const parent = nodes.at(-2); | ||
if (!parent) return; | ||
if (parent.type === 'CallExpression') { | ||
return source.slice(parent.callee.start, parent.callee.end) + '(...)'; | ||
} | ||
if (parent.type === 'Property' && !parent.computed) { | ||
return /** @type {Identifier} */ (parent.key).name; | ||
} | ||
if (parent.type === 'VariableDeclarator' && parent.id.type === 'Identifier') { | ||
return parent.id.name; | ||
} | ||
} |
@@ -11,3 +11,3 @@ /** @import { Location } from 'locate-character' */ | ||
} from '../../../../utils/ast.js'; | ||
import { dev, filename, is_ignored, locator } from '../../../../state.js'; | ||
import { dev, filename, is_ignored, locate_node, locator } from '../../../../state.js'; | ||
import { build_proxy_reassignment, should_proxy } from '../utils.js'; | ||
@@ -187,5 +187,2 @@ import { visit_assignment_expression } from '../../shared/assignments.js'; | ||
const loc = /** @type {Location} */ (locator(/** @type {number} */ (left.start))); | ||
const location = `${filename}:${loc.line}:${loc.column}`; | ||
return /** @type {Expression} */ ( | ||
@@ -202,3 +199,3 @@ context.visit( | ||
right, | ||
b.literal(location) | ||
b.literal(locate_node(left)) | ||
) | ||
@@ -205,0 +202,0 @@ ) |
@@ -1,4 +0,5 @@ | ||
/** @import { BlockStatement } from 'estree' */ | ||
/** @import { ArrowFunctionExpression, BlockStatement, CallExpression, Expression, FunctionDeclaration, FunctionExpression, Statement } from 'estree' */ | ||
/** @import { ComponentContext } from '../types' */ | ||
import { add_state_transformers } from './shared/declarations.js'; | ||
import * as b from '../../../../utils/builders.js'; | ||
@@ -11,3 +12,22 @@ /** | ||
add_state_transformers(context); | ||
const tracing = context.state.scope.tracing; | ||
if (tracing !== null) { | ||
const parent = | ||
/** @type {ArrowFunctionExpression | FunctionDeclaration | FunctionExpression} */ ( | ||
context.path.at(-1) | ||
); | ||
const is_async = parent.async; | ||
const call = b.call( | ||
'$.trace', | ||
/** @type {Expression} */ (tracing), | ||
b.thunk(b.block(node.body.map((n) => /** @type {Statement} */ (context.visit(n)))), is_async) | ||
); | ||
return b.block([b.return(is_async ? b.await(call) : call)]); | ||
} | ||
context.next(); | ||
} |
@@ -23,2 +23,6 @@ /** @import { Expression, ExpressionStatement } from 'estree' */ | ||
} | ||
if (rune === '$inspect.trace') { | ||
return b.empty; | ||
} | ||
} | ||
@@ -25,0 +29,0 @@ |
@@ -30,2 +30,3 @@ /** @import { CallExpression, Expression, Identifier, Literal, VariableDeclaration, VariableDeclarator } from 'estree' */ | ||
rune === '$inspect' || | ||
rune === '$inspect.trace' || | ||
rune === '$state.snapshot' || | ||
@@ -32,0 +33,0 @@ rune === '$host' |
@@ -13,3 +13,8 @@ /** @import { ExpressionStatement } from 'estree' */ | ||
if (rune === '$effect' || rune === '$effect.pre' || rune === '$effect.root') { | ||
if ( | ||
rune === '$effect' || | ||
rune === '$effect.pre' || | ||
rune === '$effect.root' || | ||
rune === '$inspect.trace' | ||
) { | ||
return b.empty; | ||
@@ -16,0 +21,0 @@ } |
@@ -62,2 +62,8 @@ /** @import { ClassDeclaration, Expression, FunctionDeclaration, Identifier, ImportDeclaration, MemberExpression, Node, Pattern, VariableDeclarator } from 'estree' */ | ||
/** | ||
* If tracing of reactive dependencies is enabled for this scope | ||
* @type {null | Expression} | ||
*/ | ||
tracing = null; | ||
/** | ||
* | ||
@@ -64,0 +70,0 @@ * @param {ScopeRoot} root |
@@ -0,4 +1,6 @@ | ||
/** @import { Location } from 'locate-character' */ | ||
/** @import { CompileOptions } from './types' */ | ||
/** @import { AST, Warning } from '#compiler' */ | ||
import { getLocator } from 'locate-character'; | ||
import { sanitize_location } from '../utils.js'; | ||
@@ -31,2 +33,10 @@ /** @typedef {{ start?: number, end?: number }} NodeLike */ | ||
/** | ||
* @param {AST.SvelteNode & { start?: number | undefined }} node | ||
*/ | ||
export function locate_node(node) { | ||
const loc = /** @type {Location} */ (locator(/** @type {number} */ (node.start))); | ||
return `${sanitize_location(filename)}:${loc?.line}:${loc.column}`; | ||
} | ||
/** @type {NonNullable<CompileOptions['warningFilter']>} */ | ||
@@ -33,0 +43,0 @@ export let warning_filter; |
@@ -0,4 +1,4 @@ | ||
import { sanitize_location } from '../../../utils.js'; | ||
import { untrack } from '../runtime.js'; | ||
import * as w from '../warnings.js'; | ||
import { sanitize_location } from './location.js'; | ||
@@ -5,0 +5,0 @@ /** |
@@ -39,2 +39,3 @@ /** @import { EachItem, EachState, Effect, MaybeSource, Source, TemplateNode, TransitionManager, Value } from '#client' */ | ||
import { active_effect, active_reaction } from '../../runtime.js'; | ||
import { DEV } from 'esm-env'; | ||
@@ -195,3 +196,14 @@ /** | ||
var key = get_key(value, i); | ||
item = create_item(hydrate_node, state, prev, null, value, key, i, render_fn, flags); | ||
item = create_item( | ||
hydrate_node, | ||
state, | ||
prev, | ||
null, | ||
value, | ||
key, | ||
i, | ||
render_fn, | ||
flags, | ||
get_collection | ||
); | ||
state.items.set(key, item); | ||
@@ -210,3 +222,12 @@ | ||
var effect = /** @type {Effect} */ (active_reaction); | ||
reconcile(array, state, anchor, render_fn, flags, (effect.f & INERT) !== 0, get_key); | ||
reconcile( | ||
array, | ||
state, | ||
anchor, | ||
render_fn, | ||
flags, | ||
(effect.f & INERT) !== 0, | ||
get_key, | ||
get_collection | ||
); | ||
} | ||
@@ -257,5 +278,6 @@ | ||
* @param {(value: V, index: number) => any} get_key | ||
* @param {() => V[]} get_collection | ||
* @returns {void} | ||
*/ | ||
function reconcile(array, state, anchor, render_fn, flags, is_inert, get_key) { | ||
function reconcile(array, state, anchor, render_fn, flags, is_inert, get_key, get_collection) { | ||
var is_animated = (flags & EACH_IS_ANIMATED) !== 0; | ||
@@ -326,3 +348,4 @@ var should_update = (flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0; | ||
render_fn, | ||
flags | ||
flags, | ||
get_collection | ||
); | ||
@@ -494,5 +517,17 @@ | ||
* @param {number} flags | ||
* @param {() => V[]} get_collection | ||
* @returns {EachItem} | ||
*/ | ||
function create_item(anchor, state, prev, next, value, key, index, render_fn, flags) { | ||
function create_item( | ||
anchor, | ||
state, | ||
prev, | ||
next, | ||
value, | ||
key, | ||
index, | ||
render_fn, | ||
flags, | ||
get_collection | ||
) { | ||
var previous_each_item = current_each_item; | ||
@@ -505,2 +540,12 @@ var reactive = (flags & EACH_ITEM_REACTIVE) !== 0; | ||
if (DEV && reactive) { | ||
// For tracing purposes, we need to link the source signal we create with the | ||
// collection + index so that tracing works as intended | ||
/** @type {Value} */ (v).debug = () => { | ||
var collection_index = typeof i === 'number' ? index : i.v; | ||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions | ||
get_collection()[collection_index]; | ||
}; | ||
} | ||
/** @type {EachItem} */ | ||
@@ -507,0 +552,0 @@ var item = { |
@@ -8,7 +8,6 @@ /** @import { Effect, TemplateNode } from '#client' */ | ||
import * as w from '../../warnings.js'; | ||
import { hash } from '../../../../utils.js'; | ||
import { hash, sanitize_location } from '../../../../utils.js'; | ||
import { DEV } from 'esm-env'; | ||
import { dev_current_component_function } from '../../runtime.js'; | ||
import { get_first_child, get_next_sibling } from '../operations.js'; | ||
import { sanitize_location } from '../../dev/location.js'; | ||
@@ -15,0 +14,0 @@ /** |
@@ -15,2 +15,3 @@ export { FILENAME, HMR, NAMESPACE_SVG } from '../../constants.js'; | ||
export { check_target, legacy_api } from './dev/legacy.js'; | ||
export { trace } from './dev/tracing.js'; | ||
export { inspect } from './dev/inspect.js'; | ||
@@ -17,0 +18,0 @@ export { await_block as await } from './dom/blocks/await.js'; |
@@ -16,2 +16,3 @@ /** @import { ProxyMetadata, ProxyStateObject, Source } from '#client' */ | ||
import * as e from './errors.js'; | ||
import { get_stack } from './dev/tracing.js'; | ||
@@ -26,2 +27,7 @@ /** | ||
export function proxy(value, parent = null, prev) { | ||
/** @type {Error | null} */ | ||
var stack = null; | ||
if (DEV) { | ||
stack = get_stack('CreatedAt'); | ||
} | ||
// if non-proxyable, or is already a proxy, return `value` | ||
@@ -46,3 +52,3 @@ if (typeof value !== 'object' || value === null || STATE_SYMBOL in value) { | ||
// mutations to the array are properly synced with our proxy | ||
sources.set('length', source(/** @type {any[]} */ (value).length)); | ||
sources.set('length', source(/** @type {any[]} */ (value).length, stack)); | ||
} | ||
@@ -93,3 +99,3 @@ | ||
if (s === undefined) { | ||
s = source(descriptor.value); | ||
s = source(descriptor.value, stack); | ||
sources.set(prop, s); | ||
@@ -108,3 +114,3 @@ } else { | ||
if (prop in target) { | ||
sources.set(prop, source(UNINITIALIZED)); | ||
sources.set(prop, source(UNINITIALIZED, stack)); | ||
} | ||
@@ -143,3 +149,3 @@ } else { | ||
if (s === undefined && (!exists || get_descriptor(target, prop)?.writable)) { | ||
s = source(proxy(exists ? target[prop] : UNINITIALIZED, metadata)); | ||
s = source(proxy(exists ? target[prop] : UNINITIALIZED, metadata), stack); | ||
sources.set(prop, s); | ||
@@ -212,3 +218,3 @@ } | ||
if (s === undefined) { | ||
s = source(has ? proxy(target[prop], metadata) : UNINITIALIZED); | ||
s = source(has ? proxy(target[prop], metadata) : UNINITIALIZED, stack); | ||
sources.set(prop, s); | ||
@@ -240,3 +246,3 @@ } | ||
// the value of the original item at that index. | ||
other_s = source(UNINITIALIZED); | ||
other_s = source(UNINITIALIZED, stack); | ||
sources.set(i + '', other_s); | ||
@@ -253,3 +259,3 @@ } | ||
if (!has || get_descriptor(target, prop)?.writable) { | ||
s = source(undefined); | ||
s = source(undefined, stack); | ||
set(s, proxy(value, metadata)); | ||
@@ -256,0 +262,0 @@ sources.set(prop, s); |
@@ -27,2 +27,3 @@ /** @import { Derived, Effect } from '#client' */ | ||
import { inspect_effects, set_inspect_effects } from './sources.js'; | ||
import { get_stack } from '../dev/tracing.js'; | ||
@@ -65,2 +66,6 @@ /** | ||
if (DEV) { | ||
signal.created = get_stack('CreatedAt'); | ||
} | ||
if (parent_derived !== null) { | ||
@@ -67,0 +72,0 @@ (parent_derived.children ??= []).push(signal); |
@@ -1,2 +0,2 @@ | ||
/** @import { ComponentContext, ComponentContextLegacy, Derived, Effect, Reaction, TemplateNode, TransitionManager } from '#client' */ | ||
/** @import { ComponentContext, ComponentContextLegacy, Derived, Effect, TemplateNode, TransitionManager } from '#client' */ | ||
import { | ||
@@ -19,4 +19,3 @@ check_dirtiness, | ||
untrack, | ||
skip_reaction, | ||
capture_signals | ||
skip_reaction | ||
} from '../runtime.js'; | ||
@@ -44,3 +43,2 @@ import { | ||
import * as e from '../errors.js'; | ||
import * as w from '../warnings.js'; | ||
import { DEV } from 'esm-env'; | ||
@@ -50,4 +48,2 @@ import { define_property } from '../../shared/utils.js'; | ||
import { destroy_derived } from './deriveds.js'; | ||
import { FILENAME } from '../../../constants.js'; | ||
import { get_location } from '../dev/location.js'; | ||
@@ -54,0 +50,0 @@ /** |
@@ -36,2 +36,3 @@ /** @import { Derived, Effect, Reaction, Source, Value } from '#client' */ | ||
import { legacy_mode_flag } from '../../flags/index.js'; | ||
import { get_stack } from '../dev/tracing.js'; | ||
@@ -50,6 +51,8 @@ export let inspect_effects = new Set(); | ||
* @param {V} v | ||
* @param {Error | null} [stack] | ||
* @returns {Source<V>} | ||
*/ | ||
export function source(v) { | ||
return { | ||
export function source(v, stack) { | ||
/** @type {Value} */ | ||
var signal = { | ||
f: 0, // TODO ideally we could skip this altogether, but it causes type errors | ||
@@ -61,2 +64,9 @@ v, | ||
}; | ||
if (DEV) { | ||
signal.created = stack ?? get_stack('CreatedAt'); | ||
signal.debug = null; | ||
} | ||
return signal; | ||
} | ||
@@ -166,2 +176,6 @@ | ||
if (DEV) { | ||
source.updated = get_stack('UpdatedAt'); | ||
} | ||
mark_reactions(source, DIRTY); | ||
@@ -168,0 +182,0 @@ |
@@ -17,2 +17,6 @@ import type { ComponentContext, Dom, Equals, TemplateNode, TransitionManager } from '#client'; | ||
v: V; | ||
/** Dev only */ | ||
created?: Error | null; | ||
updated?: Error | null; | ||
debug?: null | (() => void); | ||
} | ||
@@ -19,0 +23,0 @@ |
@@ -38,2 +38,3 @@ /** @import { ComponentContext, Derived, Effect, Reaction, Signal, Source, Value } from '#client' */ | ||
import { legacy_mode_flag } from '../flags/index.js'; | ||
import { tracing_expressions, get_stack } from './dev/tracing.js'; | ||
@@ -140,2 +141,7 @@ const FLUSH_MICROTASK = 0; | ||
/** @param {Set<Value> | null} value */ | ||
export function set_captured_signals(value) { | ||
captured_signals = value; | ||
} | ||
// Handling runtime component context | ||
@@ -361,3 +367,3 @@ /** @type {ComponentContext | null} */ | ||
define_property(error, 'stack', { | ||
value: error.stack + new_lines.join('\n') | ||
value: new_lines.join('\n') | ||
}); | ||
@@ -914,2 +920,23 @@ } | ||
if ( | ||
DEV && | ||
tracing_expressions !== null && | ||
active_reaction !== null && | ||
tracing_expressions.reaction === active_reaction | ||
) { | ||
// Used when mapping state between special blocks like `each` | ||
if (signal.debug) { | ||
signal.debug(); | ||
} else if (signal.created) { | ||
var entry = tracing_expressions.entries.get(signal); | ||
if (entry === undefined) { | ||
entry = { read: [] }; | ||
tracing_expressions.entries.set(signal, entry); | ||
} | ||
entry.read.push(get_stack('TracedAt')); | ||
} | ||
} | ||
return signal.v; | ||
@@ -916,0 +943,0 @@ } |
@@ -434,2 +434,3 @@ const regex_return_characters = /\r/g; | ||
'$inspect().with', | ||
'$inspect.trace', | ||
'$host' | ||
@@ -453,1 +454,9 @@ ]); | ||
} | ||
/** | ||
* Prevent devtools trying to make `location` a clickable link by inserting a zero-width space | ||
* @param {string | undefined} location | ||
*/ | ||
export function sanitize_location(location) { | ||
return location?.replace(/\//g, '/\u200b'); | ||
} |
@@ -9,3 +9,3 @@ // generated during release, do not modify | ||
*/ | ||
export const VERSION = '5.13.0'; | ||
export const VERSION = '5.14.0'; | ||
export const PUBLIC_VERSION = '5'; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2457027
54205