svelte-effect-runtime 3.4.10 → 4.0.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.
Files changed (83) hide show
  1. package/.dist/chunks/{client-CsVu54pU.js → client-DcZUV4rR.js} +76 -50
  2. package/.dist/chunks/client-DcZUV4rR.js.map +1 -0
  3. package/.dist/chunks/{vite-Dm3bXmR3.js → compiler-BBPNyXmQ.js} +84 -112
  4. package/.dist/chunks/compiler-BBPNyXmQ.js.map +1 -0
  5. package/.dist/chunks/{dispatcher-pHH4JcFR.js → dispatcher-DOx8iC6r.js} +2 -2
  6. package/.dist/chunks/{dispatcher-pHH4JcFR.js.map → dispatcher-DOx8iC6r.js.map} +1 -1
  7. package/.dist/chunks/{dispatcher-LtvFKYUp.js → dispatcher-H-Ia0Os4.js} +3 -3
  8. package/.dist/chunks/{dispatcher-LtvFKYUp.js.map → dispatcher-H-Ia0Os4.js.map} +1 -1
  9. package/.dist/chunks/{errors-Dcf0MVbq.js → errors-IJjsUwrE.js} +21 -65
  10. package/.dist/chunks/errors-IJjsUwrE.js.map +1 -0
  11. package/.dist/chunks/live-BXAKre_4.js +104 -0
  12. package/.dist/chunks/live-BXAKre_4.js.map +1 -0
  13. package/.dist/chunks/{remote-client-Bj1pZXgq.js → remote-client-CVI4t66c.js} +2 -2
  14. package/.dist/chunks/remote-client-CVI4t66c.js.map +1 -0
  15. package/.dist/chunks/{runtime-ekkMQ-wL.js → runtime-GBQz32DZ.js} +3 -3
  16. package/.dist/chunks/{runtime-ekkMQ-wL.js.map → runtime-GBQz32DZ.js.map} +1 -1
  17. package/.dist/chunks/schema-D9pct6Y5.js +89 -0
  18. package/.dist/chunks/schema-D9pct6Y5.js.map +1 -0
  19. package/.dist/chunks/server-D1LcDnZE.js +213 -0
  20. package/.dist/chunks/server-D1LcDnZE.js.map +1 -0
  21. package/.dist/chunks/source-scan-C4PsggID.js +341 -0
  22. package/.dist/chunks/source-scan-C4PsggID.js.map +1 -0
  23. package/.dist/chunks/{transform-CEK6Ccll.js → transform-jNUr65K9.js} +289 -248
  24. package/.dist/chunks/transform-jNUr65K9.js.map +1 -0
  25. package/.dist/compiler/source-scan.d.ts +198 -0
  26. package/.dist/{vite.d.ts → compiler.d.ts} +1 -1
  27. package/.dist/{vite.js → compiler.js} +1 -1
  28. package/.dist/dispatcher.js +1 -1
  29. package/.dist/errors.d.ts +15 -45
  30. package/.dist/generators.d.ts +3 -1
  31. package/.dist/internal/generators.js +20 -3
  32. package/.dist/internal/generators.js.map +1 -0
  33. package/.dist/internal/remote-client.js +1 -1
  34. package/.dist/internal/remote-server.js +1 -1
  35. package/.dist/internal/schema.d.ts +67 -0
  36. package/.dist/live.d.ts +123 -0
  37. package/.dist/markup/event-attributes.d.ts +38 -0
  38. package/.dist/markup/promise.js +1 -1
  39. package/.dist/markup/run.js +1 -1
  40. package/.dist/markup/transform/classify.d.ts +12 -4
  41. package/.dist/markup/transform/constants.d.ts +1 -0
  42. package/.dist/markup/transform/emit.d.ts +12 -4
  43. package/.dist/markup/transform/types.d.ts +1 -0
  44. package/.dist/markup/transform.js +1 -1
  45. package/.dist/markup/value.js +1 -1
  46. package/.dist/mod.d.ts +6 -3
  47. package/.dist/mod.js +5 -4
  48. package/.dist/mod.js.map +1 -1
  49. package/.dist/remote/cause-codec.d.ts +53 -0
  50. package/.dist/remote/client/form-transport.d.ts +4 -1
  51. package/.dist/remote/client/form.d.ts +10 -1
  52. package/.dist/remote/client/index.d.ts +1 -1
  53. package/.dist/remote/client/query.d.ts +6 -16
  54. package/.dist/remote/client/types.d.ts +32 -13
  55. package/.dist/remote/client.d.ts +1 -1
  56. package/.dist/remote/client.js +1 -1
  57. package/.dist/remote/server.d.ts +2 -12
  58. package/.dist/remote/server.js +1 -178
  59. package/.dist/runtime/transform.js +81 -68
  60. package/.dist/runtime/transform.js.map +1 -1
  61. package/.dist/script-transform/imports.d.ts +2 -0
  62. package/.dist/script-transform/types.d.ts +8 -0
  63. package/.dist/server/effects.d.ts +7 -7
  64. package/.dist/server/handler.d.ts +37 -0
  65. package/.dist/server/index.d.ts +4 -1
  66. package/.dist/server/schema.d.ts +2 -26
  67. package/.dist/server/types.d.ts +44 -50
  68. package/.dist/server/wrappers.d.ts +2 -2
  69. package/.dist/server.d.ts +2 -2
  70. package/.dist/server.js +88 -97
  71. package/.dist/server.js.map +1 -1
  72. package/.dist/yieldable.d.ts +41 -0
  73. package/README.md +1 -1
  74. package/package.json +4 -4
  75. package/.dist/chunks/client-CsVu54pU.js.map +0 -1
  76. package/.dist/chunks/descriptors-cOe9uRJz.js +0 -36
  77. package/.dist/chunks/descriptors-cOe9uRJz.js.map +0 -1
  78. package/.dist/chunks/errors-Dcf0MVbq.js.map +0 -1
  79. package/.dist/chunks/remote-client-Bj1pZXgq.js.map +0 -1
  80. package/.dist/chunks/transform-CEK6Ccll.js.map +0 -1
  81. package/.dist/chunks/vite-Dm3bXmR3.js.map +0 -1
  82. package/.dist/remote/server.js.map +0 -1
  83. /package/.dist/{vite → compiler}/remote-client.d.ts +0 -0
@@ -1,4 +1,5 @@
1
- import { A as YieldStarInEventCallbackError, D as UnsupportedMarkupEffectPositionError, n as AsyncEffectInSyncRuneError, t as AsyncEffectInEventCallbackError } from "./errors-Dcf0MVbq.js";
1
+ import { D as UnsupportedMarkupEffectPositionError, k as YieldStarInEventCallbackError, n as AsyncEffectInSyncRuneError, t as AsyncEffectInEventCallbackError } from "./errors-IJjsUwrE.js";
2
+ import { t as scan_svelte_effect_source } from "./source-scan-C4PsggID.js";
2
3
  import { contains_top_level_yield_star } from "../detect.js";
3
4
  import ts from "typescript";
4
5
  import { parse } from "svelte/compiler";
@@ -22,20 +23,37 @@ function make_imports(has_effect_import, has_dispatcher_import, has_untrack_impo
22
23
  dispatcher_value: "__SER___dispatcher",
23
24
  effect: "Effect",
24
25
  program: "__SER___program",
25
- untrack: "untrack"
26
+ untrack: "untrack",
27
+ yield_success: "YieldSuccess",
28
+ yieldable: "ToEffect"
26
29
  }, options = {}) {
27
30
  const needs_dispatcher = options.needs_dispatcher ?? true;
28
31
  const needs_effect = options.needs_effect ?? true;
29
32
  const needs_untrack = options.needs_untrack ?? true;
30
- const dispatcher_import = bindings.dispatcher === "get_dispatcher" ? `import { get_dispatcher } from "svelte-effect-runtime/internal/generators";` : `import { get_dispatcher as ${bindings.dispatcher} } from "svelte-effect-runtime/internal/generators";`;
33
+ const needs_yield_success = options.needs_yield_success ?? false;
34
+ const needs_yieldable = options.needs_yieldable ?? false;
35
+ const generator_import = make_generator_import(bindings, needs_dispatcher && !has_dispatcher_import, needs_yieldable, needs_yield_success);
31
36
  const untrack_import = bindings.untrack === "untrack" ? `import { untrack } from "svelte";` : `import { untrack as ${bindings.untrack} } from "svelte";`;
32
37
  const effect_import = has_effect_import ? false : bindings.effect === "Effect" ? `import { Effect } from "effect";` : `import { Effect as ${bindings.effect} } from "effect";`;
33
38
  return [
34
- needs_dispatcher && !has_dispatcher_import && dispatcher_import,
39
+ generator_import,
35
40
  needs_untrack && !has_untrack_import && untrack_import,
36
41
  needs_effect && effect_import
37
42
  ].filter(Boolean).join("\n");
38
43
  }
44
+ function make_generator_import(bindings, needs_dispatcher, needs_yieldable, needs_yield_success) {
45
+ const specifiers = [
46
+ needs_dispatcher && make_named_import("get_dispatcher", bindings.dispatcher),
47
+ needs_yieldable && make_named_import("ToEffect", bindings.yieldable),
48
+ needs_yield_success && make_named_import("YieldSuccess", bindings.yield_success, true)
49
+ ].filter((specifier) => specifier !== false);
50
+ if (specifiers.length === 0) return false;
51
+ return `import { ${specifiers.join(", ")} } from "svelte-effect-runtime/internal/generators";`;
52
+ }
53
+ function make_named_import(imported_name, local_name, type_only = false) {
54
+ const prefix = type_only ? "type " : "";
55
+ return imported_name === local_name ? `${prefix}${imported_name}` : `${prefix}${imported_name} as ${local_name}`;
56
+ }
39
57
  /**
40
58
  * Checks whether a source file imports a local binding from a module.
41
59
  *
@@ -93,7 +111,8 @@ function collect_binding_name_text(name) {
93
111
  //#region src/markup/transform/constants.ts
94
112
  const HELPERS = {
95
113
  codes: "Code",
96
- dispatcher: "Dispatcher"
114
+ dispatcher: "Dispatcher",
115
+ yieldable: "ToEffect"
97
116
  };
98
117
  //#endregion
99
118
  //#region src/markup/transform/apply.ts
@@ -144,7 +163,8 @@ function make_markup_helper_bindings(content) {
144
163
  return {
145
164
  bindings: {
146
165
  codes: name_allocator.reserve(HELPERS.codes),
147
- dispatcher: name_allocator.reserve(HELPERS.dispatcher)
166
+ dispatcher: name_allocator.reserve(HELPERS.dispatcher),
167
+ yieldable: name_allocator.reserve(HELPERS.yieldable)
148
168
  },
149
169
  name_allocator
150
170
  };
@@ -183,7 +203,7 @@ function make_import_helper(content, import_text) {
183
203
  return import_text;
184
204
  }
185
205
  function make_dispatcher_import(bindings) {
186
- return `import { ${make_import_specifier(HELPERS.dispatcher, bindings.dispatcher)}, ${make_import_specifier(HELPERS.codes, bindings.codes)} } from "svelte-effect-runtime/internal/generators";`;
206
+ return `import { ${make_import_specifier(HELPERS.dispatcher, bindings.dispatcher)}, ${make_import_specifier(HELPERS.codes, bindings.codes)}, ${make_import_specifier(HELPERS.yieldable, bindings.yieldable)} } from "svelte-effect-runtime/internal/generators";`;
187
207
  }
188
208
  function make_import_specifier(imported_name, local_name) {
189
209
  if (imported_name === local_name) return imported_name;
@@ -387,6 +407,49 @@ function add_ordered_name(names, name) {
387
407
  names.push(name);
388
408
  }
389
409
  //#endregion
410
+ //#region src/markup/event-attributes.ts
411
+ /**
412
+ * Checks whether a parsed Svelte attribute has the same event shape that
413
+ * Svelte's compiler recognizes.
414
+ *
415
+ * @example
416
+ * ```ts
417
+ * is_svelte_event_attribute({
418
+ * type: "Attribute",
419
+ * name: "onclick",
420
+ * value: { type: "ExpressionTag" },
421
+ * });
422
+ * ```
423
+ *
424
+ * @since 4.0.0
425
+ * @param attribute - Parsed Svelte attribute with its name and complete value
426
+ * shape.
427
+ * @returns Whether Svelte treats the attribute as an event attribute.
428
+ */
429
+ function is_svelte_event_attribute(attribute) {
430
+ if (attribute.type !== "Attribute" || !attribute.name?.startsWith("on")) return false;
431
+ const value = attribute.value;
432
+ return value !== true && !Array.isArray(value) || Array.isArray(value) && value.length === 1 && value[0]?.type === "ExpressionTag";
433
+ }
434
+ /**
435
+ * Normalizes an event-like attribute name to Svelte's lowercase DOM event
436
+ * spelling while keeping the legacy directive prefix intact.
437
+ *
438
+ * @example
439
+ * ```ts
440
+ * normalize_event_attribute_name("onChange");
441
+ * ```
442
+ *
443
+ * @since 3.4.11
444
+ * @param name - Event-like attribute name read from Svelte source or the
445
+ * parsed AST.
446
+ * @returns Lowercase event attribute spelling for DOM-facing Svelte nodes.
447
+ */
448
+ function normalize_event_attribute_name(name) {
449
+ if (name.startsWith("on:")) return `on:${name.slice(3).toLowerCase()}`;
450
+ return name.toLowerCase();
451
+ }
452
+ //#endregion
390
453
  //#region src/markup/transform/classify.ts
391
454
  /**
392
455
  * Matches sanitized placeholders back to their Svelte AST context.
@@ -443,6 +506,14 @@ function visit_ast_node(node, candidates, matched, classified) {
443
506
  classify_expression(node.expression, "plain", candidates, matched, classified);
444
507
  walk_ast(node.fragment, candidates, matched, classified);
445
508
  return;
509
+ case "SnippetBlock":
510
+ walk_ast(node.body, candidates, matched, classified);
511
+ return;
512
+ case "SvelteElement":
513
+ classify_expression(node.tag, "plain", candidates, matched, classified);
514
+ visit_element_attributes(node, candidates, matched, classified);
515
+ walk_ast(node.fragment, candidates, matched, classified);
516
+ return;
446
517
  case "RegularElement":
447
518
  case "Component":
448
519
  case "TitleElement":
@@ -451,7 +522,6 @@ function visit_ast_node(node, candidates, matched, classified) {
451
522
  case "SvelteBoundary":
452
523
  case "SvelteComponent":
453
524
  case "SvelteDocument":
454
- case "SvelteElement":
455
525
  case "SvelteFragment":
456
526
  case "SvelteHead":
457
527
  case "SvelteSelf":
@@ -467,28 +537,51 @@ function classify_declaration_tag(node, candidates, matched, classified) {
467
537
  }
468
538
  function visit_element_attributes(node, candidates, matched, classified) {
469
539
  for (const attr of node.attributes) {
470
- if (attr.type === "Attribute" && attr.name && is_event_attribute_name(attr.name)) {
471
- visit_attribute_value(attr.value, "event", candidates, matched, classified);
540
+ if (is_svelte_event_attribute(attr)) {
541
+ const attribute_name_replacement = make_attribute_name_replacement(node, attr);
542
+ visit_attribute_value(attr.value, "event", candidates, matched, classified, attribute_name_replacement);
472
543
  continue;
473
544
  }
474
545
  if (attr.type === "OnDirective" && attr.expression) {
475
- classify_expression(attr.expression, "event", candidates, matched, classified);
546
+ const attribute_name_replacement = make_directive_name_replacement(node, attr);
547
+ classify_expression(attr.expression, "event", candidates, matched, classified, attribute_name_replacement);
476
548
  continue;
477
549
  }
478
550
  }
479
551
  }
480
- function is_event_attribute_name(name) {
481
- return name.startsWith("on:") || /^on[a-z]/.test(name);
552
+ function make_attribute_name_replacement(node, attr) {
553
+ if (!attr.name || !should_normalize_event_attribute_name(node) || attr.start === void 0) return;
554
+ const normalized_name = normalize_event_attribute_name(attr.name);
555
+ if (normalized_name === attr.name) return;
556
+ return {
557
+ start: attr.start,
558
+ end: attr.start + attr.name.length,
559
+ text: normalized_name
560
+ };
482
561
  }
483
- function visit_attribute_value(value, kind, candidates, matched, classified) {
562
+ function make_directive_name_replacement(node, attr) {
563
+ if (!attr.name || !should_normalize_event_attribute_name(node) || attr.start === void 0) return;
564
+ const original_name = `on:${attr.name}`;
565
+ const normalized_name = normalize_event_attribute_name(original_name);
566
+ if (normalized_name === original_name) return;
567
+ return {
568
+ start: attr.start,
569
+ end: attr.start + original_name.length,
570
+ text: normalized_name
571
+ };
572
+ }
573
+ function should_normalize_event_attribute_name(node) {
574
+ return node.type !== "Component" && node.type !== "SvelteComponent" && node.type !== "SvelteSelf";
575
+ }
576
+ function visit_attribute_value(value, kind, candidates, matched, classified, attribute_name_replacement) {
484
577
  if (value === true) return;
485
578
  if (Array.isArray(value)) {
486
- for (const part of value) if (part.type === "ExpressionTag") classify_expression(part.expression, kind, candidates, matched, classified);
579
+ for (const part of value) if (part.type === "ExpressionTag") classify_expression(part.expression, kind, candidates, matched, classified, attribute_name_replacement);
487
580
  return;
488
581
  }
489
- classify_expression(value.expression, kind, candidates, matched, classified);
582
+ classify_expression(value.expression, kind, candidates, matched, classified, attribute_name_replacement);
490
583
  }
491
- function classify_expression(expression, kind, candidates, matched, classified) {
584
+ function classify_expression(expression, kind, candidates, matched, classified, attribute_name_replacement) {
492
585
  if (!expression) return;
493
586
  const found_candidates = find_candidates(expression, candidates);
494
587
  for (const candidate of found_candidates) {
@@ -496,7 +589,8 @@ function classify_expression(expression, kind, candidates, matched, classified)
496
589
  matched.add(candidate.placeholder);
497
590
  classified.push({
498
591
  candidate,
499
- kind: resolve_candidate_kind(candidate, kind)
592
+ kind: resolve_candidate_kind(candidate, kind),
593
+ attribute_name_replacement
500
594
  });
501
595
  }
502
596
  }
@@ -529,6 +623,75 @@ function is_record(value) {
529
623
  return typeof value === "object" && value !== null;
530
624
  }
531
625
  //#endregion
626
+ //#region src/script-transform/ast.ts
627
+ /**
628
+ * Checks whether a node is a `yield*` binary expression.
629
+ *
630
+ * @since 2.0.0
631
+ * @param node - TypeScript AST node to check.
632
+ * @returns Whether the node represents `yield * operand`.
633
+ */
634
+ function is_yield_star_expression$1(node) {
635
+ return ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.AsteriskToken && ts.isIdentifier(node.left) && node.left.text === "yield";
636
+ }
637
+ /**
638
+ * Checks whether a node owns its own yield semantics.
639
+ *
640
+ * @since 2.0.0
641
+ * @param node - TypeScript AST node to check.
642
+ * @returns Whether traversal should stop at this function boundary.
643
+ */
644
+ function is_function_boundary_node(node) {
645
+ return ts.isArrowFunction(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isMethodDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node);
646
+ }
647
+ /**
648
+ * Returns `true` if the node tree contains a top-level `await`.
649
+ *
650
+ * @since 2.0.0
651
+ * @param node - Root node to search.
652
+ * @returns Whether a top-level await expression was found.
653
+ */
654
+ function contains_top_level_await(node) {
655
+ if (ts.isAwaitExpression(node)) return true;
656
+ return node.getChildren().some((child) => !is_function_boundary_node(child) && contains_top_level_await(child));
657
+ }
658
+ /**
659
+ * Collects top-level `yield*` nodes under an expression.
660
+ *
661
+ * @since 2.0.0
662
+ * @param node - Root node to search.
663
+ * @param on_found - Callback invoked for each matching yield node.
664
+ * @returns Nothing.
665
+ */
666
+ function collect_yield_star_nodes(node, on_found) {
667
+ if (is_function_boundary_node(node)) return;
668
+ if (is_yield_star_expression$1(node)) {
669
+ on_found(node);
670
+ return;
671
+ }
672
+ node.forEachChild((child) => {
673
+ collect_yield_star_nodes(child, on_found);
674
+ });
675
+ }
676
+ /**
677
+ * Finds the first top-level `yield*` expression below a node.
678
+ *
679
+ * @since 2.0.0
680
+ * @param node - Root node to search.
681
+ * @param on_found - Callback invoked with the first matching node.
682
+ * @returns Nothing.
683
+ */
684
+ function find_yield_star_node(node, on_found) {
685
+ if (is_function_boundary_node(node)) return;
686
+ if (is_yield_star_expression$1(node)) {
687
+ on_found(node);
688
+ return;
689
+ }
690
+ node.forEachChild((child) => {
691
+ find_yield_star_node(child, on_found);
692
+ });
693
+ }
694
+ //#endregion
532
695
  //#region src/markup/transform/expressions.ts
