svelte-effect-runtime 2.4.0 → 2.5.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 (61) hide show
  1. package/.dist/chunks/{client-CfkB_G4o.js → client-Cj2X9lY-.js} +11 -24
  2. package/.dist/chunks/client-Cj2X9lY-.js.map +1 -0
  3. package/.dist/chunks/{dispatcher-8B6Ol80H.js → dispatcher-BVkm8qbG.js} +33 -13
  4. package/.dist/chunks/dispatcher-BVkm8qbG.js.map +1 -0
  5. package/.dist/chunks/{preprocess-Cn4JH9OO.js → preprocess-DseI9Doo.js} +206 -73
  6. package/.dist/chunks/preprocess-DseI9Doo.js.map +1 -0
  7. package/.dist/chunks/transform-BDfspL1K.js +59 -0
  8. package/.dist/chunks/transform-BDfspL1K.js.map +1 -0
  9. package/.dist/chunks/{transform-s9gKPYu5.js → transform-Cx5bjmP6.js} +267 -48
  10. package/.dist/chunks/transform-Cx5bjmP6.js.map +1 -0
  11. package/.dist/dispatcher.js +1 -1
  12. package/.dist/error.d.ts +23 -0
  13. package/.dist/internal/generators.js +1 -1
  14. package/.dist/internal/remote-client.js +1 -1
  15. package/.dist/markup/promise.js +1 -1
  16. package/.dist/markup/run.js +1 -1
  17. package/.dist/markup/transform/apply.d.ts +8 -2
  18. package/.dist/markup/transform/emit.d.ts +2 -2
  19. package/.dist/markup/transform/types.d.ts +9 -1
  20. package/.dist/markup/transform.js +1 -1
  21. package/.dist/markup/value.d.ts +1 -1
  22. package/.dist/markup/value.js +1 -1
  23. package/.dist/markup/value.js.map +1 -1
  24. package/.dist/mod.d.ts +1 -1
  25. package/.dist/mod.js +1 -1
  26. package/.dist/mod.js.map +1 -1
  27. package/.dist/preprocess/imports.d.ts +20 -1
  28. package/.dist/preprocess/index.d.ts +5 -1
  29. package/.dist/preprocess/runtime-block.d.ts +11 -1
  30. package/.dist/preprocess/types.d.ts +27 -0
  31. package/.dist/preprocess.js +2 -2
  32. package/.dist/remote/client/command.d.ts +5 -1
  33. package/.dist/remote/client/effect.d.ts +1 -1
  34. package/.dist/remote/client/failures.d.ts +3 -3
  35. package/.dist/remote/client/form-enhance.d.ts +1 -1
  36. package/.dist/remote/client/form.d.ts +1 -1
  37. package/.dist/remote/client/query.d.ts +12 -5
  38. package/.dist/remote/client/responses.d.ts +2 -2
  39. package/.dist/remote/client/types.d.ts +57 -13
  40. package/.dist/remote/client.js +1 -1
  41. package/.dist/runtime/preprocess.d.ts +1 -0
  42. package/.dist/runtime/preprocess.js +5 -29
  43. package/.dist/runtime/preprocess.js.map +1 -1
  44. package/.dist/runtime/transform.d.ts +35 -0
  45. package/.dist/server/effects.d.ts +3 -3
  46. package/.dist/server/factories.d.ts +10 -38
  47. package/.dist/server/index.d.ts +1 -1
  48. package/.dist/server/invalid.d.ts +1 -1
  49. package/.dist/server/schema.d.ts +1 -1
  50. package/.dist/server/types.d.ts +51 -31
  51. package/.dist/server/wrappers.d.ts +4 -6
  52. package/.dist/server.d.ts +1 -1
  53. package/.dist/server.js +8 -15
  54. package/.dist/server.js.map +1 -1
  55. package/.dist/vite.js +32 -1
  56. package/.dist/vite.js.map +1 -1
  57. package/package.json +1 -1
  58. package/.dist/chunks/client-CfkB_G4o.js.map +0 -1
  59. package/.dist/chunks/dispatcher-8B6Ol80H.js.map +0 -1
  60. package/.dist/chunks/preprocess-Cn4JH9OO.js.map +0 -1
  61. package/.dist/chunks/transform-s9gKPYu5.js.map +0 -1
