From 32882a956bc1283063d6da52401ac02b0aedefdb Mon Sep 17 00:00:00 2001
From: Simon H <5968653+dummdidumm@users.noreply.github.com>
Date: Wed, 2 Jul 2025 14:12:25 +0200
Subject: [PATCH] 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 ...}
foo
{/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
* tweak
* original component tag
* make render appear in tree, keep tree in sync when rerenders occur
---------
Co-authored-by: Rich Harris
---
.changeset/hot-buses-end.md | 5 +
.../3-transform/client/visitors/AwaitBlock.js | 8 +-
.../3-transform/client/visitors/EachBlock.js | 4 +-
.../3-transform/client/visitors/IfBlock.js | 4 +-
.../3-transform/client/visitors/KeyBlock.js | 8 +-
.../3-transform/client/visitors/RenderTag.js | 14 +-
.../client/visitors/shared/component.js | 10 +-
.../client/visitors/shared/utils.js | 35 +++-
packages/svelte/src/compiler/state.js | 3 +
.../svelte/src/internal/client/context.js | 40 +++-
.../src/internal/client/dev/elements.js | 2 +
.../src/internal/client/dom/blocks/await.js | 16 +-
.../client/dom/blocks/svelte-element.js | 3 +-
packages/svelte/src/internal/client/index.js | 2 +-
.../src/internal/client/reactivity/effects.js | 8 +-
.../src/internal/client/reactivity/types.d.ts | 10 +-
.../svelte/src/internal/client/runtime.js | 9 +-
.../svelte/src/internal/client/types.d.ts | 9 +
.../samples/svelte-meta-parent/_config.js | 186 ++++++++++++++++++
.../samples/svelte-meta-parent/child.svelte | 1 +
.../samples/svelte-meta-parent/main.svelte | 50 +++++
.../svelte-meta-parent/passthrough.svelte | 9 +
22 files changed, 408 insertions(+), 28 deletions(-)
create mode 100644 .changeset/hot-buses-end.md
create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-meta-parent/_config.js
create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-meta-parent/child.svelte
create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-meta-parent/main.svelte
create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-meta-parent/passthrough.svelte
diff --git a/.changeset/hot-buses-end.md b/.changeset/hot-buses-end.md
new file mode 100644
index 0000000000..dd5a28fca8
--- /dev/null
+++ b/.changeset/hot-buses-end.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+feat: add parent hierarchy to `__svelte_meta` objects
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js
index 7873cf3ddb..c550c8e17b 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js
@@ -5,7 +5,7 @@ import { extract_identifiers } from '../../../../utils/ast.js';
import * as b from '#compiler/builders';
import { create_derived } from '../utils.js';
import { get_value } from './shared/declarations.js';
-import { build_expression } from './shared/utils.js';
+import { build_expression, add_svelte_meta } from './shared/utils.js';
/**
* @param {AST.AwaitBlock} node
@@ -54,7 +54,7 @@ export function AwaitBlock(node, context) {
}
context.state.init.push(
- b.stmt(
+ add_svelte_meta(
b.call(
'$.await',
context.state.node,
@@ -64,7 +64,9 @@ export function AwaitBlock(node, context) {
: b.null,
then_block,
catch_block
- )
+ ),
+ node,
+ 'await'
)
);
}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js
index 201c4b278f..353927b865 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js
@@ -13,7 +13,7 @@ import { dev } from '../../../../state.js';
import { extract_paths, object } from '../../../../utils/ast.js';
import * as b from '#compiler/builders';
import { get_value } from './shared/declarations.js';
-import { build_expression } from './shared/utils.js';
+import { build_expression, add_svelte_meta } from './shared/utils.js';
/**
* @param {AST.EachBlock} node
@@ -337,7 +337,7 @@ export function EachBlock(node, context) {
);
}
- context.state.init.push(b.stmt(b.call('$.each', ...args)));
+ context.state.init.push(add_svelte_meta(b.call('$.each', ...args), node, 'each'));
}
/**
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js
index deab040e50..cfd2bb7b09 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js
@@ -2,7 +2,7 @@
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */
import * as b from '#compiler/builders';
-import { build_expression } from './shared/utils.js';
+import { build_expression, add_svelte_meta } from './shared/utils.js';
/**
* @param {AST.IfBlock} node
@@ -74,7 +74,7 @@ export function IfBlock(node, context) {
args.push(b.id('$$elseif'));
}
- statements.push(b.stmt(b.call('$.if', ...args)));
+ statements.push(add_svelte_meta(b.call('$.if', ...args), node, 'if'));
context.state.init.push(b.block(statements));
}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/KeyBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/KeyBlock.js
index 2f17479c7e..3add1fbe93 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/KeyBlock.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/KeyBlock.js
@@ -2,7 +2,7 @@
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */
import * as b from '#compiler/builders';
-import { build_expression } from './shared/utils.js';
+import { build_expression, add_svelte_meta } from './shared/utils.js';
/**
* @param {AST.KeyBlock} node
@@ -15,6 +15,10 @@ export function KeyBlock(node, context) {
const body = /** @type {Expression} */ (context.visit(node.fragment));
context.state.init.push(
- b.stmt(b.call('$.key', context.state.node, b.thunk(key), b.arrow([b.id('$$anchor')], body)))
+ add_svelte_meta(
+ b.call('$.key', context.state.node, b.thunk(key), b.arrow([b.id('$$anchor')], body)),
+ node,
+ 'key'
+ )
);
}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js
index c3615d9d50..7f1a4ae7ba 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js
@@ -3,7 +3,7 @@
/** @import { ComponentContext } from '../types' */
import { unwrap_optional } from '../../../../utils/ast.js';
import * as b from '#compiler/builders';
-import { build_expression } from './shared/utils.js';
+import { add_svelte_meta, build_expression } from './shared/utils.js';
/**
* @param {AST.RenderTag} node
@@ -48,16 +48,22 @@ export function RenderTag(node, context) {
}
context.state.init.push(
- b.stmt(b.call('$.snippet', context.state.node, b.thunk(snippet_function), ...args))
+ add_svelte_meta(
+ b.call('$.snippet', context.state.node, b.thunk(snippet_function), ...args),
+ node,
+ 'render'
+ )
);
} else {
context.state.init.push(
- b.stmt(
+ add_svelte_meta(
(node.expression.type === 'CallExpression' ? b.call : b.maybe_call)(
snippet_function,
context.state.node,
...args
- )
+ ),
+ node,
+ 'render'
)
);
}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js
index cb6e4de478..aa3704b50b 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js
@@ -4,7 +4,12 @@
import { dev, is_ignored } from '../../../../../state.js';
import { get_attribute_chunks, object } from '../../../../../utils/ast.js';
import * as b from '#compiler/builders';
-import { build_bind_this, memoize_expression, validate_binding } from '../shared/utils.js';
+import {
+ build_bind_this,
+ memoize_expression,
+ validate_binding,
+ add_svelte_meta
+} from '../shared/utils.js';
import { build_attribute_value } from '../shared/element.js';
import { build_event_handler } from './events.js';
import { determine_slot } from '../../../../../utils/slot.js';
@@ -483,7 +488,8 @@ export function build_component(node, component_name, context) {
);
} else {
context.state.template.push_comment();
- statements.push(b.stmt(fn(anchor)));
+
+ statements.push(add_svelte_meta(fn(anchor), node, 'component', { componentTag: node.name }));
}
return statements.length > 1 ? b.block(statements) : statements[0];
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js
index b80466ccc9..de74fede0c 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js
@@ -1,4 +1,4 @@
-/** @import { AssignmentExpression, Expression, ExpressionStatement, Identifier, MemberExpression, SequenceExpression, Literal, Super, UpdateExpression, Pattern } from 'estree' */
+/** @import { AssignmentExpression, Expression, Identifier, MemberExpression, SequenceExpression, Literal, Super, UpdateExpression, ExpressionStatement } from 'estree' */
/** @import { AST, ExpressionMetadata } from '#compiler' */
/** @import { ComponentClientTransformState, ComponentContext, Context } from '../../types' */
import { walk } from 'zimmerframe';
@@ -7,7 +7,7 @@ import * as b from '#compiler/builders';
import { sanitize_template_string } from '../../../../../utils/sanitize_template_string.js';
import { regex_is_valid_identifier } from '../../../../patterns.js';
import is_reference from 'is-reference';
-import { dev, is_ignored, locator } from '../../../../../state.js';
+import { dev, is_ignored, locator, component_name } from '../../../../../state.js';
import { build_getter, create_derived } from '../../utils.js';
/**
@@ -424,3 +424,34 @@ export function build_expression(context, expression, metadata, state = context.
return sequence;
}
+
+/**
+ * Wraps a statement/expression with dev stack tracking in dev mode
+ * @param {Expression} expression - The function call to wrap (e.g., $.if, $.each, etc.)
+ * @param {{ start?: number }} node - AST node for location info
+ * @param {'component' | 'if' | 'each' | 'await' | 'key' | 'render'} type - Type of block/component
+ * @param {Record} [additional] - Any additional properties to add to the dev stack entry
+ * @returns {ExpressionStatement} - Statement with or without dev stack wrapping
+ */
+export function add_svelte_meta(expression, node, type, additional) {
+ if (!dev) {
+ return b.stmt(expression);
+ }
+
+ const location = node.start !== undefined && locator(node.start);
+ if (!location) {
+ return b.stmt(expression);
+ }
+
+ return b.stmt(
+ b.call(
+ '$.add_svelte_meta',
+ b.arrow([], expression),
+ b.literal(type),
+ b.id(component_name),
+ b.literal(location.line),
+ b.literal(location.column),
+ additional && b.object(Object.entries(additional).map(([k, v]) => b.init(k, b.literal(v))))
+ )
+ );
+}
diff --git a/packages/svelte/src/compiler/state.js b/packages/svelte/src/compiler/state.js
index 9095651ced..5eb25dd6bb 100644
--- a/packages/svelte/src/compiler/state.js
+++ b/packages/svelte/src/compiler/state.js
@@ -16,6 +16,9 @@ export let warnings = [];
*/
export let filename;
+/**
+ * The name of the component that is used in the `export default function ...` statement.
+ */
export let component_name = '';
/**
diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js
index 7c7213b7a2..e4220149ab 100644
--- a/packages/svelte/src/internal/client/context.js
+++ b/packages/svelte/src/internal/client/context.js
@@ -1,4 +1,4 @@
-/** @import { ComponentContext } from '#client' */
+/** @import { ComponentContext, DevStackEntry } from '#client' */
import { DEV } from 'esm-env';
import { lifecycle_outside_component } from '../shared/errors.js';
@@ -11,6 +11,7 @@ import {
} from './runtime.js';
import { effect, teardown } from './reactivity/effects.js';
import { legacy_mode_flag } from '../flags/index.js';
+import { FILENAME } from '../../constants.js';
/** @type {ComponentContext | null} */
export let component_context = null;
@@ -20,6 +21,43 @@ export function set_component_context(context) {
component_context = context;
}
+/** @type {DevStackEntry | null} */
+export let dev_stack = null;
+
+/** @param {DevStackEntry | null} stack */
+export function set_dev_stack(stack) {
+ dev_stack = stack;
+}
+
+/**
+ * Execute a callback with a new dev stack entry
+ * @param {() => any} callback - Function to execute
+ * @param {DevStackEntry['type']} type - Type of block/component
+ * @param {any} component - Component function
+ * @param {number} line - Line number
+ * @param {number} column - Column number
+ * @param {Record} [additional] - Any additional properties to add to the dev stack entry
+ * @returns {any}
+ */
+export function add_svelte_meta(callback, type, component, line, column, additional) {
+ const parent = dev_stack;
+
+ dev_stack = {
+ type,
+ file: component[FILENAME],
+ line,
+ column,
+ parent,
+ ...additional
+ };
+
+ try {
+ return callback();
+ } finally {
+ dev_stack = parent;
+ }
+}
+
/**
* The current component function. Different from current component context:
* ```html
diff --git a/packages/svelte/src/internal/client/dev/elements.js b/packages/svelte/src/internal/client/dev/elements.js
index f70f893d1e..8dd54e0a2a 100644
--- a/packages/svelte/src/internal/client/dev/elements.js
+++ b/packages/svelte/src/internal/client/dev/elements.js
@@ -2,6 +2,7 @@
import { COMMENT_NODE, DOCUMENT_FRAGMENT_NODE, ELEMENT_NODE } from '#client/constants';
import { HYDRATION_END, HYDRATION_START, HYDRATION_START_ELSE } from '../../../constants.js';
import { hydrating } from '../dom/hydration.js';
+import { dev_stack } from '../context.js';
/**
* @param {any} fn
@@ -28,6 +29,7 @@ export function add_locations(fn, filename, locations) {
function assign_location(element, filename, location) {
// @ts-expect-error
element.__svelte_meta = {
+ parent: dev_stack,
loc: { file: filename, line: location[0], column: location[1] }
};
diff --git a/packages/svelte/src/internal/client/dom/blocks/await.js b/packages/svelte/src/internal/client/dom/blocks/await.js
index 47df5fc9a5..325224fff2 100644
--- a/packages/svelte/src/internal/client/dom/blocks/await.js
+++ b/packages/svelte/src/internal/client/dom/blocks/await.js
@@ -16,9 +16,11 @@ import { queue_micro_task } from '../task.js';
import { HYDRATION_START_ELSE, UNINITIALIZED } from '../../../../constants.js';
import {
component_context,
+ dev_stack,
is_runes,
set_component_context,
- set_dev_current_component_function
+ set_dev_current_component_function,
+ set_dev_stack
} from '../../context.js';
const PENDING = 0;
@@ -45,6 +47,7 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) {
/** @type {any} */
var component_function = DEV ? component_context?.function : null;
+ var dev_original_stack = DEV ? dev_stack : null;
/** @type {V | Promise | typeof UNINITIALIZED} */
var input = UNINITIALIZED;
@@ -75,7 +78,10 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) {
set_active_effect(effect);
set_active_reaction(effect); // TODO do we need both?
set_component_context(active_component_context);
- if (DEV) set_dev_current_component_function(component_function);
+ if (DEV) {
+ set_dev_current_component_function(component_function);
+ set_dev_stack(dev_original_stack);
+ }
}
try {
@@ -107,7 +113,11 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) {
}
} finally {
if (restore) {
- if (DEV) set_dev_current_component_function(null);
+ if (DEV) {
+ set_dev_current_component_function(null);
+ set_dev_stack(null);
+ }
+
set_component_context(null);
set_active_reaction(null);
set_active_effect(null);
diff --git a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js
index ffa57b2d8b..231a3621b1 100644
--- a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js
+++ b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js
@@ -18,7 +18,7 @@ import {
import { set_should_intro } from '../../render.js';
import { current_each_item, set_current_each_item } from './each.js';
import { active_effect } from '../../runtime.js';
-import { component_context } from '../../context.js';
+import { component_context, dev_stack } from '../../context.js';
import { DEV } from 'esm-env';
import { EFFECT_TRANSPARENT, ELEMENT_NODE } from '#client/constants';
import { assign_nodes } from '../template.js';
@@ -107,6 +107,7 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
if (DEV && location) {
// @ts-expect-error
element.__svelte_meta = {
+ parent: dev_stack,
loc: {
file: filename,
line: location[0],
diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js
index 60f9af9120..576a30fa77 100644
--- a/packages/svelte/src/internal/client/index.js
+++ b/packages/svelte/src/internal/client/index.js
@@ -1,6 +1,6 @@
export { createAttachmentKey as attachment } from '../../attachments/index.js';
export { FILENAME, HMR, NAMESPACE_SVG } from '../../constants.js';
-export { push, pop } from './context.js';
+export { push, pop, add_svelte_meta } from './context.js';
export { assign, assign_and, assign_or, assign_nullish } from './dev/assign.js';
export { cleanup_styles } from './dev/css.js';
export { add_locations } from './dev/elements.js';
diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js
index a2806bde81..a2de8940a6 100644
--- a/packages/svelte/src/internal/client/reactivity/effects.js
+++ b/packages/svelte/src/internal/client/reactivity/effects.js
@@ -41,7 +41,7 @@ import { DEV } from 'esm-env';
import { define_property } from '../../shared/utils.js';
import { get_next_sibling } from '../dom/operations.js';
import { derived } from './deriveds.js';
-import { component_context, dev_current_component_function } from '../context.js';
+import { component_context, dev_current_component_function, dev_stack } from '../context.js';
/**
* @param {'$effect' | '$effect.pre' | '$inspect'} rune
@@ -359,7 +359,11 @@ export function template_effect(fn, thunks = [], d = derived) {
* @param {number} flags
*/
export function block(fn, flags = 0) {
- return create_effect(RENDER_EFFECT | BLOCK_EFFECT | flags, fn, true);
+ var effect = create_effect(RENDER_EFFECT | BLOCK_EFFECT | flags, fn, true);
+ if (DEV) {
+ effect.dev_stack = dev_stack;
+ }
+ return effect;
}
/**
diff --git a/packages/svelte/src/internal/client/reactivity/types.d.ts b/packages/svelte/src/internal/client/reactivity/types.d.ts
index 88c84f27fe..80c4155705 100644
--- a/packages/svelte/src/internal/client/reactivity/types.d.ts
+++ b/packages/svelte/src/internal/client/reactivity/types.d.ts
@@ -1,4 +1,10 @@
-import type { ComponentContext, Dom, Equals, TemplateNode, TransitionManager } from '#client';
+import type {
+ ComponentContext,
+ DevStackEntry,
+ Equals,
+ TemplateNode,
+ TransitionManager
+} from '#client';
export interface Signal {
/** Flags bitmask */
@@ -80,6 +86,8 @@ export interface Effect extends Reaction {
parent: Effect | null;
/** Dev only */
component_function?: any;
+ /** Dev only. Only set for certain block effects. Contains a reference to the stack that represents the render tree */
+ dev_stack?: DevStackEntry | null;
}
export type Source = Value;
diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js
index 5a798ba3e9..8e6242447e 100644
--- a/packages/svelte/src/internal/client/runtime.js
+++ b/packages/svelte/src/internal/client/runtime.js
@@ -34,12 +34,13 @@ import { tracing_expressions, get_stack } from './dev/tracing.js';
import {
component_context,
dev_current_component_function,
+ dev_stack,
is_runes,
set_component_context,
- set_dev_current_component_function
+ set_dev_current_component_function,
+ set_dev_stack
} from './context.js';
import { handle_error, invoke_error_boundary } from './error-handling.js';
-import { snapshot } from '../shared/clone.js';
let is_flushing = false;
@@ -444,6 +445,9 @@ export function update_effect(effect) {
if (DEV) {
var previous_component_fn = dev_current_component_function;
set_dev_current_component_function(effect.component_function);
+ var previous_stack = /** @type {any} */ (dev_stack);
+ // only block effects have a dev stack, keep the current one otherwise
+ set_dev_stack(effect.dev_stack ?? dev_stack);
}
try {
@@ -478,6 +482,7 @@ export function update_effect(effect) {
if (DEV) {
set_dev_current_component_function(previous_component_fn);
+ set_dev_stack(previous_stack);
}
}
}
diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts
index 9703c2aac1..0b7310e172 100644
--- a/packages/svelte/src/internal/client/types.d.ts
+++ b/packages/svelte/src/internal/client/types.d.ts
@@ -187,4 +187,13 @@ export type SourceLocation =
| [line: number, column: number]
| [line: number, column: number, SourceLocation[]];
+export interface DevStackEntry {
+ file: string;
+ type: 'component' | 'if' | 'each' | 'await' | 'key' | 'render';
+ line: number;
+ column: number;
+ parent: DevStackEntry | null;
+ componentTag?: string;
+}
+
export * from './reactivity/types';
diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-meta-parent/_config.js b/packages/svelte/tests/runtime-runes/samples/svelte-meta-parent/_config.js
new file mode 100644
index 0000000000..eba85d5098
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/svelte-meta-parent/_config.js
@@ -0,0 +1,186 @@
+import { tick } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ mode: ['client', 'hydrate'],
+ compileOptions: {
+ dev: true
+ },
+ html: `
+
\ No newline at end of file
diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-meta-parent/main.svelte b/packages/svelte/tests/runtime-runes/samples/svelte-meta-parent/main.svelte
new file mode 100644
index 0000000000..b9bd46d8f7
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/svelte-meta-parent/main.svelte
@@ -0,0 +1,50 @@
+
+
+