svelte-effect-runtime 3.4.5 → 3.4.7

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 (69) hide show
  1. package/.dist/chunks/{client-5cizmHtc.js → client-CsVu54pU.js} +119 -56
  2. package/.dist/chunks/client-CsVu54pU.js.map +1 -0
  3. package/.dist/chunks/{descriptors-OS4VfJuW.js → descriptors-cOe9uRJz.js} +2 -2
  4. package/.dist/chunks/descriptors-cOe9uRJz.js.map +1 -0
  5. package/.dist/chunks/{dispatcher-C38yhZRs.js → dispatcher-LtvFKYUp.js} +3 -3
  6. package/.dist/chunks/dispatcher-LtvFKYUp.js.map +1 -0
  7. package/.dist/chunks/{dispatcher-DuBLHNa6.js → dispatcher-pHH4JcFR.js} +12 -11
  8. package/.dist/chunks/dispatcher-pHH4JcFR.js.map +1 -0
  9. package/.dist/chunks/{errors-0PaWf7Bq.js → errors-Dcf0MVbq.js} +3 -3
  10. package/.dist/chunks/errors-Dcf0MVbq.js.map +1 -0
  11. package/.dist/chunks/remote-client-Bj1pZXgq.js +108 -0
  12. package/.dist/chunks/remote-client-Bj1pZXgq.js.map +1 -0
  13. package/.dist/chunks/{runtime-C51Dv3K6.js → runtime-ekkMQ-wL.js} +4 -4
  14. package/.dist/chunks/runtime-ekkMQ-wL.js.map +1 -0
  15. package/.dist/chunks/{transform-Cdsx7ygD.js → transform-CEK6Ccll.js} +246 -172
  16. package/.dist/chunks/transform-CEK6Ccll.js.map +1 -0
  17. package/.dist/chunks/{vite-DR3-WaqQ.js → vite-Dm3bXmR3.js} +54 -46
  18. package/.dist/chunks/vite-Dm3bXmR3.js.map +1 -0
  19. package/.dist/detect.js.map +1 -1
  20. package/.dist/dispatcher.d.ts +1 -1
  21. package/.dist/dispatcher.js +1 -1
  22. package/.dist/errors.d.ts +1 -1
  23. package/.dist/internal/generators.js +2 -2
  24. package/.dist/internal/remote-client.js +1 -1
  25. package/.dist/markup/promise.js +1 -1
  26. package/.dist/markup/promise.js.map +1 -1
  27. package/.dist/markup/run.js +3 -3
  28. package/.dist/markup/run.js.map +1 -1
  29. package/.dist/markup/transform/index.d.ts +8 -0
  30. package/.dist/markup/transform/scan.d.ts +20 -0
  31. package/.dist/markup/transform/types.d.ts +136 -2
  32. package/.dist/markup/transform.js +1 -1
  33. package/.dist/markup/value.js +1 -1
  34. package/.dist/markup/value.js.map +1 -1
  35. package/.dist/mod.js +3 -3
  36. package/.dist/mod.js.map +1 -1
  37. package/.dist/remote/client/command.d.ts +1 -1
  38. package/.dist/remote/client/effect.d.ts +15 -1
  39. package/.dist/remote/client/index.d.ts +1 -1
  40. package/.dist/remote/client/types.d.ts +3 -1
  41. package/.dist/remote/client.js +1 -1
  42. package/.dist/remote/server.js +27 -9
  43. package/.dist/remote/server.js.map +1 -1
  44. package/.dist/remote/shared.d.ts +126 -0
  45. package/.dist/remote/shared.js +117 -5
  46. package/.dist/remote/shared.js.map +1 -1
  47. package/.dist/runtime/transform.js +3 -3
  48. package/.dist/runtime/transform.js.map +1 -1
  49. package/.dist/script-transform/types.d.ts +95 -0
  50. package/.dist/server/effects.d.ts +15 -1
  51. package/.dist/server/factories.d.ts +128 -9
  52. package/.dist/server/index.d.ts +1 -1
  53. package/.dist/server/runtime.d.ts +1 -1
  54. package/.dist/server/types.d.ts +128 -7
  55. package/.dist/server.js +28 -13
  56. package/.dist/server.js.map +1 -1
  57. package/.dist/vite/remote-client.d.ts +24 -0
  58. package/.dist/vite.d.ts +8 -3
  59. package/.dist/vite.js +1 -1
  60. package/LICENSE +29 -0
  61. package/package.json +21 -21
  62. package/.dist/chunks/client-5cizmHtc.js.map +0 -1
  63. package/.dist/chunks/descriptors-OS4VfJuW.js.map +0 -1
  64. package/.dist/chunks/dispatcher-C38yhZRs.js.map +0 -1
  65. package/.dist/chunks/dispatcher-DuBLHNa6.js.map +0 -1
  66. package/.dist/chunks/errors-0PaWf7Bq.js.map +0 -1
  67. package/.dist/chunks/runtime-C51Dv3K6.js.map +0 -1
  68. package/.dist/chunks/transform-Cdsx7ygD.js.map +0 -1
  69. package/.dist/chunks/vite-DR3-WaqQ.js.map +0 -1