@@ -2,6 +2,88 @@ import { contains_top_level_yield_star } from "../detect.js";
2
2
  import { parse } from "svelte/compiler";
3
3
  import MagicString from "magic-string";
4
4
  import ts from "typescript";
5
+ //#region src/preprocess/imports.ts
6
+ /**
7
+ * Builds the import statements injected by the script preprocessor.
8
+ *
9
+ * @since 2.0.0
10
+ * @param has_effect_import - Whether the user already imports `Effect`.
11
+ * @param has_dispatcher_import - Whether the user already imports
12
+ * `get_dispatcher`.
13
+ * @param has_untrack_import - Whether the user already imports `untrack`.
14
+ * @returns Newline-separated import statements to inject.
15
+ */
16
+ function make_imports(has_effect_import, has_dispatcher_import, has_untrack_import, bindings = {
17
+ cancel: "__SER___cancel",
18
+ dispatcher: "get_dispatcher",
19
+ dispatcher_value: "__SER___dispatcher",
20
+ effect: "Effect",
21
+ program: "__SER___program",
22
+ untrack: "untrack"
23
+ }) {
24
+ 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";`;
25
+ const untrack_import = bindings.untrack === "untrack" ? `import { untrack } from "svelte";` : `import { untrack as ${bindings.untrack} } from "svelte";`;
26
+ const effect_import = has_effect_import ? false : bindings.effect === "Effect" ? `import { Effect } from "effect";` : `import { Effect as ${bindings.effect} } from "effect";`;
27
+ return [
28
+ !has_dispatcher_import && dispatcher_import,
29
+ !has_untrack_import && untrack_import,
30
+ effect_import
31
+ ].filter(Boolean).join("\n");
32
+ }
33
+ /**
34
+ * Checks whether a source file imports a local binding from a module.
35
+ *
36
+ * @since 2.0.0
37
+ * @param source_file - Parsed TypeScript source file to inspect.
38
+ * @param module_name - Module specifier to match.
39
+ * @param local_name - Local binding name to look for.
40
+ * @returns Whether that binding is already locally available.
41
+ */
42
+ function has_local_import_binding(source_file, module_name, local_name) {
43
+ return source_file.statements.some((stmt) => {
44
+ if (!ts.isImportDeclaration(stmt) || !ts.isStringLiteral(stmt.moduleSpecifier) || stmt.moduleSpecifier.text !== module_name) return false;
45
+ const clause = stmt.importClause;
46
+ if (!clause || clause.isTypeOnly) return false;
47
+ if (clause.name?.text === local_name) return true;
48
+ const named_bindings = clause.namedBindings;
49
+ if (!named_bindings) return false;
50
+ if (ts.isNamespaceImport(named_bindings)) return named_bindings.name.text === local_name;
51
+ return named_bindings.elements.some((element) => !element.isTypeOnly && element.name.text === local_name);
52
+ });
53
+ }
54
+ /**
55
+ * Collects every local binding declared at module top level.
56
+ *
57
+ * @since 2.4.2
58
+ * @param source_file - Parsed TypeScript source file to inspect.
59
+ * @returns Top-level names declared by imports, declarations, and variables.
60
+ */
61
+ function collect_top_level_binding_names(source_file) {
62
+ return source_file.statements.flatMap(collect_statement_binding_names);
63
+ }
64
+ function collect_statement_binding_names(stmt) {
65
+ if (ts.isImportDeclaration(stmt)) return collect_import_binding_names(stmt);
66
+ if (ts.isVariableStatement(stmt)) return stmt.declarationList.declarations.flatMap((decl) => collect_binding_name_text(decl.name));
67
+ if (ts.isFunctionDeclaration(stmt) || ts.isClassDeclaration(stmt) || ts.isInterfaceDeclaration(stmt) || ts.isTypeAliasDeclaration(stmt) || ts.isEnumDeclaration(stmt) || ts.isModuleDeclaration(stmt)) return stmt.name ? [stmt.name.text] : [];
68
+ return [];
69
+ }
70
+ function collect_import_binding_names(stmt) {
71
+ const clause = stmt.importClause;
72
+ if (!clause) return [];
73
+ return [
74
+ clause.name?.text,
75
+ clause.namedBindings && ts.isNamespaceImport(clause.namedBindings) ? clause.namedBindings.name.text : void 0,
76
+ clause.namedBindings && ts.isNamedImports(clause.namedBindings) ? clause.namedBindings.elements.map((element) => element.name.text) : void 0
77
+ ].flat().filter((name) => name !== void 0);
78
+ }
79
+ function collect_binding_name_text(name) {
80
+ if (ts.isIdentifier(name)) return [name.text];
81
+ return name.elements.flatMap((element) => {
82
+ if (ts.isOmittedExpression(element)) return [];
83
+ return collect_binding_name_text(element.name);
84
+ });
85
+ }
86
+ //#endregion
5
87
  //#region src/markup/transform/constants.ts
