Comparing version 5.0.0-next.208 to 5.0.0-next.210
@@ -5,3 +5,3 @@ { | ||
"license": "MIT", | ||
"version": "5.0.0-next.208", | ||
"version": "5.0.0-next.210", | ||
"type": "module", | ||
@@ -8,0 +8,0 @@ "types": "./types/index.d.ts", |
@@ -43,4 +43,4 @@ /** @import { ArrowFunctionExpression, Expression, FunctionDeclaration, FunctionExpression } from 'estree' */ | ||
if (delegated_event !== null) { | ||
if (delegated_event.type === 'hoistable') { | ||
delegated_event.function.metadata.hoistable = true; | ||
if (delegated_event.hoisted) { | ||
delegated_event.function.metadata.hoisted = true; | ||
} | ||
@@ -54,2 +54,5 @@ | ||
/** @type {DelegatedEvent} */ | ||
const unhoisted = { hoisted: false }; | ||
/** | ||
@@ -63,3 +66,3 @@ * Checks if given event attribute can be delegated/hoisted and returns the corresponding info if so | ||
function get_delegated_event(event_name, handler, context) { | ||
// Handle delegated event handlers. Bail-out if not a delegated event. | ||
// Handle delegated event handlers. Bail out if not a delegated event. | ||
if (!handler || !is_delegated(event_name)) { | ||
@@ -69,3 +72,3 @@ return null; | ||
// If we are not working with a RegularElement, then bail-out. | ||
// If we are not working with a RegularElement, then bail out. | ||
const element = context.path.at(-1); | ||
@@ -76,4 +79,2 @@ if (element?.type !== 'RegularElement') { | ||
/** @type {DelegatedEvent} */ | ||
const non_hoistable = { type: 'non-hoistable' }; | ||
/** @type {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression | null} */ | ||
@@ -85,3 +86,3 @@ let target_function = null; | ||
// event attribute becomes part of the dynamic spread array | ||
return non_hoistable; | ||
return unhoisted; | ||
} | ||
@@ -95,4 +96,4 @@ | ||
if (context.state.analysis.module.scope.references.has(handler.name)) { | ||
// If a binding with the same name is referenced in the module scope (even if not declared there), bail-out | ||
return non_hoistable; | ||
// If a binding with the same name is referenced in the module scope (even if not declared there), bail out | ||
return unhoisted; | ||
} | ||
@@ -103,3 +104,3 @@ | ||
const parent = path.at(-1); | ||
if (parent == null) return non_hoistable; | ||
if (parent === undefined) return unhoisted; | ||
@@ -131,6 +132,6 @@ const grandparent = path.at(-2); | ||
) { | ||
return non_hoistable; | ||
return unhoisted; | ||
} | ||
} else if (parent.type !== 'FunctionDeclaration' && parent.type !== 'VariableDeclarator') { | ||
return non_hoistable; | ||
return unhoisted; | ||
} | ||
@@ -140,5 +141,5 @@ } | ||
// If the binding is exported, bail-out | ||
// If the binding is exported, bail out | ||
if (context.state.analysis.exports.find((node) => node.name === handler.name)) { | ||
return non_hoistable; | ||
return unhoisted; | ||
} | ||
@@ -159,8 +160,6 @@ | ||
// If we can't find a function, bail-out | ||
if (target_function == null) return non_hoistable; | ||
// If the function is marked as non-hoistable, bail-out | ||
if (target_function.metadata.hoistable === 'impossible') return non_hoistable; | ||
// If the function has more than one arg, then bail-out | ||
if (target_function.params.length > 1) return non_hoistable; | ||
// If we can't find a function, or the function has multiple parameters, bail out | ||
if (target_function == null || target_function.params.length > 1) { | ||
return unhoisted; | ||
} | ||
@@ -170,6 +169,6 @@ const visited_references = new Set(); | ||
for (const [reference] of scope.references) { | ||
// Bail-out if the arguments keyword is used | ||
if (reference === 'arguments') return non_hoistable; | ||
// Bail-out if references a store subscription | ||
if (scope.get(`$${reference}`)?.kind === 'store_sub') return non_hoistable; | ||
// Bail out if the arguments keyword is used | ||
if (reference === 'arguments') return unhoisted; | ||
// Bail out if references a store subscription | ||
if (scope.get(`$${reference}`)?.kind === 'store_sub') return unhoisted; | ||
@@ -181,3 +180,3 @@ const binding = scope.get(reference); | ||
if (local_binding !== null && binding !== null && local_binding.node !== binding.node) { | ||
return non_hoistable; | ||
return unhoisted; | ||
} | ||
@@ -191,13 +190,13 @@ | ||
) { | ||
return non_hoistable; | ||
return unhoisted; | ||
} | ||
// If we reference the index within an each block, then bail-out. | ||
if (binding !== null && binding.initial?.type === 'EachBlock') return non_hoistable; | ||
// If we reference the index within an each block, then bail out. | ||
if (binding !== null && binding.initial?.type === 'EachBlock') return unhoisted; | ||
if ( | ||
binding !== null && | ||
// Bail-out if the the binding is a rest param | ||
// Bail out if the the binding is a rest param | ||
(binding.declaration_kind === 'rest_param' || | ||
// Bail-out if we reference anything from the EachBlock (for now) that mutates in non-runes mode, | ||
// Bail out if we reference anything from the EachBlock (for now) that mutates in non-runes mode, | ||
(((!context.state.analysis.runes && binding.kind === 'each') || | ||
@@ -210,3 +209,3 @@ // or any normal not reactive bindings that are mutated. | ||
) { | ||
return non_hoistable; | ||
return unhoisted; | ||
} | ||
@@ -216,3 +215,3 @@ visited_references.add(reference); | ||
return { type: 'hoistable', function: target_function }; | ||
return { hoisted: true, function: target_function }; | ||
} | ||
@@ -219,0 +218,0 @@ |
@@ -7,3 +7,3 @@ /** @import { CallExpression, VariableDeclarator } from 'estree' */ | ||
import { get_parent, unwrap_optional } from '../../../utils/ast.js'; | ||
import { is_known_safe_call, is_safe_identifier } from './shared/utils.js'; | ||
import { is_pure, is_safe_identifier } from './shared/utils.js'; | ||
@@ -154,3 +154,3 @@ /** | ||
if (context.state.expression && !is_known_safe_call(node.callee, context)) { | ||
if (context.state.expression && !is_pure(node.callee, context)) { | ||
context.state.expression.has_call = true; | ||
@@ -157,0 +157,0 @@ context.state.expression.has_state = true; |
/** @import { MemberExpression } from 'estree' */ | ||
/** @import { Context } from '../types' */ | ||
import * as e from '../../../errors.js'; | ||
import { is_safe_identifier } from './shared/utils.js'; | ||
import { is_pure, is_safe_identifier } from './shared/utils.js'; | ||
@@ -18,3 +18,3 @@ /** | ||
if (context.state.expression) { | ||
if (context.state.expression && !is_pure(node, context)) { | ||
context.state.expression.has_state = true; | ||
@@ -21,0 +21,0 @@ } |
@@ -11,5 +11,4 @@ /** @import { ArrowFunctionExpression, FunctionDeclaration, FunctionExpression } from 'estree' */ | ||
node.metadata = { | ||
// module context -> already hoisted | ||
hoistable: context.state.ast_type === 'module' ? 'impossible' : false, | ||
hoistable_params: [], | ||
hoisted: false, | ||
hoisted_params: [], | ||
scope: context.state.scope | ||
@@ -16,0 +15,0 @@ }; |
@@ -7,3 +7,3 @@ /** @import { AssignmentExpression, CallExpression, Expression, Pattern, PrivateIdentifier, Super, TaggedTemplateExpression, UpdateExpression, VariableDeclarator } from 'estree' */ | ||
import * as e from '../../../../errors.js'; | ||
import { extract_identifiers } from '../../../../utils/ast.js'; | ||
import { extract_identifiers, object } from '../../../../utils/ast.js'; | ||
import * as w from '../../../../warnings.js'; | ||
@@ -171,22 +171,21 @@ | ||
/** | ||
* @param {Expression | Super} callee | ||
* @param {Expression | Super} node | ||
* @param {Context} context | ||
* @returns {boolean} | ||
*/ | ||
export function is_known_safe_call(callee, context) { | ||
// String / Number / BigInt / Boolean casting calls | ||
if (callee.type === 'Identifier') { | ||
const name = callee.name; | ||
const binding = context.state.scope.get(name); | ||
if ( | ||
binding === null && | ||
(name === 'BigInt' || name === 'String' || name === 'Number' || name === 'Boolean') | ||
) { | ||
return true; | ||
} | ||
export function is_pure(node, context) { | ||
if (node.type !== 'Identifier' && node.type !== 'MemberExpression') { | ||
return false; | ||
} | ||
// TODO add more cases | ||
const left = object(node); | ||
if (!left) return false; | ||
if (left.type === 'Identifier') { | ||
const binding = context.state.scope.get(left.name); | ||
if (binding === null) return true; // globals are assumed to be safe | ||
} | ||
// TODO add more cases (safe Svelte imports, etc) | ||
return false; | ||
} |
/** @import { TaggedTemplateExpression, VariableDeclarator } from 'estree' */ | ||
/** @import { Context } from '../types' */ | ||
import { is_known_safe_call } from './shared/utils.js'; | ||
import { is_pure } from './shared/utils.js'; | ||
@@ -10,3 +10,3 @@ /** | ||
export function TaggedTemplateExpression(node, context) { | ||
if (context.state.expression && !is_known_safe_call(node.tag, context)) { | ||
if (context.state.expression && !is_pure(node.tag, context)) { | ||
context.state.expression.has_call = true; | ||
@@ -13,0 +13,0 @@ context.state.expression.has_state = true; |
@@ -484,3 +484,3 @@ /** @import { ArrowFunctionExpression, AssignmentExpression, BinaryOperator, Expression, FunctionDeclaration, FunctionExpression, Identifier, MemberExpression, Node, Pattern, PrivateIdentifier, Statement } from 'estree' */ | ||
*/ | ||
function get_hoistable_params(node, context) { | ||
function get_hoisted_params(node, context) { | ||
const scope = context.state.scope; | ||
@@ -553,5 +553,5 @@ | ||
*/ | ||
export function build_hoistable_params(node, context) { | ||
const hoistable_params = get_hoistable_params(node, context); | ||
node.metadata.hoistable_params = hoistable_params; | ||
export function build_hoisted_params(node, context) { | ||
const hoisted_params = get_hoisted_params(node, context); | ||
node.metadata.hoisted_params = hoisted_params; | ||
@@ -562,3 +562,3 @@ /** @type {Pattern[]} */ | ||
if (node.params.length === 0) { | ||
if (hoistable_params.length > 0) { | ||
if (hoisted_params.length > 0) { | ||
// For the event object | ||
@@ -573,3 +573,3 @@ params.push(b.id('_')); | ||
params.push(...hoistable_params); | ||
params.push(...hoisted_params); | ||
return params; | ||
@@ -576,0 +576,0 @@ } |
/** @import { Attribute } from '#compiler' */ | ||
/** @import { ComponentContext } from '../types' */ | ||
import { is_event_attribute } from '../../../../utils/ast.js'; | ||
import { build_event_attribute } from './shared/element.js'; | ||
import { visit_event_attribute } from './shared/events.js'; | ||
@@ -12,4 +12,4 @@ /** | ||
if (is_event_attribute(node)) { | ||
build_event_attribute(node, context); | ||
visit_event_attribute(node, context); | ||
} | ||
} |
/** @import { FunctionDeclaration } from 'estree' */ | ||
/** @import { ComponentContext } from '../types' */ | ||
import { build_hoistable_params } from '../utils.js'; | ||
import { build_hoisted_params } from '../utils.js'; | ||
import * as b from '../../../../utils/builders.js'; | ||
@@ -11,17 +11,9 @@ | ||
export function FunctionDeclaration(node, context) { | ||
const metadata = node.metadata; | ||
const state = { ...context.state, in_constructor: false }; | ||
if (metadata?.hoistable === true) { | ||
const params = build_hoistable_params(node, context); | ||
if (node.metadata?.hoisted === true) { | ||
const params = build_hoisted_params(node, context); | ||
const body = context.visit(node.body, state); | ||
context.state.hoisted.push( | ||
/** @type {FunctionDeclaration} */ ({ | ||
...node, | ||
id: node.id !== null ? context.visit(node.id, state) : null, | ||
params, | ||
body: context.visit(node.body, state) | ||
}) | ||
); | ||
context.state.hoisted.push(/** @type {FunctionDeclaration} */ ({ ...node, params, body })); | ||
@@ -28,0 +20,0 @@ return b.empty; |
@@ -1,5 +0,15 @@ | ||
/** @import { OnDirective } from '#compiler' */ | ||
/** @import { OnDirective, SvelteNode } from '#compiler' */ | ||
/** @import { ComponentContext } from '../types' */ | ||
import { build_event } from './shared/element.js'; | ||
import * as b from '../../../../utils/builders.js'; | ||
import { build_event, build_event_handler } from './shared/events.js'; | ||
const modifiers = [ | ||
'stopPropagation', | ||
'stopImmediatePropagation', | ||
'preventDefault', | ||
'self', | ||
'trusted', | ||
'once' | ||
]; | ||
/** | ||
@@ -10,3 +20,20 @@ * @param {OnDirective} node | ||
export function OnDirective(node, context) { | ||
build_event(node, node.metadata.expression, context); | ||
if (!node.expression) { | ||
context.state.analysis.needs_props = true; | ||
} | ||
let handler = build_event_handler(node.expression, node.metadata.expression, context); | ||
for (const modifier of modifiers) { | ||
if (node.modifiers.includes(modifier)) { | ||
handler = b.call('$.' + modifier, handler); | ||
} | ||
} | ||
const capture = node.modifiers.includes('capture'); | ||
const passive = | ||
node.modifiers.includes('passive') || | ||
(node.modifiers.includes('nonpassive') ? false : undefined); | ||
return build_event(node.name, context.state.node, handler, capture, passive); | ||
} |
@@ -27,3 +27,2 @@ /** @import { Expression, ExpressionStatement, Identifier, Literal, MemberExpression, ObjectExpression, Statement } from 'estree' */ | ||
build_class_directives, | ||
build_event_attribute, | ||
build_style_directives | ||
@@ -33,2 +32,3 @@ } from './shared/element.js'; | ||
import { build_render_statement, build_update, build_update_assignment } from './shared/utils.js'; | ||
import { visit_event_attribute } from './shared/events.js'; | ||
@@ -143,2 +143,9 @@ /** | ||
lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute))); | ||
} else if (attribute.type === 'OnDirective') { | ||
const handler = /** @type {Expression} */ (context.visit(attribute)); | ||
const has_action_directive = node.attributes.find((a) => a.type === 'UseDirective'); | ||
context.state.after_update.push( | ||
b.stmt(has_action_directive ? b.call('$.effect', b.thunk(handler)) : handler) | ||
); | ||
} else { | ||
@@ -220,3 +227,3 @@ if (attribute.type === 'BindDirective') { | ||
} | ||
build_event_attribute(attribute, context); | ||
visit_event_attribute(attribute, context); | ||
continue; | ||
@@ -477,6 +484,4 @@ } | ||
const lowercase_attributes = | ||
element.metadata.svg || element.metadata.mathml || is_custom_element_node(element) | ||
? b.false | ||
: b.true; | ||
const preserve_attribute_case = | ||
element.metadata.svg || element.metadata.mathml || is_custom_element_node(element); | ||
const id = context.state.scope.generate('attributes'); | ||
@@ -493,4 +498,4 @@ | ||
b.object(values), | ||
lowercase_attributes, | ||
b.literal(context.state.analysis.css.hash), | ||
context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash), | ||
preserve_attribute_case && b.true, | ||
is_ignored(element, 'hydration_attribute_changed') && b.true | ||
@@ -497,0 +502,0 @@ ) |
@@ -9,4 +9,5 @@ /** @import { BlockStatement, Expression, ExpressionStatement, Identifier, MemberExpression, Property, Statement } from 'estree' */ | ||
import { create_derived, build_setter } from '../../utils.js'; | ||
import { build_bind_this, build_event_handler, build_validate_binding } from '../shared/utils.js'; | ||
import { build_bind_this, build_validate_binding } from '../shared/utils.js'; | ||
import { build_attribute_value } from '../shared/element.js'; | ||
import { build_event_handler } from './events.js'; | ||
@@ -73,8 +74,17 @@ /** | ||
} else if (attribute.type === 'OnDirective') { | ||
events[attribute.name] ||= []; | ||
let handler = build_event_handler(attribute, null, context); | ||
if (!attribute.expression) { | ||
context.state.analysis.needs_props = true; | ||
} | ||
let handler = build_event_handler( | ||
attribute.expression, | ||
attribute.metadata.expression, | ||
context | ||
); | ||
if (attribute.modifiers.includes('once')) { | ||
handler = b.call('$.once', handler); | ||
} | ||
events[attribute.name].push(handler); | ||
(events[attribute.name] ||= []).push(handler); | ||
} else if (attribute.type === 'SpreadAttribute') { | ||
@@ -81,0 +91,0 @@ const expression = /** @type {Expression} */ (context.visit(attribute)); |
/** @import { Expression, Identifier } from 'estree' */ | ||
/** @import { Attribute, ClassDirective, DelegatedEvent, ExpressionMetadata, ExpressionTag, Namespace, OnDirective, RegularElement, StyleDirective, SvelteElement, SvelteNode } from '#compiler' */ | ||
/** @import { Attribute, ClassDirective, ExpressionMetadata, Namespace, RegularElement, StyleDirective, SvelteElement } from '#compiler' */ | ||
/** @import { ComponentContext } from '../../types' */ | ||
import { | ||
is_capture_event, | ||
is_passive_event, | ||
normalize_attribute | ||
} from '../../../../../../utils.js'; | ||
import { get_attribute_expression } from '../../../../../utils/ast.js'; | ||
import { normalize_attribute } from '../../../../../../utils.js'; | ||
import * as b from '../../../../../utils/builders.js'; | ||
import { build_getter } from '../../utils.js'; | ||
import { build_event_handler, build_template_literal, build_update } from './utils.js'; | ||
import { build_template_literal, build_update } from './utils.js'; | ||
@@ -134,141 +129,1 @@ /** | ||
} | ||
/** | ||
* @param {Attribute & { value: ExpressionTag | [ExpressionTag] }} node | ||
* @param {ComponentContext} context | ||
*/ | ||
export function build_event_attribute(node, context) { | ||
/** @type {string[]} */ | ||
const modifiers = []; | ||
let event_name = node.name.slice(2); | ||
if (is_capture_event(event_name)) { | ||
event_name = event_name.slice(0, -7); | ||
modifiers.push('capture'); | ||
} | ||
build_event( | ||
{ | ||
name: event_name, | ||
expression: get_attribute_expression(node), | ||
modifiers, | ||
delegated: node.metadata.delegated | ||
}, | ||
!Array.isArray(node.value) && node.value?.type === 'ExpressionTag' | ||
? node.value.metadata.expression | ||
: null, | ||
context | ||
); | ||
} | ||
/** | ||
* Serializes an event handler function of the `on:` directive or an attribute starting with `on` | ||
* @param {{name: string;modifiers: string[];expression: Expression | null;delegated?: DelegatedEvent | null;}} node | ||
* @param {null | ExpressionMetadata} metadata | ||
* @param {ComponentContext} context | ||
*/ | ||
export function build_event(node, metadata, context) { | ||
const state = context.state; | ||
/** @type {Expression} */ | ||
let expression; | ||
if (node.expression) { | ||
let handler = build_event_handler(node, metadata, context); | ||
const event_name = node.name; | ||
const delegated = node.delegated; | ||
if (delegated != null) { | ||
let delegated_assignment; | ||
if (!state.events.has(event_name)) { | ||
state.events.add(event_name); | ||
} | ||
// Hoist function if we can, otherwise we leave the function as is | ||
if (delegated.type === 'hoistable') { | ||
if (delegated.function === node.expression) { | ||
const func_name = context.state.scope.root.unique('on_' + event_name); | ||
state.hoisted.push(b.var(func_name, handler)); | ||
handler = func_name; | ||
} | ||
if (node.modifiers.includes('once')) { | ||
handler = b.call('$.once', handler); | ||
} | ||
const hoistable_params = /** @type {Expression[]} */ ( | ||
delegated.function.metadata.hoistable_params | ||
); | ||
// When we hoist a function we assign an array with the function and all | ||
// hoisted closure params. | ||
const args = [handler, ...hoistable_params]; | ||
delegated_assignment = b.array(args); | ||
} else { | ||
if (node.modifiers.includes('once')) { | ||
handler = b.call('$.once', handler); | ||
} | ||
delegated_assignment = handler; | ||
} | ||
state.init.push( | ||
b.stmt( | ||
b.assignment( | ||
'=', | ||
b.member(context.state.node, b.id('__' + event_name)), | ||
delegated_assignment | ||
) | ||
) | ||
); | ||
return; | ||
} | ||
if (node.modifiers.includes('once')) { | ||
handler = b.call('$.once', handler); | ||
} | ||
const args = [ | ||
b.literal(event_name), | ||
context.state.node, | ||
handler, | ||
b.literal(node.modifiers.includes('capture')) | ||
]; | ||
if (node.modifiers.includes('passive')) { | ||
args.push(b.literal(true)); | ||
} else if (node.modifiers.includes('nonpassive')) { | ||
args.push(b.literal(false)); | ||
} else if ( | ||
is_passive_event(node.name) && | ||
/** @type {OnDirective} */ (node).type !== 'OnDirective' | ||
) { | ||
// For on:something events we don't apply passive behaviour to match Svelte 4. | ||
args.push(b.literal(true)); | ||
} | ||
// Events need to run in order with bindings/actions | ||
expression = b.call('$.event', ...args); | ||
} else { | ||
expression = b.call( | ||
'$.event', | ||
b.literal(node.name), | ||
state.node, | ||
build_event_handler(node, metadata, context) | ||
); | ||
} | ||
const parent = /** @type {SvelteNode} */ (context.path.at(-1)); | ||
const has_action_directive = | ||
parent.type === 'RegularElement' && parent.attributes.find((a) => a.type === 'UseDirective'); | ||
const statement = b.stmt( | ||
has_action_directive ? b.call('$.effect', b.thunk(expression)) : expression | ||
); | ||
if ( | ||
parent.type === 'SvelteDocument' || | ||
parent.type === 'SvelteWindow' || | ||
parent.type === 'SvelteBody' | ||
) { | ||
// These nodes are above the component tree, and its events should run parent first | ||
state.before_init.push(statement); | ||
} else { | ||
state.after_update.push(statement); | ||
} | ||
} |
/** @import { ArrowFunctionExpression, FunctionExpression, Node } from 'estree' */ | ||
/** @import { ComponentContext } from '../../types' */ | ||
import { build_hoistable_params } from '../../utils.js'; | ||
import { build_hoisted_params } from '../../utils.js'; | ||
@@ -23,4 +23,4 @@ /** | ||
if (metadata?.hoistable === true) { | ||
const params = build_hoistable_params(node, context); | ||
if (metadata?.hoisted === true) { | ||
const params = build_hoisted_params(node, context); | ||
@@ -27,0 +27,0 @@ return /** @type {FunctionExpression} */ ({ |
/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, Statement, Super, TemplateElement, TemplateLiteral } from 'estree' */ | ||
/** @import { BindDirective, ExpressionMetadata, ExpressionTag, OnDirective, SvelteNode, Text } from '#compiler' */ | ||
/** @import { BindDirective, DelegatedEvent, ExpressionMetadata, ExpressionTag, OnDirective, SvelteNode, Text } from '#compiler' */ | ||
/** @import { ComponentClientTransformState, ComponentContext } from '../../types' */ | ||
@@ -136,111 +136,2 @@ import { walk } from 'zimmerframe'; | ||
/** | ||
* Serializes the event handler function of the `on:` directive | ||
* @param {Pick<OnDirective, 'name' | 'modifiers' | 'expression'>} node | ||
* @param {null | ExpressionMetadata} metadata | ||
* @param {ComponentContext} context | ||
*/ | ||
export function build_event_handler(node, metadata, { state, visit }) { | ||
/** @type {Expression} */ | ||
let handler; | ||
if (node.expression) { | ||
handler = node.expression; | ||
// Event handlers can be dynamic (source/store/prop/conditional etc) | ||
const dynamic_handler = () => | ||
b.function( | ||
null, | ||
[b.rest(b.id('$$args'))], | ||
b.block([ | ||
b.return( | ||
b.call( | ||
b.member(/** @type {Expression} */ (visit(handler)), b.id('apply'), false, true), | ||
b.this, | ||
b.id('$$args') | ||
) | ||
) | ||
]) | ||
); | ||
if ( | ||
metadata?.has_call && | ||
!( | ||
(handler.type === 'ArrowFunctionExpression' || handler.type === 'FunctionExpression') && | ||
handler.metadata.hoistable | ||
) | ||
) { | ||
// Create a derived dynamic event handler | ||
const id = b.id(state.scope.generate('event_handler')); | ||
state.init.push( | ||
b.var(id, b.call('$.derived', b.thunk(/** @type {Expression} */ (visit(handler))))) | ||
); | ||
handler = b.function( | ||
null, | ||
[b.rest(b.id('$$args'))], | ||
b.block([ | ||
b.return( | ||
b.call( | ||
b.member(b.call('$.get', id), b.id('apply'), false, true), | ||
b.this, | ||
b.id('$$args') | ||
) | ||
) | ||
]) | ||
); | ||
} else if (handler.type === 'Identifier' || handler.type === 'MemberExpression') { | ||
const id = object(handler); | ||
const binding = id === null ? null : state.scope.get(id.name); | ||
if ( | ||
binding !== null && | ||
(binding.kind === 'state' || | ||
binding.kind === 'frozen_state' || | ||
binding.declaration_kind === 'import' || | ||
binding.kind === 'legacy_reactive' || | ||
binding.kind === 'derived' || | ||
binding.kind === 'prop' || | ||
binding.kind === 'bindable_prop' || | ||
binding.kind === 'store_sub') | ||
) { | ||
handler = dynamic_handler(); | ||
} else { | ||
handler = /** @type {Expression} */ (visit(handler)); | ||
} | ||
} else if (handler.type === 'ConditionalExpression' || handler.type === 'LogicalExpression') { | ||
handler = dynamic_handler(); | ||
} else { | ||
handler = /** @type {Expression} */ (visit(handler)); | ||
} | ||
} else { | ||
state.analysis.needs_props = true; | ||
// Function + .call to preserve "this" context as much as possible | ||
handler = b.function( | ||
null, | ||
[b.id('$$arg')], | ||
b.block([b.stmt(b.call('$.bubble_event.call', b.this, b.id('$$props'), b.id('$$arg')))]) | ||
); | ||
} | ||
if (node.modifiers.includes('stopPropagation')) { | ||
handler = b.call('$.stopPropagation', handler); | ||
} | ||
if (node.modifiers.includes('stopImmediatePropagation')) { | ||
handler = b.call('$.stopImmediatePropagation', handler); | ||
} | ||
if (node.modifiers.includes('preventDefault')) { | ||
handler = b.call('$.preventDefault', handler); | ||
} | ||
if (node.modifiers.includes('self')) { | ||
handler = b.call('$.self', handler); | ||
} | ||
if (node.modifiers.includes('trusted')) { | ||
handler = b.call('$.trusted', handler); | ||
} | ||
return handler; | ||
} | ||
/** | ||
* Serializes `bind:this` for components and elements. | ||
@@ -247,0 +138,0 @@ * @param {Identifier | MemberExpression} expression |
/** @import { SvelteBody } from '#compiler' */ | ||
/** @import { ComponentContext } from '../types' */ | ||
import * as b from '../../../../utils/builders.js'; | ||
import { visit_special_element } from './shared/special_element.js'; | ||
@@ -10,6 +10,3 @@ /** | ||
export function SvelteBody(node, context) { | ||
context.next({ | ||
...context.state, | ||
node: b.id('$.document.body') | ||
}); | ||
visit_special_element(node, '$.document.body', context); | ||
} |
/** @import { SvelteDocument } from '#compiler' */ | ||
/** @import { ComponentContext } from '../types' */ | ||
import * as b from '../../../../utils/builders.js'; | ||
import { visit_special_element } from './shared/special_element.js'; | ||
@@ -10,6 +10,3 @@ /** | ||
export function SvelteDocument(node, context) { | ||
context.next({ | ||
...context.state, | ||
node: b.id('$.document') | ||
}); | ||
visit_special_element(node, '$.document', context); | ||
} |
@@ -74,2 +74,5 @@ /** @import { BlockStatement, Expression, ExpressionStatement, Identifier, Literal, MemberExpression, ObjectExpression, Statement } from 'estree' */ | ||
lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute))); | ||
} else if (attribute.type === 'OnDirective') { | ||
const handler = /** @type {Expression} */ (context.visit(attribute, inner_context.state)); | ||
inner_context.state.after_update.push(b.stmt(handler)); | ||
} else { | ||
@@ -204,3 +207,3 @@ context.visit(attribute, inner_context.state); | ||
b.object(values), | ||
b.literal(context.state.analysis.css.hash) | ||
context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash) | ||
) | ||
@@ -226,3 +229,3 @@ ) | ||
b.object(values), | ||
b.literal(context.state.analysis.css.hash) | ||
context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash) | ||
) | ||
@@ -229,0 +232,0 @@ ) |
@@ -0,4 +1,5 @@ | ||
/** @import { Expression } from 'estree' */ | ||
/** @import { SvelteWindow } from '#compiler' */ | ||
/** @import { ComponentContext } from '../types' */ | ||
import * as b from '../../../../utils/builders.js'; | ||
import { visit_special_element } from './shared/special_element.js'; | ||
@@ -10,6 +11,3 @@ /** | ||
export function SvelteWindow(node, context) { | ||
context.next({ | ||
...context.state, | ||
node: b.id('$.window') | ||
}); | ||
visit_special_element(node, '$.window', context); | ||
} |
@@ -16,3 +16,3 @@ /** @import { CallExpression, Expression, Identifier, Literal, VariableDeclaration, VariableDeclarator } from 'estree' */ | ||
} from '../utils.js'; | ||
import { is_hoistable_function } from '../../utils.js'; | ||
import { is_hoisted_function } from '../../utils.js'; | ||
@@ -40,7 +40,7 @@ /** | ||
) { | ||
if (init != null && is_hoistable_function(init)) { | ||
const hoistable_function = context.visit(init); | ||
if (init != null && is_hoisted_function(init)) { | ||
context.state.hoisted.push( | ||
b.declaration('const', declarator.id, /** @type {Expression} */ (hoistable_function)) | ||
b.declaration('const', declarator.id, /** @type {Expression} */ (context.visit(init))) | ||
); | ||
continue; | ||
@@ -224,7 +224,5 @@ } | ||
if (init != null && is_hoistable_function(init)) { | ||
const hoistable_function = context.visit(init); | ||
if (init != null && is_hoisted_function(init)) { | ||
context.state.hoisted.push( | ||
b.declaration('const', declarator.id, /** @type {Expression} */ (hoistable_function)) | ||
b.declaration('const', declarator.id, /** @type {Expression} */ (context.visit(init))) | ||
); | ||
@@ -231,0 +229,0 @@ |
@@ -23,3 +23,3 @@ /** @import { Context } from 'zimmerframe' */ | ||
*/ | ||
export function is_hoistable_function(node) { | ||
export function is_hoisted_function(node) { | ||
if ( | ||
@@ -30,3 +30,3 @@ node.type === 'ArrowFunctionExpression' || | ||
) { | ||
return node.metadata?.hoistable === true; | ||
return node.metadata?.hoisted === true; | ||
} | ||
@@ -33,0 +33,0 @@ return false; |
@@ -85,4 +85,4 @@ import type { | ||
metadata: { | ||
hoistable: boolean | 'impossible'; | ||
hoistable_params: Pattern[]; | ||
hoisted: boolean; | ||
hoisted_params: Pattern[]; | ||
scope: Scope; | ||
@@ -94,4 +94,4 @@ }; | ||
metadata: { | ||
hoistable: boolean | 'impossible'; | ||
hoistable_params: Pattern[]; | ||
hoisted: boolean; | ||
hoisted_params: Pattern[]; | ||
scope: Scope; | ||
@@ -103,4 +103,4 @@ }; | ||
metadata: { | ||
hoistable: boolean | 'impossible'; | ||
hoistable_params: Pattern[]; | ||
hoisted: boolean; | ||
hoisted_params: Pattern[]; | ||
scope: Scope; | ||
@@ -107,0 +107,0 @@ }; |
@@ -217,6 +217,6 @@ import type { Binding, Css, ExpressionMetadata } from '#compiler'; | ||
| { | ||
type: 'hoistable'; | ||
hoisted: true; | ||
function: ArrowFunctionExpression | FunctionExpression | FunctionDeclaration; | ||
} | ||
| { type: 'non-hoistable' }; | ||
| { hoisted: false }; | ||
@@ -223,0 +223,0 @@ /** A `style:` directive */ |
@@ -150,9 +150,15 @@ import { DEV } from 'esm-env'; | ||
* @param {Record<string, any>} next New attributes - this function mutates this object | ||
* @param {boolean} lowercase_attributes | ||
* @param {string} css_hash | ||
* @param {string} [css_hash] | ||
* @param {boolean} preserve_attribute_case | ||
* @param {boolean} [skip_warning] | ||
* @returns {Record<string, any>} | ||
*/ | ||
export function set_attributes(element, prev, next, lowercase_attributes, css_hash, skip_warning) { | ||
var has_hash = css_hash.length !== 0; | ||
export function set_attributes( | ||
element, | ||
prev, | ||
next, | ||
css_hash, | ||
preserve_attribute_case = false, | ||
skip_warning | ||
) { | ||
var current = prev || {}; | ||
@@ -167,4 +173,4 @@ var is_option_element = element.tagName === 'OPTION'; | ||
if (has_hash && !next.class) { | ||
next.class = ''; | ||
if (css_hash !== undefined) { | ||
next.class = next.class ? next.class + ' ' + css_hash : css_hash; | ||
} | ||
@@ -272,3 +278,3 @@ | ||
var name = key; | ||
if (lowercase_attributes) { | ||
if (!preserve_attribute_case) { | ||
name = normalize_attribute(name); | ||
@@ -285,7 +291,2 @@ } | ||
} else if (typeof value !== 'function') { | ||
if (has_hash && name === 'class') { | ||
if (value) value += ' '; | ||
value += css_hash; | ||
} | ||
set_attribute(element, name, value); | ||
@@ -316,3 +317,3 @@ } | ||
* @param {Record<string, any>} next The new attributes - this function mutates this object | ||
* @param {string} css_hash | ||
* @param {string} [css_hash] | ||
*/ | ||
@@ -327,2 +328,6 @@ export function set_dynamic_element_attributes(node, prev, next, css_hash) { | ||
if (css_hash !== undefined) { | ||
next.class = next.class ? next.class + ' ' + css_hash : css_hash; | ||
} | ||
for (key in next) { | ||
@@ -339,4 +344,4 @@ set_custom_element_data(node, key, next[key]); | ||
next, | ||
node.namespaceURI !== NAMESPACE_SVG, | ||
css_hash | ||
css_hash, | ||
node.namespaceURI !== NAMESPACE_SVG | ||
); | ||
@@ -343,0 +348,0 @@ } |
@@ -62,4 +62,8 @@ import { teardown } from '../../reactivity/effects.js'; | ||
// defer the attachment till after it's been appended to the document. TODO: remove this once Chrome fixes | ||
// this bug. The same applies to wheel events. | ||
if (event_name.startsWith('pointer') || event_name === 'wheel') { | ||
// this bug. The same applies to wheel events and touch events. | ||
if ( | ||
event_name.startsWith('pointer') || | ||
event_name.startsWith('touch') || | ||
event_name === 'wheel' | ||
) { | ||
queue_micro_task(() => { | ||
@@ -66,0 +70,0 @@ dom.addEventListener(event_name, target_handler, options); |
@@ -255,3 +255,3 @@ /** @import { ComponentContext, ComponentContextLegacy, Effect, Reaction, TemplateNode, TransitionManager } from '#client' */ | ||
// If this legacy pre effect has already run before the end of the reset, then | ||
// bail-out to emulate the same behavior. | ||
// bail out to emulate the same behavior. | ||
if (token.ran) return; | ||
@@ -258,0 +258,0 @@ |
@@ -129,2 +129,9 @@ /** @import { Source } from './types.js' */ | ||
}, | ||
deleteProperty(target, key) { | ||
// Svelte 4 allowed for deletions on $$restProps | ||
if (target.exclude.includes(key)) return false; | ||
target.exclude.push(key); | ||
update(target.version); | ||
return true; | ||
}, | ||
has(target, key) { | ||
@@ -131,0 +138,0 @@ if (target.exclude.includes(key)) return false; |
@@ -35,2 +35,10 @@ /** @import { Source } from '#client' */ | ||
proto[method] = function (...args) { | ||
// don't memoize if there are arguments | ||
// @ts-ignore | ||
if (args.length > 0) { | ||
get(this.#time); | ||
// @ts-ignore | ||
return date_proto[method].apply(this, args); | ||
} | ||
var d = this.#deriveds.get(method); | ||
@@ -37,0 +45,0 @@ |
@@ -9,3 +9,3 @@ // generated during release, do not modify | ||
*/ | ||
export const VERSION = '5.0.0-next.208'; | ||
export const VERSION = '5.0.0-next.210'; | ||
export const PUBLIC_VERSION = '5'; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
347
2196599
48566