diff --git a/.changeset/calm-heads-glow.md b/.changeset/calm-heads-glow.md new file mode 100644 index 0000000000..ee21d566bb --- /dev/null +++ b/.changeset/calm-heads-glow.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: preserve global CSS in components without scopable elements diff --git a/.changeset/calm-render-results.md b/.changeset/calm-render-results.md new file mode 100644 index 0000000000..f3073ec4b5 --- /dev/null +++ b/.changeset/calm-render-results.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: reduce SSR render result garbage collection diff --git a/.changeset/each-fallback-scope.md b/.changeset/each-fallback-scope.md new file mode 100644 index 0000000000..5134fc462b --- /dev/null +++ b/.changeset/each-fallback-scope.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: resolve the fallback of an each block in the enclosing scope diff --git a/benchmarking/benchmarks/ssr/wrapper/wrapper_bench.js b/benchmarking/benchmarks/ssr/wrapper/wrapper_bench.js index 9a8dda617d..50634ff4e6 100644 --- a/benchmarking/benchmarks/ssr/wrapper/wrapper_bench.js +++ b/benchmarking/benchmarks/ssr/wrapper/wrapper_bench.js @@ -25,12 +25,12 @@ export const wrapper_bench = { // Do 3 loops to warm up JIT for (let i = 0; i < 3; i++) { - render(App); + render(App).body; } return await fastest_test(10, () => { for (let i = 0; i < 100; i++) { - render(App); + render(App).body; } }); } diff --git a/packages/svelte/src/compiler/phases/1-parse/index.js b/packages/svelte/src/compiler/phases/1-parse/index.js index 8edd403487..64a03630ab 100644 --- a/packages/svelte/src/compiler/phases/1-parse/index.js +++ b/packages/svelte/src/compiler/phases/1-parse/index.js @@ -5,10 +5,9 @@ import { isIdentifierStart, isIdentifierChar } from 'acorn'; import fragment from './state/fragment.js'; import * as e from '../../errors.js'; -import { create_fragment } from './utils/create.js'; +import { create_fragment, disallow_children } from '../nodes.js'; import read_options from './read/options.js'; import { is_reserved } from '../../../utils.js'; -import { disallow_children } from '../2-analyze/visitors/shared/special-element.js'; import * as state from '../../state.js'; import { is_whitespace } from './utils/whitespace.js'; diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js index 220fe24b9d..d6871bc01e 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -9,8 +9,12 @@ import read_style from '../read/style.js'; import { decode_character_references } from '../utils/html.js'; import * as e from '../../../errors.js'; import * as w from '../../../warnings.js'; -import { create_fragment } from '../utils/create.js'; -import { create_attribute, ExpressionMetadata, is_element_node } from '../../nodes.js'; +import { + create_attribute, + create_fragment, + ExpressionMetadata, + is_element_node +} from '../../nodes.js'; import { get_attribute_expression, is_expression_attribute } from '../../../utils/ast.js'; import { closing_tag_omitted } from '../../../../html-tree-validation.js'; import { list } from '../../../utils/string.js'; diff --git a/packages/svelte/src/compiler/phases/1-parse/state/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js index 9cc0a41375..97095e993a 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -3,11 +3,10 @@ /** @import { Parser } from '../index.js' */ import { walk } from 'zimmerframe'; import * as e from '../../../errors.js'; -import { ExpressionMetadata } from '../../nodes.js'; +import { create_fragment, ExpressionMetadata } from '../../nodes.js'; import { parse_expression_at, parse_statement_at } from '../acorn.js'; import read_pattern from '../read/context.js'; import read_expression, { get_loose_identifier } from '../read/expression.js'; -import { create_fragment } from '../utils/create.js'; import { find_matching_bracket, match_bracket } from '../utils/bracket.js'; const regex_whitespace_with_closing_curly_brace = /\s*}/y; diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/create.js b/packages/svelte/src/compiler/phases/1-parse/utils/create.js deleted file mode 100644 index 6030f1bd7b..0000000000 --- a/packages/svelte/src/compiler/phases/1-parse/utils/create.js +++ /dev/null @@ -1,16 +0,0 @@ -/** @import { AST } from '#compiler' */ - -/** - * @param {any} transparent - * @returns {AST.Fragment} - */ -export function create_fragment(transparent = false) { - return { - type: 'Fragment', - nodes: [], - metadata: { - transparent, - dynamic: false - } - }; -} diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index dff6bb9b5a..125f096783 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -145,6 +145,19 @@ export function prune(stylesheet, elements) { }, ComplexSelector(node) { const selectors = get_relative_selectors(node); + const rule = /** @type {Compiler.AST.CSS.Rule} */ (node.metadata.rule); + const first = selectors[0]?.selectors[0]; + const is_icss_export = + selectors.length === 1 && + selectors[0].selectors.length === 1 && + first?.type === 'PseudoClassSelector' && + first.name === 'export' && + first.args === null; + + // Global and ICSS export rules do not depend on an element in this component + if (every_is_global(selectors, 0, selectors.length, rule) || is_icss_export) { + node.metadata.used = true; + } for (const element of elements) { seen.clear(); @@ -153,12 +166,7 @@ export function prune(stylesheet, elements) { // Elements rendered through are not style-scopable. // Prevent css hash injection (class="s-...") on tags like , , + +{#each items as item} +

{item}

+{:else} + +{/each}