diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index f32f872ee082..6f6526c10bf7 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.22.0 + +### Minor Changes + +- feat: Add `idPrefix` option to `render` ([#15428](https://github.com/sveltejs/svelte/pull/15428)) + +### Patch Changes + +- fix: make dialog element and role interactive ([#15429](https://github.com/sveltejs/svelte/pull/15429)) + ## 5.21.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index f32466ce85fc..82d180d946b7 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.21.0", + "version": "5.22.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js index 24a8e5122d11..1f58a28cad73 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js @@ -30,7 +30,7 @@ const non_interactive_roles = non_abstract_roles // 'generic' is meant to have no semantic meaning. // 'cell' is treated as CellRole by the AXObject which is interactive, so we treat 'cell' it as interactive as well. !['toolbar', 'tabpanel', 'generic', 'cell'].includes(name) && - !role?.superClass.some((classes) => classes.includes('widget')) + !role?.superClass.some((classes) => classes.includes('widget') || classes.includes('window')) ); }) .concat( diff --git a/packages/svelte/src/index.d.ts b/packages/svelte/src/index.d.ts index 554510542e2e..38e60866898f 100644 --- a/packages/svelte/src/index.d.ts +++ b/packages/svelte/src/index.d.ts @@ -19,6 +19,7 @@ export interface ComponentConstructorOptions< intro?: boolean; recover?: boolean; sync?: boolean; + idPrefix?: string; $$inline?: boolean; } diff --git a/packages/svelte/src/internal/client/dom/template.js b/packages/svelte/src/internal/client/dom/template.js index 575bf55cf62b..de2df62c927f 100644 --- a/packages/svelte/src/internal/client/dom/template.js +++ b/packages/svelte/src/internal/client/dom/template.js @@ -250,12 +250,6 @@ export function append(anchor, dom) { anchor.before(/** @type {Node} */ (dom)); } -let uid = 1; - -export function reset_props_id() { - uid = 1; -} - /** * Create (or hydrate) an unique UID for the component instance. */ @@ -264,12 +258,16 @@ export function props_id() { hydrating && hydrate_node && hydrate_node.nodeType === 8 && - hydrate_node.textContent?.startsWith('#s') + hydrate_node.textContent?.startsWith(`#`) ) { const id = hydrate_node.textContent.substring(1); hydrate_next(); return id; } - return 'c' + uid++; + // @ts-expect-error This way we ensure the id is unique even across Svelte runtimes + (window.__svelte ??= {}).uid ??= 1; + + // @ts-expect-error + return `c${window.__svelte.uid++}`; } diff --git a/packages/svelte/src/internal/disclose-version.js b/packages/svelte/src/internal/disclose-version.js index 5e72f1e216ae..86c3482b5dbf 100644 --- a/packages/svelte/src/internal/disclose-version.js +++ b/packages/svelte/src/internal/disclose-version.js @@ -1,5 +1,6 @@ import { PUBLIC_VERSION } from '../version.js'; -if (typeof window !== 'undefined') - // @ts-ignore - (window.__svelte ||= { v: new Set() }).v.add(PUBLIC_VERSION); +if (typeof window !== 'undefined') { + // @ts-expect-error + ((window.__svelte ??= {}).v ??= new Set()).add(PUBLIC_VERSION); +} diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 160a1faa653e..2591dbe4eaab 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -86,9 +86,14 @@ export function element(payload, tag, attributes_fn = noop, children_fn = noop) */ export let on_destroy = []; -function props_id_generator() { +/** + * Creates an ID generator + * @param {string} prefix + * @returns {() => string} + */ +function props_id_generator(prefix) { let uid = 1; - return () => 's' + uid++; + return () => `${prefix}s${uid++}`; } /** @@ -96,11 +101,11 @@ function props_id_generator() { * Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app. * @template {Record} Props * @param {import('svelte').Component | ComponentType>} component - * @param {{ props?: Omit; context?: Map }} [options] + * @param {{ props?: Omit; context?: Map; idPrefix?: string }} [options] * @returns {RenderOutput} */ export function render(component, options = {}) { - const uid = props_id_generator(); + const uid = props_id_generator(options.idPrefix ? options.idPrefix + '-' : ''); /** @type {Payload} */ const payload = { out: '', diff --git a/packages/svelte/src/server/index.d.ts b/packages/svelte/src/server/index.d.ts index b65ce5bdaa67..d5a3b813e6cb 100644 --- a/packages/svelte/src/server/index.d.ts +++ b/packages/svelte/src/server/index.d.ts @@ -12,10 +12,18 @@ export function render< ...args: {} extends Props ? [ component: Comp extends SvelteComponent ? ComponentType : Comp, - options?: { props?: Omit; context?: Map } + options?: { + props?: Omit; + context?: Map; + idPrefix?: string; + } ] : [ component: Comp extends SvelteComponent ? ComponentType : Comp, - options: { props: Omit; context?: Map } + options: { + props: Omit; + context?: Map; + idPrefix?: string; + } ] ): RenderOutput; diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 46fbec674cff..4b9540ad2a95 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.21.0'; +export const VERSION = '5.22.0'; export const PUBLIC_VERSION = '5'; diff --git a/packages/svelte/tests/hydration/test.ts b/packages/svelte/tests/hydration/test.ts index 3bf2dd286cd0..266ac07bff39 100644 --- a/packages/svelte/tests/hydration/test.ts +++ b/packages/svelte/tests/hydration/test.ts @@ -2,9 +2,9 @@ import * as fs from 'node:fs'; import { assert } from 'vitest'; -import { compile_directory, should_update_expected } from '../helpers.js'; +import { compile_directory } from '../helpers.js'; import { assert_html_equal } from '../html_equal.js'; -import { suite, assert_ok, type BaseTest } from '../suite.js'; +import { assert_ok, suite, type BaseTest } from '../suite.js'; import { createClassComponent } from 'svelte/legacy'; import { render } from 'svelte/server'; import type { CompileOptions } from '#compiler'; @@ -13,6 +13,7 @@ import { flushSync } from 'svelte'; interface HydrationTest extends BaseTest { load_compiled?: boolean; server_props?: Record; + id_prefix?: string; props?: Record; compileOptions?: Partial; /** @@ -50,7 +51,8 @@ const { test, run } = suite(async (config, cwd) => { const head = window.document.head; const rendered = render((await import(`${cwd}/_output/server/main.svelte.js`)).default, { - props: config.server_props ?? config.props ?? {} + props: config.server_props ?? config.props ?? {}, + idPrefix: config?.id_prefix }); const override = read(`${cwd}/_override.html`); @@ -103,7 +105,8 @@ const { test, run } = suite(async (config, cwd) => { component: (await import(`${cwd}/_output/client/main.svelte.js`)).default, target, hydrate: true, - props: config.props + props: config.props, + idPrefix: config?.id_prefix }); console.warn = warn; diff --git a/packages/svelte/tests/runtime-browser/assert.js b/packages/svelte/tests/runtime-browser/assert.js index 249d19f80946..fb460c722a0f 100644 --- a/packages/svelte/tests/runtime-browser/assert.js +++ b/packages/svelte/tests/runtime-browser/assert.js @@ -119,6 +119,7 @@ function normalize_children(node) { * skip_mode?: Array<'server' | 'client' | 'hydrate'>; * html?: string; * ssrHtml?: string; + * id_prefix?: string; * props?: Props; * compileOptions?: Partial; * test?: (args: { diff --git a/packages/svelte/tests/runtime-browser/driver-ssr.js b/packages/svelte/tests/runtime-browser/driver-ssr.js index f5f15b64934f..7067e48a1fb9 100644 --- a/packages/svelte/tests/runtime-browser/driver-ssr.js +++ b/packages/svelte/tests/runtime-browser/driver-ssr.js @@ -6,5 +6,5 @@ import config from '__CONFIG__'; import { render } from 'svelte/server'; export default function () { - return render(SvelteComponent, { props: config.props || {} }); + return render(SvelteComponent, { props: config.props || {}, idPrefix: config?.id_prefix }); } diff --git a/packages/svelte/tests/runtime-browser/test-ssr.ts b/packages/svelte/tests/runtime-browser/test-ssr.ts index 2ff1659f802a..6987fac9155a 100644 --- a/packages/svelte/tests/runtime-browser/test-ssr.ts +++ b/packages/svelte/tests/runtime-browser/test-ssr.ts @@ -20,7 +20,7 @@ export async function run_ssr_test( await compile_directory(test_dir, 'server', config.compileOptions); const Component = (await import(`${test_dir}/_output/server/main.svelte.js`)).default; - const { body } = render(Component, { props: config.props || {} }); + const { body } = render(Component, { props: config.props || {}, idPrefix: config.id_prefix }); fs.writeFileSync(`${test_dir}/_output/rendered.html`, body); diff --git a/packages/svelte/tests/runtime-legacy/shared.ts b/packages/svelte/tests/runtime-legacy/shared.ts index 3ffb3092a46a..fc748ce6b299 100644 --- a/packages/svelte/tests/runtime-legacy/shared.ts +++ b/packages/svelte/tests/runtime-legacy/shared.ts @@ -11,7 +11,6 @@ import { setup_html_equal } from '../html_equal.js'; import { raf } from '../animation-helpers.js'; import type { CompileOptions } from '#compiler'; import { suite_with_variants, type BaseTest } from '../suite.js'; -import { reset_props_id } from '../../src/internal/client/dom/template.js'; type Assert = typeof import('vitest').assert & { htmlEqual(a: string, b: string, description?: string): void; @@ -37,6 +36,7 @@ export interface RuntimeTest = Record; props?: Props; server_props?: Props; + id_prefix?: string; before_test?: () => void; after_test?: () => void; test?: (args: { @@ -285,7 +285,8 @@ async function run_test_variant( // ssr into target const SsrSvelteComponent = (await import(`${cwd}/_output/server/main.svelte.js`)).default; const { html, head } = render(SsrSvelteComponent, { - props: config.server_props ?? config.props ?? {} + props: config.server_props ?? config.props ?? {}, + idPrefix: config.id_prefix }); fs.writeFileSync(`${cwd}/_output/rendered.html`, html); @@ -346,7 +347,10 @@ async function run_test_variant( if (runes) { props = proxy({ ...(config.props || {}) }); - reset_props_id(); + + // @ts-expect-error + globalThis.__svelte.uid = 1; + if (manual_hydrate) { hydrate_fn = () => { instance = hydrate(mod.default, { diff --git a/packages/svelte/tests/runtime-runes/samples/props-id-prefix/Child.svelte b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/Child.svelte new file mode 100644 index 000000000000..ad8bbd6f01ff --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/Child.svelte @@ -0,0 +1,5 @@ + + +