6
88
  const HELPERS = {
7
89
  value: "__SER___markup_value",
@@ -22,14 +104,13 @@ function blank_script_blocks(content) {
22
104
  return match.split("\n").map((l) => " ".repeat(l.length)).join("\n");
23
105
  });
24
106
  }
25
- function inject_helpers(magic, content, helpers = []) {
26
- if (content.includes(HELPERS.value)) return;
107
+ function inject_helpers(magic, content, helpers = [], bindings = HELPERS) {
27
108
  const import_helpers = unique_import_helpers(helpers);
28
109
  const local_helpers = helpers.filter((helper) => !is_import_helper(helper));
29
110
  const helper_segments = [
30
- `import { value as ${HELPERS.value} } from "svelte-effect-runtime/internal/generators";`,
31
- `import { promise as ${HELPERS.promise} } from "svelte-effect-runtime/internal/generators";`,
32
- `import { run as ${HELPERS.run} } from "svelte-effect-runtime/internal/generators";`,
111
+ `import { value as ${bindings.value} } from "svelte-effect-runtime/internal/generators";`,
112
+ `import { promise as ${bindings.promise} } from "svelte-effect-runtime/internal/generators";`,
113
+ `import { run as ${bindings.run} } from "svelte-effect-runtime/internal/generators";`,
33
114
  ...import_helpers,
34
115
  ...local_helpers
35
116
  ].map((helper) => typeof helper === "string" ? { text: helper } : helper);
@@ -53,6 +134,18 @@ function inject_helpers(magic, content, helpers = []) {
53
134
  };
54
135
  }
55
136
  }