@@ -1,8 +1,8 @@
1
- import { A as YieldStarInEventCallbackError, D as UnsupportedMarkupEffectPositionError, n as AsyncEffectInSyncRuneError, t as AsyncEffectInEventCallbackError } from "./errors-0PaWf7Bq.js";
1
+ import { A as YieldStarInEventCallbackError, D as UnsupportedMarkupEffectPositionError, n as AsyncEffectInSyncRuneError, t as AsyncEffectInEventCallbackError } from "./errors-Dcf0MVbq.js";
2
2
  import { contains_top_level_yield_star } from "../detect.js";
3
+ import ts from "typescript";
3
4
  import { parse } from "svelte/compiler";
4
5
  import MagicString from "magic-string";
5
- import ts from "typescript";
6
6
  //#region src/script-transform/imports.ts
7
7
  /**
8
8
  * Builds the import statements injected by the script transform.
@@ -244,148 +244,6 @@ function make_name_allocator(initial_names) {
244
244
  } };
245
245
  }
246
246
  //#endregion
247
- //#region src/markup/transform/classify.ts
248
- /**
249
- * Matches sanitized placeholders back to their Svelte AST context.
250
- *
251
- * @since 2.0.0
252
- * @param ast - Parsed Svelte AST for the sanitized component markup.
253
- * @param candidates - Placeholder candidates produced by the scanner.
254
- * @returns Candidates paired with the markup context that determines how they
255
- * should be emitted.
256
- */
257
- function classify_candidates(ast, candidates) {
258
- const by_placeholder = new Map(candidates.map((candidate) => [candidate.placeholder, candidate]));
259
- const classified = [];
260
- const matched = /* @__PURE__ */ new Set();
261
- walk_ast(ast.fragment, by_placeholder, matched, classified);
262
- return classified;
263
- }
264
- function walk_ast(fragment, candidates, matched, classified) {
265
- for (const node of fragment.nodes) visit_ast_node(node, candidates, matched, classified);
266
- }
267
- function visit_ast_node(node, candidates, matched, classified) {
268
- switch (node.type) {
269
- case "ExpressionTag":
270
- classify_expression(node.expression, "plain", candidates, matched, classified);
271
- return;
272
- case "IfBlock":
273
- classify_expression(node.test, "plain", candidates, matched, classified);
274
- walk_ast(node.consequent, candidates, matched, classified);
275
- if (node.alternate) walk_ast(node.alternate, candidates, matched, classified);
276
- return;
277
- case "EachBlock":
278
- classify_expression(node.expression, "each", candidates, matched, classified);
279
- walk_ast(node.body, candidates, matched, classified);
280
- if (node.fallback) walk_ast(node.fallback, candidates, matched, classified);
281
- return;
282
- case "AwaitBlock":
283
- classify_expression(node.expression, "await", candidates, matched, classified);
284
- if (node.pending) walk_ast(node.pending, candidates, matched, classified);
285
- if (node.then) walk_ast(node.then, candidates, matched, classified);
286
- if (node.catch) walk_ast(node.catch, candidates, matched, classified);
287
- return;
288
- case "RenderTag":
289
- classify_expression(node.expression, "render", candidates, matched, classified);
290
- return;
291
- case "HtmlTag":
292
- classify_expression(node.expression, "plain", candidates, matched, classified);
293
- return;
294
- case "DebugTag": return;
295
- case "ConstTag":
296
- case "DeclarationTag":
297
- classify_declaration_tag(node, candidates, matched, classified);
298
- return;
299
- case "KeyBlock":
300
- classify_expression(node.expression, "plain", candidates, matched, classified);
301
- walk_ast(node.fragment, candidates, matched, classified);
302
- return;
303
- case "RegularElement":
304
- case "Component":
305
- case "TitleElement":
306
- case "SlotElement":
307
- case "SvelteBody":
308
- case "SvelteBoundary":
309
- case "SvelteComponent":
310
- case "SvelteDocument":
311
- case "SvelteElement":
312
- case "SvelteFragment":
313
- case "SvelteHead":
314
- case "SvelteSelf":
315
- case "SvelteWindow":
316
- visit_element_attributes(node, candidates, matched, classified);
317
- walk_ast(node.fragment, candidates, matched, classified);
318
- return;
319
- default: return;
320
- }
321
- }
322
- function classify_declaration_tag(node, candidates, matched, classified) {
323
- for (const decl of node.declaration.declarations) classify_expression(decl, "plain", candidates, matched, classified);
324
- }
325
- function visit_element_attributes(node, candidates, matched, classified) {
326
- for (const attr of node.attributes) {
327
- if (attr.type === "Attribute" && attr.name && is_event_attribute_name(attr.name)) {
328
- visit_attribute_value(attr.value, "event", candidates, matched, classified);
329
- continue;
330
- }
331
- if (attr.type === "OnDirective" && attr.expression) {
332
- classify_expression(attr.expression, "event", candidates, matched, classified);
333
- continue;
334
- }
335
- }
336
- }
337
- function is_event_attribute_name(name) {
338
- return name.startsWith("on:") || /^on[a-z]/.test(name);
339
- }
340
- function visit_attribute_value(value, kind, candidates, matched, classified) {
341
- if (value === true) return;
342
- if (Array.isArray(value)) {
343
- for (const part of value) if (part.type === "ExpressionTag") classify_expression(part.expression, kind, candidates, matched, classified);
344
- return;
345
- }
346
- classify_expression(value.expression, kind, candidates, matched, classified);
347
- }
348
- function classify_expression(expression, kind, candidates, matched, classified) {
349
- if (!expression) return;
350
- const found_candidates = find_candidates(expression, candidates);
351
- for (const candidate of found_candidates) {
352
- if (matched.has(candidate.placeholder)) continue;
353
- matched.add(candidate.placeholder);
354
- classified.push({
355
- candidate,
356
- kind: resolve_candidate_kind(candidate, kind)
357
- });
358
- }
359
- }
360
- function resolve_candidate_kind(candidate, context_kind) {
361
- if (candidate.key === "render_argument") return "render_argument";
362
- return context_kind;
363
- }
364
- function find_candidates(expression, candidates) {
365
- const found = [];
366
- visit_expression_value(expression, candidates, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), found);
367
- return found;
368
- }
369
- function visit_expression_value(value, candidates, seen_nodes, seen_placeholders, found) {
370
- if (Array.isArray(value)) {
371
- for (const item of value) visit_expression_value(item, candidates, seen_nodes, seen_placeholders, found);
372
- return;
373
- }
374
- if (!is_record(value) || seen_nodes.has(value)) return;
375
- seen_nodes.add(value);
376
- if (value.type === "Identifier" && typeof value.name === "string") {
377
- const candidate = candidates.get(value.name);
378
- if (candidate && !seen_placeholders.has(candidate.placeholder)) {
379
- seen_placeholders.add(candidate.placeholder);
380
- found.push(candidate);
381
- }
382
- }
383
- for (const child of Object.values(value)) visit_expression_value(child, candidates, seen_nodes, seen_placeholders, found);
384
- }
385
- function is_record(value) {
386
- return typeof value === "object" && value !== null;
387
- }
388
- //#endregion
389
247
  //#region src/markup/transform/effect-bindings.ts
