svelte 5.41.3 → 5.42.0
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/index.js +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/server/visitors/shared/utils.js +2 -2
- package/src/compiler/phases/3-transform/utils.js +0 -19
- package/src/compiler/phases/nodes.js +1 -1
- package/src/compiler/utils/ast.js +8 -4
- package/src/compiler/utils/builders.js +5 -8
- package/src/index-client.js +1 -1
- package/src/index-server.js +4 -0
- package/src/internal/client/constants.js +6 -2
- 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/boundary.js +29 -3
- 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 +160 -22
- package/src/internal/client/reactivity/deriveds.js +5 -5
- package/src/internal/client/reactivity/effects.js +13 -5
- 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
|
@@ -6,7 +6,7 @@ import { walk } from 'zimmerframe';
|
|
|
6
6
|
import { parse } from '../1-parse/acorn.js';
|
|
7
7
|
import * as e from '../../errors.js';
|
|
8
8
|
import * as w from '../../warnings.js';
|
|
9
|
-
import { extract_identifiers } from '../../utils/ast.js';
|
|
9
|
+
import { extract_identifiers, has_await_expression } from '../../utils/ast.js';
|
|
10
10
|
import * as b from '#compiler/builders';
|
|
11
11
|
import { Scope, ScopeRoot, create_scopes, get_rune, set_scope } from '../scope.js';
|
|
12
12
|
import check_graph_for_cycles from './utils/check_graph_for_cycles.js';
|
|
@@ -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
|
};
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import * as b from '#compiler/builders';
|
|
13
13
|
import { sanitize_template_string } from '../../../../../utils/sanitize_template_string.js';
|
|
14
14
|
import { regex_whitespaces_strict } from '../../../../patterns.js';
|
|
15
|
-
import {
|
|
15
|
+
import { has_await_expression } from '../../../../../utils/ast.js';
|
|
16
16
|
|
|
17
17
|
/** Opens an if/each block, so that we can remove nodes in the case of a mismatch */
|
|
18
18
|
export const block_open = b.literal(BLOCK_OPEN);
|
|
@@ -315,7 +315,7 @@ export class PromiseOptimiser {
|
|
|
315
315
|
|
|
316
316
|
const promises = b.array(
|
|
317
317
|
this.expressions.map((expression) => {
|
|
318
|
-
return expression.type === 'AwaitExpression' && !
|
|
318
|
+
return expression.type === 'AwaitExpression' && !has_await_expression(expression.argument)
|
|
319
319
|
? expression.argument
|
|
320
320
|
: b.call(b.thunk(expression, true));
|
|
321
321
|
})
|
|
@@ -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
|
|
@@ -611,16 +611,20 @@ export function build_assignment_value(operator, left, right) {
|
|
|
611
611
|
}
|
|
612
612
|
|
|
613
613
|
/**
|
|
614
|
-
* @param {ESTree.
|
|
614
|
+
* @param {ESTree.Node} node
|
|
615
615
|
*/
|
|
616
|
-
export function
|
|
616
|
+
export function has_await_expression(node) {
|
|
617
617
|
let has_await = false;
|
|
618
618
|
|
|
619
|
-
walk(
|
|
619
|
+
walk(node, null, {
|
|
620
620
|
AwaitExpression(_node, context) {
|
|
621
621
|
has_await = true;
|
|
622
622
|
context.stop();
|
|
623
|
-
}
|
|
623
|
+
},
|
|
624
|
+
// don't traverse into these
|
|
625
|
+
FunctionDeclaration() {},
|
|
626
|
+
FunctionExpression() {},
|
|
627
|
+
ArrowFunctionExpression() {}
|
|
624
628
|
});
|
|
625
629
|
|
|
626
630
|
return has_await;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { walk } from 'zimmerframe';
|
|
3
3
|
import { regex_is_valid_identifier } from '../phases/patterns.js';
|
|
4
4
|
import { sanitize_template_string } from './sanitize_template_string.js';
|
|
5
|
-
import {
|
|
5
|
+
import { has_await_expression } from './ast.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @param {Array<ESTree.Expression | ESTree.SpreadElement | null>} elements
|
|
@@ -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
|
|
|
@@ -451,7 +449,7 @@ export function thunk(expression, async = false) {
|
|
|
451
449
|
export function unthunk(expression) {
|
|
452
450
|
// optimize `async () => await x()`, but not `async () => await x(await y)`
|
|
453
451
|
if (expression.async && expression.body.type === 'AwaitExpression') {
|
|
454
|
-
if (!
|
|
452
|
+
if (!has_await_expression(expression.body.argument)) {
|
|
455
453
|
return unthunk(arrow(expression.params, expression.body.argument));
|
|
456
454
|
}
|
|
457
455
|
}
|
|
@@ -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