137
+ function make_markup_helper_bindings(content) {
138
+ const script_tag = find_instance_script_tag(content);
139
+ const name_allocator = make_name_allocator(script_tag ? collect_script_binding_names(content.slice(script_tag.start, script_tag.end)) : []);
140
+ return {
141
+ bindings: {
142
+ value: name_allocator.reserve(HELPERS.value),
143
+ promise: name_allocator.reserve(HELPERS.promise),
144
+ run: name_allocator.reserve(HELPERS.run)
145
+ },
146
+ name_allocator
147
+ };
148
+ }
56
149
  function create_relocations(replacements, helper_insertion) {
57
150
  const edits = [helper_insertion && {
58
151
  start: helper_insertion.start,
@@ -120,6 +213,22 @@ function unique_import_helpers(helpers) {
120
213
  function is_import_helper(helper) {
121
214
  return helper.text.trimStart().startsWith("import ");
122
215
  }
216
+ function collect_script_binding_names(script_content) {
217
+ return collect_top_level_binding_names(ts.createSourceFile("markup-script.ts", script_content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS));
218
+ }
219
+ function make_name_allocator(initial_names) {
220
+ const used_names = new Set(initial_names);
221
+ return { reserve(name) {
222
+ let candidate = name;
223
+ let suffix = 1;
224
+ while (used_names.has(candidate)) {
225
+ candidate = `${name}_${suffix}`;
226
+ suffix += 1;
227
+ }
228
+ used_names.add(candidate);
229
+ return candidate;
230
+ } };
231
+ }
123
232
  //#endregion
124
233
  //#region src/markup/transform/classify.ts
125
234
  /**
@@ -168,9 +277,7 @@ function visit_ast_node(node, candidates, matched, classified) {
168
277
  case "HtmlTag":
169
278
  classify_expression(node.expression, "plain", candidates, matched, classified);
170
279
  return;
171
- case "DebugTag":
172
- classify_debug_tag(node, candidates, matched, classified);
173
- return;
280
+ case "DebugTag": return;
174
281
  case "ConstTag":
175
282
  case "DeclarationTag":
176
283
  classify_declaration_tag(node, candidates, matched, classified);
@@ -198,11 +305,6 @@ function visit_ast_node(node, candidates, matched, classified) {
198
305
  default: return;
199
306
  }
200
307
  }
201
- function classify_debug_tag(node, candidates, matched, classified) {
202
- const idents = node.identifiers;
203
- if (!idents || idents.length === 0) return;
204
- for (const ident of idents) classify_expression(ident, "plain", candidates, matched, classified);
205
- }
206
308
  function classify_declaration_tag(node, candidates, matched, classified) {
207
309
  for (const decl of node.declaration.declarations) {
208
310
  if (!decl.init) continue;
@@ -240,10 +342,14 @@ function classify_expression(expression, kind, candidates, matched, classified)
240
342
  matched.add(candidate.placeholder);
241
343
  classified.push({
242
344
  candidate,
243
- kind
345
+ kind: resolve_candidate_kind(candidate, kind)
244
346
  });
245
347
  }
246
348
  }
349
+ function resolve_candidate_kind(candidate, context_kind) {
350
+ if (candidate.key === "render_argument") return "render_argument";
351
+ return context_kind;
352
+ }
247
353
  function find_candidates(expression, candidates) {
248
354
  const found = [];
249
355
  visit_expression_value(expression, candidates, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), found);
@@ -589,6 +695,39 @@ var YieldStarInEventCallbackError = class extends PreprocessError {
589
695
  this.expression_text = expression_text;
590
696
  }
591
697
  };
698
+ /**
699
+ * Thrown when async Effect work appears in a markup position SER cannot lower
700
+ * safely.
701
+ *
702
+ * @example
703
+ * ```ts
704
+ * throw new UnsupportedMarkupEffectPositionError(
705
+ * "Component.svelte",
706
+ * "value={yield* load()}",
707
+ * );
708
+ * ```
709
+ *
710
+ * @since 2.4.2
711
+ */
712
+ var UnsupportedMarkupEffectPositionError = class extends PreprocessError {
713
+ /**
714
+ * The unsupported markup expression text.
715
+ *
716
+ * @since 2.4.2
717
+ */
718
+ expression_text;
719
+ constructor(filename, expression_text) {
720
+ super([
721
+ make_error_message("UNSUPPORTED_MARKUP_EFFECT_POSITION", `${filename}: yield* cannot be used in this markup position.`),
722
+ `Move the Effect work into a supported expression tag, block expression, render expression, declaration tag, or event handler.`,
723
+ "",
724
+ `Problematic expression:`,
725
+ expression_text
726
+ ].join("\n"), filename);
727
+ this.name = "UnsupportedMarkupEffectPositionError";
728
+ this.expression_text = expression_text;
729
+ }
730
+ };
592
731
  //#endregion
593
732
  //#region src/markup/transform/expressions.ts
594
733
  /**
@@ -693,22 +832,45 @@ function collect_free_identifiers(expr_text) {
693
832
  const fn = sf.statements[0];
694
833
  if (!ts.isFunctionDeclaration(fn) || !fn.body) return [];
695
834
  const ids = [];
835
+ const locals = /* @__PURE__ */ new Set();
696
836
  const seen = /* @__PURE__ */ new Set();
697
- visit_ids(fn.body, seen, ids);
837
+ visit_ids(fn.body, locals, seen, ids);
698
838
  return ids;
699
839
  }
700
- function visit_ids(node, seen, ids) {
701
- if (ts.isArrowFunction(node) || ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node)) return;
840
+ function visit_ids(node, locals, seen, ids) {
841
+ if (ts.isArrowFunction(node) || ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node)) {
842
+ const scoped = new Set(locals);
843
+ if (ts.isFunctionDeclaration(node) && node.name) scoped.add(node.name.text);
844
+ for (const parameter of node.parameters) add_binding_names(parameter.name, scoped);
845
+ if (node.body) visit_ids(node.body, scoped, seen, ids);
846
+ return;
847
+ }
848
+ if (ts.isVariableDeclaration(node)) {
849
+ if (node.initializer) visit_ids(node.initializer, locals, seen, ids);
850
+ add_binding_names(node.name, locals);
851
+ return;
852
+ }
853
+ if (ts.isTypeReferenceNode(node)) return;
702
854
  if (ts.isIdentifier(node)) {
703
855
  if (node.text === "yield" || node.text === "undefined" || node.text === "null" || node.text === "true" || node.text === "false" || node.text === "this") return;
704
856
  if (is_property_access_name(node)) return;
705
- if (!seen.has(node.text)) {
857
+ if (!locals.has(node.text) && !seen.has(node.text)) {
706
858
  seen.add(node.text);
707
859
  ids.push(node.text);
708
860
  }
709
861
  return;
710
862
  }
711
- node.forEachChild((child) => visit_ids(child, seen, ids));
863
+ node.forEachChild((child) => visit_ids(child, locals, seen, ids));
864
+ }
865
+ function add_binding_names(name, locals) {
866
+ if (ts.isIdentifier(name)) {
867
+ locals.add(name.text);
868
+ return;
869
+ }
870
+ for (const element of name.elements) {
871
+ if (ts.isOmittedExpression(element)) continue;
872
+ add_binding_names(element.name, locals);
873
+ }
712
874
  }
713
875
  function visit_event_body(node, context, result) {
714
876
  if (is_yield_star_expression$1(node)) {
@@ -973,10 +1135,10 @@ function to_expr_pos(pos, context) {
973
1135
  * expression rewrites.
974
1136
  * @returns Replacements ready to apply to the original component source.
975
1137
  */
976
- function emit_replacements(classified, effect_context) {
977
- return classified.map(({ candidate, kind }) => emit_replacement(candidate, kind, effect_context));
1138
+ function emit_replacements(classified, effect_context, helper_bindings, name_allocator) {
1139
+ return classified.map(({ candidate, kind }) => emit_replacement(candidate, kind, effect_context, helper_bindings, name_allocator));
978
1140
  }
979
- function emit_replacement(candidate, kind, effect_context) {
1141
+ function emit_replacement(candidate, kind, effect_context, helper_bindings, name_allocator) {
980
1142
  const normalized = normalize_effect_callback_yields(candidate.expr_text, effect_context);
981
1143
  const normalized_candidate = {
982
1144
  ...candidate,
@@ -984,24 +1146,28 @@ function emit_replacement(candidate, kind, effect_context) {
984
1146
  };
985
1147
  const id = make_cache_id(candidate);
986
1148
  const id_text = JSON.stringify(id);
987
- const helper_name = make_helper_name(candidate);
1149
+ const helper_name = make_helper_name(candidate, name_allocator);
988
1150
  let replacement_text;
989
1151
  let helpers;
990
1152
  let relocation;
991
1153
  if (kind === "await") {
992
1154
  const effect = make_effect_helper(normalized_candidate, helper_name);
993
- replacement_text = emit_promise_expression(id_text, effect);
1155
+ replacement_text = emit_promise_expression(id_text, effect, helper_bindings);
994
1156
  helpers = [...normalized.helpers, effect.helper];
995
1157
  } else if (kind === "render") {
996
1158
  const effect = make_effect_helper(normalized_candidate, helper_name);
997
- replacement_text = emit_render_expression(id_text, effect);
1159
+ replacement_text = emit_render_expression(id_text, effect, candidate, helper_bindings);
1160
+ helpers = [...normalized.helpers, effect.helper];
1161
+ } else if (kind === "render_argument") {
1162
+ const effect = make_effect_helper(normalized_candidate, helper_name);
1163
+ replacement_text = emit_each_expression(id_text, effect, helper_bindings);
998
1164
  helpers = [...normalized.helpers, effect.helper];
999
1165
  } else if (kind === "each") {
1000
1166
  const effect = make_effect_helper(normalized_candidate, helper_name);
1001
- replacement_text = emit_each_expression(id_text, effect);
1167
+ replacement_text = emit_each_expression(id_text, effect, helper_bindings);
1002
1168
  helpers = [...normalized.helpers, effect.helper];
1003
1169
  } else if (kind === "event") {
1004
- const event = make_event_handler(normalized_candidate);
1170
+ const event = make_event_handler(normalized_candidate, helper_bindings);
1005
1171
  replacement_text = event.text;
1006
1172
  helpers = normalized.helpers;
1007
1173
  relocation = make_relocation(candidate, replacement_text, {
@@ -1011,7 +1177,7 @@ function emit_replacement(candidate, kind, effect_context) {
1011
1177
  });
1012
1178
  } else {
1013
1179
  const effect = make_effect_helper(normalized_candidate, helper_name);
1014
- replacement_text = emit_each_expression(id_text, effect);
1180
+ replacement_text = emit_each_expression(id_text, effect, helper_bindings);
1015
1181
  helpers = [...normalized.helpers, effect.helper];
1016
1182
  }
1017
1183
  return {
@@ -1022,31 +1188,32 @@ function emit_replacement(candidate, kind, effect_context) {
1022
1188
  relocation
1023
1189
  };
1024
1190
  }
1025
- function make_event_handler(candidate) {
1191
+ function make_event_handler(candidate, helper_bindings) {
1026
1192
  const expr_text = candidate.expr_text;
1027
1193
  if (is_callback_function_expression(expr_text)) throw new YieldStarInEventCallbackError(candidate.filename, expr_text);
1028
1194
  if (analyze_event_body_yield_star(expr_text).has_nested_invalid_yield_star) throw new AsyncEffectInEventCallbackError(candidate.filename, expr_text);
1029
1195
  return {
1030
1196
  expr_text,
1031
- text: `(event) => { ${HELPERS.run}(function* () { ${expr_text}; }); }`
1197
+ text: `(event) => { ${helper_bindings.run}(function* () { ${expr_text}; }); }`
1032
1198
  };
1033
1199
  }
1034
- function emit_promise_expression(id_text, effect) {
1035
- return `${HELPERS.promise}(${id_text}, ${effect.deps_text}, () => ${effect.call})`;
1200
+ function emit_promise_expression(id_text, effect, helper_bindings) {
1201
+ return `${helper_bindings.promise}(${id_text}, ${effect.deps_text}, () => ${effect.call})`;
1036
1202
  }
1037
- function emit_render_expression(id_text, effect) {
1038
- return `(await ${emit_promise_expression(id_text, effect)})()`;
1203
+ function emit_render_expression(id_text, effect, candidate, helper_bindings) {
1204
+ const expression = emit_promise_expression(id_text, effect, helper_bindings);
1205
+ if (/^\s*yield\s*\*/.test(candidate.expr_text)) return `(await ${expression})()`;
1206
+ return `await ${expression}`;
1039
1207
  }
1040
- function emit_each_expression(id_text, effect) {
1041
- return `await ${emit_promise_expression(id_text, effect)}`;
1208
+ function emit_each_expression(id_text, effect, helper_bindings) {
1209
+ return `await ${emit_promise_expression(id_text, effect, helper_bindings)}`;
1042
1210
  }
1043
1211
  function make_effect_helper(candidate, helper_name) {
1044
1212
  const deps = collect_free_identifiers(candidate.expr_text);
1045
- const params_text = deps.join(", ");
1046
1213
  const args_text = deps.join(", ");
1047
1214
  const deps_text = deps.length === 0 ? "[]" : `[${args_text}]`;
1048
- const call = `${helper_name}(${args_text})`;
1049
- const text = `function* ${helper_name}(${params_text}) { return (${candidate.expr_text}); }`;
1215
+ const call = `${helper_name}()`;
1216
+ const text = `function* ${helper_name}() { return (${candidate.expr_text}); }`;
1050
1217
  const generated_start = text.indexOf(candidate.expr_text);
1051
1218
  return {
1052
1219
  call,
@@ -1063,10 +1230,10 @@ function make_effect_helper(candidate, helper_name) {
1063
1230
  };
1064
1231
  }
1065
1232
  function make_cache_id(candidate) {
1066
- return `${candidate.filename}:${candidate.start}:${candidate.end}`;
1233
+ return `${candidate.filename.replace(/[?#].*$/, "")}:${candidate.start}:${candidate.end}`;
1067
1234
  }
1068
- function make_helper_name(candidate) {
1069
- return `__SER___markup_effect_${candidate.start}_${candidate.end}`;
1235
+ function make_helper_name(candidate, name_allocator) {
1236
+ return name_allocator.reserve(`__SER___markup_effect_${candidate.start}_${candidate.end}`);
1070
1237
  }
1071
1238
  function make_relocation(candidate, replacement_text, inner) {
1072
1239
  const generated_start = replacement_text.indexOf(inner.generatedText);
@@ -1174,7 +1341,7 @@ function sanitize_markup(content, filename) {
1174
1341
  const open = content.indexOf("{", cursor);
1175
1342
  if (open === -1) break;
1176
1343
  /** Skip braces inside <script> and <style> blocks. */
1177
- if (is_inside_excluded_block(content, open)) {
1344
+ if (is_inside_excluded_block(content, open) || is_inside_html_comment(content, open)) {
1178
1345
  cursor = open + 1;
1179
1346
  continue;
1180
1347
  }
@@ -1239,6 +1406,26 @@ function sanitize_markup(content, filename) {
1239
1406
  cursor = close + 1;
1240
1407
  continue;
1241
1408
  }
1409
+ if (key === "render" && !/^\s*yield\s*\*/.test(expr_text)) {
1410
+ const render_arg_yields = collect_expression_yield_expressions(content, expr_start, expr_text);
1411
+ if (render_arg_yields.length > 0) {
1412
+ for (const render_arg_yield of render_arg_yields) {
1413
+ const placeholder = `__SER___markup_placeholder_${helper_index}`;
1414
+ helper_index += 1;
1415
+ candidates.push({
1416
+ placeholder,
1417
+ start: render_arg_yield.start,
1418
+ end: render_arg_yield.end,
1419
+ expr_text: render_arg_yield.expr_text,
1420
+ filename,
1421
+ key: "render_argument"
1422
+ });
1423
+ magic.overwrite(render_arg_yield.start, render_arg_yield.end, placeholder);
1424
+ }
1425
+ cursor = close + 1;
1426
+ continue;
1427
+ }
1428
+ }
1242
1429
  /** Create a placeholder and replace the expression (preserving tag prefixes). */
1243
1430
  const placeholder = `__SER___markup_placeholder_${helper_index}`;
1244
1431
  helper_index += 1;
@@ -1258,11 +1445,36 @@ function sanitize_markup(content, filename) {
1258
1445
  candidates
1259
1446
  };
1260
1447
  }
1448
+ function collect_expression_yield_expressions(content, expr_start, expr_text) {
1449
+ const source_file = ts.createSourceFile("markup-expression.ts", `const __SER___expr = ${expr_text};`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
1450
+ const stmt = source_file.statements[0];
1451
+ if (!stmt || !ts.isVariableStatement(stmt)) return [];
1452
+ const initializer = stmt.declarationList.declarations[0]?.initializer;
1453
+ if (!initializer || !contains_top_level_yield_star(initializer)) return [];
1454
+ const prefix_length = source_file.text.indexOf(expr_text);
1455
+ const expressions = [];
1456
+ collect_yield_star_nodes(initializer, (yield_node) => {
1457
+ const start = expr_start + yield_node.getStart(source_file) - prefix_length;
1458
+ const end = expr_start + yield_node.end - prefix_length;
1459
+ const yielded_text = content.slice(start, end).trim();
1460
+ expressions.push({
1461
+ start,
1462
+ end,
1463
+ expr_text: yielded_text
1464
+ });
1465
+ });
1466
+ return expressions;
1467
+ }
1261
1468
  function is_inside_excluded_block(content, pos) {
1262
1469
  const script = find_tag_end(content, "script", pos);
1263
1470
  const style = find_tag_end(content, "style", pos);
1264
1471
  return script !== void 0 && pos < script.end && pos > script.start || style !== void 0 && pos < style.end && pos > style.start;
1265
1472
  }
1473
+ function is_inside_html_comment(content, pos) {
1474
+ const open = content.lastIndexOf("<!--", pos);
1475
+ const close = content.lastIndexOf("-->", pos);
1476
+ return open !== -1 && open > close;
1477
+ }
1266
1478
  function find_tag_end(content, tag, after_pos) {
1267
1479
  const pattern = new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)<\\/${tag}\\s*>`, "gi");
1268
1480
  for (const match of content.matchAll(pattern)) {
@@ -1388,6 +1600,7 @@ function is_event_callback_expression(inner) {
1388
1600
  function analyze_event_yield(inner) {
1389
1601
  const event = strip_arrow_function(inner);
1390
1602
  const analysis = analyze_event_body_yield_star(event.body);
1603
+ if (new RegExp(`${HELPERS.run}(?:_\\d+)?\\(function\\*`).test(event.body)) return { has_top_level_yield_star: false };
1391
1604
  return { has_top_level_yield_star: analysis.has_top_level_yield_star || analysis.has_nested_invalid_yield_star || /\byield\s*\*/.test(event.body) };
1392
1605
  }
1393
1606
  function contains_yield_star_in_text(text) {
@@ -1429,19 +1642,25 @@ function transform_markup_effect(content, filename) {
1429
1642
  /** Find all brace expressions containing yield* and replace with placeholders. */
1430
1643
  const work = sanitize_markup(content, filename);
1431
1644
  const effect_context = collect_effect_callback_bindings(content);
1645
+ const helper_context = make_markup_helper_bindings(content);
1432
1646
  if (work.candidates.length === 0) return {
1433
1647
  code: content,
1434
1648
  has_yield: false
1435
1649
  };
1436
- const replacements = emit_replacements(classify_candidates(parse(blank_script_blocks(work.code), {
1650
+ /** Match placeholders to their AST context and build replacements. */
1651
+ const classified = classify_candidates(parse(blank_script_blocks(work.code), {
1437
1652
  filename,
1438
1653
  modern: true
1439
- }), work.candidates), effect_context);
1654
+ }), work.candidates);
1655
+ const matched = new Set(classified.map(({ candidate }) => candidate.placeholder));
1656
+ const unmatched = work.candidates.find((candidate) => !matched.has(candidate.placeholder));
1657
+ if (unmatched) throw new UnsupportedMarkupEffectPositionError(filename, unmatched.expr_text);
1658
+ const replacements = emit_replacements(classified, effect_context, helper_context.bindings, helper_context.name_allocator);
1440
1659
  const helpers = replacements.flatMap((replacement) => replacement.helpers ?? []);
1441
1660
  const magic = new MagicString(content);
1442
1661
  replacements.sort((a, b) => b.start - a.start);
1443
1662
  for (const r of replacements) magic.overwrite(r.start, r.end, r.text);
1444
- const relocations = create_relocations(replacements, inject_helpers(magic, content, helpers));
1663
+ const relocations = create_relocations(replacements, inject_helpers(magic, content, helpers, helper_context.bindings));
1445
1664
  return {
1446
1665
  code: magic.toString(),
1447
1666
  has_yield: true,
@@ -1450,6 +1669,6 @@ function transform_markup_effect(content, filename) {
1450
1669
  };
1451
1670
  }
1452
1671
  //#endregion
1453
- export { find_yield_star_node as a, AsyncEffectInSyncRuneError as c, extract_binding_names as i, AwaitInEffectWorkError as l, collect_yield_star_nodes as n, is_yield_star_expression as o, contains_top_level_await as r, collect_free_identifiers as s, transform_markup_effect as t };
1672
+ export { find_yield_star_node as a, AsyncEffectInSyncRuneError as c, collect_top_level_binding_names as d, has_local_import_binding as f, extract_binding_names as i, AwaitInEffectWorkError as l, collect_yield_star_nodes as n, is_yield_star_expression as o, make_imports as p, contains_top_level_await as r, collect_free_identifiers as s, transform_markup_effect as t, PreprocessError as u };
1454
1673
 
1455
- //# sourceMappingURL=transform-s9gKPYu5.js.map
1674
+ //# sourceMappingURL=transform-Cx5bjmP6.js.map