390
248
  const EFFECT_PACKAGE_MODULE = "effect";
391
249
  const EFFECT_DIRECT_MODULE = "effect/Effect";
@@ -529,6 +387,148 @@ function add_ordered_name(names, name) {
529
387
  names.push(name);
530
388
  }
531
389
  //#endregion
390
+ //#region src/markup/transform/classify.ts
391
+ /**
392
+ * Matches sanitized placeholders back to their Svelte AST context.
393
+ *
394
+ * @since 2.0.0
395
+ * @param ast - Parsed Svelte AST for the sanitized component markup.
396
+ * @param candidates - Placeholder candidates produced by the scanner.
397
+ * @returns Candidates paired with the markup context that determines how they
398
+ * should be emitted.
399
+ */
400
+ function classify_candidates(ast, candidates) {
401
+ const by_placeholder = new Map(candidates.map((candidate) => [candidate.placeholder, candidate]));
402
+ const classified = [];
403
+ const matched = /* @__PURE__ */ new Set();
404
+ walk_ast(ast.fragment, by_placeholder, matched, classified);
405
+ return classified;
406
+ }
407
+ function walk_ast(fragment, candidates, matched, classified) {
408
+ for (const node of fragment.nodes) visit_ast_node(node, candidates, matched, classified);
409
+ }
410
+ function visit_ast_node(node, candidates, matched, classified) {
411
+ switch (node.type) {
412
+ case "ExpressionTag":
413
+ classify_expression(node.expression, "plain", candidates, matched, classified);
414
+ return;
415
+ case "IfBlock":
416
+ classify_expression(node.test, "plain", candidates, matched, classified);
417
+ walk_ast(node.consequent, candidates, matched, classified);
418
+ if (node.alternate) walk_ast(node.alternate, candidates, matched, classified);
419
+ return;
420
+ case "EachBlock":
421
+ classify_expression(node.expression, "each", candidates, matched, classified);
422
+ walk_ast(node.body, candidates, matched, classified);
423
+ if (node.fallback) walk_ast(node.fallback, candidates, matched, classified);
424
+ return;
425
+ case "AwaitBlock":
426
+ classify_expression(node.expression, "await", candidates, matched, classified);
427
+ if (node.pending) walk_ast(node.pending, candidates, matched, classified);
428
+ if (node.then) walk_ast(node.then, candidates, matched, classified);
429
+ if (node.catch) walk_ast(node.catch, candidates, matched, classified);
430
+ return;
431
+ case "RenderTag":
432
+ classify_expression(node.expression, "render", candidates, matched, classified);
433
+ return;
434
+ case "HtmlTag":
435
+ classify_expression(node.expression, "plain", candidates, matched, classified);
436
+ return;
437
+ case "DebugTag": return;
438
+ case "ConstTag":
439
+ case "DeclarationTag":
440
+ classify_declaration_tag(node, candidates, matched, classified);
441
+ return;
442
+ case "KeyBlock":
443
+ classify_expression(node.expression, "plain", candidates, matched, classified);
444
+ walk_ast(node.fragment, candidates, matched, classified);
445
+ return;
446
+ case "RegularElement":
447
+ case "Component":
448
+ case "TitleElement":
449
+ case "SlotElement":
450
+ case "SvelteBody":
451
+ case "SvelteBoundary":
452
+ case "SvelteComponent":
453
+ case "SvelteDocument":
454
+ case "SvelteElement":
455
+ case "SvelteFragment":
456
+ case "SvelteHead":
457
+ case "SvelteSelf":
458
+ case "SvelteWindow":
459
+ visit_element_attributes(node, candidates, matched, classified);
460
+ walk_ast(node.fragment, candidates, matched, classified);
461
+ return;
462
+ default: return;
463
+ }
464
+ }
465
+ function classify_declaration_tag(node, candidates, matched, classified) {
466
+ for (const decl of node.declaration.declarations) classify_expression(decl, "plain", candidates, matched, classified);
467
+ }
468
+ function visit_element_attributes(node, candidates, matched, classified) {
469
+ 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);
472
+ continue;
473
+ }
474
+ if (attr.type === "OnDirective" && attr.expression) {
475
+ classify_expression(attr.expression, "event", candidates, matched, classified);
476
+ continue;
477
+ }
478
+ }
479
+ }
480
+ function is_event_attribute_name(name) {
481
+ return name.startsWith("on:") || /^on[a-z]/.test(name);
482
+ }
483
+ function visit_attribute_value(value, kind, candidates, matched, classified) {
484
+ if (value === true) return;
485
+ if (Array.isArray(value)) {
486
+ for (const part of value) if (part.type === "ExpressionTag") classify_expression(part.expression, kind, candidates, matched, classified);
487
+ return;
488
+ }
489
+ classify_expression(value.expression, kind, candidates, matched, classified);
490
+ }
491
+ function classify_expression(expression, kind, candidates, matched, classified) {
492
+ if (!expression) return;
493
+ const found_candidates = find_candidates(expression, candidates);
494
+ for (const candidate of found_candidates) {
495
+ if (matched.has(candidate.placeholder)) continue;
496
+ matched.add(candidate.placeholder);
497
+ classified.push({
498
+ candidate,
499
+ kind: resolve_candidate_kind(candidate, kind)
500
+ });
501
+ }
502
+ }
503
+ function resolve_candidate_kind(candidate, context_kind) {
504
+ if (candidate.key === "render_argument") return "render_argument";
505
+ return context_kind;
506
+ }
507
+ function find_candidates(expression, candidates) {
508
+ const found = [];
509
+ visit_expression_value(expression, candidates, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), found);
510
+ return found;
511
+ }
512
+ function visit_expression_value(value, candidates, seen_nodes, seen_placeholders, found) {
513
+ if (Array.isArray(value)) {
514
+ for (const item of value) visit_expression_value(item, candidates, seen_nodes, seen_placeholders, found);
515
+ return;
516
+ }
517
+ if (!is_record(value) || seen_nodes.has(value)) return;
518
+ seen_nodes.add(value);
519
+ if (value.type === "Identifier" && typeof value.name === "string") {
520
+ const candidate = candidates.get(value.name);
521
+ if (candidate && !seen_placeholders.has(candidate.placeholder)) {
522
+ seen_placeholders.add(candidate.placeholder);
523
+ found.push(candidate);
524
+ }
525
+ }
526
+ for (const child of Object.values(value)) visit_expression_value(child, candidates, seen_nodes, seen_placeholders, found);
527
+ }
528
+ function is_record(value) {
529
+ return typeof value === "object" && value !== null;
530
+ }
531
+ //#endregion
532
532
  //#region src/markup/transform/expressions.ts
