svelte 5.41.4 → 5.42.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/index.js +1 -1
- package/package.json +1 -1
- package/src/compiler/phases/2-analyze/visitors/Attribute.js +5 -183
- package/src/compiler/phases/2-analyze/visitors/shared/function.js +0 -7
- package/src/compiler/phases/3-transform/client/utils.js +2 -124
- package/src/compiler/phases/3-transform/client/visitors/FunctionDeclaration.js +0 -11
- package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +0 -18
- package/src/compiler/phases/3-transform/client/visitors/shared/events.js +1 -29
- package/src/compiler/phases/3-transform/client/visitors/shared/function.js +0 -13
- package/src/compiler/phases/3-transform/utils.js +0 -19
- package/src/compiler/phases/nodes.js +1 -1
- package/src/compiler/utils/builders.js +3 -6
- package/src/index-client.js +1 -1
- package/src/index-server.js +4 -0
- package/src/internal/client/constants.js +2 -1
- package/src/internal/client/context.js +8 -1
- package/src/internal/client/dev/inspect.js +12 -3
- package/src/internal/client/dev/tracing.js +1 -2
- package/src/internal/client/dom/blocks/branches.js +18 -3
- package/src/internal/client/dom/blocks/each.js +1 -1
- package/src/internal/client/dom/elements/attributes.js +2 -2
- package/src/internal/client/dom/elements/events.js +2 -7
- package/src/internal/client/error-handling.js +2 -2
- package/src/internal/client/errors.js +48 -0
- package/src/internal/client/proxy.js +5 -5
- package/src/internal/client/reactivity/batch.js +168 -22
- package/src/internal/client/reactivity/deriveds.js +5 -5
- package/src/internal/client/reactivity/effects.js +4 -4
- package/src/internal/client/reactivity/sources.js +19 -19
- package/src/internal/client/runtime.js +2 -2
- package/src/utils.js +1 -1
- package/src/version.js +1 -1
- package/types/index.d.ts +39 -5
- package/types/index.d.ts.map +5 -3
package/package.json
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
/** @import {
|
|
2
|
-
/** @import { AST, DelegatedEvent } from '#compiler' */
|
|
1
|
+
/** @import { AST } from '#compiler' */
|
|
3
2
|
/** @import { Context } from '../types' */
|
|
4
|
-
import { cannot_be_set_statically,
|
|
5
|
-
import {
|
|
6
|
-
get_attribute_chunks,
|
|
7
|
-
get_attribute_expression,
|
|
8
|
-
is_event_attribute
|
|
9
|
-
} from '../../../utils/ast.js';
|
|
3
|
+
import { cannot_be_set_statically, can_delegate_event } from '../../../../utils.js';
|
|
4
|
+
import { get_attribute_chunks, is_event_attribute } from '../../../utils/ast.js';
|
|
10
5
|
import { mark_subtree_dynamic } from './shared/fragment.js';
|
|
11
6
|
|
|
12
7
|
/**
|
|
@@ -64,181 +59,8 @@ export function Attribute(node, context) {
|
|
|
64
59
|
context.state.analysis.uses_event_attributes = true;
|
|
65
60
|
}
|
|
66
61
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (delegated_event !== null) {
|
|
71
|
-
if (delegated_event.hoisted) {
|
|
72
|
-
delegated_event.function.metadata.hoisted = true;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
node.metadata.delegated = delegated_event;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** @type {DelegatedEvent} */
|
|
82
|
-
const unhoisted = { hoisted: false };
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Checks if given event attribute can be delegated/hoisted and returns the corresponding info if so
|
|
86
|
-
* @param {string} event_name
|
|
87
|
-
* @param {Expression | null} handler
|
|
88
|
-
* @param {Context} context
|
|
89
|
-
* @returns {null | DelegatedEvent}
|
|
90
|
-
*/
|
|
91
|
-
function get_delegated_event(event_name, handler, context) {
|
|
92
|
-
// Handle delegated event handlers. Bail out if not a delegated event.
|
|
93
|
-
if (!handler || !is_delegated(event_name)) {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// If we are not working with a RegularElement, then bail out.
|
|
98
|
-
const element = context.path.at(-1);
|
|
99
|
-
if (element?.type !== 'RegularElement') {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** @type {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression | null} */
|
|
104
|
-
let target_function = null;
|
|
105
|
-
let binding = null;
|
|
106
|
-
|
|
107
|
-
if (element.metadata.has_spread) {
|
|
108
|
-
// event attribute becomes part of the dynamic spread array
|
|
109
|
-
return unhoisted;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (handler.type === 'ArrowFunctionExpression' || handler.type === 'FunctionExpression') {
|
|
113
|
-
target_function = handler;
|
|
114
|
-
} else if (handler.type === 'Identifier') {
|
|
115
|
-
binding = context.state.scope.get(handler.name);
|
|
116
|
-
|
|
117
|
-
if (context.state.analysis.module.scope.references.has(handler.name)) {
|
|
118
|
-
// If a binding with the same name is referenced in the module scope (even if not declared there), bail out
|
|
119
|
-
return unhoisted;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (binding != null) {
|
|
123
|
-
for (const { path } of binding.references) {
|
|
124
|
-
const parent = path.at(-1);
|
|
125
|
-
if (parent === undefined) return unhoisted;
|
|
126
|
-
|
|
127
|
-
const grandparent = path.at(-2);
|
|
128
|
-
|
|
129
|
-
/** @type {AST.RegularElement | null} */
|
|
130
|
-
let element = null;
|
|
131
|
-
/** @type {string | null} */
|
|
132
|
-
let event_name = null;
|
|
133
|
-
if (parent.type === 'OnDirective') {
|
|
134
|
-
element = /** @type {AST.RegularElement} */ (grandparent);
|
|
135
|
-
event_name = parent.name;
|
|
136
|
-
} else if (
|
|
137
|
-
parent.type === 'ExpressionTag' &&
|
|
138
|
-
grandparent?.type === 'Attribute' &&
|
|
139
|
-
is_event_attribute(grandparent)
|
|
140
|
-
) {
|
|
141
|
-
element = /** @type {AST.RegularElement} */ (path.at(-3));
|
|
142
|
-
const attribute = /** @type {AST.Attribute} */ (grandparent);
|
|
143
|
-
event_name = get_attribute_event_name(attribute.name);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (element && event_name) {
|
|
147
|
-
if (
|
|
148
|
-
element.type !== 'RegularElement' ||
|
|
149
|
-
element.metadata.has_spread ||
|
|
150
|
-
!is_delegated(event_name)
|
|
151
|
-
) {
|
|
152
|
-
return unhoisted;
|
|
153
|
-
}
|
|
154
|
-
} else if (parent.type !== 'FunctionDeclaration' && parent.type !== 'VariableDeclarator') {
|
|
155
|
-
return unhoisted;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
62
|
+
node.metadata.delegated =
|
|
63
|
+
parent?.type === 'RegularElement' && can_delegate_event(node.name.slice(2));
|
|
158
64
|
}
|
|
159
|
-
|
|
160
|
-
// If the binding is exported, bail out
|
|
161
|
-
if (context.state.analysis.exports.find((node) => node.name === handler.name)) {
|
|
162
|
-
return unhoisted;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (binding?.is_function()) {
|
|
166
|
-
target_function = binding.initial;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// If we can't find a function, or the function has multiple parameters, bail out
|
|
171
|
-
if (target_function == null || target_function.params.length > 1) {
|
|
172
|
-
return unhoisted;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const visited_references = new Set();
|
|
176
|
-
const scope = target_function.metadata.scope;
|
|
177
|
-
for (const [reference] of scope.references) {
|
|
178
|
-
// Bail out if the arguments keyword is used or $host is referenced
|
|
179
|
-
if (reference === 'arguments' || reference === '$host') return unhoisted;
|
|
180
|
-
// Bail out if references a store subscription
|
|
181
|
-
if (scope.get(`$${reference}`)?.kind === 'store_sub') return unhoisted;
|
|
182
|
-
|
|
183
|
-
const binding = scope.get(reference);
|
|
184
|
-
const local_binding = context.state.scope.get(reference);
|
|
185
|
-
|
|
186
|
-
// if the function access a snippet that can't be hoisted we bail out
|
|
187
|
-
if (
|
|
188
|
-
local_binding !== null &&
|
|
189
|
-
local_binding.initial?.type === 'SnippetBlock' &&
|
|
190
|
-
!local_binding.initial.metadata.can_hoist
|
|
191
|
-
) {
|
|
192
|
-
return unhoisted;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// If we are referencing a binding that is shadowed in another scope then bail out (unless it's declared within the function).
|
|
196
|
-
if (
|
|
197
|
-
local_binding !== null &&
|
|
198
|
-
binding !== null &&
|
|
199
|
-
local_binding.node !== binding.node &&
|
|
200
|
-
scope.declarations.get(reference) !== binding
|
|
201
|
-
) {
|
|
202
|
-
return unhoisted;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// If we have multiple references to the same store using $ prefix, bail out.
|
|
206
|
-
if (
|
|
207
|
-
binding !== null &&
|
|
208
|
-
binding.kind === 'store_sub' &&
|
|
209
|
-
visited_references.has(reference.slice(1))
|
|
210
|
-
) {
|
|
211
|
-
return unhoisted;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// If we reference the index within an each block, then bail out.
|
|
215
|
-
if (binding !== null && binding.initial?.type === 'EachBlock') return unhoisted;
|
|
216
|
-
|
|
217
|
-
if (
|
|
218
|
-
binding !== null &&
|
|
219
|
-
// Bail out if the binding is a rest param
|
|
220
|
-
(binding.declaration_kind === 'rest_param' ||
|
|
221
|
-
// Bail out if we reference anything from the EachBlock (for now) that mutates in non-runes mode,
|
|
222
|
-
(((!context.state.analysis.runes && binding.kind === 'each') ||
|
|
223
|
-
// or any normal not reactive bindings that are mutated.
|
|
224
|
-
binding.kind === 'normal') &&
|
|
225
|
-
binding.updated))
|
|
226
|
-
) {
|
|
227
|
-
return unhoisted;
|
|
228
|
-
}
|
|
229
|
-
visited_references.add(reference);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return { hoisted: true, function: target_function };
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* @param {string} event_name
|
|
237
|
-
*/
|
|
238
|
-
function get_attribute_event_name(event_name) {
|
|
239
|
-
event_name = event_name.slice(2);
|
|
240
|
-
if (is_capture_event(event_name)) {
|
|
241
|
-
event_name = event_name.slice(0, -7);
|
|
242
65
|
}
|
|
243
|
-
return event_name;
|
|
244
66
|
}
|
|
@@ -6,13 +6,6 @@
|
|
|
6
6
|
* @param {Context} context
|
|
7
7
|
*/
|
|
8
8
|
export function visit_function(node, context) {
|
|
9
|
-
// TODO retire this in favour of a more general solution based on bindings
|
|
10
|
-
node.metadata = {
|
|
11
|
-
hoisted: false,
|
|
12
|
-
hoisted_params: [],
|
|
13
|
-
scope: context.state.scope
|
|
14
|
-
};
|
|
15
|
-
|
|
16
9
|
if (context.state.expression) {
|
|
17
10
|
for (const [name] of context.state.scope.references) {
|
|
18
11
|
const binding = context.state.scope.get(name);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/** @import {
|
|
1
|
+
/** @import { BlockStatement, Expression, Identifier } from 'estree' */
|
|
2
2
|
/** @import { Binding } from '#compiler' */
|
|
3
|
-
/** @import { ClientTransformState, ComponentClientTransformState
|
|
3
|
+
/** @import { ClientTransformState, ComponentClientTransformState } from './types.js' */
|
|
4
4
|
/** @import { Analysis } from '../../types.js' */
|
|
5
5
|
/** @import { Scope } from '../../scope.js' */
|
|
6
6
|
import * as b from '#compiler/builders';
|
|
@@ -12,9 +12,6 @@ import {
|
|
|
12
12
|
PROPS_IS_UPDATED,
|
|
13
13
|
PROPS_IS_BINDABLE
|
|
14
14
|
} from '../../../../constants.js';
|
|
15
|
-
import { dev } from '../../../state.js';
|
|
16
|
-
import { walk } from 'zimmerframe';
|
|
17
|
-
import { validate_mutation } from './visitors/shared/utils.js';
|
|
18
15
|
|
|
19
16
|
/**
|
|
20
17
|
* @param {Binding} binding
|
|
@@ -46,125 +43,6 @@ export function build_getter(node, state) {
|
|
|
46
43
|
return node;
|
|
47
44
|
}
|
|
48
45
|
|
|
49
|
-
/**
|
|
50
|
-
* @param {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} node
|
|
51
|
-
* @param {ComponentContext} context
|
|
52
|
-
* @returns {Pattern[]}
|
|
53
|
-
*/
|
|
54
|
-
function get_hoisted_params(node, context) {
|
|
55
|
-
const scope = context.state.scope;
|
|
56
|
-
|
|
57
|
-
/** @type {Identifier[]} */
|
|
58
|
-
const params = [];
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* We only want to push if it's not already present to avoid name clashing
|
|
62
|
-
* @param {Identifier} id
|
|
63
|
-
*/
|
|
64
|
-
function push_unique(id) {
|
|
65
|
-
if (!params.find((param) => param.name === id.name)) {
|
|
66
|
-
params.push(id);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
for (const [reference] of scope.references) {
|
|
71
|
-
let binding = scope.get(reference);
|
|
72
|
-
|
|
73
|
-
if (binding !== null && !scope.declarations.has(reference) && binding.initial !== node) {
|
|
74
|
-
if (binding.kind === 'store_sub') {
|
|
75
|
-
// We need both the subscription for getting the value and the store for updating
|
|
76
|
-
push_unique(b.id(binding.node.name));
|
|
77
|
-
binding = /** @type {Binding} */ (scope.get(binding.node.name.slice(1)));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
let expression = context.state.transform[reference]?.read(b.id(binding.node.name));
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
// If it's a destructured derived binding, then we can extract the derived signal reference and use that.
|
|
84
|
-
// TODO this code is bad, we need to kill it
|
|
85
|
-
expression != null &&
|
|
86
|
-
typeof expression !== 'function' &&
|
|
87
|
-
expression.type === 'MemberExpression' &&
|
|
88
|
-
expression.object.type === 'CallExpression' &&
|
|
89
|
-
expression.object.callee.type === 'Identifier' &&
|
|
90
|
-
expression.object.callee.name === '$.get' &&
|
|
91
|
-
expression.object.arguments[0].type === 'Identifier'
|
|
92
|
-
) {
|
|
93
|
-
push_unique(b.id(expression.object.arguments[0].name));
|
|
94
|
-
} else if (
|
|
95
|
-
// If we are referencing a simple $$props value, then we need to reference the object property instead
|
|
96
|
-
(binding.kind === 'prop' || binding.kind === 'bindable_prop') &&
|
|
97
|
-
!is_prop_source(binding, context.state)
|
|
98
|
-
) {
|
|
99
|
-
push_unique(b.id('$$props'));
|
|
100
|
-
} else if (
|
|
101
|
-
// imports don't need to be hoisted
|
|
102
|
-
binding.declaration_kind !== 'import'
|
|
103
|
-
) {
|
|
104
|
-
// create a copy to remove start/end tags which would mess up source maps
|
|
105
|
-
push_unique(b.id(binding.node.name));
|
|
106
|
-
// rest props are often accessed through the $$props object for optimization reasons,
|
|
107
|
-
// but we can't know if the delegated event handler will use it, so we need to add both as params
|
|
108
|
-
if (binding.kind === 'rest_prop' && context.state.analysis.runes) {
|
|
109
|
-
push_unique(b.id('$$props'));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (dev) {
|
|
116
|
-
// this is a little hacky, but necessary for ownership validation
|
|
117
|
-
// to work inside hoisted event handlers
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* @param {AssignmentExpression | UpdateExpression} node
|
|
121
|
-
* @param {{ next: () => void, stop: () => void }} context
|
|
122
|
-
*/
|
|
123
|
-
function visit(node, { next, stop }) {
|
|
124
|
-
if (validate_mutation(node, /** @type {any} */ (context), node) !== node) {
|
|
125
|
-
params.push(b.id('$$ownership_validator'));
|
|
126
|
-
stop();
|
|
127
|
-
} else {
|
|
128
|
-
next();
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
walk(/** @type {Node} */ (node), null, {
|
|
133
|
-
AssignmentExpression: visit,
|
|
134
|
-
UpdateExpression: visit
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return params;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* @param {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} node
|
|
143
|
-
* @param {ComponentContext} context
|
|
144
|
-
* @returns {Pattern[]}
|
|
145
|
-
*/
|
|
146
|
-
export function build_hoisted_params(node, context) {
|
|
147
|
-
const hoisted_params = get_hoisted_params(node, context);
|
|
148
|
-
node.metadata.hoisted_params = hoisted_params;
|
|
149
|
-
|
|
150
|
-
/** @type {Pattern[]} */
|
|
151
|
-
const params = [];
|
|
152
|
-
|
|
153
|
-
if (node.params.length === 0) {
|
|
154
|
-
if (hoisted_params.length > 0) {
|
|
155
|
-
// For the event object
|
|
156
|
-
params.push(b.id(context.state.scope.generate('_')));
|
|
157
|
-
}
|
|
158
|
-
} else {
|
|
159
|
-
for (const param of node.params) {
|
|
160
|
-
params.push(/** @type {Pattern} */ (context.visit(param)));
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
params.push(...hoisted_params);
|
|
165
|
-
return params;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
46
|
/**
|
|
169
47
|
* @param {Binding} binding
|
|
170
48
|
* @param {ComponentClientTransformState} state
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
/** @import { FunctionDeclaration } from 'estree' */
|
|
2
2
|
/** @import { ComponentContext } from '../types' */
|
|
3
|
-
import { build_hoisted_params } from '../utils.js';
|
|
4
|
-
import * as b from '#compiler/builders';
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
5
|
* @param {FunctionDeclaration} node
|
|
@@ -10,14 +8,5 @@ import * as b from '#compiler/builders';
|
|
|
10
8
|
export function FunctionDeclaration(node, context) {
|
|
11
9
|
const state = { ...context.state, in_constructor: false, in_derived: false };
|
|
12
10
|
|
|
13
|
-
if (node.metadata?.hoisted === true) {
|
|
14
|
-
const params = build_hoisted_params(node, context);
|
|
15
|
-
const body = context.visit(node.body, state);
|
|
16
|
-
|
|
17
|
-
context.state.hoisted.push(/** @type {FunctionDeclaration} */ ({ ...node, params, body }));
|
|
18
|
-
|
|
19
|
-
return b.empty;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
11
|
context.next(state);
|
|
23
12
|
}
|
|
@@ -7,7 +7,6 @@ import * as b from '#compiler/builders';
|
|
|
7
7
|
import * as assert from '../../../../utils/assert.js';
|
|
8
8
|
import { get_rune } from '../../../scope.js';
|
|
9
9
|
import { get_prop_source, is_prop_source, is_state_source, should_proxy } from '../utils.js';
|
|
10
|
-
import { is_hoisted_function } from '../../utils.js';
|
|
11
10
|
import { get_value } from './shared/declarations.js';
|
|
12
11
|
|
|
13
12
|
/**
|
|
@@ -32,13 +31,6 @@ export function VariableDeclaration(node, context) {
|
|
|
32
31
|
rune === '$state.snapshot' ||
|
|
33
32
|
rune === '$host'
|
|
34
33
|
) {
|
|
35
|
-
if (init != null && is_hoisted_function(init)) {
|
|
36
|
-
context.state.hoisted.push(
|
|
37
|
-
b.const(declarator.id, /** @type {Expression} */ (context.visit(init)))
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
34
|
declarations.push(/** @type {VariableDeclarator} */ (context.visit(declarator)));
|
|
43
35
|
continue;
|
|
44
36
|
}
|
|
@@ -295,16 +287,6 @@ export function VariableDeclaration(node, context) {
|
|
|
295
287
|
const has_props = bindings.some((binding) => binding.kind === 'bindable_prop');
|
|
296
288
|
|
|
297
289
|
if (!has_state && !has_props) {
|
|
298
|
-
const init = declarator.init;
|
|
299
|
-
|
|
300
|
-
if (init != null && is_hoisted_function(init)) {
|
|
301
|
-
context.state.hoisted.push(
|
|
302
|
-
b.const(declarator.id, /** @type {Expression} */ (context.visit(init)))
|
|
303
|
-
);
|
|
304
|
-
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
290
|
declarations.push(/** @type {VariableDeclarator} */ (context.visit(declarator)));
|
|
309
291
|
continue;
|
|
310
292
|
}
|
|
@@ -26,40 +26,12 @@ export function visit_event_attribute(node, context) {
|
|
|
26
26
|
let handler = build_event_handler(tag.expression, tag.metadata.expression, context);
|
|
27
27
|
|
|
28
28
|
if (node.metadata.delegated) {
|
|
29
|
-
let delegated_assignment;
|
|
30
|
-
|
|
31
29
|
if (!context.state.events.has(event_name)) {
|
|
32
30
|
context.state.events.add(event_name);
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
// Hoist function if we can, otherwise we leave the function as is
|
|
36
|
-
if (node.metadata.delegated.hoisted) {
|
|
37
|
-
if (node.metadata.delegated.function === tag.expression) {
|
|
38
|
-
const func_name = context.state.scope.root.unique('on_' + event_name);
|
|
39
|
-
context.state.hoisted.push(b.var(func_name, handler));
|
|
40
|
-
handler = func_name;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const hoisted_params = /** @type {Expression[]} */ (
|
|
44
|
-
node.metadata.delegated.function.metadata.hoisted_params
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
// When we hoist a function we assign an array with the function and all
|
|
48
|
-
// hoisted closure params.
|
|
49
|
-
if (hoisted_params) {
|
|
50
|
-
const args = [handler, ...hoisted_params];
|
|
51
|
-
delegated_assignment = b.array(args);
|
|
52
|
-
} else {
|
|
53
|
-
delegated_assignment = handler;
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
delegated_assignment = handler;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
33
|
context.state.init.push(
|
|
60
|
-
b.stmt(
|
|
61
|
-
b.assignment('=', b.member(context.state.node, '__' + event_name), delegated_assignment)
|
|
62
|
-
)
|
|
34
|
+
b.stmt(b.assignment('=', b.member(context.state.node, '__' + event_name), handler))
|
|
63
35
|
);
|
|
64
36
|
} else {
|
|
65
37
|
const statement = b.stmt(
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
/** @import { ArrowFunctionExpression, FunctionExpression, Node } from 'estree' */
|
|
2
2
|
/** @import { ComponentContext } from '../../types' */
|
|
3
|
-
import { build_hoisted_params } from '../../utils.js';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* @param {ArrowFunctionExpression | FunctionExpression} node
|
|
7
6
|
* @param {ComponentContext} context
|
|
8
7
|
*/
|
|
9
8
|
export const visit_function = (node, context) => {
|
|
10
|
-
const metadata = node.metadata;
|
|
11
|
-
|
|
12
9
|
let state = { ...context.state, in_constructor: false, in_derived: false };
|
|
13
10
|
|
|
14
11
|
if (node.type === 'FunctionExpression') {
|
|
@@ -16,15 +13,5 @@ export const visit_function = (node, context) => {
|
|
|
16
13
|
state.in_constructor = parent.type === 'MethodDefinition' && parent.kind === 'constructor';
|
|
17
14
|
}
|
|
18
15
|
|
|
19
|
-
if (metadata?.hoisted === true) {
|
|
20
|
-
const params = build_hoisted_params(node, context);
|
|
21
|
-
|
|
22
|
-
return /** @type {FunctionExpression} */ ({
|
|
23
|
-
...node,
|
|
24
|
-
params,
|
|
25
|
-
body: context.visit(node.body, state)
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
16
|
context.next(state);
|
|
30
17
|
};
|
|
@@ -1,36 +1,17 @@
|
|
|
1
|
-
/** @import { Context } from 'zimmerframe' */
|
|
2
1
|
/** @import { TransformState } from './types.js' */
|
|
3
2
|
/** @import { AST, Binding, Namespace, ValidatedCompileOptions } from '#compiler' */
|
|
4
3
|
/** @import { Node, Expression, CallExpression, MemberExpression } from 'estree' */
|
|
5
4
|
import {
|
|
6
5
|
regex_ends_with_whitespaces,
|
|
7
6
|
regex_not_whitespace,
|
|
8
|
-
regex_starts_with_newline,
|
|
9
7
|
regex_starts_with_whitespaces
|
|
10
8
|
} from '../patterns.js';
|
|
11
|
-
import * as b from '#compiler/builders';
|
|
12
9
|
import * as e from '../../errors.js';
|
|
13
10
|
import { walk } from 'zimmerframe';
|
|
14
11
|
import { extract_identifiers } from '../../utils/ast.js';
|
|
15
12
|
import check_graph_for_cycles from '../2-analyze/utils/check_graph_for_cycles.js';
|
|
16
13
|
import is_reference from 'is-reference';
|
|
17
14
|
import { set_scope } from '../scope.js';
|
|
18
|
-
import { dev } from '../../state.js';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @param {Node} node
|
|
22
|
-
* @returns {boolean}
|
|
23
|
-
*/
|
|
24
|
-
export function is_hoisted_function(node) {
|
|
25
|
-
if (
|
|
26
|
-
node.type === 'ArrowFunctionExpression' ||
|
|
27
|
-
node.type === 'FunctionExpression' ||
|
|
28
|
-
node.type === 'FunctionDeclaration'
|
|
29
|
-
) {
|
|
30
|
-
return node.metadata?.hoisted === true;
|
|
31
|
-
}
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
15
|
|
|
35
16
|
/**
|
|
36
17
|
* Match Svelte 4 behaviour by sorting ConstTag nodes in topological order
|
|
@@ -42,8 +42,7 @@ export function arrow(params, body, async = false) {
|
|
|
42
42
|
body,
|
|
43
43
|
expression: body.type !== 'BlockStatement',
|
|
44
44
|
generator: false,
|
|
45
|
-
async
|
|
46
|
-
metadata: /** @type {any} */ (null) // should not be used by codegen
|
|
45
|
+
async
|
|
47
46
|
};
|
|
48
47
|
}
|
|
49
48
|
|
|
@@ -237,8 +236,7 @@ export function function_declaration(id, params, body, async = false) {
|
|
|
237
236
|
params,
|
|
238
237
|
body,
|
|
239
238
|
generator: false,
|
|
240
|
-
async
|
|
241
|
-
metadata: /** @type {any} */ (null) // should not be used by codegen
|
|
239
|
+
async
|
|
242
240
|
};
|
|
243
241
|
}
|
|
244
242
|
|
|
@@ -595,8 +593,7 @@ function function_builder(id, params, body, async = false) {
|
|
|
595
593
|
params,
|
|
596
594
|
body,
|
|
597
595
|
generator: false,
|
|
598
|
-
async
|
|
599
|
-
metadata: /** @type {any} */ (null) // should not be used by codegen
|
|
596
|
+
async
|
|
600
597
|
};
|
|
601
598
|
}
|
|
602
599
|
|
package/src/index-client.js
CHANGED
|
@@ -241,7 +241,7 @@ function init_update_callbacks(context) {
|
|
|
241
241
|
return (l.u ??= { a: [], b: [], m: [] });
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
export { flushSync } from './internal/client/reactivity/batch.js';
|
|
244
|
+
export { flushSync, fork } from './internal/client/reactivity/batch.js';
|
|
245
245
|
export {
|
|
246
246
|
createContext,
|
|
247
247
|
getContext,
|
package/src/index-server.js
CHANGED
|
@@ -13,13 +13,14 @@ export const INERT = 1 << 13;
|
|
|
13
13
|
export const DESTROYED = 1 << 14;
|
|
14
14
|
|
|
15
15
|
// Flags exclusive to effects
|
|
16
|
+
/** Set once an effect that should run synchronously has run */
|
|
16
17
|
export const EFFECT_RAN = 1 << 15;
|
|
17
18
|
/**
|
|
18
19
|
* 'Transparent' effects do not create a transition boundary.
|
|
19
20
|
* This is on a block effect 99% of the time but may also be on a branch effect if its parent block effect was pruned
|
|
20
21
|
*/
|
|
21
22
|
export const EFFECT_TRANSPARENT = 1 << 16;
|
|
22
|
-
export const
|
|
23
|
+
export const EAGER_EFFECT = 1 << 17;
|
|
23
24
|
export const HEAD_EFFECT = 1 << 18;
|
|
24
25
|
export const EFFECT_PRESERVED = 1 << 19;
|
|
25
26
|
export const USER_EFFECT = 1 << 20;
|
|
@@ -128,7 +128,11 @@ export function setContext(key, context) {
|
|
|
128
128
|
|
|
129
129
|
if (async_mode_flag) {
|
|
130
130
|
var flags = /** @type {Effect} */ (active_effect).f;
|
|
131
|
-
var valid =
|
|
131
|
+
var valid =
|
|
132
|
+
!active_reaction &&
|
|
133
|
+
(flags & BRANCH_EFFECT) !== 0 &&
|
|
134
|
+
// pop() runs synchronously, so this indicates we're setting context after an await
|
|
135
|
+
!(/** @type {ComponentContext} */ (component_context).i);
|
|
132
136
|
|
|
133
137
|
if (!valid) {
|
|
134
138
|
e.set_context_after_init();
|
|
@@ -173,6 +177,7 @@ export function getAllContexts() {
|
|
|
173
177
|
export function push(props, runes = false, fn) {
|
|
174
178
|
component_context = {
|
|
175
179
|
p: component_context,
|
|
180
|
+
i: false,
|
|
176
181
|
c: null,
|
|
177
182
|
e: null,
|
|
178
183
|
s: props,
|
|
@@ -208,6 +213,8 @@ export function pop(component) {
|
|
|
208
213
|
context.x = component;
|
|
209
214
|
}
|
|
210
215
|
|
|
216
|
+
context.i = true;
|
|
217
|
+
|
|
211
218
|
component_context = context.p;
|
|
212
219
|
|
|
213
220
|
if (DEV) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { UNINITIALIZED } from '../../../constants.js';
|
|
2
2
|
import { snapshot } from '../../shared/clone.js';
|
|
3
|
-
import {
|
|
3
|
+
import { eager_effect, render_effect, validate_effect } from '../reactivity/effects.js';
|
|
4
4
|
import { untrack } from '../runtime.js';
|
|
5
5
|
import { get_stack } from './tracing.js';
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ export function inspect(get_value, inspector, show_stack = false) {
|
|
|
19
19
|
// stack traces. As a consequence, reading the value might result
|
|
20
20
|
// in an error (an `$inspect(object.property)` will run before the
|
|
21
21
|
// `{#if object}...{/if}` that contains it)
|
|
22
|
-
|
|
22
|
+
eager_effect(() => {
|
|
23
23
|
try {
|
|
24
24
|
var value = get_value();
|
|
25
25
|
} catch (e) {
|
|
@@ -33,8 +33,17 @@ export function inspect(get_value, inspector, show_stack = false) {
|
|
|
33
33
|
inspector(...snap);
|
|
34
34
|
|
|
35
35
|
if (!initial) {
|
|
36
|
+
const stack = get_stack('$inspect(...)');
|
|
36
37
|
// eslint-disable-next-line no-console
|
|
37
|
-
|
|
38
|
+
|
|
39
|
+
if (stack) {
|
|
40
|
+
// eslint-disable-next-line no-console
|
|
41
|
+
console.groupCollapsed('stack trace');
|
|
42
|
+
// eslint-disable-next-line no-console
|
|
43
|
+
console.log(stack);
|
|
44
|
+
// eslint-disable-next-line no-console
|
|
45
|
+
console.groupEnd();
|
|
46
|
+
}
|
|
38
47
|
}
|
|
39
48
|
} else {
|
|
40
49
|
inspector(initial ? 'init' : 'update', ...snap);
|