feat: add a custom formatter for state proxies

custom-formatter
Rich Harris 4 months ago
parent 4092b7cbde
commit 32e3a5f2ac

@ -4,6 +4,10 @@
> `%binding%` (%location%) is binding to a non-reactive property > `%binding%` (%location%) is binding to a non-reactive property
## enable_custom_formatters
> We recommend enabling custom formatters for better results when logging `$state` objects — in your devtools, click the gear icon and check the 'Custom formatters' box
## event_handler_invalid ## event_handler_invalid
> %handler% should be a function. Did you mean to %suggestion%? > %handler% should be a function. Did you mean to %suggestion%?

@ -1,43 +1,43 @@
import { STATE_SYMBOL } from '../constants.js'; import { STATE_SYMBOL } from '../constants.js';
import { VERSION } from '../../../version.js';
import { snapshot } from '../../shared/clone.js';
import * as w from '../warnings.js';
export function monkey_patch_console() { export function install_custom_formatter() {
for (const method of Object.keys(console)) { // Custom formatters are 'supported' in Firefox, but they're worse than useless.
// @ts-expect-error // They're not supported in Firefox. We can maybe tweak this over time
const original = console[method]; var is_chrome = navigator.userAgent.includes('Chrome');
var custom_formatters_enabled = false;
if (is_chrome) {
// @ts-expect-error // @ts-expect-error
console[method] = (...args) => { (window.devtoolsFormatters ??= []).push({
for (const arg of args) { /**
if (contains_state_proxy(arg)) { * @param {any} object
// TODO make this a proper warning * @param {any} config
console.warn('contains state proxy!!!!'); */
break; header(object, config) {
} custom_formatters_enabled = true;
}
return original.apply(console, args); if (STATE_SYMBOL in object) {
}; return [
'div',
{},
['span', { style: 'font-weight: bold' }, '$state'],
['object', { object: snapshot(object), config }]
];
} }
}
/** return null;
* @param {any} value },
* @param {Set<any>} seen
* @returns {boolean}
*/
function contains_state_proxy(value, seen = new Set()) {
if (typeof value !== 'object' || value === null) return false;
if (seen.has(value)) return false;
seen.add(value);
if (STATE_SYMBOL in value) { hasBody: () => false
return true; });
} }
for (const key in value) { console.log(`Running Svelte in development mode`, { version: VERSION });
if (contains_state_proxy(value[key], seen)) {
return true; if (is_chrome && !custom_formatters_enabled) {
} w.enable_custom_formatters();
} }
} }

@ -1,5 +1,5 @@
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { monkey_patch_console } from './dev/log.js'; import { install_custom_formatter } from './dev/log.js';
export { FILENAME, HMR } from '../../constants.js'; export { FILENAME, HMR } from '../../constants.js';
export { add_locations } from './dev/elements.js'; export { add_locations } from './dev/elements.js';
@ -169,5 +169,5 @@ export {
export { strict_equals, equals } from './dev/equality.js'; export { strict_equals, equals } from './dev/equality.js';
if (DEV) { if (DEV) {
monkey_patch_console(); install_custom_formatter();
} }

@ -139,3 +139,15 @@ export function state_proxy_equality_mismatch(operator) {
console.warn("state_proxy_equality_mismatch"); console.warn("state_proxy_equality_mismatch");
} }
} }
/**
* We recommend enabling custom formatters for better results when logging `$state` objects in your devtools, click the gear icon and check the 'Custom formatters' box
*/
export function enable_custom_formatters() {
if (DEV) {
console.warn(`%c[svelte] enable_custom_formatters\n%cWe recommend enabling custom formatters for better results when logging \`$state\` objects — in your devtools, click the gear icon and check the 'Custom formatters' box`, bold, normal);
} else {
// TODO print a link to the documentation
console.warn("enable_custom_formatters");
}
}
Loading…
Cancel
Save