533
533
  /**
534
534
  * Strips an event handler arrow function down to its executable body.
@@ -702,8 +702,8 @@ function is_property_access_name(node) {
702
702
  }
703
703
  //#endregion
704
704
  //#region src/markup/transform/effect-callbacks.ts
705
- const MATCH_EFFECT_MEMBERS = new Map([["match", "matchEffect"], ["matchCause", "matchCauseEffect"]]);
706
- const EFFECTFUL_CALLBACK_MEMBERS = new Set([
705
+ const match_effect_members = /* @__PURE__ */ new Map([["match", "matchEffect"], ["matchCause", "matchCauseEffect"]]);
706
+ const effectful_callback_members = /* @__PURE__ */ new Set([
707
707
  "andThen",
708
708
  "catchAll",
709
709
  "catchAllCause",
@@ -715,11 +715,12 @@ const EFFECTFUL_CALLBACK_MEMBERS = new Set([
715
715
  "tapError",
716
716
  "tapErrorCause"
717
717
  ]);
718
- const EFFECTFUL_HANDLER_MEMBERS = new Set([
718
+ const effectful_handler_members = /* @__PURE__ */ new Set([
719
719
  "matchCauseEffect",
720
720
  "matchEffect",
721
721
  "tapBoth"
722
722
  ]);
723
+ const effectful_handler_property_names = /* @__PURE__ */ new Set(["onFailure", "onSuccess"]);
723
724
  /**
724
725
  * Rewrites effectful callback shorthand inside event handler expressions.
725
726
  *
@@ -787,7 +788,7 @@ function visit_expression(node, context) {
787
788
  }
788
789
  function rewrite_match_call(call, context) {
789
790
  const member = get_effect_member(call.expression, context);
790
- const upgraded_name = member && MATCH_EFFECT_MEMBERS.get(member.name);
791
+ const upgraded_name = member && match_effect_members.get(member.name);
791
792
  const options = get_last_object_argument(call);
792
793
  if (!member || !upgraded_name || !options) return;
793
794
  const handlers = get_handler_properties(options);
@@ -803,12 +804,12 @@ function rewrite_match_call(call, context) {
803
804
  function rewrite_effectful_handler_call(call, context) {
804
805
  const member = get_effect_member(call.expression, context);
805
806
  const options = get_last_object_argument(call);
806
- if (!member || !EFFECTFUL_HANDLER_MEMBERS.has(member.name) || !options) return;
807
+ if (!member || !effectful_handler_members.has(member.name) || !options) return;
807
808
  for (const handler of get_handler_properties(options)) if (handler.callback && is_non_generator_callback_with_top_level_yield(handler.callback)) rewrite_callback_to_effect_gen(handler.callback, context);
808
809
  }
809
810
  function rewrite_effectful_callback_arguments(call, context) {
810
811
  const member = get_effect_member(call.expression, context);
811
- if (!member || !EFFECTFUL_CALLBACK_MEMBERS.has(member.name)) return;
812
+ if (!member || !effectful_callback_members.has(member.name)) return;
812
813
  for (const argument of call.arguments) if (is_callback_expression(argument) && is_non_generator_callback_with_top_level_yield(argument)) rewrite_callback_to_effect_gen(argument, context);
813
814
  }
814
815
  function rewrite_callback_to_effect_gen(callback, context) {
@@ -860,7 +861,7 @@ function get_handler_properties(object_literal) {
860
861
  return object_literal.properties.flatMap((property) => {
861
862
  if (!ts.isPropertyAssignment(property)) return [];
862
863
  const name = get_property_name(property.name);
863
- if (name !== "onFailure" && name !== "onSuccess") return [];
864
+ if (!name || !effectful_handler_property_names.has(name)) return [];
864
865
  return [{ callback: is_callback_expression(property.initializer) ? property.initializer : void 0 }];
865
866
  });
866
867
  }
@@ -1098,14 +1099,14 @@ function slice_start(content, node) {
1098
1099
  }
1099
1100
  //#endregion
1100
1101
  //#region src/script-transform/runes.ts
1101
- const ASYNC_EXPRESSION_RUNES = new Set([
1102
+ const ASYNC_EXPRESSION_RUNES = /* @__PURE__ */ new Set([
1102
1103
  "$derived",
1103
1104
  "$state",
1104
1105
  "$state.raw",
1105
1106
  "$state.snapshot",
1106
1107
  "$bindable"
1107
1108
  ]);
1108
- const CALLBACK_RUNES = new Set([
1109
+ const CALLBACK_RUNES = /* @__PURE__ */ new Set([
1109
1110
  "$derived.by",
1110
1111
  "$effect",
1111
1112
  "$effect.pre",
@@ -1224,8 +1225,29 @@ function find_yield_star_node(node, on_found) {
1224
1225
  }
1225
1226
  //#endregion
1226
1227
  //#region src/markup/transform/scan.ts
1228
+ /**
1229
+ * Replaces markup `yield*` expressions with placeholders before Svelte parses
1230
+ * the component.
1231
+ *
1232
+ * @example
1233
+ * ```ts
1234
+ * const sanitized = sanitize_markup(
1235
+ * `<p>{yield* loadLabel()}</p>`,
1236
+ * "Label.svelte",
1237
+ * );
1238
+ * ```
1239
+ *
1240
+ * @since 2.0.0
1241
+ * @param content - Raw Svelte component source to scan for effectful markup
1242
+ * expressions.
1243
+ * @param filename - Source filename used when validation errors need to point
1244
+ * back to the component being transformed.
1245
+ * @returns Sanitized source plus placeholder candidates that should be lowered
1246
+ * after Svelte classifies their markup positions.
1247
+ */
1227
1248
  function sanitize_markup(content, filename) {
1228
1249
  const candidates = [];
1250
+ const excluded_ranges = collect_excluded_ranges(content);
1229
1251
  const magic = new MagicString(content);
1230
1252
  let helper_index = 0;
1231
1253
  let cursor = 0;
@@ -1233,8 +1255,9 @@ function sanitize_markup(content, filename) {
1233
1255
  const open = content.indexOf("{", cursor);
1234
1256
  if (open === -1) break;
1235
1257
  /** Skip braces inside <script> and <style> blocks. */
1236
- if (is_inside_excluded_block(content, open) || is_inside_html_comment(content, open)) {
1237
- cursor = open + 1;
1258
+ const excluded_range = find_excluded_range(excluded_ranges, open);
1259
+ if (excluded_range) {
1260
+ cursor = excluded_range.end;
1238
1261
  continue;
1239
1262
  }
1240
1263
  /** Find the matching closing brace. */
@@ -1359,25 +1382,68 @@ function collect_expression_yield_expressions(content, expr_start, expr_text, fi
1359
1382
  });
1360
1383
  return expressions;
1361
1384
  }
1362
- function is_inside_excluded_block(content, pos) {
1363
- const script = find_tag_end(content, "script", pos);
1364
- const style = find_tag_end(content, "style", pos);
1365
- return script !== void 0 && pos < script.end && pos > script.start || style !== void 0 && pos < style.end && pos > style.start;
1366
- }
1367
- function is_inside_html_comment(content, pos) {
1368
- const open = content.lastIndexOf("<!--", pos);
1369
- const close = content.lastIndexOf("-->", pos);
1370
- return open !== -1 && open > close;
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);
1371
1393
  }
1372
- function find_tag_end(content, tag, after_pos) {
1394
+ function collect_tag_ranges(content, tag) {
1373
1395
  const pattern = new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)<\\/${tag}\\s*>`, "gi");
1374
- for (const match of content.matchAll(pattern)) {
1375
- if (match.index === void 0) continue;
1376
- const end_pos = match.index + match[0].length;
1377
- if (match.index <= after_pos && after_pos < end_pos) return {
1396
+ return [...content.matchAll(pattern)].flatMap((match) => {
1397
+ if (match.index === void 0) return [];
1398
+ return [{
1378
1399
  start: match.index,
1379
- end: end_pos
1380
- };
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;
1381
1447
  }
1382
1448
  }
1383
1449
  /** Brace matching helpers for extracting complete markup expressions. */
@@ -1528,6 +1594,14 @@ function contains_yield_star_in_text(text) {
1528
1594
  * context for each placeholder (plain expression, #each, #await, event
1529
1595
  * handler, etc.).
1530
1596
  *
1597
+ * @example
1598
+ * ```ts
1599
+ * const result = transform_markup_effect(
1600
+ * "<button onclick={yield* save()}>Save</button>",
1601
+ * "SaveButton.svelte",
1602
+ * );
1603
+ * ```
1604
+ *
1531
1605
  * @since 2.0.0
1532
1606
  * @param content - The raw `.svelte` file content.
1533
1607
  * @param filename - The source filename, used in error messages.
@@ -1572,4 +1646,4 @@ function transform_markup_effect(content, filename, options = {}) {
1572
1646
  //#endregion
1573
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 };
1574
1648
 
1575
- //# sourceMappingURL=transform-Cdsx7ygD.js.map
1649
+ //# sourceMappingURL=transform-CEK6Ccll.js.map