diff --git a/.changeset/neat-ducks-jam.md b/.changeset/neat-ducks-jam.md new file mode 100644 index 0000000000..42d79a5544 --- /dev/null +++ b/.changeset/neat-ducks-jam.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +feat: skip over static subtrees diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/create.js b/packages/svelte/src/compiler/phases/1-parse/utils/create.js index a505333b62..13bbb30dbf 100644 --- a/packages/svelte/src/compiler/phases/1-parse/utils/create.js +++ b/packages/svelte/src/compiler/phases/1-parse/utils/create.js @@ -7,6 +7,9 @@ export function create_fragment(transparent = false) { return { type: 'Fragment', nodes: [], - transparent + metadata: { + transparent, + dynamic: false + } }; } diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index daf8a54ff9..9429d3f6d2 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -61,6 +61,7 @@ import { TaggedTemplateExpression } from './visitors/TaggedTemplateExpression.js import { Text } from './visitors/Text.js'; import { TitleElement } from './visitors/TitleElement.js'; import { UpdateExpression } from './visitors/UpdateExpression.js'; +import { UseDirective } from './visitors/UseDirective.js'; import { VariableDeclarator } from './visitors/VariableDeclarator.js'; import is_reference from 'is-reference'; @@ -166,6 +167,7 @@ const visitors = { Text, TitleElement, UpdateExpression, + UseDirective, VariableDeclarator }; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index 6923bb5aac..9bcb266eaf 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -1,5 +1,5 @@ /** @import { ArrowFunctionExpression, Expression, FunctionDeclaration, FunctionExpression } from 'estree' */ -/** @import { Attribute, DelegatedEvent, RegularElement } from '#compiler' */ +/** @import { Attribute, DelegatedEvent, RegularElement, SvelteNode } from '#compiler' */ /** @import { Context } from '../types' */ import { is_capture_event, is_delegated } from '../../../../utils.js'; import { @@ -7,6 +7,7 @@ import { get_attribute_expression, is_event_attribute } from '../../../utils/ast.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {Attribute} node @@ -15,6 +16,14 @@ import { export function Attribute(node, context) { context.next(); + // special case + if (node.name === 'value') { + const parent = /** @type {SvelteNode} */ (context.path.at(-1)); + if (parent.type === 'RegularElement' && parent.name === 'option') { + mark_subtree_dynamic(context.path); + } + } + if (node.value !== true) { for (const chunk of get_attribute_chunks(node.value)) { if (chunk.type !== 'ExpressionTag') continue; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/AwaitBlock.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/AwaitBlock.js index a7d804c194..78ebe03021 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/AwaitBlock.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/AwaitBlock.js @@ -2,6 +2,7 @@ /** @import { Context } from '../types' */ import { validate_block_not_empty, validate_opening_tag } from './shared/utils.js'; import * as e from '../../../errors.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {AwaitBlock} node @@ -38,5 +39,7 @@ export function AwaitBlock(node, context) { } } + mark_subtree_dynamic(context.path); + context.next(); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/EachBlock.js index 5855a18f7a..f66fd72de5 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/EachBlock.js @@ -2,6 +2,7 @@ /** @import { Context } from '../types' */ /** @import { Scope } from '../../scope' */ import * as e from '../../../errors.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; import { validate_block_not_empty, validate_opening_tag } from './shared/utils.js'; /** @@ -36,4 +37,6 @@ export function EachBlock(node, context) { context.visit(node.body); if (node.key) context.visit(node.key); if (node.fallback) context.visit(node.fallback); + + mark_subtree_dynamic(context.path); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js index 813f9f0905..bf1c232737 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExpressionTag.js @@ -2,6 +2,7 @@ /** @import { Context } from '../types' */ import { is_tag_valid_with_parent } from '../../../../html-tree-validation.js'; import * as e from '../../../errors.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {ExpressionTag} node @@ -14,5 +15,9 @@ export function ExpressionTag(node, context) { } } + // TODO ideally we wouldn't do this here, we'd just do it on encountering + // an `Identifier` within the tag. But we currently need to handle `{42}` etc + mark_subtree_dynamic(context.path); + context.next({ ...context.state, expression: node.metadata.expression }); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/HtmlTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/HtmlTag.js index ecac6446ae..1c6385f94b 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/HtmlTag.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/HtmlTag.js @@ -1,5 +1,6 @@ /** @import { HtmlTag } from '#compiler' */ /** @import { Context } from '../types' */ +import { mark_subtree_dynamic } from './shared/fragment.js'; import { validate_opening_tag } from './shared/utils.js'; /** @@ -11,5 +12,8 @@ export function HtmlTag(node, context) { validate_opening_tag(node, context.state, '@'); } + // unfortunately this is necessary in order to fix invalid HTML + mark_subtree_dynamic(context.path); + context.next(); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js index 557206a22b..dcea431c1a 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js @@ -6,6 +6,7 @@ import { should_proxy } from '../../3-transform/client/utils.js'; import * as e from '../../../errors.js'; import * as w from '../../../warnings.js'; import { is_rune } from '../../../../utils.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {Identifier} node @@ -19,6 +20,8 @@ export function Identifier(node, context) { return; } + mark_subtree_dynamic(context.path); + // If we are using arguments outside of a function, then throw an error if ( node.name === 'arguments' && diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js index f7b6641c44..ca8122b162 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js @@ -1,5 +1,6 @@ /** @import { IfBlock } from '#compiler' */ /** @import { Context } from '../types' */ +import { mark_subtree_dynamic } from './shared/fragment.js'; import { validate_block_not_empty, validate_opening_tag } from './shared/utils.js'; /** @@ -22,5 +23,7 @@ export function IfBlock(node, context) { validate_opening_tag(node, context.state, expected); } + mark_subtree_dynamic(context.path); + context.next(); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/KeyBlock.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/KeyBlock.js index 618ce41493..dd42d1274e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/KeyBlock.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/KeyBlock.js @@ -1,5 +1,6 @@ /** @import { KeyBlock } from '#compiler' */ /** @import { Context } from '../types' */ +import { mark_subtree_dynamic } from './shared/fragment.js'; import { validate_block_not_empty, validate_opening_tag } from './shared/utils.js'; /** @@ -13,5 +14,7 @@ export function KeyBlock(node, context) { validate_opening_tag(node, context.state, '#'); } + mark_subtree_dynamic(context.path); + context.next(); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/OnDirective.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/OnDirective.js index 152f8301aa..102a57db2d 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/OnDirective.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/OnDirective.js @@ -1,6 +1,7 @@ /** @import { OnDirective } from '#compiler' */ /** @import { Context } from '../types' */ import * as w from '../../../warnings.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {OnDirective} node @@ -21,5 +22,7 @@ export function OnDirective(node, context) { context.state.analysis.event_directive_node ??= node; } + mark_subtree_dynamic(context.path); + context.next({ ...context.state, expression: node.metadata.expression }); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/RenderTag.js index 525eb72415..5fe2353924 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RenderTag.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RenderTag.js @@ -3,6 +3,7 @@ import { unwrap_optional } from '../../../utils/ast.js'; import * as e from '../../../errors.js'; import { validate_opening_tag } from './shared/utils.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {RenderTag} node @@ -33,5 +34,7 @@ export function RenderTag(node, context) { e.render_tag_invalid_call_expression(node); } + mark_subtree_dynamic(context.path); + context.next({ ...context.state, render_tag: node }); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/SlotElement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/SlotElement.js index 7c2c0ecf6a..25d8d5835e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/SlotElement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/SlotElement.js @@ -3,6 +3,7 @@ import { is_text_attribute } from '../../../utils/ast.js'; import * as e from '../../../errors.js'; import * as w from '../../../warnings.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {SlotElement} node @@ -13,6 +14,8 @@ export function SlotElement(node, context) { w.slot_element_deprecated(node); } + mark_subtree_dynamic(context.path); + /** @type {string} */ let name = 'default'; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/SpreadAttribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/SpreadAttribute.js index feac14bb9b..b791efdd3b 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/SpreadAttribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/SpreadAttribute.js @@ -1,10 +1,13 @@ /** @import { SpreadAttribute } from '#compiler' */ /** @import { Context } from '../types' */ +import { mark_subtree_dynamic } from './shared/fragment.js'; + /** * @param {SpreadAttribute} node * @param {Context} context */ export function SpreadAttribute(node, context) { + mark_subtree_dynamic(context.path); context.next({ ...context.state, expression: node.metadata.expression }); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/StyleDirective.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/StyleDirective.js index ca9c766e32..d79722f256 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/StyleDirective.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/StyleDirective.js @@ -2,6 +2,7 @@ /** @import { Context } from '../types' */ import * as e from '../../../errors.js'; import { get_attribute_chunks } from '../../../utils/ast.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {StyleDirective} node @@ -21,6 +22,8 @@ export function StyleDirective(node, context) { node.metadata.expression.has_state = true; } } + + mark_subtree_dynamic(context.path); } else { context.next(); diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteElement.js index bb161dcf06..0228691b3e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteElement.js @@ -4,6 +4,7 @@ import { NAMESPACE_MATHML, NAMESPACE_SVG } from '../../../../constants.js'; import { is_text_attribute } from '../../../utils/ast.js'; import { check_element } from './shared/a11y.js'; import { validate_element } from './shared/element.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {SvelteElement} node @@ -58,5 +59,7 @@ export function SvelteElement(node, context) { } } + mark_subtree_dynamic(context.path); + context.next({ ...context.state, parent_element: null }); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteHead.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteHead.js index 713f7c4ca6..93b610521a 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteHead.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteHead.js @@ -1,6 +1,7 @@ /** @import { SvelteHead } from '#compiler' */ /** @import { Context } from '../types' */ import * as e from '../../../errors.js'; +import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {SvelteHead} node @@ -11,5 +12,7 @@ export function SvelteHead(node, context) { e.svelte_head_illegal_attribute(attribute); } + mark_subtree_dynamic(context.path); + context.next(); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/UseDirective.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/UseDirective.js new file mode 100644 index 0000000000..9b2d0e599f --- /dev/null +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/UseDirective.js @@ -0,0 +1,12 @@ +/** @import { UseDirective } from '#compiler' */ +/** @import { Context } from '../types' */ +import { mark_subtree_dynamic } from './shared/fragment.js'; + +/** + * @param {UseDirective} node + * @param {Context} context + */ +export function UseDirective(node, context) { + mark_subtree_dynamic(context.path); + context.next(); +} diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js index e6bb3c067e..9c42306f69 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js @@ -8,12 +8,15 @@ import { validate_attribute_name, validate_slot_attribute } from './attribute.js'; +import { mark_subtree_dynamic } from './fragment.js'; /** * @param {Component | SvelteComponent | SvelteSelf} node * @param {Context} context */ export function visit_component(node, context) { + mark_subtree_dynamic(context.path); + for (const attribute of node.attributes) { if ( attribute.type !== 'Attribute' && diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/fragment.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/fragment.js new file mode 100644 index 0000000000..23ed36b3fb --- /dev/null +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/fragment.js @@ -0,0 +1,15 @@ +/** @import { SvelteNode } from '#compiler' */ + +/** + * @param {SvelteNode[]} path + */ +export function mark_subtree_dynamic(path) { + let i = path.length; + while (i--) { + const node = path[i]; + if (node.type === 'Fragment') { + if (node.metadata.dynamic) return; + node.metadata.dynamic = true; + } + } +} diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 890713f22a..4625195f63 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -313,9 +313,8 @@ export function RegularElement(node, context) { /** Whether or not we need to wrap the children in `{...}` to avoid declaration conflicts */ const has_declaration = node.fragment.nodes.some((node) => node.type === 'SnippetBlock'); - const child_state = has_declaration - ? { ...state, init: [], update: [], after_update: [] } - : state; + /** @type {typeof state} */ + const child_state = { ...state, init: [], update: [], after_update: [] }; for (const node of hoisted) { context.visit(node, child_state); @@ -353,6 +352,10 @@ export function RegularElement(node, context) { ...child_state.after_update ]) ); + } else if (node.fragment.metadata.dynamic) { + context.state.init.push(...child_state.init); + context.state.update.push(...child_state.update); + context.state.after_update.push(...child_state.after_update); } if (has_direction_attribute) { diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index dba086b51f..66a49f87c7 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -636,7 +636,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) { }, Fragment: (node, context) => { - const scope = context.state.scope.child(node.transparent); + const scope = context.state.scope.child(node.metadata.transparent); scopes.set(node, scope); context.next({ scope }); }, diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index 318de6953c..9971af9977 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -29,12 +29,18 @@ export interface BaseNode { export interface Fragment { type: 'Fragment'; nodes: Array; - /** - * Fragments declare their own scopes. A transparent fragment is one whose scope - * is not represented by a scope in the resulting JavaScript (e.g. an element scope), - * and should therefore delegate to parent scopes when generating unique identifiers - */ - transparent: boolean; + metadata: { + /** + * Fragments declare their own scopes. A transparent fragment is one whose scope + * is not represented by a scope in the resulting JavaScript (e.g. an element scope), + * and should therefore delegate to parent scopes when generating unique identifiers + */ + transparent: boolean; + /** + * Whether or not we need to traverse into the fragment during mount/hydrate + */ + dynamic: boolean; + }; } /** diff --git a/packages/svelte/tests/parser-modern/samples/comment-before-script/output.json b/packages/svelte/tests/parser-modern/samples/comment-before-script/output.json index 5051d9ba4d..13c099a857 100644 --- a/packages/svelte/tests/parser-modern/samples/comment-before-script/output.json +++ b/packages/svelte/tests/parser-modern/samples/comment-before-script/output.json @@ -20,8 +20,7 @@ "raw": "\n", "data": "\n" } - ], - "transparent": false + ] }, "options": null, "instance": { diff --git a/packages/svelte/tests/parser-modern/samples/css-nth-syntax/output.json b/packages/svelte/tests/parser-modern/samples/css-nth-syntax/output.json index e8f0ca2a4b..a126acb4c3 100644 --- a/packages/svelte/tests/parser-modern/samples/css-nth-syntax/output.json +++ b/packages/svelte/tests/parser-modern/samples/css-nth-syntax/output.json @@ -1108,12 +1108,10 @@ "raw": "Foo", "data": "Foo" } - ], - "transparent": true + ] } } - ], - "transparent": false + ] }, "options": null } diff --git a/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/output.json b/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/output.json index ed7bd7c8e0..e410cf2a80 100644 --- a/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/output.json +++ b/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/output.json @@ -403,8 +403,7 @@ "type": "Root", "fragment": { "type": "Fragment", - "nodes": [], - "transparent": false + "nodes": [] }, "options": null } diff --git a/packages/svelte/tests/parser-modern/samples/each-block-object-pattern/output.json b/packages/svelte/tests/parser-modern/samples/each-block-object-pattern/output.json index f89be70f77..8fc3feb916 100644 --- a/packages/svelte/tests/parser-modern/samples/each-block-object-pattern/output.json +++ b/packages/svelte/tests/parser-modern/samples/each-block-object-pattern/output.json @@ -144,8 +144,7 @@ } } } - ], - "transparent": true + ] } }, { @@ -155,8 +154,7 @@ "raw": "\n", "data": "\n" } - ], - "transparent": false + ] }, "context": { "type": "ObjectPattern", @@ -310,8 +308,7 @@ ] } } - ], - "transparent": false + ] }, "options": null } diff --git a/packages/svelte/tests/parser-modern/samples/if-block-else/output.json b/packages/svelte/tests/parser-modern/samples/if-block-else/output.json index 1193af156e..cf47e3e0bb 100644 --- a/packages/svelte/tests/parser-modern/samples/if-block-else/output.json +++ b/packages/svelte/tests/parser-modern/samples/if-block-else/output.json @@ -54,8 +54,7 @@ "raw": "foo", "data": "foo" } - ], - "transparent": true + ] } }, { @@ -65,8 +64,7 @@ "raw": "\n", "data": "\n" } - ], - "transparent": false + ] }, "alternate": { "type": "Fragment", @@ -94,8 +92,7 @@ "raw": "not foo", "data": "not foo" } - ], - "transparent": true + ] } }, { @@ -105,12 +102,10 @@ "raw": "\n", "data": "\n" } - ], - "transparent": false + ] } } - ], - "transparent": false + ] }, "options": null } diff --git a/packages/svelte/tests/parser-modern/samples/if-block-elseif/output.json b/packages/svelte/tests/parser-modern/samples/if-block-elseif/output.json index e68b154a55..b10d50c939 100644 --- a/packages/svelte/tests/parser-modern/samples/if-block-elseif/output.json +++ b/packages/svelte/tests/parser-modern/samples/if-block-elseif/output.json @@ -87,8 +87,7 @@ "raw": "x is greater than 10", "data": "x is greater than 10" } - ], - "transparent": true + ] } }, { @@ -98,8 +97,7 @@ "raw": "\n", "data": "\n" } - ], - "transparent": false + ] }, "alternate": { "type": "Fragment", @@ -184,8 +182,7 @@ "raw": "x is less than 5", "data": "x is less than 5" } - ], - "transparent": true + ] } }, { @@ -195,17 +192,14 @@ "raw": "\n", "data": "\n" } - ], - "transparent": false + ] }, "alternate": null } - ], - "transparent": false + ] } } - ], - "transparent": false + ] }, "options": null } diff --git a/packages/svelte/tests/parser-modern/samples/if-block/output.json b/packages/svelte/tests/parser-modern/samples/if-block/output.json index 965f2b5614..f363ed8274 100644 --- a/packages/svelte/tests/parser-modern/samples/if-block/output.json +++ b/packages/svelte/tests/parser-modern/samples/if-block/output.json @@ -38,13 +38,11 @@ "raw": "bar", "data": "bar" } - ], - "transparent": false + ] }, "alternate": null } - ], - "transparent": false + ] }, "options": null } diff --git a/packages/svelte/tests/parser-modern/samples/options/output.json b/packages/svelte/tests/parser-modern/samples/options/output.json index 23ccf3b2f7..27a5cfce15 100644 --- a/packages/svelte/tests/parser-modern/samples/options/output.json +++ b/packages/svelte/tests/parser-modern/samples/options/output.json @@ -21,8 +21,7 @@ "raw": "\n\n", "data": "\n\n" } - ], - "transparent": false + ] }, "options": { "start": 0, diff --git a/packages/svelte/tests/parser-modern/samples/semicolon-inside-quotes/output.json b/packages/svelte/tests/parser-modern/samples/semicolon-inside-quotes/output.json index b900bfc0b3..33dd78879c 100644 --- a/packages/svelte/tests/parser-modern/samples/semicolon-inside-quotes/output.json +++ b/packages/svelte/tests/parser-modern/samples/semicolon-inside-quotes/output.json @@ -98,8 +98,7 @@ "raw": "\n\tSemicolon inside quotes\n", "data": "\n\tSemicolon inside quotes\n" } - ], - "transparent": true + ] } }, { @@ -109,8 +108,7 @@ "raw": "\n", "data": "\n" } - ], - "transparent": false + ] }, "options": null } diff --git a/packages/svelte/tests/parser-modern/samples/snippets/output.json b/packages/svelte/tests/parser-modern/samples/snippets/output.json index 0926c2fb26..2cf2596b18 100644 --- a/packages/svelte/tests/parser-modern/samples/snippets/output.json +++ b/packages/svelte/tests/parser-modern/samples/snippets/output.json @@ -112,8 +112,7 @@ "name": "msg" } } - ], - "transparent": true + ] } }, { @@ -123,8 +122,7 @@ "raw": "\n", "data": "\n" } - ], - "transparent": false + ] } }, { @@ -189,8 +187,7 @@ "optional": false } } - ], - "transparent": false + ] }, "options": null, "instance": { diff --git a/packages/svelte/tests/parser-modern/samples/template-shadowroot/output.json b/packages/svelte/tests/parser-modern/samples/template-shadowroot/output.json index eb9ffe51de..65d716a82b 100644 --- a/packages/svelte/tests/parser-modern/samples/template-shadowroot/output.json +++ b/packages/svelte/tests/parser-modern/samples/template-shadowroot/output.json @@ -56,12 +56,10 @@ "attributes": [], "fragment": { "type": "Fragment", - "nodes": [], - "transparent": true + "nodes": [] } } - ], - "transparent": true + ] } }, { @@ -71,8 +69,7 @@ "raw": "\n", "data": "\n" } - ], - "transparent": true + ] } }, { @@ -115,12 +112,10 @@ "attributes": [], "fragment": { "type": "Fragment", - "nodes": [], - "transparent": true + "nodes": [] } } - ], - "transparent": true + ] } }, { @@ -130,8 +125,7 @@ "raw": "\n", "data": "\n" } - ], - "transparent": true + ] } }, { @@ -149,12 +143,10 @@ "attributes": [], "fragment": { "type": "Fragment", - "nodes": [], - "transparent": true + "nodes": [] } } - ], - "transparent": false + ] }, "options": null } diff --git a/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json b/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json index 117496744b..5d72e01b0a 100644 --- a/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json +++ b/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json @@ -350,12 +350,10 @@ "name": "count" } } - ], - "transparent": true + ] } } - ], - "transparent": false + ] }, "options": null, "instance": { diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js new file mode 100644 index 0000000000..20d29d5359 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js @@ -0,0 +1,10 @@ +import "svelte/internal/disclose-version"; +import * as $ from "svelte/internal/client"; + +var root = $.template(`
`); + +export default function Skip_static_subtree($$anchor) { + var header = root(); + + $.append($$anchor, header); +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js new file mode 100644 index 0000000000..77851c330e --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js @@ -0,0 +1,5 @@ +import * as $ from "svelte/internal/server"; + +export default function Skip_static_subtree($$payload) { + $$payload.out += `
`; +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/index.svelte b/packages/svelte/tests/snapshot/samples/skip-static-subtree/index.svelte new file mode 100644 index 0000000000..f5ee042585 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/index.svelte @@ -0,0 +1,6 @@ +
+ +
diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 3c0dede5b0..0895821a66 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1486,12 +1486,18 @@ declare module 'svelte/compiler' { interface Fragment { type: 'Fragment'; nodes: Array; - /** - * Fragments declare their own scopes. A transparent fragment is one whose scope - * is not represented by a scope in the resulting JavaScript (e.g. an element scope), - * and should therefore delegate to parent scopes when generating unique identifiers - */ - transparent: boolean; + metadata: { + /** + * Fragments declare their own scopes. A transparent fragment is one whose scope + * is not represented by a scope in the resulting JavaScript (e.g. an element scope), + * and should therefore delegate to parent scopes when generating unique identifiers + */ + transparent: boolean; + /** + * Whether or not we need to traverse into the fragment during mount/hydrate + */ + dynamic: boolean; + }; } /**