{id}

diff --git a/packages/svelte/tests/runtime-runes/samples/props-id-prefix/_config.js b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/_config.js new file mode 100644 index 000000000000..6d4306c413f1 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/_config.js @@ -0,0 +1,60 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + id_prefix: 'myPrefix', + test({ assert, target, variant }) { + if (variant === 'dom') { + assert.htmlEqual( + target.innerHTML, + ` + +

c1

+

c2

+

c3

+

c4

+ ` + ); + } else { + assert.htmlEqual( + target.innerHTML, + ` + +

myPrefix-s1

+

myPrefix-s2

+

myPrefix-s3

+

myPrefix-s4

+ ` + ); + } + + let button = target.querySelector('button'); + flushSync(() => button?.click()); + + if (variant === 'dom') { + assert.htmlEqual( + target.innerHTML, + ` + +

c1

+

c2

+

c3

+

c4

+

c5

+ ` + ); + } else { + assert.htmlEqual( + target.innerHTML, + ` + +

myPrefix-s1

+

myPrefix-s2

+

myPrefix-s3

+

myPrefix-s4

+

c1

+ ` + ); + } + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/props-id-prefix/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/main.svelte new file mode 100644 index 000000000000..646bb2ebdefe --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/main.svelte @@ -0,0 +1,19 @@ + + + + +

{id}

+ + + + + +{#if show} + +{/if} diff --git a/packages/svelte/tests/server-side-rendering/test.ts b/packages/svelte/tests/server-side-rendering/test.ts index f76c5b539f24..3e5753942745 100644 --- a/packages/svelte/tests/server-side-rendering/test.ts +++ b/packages/svelte/tests/server-side-rendering/test.ts @@ -15,6 +15,7 @@ import type { CompileOptions } from '#compiler'; interface SSRTest extends BaseTest { compileOptions?: Partial; props?: Record; + id_prefix?: string; withoutNormalizeHtml?: boolean; errors?: string[]; } @@ -33,7 +34,7 @@ const { test, run } = suite(async (config, test_dir) => { const Component = (await import(`${test_dir}/_output/server/main.svelte.js`)).default; const expected_html = try_read_file(`${test_dir}/_expected.html`); - const rendered = render(Component, { props: config.props || {} }); + const rendered = render(Component, { props: config.props || {}, idPrefix: config.id_prefix }); const { body, head } = rendered; fs.writeFileSync(`${test_dir}/_output/rendered.html`, body); diff --git a/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte b/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte index 1adea90ba39b..759f969d8267 100644 --- a/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte +++ b/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte @@ -3,6 +3,7 @@
{}} on:keypress={() => {}}>
+ {}}>alert

