Skip to content

Commit df5105e

Browse files
authored
breaking: change $inspect API (#9838)
* breaking: change `$inspect` API `$inspect` now takes 1-n arguments, and inspections modification happens through `.with(..)` closes #9737 * lint
1 parent 26c6d6f commit df5105e

File tree

18 files changed

+155
-62
lines changed

18 files changed

+155
-62
lines changed

.changeset/wise-donkeys-marry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
breaking: change `$inspect` API

packages/svelte/src/compiler/errors.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ const runes = {
190190
'invalid-derived-binding': () => `Invalid binding to derived state`,
191191
/**
192192
* @param {string} rune
193-
* @param {number[]} args
193+
* @param {Array<number | string>} args
194194
*/
195195
'invalid-rune-args-length': (rune, args) =>
196196
`${rune} can only be called with ${list(args, 'or')} ${

packages/svelte/src/compiler/phases/2-analyze/validation.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -711,8 +711,14 @@ function validate_call_expression(node, scope, path) {
711711
}
712712

713713
if (rune === '$inspect') {
714-
if (node.arguments.length < 1 || node.arguments.length > 2) {
715-
error(node, 'invalid-rune-args-length', rune, [1, 2]);
714+
if (node.arguments.length < 1) {
715+
error(node, 'invalid-rune-args-length', rune, [1, 'more']);
716+
}
717+
}
718+
719+
if (rune === '$inspect().with') {
720+
if (node.arguments.length !== 1) {
721+
error(node, 'invalid-rune-args-length', rune, [1]);
716722
}
717723
}
718724
}

packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { get_rune } from '../../../scope.js';
2-
import { is_hoistable_function } from '../../utils.js';
2+
import { is_hoistable_function, transform_inspect_rune } from '../../utils.js';
33
import * as b from '../../../../utils/builders.js';
44
import * as assert from '../../../../utils/assert.js';
55
import { create_state_declarators, get_prop_source, should_proxy } from '../utils.js';
@@ -301,33 +301,24 @@ export const javascript_visitors_runes = {
301301

302302
context.next();
303303
},
304-
CallExpression(node, { state, next, visit }) {
305-
const rune = get_rune(node, state.scope);
304+
CallExpression(node, context) {
305+
const rune = get_rune(node, context.state.scope);
306306

307307
if (rune === '$effect.active') {
308308
return b.call('$.effect_active');
309309
}
310310

311311
if (rune === '$effect.root') {
312312
const args = /** @type {import('estree').Expression[]} */ (
313-
node.arguments.map((arg) => visit(arg))
313+
node.arguments.map((arg) => context.visit(arg))
314314
);
315315
return b.call('$.user_root_effect', ...args);
316316
}
317317

318-
if (rune === '$inspect') {
319-
if (state.options.dev) {
320-
const arg = /** @type {import('estree').Expression} */ (visit(node.arguments[0]));
321-
const fn =
322-
node.arguments[1] &&
323-
/** @type {import('estree').Expression} */ (visit(node.arguments[1]));
324-
325-
return b.call('$.inspect', b.thunk(arg), fn);
326-
}
327-
328-
return b.unary('void', b.literal(0));
318+
if (rune === '$inspect' || rune === '$inspect().with') {
319+
return transform_inspect_rune(node, context);
329320
}
330321

331-
next();
322+
context.next();
332323
}
333324
};

packages/svelte/src/compiler/phases/3-transform/server/transform-server.js

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import {
1717
clean_nodes,
1818
determine_element_namespace,
1919
escape_html,
20-
infer_namespace
20+
infer_namespace,
21+
transform_inspect_rune
2122
} from '../utils.js';
2223
import { create_attribute, is_custom_element_node, is_element_node } from '../../nodes.js';
2324
import { error } from '../../../errors.js';
@@ -630,26 +631,18 @@ const javascript_visitors_runes = {
630631
}
631632
context.next();
632633
},
633-
CallExpression(node, { state, next, visit }) {
634-
const rune = get_rune(node, state.scope);
634+
CallExpression(node, context) {
635+
const rune = get_rune(node, context.state.scope);
635636

636637
if (rune === '$effect.active') {
637638
return b.literal(false);
638639
}
639640

640-
if (rune === '$inspect') {
641-
if (state.options.dev) {
642-
const args = /** @type {import('estree').Expression[]} */ (
643-
node.arguments.map((arg) => visit(arg))
644-
);
645-
646-
return b.call('console.log', ...args);
647-
}
648-
649-
return b.unary('void', b.literal(0));
641+
if (rune === '$inspect' || rune === '$inspect().with') {
642+
return transform_inspect_rune(node, context);
650643
}
651644

652-
next();
645+
context.next();
653646
}
654647
};
655648

packages/svelte/src/compiler/phases/3-transform/utils.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,35 @@ export function determine_element_namespace(node, namespace, path) {
271271

272272
return namespace;
273273
}
274+
275+
/**
276+
* @template {import('./types.js').TransformState} T
277+
* @param {import('estree').CallExpression} node
278+
* @param {import('zimmerframe').Context<any, T>} context
279+
*/
280+
export function transform_inspect_rune(node, context) {
281+
const { state, visit } = context;
282+
const as_fn = state.options.generate === 'client';
283+
284+
if (!state.options.dev) return b.unary('void', b.literal(0));
285+
286+
if (node.callee.type === 'MemberExpression') {
287+
const raw_inspect_args = /** @type {import('estree').CallExpression} */ (node.callee.object)
288+
.arguments;
289+
const inspect_args =
290+
/** @type {Array<import('estree').Expression>} */
291+
(raw_inspect_args.map((arg) => visit(arg)));
292+
const with_arg = /** @type {import('estree').Expression} */ (visit(node.arguments[0]));
293+
294+
return b.call(
295+
'$.inspect',
296+
as_fn ? b.thunk(b.array(inspect_args)) : b.array(inspect_args),
297+
with_arg
298+
);
299+
} else {
300+
const arg = node.arguments.map(
301+
(arg) => /** @type {import('estree').Expression} */ (visit(arg))
302+
);
303+
return b.call('$.inspect', as_fn ? b.thunk(b.array(arg)) : b.array(arg));
304+
}
305+
}

packages/svelte/src/compiler/phases/constants.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ export const Runes = /** @type {const} */ ([
7878
'$effect.pre',
7979
'$effect.active',
8080
'$effect.root',
81-
'$inspect'
81+
'$inspect',
82+
'$inspect().with'
8283
]);
8384

8485
/**

packages/svelte/src/compiler/phases/scope.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,11 @@ export function get_rune(node, scope) {
699699
n = n.object;
700700
}
701701

702+
if (n.type === 'CallExpression' && n.callee.type === 'Identifier') {
703+
joined = '()' + joined;
704+
n = n.callee;
705+
}
706+
702707
if (n.type !== 'Identifier') return null;
703708

704709
joined = n.name + joined;

packages/svelte/src/internal/client/runtime.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
PROPS_IS_UPDATED
1010
} from '../../constants.js';
1111
import { readonly } from './proxy/readonly.js';
12-
import { proxy } from './proxy/proxy.js';
12+
import { proxy, unstate } from './proxy/proxy.js';
1313

1414
export const SOURCE = 1;
1515
export const DERIVED = 1 << 1;
@@ -1775,19 +1775,28 @@ function deep_read(value, visited = new Set()) {
17751775
}
17761776
}
17771777

1778+
// TODO remove in a few versions, before 5.0 at the latest
1779+
let warned_inspect_changed = false;
1780+
17781781
/**
1779-
* @param {() => any} get_value
1780-
* @param {Function} inspect
1781-
* @returns {void}
1782+
* @param {() => any[]} get_value
1783+
* @param {Function} [inspect]
17821784
*/
17831785
// eslint-disable-next-line no-console
17841786
export function inspect(get_value, inspect = console.log) {
17851787
let initial = true;
17861788

17871789
pre_effect(() => {
17881790
const fn = () => {
1789-
const value = get_value();
1790-
inspect(value, initial ? 'init' : 'update');
1791+
const value = get_value().map(unstate);
1792+
if (value.length === 2 && typeof value[1] === 'function' && !warned_inspect_changed) {
1793+
// eslint-disable-next-line no-console
1794+
console.warn(
1795+
'$inspect() API has changed. See https://svelte-5-preview.vercel.app/docs/runes#$inspect for more information.'
1796+
);
1797+
warned_inspect_changed = true;
1798+
}
1799+
inspect(initial ? 'init' : 'update', ...value);
17911800
};
17921801

17931802
inspect_fn = fn;

packages/svelte/src/internal/server/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,3 +552,12 @@ export function loop_guard(timeout) {
552552
}
553553
};
554554
}
555+
556+
/**
557+
* @param {any[]} args
558+
* @param {Function} [inspect]
559+
*/
560+
// eslint-disable-next-line no-console
561+
export function inspect(args, inspect = console.log) {
562+
inspect('init', ...args);
563+
}

0 commit comments

Comments
 (0)