svelte-effect-runtime 1.6.2 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dist/chunks/client-BeOE81fW.js +577 -0
- package/.dist/chunks/client-BeOE81fW.js.map +1 -0
- package/.dist/chunks/descriptors-CU2g4U_i.js +36 -0
- package/.dist/chunks/descriptors-CU2g4U_i.js.map +1 -0
- package/.dist/chunks/dispatcher-BSg2-ttN.js +339 -0
- package/.dist/chunks/dispatcher-BSg2-ttN.js.map +1 -0
- package/.dist/chunks/preprocess-BZyUYWY7.js +430 -0
- package/.dist/chunks/preprocess-BZyUYWY7.js.map +1 -0
- package/.dist/chunks/transform-BtlxgJrg.js +836 -0
- package/.dist/chunks/transform-BtlxgJrg.js.map +1 -0
- package/.dist/detect.d.ts +34 -0
- package/.dist/detect.js +56 -0
- package/.dist/detect.js.map +1 -0
- package/.dist/dispatcher/deps.d.ts +14 -0
- package/.dist/dispatcher/fibers.d.ts +52 -0
- package/.dist/dispatcher/index.d.ts +138 -0
- package/.dist/dispatcher/types.d.ts +63 -0
- package/.dist/dispatcher.d.ts +2 -0
- package/.dist/dispatcher.js +2 -0
- package/.dist/error.d.ts +77 -0
- package/.dist/generators.d.ts +16 -0
- package/.dist/internal/descriptors.d.ts +17 -0
- package/.dist/internal/generators.d.ts +6 -0
- package/.dist/internal/generators.js +5 -0
- package/.dist/internal/remote-client.d.ts +6 -0
- package/.dist/internal/remote-client.js +2 -0
- package/.dist/internal/remote-server.d.ts +6 -0
- package/.dist/internal/remote-server.js +2 -0
- package/.dist/markup/promise.d.ts +15 -0
- package/.dist/markup/promise.js +26 -0
- package/.dist/markup/promise.js.map +1 -0
- package/.dist/markup/run.d.ts +12 -0
- package/.dist/markup/run.js +21 -0
- package/.dist/markup/run.js.map +1 -0
- package/.dist/markup/transform/apply.d.ts +6 -0
- package/.dist/markup/transform/classify.d.ts +15 -0
- package/.dist/markup/transform/constants.d.ts +5 -0
- package/.dist/markup/transform/emit.d.ts +12 -0
- package/.dist/markup/transform/expressions.d.ts +39 -0
- package/.dist/markup/transform/index.d.ts +19 -0
- package/.dist/markup/transform/scan.d.ts +8 -0
- package/.dist/markup/transform/types.d.ts +64 -0
- package/.dist/markup/transform.d.ts +1 -0
- package/.dist/markup/transform.js +2 -0
- package/.dist/markup/value.d.ts +20 -0
- package/.dist/markup/value.js +32 -0
- package/.dist/markup/value.js.map +1 -0
- package/.dist/mod.d.ts +186 -0
- package/.dist/mod.js +190 -0
- package/.dist/mod.js.map +1 -0
- package/.dist/preprocess/ast.d.ts +51 -0
- package/.dist/preprocess/imports.d.ts +22 -0
- package/.dist/preprocess/index.d.ts +21 -0
- package/.dist/preprocess/lower.d.ts +12 -0
- package/.dist/preprocess/runtime-block.d.ts +8 -0
- package/.dist/preprocess/source.d.ts +29 -0
- package/.dist/preprocess/types.d.ts +70 -0
- package/.dist/preprocess.d.ts +2 -0
- package/.dist/preprocess.js +3 -0
- package/.dist/remote/client/command.d.ts +17 -0
- package/.dist/remote/client/effect.d.ts +15 -0
- package/.dist/remote/client/failures.d.ts +57 -0
- package/.dist/remote/client/form-data.d.ts +8 -0
- package/.dist/remote/client/form-enhance.d.ts +9 -0
- package/.dist/remote/client/form-transport.d.ts +20 -0
- package/.dist/remote/client/form.d.ts +21 -0
- package/.dist/remote/client/index.d.ts +4 -0
- package/.dist/remote/client/query-result.d.ts +14 -0
- package/.dist/remote/client/query.d.ts +57 -0
- package/.dist/remote/client/responses.d.ts +28 -0
- package/.dist/remote/client/types.d.ts +83 -0
- package/.dist/remote/client/utils.d.ts +11 -0
- package/.dist/remote/client.d.ts +2 -0
- package/.dist/remote/client.js +2 -0
- package/.dist/remote/server.d.ts +66 -0
- package/.dist/remote/server.js +104 -0
- package/.dist/remote/server.js.map +1 -0
- package/.dist/remote/shared.d.ts +202 -0
- package/.dist/remote/shared.js +161 -0
- package/.dist/remote/shared.js.map +1 -0
- package/.dist/runtime/preprocess.d.ts +38 -0
- package/.dist/runtime/preprocess.js +54 -0
- package/.dist/runtime/preprocess.js.map +1 -0
- package/.dist/server/effects.d.ts +49 -0
- package/.dist/server/factories.d.ts +97 -0
- package/.dist/server/index.d.ts +3 -0
- package/.dist/server/invalid.d.ts +9 -0
- package/.dist/server/runtime.d.ts +66 -0
- package/.dist/server/schema.d.ts +42 -0
- package/.dist/server/types.d.ts +271 -0
- package/.dist/server/wrappers.d.ts +45 -0
- package/.dist/server.d.ts +2 -0
- package/.dist/server.js +527 -0
- package/.dist/server.js.map +1 -0
- package/.dist/vite.d.ts +42 -0
- package/.dist/vite.js +118 -0
- package/.dist/vite.js.map +1 -0
- package/README.md +2 -0
- package/package.json +66 -101
- package/dist/chunks/client-vV6UAwoP.js +0 -199
- package/dist/chunks/client-vV6UAwoP.js.map +0 -1
- package/dist/chunks/effect-Crj3S8Cg.js +0 -51
- package/dist/chunks/effect-Crj3S8Cg.js.map +0 -1
- package/dist/chunks/effect-compat-BhW0YPIs.js +0 -103
- package/dist/chunks/effect-compat-BhW0YPIs.js.map +0 -1
- package/dist/chunks/magic-string.es-BnGH-5YR.js +0 -1013
- package/dist/chunks/magic-string.es-BnGH-5YR.js.map +0 -1
- package/dist/chunks/markup-CkPUZIJt.js +0 -13390
- package/dist/chunks/markup-CkPUZIJt.js.map +0 -1
- package/dist/chunks/preprocess-BFePqOC6.js +0 -49
- package/dist/chunks/preprocess-BFePqOC6.js.map +0 -1
- package/dist/chunks/server-Dea1CJuY.js +0 -704
- package/dist/chunks/server-Dea1CJuY.js.map +0 -1
- package/dist/chunks/v3-D0c0MKPf.js +0 -1
- package/dist/chunks/v4-MMhHmzIG.js +0 -1
- package/dist/chunks/vite-BlUTKv2u.js +0 -152
- package/dist/chunks/vite-BlUTKv2u.js.map +0 -1
- package/dist/client.d.ts +0 -1
- package/dist/client.js +0 -2
- package/dist/effect.d.ts +0 -1
- package/dist/effect.js +0 -2
- package/dist/internal/app-server-shim.d.ts +0 -8
- package/dist/internal/effect-compat.d.ts +0 -21
- package/dist/internal/markup.d.ts +0 -11
- package/dist/internal/markup.js +0 -2
- package/dist/internal/remote-client.d.ts +0 -36
- package/dist/internal/remote-client.js +0 -527
- package/dist/internal/remote-client.js.map +0 -1
- package/dist/internal/remote-shared.d.ts +0 -186
- package/dist/internal/remote-shared.js +0 -122
- package/dist/internal/remote-shared.js.map +0 -1
- package/dist/internal/transform.d.ts +0 -18
- package/dist/internal/transform.js +0 -418
- package/dist/internal/transform.js.map +0 -1
- package/dist/language-server.d.ts +0 -9
- package/dist/language-server.js +0 -4
- package/dist/mod.d.ts +0 -22
- package/dist/mod.js +0 -3
- package/dist/preprocess.d.ts +0 -29
- package/dist/preprocess.js +0 -20
- package/dist/preprocess.js.map +0 -1
- package/dist/root-node.d.ts +0 -1
- package/dist/root-node.js +0 -63
- package/dist/root-node.js.map +0 -1
- package/dist/server.d.ts +0 -81
- package/dist/server.js +0 -85
- package/dist/server.js.map +0 -1
- package/dist/v3/client.d.ts +0 -117
- package/dist/v3/effect.d.ts +0 -17
- package/dist/v3/mod.d.ts +0 -27
- package/dist/v3/preprocess.d.ts +0 -28
- package/dist/v3/root-node.d.ts +0 -59
- package/dist/v3/server.d.ts +0 -297
- package/dist/v3/vite.d.ts +0 -46
- package/dist/v4/client.d.ts +0 -2
- package/dist/v4/effect.d.ts +0 -24
- package/dist/v4/effect.js +0 -19
- package/dist/v4/effect.js.map +0 -1
- package/dist/v4/mod.d.ts +0 -27
- package/dist/v4/mod.js +0 -3
- package/dist/v4/preprocess.d.ts +0 -34
- package/dist/v4/preprocess.js +0 -32
- package/dist/v4/preprocess.js.map +0 -1
- package/dist/v4/root-node.d.ts +0 -59
- package/dist/v4/root-node.js +0 -4
- package/dist/v4/server.d.ts +0 -340
- package/dist/v4/server.js +0 -71
- package/dist/v4/server.js.map +0 -1
- package/dist/v4/vite.d.ts +0 -34
- package/dist/v4/vite.js +0 -31
- package/dist/v4/vite.js.map +0 -1
- package/dist/vite.d.ts +0 -31
- package/dist/vite.js +0 -24
- package/dist/vite.js.map +0 -1
|
@@ -0,0 +1,836 @@
|
|
|
1
|
+
import { contains_top_level_yield_star } from "../detect.js";
|
|
2
|
+
import { parse } from "svelte/compiler";
|
|
3
|
+
import MagicString from "magic-string";
|
|
4
|
+
import ts from "typescript";
|
|
5
|
+
//#region src/markup/transform/constants.ts
|
|
6
|
+
const HELPERS = {
|
|
7
|
+
value: "__ser_markup_value",
|
|
8
|
+
promise: "__ser_markup_promise",
|
|
9
|
+
run: "__ser_markup_run"
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/markup/transform/apply.ts
|
|
13
|
+
function create_source_map(magic, filename) {
|
|
14
|
+
return magic.generateMap({
|
|
15
|
+
hires: true,
|
|
16
|
+
includeContent: true,
|
|
17
|
+
source: filename
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function blank_script_blocks(content) {
|
|
21
|
+
return content.replace(/<script\b[^>]*>[\s\S]*?<\/script\s*>/gi, (match) => {
|
|
22
|
+
return match.split("\n").map((l) => " ".repeat(l.length)).join("\n");
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function inject_helpers(magic, content, helpers = []) {
|
|
26
|
+
if (content.includes(HELPERS.value)) return;
|
|
27
|
+
const helper_segments = [
|
|
28
|
+
`import { value as ${HELPERS.value} } from "svelte-effect-runtime/internal/generators";`,
|
|
29
|
+
`import { promise as ${HELPERS.promise} } from "svelte-effect-runtime/internal/generators";`,
|
|
30
|
+
`import { run as ${HELPERS.run} } from "svelte-effect-runtime/internal/generators";`,
|
|
31
|
+
...helpers
|
|
32
|
+
].map((helper) => typeof helper === "string" ? { text: helper } : helper);
|
|
33
|
+
const helper_block = helper_segments.map((segment) => segment.text).join("\n");
|
|
34
|
+
const script_tag = find_instance_script_tag(content);
|
|
35
|
+
if (script_tag) {
|
|
36
|
+
const text = `\n${helper_block}\n`;
|
|
37
|
+
magic.appendLeft(script_tag.end, text);
|
|
38
|
+
return {
|
|
39
|
+
start: script_tag.end,
|
|
40
|
+
text,
|
|
41
|
+
relocations: make_insertion_relocations(helper_segments, "\n")
|
|
42
|
+
};
|
|
43
|
+
} else {
|
|
44
|
+
const text = `<script>\n${helper_block}\n<\/script>\n\n`;
|
|
45
|
+
magic.prepend(text);
|
|
46
|
+
return {
|
|
47
|
+
start: 0,
|
|
48
|
+
text,
|
|
49
|
+
relocations: make_insertion_relocations(helper_segments, "<script>\n")
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function create_relocations(replacements, helper_insertion) {
|
|
54
|
+
const edits = [helper_insertion && {
|
|
55
|
+
start: helper_insertion.start,
|
|
56
|
+
removedLength: 0,
|
|
57
|
+
insertedLength: helper_insertion.text.length
|
|
58
|
+
}, ...replacements.map((replacement) => ({
|
|
59
|
+
start: replacement.start,
|
|
60
|
+
removedLength: replacement.end - replacement.start,
|
|
61
|
+
insertedLength: replacement.text.length
|
|
62
|
+
}))].filter(Boolean);
|
|
63
|
+
const replacement_relocations = replacements.flatMap((replacement) => {
|
|
64
|
+
if (!replacement.relocation) return [];
|
|
65
|
+
const delta_before = edits.filter((edit) => edit.start < replacement.start).reduce((total, edit) => total + edit.insertedLength - edit.removedLength, 0);
|
|
66
|
+
const generated_start = replacement.start + delta_before;
|
|
67
|
+
return [{
|
|
68
|
+
originalStart: replacement.relocation.originalStart,
|
|
69
|
+
originalEnd: replacement.relocation.originalEnd,
|
|
70
|
+
generatedStart: generated_start + replacement.relocation.generatedStartInReplacement,
|
|
71
|
+
generatedEnd: generated_start + replacement.relocation.generatedEndInReplacement
|
|
72
|
+
}];
|
|
73
|
+
});
|
|
74
|
+
const helper_relocations = helper_insertion?.relocations?.map((relocation) => ({
|
|
75
|
+
originalStart: relocation.originalStart,
|
|
76
|
+
originalEnd: relocation.originalEnd,
|
|
77
|
+
generatedStart: helper_insertion.start + relocation.generatedStartInReplacement,
|
|
78
|
+
generatedEnd: helper_insertion.start + relocation.generatedEndInReplacement
|
|
79
|
+
})) ?? [];
|
|
80
|
+
return [...replacement_relocations, ...helper_relocations];
|
|
81
|
+
}
|
|
82
|
+
function make_insertion_relocations(segments, prefix) {
|
|
83
|
+
const relocations = [];
|
|
84
|
+
let offset = prefix.length;
|
|
85
|
+
for (const segment of segments) {
|
|
86
|
+
if (segment.relocation) relocations.push({
|
|
87
|
+
originalStart: segment.relocation.originalStart,
|
|
88
|
+
originalEnd: segment.relocation.originalEnd,
|
|
89
|
+
generatedStartInReplacement: offset + segment.relocation.generatedStartInReplacement,
|
|
90
|
+
generatedEndInReplacement: offset + segment.relocation.generatedEndInReplacement
|
|
91
|
+
});
|
|
92
|
+
offset += segment.text.length + 1;
|
|
93
|
+
}
|
|
94
|
+
return relocations;
|
|
95
|
+
}
|
|
96
|
+
function find_instance_script_tag(content) {
|
|
97
|
+
for (const match of content.matchAll(/<script\b([^>]*)>([\s\S]*?)<\/script\s*>/gi)) {
|
|
98
|
+
if (match.index === void 0) continue;
|
|
99
|
+
const attrs = match[1] ?? "";
|
|
100
|
+
if (/\bcontext\s*=\s*["']module["']/.test(attrs) || /\bmodule\b/.test(attrs)) continue;
|
|
101
|
+
const open_end = match[0].indexOf(">") + 1;
|
|
102
|
+
return {
|
|
103
|
+
start: match.index + open_end,
|
|
104
|
+
end: match.index + match[0].length - 9
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/markup/transform/classify.ts
|
|
110
|
+
/**
|
|
111
|
+
* Matches sanitized placeholders back to their Svelte AST context.
|
|
112
|
+
*
|
|
113
|
+
* @since 2.0.0
|
|
114
|
+
* @param ast - Parsed Svelte AST for the sanitized component markup.
|
|
115
|
+
* @param candidates - Placeholder candidates produced by the scanner.
|
|
116
|
+
* @returns Candidates paired with the markup context that determines how they
|
|
117
|
+
* should be emitted.
|
|
118
|
+
*/
|
|
119
|
+
function classify_candidates(ast, candidates) {
|
|
120
|
+
const by_placeholder = new Map(candidates.map((candidate) => [candidate.placeholder, candidate]));
|
|
121
|
+
const classified = [];
|
|
122
|
+
const matched = /* @__PURE__ */ new Set();
|
|
123
|
+
walk_ast(ast.fragment, by_placeholder, matched, classified);
|
|
124
|
+
return classified;
|
|
125
|
+
}
|
|
126
|
+
function walk_ast(fragment, candidates, matched, classified) {
|
|
127
|
+
for (const node of fragment.nodes) visit_ast_node(node, candidates, matched, classified);
|
|
128
|
+
}
|
|
129
|
+
function visit_ast_node(node, candidates, matched, classified) {
|
|
130
|
+
switch (node.type) {
|
|
131
|
+
case "ExpressionTag":
|
|
132
|
+
classify_expression(node.expression, "plain", candidates, matched, classified);
|
|
133
|
+
return;
|
|
134
|
+
case "IfBlock":
|
|
135
|
+
classify_expression(node.test, "plain", candidates, matched, classified);
|
|
136
|
+
walk_ast(node.consequent, candidates, matched, classified);
|
|
137
|
+
if (node.alternate) walk_ast(node.alternate, candidates, matched, classified);
|
|
138
|
+
return;
|
|
139
|
+
case "EachBlock":
|
|
140
|
+
classify_expression(node.expression, "each", candidates, matched, classified);
|
|
141
|
+
walk_ast(node.body, candidates, matched, classified);
|
|
142
|
+
if (node.fallback) walk_ast(node.fallback, candidates, matched, classified);
|
|
143
|
+
return;
|
|
144
|
+
case "AwaitBlock":
|
|
145
|
+
classify_expression(node.expression, "await", candidates, matched, classified);
|
|
146
|
+
if (node.pending) walk_ast(node.pending, candidates, matched, classified);
|
|
147
|
+
if (node.then) walk_ast(node.then, candidates, matched, classified);
|
|
148
|
+
if (node.catch) walk_ast(node.catch, candidates, matched, classified);
|
|
149
|
+
return;
|
|
150
|
+
case "RenderTag":
|
|
151
|
+
classify_expression(node.expression, "render", candidates, matched, classified);
|
|
152
|
+
return;
|
|
153
|
+
case "HtmlTag":
|
|
154
|
+
classify_expression(node.expression, "plain", candidates, matched, classified);
|
|
155
|
+
return;
|
|
156
|
+
case "DebugTag":
|
|
157
|
+
classify_debug_tag(node, candidates, matched, classified);
|
|
158
|
+
return;
|
|
159
|
+
case "ConstTag":
|
|
160
|
+
case "DeclarationTag":
|
|
161
|
+
classify_declaration_tag(node, candidates, matched, classified);
|
|
162
|
+
return;
|
|
163
|
+
case "KeyBlock":
|
|
164
|
+
classify_expression(node.expression, "plain", candidates, matched, classified);
|
|
165
|
+
walk_ast(node.fragment, candidates, matched, classified);
|
|
166
|
+
return;
|
|
167
|
+
case "RegularElement":
|
|
168
|
+
case "Component":
|
|
169
|
+
case "TitleElement":
|
|
170
|
+
case "SlotElement":
|
|
171
|
+
case "SvelteBody":
|
|
172
|
+
case "SvelteBoundary":
|
|
173
|
+
case "SvelteComponent":
|
|
174
|
+
case "SvelteDocument":
|
|
175
|
+
case "SvelteElement":
|
|
176
|
+
case "SvelteFragment":
|
|
177
|
+
case "SvelteHead":
|
|
178
|
+
case "SvelteSelf":
|
|
179
|
+
case "SvelteWindow":
|
|
180
|
+
visit_element_attributes(node, candidates, matched, classified);
|
|
181
|
+
walk_ast(node.fragment, candidates, matched, classified);
|
|
182
|
+
return;
|
|
183
|
+
default: return;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function classify_debug_tag(node, candidates, matched, classified) {
|
|
187
|
+
const idents = node.identifiers;
|
|
188
|
+
if (!idents || idents.length === 0) return;
|
|
189
|
+
for (const ident of idents) classify_expression(ident, "plain", candidates, matched, classified);
|
|
190
|
+
}
|
|
191
|
+
function classify_declaration_tag(node, candidates, matched, classified) {
|
|
192
|
+
for (const decl of node.declaration.declarations) {
|
|
193
|
+
if (!decl.init) continue;
|
|
194
|
+
classify_expression(decl.init, "plain", candidates, matched, classified);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function visit_element_attributes(node, candidates, matched, classified) {
|
|
198
|
+
for (const attr of node.attributes) {
|
|
199
|
+
if (attr.type === "Attribute" && attr.name && (attr.name.startsWith("on:") || /^on[a-z]/.test(attr.name))) {
|
|
200
|
+
visit_attribute_value(attr.value, "event", candidates, matched, classified);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (attr.type === "OnDirective" && attr.expression) {
|
|
204
|
+
classify_expression(attr.expression, "event", candidates, matched, classified);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function visit_attribute_value(value, kind, candidates, matched, classified) {
|
|
210
|
+
if (value === true) return;
|
|
211
|
+
if (Array.isArray(value)) {
|
|
212
|
+
for (const part of value) if (part.type === "ExpressionTag") classify_expression(part.expression, kind, candidates, matched, classified);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
classify_expression(value.expression, kind, candidates, matched, classified);
|
|
216
|
+
}
|
|
217
|
+
function classify_expression(expression, kind, candidates, matched, classified) {
|
|
218
|
+
if (!expression) return;
|
|
219
|
+
const candidate = find_candidate(expression, candidates);
|
|
220
|
+
if (!candidate || matched.has(candidate.placeholder)) return;
|
|
221
|
+
matched.add(candidate.placeholder);
|
|
222
|
+
classified.push({
|
|
223
|
+
candidate,
|
|
224
|
+
kind
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
function find_candidate(expression, candidates) {
|
|
228
|
+
if (expression.type === "Identifier" && expression.name) return candidates.get(expression.name);
|
|
229
|
+
if (expression.type === "CallExpression" && expression.callee?.type === "Identifier" && expression.callee.name) return candidates.get(expression.callee.name);
|
|
230
|
+
}
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region src/markup/transform/expressions.ts
|
|
233
|
+
/**
|
|
234
|
+
* Strips an event handler arrow function down to its executable body.
|
|
235
|
+
*
|
|
236
|
+
* @since 2.0.0
|
|
237
|
+
* @param expr - Event handler expression text from the original markup.
|
|
238
|
+
* @returns Handler parameters, body text, and body offsets inside `expr`.
|
|
239
|
+
*/
|
|
240
|
+
function strip_arrow_function(expr) {
|
|
241
|
+
const arrow_idx = expr.indexOf("=>");
|
|
242
|
+
if (arrow_idx === -1) return {
|
|
243
|
+
params: "()",
|
|
244
|
+
body: expr,
|
|
245
|
+
body_start: 0,
|
|
246
|
+
body_end: expr.length
|
|
247
|
+
};
|
|
248
|
+
const params = expr.slice(0, arrow_idx).trim();
|
|
249
|
+
const raw_body = expr.slice(arrow_idx + 2);
|
|
250
|
+
const leading_ws = raw_body.length - raw_body.trimStart().length;
|
|
251
|
+
let body_start = arrow_idx + 2 + leading_ws;
|
|
252
|
+
let body_end = expr.length - (raw_body.length - raw_body.trimEnd().length);
|
|
253
|
+
let body = expr.slice(body_start, body_end);
|
|
254
|
+
if (body.startsWith("{") && body.endsWith("}")) {
|
|
255
|
+
body_start += 1;
|
|
256
|
+
body_end -= 1;
|
|
257
|
+
body = body.slice(1, -1);
|
|
258
|
+
}
|
|
259
|
+
const body_leading_ws = body.length - body.trimStart().length;
|
|
260
|
+
const body_trailing_ws = body.length - body.trimEnd().length;
|
|
261
|
+
body_start += body_leading_ws;
|
|
262
|
+
body_end -= body_trailing_ws;
|
|
263
|
+
body = body.trim();
|
|
264
|
+
if (body.endsWith(";")) {
|
|
265
|
+
body = body.slice(0, -1);
|
|
266
|
+
body_end -= 1;
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
params,
|
|
270
|
+
body,
|
|
271
|
+
body_start,
|
|
272
|
+
body_end
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Classifies `yield*` placement inside an event handler body.
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```ts
|
|
280
|
+
* analyze_event_body_yield_star("yield* save()");
|
|
281
|
+
* ```
|
|
282
|
+
*
|
|
283
|
+
* @since 2.0.0
|
|
284
|
+
* @param body - Event handler body text after the outer arrow has been
|
|
285
|
+
* stripped.
|
|
286
|
+
* @returns Whether the body has top-level yield* expressions and whether any
|
|
287
|
+
* yield* appears inside a nested non-generator callback.
|
|
288
|
+
*/
|
|
289
|
+
function analyze_event_body_yield_star(body) {
|
|
290
|
+
const wrapped = `function* __ser_event() { ${body}; }`;
|
|
291
|
+
const stmt = ts.createSourceFile("event.ts", wrapped, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS).statements[0];
|
|
292
|
+
if (!ts.isFunctionDeclaration(stmt) || !stmt.body) return {
|
|
293
|
+
has_top_level_yield_star: false,
|
|
294
|
+
has_nested_invalid_yield_star: /\byield\s*\*/.test(body)
|
|
295
|
+
};
|
|
296
|
+
const result = {
|
|
297
|
+
has_top_level_yield_star: false,
|
|
298
|
+
has_nested_invalid_yield_star: false
|
|
299
|
+
};
|
|
300
|
+
visit_event_body(stmt.body, "top_level", result);
|
|
301
|
+
return result;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Collects free identifiers that must be captured as reactive dependencies.
|
|
305
|
+
*
|
|
306
|
+
* @since 2.0.0
|
|
307
|
+
* @param expr_text - Markup expression text to inspect.
|
|
308
|
+
* @returns Identifier names referenced by the expression.
|
|
309
|
+
*/
|
|
310
|
+
function collect_free_identifiers(expr_text) {
|
|
311
|
+
const wrapped = `function* __w() { return (${expr_text}); }`;
|
|
312
|
+
let sf;
|
|
313
|
+
try {
|
|
314
|
+
sf = ts.createSourceFile("expr.ts", wrapped, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
315
|
+
} catch {
|
|
316
|
+
return [];
|
|
317
|
+
}
|
|
318
|
+
const fn = sf.statements[0];
|
|
319
|
+
if (!ts.isFunctionDeclaration(fn) || !fn.body) return [];
|
|
320
|
+
const ids = [];
|
|
321
|
+
const seen = /* @__PURE__ */ new Set();
|
|
322
|
+
visit_ids(fn.body, seen, ids);
|
|
323
|
+
return ids;
|
|
324
|
+
}
|
|
325
|
+
function visit_ids(node, seen, ids) {
|
|
326
|
+
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node)) return;
|
|
327
|
+
if (ts.isIdentifier(node)) {
|
|
328
|
+
if (node.text === "yield" || node.text === "undefined" || node.text === "null" || node.text === "true" || node.text === "false" || node.text === "this") return;
|
|
329
|
+
if (is_property_access_name(node)) return;
|
|
330
|
+
if (!seen.has(node.text)) {
|
|
331
|
+
seen.add(node.text);
|
|
332
|
+
ids.push(node.text);
|
|
333
|
+
}
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
node.forEachChild((child) => visit_ids(child, seen, ids));
|
|
337
|
+
}
|
|
338
|
+
function visit_event_body(node, context, result) {
|
|
339
|
+
if (is_yield_star_expression(node)) {
|
|
340
|
+
if (context === "top_level") result.has_top_level_yield_star = true;
|
|
341
|
+
else if (context === "nested_invalid") result.has_nested_invalid_yield_star = true;
|
|
342
|
+
node.forEachChild((child) => visit_event_body(child, context, result));
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (is_nested_function_boundary(node)) {
|
|
346
|
+
const next_context = is_generator_function_boundary(node) ? "nested_generator" : "nested_invalid";
|
|
347
|
+
node.forEachChild((child) => visit_event_body(child, next_context, result));
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
node.forEachChild((child) => visit_event_body(child, context, result));
|
|
351
|
+
}
|
|
352
|
+
function is_nested_function_boundary(node) {
|
|
353
|
+
return ts.isArrowFunction(node) || ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node);
|
|
354
|
+
}
|
|
355
|
+
function is_generator_function_boundary(node) {
|
|
356
|
+
return (ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) && node.asteriskToken !== void 0;
|
|
357
|
+
}
|
|
358
|
+
function is_yield_star_expression(node) {
|
|
359
|
+
if (ts.isYieldExpression(node)) return node.asteriskToken !== void 0;
|
|
360
|
+
return ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.AsteriskToken && ts.isIdentifier(node.left) && node.left.text === "yield";
|
|
361
|
+
}
|
|
362
|
+
function is_property_access_name(node) {
|
|
363
|
+
const parent = node.parent;
|
|
364
|
+
return ts.isPropertyAccessExpression(parent) && parent.name === node || ts.isPropertyAssignment(parent) && parent.name === node || ts.isBindingElement(parent) && parent.propertyName === node || ts.isImportSpecifier(parent) || ts.isExportSpecifier(parent);
|
|
365
|
+
}
|
|
366
|
+
//#endregion
|
|
367
|
+
//#region src/markup/transform/emit.ts
|
|
368
|
+
/**
|
|
369
|
+
* Emits source edits for classified markup Effect expressions.
|
|
370
|
+
*
|
|
371
|
+
* @since 2.0.0
|
|
372
|
+
* @param classified - Candidates paired with their Svelte markup context.
|
|
373
|
+
* @returns Replacements ready to apply to the original component source.
|
|
374
|
+
*/
|
|
375
|
+
function emit_replacements(classified) {
|
|
376
|
+
return classified.map(({ candidate, kind }) => emit_replacement(candidate, kind));
|
|
377
|
+
}
|
|
378
|
+
function emit_replacement(candidate, kind) {
|
|
379
|
+
const id = make_cache_id(candidate);
|
|
380
|
+
const id_text = JSON.stringify(id);
|
|
381
|
+
const helper_name = make_helper_name(candidate);
|
|
382
|
+
let replacement_text;
|
|
383
|
+
let helpers;
|
|
384
|
+
let relocation;
|
|
385
|
+
if (kind === "await") {
|
|
386
|
+
const effect = make_effect_helper(candidate, helper_name);
|
|
387
|
+
replacement_text = emit_promise_expression(id_text, effect);
|
|
388
|
+
helpers = [effect.helper];
|
|
389
|
+
} else if (kind === "render") {
|
|
390
|
+
const effect = make_effect_helper(candidate, helper_name);
|
|
391
|
+
replacement_text = emit_render_expression(id_text, effect);
|
|
392
|
+
helpers = [effect.helper];
|
|
393
|
+
} else if (kind === "each") {
|
|
394
|
+
const effect = make_effect_helper(candidate, helper_name);
|
|
395
|
+
replacement_text = emit_each_expression(id_text, effect);
|
|
396
|
+
helpers = [effect.helper];
|
|
397
|
+
} else if (kind === "event") {
|
|
398
|
+
const event = strip_arrow_function(candidate.expr_text);
|
|
399
|
+
replacement_text = `${event.params} => { void ${HELPERS.run}(function* () { ${event.body}; }); }`;
|
|
400
|
+
helpers = [];
|
|
401
|
+
relocation = make_relocation(candidate, replacement_text, {
|
|
402
|
+
originalStart: event.body_start,
|
|
403
|
+
originalEnd: event.body_end,
|
|
404
|
+
generatedText: event.body
|
|
405
|
+
});
|
|
406
|
+
} else {
|
|
407
|
+
const effect = make_effect_helper(candidate, helper_name);
|
|
408
|
+
replacement_text = emit_each_expression(id_text, effect);
|
|
409
|
+
helpers = [effect.helper];
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
start: candidate.start,
|
|
413
|
+
end: candidate.end,
|
|
414
|
+
text: replacement_text,
|
|
415
|
+
helpers,
|
|
416
|
+
relocation
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
function emit_promise_expression(id_text, effect) {
|
|
420
|
+
return `${HELPERS.promise}(${id_text}, ${effect.deps_text}, () => ${effect.call})`;
|
|
421
|
+
}
|
|
422
|
+
function emit_render_expression(id_text, effect) {
|
|
423
|
+
return `(await ${emit_promise_expression(id_text, effect)})()`;
|
|
424
|
+
}
|
|
425
|
+
function emit_each_expression(id_text, effect) {
|
|
426
|
+
return `await ${emit_promise_expression(id_text, effect)}`;
|
|
427
|
+
}
|
|
428
|
+
function make_effect_helper(candidate, helper_name) {
|
|
429
|
+
const deps = collect_free_identifiers(candidate.expr_text);
|
|
430
|
+
const params_text = deps.join(", ");
|
|
431
|
+
const args_text = deps.join(", ");
|
|
432
|
+
const deps_text = deps.length === 0 ? "[]" : `[${args_text}]`;
|
|
433
|
+
const call = `${helper_name}(${args_text})`;
|
|
434
|
+
const text = `function* ${helper_name}(${params_text}) { return (${candidate.expr_text}); }`;
|
|
435
|
+
const generated_start = text.indexOf(candidate.expr_text);
|
|
436
|
+
return {
|
|
437
|
+
call,
|
|
438
|
+
deps_text,
|
|
439
|
+
helper: {
|
|
440
|
+
text,
|
|
441
|
+
relocation: {
|
|
442
|
+
originalStart: candidate.start,
|
|
443
|
+
originalEnd: candidate.end,
|
|
444
|
+
generatedStartInReplacement: generated_start,
|
|
445
|
+
generatedEndInReplacement: generated_start + candidate.expr_text.length
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function make_cache_id(candidate) {
|
|
451
|
+
return `${candidate.filename}:${candidate.start}:${candidate.end}`;
|
|
452
|
+
}
|
|
453
|
+
function make_helper_name(candidate) {
|
|
454
|
+
return `__ser_markup_effect_${candidate.start}_${candidate.end}`;
|
|
455
|
+
}
|
|
456
|
+
function make_relocation(candidate, replacement_text, inner) {
|
|
457
|
+
const generated_start = replacement_text.indexOf(inner.generatedText);
|
|
458
|
+
if (generated_start === -1) return;
|
|
459
|
+
return {
|
|
460
|
+
originalStart: candidate.start + inner.originalStart,
|
|
461
|
+
originalEnd: candidate.start + inner.originalEnd,
|
|
462
|
+
generatedStartInReplacement: generated_start,
|
|
463
|
+
generatedEndInReplacement: generated_start + inner.generatedText.length
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
//#endregion
|
|
467
|
+
//#region src/error.ts
|
|
468
|
+
/**
|
|
469
|
+
* Base error class for all preprocessor errors emitted during script and
|
|
470
|
+
* markup transformation. Carries the source filename so error messages can
|
|
471
|
+
* reference the affected file.
|
|
472
|
+
*
|
|
473
|
+
* @since 2.0.0
|
|
474
|
+
*/
|
|
475
|
+
var PreprocessError = class extends Error {
|
|
476
|
+
/**
|
|
477
|
+
* The source filename that triggered this error.
|
|
478
|
+
*
|
|
479
|
+
* @since 2.0.0
|
|
480
|
+
*/
|
|
481
|
+
filename;
|
|
482
|
+
constructor(message, filename) {
|
|
483
|
+
super(message);
|
|
484
|
+
this.name = "PreprocessError";
|
|
485
|
+
this.filename = filename;
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
/**
|
|
489
|
+
* Thrown when `await` is used at the top level of a `<script effect>` block.
|
|
490
|
+
* Top-level `await` is not supported — users should use
|
|
491
|
+
* `yield* Effect.promise(...)` or `yield* Effect.tryPromise(...)` instead.
|
|
492
|
+
*
|
|
493
|
+
* @since 2.0.0
|
|
494
|
+
*/
|
|
495
|
+
var TopLevelAwaitError = class extends PreprocessError {
|
|
496
|
+
/**
|
|
497
|
+
* The full text of the problematic statement containing the `await`.
|
|
498
|
+
*
|
|
499
|
+
* @since 2.0.0
|
|
500
|
+
*/
|
|
501
|
+
statement_text;
|
|
502
|
+
constructor(filename, statement_text) {
|
|
503
|
+
super([
|
|
504
|
+
`${filename}: top-level await is not supported in <script effect>.`,
|
|
505
|
+
`Use yield* Effect.promise(...) or yield* Effect.tryPromise(...) instead.`,
|
|
506
|
+
"",
|
|
507
|
+
`Problematic statement:`,
|
|
508
|
+
statement_text
|
|
509
|
+
].join("\n"), filename);
|
|
510
|
+
this.name = "TopLevelAwaitError";
|
|
511
|
+
this.statement_text = statement_text;
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
/**
|
|
515
|
+
* Thrown when an event handler contains `yield*` inside a nested callback that
|
|
516
|
+
* is not itself a generator function.
|
|
517
|
+
*
|
|
518
|
+
* @example
|
|
519
|
+
* ```ts
|
|
520
|
+
* throw new NestedYieldStarInEventHandlerError(
|
|
521
|
+
* "Component.svelte",
|
|
522
|
+
* "Effect.try(() => yield* save())",
|
|
523
|
+
* );
|
|
524
|
+
* ```
|
|
525
|
+
*
|
|
526
|
+
* @since 2.0.0
|
|
527
|
+
*/
|
|
528
|
+
var NestedYieldStarInEventHandlerError = class extends PreprocessError {
|
|
529
|
+
/**
|
|
530
|
+
* The full text of the problematic event handler body.
|
|
531
|
+
*
|
|
532
|
+
* @since 2.0.0
|
|
533
|
+
*/
|
|
534
|
+
expression_text;
|
|
535
|
+
constructor(filename, expression_text) {
|
|
536
|
+
super([
|
|
537
|
+
`${filename}: yield* cannot be used inside a nested non-generator callback in a markup event handler.`,
|
|
538
|
+
`Move the yield* to the event handler body. Effect.try and Effect.sync callbacks are plain synchronous JavaScript; do not call Effect-returning functions inside them.`,
|
|
539
|
+
"",
|
|
540
|
+
`Run the remote Effect directly:`,
|
|
541
|
+
` onclick={() => yield* UpvotePost(id)}`,
|
|
542
|
+
"",
|
|
543
|
+
`Recover from remote failures by composing the Effect value:`,
|
|
544
|
+
` onclick={() => yield* UpvotePost(id).pipe(Effect.catch(() => Effect.void))}`,
|
|
545
|
+
"",
|
|
546
|
+
`Problematic expression:`,
|
|
547
|
+
expression_text
|
|
548
|
+
].join("\n"), filename);
|
|
549
|
+
this.name = "NestedYieldStarInEventHandlerError";
|
|
550
|
+
this.expression_text = expression_text;
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
//#endregion
|
|
554
|
+
//#region src/markup/transform/scan.ts
|
|
555
|
+
function sanitize_markup(content, filename) {
|
|
556
|
+
const candidates = [];
|
|
557
|
+
const magic = new MagicString(content);
|
|
558
|
+
let helper_index = 0;
|
|
559
|
+
let cursor = 0;
|
|
560
|
+
while (cursor < content.length) {
|
|
561
|
+
const open = content.indexOf("{", cursor);
|
|
562
|
+
if (open === -1) break;
|
|
563
|
+
/** Skip braces inside <script> and <style> blocks. */
|
|
564
|
+
if (is_inside_excluded_block(content, open)) {
|
|
565
|
+
cursor = open + 1;
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
/** Find the matching closing brace. */
|
|
569
|
+
const close = find_closing_brace(content, open + 1);
|
|
570
|
+
if (close === -1) {
|
|
571
|
+
cursor = open + 1;
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
const inner = content.slice(open + 1, close);
|
|
575
|
+
const trimmed = inner.trimStart();
|
|
576
|
+
const leading_ws = inner.length - trimmed.length;
|
|
577
|
+
const tag_info = get_tag_info(trimmed);
|
|
578
|
+
const declaration_initializers = collect_declaration_initializers(content, open, leading_ws, trimmed);
|
|
579
|
+
if (declaration_initializers.length > 0) {
|
|
580
|
+
for (const initializer of declaration_initializers) {
|
|
581
|
+
const placeholder = `__ser_markup_placeholder_${helper_index}`;
|
|
582
|
+
helper_index += 1;
|
|
583
|
+
candidates.push({
|
|
584
|
+
placeholder,
|
|
585
|
+
start: initializer.start,
|
|
586
|
+
end: initializer.end,
|
|
587
|
+
expr_text: initializer.expr_text,
|
|
588
|
+
filename,
|
|
589
|
+
key: "plain"
|
|
590
|
+
});
|
|
591
|
+
magic.overwrite(initializer.start, initializer.end, placeholder);
|
|
592
|
+
}
|
|
593
|
+
cursor = close + 1;
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
let expr_body = trimmed.slice(tag_info.prefix_length);
|
|
597
|
+
/** For @const, only use the RHS after `=` as the expression body. */
|
|
598
|
+
const equal_idx = tag_info.kind === "plain" && trimmed.startsWith("@const ") ? expr_body.indexOf("=") : -1;
|
|
599
|
+
if (!((is_event_expression(inner) ? analyze_event_yield(inner, filename) : void 0)?.has_top_level_yield_star ?? contains_yield_star_in_text(expr_body))) {
|
|
600
|
+
cursor = close + 1;
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
/** The expression starts after the tag prefix. For @const, after the `=`. */
|
|
604
|
+
let extra_prefix = 0;
|
|
605
|
+
if (equal_idx !== -1) {
|
|
606
|
+
const after_eq_raw = expr_body.slice(equal_idx + 1);
|
|
607
|
+
expr_body = after_eq_raw.trimStart();
|
|
608
|
+
extra_prefix = equal_idx + 1 + (after_eq_raw.length - expr_body.length);
|
|
609
|
+
}
|
|
610
|
+
const expr_start = open + 1 + leading_ws + tag_info.prefix_length + extra_prefix;
|
|
611
|
+
/** For each/await, the expression ends before ` as ` or ` then `/` catch `. */
|
|
612
|
+
let expr_end = close;
|
|
613
|
+
const key = tag_info.kind;
|
|
614
|
+
if (key === "each") {
|
|
615
|
+
const as_idx = expr_body.lastIndexOf(" as ");
|
|
616
|
+
if (as_idx !== -1) expr_end = expr_start + as_idx;
|
|
617
|
+
}
|
|
618
|
+
if (key === "await") {
|
|
619
|
+
const then_idx = expr_body.indexOf(" then ");
|
|
620
|
+
const catch_idx = expr_body.indexOf(" catch ");
|
|
621
|
+
const boundary = Math.min(then_idx === -1 ? Infinity : then_idx, catch_idx === -1 ? Infinity : catch_idx);
|
|
622
|
+
if (boundary !== Infinity) expr_end = expr_start + boundary;
|
|
623
|
+
}
|
|
624
|
+
const expr_text = content.slice(expr_start, expr_end).trim();
|
|
625
|
+
if (expr_text.length === 0) {
|
|
626
|
+
cursor = close + 1;
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
/** Create a placeholder and replace the expression (preserving tag prefixes). */
|
|
630
|
+
const placeholder = `__ser_markup_placeholder_${helper_index}`;
|
|
631
|
+
helper_index += 1;
|
|
632
|
+
candidates.push({
|
|
633
|
+
placeholder,
|
|
634
|
+
start: expr_start,
|
|
635
|
+
end: expr_end,
|
|
636
|
+
expr_text,
|
|
637
|
+
filename,
|
|
638
|
+
key
|
|
639
|
+
});
|
|
640
|
+
magic.overwrite(expr_start, expr_end, key === "render" ? `${placeholder}()` : placeholder);
|
|
641
|
+
cursor = close + 1;
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
code: magic.toString(),
|
|
645
|
+
candidates
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
function is_inside_excluded_block(content, pos) {
|
|
649
|
+
const script = find_tag_end(content, "script", pos);
|
|
650
|
+
const style = find_tag_end(content, "style", pos);
|
|
651
|
+
return script !== void 0 && pos < script.end && pos > script.start || style !== void 0 && pos < style.end && pos > style.start;
|
|
652
|
+
}
|
|
653
|
+
function find_tag_end(content, tag, after_pos) {
|
|
654
|
+
const pattern = new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)<\\/${tag}\\s*>`, "gi");
|
|
655
|
+
for (const match of content.matchAll(pattern)) {
|
|
656
|
+
if (match.index === void 0) continue;
|
|
657
|
+
const end_pos = match.index + match[0].length;
|
|
658
|
+
if (match.index <= after_pos && after_pos < end_pos) return {
|
|
659
|
+
start: match.index,
|
|
660
|
+
end: end_pos
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
/** Brace matching helpers for extracting complete markup expressions. */
|
|
665
|
+
function find_closing_brace(content, start) {
|
|
666
|
+
let depth = 0;
|
|
667
|
+
for (let i = start; i < content.length; i += 1) {
|
|
668
|
+
const ch = content[i];
|
|
669
|
+
if (ch === "{" && content[i - 1] !== "$") depth += 1;
|
|
670
|
+
else if (ch === "}") {
|
|
671
|
+
if (depth === 0) return i;
|
|
672
|
+
depth -= 1;
|
|
673
|
+
} else if (ch === "'" || ch === "\"" || ch === "`") {
|
|
674
|
+
i = skip_string(content, i, ch);
|
|
675
|
+
if (i === -1) return -1;
|
|
676
|
+
} else if (ch === "/" && content[i + 1] === "/") i = skip_line_comment(content, i);
|
|
677
|
+
else if (ch === "/" && content[i + 1] === "*") {
|
|
678
|
+
i = skip_block_comment(content, i);
|
|
679
|
+
if (i === -1) return -1;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return -1;
|
|
683
|
+
}
|
|
684
|
+
function skip_string(content, start, quote) {
|
|
685
|
+
for (let i = start + 1; i < content.length; i += 1) {
|
|
686
|
+
if (content[i] === "\\") {
|
|
687
|
+
i += 1;
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
if (content[i] === quote) return i;
|
|
691
|
+
}
|
|
692
|
+
return -1;
|
|
693
|
+
}
|
|
694
|
+
function skip_line_comment(content, start) {
|
|
695
|
+
for (let i = start + 2; i < content.length; i += 1) if (content[i] === "\n") return i;
|
|
696
|
+
return content.length;
|
|
697
|
+
}
|
|
698
|
+
function skip_block_comment(content, start) {
|
|
699
|
+
for (let i = start + 2; i < content.length; i += 1) if (content[i] === "*" && content[i + 1] === "/") return i + 1;
|
|
700
|
+
return -1;
|
|
701
|
+
}
|
|
702
|
+
function get_tag_info(trimmed) {
|
|
703
|
+
if (trimmed.startsWith("#each ")) return {
|
|
704
|
+
kind: "each",
|
|
705
|
+
prefix_length: 6
|
|
706
|
+
};
|
|
707
|
+
if (trimmed.startsWith("#await ")) return {
|
|
708
|
+
kind: "await",
|
|
709
|
+
prefix_length: 7
|
|
710
|
+
};
|
|
711
|
+
if (trimmed.startsWith("@render ")) return {
|
|
712
|
+
kind: "render",
|
|
713
|
+
prefix_length: 8
|
|
714
|
+
};
|
|
715
|
+
/** Strip prefix-only tags — the expression starts after the tag keyword. */
|
|
716
|
+
if (trimmed.startsWith("#if ")) return {
|
|
717
|
+
kind: "plain",
|
|
718
|
+
prefix_length: 4
|
|
719
|
+
};
|
|
720
|
+
if (trimmed.startsWith(":else if ")) return {
|
|
721
|
+
kind: "plain",
|
|
722
|
+
prefix_length: 9
|
|
723
|
+
};
|
|
724
|
+
if (trimmed.startsWith("#key ")) return {
|
|
725
|
+
kind: "plain",
|
|
726
|
+
prefix_length: 5
|
|
727
|
+
};
|
|
728
|
+
if (trimmed.startsWith("@const ")) return {
|
|
729
|
+
kind: "plain",
|
|
730
|
+
prefix_length: 7
|
|
731
|
+
};
|
|
732
|
+
if (trimmed.startsWith("@html ")) return {
|
|
733
|
+
kind: "plain",
|
|
734
|
+
prefix_length: 6
|
|
735
|
+
};
|
|
736
|
+
if (trimmed.startsWith("@debug ")) return {
|
|
737
|
+
kind: "plain",
|
|
738
|
+
prefix_length: 7
|
|
739
|
+
};
|
|
740
|
+
return {
|
|
741
|
+
kind: "plain",
|
|
742
|
+
prefix_length: 0
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
function collect_declaration_initializers(content, open, leading_ws, trimmed) {
|
|
746
|
+
if (!is_declaration_tag_text(trimmed)) return [];
|
|
747
|
+
const source_file = ts.createSourceFile("declaration-tag.ts", `${trimmed};`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
748
|
+
const stmt = source_file.statements[0];
|
|
749
|
+
if (!stmt || !ts.isVariableStatement(stmt)) return [];
|
|
750
|
+
const tag_start = open + 1 + leading_ws;
|
|
751
|
+
return stmt.declarationList.declarations.filter((decl) => decl.initializer && contains_top_level_yield_star(decl.initializer)).map((decl) => {
|
|
752
|
+
const initializer = decl.initializer;
|
|
753
|
+
const start = tag_start + initializer.getStart(source_file);
|
|
754
|
+
const end = tag_start + initializer.end;
|
|
755
|
+
return {
|
|
756
|
+
start,
|
|
757
|
+
end,
|
|
758
|
+
expr_text: content.slice(start, end).trim()
|
|
759
|
+
};
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
function is_declaration_tag_text(trimmed) {
|
|
763
|
+
return /^(?:const|let)\s/.test(trimmed);
|
|
764
|
+
}
|
|
765
|
+
function is_event_expression(inner) {
|
|
766
|
+
const trimmed = inner.trimStart();
|
|
767
|
+
return /^(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/.test(trimmed);
|
|
768
|
+
}
|
|
769
|
+
function analyze_event_yield(inner, filename) {
|
|
770
|
+
const event = strip_arrow_function(inner);
|
|
771
|
+
const analysis = analyze_event_body_yield_star(event.body);
|
|
772
|
+
if (analysis.has_nested_invalid_yield_star) throw new NestedYieldStarInEventHandlerError(filename, event.body);
|
|
773
|
+
return { has_top_level_yield_star: analysis.has_top_level_yield_star };
|
|
774
|
+
}
|
|
775
|
+
function contains_yield_star_in_text(text) {
|
|
776
|
+
if (!/\byield\s*\*/.test(text)) return false;
|
|
777
|
+
try {
|
|
778
|
+
const stmt = ts.createSourceFile("expr.ts", `const x = ${text};`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS).statements[0];
|
|
779
|
+
if (!ts.isVariableStatement(stmt)) return false;
|
|
780
|
+
const decl = stmt.declarationList.declarations[0];
|
|
781
|
+
if (!decl?.initializer) return false;
|
|
782
|
+
return contains_top_level_yield_star(decl.initializer);
|
|
783
|
+
} catch {
|
|
784
|
+
return true;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
/** Free identifier collection helpers for generated closures. */
|
|
788
|
+
//#endregion
|
|
789
|
+
//#region src/markup/transform/index.ts
|
|
790
|
+
/**
|
|
791
|
+
* Transforms Svelte markup containing `{yield* expr}` brace expressions
|
|
792
|
+
* into calls to the markup runtime helpers (`value`, `promise`, `run`).
|
|
793
|
+
*
|
|
794
|
+
* Strategy: first find all brace expressions containing `yield*` via
|
|
795
|
+
* character scanning, replace them with placeholder identifiers, then
|
|
796
|
+
* parse the sanitized markup with Svelte's AST to determine the correct
|
|
797
|
+
* context for each placeholder (plain expression, #each, #await, event
|
|
798
|
+
* handler, etc.).
|
|
799
|
+
*
|
|
800
|
+
* @since 2.0.0
|
|
801
|
+
* @param content - The raw `.svelte` file content.
|
|
802
|
+
* @param filename - The source filename, used in error messages.
|
|
803
|
+
* @returns The transformed markup and a flag indicating whether yield* was
|
|
804
|
+
* found.
|
|
805
|
+
*/
|
|
806
|
+
function transform_markup_effect(content, filename) {
|
|
807
|
+
if (!/\byield\s*\*/.test(content)) return {
|
|
808
|
+
code: content,
|
|
809
|
+
has_yield: false
|
|
810
|
+
};
|
|
811
|
+
/** Find all brace expressions containing yield* and replace with placeholders. */
|
|
812
|
+
const work = sanitize_markup(content, filename);
|
|
813
|
+
if (work.candidates.length === 0) return {
|
|
814
|
+
code: content,
|
|
815
|
+
has_yield: false
|
|
816
|
+
};
|
|
817
|
+
const replacements = emit_replacements(classify_candidates(parse(blank_script_blocks(work.code), {
|
|
818
|
+
filename,
|
|
819
|
+
modern: true
|
|
820
|
+
}), work.candidates));
|
|
821
|
+
const helpers = replacements.flatMap((replacement) => replacement.helpers ?? []);
|
|
822
|
+
const magic = new MagicString(content);
|
|
823
|
+
replacements.sort((a, b) => b.start - a.start);
|
|
824
|
+
for (const r of replacements) magic.overwrite(r.start, r.end, r.text);
|
|
825
|
+
const relocations = create_relocations(replacements, inject_helpers(magic, content, helpers));
|
|
826
|
+
return {
|
|
827
|
+
code: magic.toString(),
|
|
828
|
+
has_yield: true,
|
|
829
|
+
map: create_source_map(magic, filename),
|
|
830
|
+
relocations
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
//#endregion
|
|
834
|
+
export { TopLevelAwaitError as n, transform_markup_effect as t };
|
|
835
|
+
|
|
836
|
+
//# sourceMappingURL=transform-BtlxgJrg.js.map
|