{}}>Heading

Heading

diff --git a/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/warnings.json b/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/warnings.json index 760866d13614..e8bcd0cc1a03 100644 --- a/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/warnings.json +++ b/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/warnings.json @@ -3,60 +3,60 @@ "code": "a11y_no_noninteractive_element_interactions", "end": { "column": 51, - "line": 10 + "line": 11 }, "message": "Non-interactive element `
` should not be assigned mouse or keyboard event listeners", "start": { "column": 0, - "line": 10 + "line": 11 } }, { "code": "a11y_no_noninteractive_element_interactions", "end": { "column": 58, - "line": 11 + "line": 12 }, "message": "Non-interactive element `

` should not be assigned mouse or keyboard event listeners", "start": { "column": 0, - "line": 11 + "line": 12 } }, { "code": "a11y_no_noninteractive_element_interactions", "end": { "column": 50, - "line": 12 + "line": 13 }, "message": "Non-interactive element `

` should not be assigned mouse or keyboard event listeners", "start": { "column": 0, - "line": 12 + "line": 13 } }, { "code": "a11y_no_noninteractive_element_interactions", "end": { "column": 30, - "line": 13 + "line": 14 }, "message": "Non-interactive element `

` should not be assigned mouse or keyboard event listeners", "start": { "column": 0, - "line": 13 + "line": 14 } }, { "code": "a11y_no_noninteractive_element_interactions", "end": { "column": 50, - "line": 14 + "line": 15 }, "message": "Non-interactive element `

` should not be assigned mouse or keyboard event listeners", "start": { "column": 0, - "line": 14 + "line": 15 } } ] diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 4c47661af897..c3dbdcac791e 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -16,6 +16,7 @@ declare module 'svelte' { intro?: boolean; recover?: boolean; sync?: boolean; + idPrefix?: string; $$inline?: boolean; } @@ -2080,11 +2081,19 @@ declare module 'svelte/server' { ...args: {} extends Props ? [ component: Comp extends SvelteComponent ? ComponentType : Comp, - options?: { props?: Omit; context?: Map } + options?: { + props?: Omit; + context?: Map; + idPrefix?: string; + } ] : [ component: Comp extends SvelteComponent ? ComponentType : Comp, - options: { props: Omit; context?: Map } + options: { + props: Omit; + context?: Map; + idPrefix?: string; + } ] ): RenderOutput; interface RenderOutput { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy