From 504a7536c6fd1aa2c977503d6a4e7abb2b806902 Mon Sep 17 00:00:00 2001 From: Nic Polumeyv Date: Mon, 31 Aug 2026 04:49:15 -0400 Subject: [PATCH 1/7] perf: stop scanning the whole template for every expression (#18738) Parsing a 90 KB component drops from 460 ms to 35 ms; typical components parse ~30% faster. `read_pattern` blanked out the entire template before the pattern with `replace(/[^\n]/g, ' ')` to keep positions aligned, and `acorn.parseExpressionAt` re-counted lines from the top of the file on every call. Acorn never looks before `pos`, so the real template can be the prefix, and acorn 8.18's `startLocation` takes the line we already have from the locator. The blanking also had `loc.column` off by one for every node inside a destructured pattern (noted in #18087), hence the snapshot updates. --- packages/svelte/package.json | 2 +- .../src/compiler/phases/1-parse/acorn.js | 31 ++++- .../compiler/phases/1-parse/read/context.js | 21 +-- .../src/compiler/phases/1-parse/state/tag.js | 9 +- .../each-block-destructured/output.json | 20 +-- .../output.json | 131 +++++++++--------- .../each-block-object-pattern/output.json | 131 +++++++++--------- .../samples/loose-valid-each-as/output.json | 21 +-- pnpm-lock.yaml | 47 +++---- 9 files changed, 209 insertions(+), 204 deletions(-) diff --git a/packages/svelte/package.json b/packages/svelte/package.json index b8a25432eb..73261b2864 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -175,7 +175,7 @@ "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.10", "@types/estree": "^1.0.5", - "acorn": "^8.12.1", + "acorn": "^8.18.0", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", diff --git a/packages/svelte/src/compiler/phases/1-parse/acorn.js b/packages/svelte/src/compiler/phases/1-parse/acorn.js index 070183f984..3c3ad2bc8a 100644 --- a/packages/svelte/src/compiler/phases/1-parse/acorn.js +++ b/packages/svelte/src/compiler/phases/1-parse/acorn.js @@ -5,6 +5,7 @@ import * as acorn from 'acorn'; import { walk } from 'zimmerframe'; import { tsPlugin } from '@sveltejs/acorn-typescript'; import * as e from '../../errors.js'; +import { locator } from '../../state.js'; const JSParser = acorn.Parser; const TSParser = JSParser.extend(tsPlugin()); @@ -87,7 +88,8 @@ export function parse_expression_at(parser, source, index) { sourceType: 'module', ecmaVersion: 16, locations: true, - preserveParens: true + preserveParens: true, + startLocation: start_location(parser, index) }); add_comments(ast); @@ -112,7 +114,13 @@ export function parse_statement_at(parser, source, index) { try { // This is like parseExpressionAt but for statements const p = new acorn( - { onComment, sourceType: 'module', ecmaVersion: 16, locations: true }, + { + onComment, + sourceType: 'module', + ecmaVersion: 16, + locations: true, + startLocation: start_location(parser, index) + }, source, index ); @@ -128,6 +136,25 @@ export function parse_statement_at(parser, source, index) { } } +const regex_non_lf_line_break = /\r(?!\n)|[\u2028\u2029]/; + +let last_template = ''; +let lf_only = true; + +/** + * Without `startLocation`, acorn counts the lines before `index` on every call. + * It also breaks lines on bare `\r`, `\u2028` and `\u2029`, which the locator doesn't, so those templates are left to acorn + * @param {Parser} parser + * @param {number} index + */ +function start_location(parser, index) { + if (parser.template !== last_template) { + last_template = parser.template; + lf_only = !regex_non_lf_line_break.test(last_template); + } + return lf_only ? locator(index) : undefined; +} + const regex_position_indicator = / \(\d+:\d+\)$/; /** diff --git a/packages/svelte/src/compiler/phases/1-parse/read/context.js b/packages/svelte/src/compiler/phases/1-parse/read/context.js index cdb239ef5b..821fdcf338 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/context.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/context.js @@ -2,7 +2,6 @@ /** @import { Parser } from '../index.js' */ import { match_bracket } from '../utils/bracket.js'; import { parse_expression_at, remove_parens } from '../acorn.js'; -import { regex_not_newline_characters } from '../../patterns.js'; import * as e from '../../../errors.js'; /** @@ -33,24 +32,10 @@ export default function read_pattern(parser) { i = match_bracket(parser, start); parser.index = i; - const pattern_string = parser.template.slice(start, i); - - // the length of the `space_with_newline` has to be start - 1 - // because we added a `(` in front of the pattern_string, - // which shifted the entire string to right by 1 - // so we offset it by removing 1 character in the `space_with_newline` - // to achieve that, we remove the 1st space encountered, - // so it will not affect the `column` of the node - let space_with_newline = parser.template - .slice(0, start) - .replace(regex_not_newline_characters, ' '); - const first_space = space_with_newline.indexOf(' '); - space_with_newline = - space_with_newline.slice(0, first_space) + space_with_newline.slice(first_space + 1); - + // acorn never reads before `start`, so the template itself can serve as the prefix /** @type {any} */ let expression = remove_parens( - parse_expression_at(parser, `${space_with_newline}(${pattern_string} = 1)`, start - 1) + parse_expression_at(parser, parser.template.slice(0, i) + ' = 1', start) ); expression = expression.left; @@ -80,7 +65,7 @@ function read_type_annotation(parser) { const insert = '_ as '; let a = parser.index - insert.length; const template = - parser.template.slice(0, a).replace(/[^\n]/g, ' ') + + parser.template.slice(0, a) + insert + // If this is a type annotation for a function parameter, Acorn-TS will treat subsequent // parameters as part of a sequence expression instead, and will then error on optional 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 f5314d04ed..9cc0a41375 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -488,12 +488,13 @@ function open(parser) { parser.eat(')', true); } - const prelude = parser.template.slice(0, params_start).replace(/\S/g, ' '); - const params = parser.template.slice(params_start, parser.index); - let function_expression = matched ? /** @type {ArrowFunctionExpression} */ ( - parse_expression_at(parser, prelude + `${params} => {}`, params_start) + parse_expression_at( + parser, + parser.template.slice(0, parser.index) + ' => {}', + params_start + ) ) : { params: [] }; diff --git a/packages/svelte/tests/parser-legacy/samples/each-block-destructured/output.json b/packages/svelte/tests/parser-legacy/samples/each-block-destructured/output.json index 684051e304..d87eefe38c 100644 --- a/packages/svelte/tests/parser-legacy/samples/each-block-destructured/output.json +++ b/packages/svelte/tests/parser-legacy/samples/each-block-destructured/output.json @@ -82,11 +82,11 @@ "loc": { "start": { "line": 5, - "column": 19 + "column": 18 }, "end": { "line": 5, - "column": 40 + "column": 39 } }, "elements": [ @@ -97,11 +97,11 @@ "loc": { "start": { "line": 5, - "column": 20 + "column": 19 }, "end": { "line": 5, - "column": 23 + "column": 22 } }, "name": "key" @@ -113,11 +113,11 @@ "loc": { "start": { "line": 5, - "column": 25 + "column": 24 }, "end": { "line": 5, - "column": 30 + "column": 29 } }, "name": "value" @@ -129,11 +129,11 @@ "loc": { "start": { "line": 5, - "column": 32 + "column": 31 }, "end": { "line": 5, - "column": 39 + "column": 38 } }, "argument": { @@ -143,11 +143,11 @@ "loc": { "start": { "line": 5, - "column": 35 + "column": 34 }, "end": { "line": 5, - "column": 39 + "column": 38 } }, "name": "rest" diff --git a/packages/svelte/tests/parser-modern/samples/each-block-object-pattern-special-characters/output.json b/packages/svelte/tests/parser-modern/samples/each-block-object-pattern-special-characters/output.json index 7fd5deb3ea..40127f1ef9 100644 --- a/packages/svelte/tests/parser-modern/samples/each-block-object-pattern-special-characters/output.json +++ b/packages/svelte/tests/parser-modern/samples/each-block-object-pattern-special-characters/output.json @@ -170,11 +170,11 @@ "loc": { "start": { "line": 3, - "column": 13 + "column": 12 }, "end": { "line": 3, - "column": 24 + "column": 23 } }, "properties": [ @@ -185,11 +185,11 @@ "loc": { "start": { "line": 3, - "column": 15 + "column": 14 }, "end": { "line": 3, - "column": 22 + "column": 21 } }, "method": false, @@ -202,11 +202,11 @@ "loc": { "start": { "line": 3, - "column": 15 + "column": 14 }, "end": { "line": 3, - "column": 16 + "column": 15 } }, "name": "y" @@ -218,11 +218,11 @@ "loc": { "start": { "line": 3, - "column": 15 + "column": 14 }, "end": { "line": 3, - "column": 22 + "column": 21 } }, "left": { @@ -232,11 +232,11 @@ "loc": { "start": { "line": 3, - "column": 15 + "column": 14 }, "end": { "line": 3, - "column": 16 + "column": 15 } }, "name": "y" @@ -248,11 +248,11 @@ "loc": { "start": { "line": 3, - "column": 19 + "column": 18 }, "end": { "line": 3, - "column": 22 + "column": 21 } }, "value": "{", @@ -302,11 +302,11 @@ "loc": { "start": { "line": 5, - "column": 13 + "column": 12 }, "end": { "line": 5, - "column": 24 + "column": 23 } }, "properties": [ @@ -317,11 +317,11 @@ "loc": { "start": { "line": 5, - "column": 15 + "column": 14 }, "end": { "line": 5, - "column": 22 + "column": 21 } }, "method": false, @@ -334,11 +334,11 @@ "loc": { "start": { "line": 5, - "column": 15 + "column": 14 }, "end": { "line": 5, - "column": 16 + "column": 15 } }, "name": "y" @@ -350,11 +350,11 @@ "loc": { "start": { "line": 5, - "column": 15 + "column": 14 }, "end": { "line": 5, - "column": 22 + "column": 21 } }, "left": { @@ -364,11 +364,11 @@ "loc": { "start": { "line": 5, - "column": 15 + "column": 14 }, "end": { "line": 5, - "column": 16 + "column": 15 } }, "name": "y" @@ -380,11 +380,11 @@ "loc": { "start": { "line": 5, - "column": 19 + "column": 18 }, "end": { "line": 5, - "column": 22 + "column": 21 } }, "value": "]", @@ -434,11 +434,11 @@ "loc": { "start": { "line": 7, - "column": 13 + "column": 12 }, "end": { "line": 7, - "column": 29 + "column": 28 } }, "properties": [ @@ -449,11 +449,11 @@ "loc": { "start": { "line": 7, - "column": 15 + "column": 14 }, "end": { "line": 7, - "column": 27 + "column": 26 } }, "method": false, @@ -466,11 +466,11 @@ "loc": { "start": { "line": 7, - "column": 15 + "column": 14 }, "end": { "line": 7, - "column": 16 + "column": 15 } }, "name": "y" @@ -482,11 +482,11 @@ "loc": { "start": { "line": 7, - "column": 15 + "column": 14 }, "end": { "line": 7, - "column": 27 + "column": 26 } }, "left": { @@ -496,11 +496,11 @@ "loc": { "start": { "line": 7, - "column": 15 + "column": 14 }, "end": { "line": 7, - "column": 16 + "column": 15 } }, "name": "y" @@ -512,11 +512,11 @@ "loc": { "start": { "line": 7, - "column": 19 + "column": 18 }, "end": { "line": 7, - "column": 27 + "column": 26 } }, "expressions": [ @@ -527,11 +527,11 @@ "loc": { "start": { "line": 7, - "column": 22 + "column": 21 }, "end": { "line": 7, - "column": 25 + "column": 24 } }, "expressions": [], @@ -543,11 +543,11 @@ "loc": { "start": { "line": 7, - "column": 23 + "column": 22 }, "end": { "line": 7, - "column": 24 + "column": 23 } }, "value": { @@ -567,11 +567,11 @@ "loc": { "start": { "line": 7, - "column": 20 + "column": 19 }, "end": { "line": 7, - "column": 20 + "column": 19 } }, "value": { @@ -587,11 +587,11 @@ "loc": { "start": { "line": 7, - "column": 26 + "column": 25 }, "end": { "line": 7, - "column": 26 + "column": 25 } }, "value": { @@ -646,11 +646,11 @@ "loc": { "start": { "line": 9, - "column": 13 + "column": 12 }, "end": { "line": 9, - "column": 32 + "column": 31 } }, "properties": [ @@ -661,11 +661,11 @@ "loc": { "start": { "line": 9, - "column": 15 + "column": 14 }, "end": { "line": 9, - "column": 30 + "column": 29 } }, "method": false, @@ -678,11 +678,11 @@ "loc": { "start": { "line": 9, - "column": 15 + "column": 14 }, "end": { "line": 9, - "column": 16 + "column": 15 } }, "name": "y" @@ -694,11 +694,11 @@ "loc": { "start": { "line": 9, - "column": 15 + "column": 14 }, "end": { "line": 9, - "column": 30 + "column": 29 } }, "left": { @@ -708,11 +708,11 @@ "loc": { "start": { "line": 9, - "column": 15 + "column": 14 }, "end": { "line": 9, - "column": 16 + "column": 15 } }, "name": "y" @@ -724,11 +724,11 @@ "loc": { "start": { "line": 9, - "column": 19 + "column": 18 }, "end": { "line": 9, - "column": 30 + "column": 29 } }, "expressions": [ @@ -739,11 +739,11 @@ "loc": { "start": { "line": 9, - "column": 22 + "column": 21 }, "end": { "line": 9, - "column": 28 + "column": 27 } }, "expressions": [], @@ -755,11 +755,11 @@ "loc": { "start": { "line": 9, - "column": 23 + "column": 22 }, "end": { "line": 9, - "column": 27 + "column": 26 } }, "value": { @@ -779,11 +779,11 @@ "loc": { "start": { "line": 9, - "column": 20 + "column": 19 }, "end": { "line": 9, - "column": 20 + "column": 19 } }, "value": { @@ -799,11 +799,11 @@ "loc": { "start": { "line": 9, - "column": 29 + "column": 28 }, "end": { "line": 9, - "column": 29 + "column": 28 } }, "value": { @@ -822,5 +822,6 @@ } ] }, - "options": null + "options": null, + "comments": [] } 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 7e2fe09ea2..60fb4c4c09 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 @@ -495,11 +495,11 @@ "loc": { "start": { "line": 5, - "column": 18 + "column": 17 }, "end": { "line": 5, - "column": 57 + "column": 56 } }, "properties": [ @@ -510,11 +510,11 @@ "loc": { "start": { "line": 5, - "column": 20 + "column": 19 }, "end": { "line": 5, - "column": 42 + "column": 41 } }, "method": false, @@ -527,11 +527,11 @@ "loc": { "start": { "line": 5, - "column": 20 + "column": 19 }, "end": { "line": 5, - "column": 24 + "column": 23 } }, "name": "name" @@ -543,11 +543,11 @@ "loc": { "start": { "line": 5, - "column": 20 + "column": 19 }, "end": { "line": 5, - "column": 42 + "column": 41 } }, "left": { @@ -557,11 +557,11 @@ "loc": { "start": { "line": 5, - "column": 20 + "column": 19 }, "end": { "line": 5, - "column": 24 + "column": 23 } }, "name": "name" @@ -573,11 +573,11 @@ "loc": { "start": { "line": 5, - "column": 27 + "column": 26 }, "end": { "line": 5, - "column": 42 + "column": 41 } }, "expressions": [ @@ -588,11 +588,11 @@ "loc": { "start": { "line": 5, - "column": 35 + "column": 34 }, "end": { "line": 5, - "column": 40 + "column": 39 } }, "value": "Doe", @@ -607,11 +607,11 @@ "loc": { "start": { "line": 5, - "column": 28 + "column": 27 }, "end": { "line": 5, - "column": 33 + "column": 32 } }, "value": { @@ -627,11 +627,11 @@ "loc": { "start": { "line": 5, - "column": 41 + "column": 40 }, "end": { "line": 5, - "column": 41 + "column": 40 } }, "value": { @@ -652,11 +652,11 @@ "loc": { "start": { "line": 5, - "column": 44 + "column": 43 }, "end": { "line": 5, - "column": 55 + "column": 54 } }, "method": false, @@ -669,11 +669,11 @@ "loc": { "start": { "line": 5, - "column": 44 + "column": 43 }, "end": { "line": 5, - "column": 48 + "column": 47 } }, "name": "cool" @@ -685,11 +685,11 @@ "loc": { "start": { "line": 5, - "column": 44 + "column": 43 }, "end": { "line": 5, - "column": 55 + "column": 54 } }, "left": { @@ -699,11 +699,11 @@ "loc": { "start": { "line": 5, - "column": 44 + "column": 43 }, "end": { "line": 5, - "column": 48 + "column": 47 } }, "name": "cool" @@ -715,11 +715,11 @@ "loc": { "start": { "line": 5, - "column": 51 + "column": 50 }, "end": { "line": 5, - "column": 55 + "column": 54 } }, "value": true, @@ -906,11 +906,11 @@ "loc": { "start": { "line": 9, - "column": 18 + "column": 17 }, "end": { "line": 9, - "column": 79 + "column": 78 } }, "properties": [ @@ -921,11 +921,11 @@ "loc": { "start": { "line": 9, - "column": 20 + "column": 19 }, "end": { "line": 9, - "column": 64 + "column": 63 } }, "method": false, @@ -938,11 +938,11 @@ "loc": { "start": { "line": 9, - "column": 20 + "column": 19 }, "end": { "line": 9, - "column": 24 + "column": 23 } }, "name": "name" @@ -954,11 +954,11 @@ "loc": { "start": { "line": 9, - "column": 20 + "column": 19 }, "end": { "line": 9, - "column": 64 + "column": 63 } }, "left": { @@ -968,11 +968,11 @@ "loc": { "start": { "line": 9, - "column": 20 + "column": 19 }, "end": { "line": 9, - "column": 24 + "column": 23 } }, "name": "name" @@ -984,11 +984,11 @@ "loc": { "start": { "line": 9, - "column": 27 + "column": 26 }, "end": { "line": 9, - "column": 64 + "column": 63 } }, "callee": { @@ -998,11 +998,11 @@ "loc": { "start": { "line": 9, - "column": 28 + "column": 27 }, "end": { "line": 9, - "column": 61 + "column": 60 } }, "id": null, @@ -1017,11 +1017,11 @@ "loc": { "start": { "line": 9, - "column": 34 + "column": 33 }, "end": { "line": 9, - "column": 61 + "column": 60 } }, "body": [ @@ -1032,11 +1032,11 @@ "loc": { "start": { "line": 9, - "column": 36 + "column": 35 }, "end": { "line": 9, - "column": 59 + "column": 58 } }, "argument": { @@ -1046,11 +1046,11 @@ "loc": { "start": { "line": 9, - "column": 43 + "column": 42 }, "end": { "line": 9, - "column": 58 + "column": 57 } }, "expressions": [ @@ -1061,11 +1061,11 @@ "loc": { "start": { "line": 9, - "column": 51 + "column": 50 }, "end": { "line": 9, - "column": 56 + "column": 55 } }, "value": "Doe", @@ -1080,11 +1080,11 @@ "loc": { "start": { "line": 9, - "column": 44 + "column": 43 }, "end": { "line": 9, - "column": 49 + "column": 48 } }, "value": { @@ -1100,11 +1100,11 @@ "loc": { "start": { "line": 9, - "column": 57 + "column": 56 }, "end": { "line": 9, - "column": 57 + "column": 56 } }, "value": { @@ -1132,11 +1132,11 @@ "loc": { "start": { "line": 9, - "column": 66 + "column": 65 }, "end": { "line": 9, - "column": 77 + "column": 76 } }, "method": false, @@ -1149,11 +1149,11 @@ "loc": { "start": { "line": 9, - "column": 66 + "column": 65 }, "end": { "line": 9, - "column": 70 + "column": 69 } }, "name": "cool" @@ -1165,11 +1165,11 @@ "loc": { "start": { "line": 9, - "column": 66 + "column": 65 }, "end": { "line": 9, - "column": 77 + "column": 76 } }, "left": { @@ -1179,11 +1179,11 @@ "loc": { "start": { "line": 9, - "column": 66 + "column": 65 }, "end": { "line": 9, - "column": 70 + "column": 69 } }, "name": "cool" @@ -1195,11 +1195,11 @@ "loc": { "start": { "line": 9, - "column": 73 + "column": 72 }, "end": { "line": 9, - "column": 77 + "column": 76 } }, "value": true, @@ -1213,5 +1213,6 @@ } ] }, - "options": null + "options": null, + "comments": [] } diff --git a/packages/svelte/tests/parser-modern/samples/loose-valid-each-as/output.json b/packages/svelte/tests/parser-modern/samples/loose-valid-each-as/output.json index 441cf71519..a4181faf3e 100644 --- a/packages/svelte/tests/parser-modern/samples/loose-valid-each-as/output.json +++ b/packages/svelte/tests/parser-modern/samples/loose-valid-each-as/output.json @@ -133,11 +133,11 @@ "loc": { "start": { "line": 5, - "column": 15 + "column": 14 }, "end": { "line": 5, - "column": 39 + "column": 38 } }, "elements": [ @@ -148,11 +148,11 @@ "loc": { "start": { "line": 5, - "column": 16 + "column": 15 }, "end": { "line": 5, - "column": 19 + "column": 18 } }, "name": "key" @@ -164,11 +164,11 @@ "loc": { "start": { "line": 5, - "column": 21 + "column": 20 }, "end": { "line": 5, - "column": 38 + "column": 37 } }, "left": { @@ -178,11 +178,11 @@ "loc": { "start": { "line": 5, - "column": 21 + "column": 20 }, "end": { "line": 5, - "column": 26 + "column": 25 } }, "name": "value" @@ -194,11 +194,11 @@ "loc": { "start": { "line": 5, - "column": 29 + "column": 28 }, "end": { "line": 5, - "column": 38 + "column": 37 } }, "value": "default", @@ -211,6 +211,7 @@ ] }, "options": null, + "comments": [], "instance": { "type": "Script", "start": 0, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c51620fff5..f9897fb4b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -76,13 +76,13 @@ importers: version: 1.5.0 '@sveltejs/acorn-typescript': specifier: ^1.0.10 - version: 1.0.10(acorn@8.16.0) + version: 1.0.10(acorn@8.18.0) '@types/estree': specifier: ^1.0.5 version: 1.0.8 acorn: - specifier: ^8.12.1 - version: 8.16.0 + specifier: ^8.18.0 + version: 8.18.0 aria-query: specifier: 5.3.1 version: 5.3.1 @@ -1241,13 +1241,8 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.16.0: - resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} - engines: {node: '>=0.4.0'} - hasBin: true - - acorn@8.17.0: - resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} engines: {node: '>=0.4.0'} hasBin: true @@ -3302,15 +3297,15 @@ snapshots: '@stylistic/eslint-plugin-js@1.8.0(eslint@10.0.0)': dependencies: '@types/eslint': 8.56.12 - acorn: 8.17.0 + acorn: 8.18.0 escape-string-regexp: 4.0.0 eslint: 10.0.0 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - '@sveltejs/acorn-typescript@1.0.10(acorn@8.16.0)': + '@sveltejs/acorn-typescript@1.0.10(acorn@8.18.0)': dependencies: - acorn: 8.16.0 + acorn: 8.18.0 '@sveltejs/eslint-config@9.0.0(@eslint/js@10.0.1(eslint@10.0.0))(@stylistic/eslint-plugin-js@1.8.0(eslint@10.0.0))(eslint-config-prettier@9.1.0(eslint@10.0.0))(eslint-plugin-n@17.24.0(eslint@10.0.0)(typescript@5.5.4))(eslint-plugin-svelte@3.15.0(eslint@10.0.0)(svelte@packages+svelte))(eslint@10.0.0)(typescript-eslint@8.56.0(eslint@10.0.0)(typescript@5.5.4))(typescript@5.5.4)': dependencies: @@ -3532,17 +3527,11 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - acorn-jsx@5.3.2(acorn@8.16.0): + acorn-jsx@5.3.2(acorn@8.18.0): dependencies: - acorn: 8.16.0 - - acorn-jsx@5.3.2(acorn@8.17.0): - dependencies: - acorn: 8.17.0 - - acorn@8.16.0: {} + acorn: 8.18.0 - acorn@8.17.0: {} + acorn@8.18.0: {} agent-base@7.1.1: dependencies: @@ -3898,20 +3887,20 @@ snapshots: espree@10.1.0: dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) eslint-visitor-keys: 4.2.1 espree@11.1.0: dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) eslint-visitor-keys: 5.0.0 espree@9.6.1: dependencies: - acorn: 8.17.0 - acorn-jsx: 5.3.2(acorn@8.17.0) + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -4664,7 +4653,7 @@ snapshots: terser@5.27.0: dependencies: '@jridgewell/source-map': 0.3.6 - acorn: 8.16.0 + acorn: 8.18.0 commander: 2.20.3 source-map-support: 0.5.21 From 34142af3a1125dd512bfb88b396a2e1de1dace45 Mon Sep 17 00:00:00 2001 From: Nic Polumeyv Date: Mon, 31 Aug 2026 04:49:16 -0400 Subject: [PATCH 2/7] perf: build identifier and member chain expressions without acorn (#18740) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After #18738 the biggest thing left in the parser profile was acorn's constructor, `getOptions` and `wordsRegexp` alone were around 12% of parse time. `parseExpressionAt` is `new Parser()` followed by `parseExpression()`, and we call it for every expression in the template. For something like `{name}` the constructor (normalizing options, four keyword regexes, scope state) is about 2 µs while the actual parse is under 1 µs, and components have dozens of these. The fix seemed to belong in acorn, cache the options and regexes so repeated `parseExpressionAt` calls stop redoing them. I lasted about twenty minutes in acorn's constructor... I was way in over my head. The Svelte alternative was one parser per component, reset between expressions, but that means re-initializing acorn's and acorn-typescript's internals by hand and breaking whenever either adds a field. I realized most of the expressions in the test corpus are an identifier or an `a.b.c` chain, and you don't need a JS parser for those. So that's this fix. `read_expression` now scans for that shape, builds the nodes itself in the form acorn would, and hands everything else to acorn like before.No behavior change, verified byte-for-byte against acorn on the test corpus. About 18% faster parsing on typical components on top of #18738. I guess this should be a fix for acorn, rather than here, so I'm not against closing this. --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> Co-authored-by: Simon Holthausen --- .changeset/easy-points-tan.md | 5 + .../src/compiler/phases/1-parse/acorn.js | 13 +- .../phases/1-parse/read/expression.js | 112 +++++++++++++++++- 3 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 .changeset/easy-points-tan.md diff --git a/.changeset/easy-points-tan.md b/.changeset/easy-points-tan.md new file mode 100644 index 0000000000..60edbf94eb --- /dev/null +++ b/.changeset/easy-points-tan.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +perf: speed up parser interactions with Acorn or avoid them where possible diff --git a/packages/svelte/src/compiler/phases/1-parse/acorn.js b/packages/svelte/src/compiler/phases/1-parse/acorn.js index 3c3ad2bc8a..add61ea19f 100644 --- a/packages/svelte/src/compiler/phases/1-parse/acorn.js +++ b/packages/svelte/src/compiler/phases/1-parse/acorn.js @@ -142,17 +142,24 @@ let last_template = ''; let lf_only = true; /** - * Without `startLocation`, acorn counts the lines before `index` on every call. - * It also breaks lines on bare `\r`, `\u2028` and `\u2029`, which the locator doesn't, so those templates are left to acorn + * Without `startLocation`, acorn counts the lines before `index` on every call * @param {Parser} parser * @param {number} index */ function start_location(parser, index) { + return has_lf_line_breaks_only(parser) ? locator(index) : undefined; +} + +/** + * acorn breaks lines on bare `\r`, `\u2028` and `\u2029`, which the locator doesn't + * @param {Parser} parser + */ +export function has_lf_line_breaks_only(parser) { if (parser.template !== last_template) { last_template = parser.template; lf_only = !regex_non_lf_line_break.test(last_template); } - return lf_only ? locator(index) : undefined; + return lf_only; } const regex_position_indicator = / \(\d+:\d+\)$/; diff --git a/packages/svelte/src/compiler/phases/1-parse/read/expression.js b/packages/svelte/src/compiler/phases/1-parse/read/expression.js index 1c8f097c2f..b63ad443a8 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/expression.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/expression.js @@ -1,9 +1,13 @@ -/** @import { Expression } from 'estree' */ +/** @import { Expression, Identifier } from 'estree' */ /** @import { Parser } from '../index.js' */ -import { parse_expression_at, remove_parens } from '../acorn.js'; +// @ts-expect-error acorn type definitions are borked in the release we use +import { isIdentifierStart, isIdentifierChar } from 'acorn'; +import { has_lf_line_breaks_only, parse_expression_at, remove_parens } from '../acorn.js'; import { regex_whitespace } from '../../patterns.js'; import * as e from '../../../errors.js'; import { find_matching_bracket } from '../utils/bracket.js'; +import { is_reserved } from '../../../../utils.js'; +import { locator } from '../../../state.js'; /** * @param {Parser} parser @@ -33,6 +37,9 @@ export function get_loose_identifier(parser, opening_token) { * @returns {Expression} */ export default function read_expression(parser, opening_token, disallow_loose) { + const simple = read_simple_expression(parser); + if (simple) return simple; + try { const node = parse_expression_at(parser, parser.template, parser.index); @@ -57,3 +64,104 @@ export default function read_expression(parser, opening_token, disallow_loose) { throw err; } } + +/** + * Most template expressions are an identifier or a `a.b.c` member chain followed by `}`. + * Those are built directly for better parse performance, with the same shape acorn would produce; anything else goes to acorn + * @param {Parser} parser + * @returns {Expression | null} + */ +function read_simple_expression(parser) { + if (!has_lf_line_breaks_only(parser)) return null; + + const template = parser.template; + const index = parser.index; + + parser.allow_whitespace(); + const start = parser.index; + + let end = read_word(template, start); + if (end === -1 || is_reserved(template.slice(start, end))) { + parser.index = index; + return null; + } + + /** @type {Expression} */ + let node = identifier(template, start, end); + + while (template[end] === '.') { + const property_end = read_word(template, end + 1); + if (property_end === -1) { + parser.index = index; + return null; + } + + node = { + type: 'MemberExpression', + start, + end: property_end, + loc: { start: position(start), end: position(property_end) }, + object: node, + property: identifier(template, end + 1, property_end), + computed: false, + optional: false + }; + + end = property_end; + } + + parser.index = end; + parser.allow_whitespace(); + + if (!parser.match('}')) { + parser.index = index; + return null; + } + + parser.index = end; + return node; +} + +/** + * @param {string} template + * @param {number} start + * @returns {number} the end of the identifier starting at `start`, or -1 + */ +function read_word(template, start) { + if (start >= template.length) return -1; + + const code = /** @type {number} */ (template.codePointAt(start)); + if (!isIdentifierStart(code, true)) return -1; + + let end = start + (code <= 0xffff ? 1 : 2); + + while (end < template.length) { + const code = /** @type {number} */ (template.codePointAt(end)); + if (!isIdentifierChar(code, true)) break; + end += code <= 0xffff ? 1 : 2; + } + + return end; +} + +/** + * @param {string} template + * @param {number} start + * @param {number} end + * @returns {Identifier} + */ +function identifier(template, start, end) { + return { + type: 'Identifier', + start, + end, + loc: { start: position(start), end: position(end) }, + name: template.slice(start, end) + }; +} + +/** @param {number} index */ +function position(index) { + const { line, column } = locator(index); + return { line, column }; +} From b1142efe966cc7091fb6d79b1756e79ad97b4f59 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:19:55 +0200 Subject: [PATCH 3/7] perf: speed up parser a little bit (#18736) ~5% in local benchmarks by avoiding regex in some places --- .changeset/tall-kids-juggle.md | 5 + .../src/compiler/phases/1-parse/index.js | 42 ++++---- .../compiler/phases/1-parse/read/script.js | 2 +- .../src/compiler/phases/1-parse/read/style.js | 8 +- .../compiler/phases/1-parse/state/element.js | 100 +++++++++++------- .../src/compiler/phases/1-parse/utils/html.js | 2 + .../phases/1-parse/utils/whitespace.js | 18 ++++ packages/svelte/src/compiler/state.js | 14 ++- .../src/compiler/utils/compile_diagnostic.js | 2 +- 9 files changed, 123 insertions(+), 70 deletions(-) create mode 100644 .changeset/tall-kids-juggle.md create mode 100644 packages/svelte/src/compiler/phases/1-parse/utils/whitespace.js diff --git a/.changeset/tall-kids-juggle.md b/.changeset/tall-kids-juggle.md new file mode 100644 index 0000000000..f116590a28 --- /dev/null +++ b/.changeset/tall-kids-juggle.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +perf: avoid regex matching in parser where possible diff --git a/packages/svelte/src/compiler/phases/1-parse/index.js b/packages/svelte/src/compiler/phases/1-parse/index.js index fa7b292640..8edd403487 100644 --- a/packages/svelte/src/compiler/phases/1-parse/index.js +++ b/packages/svelte/src/compiler/phases/1-parse/index.js @@ -10,25 +10,7 @@ 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'; - -/** @param {number} cc */ -function is_whitespace(cc) { - // fast path for common whitespace - if (cc === 32 || (cc <= 13 && cc >= 9)) return true; - // rare whitespace — \u00a0, \u1680, \u2000-\u200a, \u2028, \u2029, \u202f, \u205f, \u3000, \ufeff - if (cc < 160) return false; - return ( - cc === 160 || - cc === 5760 || - (cc >= 8192 && cc <= 8202) || - cc === 8232 || - cc === 8233 || - cc === 8239 || - cc === 8287 || - cc === 12288 || - cc === 65279 - ); -} +import { is_whitespace } from './utils/whitespace.js'; const regex_lang_attribute = /|]*|(?:[^=>'"/]+=(?:"[^"]*"|'[^']*'|[^>\s]+)\s+)*)lang=(["'])?([^"' >]+)\1[^>]*>/g; @@ -278,8 +260,27 @@ export class Parser { }; } + /** @param {string} delimiter */ + read_until(delimiter) { + if (this.index >= this.template.length) { + if (this.loose) return ''; + e.unexpected_eof(this.template.length); + } + + const start = this.index; + const index = this.template.indexOf(delimiter, start); + + if (index !== -1) { + this.index = index; + return this.template.slice(start, this.index); + } + + this.index = this.template.length; + return this.template.slice(start); + } + /** @param {RegExp} pattern */ - read_until(pattern) { + read_until_regex(pattern) { if (this.index >= this.template.length) { if (this.loose) return ''; e.unexpected_eof(this.template.length); @@ -302,6 +303,7 @@ export class Parser { e.expected_whitespace(this.index); } + this.index++; this.allow_whitespace(); } diff --git a/packages/svelte/src/compiler/phases/1-parse/read/script.js b/packages/svelte/src/compiler/phases/1-parse/read/script.js index 4472ce61c3..48fb540f9c 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/script.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/script.js @@ -22,7 +22,7 @@ const ALLOWED_ATTRIBUTES = ['context', 'generics', 'lang', 'module']; */ export function read_script(parser, start, attributes) { const script_start = parser.index; - const data = parser.read_until(regex_closing_script_tag); + const data = parser.read_until_regex(regex_closing_script_tag); if (parser.index >= parser.template.length) { e.element_unclosed(parser.template.length, 'script'); } diff --git a/packages/svelte/src/compiler/phases/1-parse/read/style.js b/packages/svelte/src/compiler/phases/1-parse/read/style.js index df036d0d33..574c353adb 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/style.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js @@ -16,8 +16,6 @@ const REGEX_WHITESPACE_OR_COLON = /[\s:]/; const REGEX_LEADING_HYPHEN_OR_DIGIT = /-?\d/y; const REGEX_VALID_IDENTIFIER_CHAR = /[a-zA-Z0-9_-]/; const REGEX_UNICODE_SEQUENCE = /\\[0-9a-fA-F]{1,6}(\r\n|\s)?/y; -const REGEX_COMMENT_CLOSE = /\*\//; -const REGEX_HTML_COMMENT_CLOSE = /-->/; /** * @param {Parser} parser @@ -478,7 +476,7 @@ function read_block_item(parser) { function read_declaration(parser) { const start = parser.index; - const property = parser.read_until(REGEX_WHITESPACE_OR_COLON); + const property = parser.read_until_regex(REGEX_WHITESPACE_OR_COLON); parser.allow_whitespace(); parser.eat(':'); let index = parser.index; @@ -661,7 +659,7 @@ function allow_comment_or_whitespace(parser, capture_comments = true) { } if (parser.eat(''); parser.eat('-->', true); } @@ -676,7 +674,7 @@ function allow_comment_or_whitespace(parser, capture_comments = true) { function read_comment(parser) { const start = parser.index; parser.eat('/*', true); - const value = parser.read_until(REGEX_COMMENT_CLOSE); + const value = parser.read_until('*/'); parser.eat('*/', true); const end = parser.index; 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 a14dd167a5..220fe24b9d 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -15,13 +15,10 @@ import { get_attribute_expression, is_expression_attribute } from '../../../util import { closing_tag_omitted } from '../../../../html-tree-validation.js'; import { list } from '../../../utils/string.js'; import { locator } from '../../../state.js'; -import * as b from '#compiler/builders'; +import { is_whitespace } from '../utils/whitespace.js'; const regex_invalid_unquoted_attribute_value = /(\/>|[\s"'=<>`])/y; const regex_closing_textarea_tag = /<\/textarea(\s[^>]*)?>/iy; -const regex_closing_comment = /-->/; -const regex_whitespace_or_slash_or_closing_tag = /(\s|\/|>)/; -const regex_token_ending_character = /[\s=/>"']/; const regex_starts_with_quote_characters = /["']/y; const regex_attribute_value = /(?:"([^"]*)"|'([^'])*'|([^>\s]+))/y; const regex_doctype_name = /^![a-zA-Z]+$/; @@ -67,7 +64,7 @@ export default function element(parser) { let parent = parser.current(); if (parser.eat('!--')) { - const data = parser.read_until(regex_closing_comment); + const data = parser.read_until('-->'); parser.eat('-->', true); parser.append({ @@ -81,7 +78,7 @@ export default function element(parser) { } if (parser.eat('/')) { - const name = parser.read_until(regex_whitespace_or_slash_or_closing_tag); + const name = read_tag_name(parser); parser.allow_whitespace(); parser.eat('>', true); @@ -137,7 +134,7 @@ export default function element(parser) { return; } - const tag = read_tag(parser, regex_whitespace_or_slash_or_closing_tag); + const tag = read_tag(parser); if (tag.name.startsWith('svelte:') && !meta_tags.has(tag.name)) { const bounds = { start: start + 1, end: start + 1 + tag.name.length }; @@ -475,7 +472,7 @@ function parent_is_shadowroot_template(stack) { function read_static_attribute(parser) { const start = parser.index; - const tag = read_tag(parser, regex_token_ending_character); + const tag = read_tag(parser, true); if (!tag.name) return null; /** @type {true | Array} */ @@ -607,7 +604,7 @@ function read_attribute(parser) { } } - const tag = read_tag(parser, regex_token_ending_character); + const tag = read_tag(parser, true); if (!tag.name) return null; @@ -731,7 +728,7 @@ function read_comment(parser) { const start = parser.index; if (parser.eat('//')) { - const value = parser.read_until(/\n/); + const value = parser.read_until('\n'); const end = parser.index; return { @@ -747,7 +744,7 @@ function read_comment(parser) { } if (parser.eat('/*')) { - const value = parser.read_until(/\*\//); + const value = parser.read_until('*/'); parser.eat('*/'); const end = parser.index; @@ -847,25 +844,21 @@ function read_attribute_value(parser) { * @returns {any[]} */ function read_sequence(parser, done, location) { - /** @type {AST.Text} */ - let current_chunk = { - start: parser.index, - end: -1, - type: 'Text', - raw: '', - data: '' - }; - /** @type {Array} */ const chunks = []; + let chunk_start = parser.index; /** @param {number} end */ function flush(end) { - if (end > current_chunk.start) { - current_chunk.raw = parser.template.slice(current_chunk.start, end); - current_chunk.data = decode_character_references(current_chunk.raw, true); - current_chunk.end = end; - chunks.push(current_chunk); + if (end > chunk_start) { + const raw = parser.template.slice(chunk_start, end); + chunks.push({ + start: chunk_start, + end, + type: 'Text', + raw, + data: decode_character_references(raw, true) + }); } } @@ -879,12 +872,14 @@ function read_sequence(parser, done, location) { if (parser.match('#')) { const index = parser.index - 1; parser.eat('#'); - const name = parser.read_until(/[^a-z]/); + // const name = parser.read_until_regex(/[^a-z]/); + const name = read_lowercase_name(parser); e.block_invalid_placement(index, name, location); } else if (parser.match('@')) { const index = parser.index - 1; parser.eat('@'); - const name = parser.read_until(/[^a-z]/); + // const name = parser.read_until_regex(/[^a-z]/); + const name = read_lowercase_name(parser); e.tag_invalid_placement(index, name, location); } @@ -907,14 +902,7 @@ function read_sequence(parser, done, location) { }; chunks.push(chunk); - - current_chunk = { - start: parser.index, - end: -1, - type: 'Text', - raw: '', - data: '' - }; + chunk_start = parser.index; } else { parser.index++; } @@ -929,12 +917,36 @@ function read_sequence(parser, done, location) { /** * @param {Parser} parser - * @param {RegExp} regex + * @param {boolean} [attribute] + */ +function read_tag_name(parser, attribute = false) { + const start = parser.index; + if (start >= parser.template.length && !parser.loose) e.unexpected_eof(parser.template.length); + + while (parser.index < parser.template.length) { + const cc = parser.template.charCodeAt(parser.index); + if ( + is_whitespace(cc) || + cc === 47 || // / + cc === 62 || // > + (attribute && (cc === 34 || cc === 39 || cc === 61)) // " ' = + ) { + break; + } + parser.index += 1; + } + + return parser.template.slice(start, parser.index); +} + +/** + * @param {Parser} parser + * @param {boolean} [attribute] * @returns {Identifier & { start: number, end: number, loc: SourceLocation }} */ -function read_tag(parser, regex) { +function read_tag(parser, attribute = false) { const start = parser.index; - const name = parser.read_until(regex); + const name = read_tag_name(parser, attribute); const end = parser.index; return { @@ -948,3 +960,15 @@ function read_tag(parser, regex) { } }; } + +/** @param {Parser} parser */ +function read_lowercase_name(parser) { + const start = parser.index; + while (parser.index < parser.template.length) { + const cc = parser.template.charCodeAt(parser.index); + // a-z + if (cc < 97 || cc > 122) break; + parser.index += 1; + } + return parser.template.slice(start, parser.index); +} diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/html.js b/packages/svelte/src/compiler/phases/1-parse/utils/html.js index ccb2005054..dd0b0224a6 100644 --- a/packages/svelte/src/compiler/phases/1-parse/utils/html.js +++ b/packages/svelte/src/compiler/phases/1-parse/utils/html.js @@ -38,6 +38,8 @@ const entity_pattern_attr_value = get_entity_pattern(true); * @param {boolean} is_attribute_value */ export function decode_character_references(html, is_attribute_value) { + if (html.indexOf('&') === -1) return html; // fast path + const entity_pattern = is_attribute_value ? entity_pattern_attr_value : entity_pattern_content; return html.replace( entity_pattern, diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/whitespace.js b/packages/svelte/src/compiler/phases/1-parse/utils/whitespace.js new file mode 100644 index 0000000000..b9a7efc9a3 --- /dev/null +++ b/packages/svelte/src/compiler/phases/1-parse/utils/whitespace.js @@ -0,0 +1,18 @@ +/** @param {number} cc */ +export function is_whitespace(cc) { + // fast path for common whitespace + if (cc === 32 || (cc <= 13 && cc >= 9)) return true; + // rare whitespace — \u00a0, \u1680, \u2000-\u200a, \u2028, \u2029, \u202f, \u205f, \u3000, \ufeff + if (cc < 160) return false; + return ( + cc === 160 || + cc === 5760 || + (cc >= 8192 && cc <= 8202) || + cc === 8232 || + cc === 8233 || + cc === 8239 || + cc === 8287 || + cc === 12288 || + cc === 65279 + ); +} diff --git a/packages/svelte/src/compiler/state.js b/packages/svelte/src/compiler/state.js index 5ae001ec50..51a02a61e1 100644 --- a/packages/svelte/src/compiler/state.js +++ b/packages/svelte/src/compiler/state.js @@ -33,10 +33,14 @@ export let component_name = ''; export let source; /** - * The source code split into lines (set by `set_source`) - * @type {string[]} + * The source code split into lines, initialized when a diagnostic needs a code frame + * @type {string[] | undefined} */ -export let source_lines = []; +let source_lines; + +export function get_source_lines() { + return (source_lines ??= source.split('\n')); +} /** * True if compiling with `dev: true` @@ -52,7 +56,7 @@ export let locator; /** @param {string} value */ export function set_source(value) { source = value; - source_lines = source.split('\n'); + source_lines = undefined; const l = getLocator(source, { offsetLine: 1 }); @@ -141,7 +145,7 @@ export function reset(state) { runes = false; component_name = UNKNOWN_FILENAME; source = ''; - source_lines = []; + source_lines = undefined; filename = (state.filename ?? UNKNOWN_FILENAME).replace(/\\/g, '/'); warning_filter = state.warning ?? (() => true); warnings = []; diff --git a/packages/svelte/src/compiler/utils/compile_diagnostic.js b/packages/svelte/src/compiler/utils/compile_diagnostic.js index 95d028ee35..ab0732c958 100644 --- a/packages/svelte/src/compiler/utils/compile_diagnostic.js +++ b/packages/svelte/src/compiler/utils/compile_diagnostic.js @@ -15,7 +15,7 @@ function tabs_to_spaces(str) { * @param {number} column */ function get_code_frame(line, column) { - const lines = state.source_lines; + const lines = state.get_source_lines(); const frame_start = Math.max(0, line - 2); const frame_end = Math.min(line + 3, lines.length); const digits = String(frame_end + 1).length; From b2c22ab66d0978dfa14bb92196a6ab45ad0031a4 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:25:50 +0200 Subject: [PATCH 4/7] chore: add regression test for teardown value behavior (#18751) #18746 would've introduced a bug that makes this test fail. Add it now so we don't regress in the future --- .../effect-loop-teardown-value/_config.js | 9 ++++++ .../effect-loop-teardown-value/main.svelte | 28 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-loop-teardown-value/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-loop-teardown-value/main.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/effect-loop-teardown-value/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-loop-teardown-value/_config.js new file mode 100644 index 0000000000..9dc79c3c5a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-loop-teardown-value/_config.js @@ -0,0 +1,9 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, target, logs }) { + flushSync(() => target.querySelector('button')?.click()); + assert.deepEqual(logs, ['setup: two', 'cleanup: two']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/effect-loop-teardown-value/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-loop-teardown-value/main.svelte new file mode 100644 index 0000000000..b57cfd65e0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-loop-teardown-value/main.svelte @@ -0,0 +1,28 @@ + + + From b580455c9cf5c903c1726866e6808b81332480fc Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:58:37 +0200 Subject: [PATCH 5/7] fix: serialize input default values during SSR (#18733) Alternative to #18452 --------- Co-authored-by: svelte-triage-bot --- .changeset/green-inputs-ssr.md | 5 +++++ .../3-transform/server/visitors/shared/element.js | 8 ++++++++ packages/svelte/src/internal/server/index.js | 11 +++++++++-- .../samples/input-default-value/_config.js | 3 +++ .../samples/input-default-value/_expected.html | 6 ++++++ .../samples/input-default-value/main.svelte | 10 ++++++++++ 6 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 .changeset/green-inputs-ssr.md create mode 100644 packages/svelte/tests/server-side-rendering/samples/input-default-value/_config.js create mode 100644 packages/svelte/tests/server-side-rendering/samples/input-default-value/_expected.html create mode 100644 packages/svelte/tests/server-side-rendering/samples/input-default-value/main.svelte diff --git a/.changeset/green-inputs-ssr.md b/.changeset/green-inputs-ssr.md new file mode 100644 index 0000000000..e034d0e79a --- /dev/null +++ b/.changeset/green-inputs-ssr.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: serialize input default values during server rendering diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js index b4f4facb5d..c19d760f48 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js @@ -80,6 +80,14 @@ export function build_element_attributes(node, context, transform) { ) { events_to_capture.add(attribute.name); } + } else if ( + node.type === 'RegularElement' && + node.name === 'input' && + (attribute.name === 'defaultValue' || attribute.name === 'defaultChecked') + ) { + attributes.push(attribute); + // deopt to spread at runtime, where we can handle interaction of value/defaultValue etc + has_spread = true; // the defaultValue/defaultChecked properties don't exist as attributes } else if (attribute.name !== 'defaultValue' && attribute.name !== 'defaultChecked') { if (attribute.name === 'class') { diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 3d8ec5fe5e..20bef78306 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -146,8 +146,10 @@ export function attributes(attrs, css_hash, classes, styles, flags = 0) { const is_html = (flags & ELEMENT_IS_NAMESPACED) === 0; const lowercase = (flags & ELEMENT_PRESERVE_ATTRIBUTE_CASE) === 0; const is_input = (flags & ELEMENT_IS_INPUT) !== 0; + const names = Object.keys(attrs); - for (name of Object.keys(attrs)) { + outer: for (let i = 0; i < names.length; i++) { + name = names[i]; // omit functions, internal svelte properties and invalid attribute names if (typeof attrs[name] === 'function') continue; if (name[0] === '$' && name[1] === '$') continue; // faster than name.startsWith('$$') @@ -163,8 +165,13 @@ export function attributes(attrs, css_hash, classes, styles, flags = 0) { if (is_input) { if (name === 'defaultvalue' || name === 'defaultchecked') { + // value/checked takes precedence over defaultValue/defaultChecked name = name === 'defaultvalue' ? 'value' : 'checked'; - if (attrs[name]) continue; + if (name in attrs) continue; + // We're checking prior entries aswell because "name in attrs" is not enough as the attributes may have different casing + for (let j = 0; j < names.length; j++) { + if (names[j].toLowerCase() === name) continue outer; + } } } diff --git a/packages/svelte/tests/server-side-rendering/samples/input-default-value/_config.js b/packages/svelte/tests/server-side-rendering/samples/input-default-value/_config.js new file mode 100644 index 0000000000..f47bee71df --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/input-default-value/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); diff --git a/packages/svelte/tests/server-side-rendering/samples/input-default-value/_expected.html b/packages/svelte/tests/server-side-rendering/samples/input-default-value/_expected.html new file mode 100644 index 0000000000..87635c9545 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/input-default-value/_expected.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/svelte/tests/server-side-rendering/samples/input-default-value/main.svelte b/packages/svelte/tests/server-side-rendering/samples/input-default-value/main.svelte new file mode 100644 index 0000000000..e1ad517850 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/input-default-value/main.svelte @@ -0,0 +1,10 @@ + + + + + + + + From 9d91a40fafcb126b857384fc39219d44877df631 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 31 Aug 2026 16:23:42 +0200 Subject: [PATCH 6/7] chore: bump packages (#18752) esrap bump ensures a bugfix, devalue silences vulnerability reports --- packages/svelte/package.json | 12 +- packages/svelte/src/compiler/utils/ast.js | 7 +- packages/svelte/src/internal/types.d.ts | 6 + pnpm-lock.yaml | 460 ++++------------------ 4 files changed, 94 insertions(+), 391 deletions(-) diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 73261b2864..53e5516bf6 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -171,17 +171,17 @@ "web-features": "^3.29.0" }, "dependencies": { - "@jridgewell/remapping": "^2.3.4", - "@jridgewell/sourcemap-codec": "^1.5.0", - "@sveltejs/acorn-typescript": "^1.0.10", - "@types/estree": "^1.0.5", + "@jridgewell/remapping": "^2.3.5", + "@jridgewell/sourcemap-codec": "^1.6.0", + "@sveltejs/acorn-typescript": "^1.0.13", + "@types/estree": "^1.0.9", "acorn": "^8.18.0", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", - "devalue": "^5.8.1", + "devalue": "^5.9.2", "esm-env": "^1.2.1", - "esrap": "^2.2.12", + "esrap": "^2.3.6", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", diff --git a/packages/svelte/src/compiler/utils/ast.js b/packages/svelte/src/compiler/utils/ast.js index 75aadd905b..927674bd18 100644 --- a/packages/svelte/src/compiler/utils/ast.js +++ b/packages/svelte/src/compiler/utils/ast.js @@ -286,7 +286,7 @@ function _extract_paths(paths, inserts, param, expression, update_expression, ha const props = []; for (const p of param.properties) { - if (p.type === 'Property' && p.key.type !== 'PrivateIdentifier') { + if (p.type === 'Property') { if (p.key.type === 'Identifier' && !p.computed) { props.push(b.literal(p.key.name)); } else if (p.key.type === 'Literal') { @@ -548,10 +548,7 @@ export function is_expression_async(expression) { if (property.type === 'SpreadElement') { return is_expression_async(property.argument); } else if (property.type === 'Property') { - return ( - (property.key.type !== 'PrivateIdentifier' && is_expression_async(property.key)) || - is_expression_async(property.value) - ); + return is_expression_async(property.key) || is_expression_async(property.value); } }); } diff --git a/packages/svelte/src/internal/types.d.ts b/packages/svelte/src/internal/types.d.ts index 12b2e5d4fb..2f9ff8d75b 100644 --- a/packages/svelte/src/internal/types.d.ts +++ b/packages/svelte/src/internal/types.d.ts @@ -1,2 +1,8 @@ /** Anything except a function */ export type NotFunction = T extends Function ? never : T; + +declare global { + // @ts-ignore devalue has it in its types, but it's not part of the standard lib at the version our runtime is. + // We're not actually doing anything with it so we silence the error this ay + type Float16Array = any; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9897fb4b4..e7fee7b592 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -69,17 +69,17 @@ importers: packages/svelte: dependencies: '@jridgewell/remapping': - specifier: ^2.3.4 - version: 2.3.4 + specifier: ^2.3.5 + version: 2.3.5 '@jridgewell/sourcemap-codec': - specifier: ^1.5.0 - version: 1.5.0 + specifier: ^1.6.0 + version: 1.6.0 '@sveltejs/acorn-typescript': - specifier: ^1.0.10 - version: 1.0.10(acorn@8.18.0) + specifier: ^1.0.13 + version: 1.0.13(acorn@8.18.0) '@types/estree': - specifier: ^1.0.5 - version: 1.0.8 + specifier: ^1.0.9 + version: 1.0.9 acorn: specifier: ^8.18.0 version: 8.18.0 @@ -93,14 +93,14 @@ importers: specifier: ^2.1.1 version: 2.1.1 devalue: - specifier: ^5.8.1 - version: 5.8.1 + specifier: ^5.9.2 + version: 5.9.2 esm-env: specifier: ^1.2.1 version: 1.2.1 esrap: - specifier: ^2.2.12 - version: 2.2.12(@typescript-eslint/types@8.59.4) + specifier: ^2.3.6 + version: 2.3.6(@typescript-eslint/types@8.59.4) is-reference: specifier: ^3.0.3 version: 3.0.3 @@ -109,7 +109,7 @@ importers: version: 3.0.0 magic-string: specifier: ^0.30.11 - version: 0.30.17 + version: 0.30.21 zimmerframe: specifier: ^1.1.2 version: 1.1.2 @@ -122,16 +122,16 @@ importers: version: 1.62.1 '@rollup/plugin-commonjs': specifier: ^28.0.1 - version: 28.0.1(rollup@4.60.1) + version: 28.0.1(rollup@4.62.2) '@rollup/plugin-node-resolve': specifier: ^15.3.0 - version: 15.3.0(rollup@4.60.1) + version: 15.3.0(rollup@4.62.2) '@rollup/plugin-terser': specifier: ^0.4.4 - version: 0.4.4(rollup@4.60.1) + version: 0.4.4(rollup@4.62.2) '@rollup/plugin-virtual': specifier: ^3.0.2 - version: 3.0.2(rollup@4.60.1) + version: 3.0.2(rollup@4.62.2) '@types/aria-query': specifier: ^5.0.4 version: 5.0.4 @@ -152,13 +152,13 @@ importers: version: 0.28.1 rollup: specifier: ^4.59.0 - version: 4.60.1 + version: 4.62.2 source-map: specifier: ^0.7.4 version: 0.7.4 tinyglobby: specifier: ^0.2.12 - version: 0.2.15 + version: 0.2.17 typescript: specifier: ^5.5.4 version: 5.5.4 @@ -185,7 +185,7 @@ importers: version: link:../../packages/svelte tinyglobby: specifier: ^0.2.12 - version: 0.2.15 + version: 0.2.17 vite: specifier: ^7.3.5 version: 7.3.5(@types/node@24.5.2)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) @@ -682,8 +682,8 @@ packages: resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} - '@jridgewell/remapping@2.3.4': - resolution: {integrity: sha512-aG+WvAz17rhbzhKNkSeMLgbkPPK82ovXdONvmucbGhUqcroRFLLVhoGAk4xEI17gHpXgNX3sr0/B1ybRUsbEWw==} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.1': resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} @@ -696,11 +696,8 @@ packages: '@jridgewell/source-map@0.3.6': resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} @@ -776,277 +773,139 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.60.1': - resolution: {integrity: sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==} - cpu: [arm] - os: [android] - '@rollup/rollup-android-arm-eabi@4.62.2': resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.60.1': - resolution: {integrity: sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==} - cpu: [arm64] - os: [android] - '@rollup/rollup-android-arm64@4.62.2': resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.60.1': - resolution: {integrity: sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==} - cpu: [arm64] - os: [darwin] - '@rollup/rollup-darwin-arm64@4.62.2': resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.60.1': - resolution: {integrity: sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==} - cpu: [x64] - os: [darwin] - '@rollup/rollup-darwin-x64@4.62.2': resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.60.1': - resolution: {integrity: sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==} - cpu: [arm64] - os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.62.2': resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.60.1': - resolution: {integrity: sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==} - cpu: [x64] - os: [freebsd] - '@rollup/rollup-freebsd-x64@4.62.2': resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.60.1': - resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==} - cpu: [arm] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm-gnueabihf@4.62.2': resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.60.1': - resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==} - cpu: [arm] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm-musleabihf@4.62.2': resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.60.1': - resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm64-gnu@4.62.2': resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.60.1': - resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==} - cpu: [arm64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm64-musl@4.62.2': resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.60.1': - resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==} - cpu: [loong64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-loong64-gnu@4.62.2': resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.60.1': - resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==} - cpu: [loong64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-loong64-musl@4.62.2': resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.60.1': - resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-ppc64-gnu@4.62.2': resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.60.1': - resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==} - cpu: [ppc64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-ppc64-musl@4.62.2': resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.60.1': - resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.62.2': resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.60.1': - resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==} - cpu: [riscv64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-riscv64-musl@4.62.2': resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.60.1': - resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==} - cpu: [s390x] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-s390x-gnu@4.62.2': resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.60.1': - resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==} - cpu: [x64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.62.2': resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.60.1': - resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==} - cpu: [x64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-x64-musl@4.62.2': resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.60.1': - resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==} - cpu: [x64] - os: [openbsd] - '@rollup/rollup-openbsd-x64@4.62.2': resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.60.1': - resolution: {integrity: sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==} - cpu: [arm64] - os: [openharmony] - '@rollup/rollup-openharmony-arm64@4.62.2': resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.60.1': - resolution: {integrity: sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==} - cpu: [arm64] - os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.62.2': resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.1': - resolution: {integrity: sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==} - cpu: [ia32] - os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.62.2': resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.60.1': - resolution: {integrity: sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-gnu@4.62.2': resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.1': - resolution: {integrity: sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-msvc@4.62.2': resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} cpu: [x64] @@ -1061,8 +920,8 @@ packages: peerDependencies: eslint: '>=8.40.0' - '@sveltejs/acorn-typescript@1.0.10': - resolution: {integrity: sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA==} + '@sveltejs/acorn-typescript@1.0.13': + resolution: {integrity: sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ==} peerDependencies: acorn: ^8.9.0 @@ -1108,9 +967,6 @@ packages: '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} @@ -1426,8 +1282,8 @@ packages: engines: {node: '>=0.10'} hasBin: true - devalue@5.8.1: - resolution: {integrity: sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==} + devalue@5.9.2: + resolution: {integrity: sha512-po4PAY5c53tw5XMocSnf8A/5OHhbbUftpr93aEN6BBoAdntUmK7vu7wOATqvt7cXO7m1Cl4gMVn6p7n6n4mj0w==} dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} @@ -1564,8 +1420,8 @@ packages: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} - esrap@2.2.12: - resolution: {integrity: sha512-On0QbLyaiAkVC4eXtgnXK9Kh2opit+3rcUSOc45DqJ2s/X2eXAHsGOKRSJ6IDagQEW5vPyivANfXUiqgXC67Rw==} + esrap@2.3.6: + resolution: {integrity: sha512-yc0OC12UjPqLoc+fe+v5GNs4TOjAigUw3sTikfC+xeBPGUw7gDRz3DtYaqEhxyMVJojcSWJw7jT0QWR+CbuE/A==} peerDependencies: '@typescript-eslint/types': ^8.2.0 peerDependenciesMeta: @@ -1947,9 +1803,6 @@ packages: lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -1995,11 +1848,6 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - nanoid@3.3.13: resolution: {integrity: sha512-sPdqC6ByMVVGvF1ynvvMo0/o+oD1VX7DaHhijt1bFgjvBkHBib4t49GoNDhf2NDta4oeUNlaGbSt5K7qjZ955Q==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2148,10 +1996,6 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.9: - resolution: {integrity: sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==} - engines: {node: ^10 || ^12 || >=14} - prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -2212,11 +2056,6 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.60.1: - resolution: {integrity: sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - rollup@4.62.2: resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2251,11 +2090,6 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} - hasBin: true - semver@7.8.5: resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} @@ -2365,10 +2199,6 @@ packages: resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} engines: {node: '>=18'} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} @@ -2682,7 +2512,7 @@ snapshots: outdent: 0.5.0 prettier: 2.8.8 resolve-from: 5.0.0 - semver: 7.7.4 + semver: 7.8.5 '@changesets/assemble-release-plan@6.0.9': dependencies: @@ -2691,7 +2521,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 - semver: 7.7.4 + semver: 7.8.5 '@changesets/changelog-git@0.2.1': dependencies: @@ -2729,7 +2559,7 @@ snapshots: package-manager-detector: 0.2.11 picocolors: 1.1.1 resolve-from: 5.0.0 - semver: 7.7.4 + semver: 7.8.5 spawndamnit: 3.0.1 term-size: 2.2.1 transitivePeerDependencies: @@ -2754,7 +2584,7 @@ snapshots: '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 picocolors: 1.1.1 - semver: 7.7.4 + semver: 7.8.5 '@changesets/get-github-info@1.0.0-next.4': dependencies: @@ -3040,10 +2870,10 @@ snapshots: '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/remapping@2.3.4': + '@jridgewell/remapping@2.3.5': dependencies: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.31 @@ -3057,14 +2887,12 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/sourcemap-codec@1.5.0': {} - - '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/sourcemap-codec@1.6.0': {} '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.6.0 '@manypkg/find-root@1.1.0': dependencies: @@ -3100,195 +2928,120 @@ snapshots: '@polka/url@1.0.0-next.25': {} - '@rollup/plugin-commonjs@28.0.1(rollup@4.60.1)': + '@rollup/plugin-commonjs@28.0.1(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.60.1) + '@rollup/pluginutils': 5.1.0(rollup@4.62.2) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.4) is-reference: 1.2.1 - magic-string: 0.30.17 + magic-string: 0.30.21 picomatch: 4.0.4 optionalDependencies: - rollup: 4.60.1 + rollup: 4.62.2 - '@rollup/plugin-node-resolve@15.3.0(rollup@4.60.1)': + '@rollup/plugin-node-resolve@15.3.0(rollup@4.62.2)': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.60.1) + '@rollup/pluginutils': 5.1.0(rollup@4.62.2) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.8 optionalDependencies: - rollup: 4.60.1 + rollup: 4.62.2 - '@rollup/plugin-terser@0.4.4(rollup@4.60.1)': + '@rollup/plugin-terser@0.4.4(rollup@4.62.2)': dependencies: serialize-javascript: 6.0.2 smob: 1.4.1 terser: 5.27.0 optionalDependencies: - rollup: 4.60.1 + rollup: 4.62.2 - '@rollup/plugin-virtual@3.0.2(rollup@4.60.1)': + '@rollup/plugin-virtual@3.0.2(rollup@4.62.2)': optionalDependencies: - rollup: 4.60.1 + rollup: 4.62.2 - '@rollup/pluginutils@5.1.0(rollup@4.60.1)': + '@rollup/pluginutils@5.1.0(rollup@4.62.2)': dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 2.3.2 optionalDependencies: - rollup: 4.60.1 - - '@rollup/rollup-android-arm-eabi@4.60.1': - optional: true + rollup: 4.62.2 '@rollup/rollup-android-arm-eabi@4.62.2': optional: true - '@rollup/rollup-android-arm64@4.60.1': - optional: true - '@rollup/rollup-android-arm64@4.62.2': optional: true - '@rollup/rollup-darwin-arm64@4.60.1': - optional: true - '@rollup/rollup-darwin-arm64@4.62.2': optional: true - '@rollup/rollup-darwin-x64@4.60.1': - optional: true - '@rollup/rollup-darwin-x64@4.62.2': optional: true - '@rollup/rollup-freebsd-arm64@4.60.1': - optional: true - '@rollup/rollup-freebsd-arm64@4.62.2': optional: true - '@rollup/rollup-freebsd-x64@4.60.1': - optional: true - '@rollup/rollup-freebsd-x64@4.62.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.1': - optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.62.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.1': - optional: true - '@rollup/rollup-linux-arm-musleabihf@4.62.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.1': - optional: true - '@rollup/rollup-linux-arm64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.60.1': - optional: true - '@rollup/rollup-linux-arm64-musl@4.62.2': optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.1': - optional: true - '@rollup/rollup-linux-loong64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-loong64-musl@4.60.1': - optional: true - '@rollup/rollup-linux-loong64-musl@4.62.2': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.1': - optional: true - '@rollup/rollup-linux-ppc64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.1': - optional: true - '@rollup/rollup-linux-ppc64-musl@4.62.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.1': - optional: true - '@rollup/rollup-linux-riscv64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.1': - optional: true - '@rollup/rollup-linux-riscv64-musl@4.62.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.1': - optional: true - '@rollup/rollup-linux-s390x-gnu@4.62.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.60.1': - optional: true - '@rollup/rollup-linux-x64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-x64-musl@4.60.1': - optional: true - '@rollup/rollup-linux-x64-musl@4.62.2': optional: true - '@rollup/rollup-openbsd-x64@4.60.1': - optional: true - '@rollup/rollup-openbsd-x64@4.62.2': optional: true - '@rollup/rollup-openharmony-arm64@4.60.1': - optional: true - '@rollup/rollup-openharmony-arm64@4.62.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.1': - optional: true - '@rollup/rollup-win32-arm64-msvc@4.62.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.1': - optional: true - '@rollup/rollup-win32-ia32-msvc@4.62.2': optional: true - '@rollup/rollup-win32-x64-gnu@4.60.1': - optional: true - '@rollup/rollup-win32-x64-gnu@4.62.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.60.1': - optional: true - '@rollup/rollup-win32-x64-msvc@4.62.2': optional: true @@ -3303,7 +3056,7 @@ snapshots: eslint-visitor-keys: 3.4.3 espree: 9.6.1 - '@sveltejs/acorn-typescript@1.0.10(acorn@8.18.0)': + '@sveltejs/acorn-typescript@1.0.13(acorn@8.18.0)': dependencies: acorn: 8.18.0 @@ -3333,7 +3086,7 @@ snapshots: '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.0(svelte@packages+svelte)(vite@7.3.5(@types/node@24.5.2)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@7.3.5(@types/node@24.5.2)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.4.3 deepmerge: 4.3.1 - magic-string: 0.30.17 + magic-string: 0.30.21 svelte: link:packages/svelte vite: 7.3.5(@types/node@24.5.2)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vitefu: 1.1.1(vite@7.3.5(@types/node@24.5.2)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) @@ -3356,8 +3109,6 @@ snapshots: '@types/esrecurse@4.3.1': {} - '@types/estree@1.0.8': {} - '@types/estree@1.0.9': {} '@types/json-schema@7.0.15': {} @@ -3409,7 +3160,7 @@ snapshots: '@typescript-eslint/project-service@8.56.0(typescript@5.5.4)': dependencies: '@typescript-eslint/tsconfig-utils': 8.56.0(typescript@5.5.4) - '@typescript-eslint/types': 8.56.0 + '@typescript-eslint/types': 8.59.4 debug: 4.4.3 typescript: 5.5.4 transitivePeerDependencies: @@ -3438,8 +3189,7 @@ snapshots: '@typescript-eslint/types@8.56.0': {} - '@typescript-eslint/types@8.59.4': - optional: true + '@typescript-eslint/types@8.59.4': {} '@typescript-eslint/typescript-estree@8.56.0(typescript@5.5.4)': dependencies: @@ -3449,7 +3199,7 @@ snapshots: '@typescript-eslint/visitor-keys': 8.56.0 debug: 4.4.3 minimatch: 9.0.5 - semver: 7.7.4 + semver: 7.8.5 tinyglobby: 0.2.17 ts-api-utils: 2.4.0(typescript@5.5.4) typescript: 5.5.4 @@ -3683,7 +3433,7 @@ snapshots: detect-libc@1.0.3: optional: true - devalue@5.8.1: {} + devalue@5.9.2: {} dir-glob@3.0.1: dependencies: @@ -3692,12 +3442,12 @@ snapshots: dts-buddy@0.5.5(typescript@5.5.4): dependencies: '@jridgewell/source-map': 0.3.6 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.6.0 kleur: 4.1.5 locate-character: 3.0.0 - magic-string: 0.30.17 + magic-string: 0.30.21 sade: 1.8.1 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 ts-api-utils: 1.3.0(typescript@5.5.4) typescript: 5.5.4 @@ -3815,15 +3565,15 @@ snapshots: eslint-plugin-svelte@3.15.0(eslint@10.0.0)(svelte@packages+svelte): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.0) - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.6.0 eslint: 10.0.0 esutils: 2.0.3 globals: 16.3.0 known-css-properties: 0.37.0 - postcss: 8.5.9 - postcss-load-config: 3.1.4(postcss@8.5.9) - postcss-safe-parser: 7.0.1(postcss@8.5.9) - semver: 7.7.4 + postcss: 8.5.15 + postcss-load-config: 3.1.4(postcss@8.5.15) + postcss-safe-parser: 7.0.1(postcss@8.5.15) + semver: 7.8.5 svelte-eslint-parser: 1.4.1(svelte@packages+svelte) optionalDependencies: svelte: link:packages/svelte @@ -3838,7 +3588,7 @@ snapshots: eslint-scope@9.1.0: dependencies: '@types/esrecurse': 4.3.1 - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 esrecurse: 4.3.0 estraverse: 5.3.0 @@ -3859,7 +3609,7 @@ snapshots: '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 ajv: 6.12.6 cross-spawn: 7.0.6 debug: 4.4.3 @@ -3909,9 +3659,9 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@2.2.12(@typescript-eslint/types@8.59.4): + esrap@2.3.6(@typescript-eslint/types@8.59.4): dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 optionalDependencies: '@typescript-eslint/types': 8.59.4 @@ -3925,7 +3675,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 esutils@2.0.3: {} @@ -4111,11 +3861,11 @@ snapshots: is-reference@1.2.1: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 is-reference@3.0.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 is-subdir@1.2.0: dependencies: @@ -4260,13 +4010,9 @@ snapshots: lodash.startcase@4.4.0: {} - magic-string@0.30.17: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - magic-string@0.30.21: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 magicast@0.5.3: dependencies: @@ -4276,7 +4022,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.4 + semver: 7.8.5 merge2@1.4.1: {} @@ -4305,8 +4051,6 @@ snapshots: ms@2.1.3: {} - nanoid@3.3.11: {} - nanoid@3.3.13: {} natural-compare@1.4.0: {} @@ -4403,16 +4147,16 @@ snapshots: '@polka/url': 1.0.0-next.25 trouter: 4.0.0 - postcss-load-config@3.1.4(postcss@8.5.9): + postcss-load-config@3.1.4(postcss@8.5.15): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: - postcss: 8.5.9 + postcss: 8.5.15 - postcss-safe-parser@7.0.1(postcss@8.5.9): + postcss-safe-parser@7.0.1(postcss@8.5.15): dependencies: - postcss: 8.5.9 + postcss: 8.5.15 postcss-scss@4.0.9(postcss@8.5.15): dependencies: @@ -4429,12 +4173,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.9: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - prelude-ls@1.2.1: {} prettier-plugin-svelte@3.4.0(prettier@3.2.4)(svelte@packages+svelte): @@ -4482,37 +4220,6 @@ snapshots: reusify@1.0.4: {} - rollup@4.60.1: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.1 - '@rollup/rollup-android-arm64': 4.60.1 - '@rollup/rollup-darwin-arm64': 4.60.1 - '@rollup/rollup-darwin-x64': 4.60.1 - '@rollup/rollup-freebsd-arm64': 4.60.1 - '@rollup/rollup-freebsd-x64': 4.60.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.1 - '@rollup/rollup-linux-arm-musleabihf': 4.60.1 - '@rollup/rollup-linux-arm64-gnu': 4.60.1 - '@rollup/rollup-linux-arm64-musl': 4.60.1 - '@rollup/rollup-linux-loong64-gnu': 4.60.1 - '@rollup/rollup-linux-loong64-musl': 4.60.1 - '@rollup/rollup-linux-ppc64-gnu': 4.60.1 - '@rollup/rollup-linux-ppc64-musl': 4.60.1 - '@rollup/rollup-linux-riscv64-gnu': 4.60.1 - '@rollup/rollup-linux-riscv64-musl': 4.60.1 - '@rollup/rollup-linux-s390x-gnu': 4.60.1 - '@rollup/rollup-linux-x64-gnu': 4.60.1 - '@rollup/rollup-linux-x64-musl': 4.60.1 - '@rollup/rollup-openbsd-x64': 4.60.1 - '@rollup/rollup-openharmony-arm64': 4.60.1 - '@rollup/rollup-win32-arm64-msvc': 4.60.1 - '@rollup/rollup-win32-ia32-msvc': 4.60.1 - '@rollup/rollup-win32-x64-gnu': 4.60.1 - '@rollup/rollup-win32-x64-msvc': 4.60.1 - fsevents: 2.3.3 - rollup@4.62.2: dependencies: '@types/estree': 1.0.9 @@ -4571,8 +4278,6 @@ snapshots: dependencies: xmlchars: 2.2.0 - semver@7.7.4: {} - semver@7.8.5: {} serialize-javascript@6.0.2: @@ -4661,11 +4366,6 @@ snapshots: tinyexec@1.1.2: {} - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.4) @@ -4830,7 +4530,7 @@ snapshots: std-env: 4.1.0 tinybench: 2.9.0 tinyexec: 1.1.2 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 tinyrainbow: 3.1.0 vite: 7.3.5(@types/node@20.19.17)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) why-is-node-running: 2.3.0 From 4bf15ae6f304b9aa49a4396c3f51dc423d0722dc Mon Sep 17 00:00:00 2001 From: Bao Nguyen <39545125+giaBaoJS@users.noreply.github.com> Date: Tue, 1 Sep 2026 03:23:00 +0700 Subject: [PATCH 7/7] fix: throw when `setContext` is called after an `await` during SSR (#18739) Fixes #17233. --- .changeset/olive-mice-argue.md | 5 +++++ .../98-reference/.generated/client-errors.md | 8 -------- .../98-reference/.generated/shared-errors.md | 8 ++++++++ packages/svelte/messages/client-errors/errors.md | 6 ------ packages/svelte/messages/shared-errors/errors.md | 6 ++++++ packages/svelte/src/internal/client/errors.js | 16 ---------------- packages/svelte/src/internal/server/context.js | 11 +++++++++-- packages/svelte/src/internal/server/renderer.js | 13 ++++++++++--- packages/svelte/src/internal/server/types.d.ts | 2 ++ packages/svelte/src/internal/shared/errors.js | 16 ++++++++++++++++ .../async-context-throws-after-await/_config.js | 3 +-- 11 files changed, 57 insertions(+), 37 deletions(-) create mode 100644 .changeset/olive-mice-argue.md diff --git a/.changeset/olive-mice-argue.md b/.changeset/olive-mice-argue.md new file mode 100644 index 0000000000..6d749bcc3b --- /dev/null +++ b/.changeset/olive-mice-argue.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: throw `set_context_after_init` when `setContext` is called after an `await` during SSR diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index 2ab442afc3..3f37be269d 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -225,14 +225,6 @@ Rest element properties of `$props()` such as `%property%` are readonly The `%rune%` rune is only available inside `.svelte` and `.svelte.js/ts` files ``` -### set_context_after_init - -``` -`setContext` must be called when a component first initializes, not in a subsequent effect or after an `await` expression -``` - -This restriction only applies when using the `experimental.async` option, which will be active by default in Svelte 6. - ### state_descriptors_fixed ``` diff --git a/documentation/docs/98-reference/.generated/shared-errors.md b/documentation/docs/98-reference/.generated/shared-errors.md index 44616d7c8d..db8c45c0c7 100644 --- a/documentation/docs/98-reference/.generated/shared-errors.md +++ b/documentation/docs/98-reference/.generated/shared-errors.md @@ -80,6 +80,14 @@ Context was not set in the current component or any of its ancestors The [`createContext()`](svelte#createContext) utility returns a `[get, set, has]` triplet of functions. `get` will throw an error if `set` was not used to set the context in the current component or any of its ancestors. +### set_context_after_init + +``` +`setContext` must be called when a component first initializes, not in a subsequent effect or after an `await` expression +``` + +This restriction only applies when using the `experimental.async` option, which will be active by default in Svelte 6. + ### snippet_without_render_tag ``` diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index 3b00d1516b..85bdd68010 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -171,12 +171,6 @@ This can happen if you render a hydratable on the client that was not rendered o > The `%rune%` rune is only available inside `.svelte` and `.svelte.js/ts` files -## set_context_after_init - -> `setContext` must be called when a component first initializes, not in a subsequent effect or after an `await` expression - -This restriction only applies when using the `experimental.async` option, which will be active by default in Svelte 6. - ## state_descriptors_fixed > Property descriptors defined on `$state` objects must contain `value` and always be `enumerable`, `configurable` and `writable`. diff --git a/packages/svelte/messages/shared-errors/errors.md b/packages/svelte/messages/shared-errors/errors.md index e005e34fc8..acb59b4190 100644 --- a/packages/svelte/messages/shared-errors/errors.md +++ b/packages/svelte/messages/shared-errors/errors.md @@ -66,6 +66,12 @@ Certain lifecycle methods can only be used during component initialisation. To f The [`createContext()`](svelte#createContext) utility returns a `[get, set, has]` triplet of functions. `get` will throw an error if `set` was not used to set the context in the current component or any of its ancestors. +## set_context_after_init + +> `setContext` must be called when a component first initializes, not in a subsequent effect or after an `await` expression + +This restriction only applies when using the `experimental.async` option, which will be active by default in Svelte 6. + ## snippet_without_render_tag > Attempted to render a snippet without a `{@render}` block. This would cause the snippet code to be stringified instead of its content being rendered to the DOM. To fix this, change `{snippet}` to `{@render snippet()}`. diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index d60c2dd280..175bb2e528 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -429,22 +429,6 @@ export function rune_outside_svelte(rune) { } } -/** - * `setContext` must be called when a component first initializes, not in a subsequent effect or after an `await` expression - * @returns {never} - */ -export function set_context_after_init() { - if (DEV) { - const error = new Error(`set_context_after_init\n\`setContext\` must be called when a component first initializes, not in a subsequent effect or after an \`await\` expression\nhttps://svelte.dev/e/set_context_after_init`); - - error.name = 'Svelte error'; - - throw error; - } else { - throw new Error(`https://svelte.dev/e/set_context_after_init`); - } -} - /** * Property descriptors defined on `$state` objects must contain `value` and always be `enumerable`, `configurable` and `writable`. * @returns {never} diff --git a/packages/svelte/src/internal/server/context.js b/packages/svelte/src/internal/server/context.js index b204c4c138..80d437907e 100644 --- a/packages/svelte/src/internal/server/context.js +++ b/packages/svelte/src/internal/server/context.js @@ -1,6 +1,7 @@ /** @import { SSRContext } from '#server' */ import { DEV } from 'esm-env'; import { create_context, get_or_init_context_map } from '../shared/context.js'; +import * as e from './errors.js'; /** @type {SSRContext | null} */ export var ssr_context = null; @@ -40,7 +41,13 @@ export function getContext(key) { * @returns {T} */ export function setContext(key, context) { - get_or_init_context_map(ssr_context, 'setContext').set(key, context); + const context_map = get_or_init_context_map(ssr_context, 'setContext'); + + if (/** @type {SSRContext} */ (ssr_context).i) { + e.set_context_after_init(); + } + + context_map.set(key, context); return context; } @@ -61,7 +68,7 @@ export function getAllContexts() { * @param {Function} [fn] */ export function push(fn) { - ssr_context = { p: ssr_context, c: null, r: null }; + ssr_context = { p: ssr_context, c: null, r: null, i: false }; if (DEV) { ssr_context.function = fn; diff --git a/packages/svelte/src/internal/server/renderer.js b/packages/svelte/src/internal/server/renderer.js index a30c302693..6b3c1b28dd 100644 --- a/packages/svelte/src/internal/server/renderer.js +++ b/packages/svelte/src/internal/server/renderer.js @@ -162,6 +162,11 @@ export class Renderer { let promise = Promise.resolve(thunks[0]()); const promises = [promise]; + if (context !== null && thunks.length > 1) { + // the remaining thunks run after an `await`, by which point it is too late to set context + context.i = true; + } + for (const fn of thunks.slice(1)) { promise = promise.then(() => { const previous_context = ssr_context; @@ -209,7 +214,8 @@ export class Renderer { ...ssr_context, p: parent, c: null, - r: child + r: child, + i: ssr_context?.i ?? false }); const result = fn(child); @@ -260,7 +266,8 @@ export class Renderer { ...ssr_context, p: parent_context, c: null, - r: child + r: child, + i: ssr_context?.i ?? false }); try { @@ -859,7 +866,7 @@ export class Renderer { try { /** @type {SSRContext} */ - const context = { p: null, c: options.context ?? null, r: renderer }; + const context = { p: null, c: options.context ?? null, r: renderer, i: false }; set_ssr_context(context); renderer.push(BLOCK_OPEN); diff --git a/packages/svelte/src/internal/server/types.d.ts b/packages/svelte/src/internal/server/types.d.ts index 899255d366..0e8a4cb678 100644 --- a/packages/svelte/src/internal/server/types.d.ts +++ b/packages/svelte/src/internal/server/types.d.ts @@ -9,6 +9,8 @@ export interface SSRContext { c: null | Map; /** renderer */ r: null | Renderer; + /** True if initialized, i.e. an `await` was reached */ + i: boolean; /** dev mode only: the current component function */ function?: any; /** dev mode only: the current element */ diff --git a/packages/svelte/src/internal/shared/errors.js b/packages/svelte/src/internal/shared/errors.js index 9e41788dc1..6cbbe82ca5 100644 --- a/packages/svelte/src/internal/shared/errors.js +++ b/packages/svelte/src/internal/shared/errors.js @@ -101,6 +101,22 @@ export function missing_context() { } } +/** + * `setContext` must be called when a component first initializes, not in a subsequent effect or after an `await` expression + * @returns {never} + */ +export function set_context_after_init() { + if (DEV) { + const error = new Error(`set_context_after_init\n\`setContext\` must be called when a component first initializes, not in a subsequent effect or after an \`await\` expression\nhttps://svelte.dev/e/set_context_after_init`); + + error.name = 'Svelte error'; + + throw error; + } else { + throw new Error(`https://svelte.dev/e/set_context_after_init`); + } +} + /** * Attempted to render a snippet without a `{@render}` block. This would cause the snippet code to be stringified instead of its content being rendered to the DOM. To fix this, change `{snippet}` to `{@render snippet()}`. * @returns {never} diff --git a/packages/svelte/tests/server-side-rendering/samples/async-context-throws-after-await/_config.js b/packages/svelte/tests/server-side-rendering/samples/async-context-throws-after-await/_config.js index 2d1b6be570..85622d23d3 100644 --- a/packages/svelte/tests/server-side-rendering/samples/async-context-throws-after-await/_config.js +++ b/packages/svelte/tests/server-side-rendering/samples/async-context-throws-after-await/_config.js @@ -1,7 +1,6 @@ import { test } from '../../test'; export default test({ - skip: true, // TODO it appears there might be an actual bug here; the promise isn't ever actually awaited in spite of being awaited in the component mode: ['async'], - error: 'lifecycle_outside_component' + error: 'set_context_after_init' });