533
696
  /**
534
697
  * Strips an event handler arrow function down to its executable body.
@@ -673,7 +836,7 @@ function add_binding_names(name, locals) {
673
836
  }
674
837
  }
675
838
  function visit_event_body(node, context, result) {
676
- if (is_yield_star_expression$1(node)) {
839
+ if (is_yield_star_expression(node)) {
677
840
  if (context === "top_level") result.has_top_level_yield_star = true;
678
841
  else if (context === "nested_invalid") result.has_nested_invalid_yield_star = true;
679
842
  node.forEachChild((child) => visit_event_body(child, context, result));
@@ -692,7 +855,7 @@ function is_nested_function_boundary(node) {
692
855
  function is_generator_function_boundary(node) {
693
856
  return (ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) && node.asteriskToken !== void 0;
694
857
  }
695
- function is_yield_star_expression$1(node) {
858
+ function is_yield_star_expression(node) {
696
859
  if (ts.isYieldExpression(node)) return node.asteriskToken !== void 0;
697
860
  return ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.AsteriskToken && ts.isIdentifier(node.left) && node.left.text === "yield";
698
861
  }
@@ -937,7 +1100,22 @@ function to_expr_pos(pos, context) {
937
1100
  * @returns Replacements ready to apply to the original component source.
938
1101
  */
939
1102
  function emit_replacements(classified, effect_context, helper_bindings, name_allocator, target) {
940
- return classified.map(({ candidate, kind }) => emit_replacement(candidate, kind, effect_context, helper_bindings, name_allocator, target));
1103
+ const expression_replacements = classified.map(({ candidate, kind }) => emit_replacement(candidate, kind, effect_context, helper_bindings, name_allocator, target));
1104
+ return [...unique_attribute_name_replacements(classified).map((replacement) => ({
1105
+ start: replacement.start,
1106
+ end: replacement.end,
1107
+ text: replacement.text
1108
+ })), ...expression_replacements];
1109
+ }
1110
+ function unique_attribute_name_replacements(classified) {
1111
+ const seen = /* @__PURE__ */ new Set();
1112
+ return classified.map((entry) => entry.attribute_name_replacement).filter((replacement) => {
1113
+ if (!replacement) return false;
1114
+ const key = `${replacement.start}:${replacement.end}:${replacement.text}`;
1115
+ if (seen.has(key)) return false;
1116
+ seen.add(key);
1117
+ return true;
1118
+ });
941
1119
  }
942
1120
  function emit_replacement(candidate, kind, effect_context, helper_bindings, name_allocator, target) {
943
1121
  const normalized = normalize_effect_callback_yields(candidate.expr_text, effect_context);
@@ -953,32 +1131,28 @@ function emit_replacement(candidate, kind, effect_context, helper_bindings, name
953
1131
  let helpers;
954
1132
  let relocation;
955
1133
  if (kind === "await") {
956
- const effect = make_effect_helper(normalized_candidate, helper_name);
1134
+ const effect = make_effect_helper(normalized_candidate, helper_name, helper_bindings);
957
1135
  replacement_text = emit_promise_expression(id_text, effect, helper_bindings, "undefined", `{ ssr: "pending" }`);
958
1136
  helpers = [...normalized.helpers, effect.helper];
959
1137
  } else if (kind === "render") {
960
- const effect = make_effect_helper(normalized_candidate, helper_name);
1138
+ const effect = make_effect_helper(normalized_candidate, helper_name, helper_bindings);
961
1139
  replacement_text = emit_render_expression(id_text, effect, candidate, helper_bindings, is_server_target);
962
1140
  helpers = [...normalized.helpers, effect.helper];
963
1141
  } else if (kind === "render_argument") {
964
- const effect = make_effect_helper(normalized_candidate, helper_name);
1142
+ const effect = make_effect_helper(normalized_candidate, helper_name, helper_bindings);
965
1143
  replacement_text = emit_await_expression(id_text, effect, helper_bindings, server_fallback(is_server_target, "undefined"));
966
1144
  helpers = [...normalized.helpers, effect.helper];
967
1145
  } else if (kind === "each") {
968
- const effect = make_effect_helper(normalized_candidate, helper_name);
1146
+ const effect = make_effect_helper(normalized_candidate, helper_name, helper_bindings);
969
1147
  replacement_text = emit_await_expression(id_text, effect, helper_bindings, server_fallback(is_server_target, "[]"));
970
1148
  helpers = [...normalized.helpers, effect.helper];
971
1149
  } else if (kind === "event") {
972
1150
  const event = make_event_handler(normalized_candidate, helper_bindings);
973
1151
  replacement_text = event.text;
974
1152
  helpers = normalized.helpers;
975
- relocation = make_relocation(candidate, replacement_text, {
976
- originalStart: 0,
977
- originalEnd: candidate.expr_text.length,
978
- generatedText: event.expr_text
979
- });
1153
+ relocation = make_pending_relocation(candidate, replacement_text, event.expr_text, event.relocation);
980
1154
  } else {
981
- const effect = make_effect_helper(normalized_candidate, helper_name);
1155
+ const effect = make_effect_helper(normalized_candidate, helper_name, helper_bindings);
982
1156
  replacement_text = emit_await_expression(id_text, effect, helper_bindings, server_fallback(is_server_target, "undefined"));
983
1157
  helpers = [...normalized.helpers, effect.helper];
984
1158
  }
@@ -994,9 +1168,11 @@ function make_event_handler(candidate, helper_bindings) {
994
1168
  const expr_text = candidate.expr_text;
995
1169
  if (is_callback_function_expression(expr_text)) throw new YieldStarInEventCallbackError(candidate.filename, expr_text);
996
1170
  if (analyze_event_body_yield_star(expr_text).has_nested_invalid_yield_star) throw new AsyncEffectInEventCallbackError(candidate.filename, expr_text);
1171
+ const wrapped_expr_text = wrap_yield_stars(expr_text, helper_bindings);
997
1172
  return {
998
- expr_text,
999
- text: `(event) => { ${helper_bindings.dispatcher}.emit({ type: ${helper_bindings.codes}.Markup.Run, fn: function* () { ${expr_text}; } }); }`
1173
+ expr_text: wrapped_expr_text,
1174
+ relocation: make_yield_operand_relocation(expr_text, wrapped_expr_text, helper_bindings),
1175
+ text: `(event) => { ${helper_bindings.dispatcher}.emit({ type: ${helper_bindings.codes}.Markup.Run, fn: function* () { ${wrapped_expr_text}; } }); }`
1000
1176
  };
1001
1177
  }
1002
1178
  function emit_promise_expression(id_text, effect, helper_bindings, ssr_fallback, options) {
@@ -1021,41 +1197,91 @@ function emit_await_expression(id_text, effect, helper_bindings, ssr_fallback) {
1021
1197
  function server_fallback(is_server_target, fallback) {
1022
1198
  return is_server_target ? fallback : void 0;
1023
1199
  }
1024
- function make_effect_helper(candidate, helper_name) {
1200
+ function make_effect_helper(candidate, helper_name, helper_bindings) {
1025
1201
  const deps = collect_free_identifiers(candidate.expr_text);
1026
1202
  const args_text = deps.join(", ");
1027
1203
  const deps_text = deps.length === 0 ? "[]" : `[${args_text}]`;
1028
1204
  const call = `${helper_name}()`;
1029
- const text = `function* ${helper_name}() { return (${candidate.expr_text}); }`;
1030
- const generated_start = text.indexOf(candidate.expr_text);
1205
+ const effect_text = wrap_yield_stars(candidate.expr_text, helper_bindings);
1206
+ const text = `function* ${helper_name}() { return (${effect_text}); }`;
1207
+ const generated_start = text.indexOf(effect_text);
1208
+ const relocation = make_yield_operand_relocation(candidate.expr_text, effect_text, helper_bindings) ?? {
1209
+ originalStart: 0,
1210
+ originalEnd: candidate.expr_text.length,
1211
+ generatedStart: 0,
1212
+ generatedEnd: effect_text.length
1213
+ };
1031
1214
  return {
1032
1215
  call,
1033
1216
  deps_text,
1034
1217
  helper: {
1035
1218
  text,
1036
1219
  relocation: {
1037
- originalStart: candidate.start,
1038
- originalEnd: candidate.end,
1039
- generatedStartInReplacement: generated_start,
1040
- generatedEndInReplacement: generated_start + candidate.expr_text.length
1220
+ originalStart: candidate.start + relocation.originalStart,
1221
+ originalEnd: candidate.start + relocation.originalEnd,
1222
+ generatedStartInReplacement: generated_start + relocation.generatedStart,
1223
+ generatedEndInReplacement: generated_start + relocation.generatedEnd
1041
1224
  }
1042
1225
  }
1043
1226
  };
1044
1227
  }
1228
+ function wrap_yield_stars(text, helper_bindings) {
1229
+ const source_file = ts.createSourceFile("markup-yield.ts", text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
1230
+ const replacements = [];
1231
+ collect_yield_star_nodes(source_file, (node) => {
1232
+ const yield_text = text.slice(node.getStart(source_file), node.end).trim();
1233
+ replacements.push({
1234
+ start: node.getStart(source_file),
1235
+ end: node.end,
1236
+ text: `yield* ${helper_bindings.yieldable}(${strip_yield_star(yield_text)})`
1237
+ });
1238
+ });
1239
+ if (replacements.length === 0) return text;
1240
+ replacements.sort((a, b) => b.start - a.start);
1241
+ let output = text;
1242
+ for (const replacement of replacements) output = output.slice(0, replacement.start) + replacement.text + output.slice(replacement.end);
1243
+ return output;
1244
+ }
1245
+ function strip_yield_star(yield_text) {
1246
+ return yield_text.replace(/^yield\s*\*\s*/, "");
1247
+ }
1248
+ function make_yield_operand_relocation(original_text, generated_text, helper_bindings) {
1249
+ const source_file = ts.createSourceFile("markup-yield-relocation.ts", original_text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
1250
+ let yield_node;
1251
+ collect_yield_star_nodes(source_file, (node) => {
1252
+ yield_node ??= node;
1253
+ });
1254
+ if (!yield_node || !ts.isBinaryExpression(yield_node)) return;
1255
+ const operand = yield_node.right;
1256
+ const original_start = operand.getStart(source_file);
1257
+ const original_end = operand.end;
1258
+ const operand_text = original_text.slice(original_start, original_end).trim();
1259
+ const wrapper_text = `${helper_bindings.yieldable}(${operand_text})`;
1260
+ const wrapper_start = generated_text.indexOf(wrapper_text);
1261
+ if (wrapper_start === -1) return;
1262
+ const generated_start = wrapper_start + wrapper_text.indexOf(operand_text);
1263
+ return {
1264
+ originalStart: original_start,
1265
+ originalEnd: original_end,
1266
+ generatedStart: generated_start,
1267
+ generatedEnd: generated_start + operand_text.length
1268
+ };
1269
+ }
1045
1270
  function make_cache_id(candidate) {
1046
1271
  return `${candidate.filename.replace(/[?#].*$/, "")}:${candidate.start}:${candidate.end}`;
1047
1272
  }
1048
1273
  function make_helper_name(candidate, name_allocator) {
1049
1274
  return name_allocator.reserve(`__SER___markup_effect_${candidate.start}_${candidate.end}`);
1050
1275
  }
1051
- function make_relocation(candidate, replacement_text, inner) {
1052
- const generated_start = replacement_text.indexOf(inner.generatedText);
1276
+ function make_pending_relocation(candidate, replacement_text, expression_text, relocation) {
1277
+ if (!relocation) return;
1278
+ const generated_start = replacement_text.indexOf(expression_text);
1053
1279
  if (generated_start === -1) return;
1054
1280
  return {
1055
- originalStart: candidate.start + inner.originalStart,
1056
- originalEnd: candidate.start + inner.originalEnd,
1057
- generatedStartInReplacement: generated_start,
1058
- generatedEndInReplacement: generated_start + inner.generatedText.length
1281
+ originalStart: candidate.start + relocation.originalStart,
1282
+ originalEnd: candidate.start + relocation.originalEnd,
1283
+ generatedStartInReplacement: generated_start + relocation.generatedStart,
1284
+ generatedEndInReplacement: generated_start + relocation.generatedEnd
1059
1285
  };
1060
1286
  }
1061
1287
  //#endregion
@@ -1155,75 +1381,6 @@ function is_rune_root(name) {
1155
1381
  return name === "$bindable" || name === "$derived" || name === "$effect" || name === "$host" || name === "$inspect" || name === "$props" || name === "$state";
1156
1382
  }
1157
1383
  //#endregion
1158
- //#region src/script-transform/ast.ts
1159
- /**
1160
- * Checks whether a node is a `yield*` binary expression.
1161
- *
1162
- * @since 2.0.0
1163
- * @param node - TypeScript AST node to check.
1164
- * @returns Whether the node represents `yield * operand`.
1165
- */
1166
- function is_yield_star_expression(node) {
1167
- return ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.AsteriskToken && ts.isIdentifier(node.left) && node.left.text === "yield";
1168
- }
1169
- /**
1170
- * Checks whether a node owns its own yield semantics.
1171
- *
1172
- * @since 2.0.0
1173
- * @param node - TypeScript AST node to check.
1174
- * @returns Whether traversal should stop at this function boundary.
1175
- */
1176
- function is_function_boundary_node(node) {
1177
- return ts.isArrowFunction(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isMethodDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node);
1178
- }
1179
- /**
1180
- * Returns `true` if the node tree contains a top-level `await`.
1181
- *
1182
- * @since 2.0.0
1183
- * @param node - Root node to search.
1184
- * @returns Whether a top-level await expression was found.
1185
- */
1186
- function contains_top_level_await(node) {
1187
- if (ts.isAwaitExpression(node)) return true;
1188
- return node.getChildren().some((child) => !is_function_boundary_node(child) && contains_top_level_await(child));
1189
- }
1190
- /**
1191
- * Collects top-level `yield*` nodes under an expression.
1192
- *
1193
- * @since 2.0.0
1194
- * @param node - Root node to search.
1195
- * @param on_found - Callback invoked for each matching yield node.
1196
- * @returns Nothing.
1197
- */
1198
- function collect_yield_star_nodes(node, on_found) {
1199
- if (is_function_boundary_node(node)) return;
1200
- if (is_yield_star_expression(node)) {
1201
- on_found(node);
1202
- return;
1203
- }
1204
- node.forEachChild((child) => {
1205
- collect_yield_star_nodes(child, on_found);
1206
- });
1207
- }
1208
- /**
1209
- * Finds the first top-level `yield*` expression below a node.
1210
- *
1211
- * @since 2.0.0
1212
- * @param node - Root node to search.
1213
- * @param on_found - Callback invoked with the first matching node.
1214
- * @returns Nothing.
1215
- */
1216
- function find_yield_star_node(node, on_found) {
1217
- if (is_function_boundary_node(node)) return;
1218
- if (is_yield_star_expression(node)) {
1219
- on_found(node);
1220
- return;
1221
- }
1222
- node.forEachChild((child) => {
1223
- find_yield_star_node(child, on_found);
1224
- });
1225
- }
1226
- //#endregion
1227
1384
  //#region src/markup/transform/scan.ts
1228
1385
  /**
1229
1386
  * Replaces markup `yield*` expressions with placeholders before Svelte parses
@@ -1247,26 +1404,13 @@ function find_yield_star_node(node, on_found) {
1247
1404
  */
1248
1405
  function sanitize_markup(content, filename) {
1249
1406
  const candidates = [];
1250
- const excluded_ranges = collect_excluded_ranges(content);
1407
+ const source_scan = scan_svelte_effect_source(content, filename);
1251
1408
  const magic = new MagicString(content);
1252
1409
  let helper_index = 0;
1253
- let cursor = 0;
1254
- while (cursor < content.length) {
1255
- const open = content.indexOf("{", cursor);
1256
- if (open === -1) break;
1257
- /** Skip braces inside <script> and <style> blocks. */
1258
- const excluded_range = find_excluded_range(excluded_ranges, open);
1259
- if (excluded_range) {
1260
- cursor = excluded_range.end;
1261
- continue;
1262
- }
1263
- /** Find the matching closing brace. */
1264
- const close = find_closing_brace(content, open + 1);
1265
- if (close === -1) {
1266
- cursor = open + 1;
1267
- continue;
1268
- }
1269
- const inner = content.slice(open + 1, close);
1410
+ for (const expression of source_scan.markup_expressions) {
1411
+ const open = expression.open;
1412
+ const close = expression.close;
1413
+ const inner = expression.inner;
1270
1414
  const trimmed = inner.trimStart();
1271
1415
  const leading_ws = inner.length - trimmed.length;
1272
1416
  const tag_info = get_tag_info(trimmed);
@@ -1285,16 +1429,12 @@ function sanitize_markup(content, filename) {
1285
1429
  });
1286
1430
  magic.overwrite(declaration_yield.start, declaration_yield.end, placeholder);
1287
1431
  }
1288
- cursor = close + 1;
1289
1432
  continue;
1290
1433
  }
1291
1434
  let expr_body = trimmed.slice(tag_info.prefix_length);
1292
1435
  /** For @const, only use the RHS after `=` as the expression body. */
1293
1436
  const equal_idx = tag_info.kind === "plain" && trimmed.startsWith("@const ") ? expr_body.indexOf("=") : -1;
1294
- if (!((is_event_callback_expression(inner) ? analyze_event_yield(inner) : void 0)?.has_top_level_yield_star ?? contains_yield_star_in_text(expr_body))) {
1295
- cursor = close + 1;
1296
- continue;
1297
- }
1437
+ if (!((is_event_callback_expression(inner) ? analyze_event_yield(inner) : void 0)?.has_top_level_yield_star ?? contains_yield_star_in_text(expr_body))) continue;
1298
1438
  /** The expression starts after the tag prefix. For @const, after the `=`. */
1299
1439
  let extra_prefix = 0;
1300
1440
  if (equal_idx !== -1) {
@@ -1317,10 +1457,7 @@ function sanitize_markup(content, filename) {
1317
1457
  if (boundary !== Infinity) expr_end = expr_start + boundary;
1318
1458
  }
1319
1459
  const expr_text = content.slice(expr_start, expr_end).trim();
1320
- if (expr_text.length === 0) {
1321
- cursor = close + 1;
1322
- continue;
1323
- }
1460
+ if (expr_text.length === 0) continue;
1324
1461
  validate_expression_yield_usage(expr_text, filename);
1325
1462
  if (key === "render" && !/^\s*yield\s*\*/.test(expr_text)) {
1326
1463
  const render_arg_yields = collect_expression_yield_expressions(content, expr_start, expr_text, filename);
@@ -1338,7 +1475,6 @@ function sanitize_markup(content, filename) {
1338
1475
  });
1339
1476
  magic.overwrite(render_arg_yield.start, render_arg_yield.end, placeholder);
1340
1477
  }
1341
- cursor = close + 1;
1342
1478
  continue;
1343
1479
  }
1344
1480
  }
@@ -1354,7 +1490,6 @@ function sanitize_markup(content, filename) {
1354
1490
  key
1355
1491
  });
1356
1492
  magic.overwrite(expr_start, expr_end, key === "render" ? `${placeholder}()` : placeholder);
1357
- cursor = close + 1;
1358
1493
  }
1359
1494
  return {
1360
1495
  code: magic.toString(),
@@ -1382,108 +1517,6 @@ function collect_expression_yield_expressions(content, expr_start, expr_text, fi
1382
1517
  });
1383
1518
  return expressions;
1384
1519
  }
1385
- function collect_excluded_ranges(content) {
1386
- const ranges = [
1387
- ...collect_tag_ranges(content, "script"),
1388
- ...collect_tag_ranges(content, "style"),
1389
- ...collect_html_comment_ranges(content)
1390
- ];
1391
- ranges.sort((a, b) => a.start - b.start);
1392
- return merge_ranges(ranges);
1393
- }
1394
- function collect_tag_ranges(content, tag) {
1395
- const pattern = new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)<\\/${tag}\\s*>`, "gi");
1396
- return [...content.matchAll(pattern)].flatMap((match) => {
1397
- if (match.index === void 0) return [];
1398
- return [{
1399
- start: match.index,
1400
- end: match.index + match[0].length
1401
- }];
1402
- });
1403
- }
1404
- function collect_html_comment_ranges(content) {
1405
- const ranges = [];
1406
- let cursor = 0;
1407
- while (cursor < content.length) {
1408
- const start = content.indexOf("<!--", cursor);
1409
- if (start === -1) break;
1410
- const close = content.indexOf("-->", start + 4);
1411
- const end = close === -1 ? content.length : close + 3;
1412
- ranges.push({
1413
- start,
1414
- end
1415
- });
1416
- cursor = end;
1417
- }
1418
- return ranges;
1419
- }
1420
- function merge_ranges(ranges) {
1421
- const merged = [];
1422
- for (const range of ranges) {
1423
- const previous = merged.at(-1);
1424
- if (!previous || range.start > previous.end) {
1425
- merged.push({ ...range });
1426
- continue;
1427
- }
1428
- previous.end = Math.max(previous.end, range.end);
1429
- }
1430
- return merged;
1431
- }
1432
- function find_excluded_range(ranges, pos) {
1433
- let low = 0;
1434
- let high = ranges.length - 1;
1435
- while (low <= high) {
1436
- const mid = Math.floor((low + high) / 2);
1437
- const range = ranges[mid];
1438
- if (pos <= range.start) {
1439
- high = mid - 1;
1440
- continue;
1441
- }
1442
- if (pos >= range.end) {
1443
- low = mid + 1;
1444
- continue;
1445
- }
1446
- return range;
1447
- }
1448
- }
1449
- /** Brace matching helpers for extracting complete markup expressions. */
1450
- function find_closing_brace(content, start) {
1451
- let depth = 0;
1452
- for (let i = start; i < content.length; i += 1) {
1453
- const ch = content[i];
1454
- if (ch === "{" && content[i - 1] !== "$") depth += 1;
1455
- else if (ch === "}") {
1456
- if (depth === 0) return i;
1457
- depth -= 1;
1458
- } else if (ch === "'" || ch === "\"" || ch === "`") {
1459
- i = skip_string(content, i, ch);
1460
- if (i === -1) return -1;
1461
- } else if (ch === "/" && content[i + 1] === "/") i = skip_line_comment(content, i);
1462
- else if (ch === "/" && content[i + 1] === "*") {
1463
- i = skip_block_comment(content, i);
1464
- if (i === -1) return -1;
1465
- }
1466
- }
1467
- return -1;
1468
- }
1469
- function skip_string(content, start, quote) {
1470
- for (let i = start + 1; i < content.length; i += 1) {
1471
- if (content[i] === "\\") {
1472
- i += 1;
1473
- continue;
1474
- }
1475
- if (content[i] === quote) return i;
1476
- }
1477
- return -1;
1478
- }
1479
- function skip_line_comment(content, start) {
1480
- for (let i = start + 2; i < content.length; i += 1) if (content[i] === "\n") return i;
1481
- return content.length;
1482
- }
1483
- function skip_block_comment(content, start) {
1484
- for (let i = start + 2; i < content.length; i += 1) if (content[i] === "*" && content[i + 1] === "/") return i + 1;
1485
- return -1;
1486
- }
1487
1520
  function get_tag_info(trimmed) {
1488
1521
  if (trimmed.startsWith("#each ")) return {
1489
1522
  kind: "each",
@@ -1498,6 +1531,10 @@ function get_tag_info(trimmed) {
1498
1531
  prefix_length: 8
1499
1532
  };
1500
1533
  /** Strip prefix-only tags — the expression starts after the tag keyword. */
1534
+ if (trimmed.startsWith("@attach ")) return {
1535
+ kind: "plain",
1536
+ prefix_length: 8
1537
+ };
1501
1538
  if (trimmed.startsWith("#if ")) return {
1502
1539
  kind: "plain",
1503
1540
  prefix_length: 4
@@ -1522,6 +1559,10 @@ function get_tag_info(trimmed) {
1522
1559
  kind: "plain",
1523
1560
  prefix_length: 7
1524
1561
  };
1562
+ if (trimmed.startsWith("...")) return {
1563
+ kind: "plain",
1564
+ prefix_length: 3
1565
+ };
1525
1566
  return {
1526
1567
  kind: "plain",
1527
1568
  prefix_length: 0
@@ -1644,6 +1685,6 @@ function transform_markup_effect(content, filename, options = {}) {
1644
1685
  };
1645
1686
  }
1646
1687
  //#endregion
1647
- export { is_yield_star_expression as a, slice as c, collect_top_level_binding_names as d, has_local_import_binding as f, find_yield_star_node as i, slice_start as l, collect_yield_star_nodes as n, validate_rune_yield_usage as o, make_imports as p, contains_top_level_await as r, create_source_map as s, transform_markup_effect as t, collect_free_identifiers as u };
1688
+ export { slice_start as a, contains_top_level_await as c, collect_top_level_binding_names as d, has_local_import_binding as f, slice as i, find_yield_star_node as l, validate_rune_yield_usage as n, collect_free_identifiers as o, make_imports as p, create_source_map as r, collect_yield_star_nodes as s, transform_markup_effect as t, is_yield_star_expression$1 as u };
1648
1689
 
1649
- //# sourceMappingURL=transform-CEK6Ccll.js.map
1690
+ //# sourceMappingURL=transform-jNUr65K9.js.map