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.
Files changed (25) hide show
  1. package/compiler/index.js +1 -1
  2. package/elements.d.ts +18 -0
  3. package/package.json +1 -1
  4. package/src/compiler/phases/2-analyze/visitors/Attribute.js +4 -5
  5. package/src/compiler/phases/2-analyze/visitors/CallExpression.js +0 -2
  6. package/src/compiler/phases/2-analyze/visitors/ExportDefaultDeclaration.js +7 -2
  7. package/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js +0 -18
  8. package/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js +2 -23
  9. package/src/compiler/phases/2-analyze/visitors/ExpressionTag.js +5 -0
  10. package/src/compiler/phases/2-analyze/visitors/Identifier.js +3 -14
  11. package/src/compiler/phases/2-analyze/visitors/MemberExpression.js +0 -3
  12. package/src/compiler/phases/2-analyze/visitors/RegularElement.js +0 -10
  13. package/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js +0 -1
  14. package/src/compiler/phases/2-analyze/visitors/shared/utils.js +20 -1
  15. package/src/compiler/phases/3-transform/client/utils.js +5 -5
  16. package/src/compiler/phases/3-transform/client/visitors/ClassBody.js +1 -1
  17. package/src/compiler/phases/3-transform/client/visitors/Fragment.js +5 -5
  18. package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +20 -56
  19. package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +14 -54
  20. package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +37 -55
  21. package/src/compiler/phases/nodes.js +1 -2
  22. package/src/compiler/types/index.d.ts +0 -2
  23. package/src/internal/client/index.js +0 -1
  24. package/src/utils.js +15 -2
  25. 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
@@ -2,7 +2,7 @@
2
2
  "name": "svelte",
3
3
  "description": "Cybernetically enhanced web apps",
4
4
  "license": "MIT",
5
- "version": "5.2.4",
5
+ "version": "5.2.6",
6
6
  "type": "module",
7
7
  "types": "./types/index.d.ts",
8
8
  "engines": {
@@ -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 { is_boolean_attribute, is_capture_event, is_delegated } from '../../../../utils.js';
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.name.startsWith('on')) {
33
+ if (is_event_attribute(node)) {
34
34
  mark_subtree_dynamic(context.path);
35
35
  }
36
36
 
37
- if (parent.type === 'RegularElement' && is_boolean_attribute(node.name.toLowerCase())) {
38
- node.metadata.expression.can_inline = false;
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, Node } from 'estree' */
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 === 'instance') {
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, Node } from 'estree' */
2
- /** @import { Binding } from '#compiler' */
1
+ /** @import { ExportSpecifier } from 'estree' */
3
2
  /** @import { Context } from '../types' */
4
- /** @import { Scope } from '../../scope' */
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.every((node) => node.type === 'Text' || node.type === 'ExpressionTag') &&
145
- trimmed.some((node) => node.type === 'ExpressionTag' && !node.metadata.expression.can_inline);
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, null, {
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'), null, { ...context, state });
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, null, { ...context, state });
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, Literal, MemberExpression, ObjectExpression, Statement } from 'estree' */
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
- get_attribute_name
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 !== 'autofocus' &&
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
- is_text &&
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' && !node.metadata.expression.can_inline);
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(b.assignment('=', b.member(context.state.node, 'textContent'), value))
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 (we don't need to reset if it's been inlined)
376
- let needs_reset = !trimmed.every(
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), node, {
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 { escape_html } from '../../../../../../escaping.js';
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 {AST.RegularElement | null} element
15
+ * @param {boolean} is_element
17
16
  * @param {ComponentContext} context
18
17
  */
19
- export function process_children(nodes, initial, element, { visit, state }) {
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
- const { has_state, has_call, value, can_inline } = build_template_chunk(sequence, visit, state);
66
-
67
- if (can_inline) {
64
+ if (sequence.every((node) => node.type === 'Text')) {
68
65
  skipped += 1;
69
- const raw = element?.name === 'script' || element?.name === 'style';
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 && element) {
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 === 'autofocus' || attribute.name === 'muted') {
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.metadata.expression.can_inline) {
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
- }