track ownership of state and mutations

pull/10464/head
Rich Harris 3 years ago
parent b126e6a3eb
commit f05319fb11

@ -437,6 +437,11 @@ export function client_component(source, analysis, options) {
}
}
if (options.dev && state.options.filename) {
body.unshift(b.stmt(b.call(b.id('$.push_module'), b.literal(state.options.filename))));
body.push(b.stmt(b.call(b.id('$.pop_module'), b.literal(state.options.filename))));
}
return {
type: 'Program',
sourceType: 'module',

@ -0,0 +1,74 @@
/** @typedef {{ file: string, line: number, column: number }} Location */
/** @type {Record<string, Array<{ start: Location, end: Location, filename: string }>>} */
const boundaries = {};
const chrome_pattern = /\((.+):(\d+):(\d+)\)$/;
const firefox_pattern = /@(.+):(\d+):(\d+)$/;
export function get_stack() {
const stack = new Error().stack;
if (!stack) return null;
const entries = [];
for (const line of stack.split('\n').slice(1)) {
let match = chrome_pattern.exec(line) ?? firefox_pattern.exec(line);
if (match) {
entries.push({
file: match[1],
line: +match[2],
column: +match[3]
});
}
}
return entries;
}
export function get_module() {
const stack = get_stack();
if (!stack) return null;
for (const entry of stack) {
if (entry) {
const modules = boundaries[entry.file];
for (const module of modules) {
if (module.start.line < entry.line && module.end.line > entry.line) {
return module.filename;
}
}
}
}
return null;
}
/**
* @param {string} filename The original `path/to/Blah.svelte` filename
*/
export function push_module(filename) {
const start = get_stack()?.[1];
if (start) {
(boundaries[start.file] ??= []).push({
start,
// @ts-expect-error
end: null,
filename
});
}
}
/**
* @param {string} filename
*/
export function pop_module(filename) {
const end = get_stack()?.[1];
if (end) {
// @ts-expect-error
boundaries[end.file].at(-1).end = end;
}
}

@ -20,6 +20,7 @@ import {
is_frozen,
object_prototype
} from './utils.js';
import { get_module } from './dev.js';
export const STATE_SYMBOL = Symbol('$state');
@ -52,7 +53,8 @@ export function proxy(value, immutable = true) {
a: is_array(value),
i: immutable,
p: proxy,
t: value
t: value,
o: DEV ? get_module() : ''
}),
writable: true,
enumerable: false
@ -249,6 +251,13 @@ const state_proxy_handler = {
// @ts-ignore
target[prop] = value;
if (DEV) {
const site = get_module();
if (site !== metadata.o) {
console.error(`mutating state outside the component where it was created: ${site}`);
}
}
if (not_has) {
// If we have mutated an array directly, we might need to
// signal that length has also changed. Do it before updating metadata

@ -406,6 +406,8 @@ export interface ProxyMetadata<T = Record<string | symbol, any>> {
p: ProxyStateObject<T>;
/** The original target this proxy was created for */
t: T;
/** The component that 'owns' this state, if any */
o: string;
}
export type ProxyStateObject<T = Record<string | symbol, any>> = T & {

@ -40,6 +40,7 @@ export {
freeze,
init
} from './client/runtime.js';
export * from './client/dev.js';
export * from './client/each.js';
export * from './client/render.js';
export * from './client/validate.js';

Loading…
Cancel
Save