svelte 5.2.4 → 5.2.6
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/elements.d.ts +18 -0
- package/package.json +1 -1
- package/src/compiler/phases/2-analyze/visitors/Attribute.js +4 -5
- package/src/compiler/phases/2-analyze/visitors/CallExpression.js +0 -2
- package/src/compiler/phases/2-analyze/visitors/ExportDefaultDeclaration.js +7 -2
- package/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js +0 -18
- package/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js +2 -23
- package/src/compiler/phases/2-analyze/visitors/ExpressionTag.js +5 -0
- package/src/compiler/phases/2-analyze/visitors/Identifier.js +3 -14
- package/src/compiler/phases/2-analyze/visitors/MemberExpression.js +0 -3
- package/src/compiler/phases/2-analyze/visitors/RegularElement.js +0 -10
- package/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js +0 -1
- package/src/compiler/phases/2-analyze/visitors/shared/utils.js +20 -1
- package/src/compiler/phases/3-transform/client/utils.js +5 -5
- package/src/compiler/phases/3-transform/client/visitors/ClassBody.js +1 -1
- package/src/compiler/phases/3-transform/client/visitors/Fragment.js +5 -5
- package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +20 -56
- package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +14 -54
- package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +37 -55
- package/src/compiler/phases/nodes.js +1 -2
- package/src/compiler/types/index.d.ts +0 -2
- package/src/internal/client/index.js +0 -1
- package/src/utils.js +15 -2
- package/src/version.js +1 -1
package/elements.d.ts
CHANGED
|
@@ -60,6 +60,10 @@ export type AnimationEventHandler<T extends EventTarget> = EventHandler<Animatio
|
|
|
60
60
|
export type TransitionEventHandler<T extends EventTarget> = EventHandler<TransitionEvent, T>;
|
|
61
61
|
export type MessageEventHandler<T extends EventTarget> = EventHandler<MessageEvent, T>;
|
|
62
62
|
export type ToggleEventHandler<T extends EventTarget> = EventHandler<ToggleEvent, T>;
|
|
63
|
+
export type ContentVisibilityAutoStateChangeEventHandler<T extends EventTarget> = EventHandler<
|
|
64
|
+
ContentVisibilityAutoStateChangeEvent,
|
|
65
|
+
T
|
|
66
|
+
>;
|
|
63
67
|
|
|
64
68
|
export type FullAutoFill =
|
|
65
69
|
| AutoFill
|
|
@@ -157,6 +161,20 @@ export interface DOMAttributes<T extends EventTarget> {
|
|
|
157
161
|
ontoggle?: ToggleEventHandler<T> | undefined | null;
|
|
158
162
|
ontogglecapture?: ToggleEventHandler<T> | undefined | null;
|
|
159
163
|
|
|
164
|
+
// Content visibility Events
|
|
165
|
+
'on:contentvisibilityautostatechange'?:
|
|
166
|
+
| ContentVisibilityAutoStateChangeEventHandler<T>
|
|
167
|
+
| undefined
|
|
168
|
+
| null;
|
|
169
|
+
oncontentvisibilityautostatechange?:
|
|
170
|
+
| ContentVisibilityAutoStateChangeEventHandler<T>
|
|
171
|
+
| undefined
|
|
172
|
+
| null;
|
|
173
|
+
oncontentvisibilityautostatechangecapture?:
|
|
174
|
+
| ContentVisibilityAutoStateChangeEventHandler<T>
|
|
175
|
+
| undefined
|
|
176
|
+
| null;
|
|
177
|
+
|
|
160
178
|
// Keyboard Events
|
|
161
179
|
'on:keydown'?: KeyboardEventHandler<T> | undefined | null;
|
|
162
180
|
onkeydown?: KeyboardEventHandler<T> | undefined | null;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** @import { ArrowFunctionExpression, Expression, FunctionDeclaration, FunctionExpression } from 'estree' */
|
|
2
2
|
/** @import { AST, DelegatedEvent, SvelteNode } from '#compiler' */
|
|
3
3
|
/** @import { Context } from '../types' */
|
|
4
|
-
import {
|
|
4
|
+
import { cannot_be_set_statically, is_capture_event, is_delegated } from '../../../../utils.js';
|
|
5
5
|
import {
|
|
6
6
|
get_attribute_chunks,
|
|
7
7
|
get_attribute_expression,
|
|
@@ -30,12 +30,12 @@ export function Attribute(node, context) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
if (node
|
|
33
|
+
if (is_event_attribute(node)) {
|
|
34
34
|
mark_subtree_dynamic(context.path);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
if (
|
|
38
|
-
|
|
37
|
+
if (cannot_be_set_statically(node.name)) {
|
|
38
|
+
mark_subtree_dynamic(context.path);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
if (node.value !== true) {
|
|
@@ -51,7 +51,6 @@ export function Attribute(node, context) {
|
|
|
51
51
|
|
|
52
52
|
node.metadata.expression.has_state ||= chunk.metadata.expression.has_state;
|
|
53
53
|
node.metadata.expression.has_call ||= chunk.metadata.expression.has_call;
|
|
54
|
-
node.metadata.expression.can_inline &&= chunk.metadata.expression.can_inline;
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
if (is_event_attribute(node)) {
|
|
@@ -179,8 +179,6 @@ export function CallExpression(node, context) {
|
|
|
179
179
|
if (!is_pure(node.callee, context) || context.state.expression.dependencies.size > 0) {
|
|
180
180
|
context.state.expression.has_call = true;
|
|
181
181
|
context.state.expression.has_state = true;
|
|
182
|
-
context.state.expression.can_inline = false;
|
|
183
|
-
mark_subtree_dynamic(context.path);
|
|
184
182
|
}
|
|
185
183
|
}
|
|
186
184
|
}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
/** @import { ExportDefaultDeclaration
|
|
1
|
+
/** @import { ExportDefaultDeclaration } from 'estree' */
|
|
2
2
|
/** @import { Context } from '../types' */
|
|
3
3
|
import * as e from '../../../errors.js';
|
|
4
|
+
import { validate_export } from './shared/utils.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @param {ExportDefaultDeclaration} node
|
|
7
8
|
* @param {Context} context
|
|
8
9
|
*/
|
|
9
10
|
export function ExportDefaultDeclaration(node, context) {
|
|
10
|
-
if (context.state.ast_type
|
|
11
|
+
if (!context.state.ast_type /* .svelte.js module */) {
|
|
12
|
+
if (node.declaration.type === 'Identifier') {
|
|
13
|
+
validate_export(node, context.state.scope, node.declaration.name);
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
11
16
|
e.module_illegal_default_export(node);
|
|
12
17
|
}
|
|
13
18
|
|
|
@@ -57,23 +57,5 @@ export function ExportNamedDeclaration(node, context) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
|
|
61
|
-
if (!context.state.ast_type /* .svelte.js module */ || context.state.ast_type === 'module') {
|
|
62
|
-
for (const specified of node.specifiers) {
|
|
63
|
-
if (specified.local.type !== 'Identifier') continue;
|
|
64
|
-
|
|
65
|
-
const binding = context.state.scope.get(specified.local.name);
|
|
66
|
-
|
|
67
|
-
if (!binding) continue;
|
|
68
|
-
|
|
69
|
-
if (binding.kind === 'derived') {
|
|
70
|
-
e.derived_invalid_export(node);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if ((binding.kind === 'state' || binding.kind === 'raw_state') && binding.reassigned) {
|
|
74
|
-
e.state_invalid_export(node);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
60
|
}
|
|
79
61
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
/** @import { ExportSpecifier
|
|
2
|
-
/** @import { Binding } from '#compiler' */
|
|
1
|
+
/** @import { ExportSpecifier } from 'estree' */
|
|
3
2
|
/** @import { Context } from '../types' */
|
|
4
|
-
|
|
5
|
-
import * as e from '../../../errors.js';
|
|
3
|
+
import { validate_export } from './shared/utils.js';
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* @param {ExportSpecifier} node
|
|
@@ -30,22 +28,3 @@ export function ExportSpecifier(node, context) {
|
|
|
30
28
|
validate_export(node, context.state.scope, local_name);
|
|
31
29
|
}
|
|
32
30
|
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
* @param {Node} node
|
|
37
|
-
* @param {Scope} scope
|
|
38
|
-
* @param {string} name
|
|
39
|
-
*/
|
|
40
|
-
function validate_export(node, scope, name) {
|
|
41
|
-
const binding = scope.get(name);
|
|
42
|
-
if (!binding) return;
|
|
43
|
-
|
|
44
|
-
if (binding.kind === 'derived') {
|
|
45
|
-
e.derived_invalid_export(node);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if ((binding.kind === 'state' || binding.kind === 'raw_state') && binding.reassigned) {
|
|
49
|
-
e.state_invalid_export(node);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** @import { Context } from '../types' */
|
|
3
3
|
import { is_tag_valid_with_parent } from '../../../../html-tree-validation.js';
|
|
4
4
|
import * as e from '../../../errors.js';
|
|
5
|
+
import { mark_subtree_dynamic } from './shared/fragment.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @param {AST.ExpressionTag} node
|
|
@@ -14,5 +15,9 @@ export function ExpressionTag(node, context) {
|
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
// TODO ideally we wouldn't do this here, we'd just do it on encountering
|
|
19
|
+
// an `Identifier` within the tag. But we currently need to handle `{42}` etc
|
|
20
|
+
mark_subtree_dynamic(context.path);
|
|
21
|
+
|
|
17
22
|
context.next({ ...context.state, expression: node.metadata.expression });
|
|
18
23
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** @import { Expression, Identifier } from 'estree' */
|
|
2
|
+
/** @import { EachBlock } from '#compiler' */
|
|
2
3
|
/** @import { Context } from '../types' */
|
|
3
4
|
import is_reference from 'is-reference';
|
|
4
5
|
import { should_proxy } from '../../3-transform/client/utils.js';
|
|
@@ -19,6 +20,8 @@ export function Identifier(node, context) {
|
|
|
19
20
|
return;
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
mark_subtree_dynamic(context.path);
|
|
24
|
+
|
|
22
25
|
// If we are using arguments outside of a function, then throw an error
|
|
23
26
|
if (
|
|
24
27
|
node.name === 'arguments' &&
|
|
@@ -84,12 +87,6 @@ export function Identifier(node, context) {
|
|
|
84
87
|
}
|
|
85
88
|
}
|
|
86
89
|
|
|
87
|
-
// no binding means global, and we can't inline e.g. `<span>{location}</span>`
|
|
88
|
-
// because it could change between component renders. if there _is_ a
|
|
89
|
-
// binding and it is outside module scope, the expression cannot
|
|
90
|
-
// be inlined (TODO allow inlining in more cases - e.g. primitive consts)
|
|
91
|
-
let can_inline = !!binding && !binding.scope.parent && binding.kind === 'normal';
|
|
92
|
-
|
|
93
90
|
if (binding) {
|
|
94
91
|
if (context.state.expression) {
|
|
95
92
|
context.state.expression.dependencies.add(binding);
|
|
@@ -125,12 +122,4 @@ export function Identifier(node, context) {
|
|
|
125
122
|
w.reactive_declaration_module_script_dependency(node);
|
|
126
123
|
}
|
|
127
124
|
}
|
|
128
|
-
|
|
129
|
-
if (!can_inline) {
|
|
130
|
-
if (context.state.expression) {
|
|
131
|
-
context.state.expression.can_inline = false;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
mark_subtree_dynamic(context.path);
|
|
135
|
-
}
|
|
136
125
|
}
|
|
@@ -20,9 +20,6 @@ export function MemberExpression(node, context) {
|
|
|
20
20
|
|
|
21
21
|
if (context.state.expression && !is_pure(node, context)) {
|
|
22
22
|
context.state.expression.has_state = true;
|
|
23
|
-
context.state.expression.can_inline = false;
|
|
24
|
-
|
|
25
|
-
mark_subtree_dynamic(context.path);
|
|
26
23
|
}
|
|
27
24
|
|
|
28
25
|
if (!is_safe_identifier(node, context.state.scope)) {
|
|
@@ -75,16 +75,6 @@ export function RegularElement(node, context) {
|
|
|
75
75
|
node.attributes.push(create_attribute('value', child.start, child.end, [child]));
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
if (
|
|
79
|
-
node.attributes.some(
|
|
80
|
-
(attribute) =>
|
|
81
|
-
attribute.type === 'Attribute' &&
|
|
82
|
-
(attribute.name === 'autofocus' || attribute.name === 'muted')
|
|
83
|
-
)
|
|
84
|
-
) {
|
|
85
|
-
mark_subtree_dynamic(context.path);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
78
|
const binding = context.state.scope.get(node.name);
|
|
89
79
|
if (
|
|
90
80
|
binding !== null &&
|
|
@@ -10,7 +10,6 @@ export function TaggedTemplateExpression(node, context) {
|
|
|
10
10
|
if (context.state.expression && !is_pure(node.tag, context)) {
|
|
11
11
|
context.state.expression.has_call = true;
|
|
12
12
|
context.state.expression.has_state = true;
|
|
13
|
-
context.state.expression.can_inline = false;
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
if (node.tag.type === 'Identifier') {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { AssignmentExpression, Expression, Literal, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */
|
|
1
|
+
/** @import { AssignmentExpression, Expression, Literal, Node, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */
|
|
2
2
|
/** @import { AST, Binding } from '#compiler' */
|
|
3
3
|
/** @import { AnalysisState, Context } from '../../types' */
|
|
4
4
|
/** @import { Scope } from '../../../scope' */
|
|
@@ -263,3 +263,22 @@ export function validate_identifier_name(binding, function_depth) {
|
|
|
263
263
|
}
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Checks that the exported name is not a derived or reassigned state variable.
|
|
269
|
+
* @param {Node} node
|
|
270
|
+
* @param {Scope} scope
|
|
271
|
+
* @param {string} name
|
|
272
|
+
*/
|
|
273
|
+
export function validate_export(node, scope, name) {
|
|
274
|
+
const binding = scope.get(name);
|
|
275
|
+
if (!binding) return;
|
|
276
|
+
|
|
277
|
+
if (binding.kind === 'derived') {
|
|
278
|
+
e.derived_invalid_export(node);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if ((binding.kind === 'state' || binding.kind === 'raw_state') && binding.reassigned) {
|
|
282
|
+
e.state_invalid_export(node);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
/** @import { ClientTransformState, ComponentClientTransformState, ComponentContext } from './types.js' */
|
|
4
4
|
/** @import { Analysis } from '../../types.js' */
|
|
5
5
|
/** @import { Scope } from '../../scope.js' */
|
|
6
|
+
import * as b from '../../../utils/builders.js';
|
|
7
|
+
import { extract_identifiers, is_simple_expression } from '../../../utils/ast.js';
|
|
6
8
|
import {
|
|
7
|
-
PROPS_IS_BINDABLE,
|
|
8
|
-
PROPS_IS_IMMUTABLE,
|
|
9
9
|
PROPS_IS_LAZY_INITIAL,
|
|
10
|
+
PROPS_IS_IMMUTABLE,
|
|
10
11
|
PROPS_IS_RUNES,
|
|
11
|
-
PROPS_IS_UPDATED
|
|
12
|
+
PROPS_IS_UPDATED,
|
|
13
|
+
PROPS_IS_BINDABLE
|
|
12
14
|
} from '../../../../constants.js';
|
|
13
15
|
import { dev } from '../../../state.js';
|
|
14
|
-
import { extract_identifiers, is_simple_expression } from '../../../utils/ast.js';
|
|
15
|
-
import * as b from '../../../utils/builders.js';
|
|
16
16
|
import { get_value } from './visitors/shared/declarations.js';
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -28,7 +28,7 @@ export function ClassBody(node, context) {
|
|
|
28
28
|
|
|
29
29
|
for (const definition of node.body) {
|
|
30
30
|
if (
|
|
31
|
-
definition.type === 'PropertyDefinition' &&
|
|
31
|
+
(definition.type === 'PropertyDefinition' || definition.type === 'MethodDefinition') &&
|
|
32
32
|
(definition.key.type === 'Identifier' ||
|
|
33
33
|
definition.key.type === 'PrivateIdentifier' ||
|
|
34
34
|
definition.key.type === 'Literal')
|
|
@@ -141,14 +141,14 @@ export function Fragment(node, context) {
|
|
|
141
141
|
const id = b.id(context.state.scope.generate('fragment'));
|
|
142
142
|
|
|
143
143
|
const use_space_template =
|
|
144
|
-
trimmed.
|
|
145
|
-
trimmed.
|
|
144
|
+
trimmed.some((node) => node.type === 'ExpressionTag') &&
|
|
145
|
+
trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag');
|
|
146
146
|
|
|
147
147
|
if (use_space_template) {
|
|
148
148
|
// special case — we can use `$.text` instead of creating a unique template
|
|
149
149
|
const id = b.id(context.state.scope.generate('text'));
|
|
150
150
|
|
|
151
|
-
process_children(trimmed, () => id,
|
|
151
|
+
process_children(trimmed, () => id, false, {
|
|
152
152
|
...context,
|
|
153
153
|
state
|
|
154
154
|
});
|
|
@@ -158,12 +158,12 @@ export function Fragment(node, context) {
|
|
|
158
158
|
} else {
|
|
159
159
|
if (is_standalone) {
|
|
160
160
|
// no need to create a template, we can just use the existing block's anchor
|
|
161
|
-
process_children(trimmed, () => b.id('$$anchor'),
|
|
161
|
+
process_children(trimmed, () => b.id('$$anchor'), false, { ...context, state });
|
|
162
162
|
} else {
|
|
163
163
|
/** @type {(is_text: boolean) => Expression} */
|
|
164
164
|
const expression = (is_text) => b.call('$.first_child', id, is_text && b.true);
|
|
165
165
|
|
|
166
|
-
process_children(trimmed, expression,
|
|
166
|
+
process_children(trimmed, expression, false, { ...context, state });
|
|
167
167
|
|
|
168
168
|
let flags = TEMPLATE_FRAGMENT;
|
|
169
169
|
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
/** @import { Expression, ExpressionStatement, Identifier,
|
|
1
|
+
/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { SourceLocation } from '#shared' */
|
|
4
4
|
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */
|
|
5
5
|
/** @import { Scope } from '../../../scope' */
|
|
6
|
-
import { escape_html } from '../../../../../escaping.js';
|
|
7
6
|
import {
|
|
7
|
+
cannot_be_set_statically,
|
|
8
8
|
is_boolean_attribute,
|
|
9
9
|
is_dom_property,
|
|
10
10
|
is_load_error_element,
|
|
11
11
|
is_void
|
|
12
12
|
} from '../../../../../utils.js';
|
|
13
|
+
import { escape_html } from '../../../../../escaping.js';
|
|
13
14
|
import { dev, is_ignored, locator } from '../../../../state.js';
|
|
14
15
|
import { is_event_attribute, is_text_attribute } from '../../../../utils/ast.js';
|
|
15
16
|
import * as b from '../../../../utils/builders.js';
|
|
@@ -17,13 +18,12 @@ import { is_custom_element_node } from '../../../nodes.js';
|
|
|
17
18
|
import { clean_nodes, determine_namespace_for_children } from '../../utils.js';
|
|
18
19
|
import { build_getter, create_derived } from '../utils.js';
|
|
19
20
|
import {
|
|
21
|
+
get_attribute_name,
|
|
20
22
|
build_attribute_value,
|
|
21
23
|
build_class_directives,
|
|
22
|
-
build_set_attributes,
|
|
23
24
|
build_style_directives,
|
|
24
|
-
|
|
25
|
+
build_set_attributes
|
|
25
26
|
} from './shared/element.js';
|
|
26
|
-
import { visit_event_attribute } from './shared/events.js';
|
|
27
27
|
import { process_children } from './shared/fragment.js';
|
|
28
28
|
import {
|
|
29
29
|
build_render_statement,
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
build_update,
|
|
32
32
|
build_update_assignment
|
|
33
33
|
} from './shared/utils.js';
|
|
34
|
+
import { visit_event_attribute } from './shared/events.js';
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* @param {AST.RegularElement} node
|
|
@@ -262,8 +263,7 @@ export function RegularElement(node, context) {
|
|
|
262
263
|
|
|
263
264
|
if (
|
|
264
265
|
!is_custom_element &&
|
|
265
|
-
attribute.name
|
|
266
|
-
attribute.name !== 'muted' &&
|
|
266
|
+
!cannot_be_set_statically(attribute.name) &&
|
|
267
267
|
(attribute.value === true || is_text_attribute(attribute))
|
|
268
268
|
) {
|
|
269
269
|
const name = get_attribute_name(node, attribute);
|
|
@@ -352,32 +352,30 @@ export function RegularElement(node, context) {
|
|
|
352
352
|
|
|
353
353
|
// special case — if an element that only contains text, we don't need
|
|
354
354
|
// to descend into it if the text is non-reactive
|
|
355
|
-
const is_text = trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag');
|
|
356
|
-
|
|
357
355
|
// in the rare case that we have static text that can't be inlined
|
|
358
356
|
// (e.g. `<span>{location}</span>`), set `textContent` programmatically
|
|
359
357
|
const use_text_content =
|
|
360
|
-
|
|
358
|
+
trimmed.every((node) => node.type === 'Text' || node.type === 'ExpressionTag') &&
|
|
361
359
|
trimmed.every((node) => node.type === 'Text' || !node.metadata.expression.has_state) &&
|
|
362
|
-
trimmed.some((node) => node.type === 'ExpressionTag'
|
|
360
|
+
trimmed.some((node) => node.type === 'ExpressionTag');
|
|
363
361
|
|
|
364
362
|
if (use_text_content) {
|
|
365
|
-
let { value } = build_template_chunk(trimmed, context.visit, child_state);
|
|
366
|
-
|
|
367
363
|
child_state.init.push(
|
|
368
|
-
b.stmt(
|
|
364
|
+
b.stmt(
|
|
365
|
+
b.assignment(
|
|
366
|
+
'=',
|
|
367
|
+
b.member(context.state.node, 'textContent'),
|
|
368
|
+
build_template_chunk(trimmed, context.visit, child_state).value
|
|
369
|
+
)
|
|
370
|
+
)
|
|
369
371
|
);
|
|
370
372
|
} else {
|
|
371
373
|
/** @type {Expression} */
|
|
372
374
|
let arg = context.state.node;
|
|
373
375
|
|
|
374
376
|
// If `hydrate_node` is set inside the element, we need to reset it
|
|
375
|
-
// after the element has been hydrated
|
|
376
|
-
let needs_reset =
|
|
377
|
-
(node) =>
|
|
378
|
-
node.type === 'Text' ||
|
|
379
|
-
(node.type === 'ExpressionTag' && node.metadata.expression.can_inline)
|
|
380
|
-
);
|
|
377
|
+
// after the element has been hydrated
|
|
378
|
+
let needs_reset = trimmed.some((node) => node.type !== 'Text');
|
|
381
379
|
|
|
382
380
|
// The same applies if it's a `<template>` element, since we need to
|
|
383
381
|
// set the value of `hydrate_node` to `node.content`
|
|
@@ -387,7 +385,7 @@ export function RegularElement(node, context) {
|
|
|
387
385
|
arg = b.member(arg, 'content');
|
|
388
386
|
}
|
|
389
387
|
|
|
390
|
-
process_children(trimmed, (is_text) => b.call('$.child', arg, is_text && b.true),
|
|
388
|
+
process_children(trimmed, (is_text) => b.call('$.child', arg, is_text && b.true), true, {
|
|
391
389
|
...context,
|
|
392
390
|
state: child_state
|
|
393
391
|
});
|
|
@@ -587,44 +585,10 @@ function build_element_attribute_update_assignment(element, node_id, attribute,
|
|
|
587
585
|
state.update.push(update);
|
|
588
586
|
}
|
|
589
587
|
return true;
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
// we need to special case textarea value because it's not an actual attribute
|
|
593
|
-
const can_inline =
|
|
594
|
-
(attribute.name !== 'value' || element.name !== 'textarea') &&
|
|
595
|
-
attribute.metadata.expression.can_inline;
|
|
596
|
-
|
|
597
|
-
if (can_inline) {
|
|
598
|
-
/** @type {Literal | undefined} */
|
|
599
|
-
let literal = undefined;
|
|
600
|
-
|
|
601
|
-
if (value.type === 'Literal') {
|
|
602
|
-
literal = value;
|
|
603
|
-
} else if (value.type === 'Identifier') {
|
|
604
|
-
const binding = context.state.scope.get(value.name);
|
|
605
|
-
if (binding && binding.initial?.type === 'Literal' && !binding.reassigned) {
|
|
606
|
-
literal = binding.initial;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
if (literal && escape_html(literal.value, true) === String(literal.value)) {
|
|
611
|
-
if (is_boolean_attribute(name)) {
|
|
612
|
-
if (literal.value) {
|
|
613
|
-
context.state.template.push(` ${name}`);
|
|
614
|
-
}
|
|
615
|
-
} else {
|
|
616
|
-
context.state.template.push(` ${name}="`, value, '"');
|
|
617
|
-
}
|
|
618
|
-
} else {
|
|
619
|
-
context.state.template.push(
|
|
620
|
-
b.call('$.attr', b.literal(name), value, is_boolean_attribute(name) && b.true)
|
|
621
|
-
);
|
|
622
|
-
}
|
|
623
588
|
} else {
|
|
624
589
|
state.init.push(update);
|
|
590
|
+
return false;
|
|
625
591
|
}
|
|
626
|
-
|
|
627
|
-
return false;
|
|
628
592
|
}
|
|
629
593
|
|
|
630
594
|
/**
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/** @import { Expression } from 'estree' */
|
|
2
2
|
/** @import { AST, SvelteNode } from '#compiler' */
|
|
3
|
-
/** @import { Scope } from '../../../../scope.js' */
|
|
4
3
|
/** @import { ComponentContext } from '../../types' */
|
|
5
|
-
import {
|
|
6
|
-
import { is_event_attribute } from '../../../../../utils/ast.js';
|
|
4
|
+
import { cannot_be_set_statically } from '../../../../../../utils.js';
|
|
5
|
+
import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js';
|
|
7
6
|
import * as b from '../../../../../utils/builders.js';
|
|
8
7
|
import { build_template_chunk, build_update } from './utils.js';
|
|
9
8
|
|
|
@@ -13,10 +12,10 @@ import { build_template_chunk, build_update } from './utils.js';
|
|
|
13
12
|
* corresponding template node references these updates are applied to.
|
|
14
13
|
* @param {SvelteNode[]} nodes
|
|
15
14
|
* @param {(is_text: boolean) => Expression} initial
|
|
16
|
-
* @param {
|
|
15
|
+
* @param {boolean} is_element
|
|
17
16
|
* @param {ComponentContext} context
|
|
18
17
|
*/
|
|
19
|
-
export function process_children(nodes, initial,
|
|
18
|
+
export function process_children(nodes, initial, is_element, { visit, state }) {
|
|
20
19
|
const within_bound_contenteditable = state.metadata.bound_contenteditable;
|
|
21
20
|
let prev = initial;
|
|
22
21
|
let skipped = 0;
|
|
@@ -62,17 +61,16 @@ export function process_children(nodes, initial, element, { visit, state }) {
|
|
|
62
61
|
* @param {Sequence} sequence
|
|
63
62
|
*/
|
|
64
63
|
function flush_sequence(sequence) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (can_inline) {
|
|
64
|
+
if (sequence.every((node) => node.type === 'Text')) {
|
|
68
65
|
skipped += 1;
|
|
69
|
-
|
|
70
|
-
state.template.push(raw ? value : escape_inline_expression(value, state.scope));
|
|
66
|
+
state.template.push(sequence.map((node) => node.raw).join(''));
|
|
71
67
|
return;
|
|
72
68
|
}
|
|
73
69
|
|
|
74
70
|
state.template.push(' ');
|
|
75
71
|
|
|
72
|
+
const { has_state, has_call, value } = build_template_chunk(sequence, visit, state);
|
|
73
|
+
|
|
76
74
|
// if this is a standalone `{expression}`, make sure we handle the case where
|
|
77
75
|
// no text node was created because the expression was empty during SSR
|
|
78
76
|
const is_text = sequence.length === 1;
|
|
@@ -100,9 +98,9 @@ export function process_children(nodes, initial, element, { visit, state }) {
|
|
|
100
98
|
|
|
101
99
|
let child_state = state;
|
|
102
100
|
|
|
103
|
-
if (is_static_element(node)) {
|
|
101
|
+
if (is_static_element(node, state)) {
|
|
104
102
|
skipped += 1;
|
|
105
|
-
} else if (node.type === 'EachBlock' && nodes.length === 1 &&
|
|
103
|
+
} else if (node.type === 'EachBlock' && nodes.length === 1 && is_element) {
|
|
106
104
|
node.metadata.is_controlled = true;
|
|
107
105
|
} else {
|
|
108
106
|
const id = flush_node(false, node.type === 'RegularElement' ? node.name : 'node');
|
|
@@ -127,8 +125,9 @@ export function process_children(nodes, initial, element, { visit, state }) {
|
|
|
127
125
|
|
|
128
126
|
/**
|
|
129
127
|
* @param {SvelteNode} node
|
|
128
|
+
* @param {ComponentContext["state"]} state
|
|
130
129
|
*/
|
|
131
|
-
function is_static_element(node) {
|
|
130
|
+
function is_static_element(node, state) {
|
|
132
131
|
if (node.type !== 'RegularElement') return false;
|
|
133
132
|
if (node.fragment.metadata.dynamic) return false;
|
|
134
133
|
if (node.name.includes('-')) return false; // we're setting all attributes on custom elements through properties
|
|
@@ -142,7 +141,7 @@ function is_static_element(node) {
|
|
|
142
141
|
return false;
|
|
143
142
|
}
|
|
144
143
|
|
|
145
|
-
if (attribute.name
|
|
144
|
+
if (cannot_be_set_statically(attribute.name)) {
|
|
146
145
|
return false;
|
|
147
146
|
}
|
|
148
147
|
|
|
@@ -155,49 +154,10 @@ function is_static_element(node) {
|
|
|
155
154
|
return false;
|
|
156
155
|
}
|
|
157
156
|
|
|
158
|
-
if (!attribute
|
|
157
|
+
if (attribute.value !== true && !is_text_attribute(attribute)) {
|
|
159
158
|
return false;
|
|
160
159
|
}
|
|
161
160
|
}
|
|
162
161
|
|
|
163
162
|
return true;
|
|
164
163
|
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* @param {Expression} node
|
|
168
|
-
* @param {Scope} scope
|
|
169
|
-
* @returns {Expression}
|
|
170
|
-
*/
|
|
171
|
-
function escape_inline_expression(node, scope) {
|
|
172
|
-
if (node.type === 'Literal') {
|
|
173
|
-
if (typeof node.value === 'string') {
|
|
174
|
-
return b.literal(escape_html(node.value));
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return node;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (node.type === 'TemplateLiteral') {
|
|
181
|
-
return b.template(
|
|
182
|
-
node.quasis.map((q) => b.quasi(escape_html(q.value.cooked))),
|
|
183
|
-
node.expressions.map((expression) => escape_inline_expression(expression, scope))
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* If we can't determine the range of possible values statically, wrap in
|
|
189
|
-
* `$.escape(...)`. TODO expand this to cover more cases
|
|
190
|
-
*/
|
|
191
|
-
let needs_escape = true;
|
|
192
|
-
|
|
193
|
-
if (node.type === 'Identifier') {
|
|
194
|
-
const binding = scope.get(node.name);
|
|
195
|
-
|
|
196
|
-
// TODO handle more cases
|
|
197
|
-
if (binding?.initial?.type === 'Literal' && !binding.reassigned) {
|
|
198
|
-
needs_escape = escape_html(binding.initial.value) !== String(binding.initial.value);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return needs_escape ? b.call('$.escape', node) : node;
|
|
203
|
-
}
|