handle dynamic elements too

pull/11514/head
Rich Harris 2 years ago
parent 2d97b87c9d
commit 4a0e639732

@ -2180,19 +2180,21 @@ export const template_visitors = {
})
);
const args = [
context.state.node,
get_tag,
node.metadata.svg || node.metadata.mathml ? b.true : b.false
];
if (inner.length > 0) {
args.push(b.arrow([element_id, b.id('$$anchor')], b.block(inner)));
}
if (dynamic_namespace) {
if (inner.length === 0) args.push(b.id('undefined'));
args.push(b.thunk(serialize_attribute_value(dynamic_namespace, context)[1]));
}
context.state.init.push(b.stmt(b.call('$.element', ...args)));
const location = context.state.options.dev && context.state.source_locator(node.start);
context.state.init.push(
b.stmt(
b.call(
'$.element',
context.state.node,
get_tag,
node.metadata.svg || node.metadata.mathml ? b.true : b.false,
inner.length > 0 && b.arrow([element_id, b.id('$$anchor')], b.block(inner)),
dynamic_namespace && b.thunk(serialize_attribute_value(dynamic_namespace, context)[1]),
location && b.array([b.literal(location.line), b.literal(location.column)])
)
)
);
},
EachBlock(node, context) {
const each_node_meta = node.metadata;

@ -99,19 +99,34 @@ export function labeled(name, body) {
/**
* @param {string | import('estree').Expression} callee
* @param {...(import('estree').Expression | import('estree').SpreadElement)} args
* @param {...(import('estree').Expression | import('estree').SpreadElement | false | undefined)} args
* @returns {import('estree').CallExpression}
*/
export function call(callee, ...args) {
if (typeof callee === 'string') callee = id(callee);
args = args.slice();
while (args.length > 0 && !args.at(-1)) args.pop();
// replacing missing arguments with `undefined`, unless they're at the end in which case remove them
let i = args.length;
let popping = true;
while (i--) {
if (!args[i]) {
if (popping) {
args.pop();
} else {
args[i] = id('undefined');
}
} else {
popping = false;
}
}
return {
type: 'CallExpression',
callee,
arguments: args,
arguments: /** @type {Array<import('estree').Expression | import('estree').SpreadElement>} */ (
args
),
optional: false
};
}

@ -12,8 +12,9 @@ import {
import { is_array } from '../../utils.js';
import { set_should_intro } from '../../render.js';
import { current_each_item, set_current_each_item } from './each.js';
import { current_effect } from '../../runtime.js';
import { current_component_context, current_effect } from '../../runtime.js';
import { push_template_node } from '../template.js';
import { DEV } from 'esm-env';
/**
* @param {import('#client').Effect} effect
@ -42,11 +43,14 @@ function swap_block_dom(effect, from, to) {
* @param {boolean} is_svg
* @param {undefined | ((element: Element, anchor: Node | null) => void)} render_fn,
* @param {undefined | (() => string)} get_namespace
* @param {undefined | [number, number]} location
* @returns {void}
*/
export function element(anchor, get_tag, is_svg, render_fn, get_namespace) {
export function element(anchor, get_tag, is_svg, render_fn, get_namespace, location) {
const parent_effect = /** @type {import('#client').Effect} */ (current_effect);
const filename = DEV && current_component_context?.function.filename;
render_effect(() => {
/** @type {string | null} */
let tag;
@ -108,6 +112,17 @@ export function element(anchor, get_tag, is_svg, render_fn, get_namespace) {
? document.createElementNS(ns, next_tag)
: document.createElement(next_tag);
if (DEV && location) {
// @ts-expect-error
element.__svelte_meta = {
loc: {
filename,
line: location[0],
column: location[1]
}
};
}
if (render_fn) {
// If hydrating, use the existing ssr comment as the anchor so that the
// inner open and close methods can pick up the existing nodes correctly

@ -0,0 +1,40 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
compileOptions: {
dev: true
},
html: `<button>toggle</button><p>before</p><p>after</p>`,
async test({ target, assert }) {
const btn = target.querySelector('button');
const ps = target.querySelectorAll('p');
// @ts-expect-error
assert.deepEqual(ps[0].__svelte_meta.loc, {
filename: '.../samples/svelte-meta-dynamic/main.svelte',
line: 7,
column: 0
});
// @ts-expect-error
assert.deepEqual(ps[1].__svelte_meta.loc, {
filename: '.../samples/svelte-meta-dynamic/main.svelte',
line: 13,
column: 0
});
flushSync(() => btn?.click());
const strong = target.querySelector('strong');
// @ts-expect-error
assert.deepEqual(strong.__svelte_meta.loc, {
filename: '.../samples/svelte-meta-dynamic/main.svelte',
line: 10,
column: 1
});
}
});

@ -0,0 +1,13 @@
<script>
let condition = $state(false);
</script>
<button onclick={() => condition = !condition}>toggle</button>
<svelte:element this={'p'}>before</svelte:element>
{#if condition}
<svelte:element this={'strong'}>during</svelte:element>
{/if}
<svelte:element this={'p'}>after</svelte:element>

@ -1,4 +1,4 @@
import { flushSync } from '../../../../src/index-client.js';
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({

Loading…
Cancel
Save