diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index a68477bc7b..a9e3e3d1e4 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -7,12 +7,11 @@ import MagicString from 'magic-string'; import { walk } from 'zimmerframe'; import { parse } from '../phases/1-parse/index.js'; import { analyze_component } from '../phases/2-analyze/index.js'; -import { validate_component_options } from '../validate-options.js'; import { get_rune } from '../phases/scope.js'; import { reset, reset_warning_filter } from '../state.js'; import { extract_identifiers } from '../utils/ast.js'; -import { regex_is_valid_identifier } from '../phases/patterns.js'; import { migrate_svelte_ignore } from '../utils/extract_svelte_ignore.js'; +import { validate_component_options } from '../validate-options.js'; /** * Does a best-effort migration of Svelte code towards using runes, event attributes and render tags. @@ -63,7 +62,9 @@ export function migrate(source) { self: analysis.root.unique('self').name, trusted: analysis.root.unique('trusted').name, createBubbler: analysis.root.unique('createBubbler').name, - bubble: analysis.root.unique('bubble').name + bubble: analysis.root.unique('bubble').name, + passive: analysis.root.unique('passive').name, + nonpassive: analysis.root.unique('nonpassive').name }, legacy_imports: new Set(), script_insertions: new Set() @@ -728,6 +729,7 @@ function handle_events(element, state) { } const handlers = []; + const explicit_passive_handlers = []; for (let i = 0; i < nodes.length; i += 1) { const node = nodes[i]; @@ -755,17 +757,36 @@ function handle_events(element, state) { ); } + let has_passive = false; + let has_nonpassive = false; + for (const modifier of sorted_modifier) { + has_passive ||= modifier === 'passive'; + has_nonpassive ||= modifier === 'nonpassive'; if (modifier !== 'capture' && modifier !== 'passive' && modifier !== 'nonpassive') { state.legacy_imports.add(modifier); body = `${state.legacy_imports_names[modifier]}(${body})`; } } - handlers.push({ - handler: body, - indent, - needs_line_delete - }); + if (has_passive || has_nonpassive) { + if (has_passive) { + state.legacy_imports.add('passive'); + } + if (has_nonpassive) { + state.legacy_imports.add('nonpassive'); + } + explicit_passive_handlers.push({ + handler: `use:${has_nonpassive ? state.legacy_imports_names.nonpassive : state.legacy_imports_names.passive}={{ handler: () => ${body}, event: '${node.name}' }}`, + indent, + needs_line_delete + }); + } else { + handlers.push({ + handler: body, + indent, + needs_line_delete + }); + } state.str.remove(needs_line_delete ? new_line_index : node.start, node.end); } @@ -773,17 +794,25 @@ function handle_events(element, state) { if (first_node) { let handlers_body = ''; - for (let handler of handlers) { + for (const handler of handlers) { handlers_body += `${handler.needs_line_delete || nodes.length > 1 ? `\n${handler.indent}` : ''}${handler.handler},`; } handlers_body = handlers_body.substring(0, handlers_body.length - 1); - if (handlers_body === name) { - state.str.overwrite(first_node.start, first_node.end, `{${name}}`); - } else { - state.str.overwrite( - first_node.start, + if (handlers_body) { + if (handlers_body === name) { + state.str.overwrite(first_node.start, first_node.end, `{${name}}`); + } else { + state.str.overwrite( + first_node.start, + first_node.end, + `${name}={${nodes.length > 1 ? `${state.legacy_imports_names.handlers}(` : ''}${handlers_body}${nodes.length > 1 ? ')' : ''}}` + ); + } + } + for (const passive_handler of explicit_passive_handlers) { + state.str.appendRight( first_node.end, - `${name}={${nodes.length > 1 ? `${state.legacy_imports_names.handlers}(` : ''}${handlers_body}${nodes.length > 1 ? ')' : ''}}` + `${passive_handler.needs_line_delete || nodes.length > 1 ? `\n${passive_handler.indent}` : ''}${passive_handler.handler}` ); } } diff --git a/packages/svelte/src/internal/client/dom/legacy/event-modifiers.js b/packages/svelte/src/internal/client/dom/legacy/event-modifiers.js index 9712fd09ec..37c4e68909 100644 --- a/packages/svelte/src/internal/client/dom/legacy/event-modifiers.js +++ b/packages/svelte/src/internal/client/dom/legacy/event-modifiers.js @@ -1,3 +1,7 @@ +import { noop } from '../../../shared/utils.js'; +import { user_pre_effect } from '../../reactivity/effects.js'; +import { on } from '../elements/events.js'; + /** * Substitute for the `trusted` event modifier * @deprecated @@ -93,3 +97,29 @@ export function preventDefault(fn) { return fn?.apply(this, args); }; } + +/** + * Substitute for the `passive` event modifier. It's an action. + * @deprecated + * @type {import("svelte/action").Action EventListener, event: string }>} + */ +export function passive(node, { handler, event }) { + user_pre_effect(() => { + return on(node, event, handler() ?? noop, { + passive: true + }); + }); +} + +/** + * Substitute for the `nonpassive` event modifier. It's an action. + * @deprecated + * @type {import("svelte/action").Action EventListener, event: string }>} + */ +export function nonpassive(node, { handler, event }) { + user_pre_effect(() => { + return on(node, event, handler() ?? noop, { + passive: false + }); + }); +} diff --git a/packages/svelte/src/legacy/legacy-client.js b/packages/svelte/src/legacy/legacy-client.js index 9711c8ef55..44f4e8c8fe 100644 --- a/packages/svelte/src/legacy/legacy-client.js +++ b/packages/svelte/src/legacy/legacy-client.js @@ -243,5 +243,7 @@ export { self, stopImmediatePropagation, stopPropagation, - trusted + trusted, + passive, + nonpassive } from '../internal/client/dom/legacy/event-modifiers.js'; diff --git a/packages/svelte/tests/migrate/samples/event-handlers-with-alias/input.svelte b/packages/svelte/tests/migrate/samples/event-handlers-with-alias/input.svelte index 9e6852b103..7b59884be3 100644 --- a/packages/svelte/tests/migrate/samples/event-handlers-with-alias/input.svelte +++ b/packages/svelte/tests/migrate/samples/event-handlers-with-alias/input.svelte @@ -8,6 +8,8 @@ let self; let createBubbler; let bubble; + let passive; + let nonpassive; @@ -39,6 +41,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +