From 9efe672df2540c51c2cfa817fd32e7667d5712ec Mon Sep 17 00:00:00 2001 From: Justin Paige <77657745+jhpaige@users.noreply.github.com> Date: Wed, 6 Aug 2025 14:16:35 -0400 Subject: [PATCH 1/8] Update 18-class.md (#16558) Rest of doc references 5.16 but this is from 5.19 so added the clarification. --- documentation/docs/03-template-syntax/18-class.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/03-template-syntax/18-class.md b/documentation/docs/03-template-syntax/18-class.md index 1ea4a208dfdc..db85db4b37f4 100644 --- a/documentation/docs/03-template-syntax/18-class.md +++ b/documentation/docs/03-template-syntax/18-class.md @@ -71,7 +71,7 @@ The user of this component has the same flexibility to use a mixture of objects, ``` -Svelte also exposes the `ClassValue` type, which is the type of value that the `class` attribute on elements accept. This is useful if you want to use a type-safe class name in component props: +Since Svelte 5.19, Svelte also exposes the `ClassValue` type, which is the type of value that the `class` attribute on elements accept. This is useful if you want to use a type-safe class name in component props: ```svelte + +{der} + + \ No newline at end of file From f00b364d5b5380f430ed6ba0c1b0be7de0a5a2c1 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Sat, 9 Aug 2025 09:24:44 -0700 Subject: [PATCH 4/8] chore: switch to jrdigewell version of remapping library (#16587) --- packages/svelte/package.json | 2 +- packages/svelte/src/compiler/preprocess/index.js | 4 ++-- .../svelte/src/compiler/preprocess/private.d.ts | 2 +- packages/svelte/src/compiler/utils/mapped_code.js | 4 ++-- pnpm-lock.yaml | 14 +++++++++++--- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 0b14ab4b40c7..2269d5603f2a 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -166,7 +166,7 @@ "vitest": "^2.1.9" }, "dependencies": { - "@ampproject/remapping": "^2.3.0", + "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", diff --git a/packages/svelte/src/compiler/preprocess/index.js b/packages/svelte/src/compiler/preprocess/index.js index afef898471c6..429c2dcff150 100644 --- a/packages/svelte/src/compiler/preprocess/index.js +++ b/packages/svelte/src/compiler/preprocess/index.js @@ -1,6 +1,6 @@ /** @import { Processed, Preprocessor, MarkupPreprocessor, PreprocessorGroup } from './public.js' */ /** @import { SourceUpdate, Source } from './private.js' */ -/** @import { DecodedSourceMap, RawSourceMap } from '@ampproject/remapping' */ +/** @import { DecodedSourceMap, RawSourceMap } from '@jridgewell/remapping' */ import { getLocator } from 'locate-character'; import { MappedCode, @@ -25,7 +25,7 @@ class PreprocessResult { // sourcemap_list is sorted in reverse order from last map (index 0) to first map (index -1) // so we use sourcemap_list.unshift() to add new maps - // https://github.com/ampproject/remapping#multiple-transformations-of-a-file + // https://github.com/jridgewell/sourcemaps/tree/main/packages/remapping#multiple-transformations-of-a-file /** * @default [] diff --git a/packages/svelte/src/compiler/preprocess/private.d.ts b/packages/svelte/src/compiler/preprocess/private.d.ts index 5d8c5ed44eff..2c331cee1267 100644 --- a/packages/svelte/src/compiler/preprocess/private.d.ts +++ b/packages/svelte/src/compiler/preprocess/private.d.ts @@ -1,4 +1,4 @@ -import { DecodedSourceMap } from '@ampproject/remapping'; +import { DecodedSourceMap } from '@jridgewell/remapping'; import { Location } from 'locate-character'; import { MappedCode } from '../utils/mapped_code.js'; diff --git a/packages/svelte/src/compiler/utils/mapped_code.js b/packages/svelte/src/compiler/utils/mapped_code.js index f23e3be24519..7686ba59c672 100644 --- a/packages/svelte/src/compiler/utils/mapped_code.js +++ b/packages/svelte/src/compiler/utils/mapped_code.js @@ -2,8 +2,8 @@ /** @import { Processed } from '../preprocess/public.js' */ /** @import { SourceMap } from 'magic-string' */ /** @import { Source } from '../preprocess/private.js' */ -/** @import { DecodedSourceMap, SourceMapSegment, RawSourceMap } from '@ampproject/remapping' */ -import remapping from '@ampproject/remapping'; +/** @import { DecodedSourceMap, SourceMapSegment, RawSourceMap } from '@jridgewell/remapping' */ +import remapping from '@jridgewell/remapping'; import { push_array } from './push_array.js'; /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 315d699e25ac..cad5fefdf8c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,9 +62,9 @@ importers: packages/svelte: dependencies: - '@ampproject/remapping': - specifier: ^2.3.0 - version: 2.3.0 + '@jridgewell/remapping': + specifier: ^2.3.4 + version: 2.3.4 '@jridgewell/sourcemap-codec': specifier: ^1.5.0 version: 1.5.0 @@ -457,6 +457,9 @@ 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/resolve-uri@3.1.1': resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} @@ -2777,6 +2780,11 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/remapping@2.3.4': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/resolve-uri@3.1.1': {} '@jridgewell/set-array@1.2.1': {} From d2ba2589fee2ec6985c0fe0fbd800ab34518378e Mon Sep 17 00:00:00 2001 From: shamokit <91047157+shamokit@users.noreply.github.com> Date: Sun, 10 Aug 2025 01:26:16 +0900 Subject: [PATCH 5/8] fix: add hint popover (#16581) * fix: add hint popover * changeset --------- Co-authored-by: Rich Harris --- .changeset/metal-months-fail.md | 5 +++++ packages/svelte/elements.d.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/metal-months-fail.md diff --git a/.changeset/metal-months-fail.md b/.changeset/metal-months-fail.md new file mode 100644 index 000000000000..555ec2c0e8d9 --- /dev/null +++ b/.changeset/metal-months-fail.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: add `hint` as a possible value for `popover` attribute diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts index b3d44d9ed6b4..f63a31a96bd3 100644 --- a/packages/svelte/elements.d.ts +++ b/packages/svelte/elements.d.ts @@ -781,7 +781,7 @@ export interface HTMLAttributes extends AriaAttributes, D title?: string | undefined | null; translate?: 'yes' | 'no' | '' | undefined | null; inert?: boolean | undefined | null; - popover?: 'auto' | 'manual' | '' | undefined | null; + popover?: 'auto' | 'manual' | 'hint' | '' | undefined | null; writingsuggestions?: Booleanish | undefined | null; // Unknown From 0480f040c0f220dc090a263e81cfcd4a8db9737f Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 11 Aug 2025 20:41:34 +0200 Subject: [PATCH 6/8] fix: skip effects inside dynamic component that is about to be destroyed (#16601) * fix: skip effects inside dynamic component that is about to be destroyed When a dynamic component was updated to a different instance and its props were updated at the same time, effects inside the component were still called with the already-changed props. The fix is to mark the branch as skipped to never got to those effects. Fixes #16387 * undo accidental commit --- .changeset/neat-files-do.md | 5 +++++ .../client/dom/blocks/svelte-component.js | 4 +++- .../svelte-component-props-update/Comp-1.svelte | 7 +++++++ .../svelte-component-props-update/Comp-2.svelte | 1 + .../svelte-component-props-update/_config.js | 11 +++++++++++ .../svelte-component-props-update/main.svelte | 16 ++++++++++++++++ 6 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 .changeset/neat-files-do.md create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/Comp-1.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/Comp-2.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/main.svelte diff --git a/.changeset/neat-files-do.md b/.changeset/neat-files-do.md new file mode 100644 index 000000000000..ddba170c78d1 --- /dev/null +++ b/.changeset/neat-files-do.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: skip effects inside dynamic component that is about to be destroyed diff --git a/packages/svelte/src/internal/client/dom/blocks/svelte-component.js b/packages/svelte/src/internal/client/dom/blocks/svelte-component.js index f16da9c42703..2697722b3953 100644 --- a/packages/svelte/src/internal/client/dom/blocks/svelte-component.js +++ b/packages/svelte/src/internal/client/dom/blocks/svelte-component.js @@ -62,8 +62,10 @@ export function component(node, get_component, render_fn) { if (defer) { offscreen_fragment = document.createDocumentFragment(); offscreen_fragment.append((target = create_text())); + if (effect) { + /** @type {Batch} */ (current_batch).skipped_effects.add(effect); + } } - pending_effect = branch(() => render_fn(target, component)); } diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/Comp-1.svelte b/packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/Comp-1.svelte new file mode 100644 index 000000000000..fdafa27c3cd0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/Comp-1.svelte @@ -0,0 +1,7 @@ + + +{#each data.obj.arr as i} +

{i}

+{/each} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/Comp-2.svelte b/packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/Comp-2.svelte new file mode 100644 index 000000000000..e345a7697c6e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/Comp-2.svelte @@ -0,0 +1 @@ +

Comp 2

\ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/_config.js b/packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/_config.js new file mode 100644 index 000000000000..ff5ca12dbf75 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/_config.js @@ -0,0 +1,11 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const [btn] = target.querySelectorAll('button'); + btn.click(); + flushSync(); + assert.htmlEqual(target.innerHTML, `

Comp 2

`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/main.svelte b/packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/main.svelte new file mode 100644 index 000000000000..abd785fff376 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-component-props-update/main.svelte @@ -0,0 +1,16 @@ + + + + + From ca442e4f54c5335dbe65e51e0a4293f6d7e712a7 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 11 Aug 2025 21:18:55 +0200 Subject: [PATCH 7/8] chore: add script to generate test from playground (#16602) * chore: add script to generate test from playground I often play around with stuff locally and then turn it into a test, this automates the last mile of that * ask if should override --- playgrounds/sandbox/package.json | 3 +- playgrounds/sandbox/scripts/create-test.js | 155 +++++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 playgrounds/sandbox/scripts/create-test.js diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index 3ab65ac4b50f..f11da983f60e 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -11,7 +11,8 @@ "prod": "npm run build && node dist/server/ssr-prod", "preview": "vite preview", "download": "node scripts/download.js", - "hash": "node scripts/hash.js" + "hash": "node scripts/hash.js", + "create-test": "node scripts/create-test.js" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^4.0.0-next.6", diff --git a/playgrounds/sandbox/scripts/create-test.js b/playgrounds/sandbox/scripts/create-test.js new file mode 100644 index 000000000000..c733f194195b --- /dev/null +++ b/playgrounds/sandbox/scripts/create-test.js @@ -0,0 +1,155 @@ +// Creates a test from the existing playground. cwd needs to be playground/sandbox +import fs from 'fs'; +import path from 'path'; + +// Get target folder from command line arguments +let target_folder = process.argv[2]; +if (!target_folder) { + console.error( + 'Please provide a target folder as an argument. Example: node create-test.js runtime-runes/my-test' + ); + process.exit(1); +} +if (!target_folder.includes('/')) { + target_folder = 'runtime-runes/' + target_folder; +} +if (!target_folder.startsWith('runtime-')) { + console.error( + 'Target folder must start with "runtime-" (can only convert to these kinds of tests)' + ); + process.exit(1); +} +target_folder = path.join( + path.resolve('../../packages/svelte/tests', target_folder.split('/')[0]), + 'samples', + target_folder.split('/')[1] +); + +const exists = fs.existsSync(target_folder); + +// Check if target folder already exists and ask for confirmation +if (exists) { + console.log(`Target folder "${target_folder}" already exists.`); + process.stdout.write('Do you want to override the existing test? (Y/n): '); + + // Read user input synchronously + const stdin = process.stdin; + stdin.setRawMode(true); + stdin.resume(); + stdin.setEncoding('utf8'); + + const response = await new Promise((resolve) => { + stdin.on('data', (key) => { + stdin.setRawMode(false); + stdin.pause(); + process.stdout.write('\n'); + resolve(key); + }); + }); + + if (response.toLowerCase() === 'n') { + console.log('Operation cancelled.'); + process.exit(0); + } + + // Clear the existing target folder except for _config.js + const files = fs.readdirSync(target_folder); + for (const file of files) { + if (file !== '_config.js') { + const filePath = path.join(target_folder, file); + fs.rmSync(filePath, { recursive: true, force: true }); + } + } +} else { + fs.mkdirSync(target_folder, { recursive: true }); +} + +// Starting file +const app_svelte_path = path.resolve('./src/App.svelte'); +const collected_files = new Set(); +const processed_files = new Set(); + +function collect_imports(file_path) { + if (processed_files.has(file_path) || !fs.existsSync(file_path)) { + return; + } + + processed_files.add(file_path); + collected_files.add(file_path); + + const content = fs.readFileSync(file_path, 'utf8'); + + // Regex to match import statements + const import_regex = /import\s+(?:[^'"]*\s+from\s+)?['"]([^'"]+)['"]/g; + let match; + + while ((match = import_regex.exec(content)) !== null) { + const import_path = match[1]; + + // Skip node_modules imports + if (!import_path.startsWith('.')) { + continue; + } + + // Resolve relative import path + const resolved_path = path.resolve(path.dirname(file_path), import_path); + + // Try different extensions if file doesn't exist + const extensions = ['', '.svelte', '.js', '.ts']; + let actual_path = null; + + for (const ext of extensions) { + const test_path = resolved_path + ext; + if (fs.existsSync(test_path)) { + actual_path = test_path; + break; + } + } + + if (actual_path) { + collect_imports(actual_path); + } + } +} + +// Start collecting from App.svelte +collect_imports(app_svelte_path); + +// Copy collected files to target folder +for (const file_path of collected_files) { + const relative_path = path.relative(path.resolve('./src'), file_path); + let target_path = path.join(target_folder, relative_path); + + // Rename App.svelte to main.svelte + if (path.basename(file_path) === 'App.svelte') { + target_path = path.join(target_folder, path.dirname(relative_path), 'main.svelte'); + } + + // Ensure target directory exists + const target_dir = path.dirname(target_path); + if (!fs.existsSync(target_dir)) { + fs.mkdirSync(target_dir, { recursive: true }); + } + + // Copy file + fs.copyFileSync(file_path, target_path); + console.log(`Copied: ${file_path} -> ${target_path}`); +} + +// Create empty _config.js +if (!exists) { + const config_path = path.join(target_folder, '_config.js'); + fs.writeFileSync( + config_path, + `import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + } +}); +` + ); + console.log(`Created: ${config_path}`); +} + +console.log(`\nTest files created in: ${target_folder}`); From 2e02868ef1d97fd11ed9c13346543f4a8d941953 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:21:31 -0400 Subject: [PATCH 8/8] Version Packages (#16573) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/khaki-feet-teach.md | 5 ----- .changeset/metal-months-fail.md | 5 ----- .changeset/neat-files-do.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/khaki-feet-teach.md delete mode 100644 .changeset/metal-months-fail.md delete mode 100644 .changeset/neat-files-do.md diff --git a/.changeset/khaki-feet-teach.md b/.changeset/khaki-feet-teach.md deleted file mode 100644 index 51b724428f0a..000000000000 --- a/.changeset/khaki-feet-teach.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: wrap `abort` in `without_reactive_context` diff --git a/.changeset/metal-months-fail.md b/.changeset/metal-months-fail.md deleted file mode 100644 index 555ec2c0e8d9..000000000000 --- a/.changeset/metal-months-fail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"svelte": patch ---- - -fix: add `hint` as a possible value for `popover` attribute diff --git a/.changeset/neat-files-do.md b/.changeset/neat-files-do.md deleted file mode 100644 index ddba170c78d1..000000000000 --- a/.changeset/neat-files-do.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: skip effects inside dynamic component that is about to be destroyed diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 77f97a1c1192..bb61b50dd559 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.38.1 + +### Patch Changes + +- fix: wrap `abort` in `without_reactive_context` ([#16570](https://github.com/sveltejs/svelte/pull/16570)) + +- fix: add `hint` as a possible value for `popover` attribute ([#16581](https://github.com/sveltejs/svelte/pull/16581)) + +- fix: skip effects inside dynamic component that is about to be destroyed ([#16601](https://github.com/sveltejs/svelte/pull/16601)) + ## 5.38.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 2269d5603f2a..8904c103e31a 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.38.0", + "version": "5.38.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 0bba536c1d72..cf71ae709a5b 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.38.0'; +export const VERSION = '5.38.1'; export const PUBLIC_VERSION = '5';