mirror of https://github.com/sveltejs/svelte
feat: add parent hierarchy to `__svelte_meta` objects at dev time (#16255)
* feat: add parent hierarchy to `__svelte_meta` objects at dev time This adds a `parent` property to the `__svelte_meta` properties that are added to elements at dev time. This property represents the closest non-element parent the element is related to. For example for `{#if ...}<div>foo</div>{/if}` the `parent` of the div would be the line/column of the if block. The parent is recursive and goes upwards (through component boundaries) until the root component is reached, which has no parent. part of #11389 * oops * Apply suggestions from code review Co-authored-by: Rich Harris <rich.harris@vercel.com> * tweak * original component tag * make render appear in tree, keep tree in sync when rerenders occur --------- Co-authored-by: Rich Harris <rich.harris@vercel.com>pull/16283/head
parent
9dddb31b4a
commit
32882a956b
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: add parent hierarchy to `__svelte_meta` objects
|
@ -0,0 +1,186 @@
|
|||||||
|
import { tick } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
mode: ['client', 'hydrate'],
|
||||||
|
compileOptions: {
|
||||||
|
dev: true
|
||||||
|
},
|
||||||
|
html: `
|
||||||
|
<p>no parent</p>
|
||||||
|
<button>toggle</button>
|
||||||
|
<p>if</p>
|
||||||
|
<p>each</p>
|
||||||
|
<p>loading</p>
|
||||||
|
<p>key</p>
|
||||||
|
<p>hi</p>
|
||||||
|
<p>hi</p>
|
||||||
|
<p>hi</p>
|
||||||
|
<p>hi</p>
|
||||||
|
<p>hi</p>
|
||||||
|
`,
|
||||||
|
|
||||||
|
async test({ target, assert }) {
|
||||||
|
function check() {
|
||||||
|
const [main, if_, each, await_, key, child1, child2, child3, child4, dynamic] =
|
||||||
|
target.querySelectorAll('p');
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
assert.deepEqual(main.__svelte_meta.parent, null);
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
assert.deepEqual(if_.__svelte_meta.parent, {
|
||||||
|
file: 'main.svelte',
|
||||||
|
type: 'if',
|
||||||
|
line: 12,
|
||||||
|
column: 0,
|
||||||
|
parent: null
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
assert.deepEqual(each.__svelte_meta.parent, {
|
||||||
|
file: 'main.svelte',
|
||||||
|
type: 'each',
|
||||||
|
line: 16,
|
||||||
|
column: 0,
|
||||||
|
parent: null
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
assert.deepEqual(await_.__svelte_meta.parent, {
|
||||||
|
file: 'main.svelte',
|
||||||
|
type: 'await',
|
||||||
|
line: 20,
|
||||||
|
column: 0,
|
||||||
|
parent: null
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
assert.deepEqual(key.__svelte_meta.parent, {
|
||||||
|
file: 'main.svelte',
|
||||||
|
type: 'key',
|
||||||
|
line: 26,
|
||||||
|
column: 0,
|
||||||
|
parent: null
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
assert.deepEqual(child1.__svelte_meta.parent, {
|
||||||
|
file: 'main.svelte',
|
||||||
|
type: 'component',
|
||||||
|
componentTag: 'Child',
|
||||||
|
line: 30,
|
||||||
|
column: 0,
|
||||||
|
parent: null
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
assert.deepEqual(child2.__svelte_meta.parent, {
|
||||||
|
file: 'main.svelte',
|
||||||
|
type: 'component',
|
||||||
|
componentTag: 'Child',
|
||||||
|
line: 33,
|
||||||
|
column: 1,
|
||||||
|
parent: {
|
||||||
|
file: 'passthrough.svelte',
|
||||||
|
type: 'render',
|
||||||
|
line: 5,
|
||||||
|
column: 0,
|
||||||
|
parent: {
|
||||||
|
file: 'main.svelte',
|
||||||
|
type: 'component',
|
||||||
|
componentTag: 'Passthrough',
|
||||||
|
line: 32,
|
||||||
|
column: 0,
|
||||||
|
parent: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
assert.deepEqual(child3.__svelte_meta.parent, {
|
||||||
|
file: 'main.svelte',
|
||||||
|
type: 'component',
|
||||||
|
componentTag: 'Child',
|
||||||
|
line: 38,
|
||||||
|
column: 2,
|
||||||
|
parent: {
|
||||||
|
file: 'passthrough.svelte',
|
||||||
|
type: 'render',
|
||||||
|
line: 5,
|
||||||
|
column: 0,
|
||||||
|
parent: {
|
||||||
|
file: 'main.svelte',
|
||||||
|
type: 'component',
|
||||||
|
componentTag: 'Passthrough',
|
||||||
|
line: 37,
|
||||||
|
column: 1,
|
||||||
|
parent: {
|
||||||
|
file: 'passthrough.svelte',
|
||||||
|
type: 'render',
|
||||||
|
line: 5,
|
||||||
|
column: 0,
|
||||||
|
parent: {
|
||||||
|
file: 'main.svelte',
|
||||||
|
type: 'component',
|
||||||
|
componentTag: 'Passthrough',
|
||||||
|
line: 36,
|
||||||
|
column: 0,
|
||||||
|
parent: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
assert.deepEqual(child4.__svelte_meta.parent, {
|
||||||
|
file: 'passthrough.svelte',
|
||||||
|
type: 'render',
|
||||||
|
line: 8,
|
||||||
|
column: 1,
|
||||||
|
parent: {
|
||||||
|
file: 'passthrough.svelte',
|
||||||
|
type: 'if',
|
||||||
|
line: 7,
|
||||||
|
column: 0,
|
||||||
|
parent: {
|
||||||
|
file: 'main.svelte',
|
||||||
|
type: 'component',
|
||||||
|
componentTag: 'Passthrough',
|
||||||
|
line: 43,
|
||||||
|
column: 1,
|
||||||
|
parent: {
|
||||||
|
file: 'main.svelte',
|
||||||
|
type: 'if',
|
||||||
|
line: 42,
|
||||||
|
column: 0,
|
||||||
|
parent: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
assert.deepEqual(dynamic.__svelte_meta.parent, {
|
||||||
|
file: 'main.svelte',
|
||||||
|
type: 'component',
|
||||||
|
componentTag: 'x.y',
|
||||||
|
line: 50,
|
||||||
|
column: 0,
|
||||||
|
parent: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await tick();
|
||||||
|
check();
|
||||||
|
|
||||||
|
// Test that stack is kept when re-rendering
|
||||||
|
const button = target.querySelector('button');
|
||||||
|
button?.click();
|
||||||
|
await tick();
|
||||||
|
button?.click();
|
||||||
|
await tick();
|
||||||
|
check();
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1 @@
|
|||||||
|
<p>hi</p>
|
@ -0,0 +1,50 @@
|
|||||||
|
<script>
|
||||||
|
import Child from "./child.svelte";
|
||||||
|
import Passthrough from "./passthrough.svelte";
|
||||||
|
let x = { y: Child }
|
||||||
|
let key = 'test';
|
||||||
|
let show = $state(true);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p>no parent</p>
|
||||||
|
<button onclick={() => show = !show}>toggle</button>
|
||||||
|
|
||||||
|
{#if true}
|
||||||
|
<p>if</p>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#each [1]}
|
||||||
|
<p>each</p>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
{#await Promise.resolve()}
|
||||||
|
<p>loading</p>
|
||||||
|
{:then}
|
||||||
|
<p>await</p>
|
||||||
|
{/await}
|
||||||
|
|
||||||
|
{#key key}
|
||||||
|
<p>key</p>
|
||||||
|
{/key}
|
||||||
|
|
||||||
|
<Child />
|
||||||
|
|
||||||
|
<Passthrough>
|
||||||
|
<Child />
|
||||||
|
</Passthrough>
|
||||||
|
|
||||||
|
<Passthrough>
|
||||||
|
<Passthrough>
|
||||||
|
<Child />
|
||||||
|
</Passthrough>
|
||||||
|
</Passthrough>
|
||||||
|
|
||||||
|
{#if show}
|
||||||
|
<Passthrough>
|
||||||
|
{#snippet named()}
|
||||||
|
<p>hi</p>
|
||||||
|
{/snippet}
|
||||||
|
</Passthrough>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<x.y />
|
@ -0,0 +1,9 @@
|
|||||||
|
<script>
|
||||||
|
let { children, named } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{@render children?.()}
|
||||||
|
|
||||||
|
{#if true}
|
||||||
|
{@render named?.()}
|
||||||
|
{/if}
|
Loading…
Reference in new issue