svelte-effect-runtime 2.0.1 → 2.1.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.
- package/.dist/chunks/{client-BeOE81fW.js → client-zavjI3Qd.js} +9 -9
- package/.dist/chunks/client-zavjI3Qd.js.map +1 -0
- package/.dist/chunks/{dispatcher-BSg2-ttN.js → dispatcher-g0HYqHOK.js} +3 -3
- package/.dist/chunks/{dispatcher-BSg2-ttN.js.map → dispatcher-g0HYqHOK.js.map} +1 -1
- package/.dist/chunks/{preprocess-BZyUYWY7.js → preprocess-33aQ41uE.js} +185 -156
- package/.dist/chunks/preprocess-33aQ41uE.js.map +1 -0
- package/.dist/chunks/{transform-BtlxgJrg.js → transform-B4g76Ur4.js} +337 -113
- package/.dist/chunks/transform-B4g76Ur4.js.map +1 -0
- package/.dist/dispatcher.js +1 -1
- package/.dist/error.d.ts +54 -14
- package/.dist/internal/generators.js +1 -1
- package/.dist/internal/remote-client.js +1 -1
- package/.dist/markup/promise.js +1 -1
- package/.dist/markup/run.js +1 -1
- package/.dist/markup/transform/expressions.d.ts +8 -0
- package/.dist/markup/transform.js +1 -1
- package/.dist/markup/value.js +1 -1
- package/.dist/mod.d.ts +1 -1
- package/.dist/mod.js +2 -2
- package/.dist/mod.js.map +1 -1
- package/.dist/preprocess/imports.d.ts +1 -2
- package/.dist/preprocess/index.d.ts +1 -1
- package/.dist/preprocess/lower.d.ts +1 -1
- package/.dist/preprocess/runes.d.ts +24 -0
- package/.dist/preprocess/runtime-block.d.ts +5 -4
- package/.dist/preprocess/types.d.ts +15 -3
- package/.dist/preprocess.js +2 -2
- package/.dist/remote/client.js +1 -1
- package/.dist/remote/server.js +3 -3
- package/.dist/remote/server.js.map +1 -1
- package/.dist/runtime/preprocess.js +2 -2
- package/.dist/server.js +8 -8
- package/.dist/server.js.map +1 -1
- package/package.json +1 -1
- package/.dist/chunks/client-BeOE81fW.js.map +0 -1
- package/.dist/chunks/preprocess-BZyUYWY7.js.map +0 -1
- package/.dist/chunks/transform-BtlxgJrg.js.map +0 -1
|
@@ -196,7 +196,7 @@ function classify_declaration_tag(node, candidates, matched, classified) {
|
|
|
196
196
|
}
|
|
197
197
|
function visit_element_attributes(node, candidates, matched, classified) {
|
|
198
198
|
for (const attr of node.attributes) {
|
|
199
|
-
if (attr.type === "Attribute" && attr.name && (attr.name
|
|
199
|
+
if (attr.type === "Attribute" && attr.name && is_event_attribute_name(attr.name)) {
|
|
200
200
|
visit_attribute_value(attr.value, "event", candidates, matched, classified);
|
|
201
201
|
continue;
|
|
202
202
|
}
|
|
@@ -206,6 +206,9 @@ function visit_element_attributes(node, candidates, matched, classified) {
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
+
function is_event_attribute_name(name) {
|
|
210
|
+
return name.startsWith("on:") || /^on[a-z]/.test(name);
|
|
211
|
+
}
|
|
209
212
|
function visit_attribute_value(value, kind, candidates, matched, classified) {
|
|
210
213
|
if (value === true) return;
|
|
211
214
|
if (Array.isArray(value)) {
|
|
@@ -216,19 +219,219 @@ function visit_attribute_value(value, kind, candidates, matched, classified) {
|
|
|
216
219
|
}
|
|
217
220
|
function classify_expression(expression, kind, candidates, matched, classified) {
|
|
218
221
|
if (!expression) return;
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
222
|
+
const found_candidates = find_candidates(expression, candidates);
|
|
223
|
+
for (const candidate of found_candidates) {
|
|
224
|
+
if (matched.has(candidate.placeholder)) continue;
|
|
225
|
+
matched.add(candidate.placeholder);
|
|
226
|
+
classified.push({
|
|
227
|
+
candidate,
|
|
228
|
+
kind
|
|
229
|
+
});
|
|
230
|
+
}
|
|
226
231
|
}
|
|
227
|
-
function
|
|
228
|
-
|
|
229
|
-
|
|
232
|
+
function find_candidates(expression, candidates) {
|
|
233
|
+
const found = [];
|
|
234
|
+
visit_expression_value(expression, candidates, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), found);
|
|
235
|
+
return found;
|
|
236
|
+
}
|
|
237
|
+
function visit_expression_value(value, candidates, seen_nodes, seen_placeholders, found) {
|
|
238
|
+
if (Array.isArray(value)) {
|
|
239
|
+
for (const item of value) visit_expression_value(item, candidates, seen_nodes, seen_placeholders, found);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (!is_record(value) || seen_nodes.has(value)) return;
|
|
243
|
+
seen_nodes.add(value);
|
|
244
|
+
if (value.type === "Identifier" && typeof value.name === "string") {
|
|
245
|
+
const candidate = candidates.get(value.name);
|
|
246
|
+
if (candidate && !seen_placeholders.has(candidate.placeholder)) {
|
|
247
|
+
seen_placeholders.add(candidate.placeholder);
|
|
248
|
+
found.push(candidate);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
for (const child of Object.values(value)) visit_expression_value(child, candidates, seen_nodes, seen_placeholders, found);
|
|
252
|
+
}
|
|
253
|
+
function is_record(value) {
|
|
254
|
+
return typeof value === "object" && value !== null;
|
|
230
255
|
}
|
|
231
256
|
//#endregion
|
|
257
|
+
//#region src/error.ts
|
|
258
|
+
/**
|
|
259
|
+
* Formats a runtime-owned error message with a stable screaming-case code.
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```ts
|
|
263
|
+
* throw new Error(make_error_message("DISPATCHER_DISPOSED", "Dispatcher has been disposed"));
|
|
264
|
+
* ```
|
|
265
|
+
*
|
|
266
|
+
* @since 2.0.0
|
|
267
|
+
* @param code - Stable screaming-case identifier for the error category.
|
|
268
|
+
* @param message - Human-readable error message without the leading code.
|
|
269
|
+
* @returns The complete error message prefixed with the stable code.
|
|
270
|
+
*/
|
|
271
|
+
function make_error_message(code, message) {
|
|
272
|
+
return `[${code}]: ${message}`;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Base error class for all preprocessor errors emitted during script and
|
|
276
|
+
* markup transformation. Carries the source filename so error messages can
|
|
277
|
+
* reference the affected file.
|
|
278
|
+
*
|
|
279
|
+
* @since 2.0.0
|
|
280
|
+
*/
|
|
281
|
+
var PreprocessError = class extends Error {
|
|
282
|
+
/**
|
|
283
|
+
* The source filename that triggered this error.
|
|
284
|
+
*
|
|
285
|
+
* @since 2.0.0
|
|
286
|
+
*/
|
|
287
|
+
filename;
|
|
288
|
+
constructor(message, filename) {
|
|
289
|
+
super(message);
|
|
290
|
+
this.name = "PreprocessError";
|
|
291
|
+
this.filename = filename;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* Thrown when a statement mixes JavaScript `await` with Effect `yield*` work
|
|
296
|
+
* that must be lowered into an `Effect.gen` program.
|
|
297
|
+
*
|
|
298
|
+
* @since 2.0.0
|
|
299
|
+
*/
|
|
300
|
+
var AwaitInEffectWorkError = class extends PreprocessError {
|
|
301
|
+
/**
|
|
302
|
+
* The full text of the problematic statement containing mixed async work.
|
|
303
|
+
*
|
|
304
|
+
* @since 2.0.0
|
|
305
|
+
*/
|
|
306
|
+
statement_text;
|
|
307
|
+
constructor(filename, statement_text) {
|
|
308
|
+
super([
|
|
309
|
+
make_error_message("AWAIT_IN_EFFECT_WORK", `${filename}: await cannot be mixed with yield* in Effect work.`),
|
|
310
|
+
`Top-level await is supported as ordinary Svelte async rendering, but statements lowered into Effect.gen must use yield* for async Effect work.`,
|
|
311
|
+
"",
|
|
312
|
+
`Problematic statement:`,
|
|
313
|
+
statement_text
|
|
314
|
+
].join("\n"), filename);
|
|
315
|
+
this.name = "AwaitInEffectWorkError";
|
|
316
|
+
this.statement_text = statement_text;
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
/**
|
|
320
|
+
* Thrown when async Effect work appears inside a Svelte rune position that
|
|
321
|
+
* must stay synchronous.
|
|
322
|
+
*
|
|
323
|
+
* @since 2.0.0
|
|
324
|
+
*/
|
|
325
|
+
var AsyncEffectInSyncRuneError = class extends PreprocessError {
|
|
326
|
+
/**
|
|
327
|
+
* The name of the rune that contained async Effect work.
|
|
328
|
+
*
|
|
329
|
+
* @since 2.0.0
|
|
330
|
+
*/
|
|
331
|
+
rune_name;
|
|
332
|
+
/**
|
|
333
|
+
* The full text of the expression that triggered the error.
|
|
334
|
+
*
|
|
335
|
+
* @since 2.0.0
|
|
336
|
+
*/
|
|
337
|
+
expression_text;
|
|
338
|
+
constructor(rune_name, expression_text, filename) {
|
|
339
|
+
super([
|
|
340
|
+
make_error_message("ASYNC_EFFECT_IN_SYNC_RUNE", `${filename}: yield* cannot be used inside ${rune_name}().`),
|
|
341
|
+
`${rune_name}() must stay synchronous. Do not put async Effect work inside this rune.`,
|
|
342
|
+
"",
|
|
343
|
+
`Problematic expression:`,
|
|
344
|
+
expression_text
|
|
345
|
+
].join("\n"), filename);
|
|
346
|
+
this.name = "AsyncEffectInSyncRuneError";
|
|
347
|
+
this.rune_name = rune_name;
|
|
348
|
+
this.expression_text = expression_text;
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
/**
|
|
352
|
+
* Thrown when async Effect work appears inside a non-generator callback nested
|
|
353
|
+
* in a markup event handler.
|
|
354
|
+
*
|
|
355
|
+
* @example
|
|
356
|
+
* ```ts
|
|
357
|
+
* throw new AsyncEffectInEventCallbackError(
|
|
358
|
+
* "Component.svelte",
|
|
359
|
+
* "Effect.try(() => yield* save())",
|
|
360
|
+
* );
|
|
361
|
+
* ```
|
|
362
|
+
*
|
|
363
|
+
* @since 2.0.0
|
|
364
|
+
*/
|
|
365
|
+
var AsyncEffectInEventCallbackError = class extends PreprocessError {
|
|
366
|
+
/**
|
|
367
|
+
* The full text of the problematic event handler body.
|
|
368
|
+
*
|
|
369
|
+
* @since 2.0.0
|
|
370
|
+
*/
|
|
371
|
+
expression_text;
|
|
372
|
+
constructor(filename, expression_text) {
|
|
373
|
+
super([
|
|
374
|
+
make_error_message("ASYNC_EFFECT_IN_EVENT_CALLBACK", `${filename}: yield* cannot be used inside a nested non-generator callback in a markup event handler.`),
|
|
375
|
+
`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.`,
|
|
376
|
+
"",
|
|
377
|
+
`Run the remote Effect directly:`,
|
|
378
|
+
` onclick={yield* UpvotePost(id)}`,
|
|
379
|
+
"",
|
|
380
|
+
`Recover from remote failures by composing the Effect value:`,
|
|
381
|
+
` onclick={yield* UpvotePost(id).pipe(Effect.catch(() => Effect.void))}`,
|
|
382
|
+
"",
|
|
383
|
+
`Problematic expression:`,
|
|
384
|
+
expression_text
|
|
385
|
+
].join("\n"), filename);
|
|
386
|
+
this.name = "AsyncEffectInEventCallbackError";
|
|
387
|
+
this.expression_text = expression_text;
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
/**
|
|
391
|
+
* Thrown when an event handler callback contains the old raw `yield*`
|
|
392
|
+
* shorthand. Effectful event handlers must put `yield*` directly in the event
|
|
393
|
+
* attribute so the callback boundary is generated by the markup runtime.
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* ```ts
|
|
397
|
+
* throw new YieldStarInEventCallbackError(
|
|
398
|
+
* "Component.svelte",
|
|
399
|
+
* "() => yield* save()",
|
|
400
|
+
* );
|
|
401
|
+
* ```
|
|
402
|
+
*
|
|
403
|
+
* @since 2.0.0
|
|
404
|
+
* @param filename - Svelte component filename used to identify where the
|
|
405
|
+
* invalid event handler callback was found.
|
|
406
|
+
* @param expression_text - Original event handler callback text that contained
|
|
407
|
+
* `yield*` and should be rewritten as a direct event Effect expression.
|
|
408
|
+
*/
|
|
409
|
+
var YieldStarInEventCallbackError = class extends PreprocessError {
|
|
410
|
+
/**
|
|
411
|
+
* The full text of the problematic event handler callback.
|
|
412
|
+
*
|
|
413
|
+
* @since 2.0.0
|
|
414
|
+
*/
|
|
415
|
+
expression_text;
|
|
416
|
+
constructor(filename, expression_text) {
|
|
417
|
+
super([
|
|
418
|
+
make_error_message("ASYNC_EFFECT_IN_EVENT_HANDLER_CALLBACK", `${filename}: yield* in markup event handlers must be written directly as the event attribute value.`),
|
|
419
|
+
`SER generates the event callback for effectful event handlers; do not put yield* inside a JavaScript callback.`,
|
|
420
|
+
"",
|
|
421
|
+
`Use this form:`,
|
|
422
|
+
` onclick={yield* UpvotePost(id)}`,
|
|
423
|
+
"",
|
|
424
|
+
`Instead of this form:`,
|
|
425
|
+
` onclick={() => yield* UpvotePost(id)}`,
|
|
426
|
+
"",
|
|
427
|
+
`Problematic expression:`,
|
|
428
|
+
expression_text
|
|
429
|
+
].join("\n"), filename);
|
|
430
|
+
this.name = "YieldStarInEventCallbackError";
|
|
431
|
+
this.expression_text = expression_text;
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
//#endregion
|
|
232
435
|
//#region src/markup/transform/expressions.ts
|
|
233
436
|
/**
|
|
234
437
|
* Strips an event handler arrow function down to its executable body.
|
|
@@ -273,6 +476,20 @@ function strip_arrow_function(expr) {
|
|
|
273
476
|
};
|
|
274
477
|
}
|
|
275
478
|
/**
|
|
479
|
+
* Returns whether an expression is a callback function.
|
|
480
|
+
*
|
|
481
|
+
* @since 2.0.0
|
|
482
|
+
* @param expr - Expression text from a markup attribute or expression tag.
|
|
483
|
+
* @returns Whether the expression parses as an arrow or function expression.
|
|
484
|
+
*/
|
|
485
|
+
function is_callback_function_expression(expr) {
|
|
486
|
+
const wrapped = `const __ser_callback = ${expr};`;
|
|
487
|
+
const stmt = ts.createSourceFile("callback.ts", wrapped, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS).statements[0];
|
|
488
|
+
if (!ts.isVariableStatement(stmt)) return false;
|
|
489
|
+
const initializer = stmt.declarationList.declarations[0]?.initializer;
|
|
490
|
+
return initializer !== void 0 && (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer));
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
276
493
|
* Classifies `yield*` placement inside an event handler body.
|
|
277
494
|
*
|
|
278
495
|
* @example
|
|
@@ -336,7 +553,7 @@ function visit_ids(node, seen, ids) {
|
|
|
336
553
|
node.forEachChild((child) => visit_ids(child, seen, ids));
|
|
337
554
|
}
|
|
338
555
|
function visit_event_body(node, context, result) {
|
|
339
|
-
if (is_yield_star_expression(node)) {
|
|
556
|
+
if (is_yield_star_expression$1(node)) {
|
|
340
557
|
if (context === "top_level") result.has_top_level_yield_star = true;
|
|
341
558
|
else if (context === "nested_invalid") result.has_nested_invalid_yield_star = true;
|
|
342
559
|
node.forEachChild((child) => visit_event_body(child, context, result));
|
|
@@ -355,7 +572,7 @@ function is_nested_function_boundary(node) {
|
|
|
355
572
|
function is_generator_function_boundary(node) {
|
|
356
573
|
return (ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) && node.asteriskToken !== void 0;
|
|
357
574
|
}
|
|
358
|
-
function is_yield_star_expression(node) {
|
|
575
|
+
function is_yield_star_expression$1(node) {
|
|
359
576
|
if (ts.isYieldExpression(node)) return node.asteriskToken !== void 0;
|
|
360
577
|
return ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.AsteriskToken && ts.isIdentifier(node.left) && node.left.text === "yield";
|
|
361
578
|
}
|
|
@@ -395,13 +612,12 @@ function emit_replacement(candidate, kind) {
|
|
|
395
612
|
replacement_text = emit_each_expression(id_text, effect);
|
|
396
613
|
helpers = [effect.helper];
|
|
397
614
|
} else if (kind === "event") {
|
|
398
|
-
|
|
399
|
-
replacement_text = `${event.params} => { void ${HELPERS.run}(function* () { ${event.body}; }); }`;
|
|
615
|
+
replacement_text = make_event_handler(candidate).text;
|
|
400
616
|
helpers = [];
|
|
401
617
|
relocation = make_relocation(candidate, replacement_text, {
|
|
402
|
-
originalStart:
|
|
403
|
-
originalEnd:
|
|
404
|
-
generatedText:
|
|
618
|
+
originalStart: 0,
|
|
619
|
+
originalEnd: candidate.expr_text.length,
|
|
620
|
+
generatedText: candidate.expr_text
|
|
405
621
|
});
|
|
406
622
|
} else {
|
|
407
623
|
const effect = make_effect_helper(candidate, helper_name);
|
|
@@ -416,6 +632,11 @@ function emit_replacement(candidate, kind) {
|
|
|
416
632
|
relocation
|
|
417
633
|
};
|
|
418
634
|
}
|
|
635
|
+
function make_event_handler(candidate) {
|
|
636
|
+
if (is_callback_function_expression(candidate.expr_text)) throw new YieldStarInEventCallbackError(candidate.filename, candidate.expr_text);
|
|
637
|
+
if (analyze_event_body_yield_star(candidate.expr_text).has_nested_invalid_yield_star) throw new AsyncEffectInEventCallbackError(candidate.filename, candidate.expr_text);
|
|
638
|
+
return { text: `(event) => { void ${HELPERS.run}(function* () { ${candidate.expr_text}; }); }` };
|
|
639
|
+
}
|
|
419
640
|
function emit_promise_expression(id_text, effect) {
|
|
420
641
|
return `${HELPERS.promise}(${id_text}, ${effect.deps_text}, () => ${effect.call})`;
|
|
421
642
|
}
|
|
@@ -464,92 +685,90 @@ function make_relocation(candidate, replacement_text, inner) {
|
|
|
464
685
|
};
|
|
465
686
|
}
|
|
466
687
|
//#endregion
|
|
467
|
-
//#region src/
|
|
688
|
+
//#region src/preprocess/ast.ts
|
|
468
689
|
/**
|
|
469
|
-
*
|
|
470
|
-
* markup transformation. Carries the source filename so error messages can
|
|
471
|
-
* reference the affected file.
|
|
690
|
+
* Checks whether a node is a `yield*` binary expression.
|
|
472
691
|
*
|
|
473
692
|
* @since 2.0.0
|
|
693
|
+
* @param node - TypeScript AST node to check.
|
|
694
|
+
* @returns Whether the node represents `yield * operand`.
|
|
474
695
|
*/
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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
|
-
};
|
|
696
|
+
function is_yield_star_expression(node) {
|
|
697
|
+
return ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.AsteriskToken && ts.isIdentifier(node.left) && node.left.text === "yield";
|
|
698
|
+
}
|
|
488
699
|
/**
|
|
489
|
-
*
|
|
490
|
-
* Top-level `await` is not supported — users should use
|
|
491
|
-
* `yield* Effect.promise(...)` or `yield* Effect.tryPromise(...)` instead.
|
|
700
|
+
* Checks whether a node owns its own yield semantics.
|
|
492
701
|
*
|
|
493
702
|
* @since 2.0.0
|
|
703
|
+
* @param node - TypeScript AST node to check.
|
|
704
|
+
* @returns Whether traversal should stop at this function boundary.
|
|
494
705
|
*/
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
706
|
+
function is_function_boundary_node(node) {
|
|
707
|
+
return ts.isArrowFunction(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isMethodDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node);
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Returns `true` if the node tree contains a top-level `await`.
|
|
711
|
+
*
|
|
712
|
+
* @since 2.0.0
|
|
713
|
+
* @param node - Root node to search.
|
|
714
|
+
* @returns Whether a top-level await expression was found.
|
|
715
|
+
*/
|
|
716
|
+
function contains_top_level_await(node) {
|
|
717
|
+
if (ts.isAwaitExpression(node)) return true;
|
|
718
|
+
return node.getChildren().some((child) => !is_function_boundary_node(child) && contains_top_level_await(child));
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Collects top-level `yield*` nodes under an expression.
|
|
722
|
+
*
|
|
723
|
+
* @since 2.0.0
|
|
724
|
+
* @param node - Root node to search.
|
|
725
|
+
* @param on_found - Callback invoked for each matching yield node.
|
|
726
|
+
* @returns Nothing.
|
|
727
|
+
*/
|
|
728
|
+
function collect_yield_star_nodes(node, on_found) {
|
|
729
|
+
if (is_function_boundary_node(node)) return;
|
|
730
|
+
if (is_yield_star_expression(node)) {
|
|
731
|
+
on_found(node);
|
|
732
|
+
return;
|
|
512
733
|
}
|
|
513
|
-
|
|
734
|
+
node.forEachChild((child) => {
|
|
735
|
+
collect_yield_star_nodes(child, on_found);
|
|
736
|
+
});
|
|
737
|
+
}
|
|
514
738
|
/**
|
|
515
|
-
*
|
|
516
|
-
* is not itself a generator function.
|
|
739
|
+
* Finds the first top-level `yield*` expression below a node.
|
|
517
740
|
*
|
|
518
|
-
* @
|
|
519
|
-
*
|
|
520
|
-
*
|
|
521
|
-
*
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
741
|
+
* @since 2.0.0
|
|
742
|
+
* @param node - Root node to search.
|
|
743
|
+
* @param on_found - Callback invoked with the first matching node.
|
|
744
|
+
* @returns Nothing.
|
|
745
|
+
*/
|
|
746
|
+
function find_yield_star_node(node, on_found) {
|
|
747
|
+
if (is_function_boundary_node(node)) return;
|
|
748
|
+
if (is_yield_star_expression(node)) {
|
|
749
|
+
on_found(node);
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
node.forEachChild((child) => {
|
|
753
|
+
find_yield_star_node(child, on_found);
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Extracts identifier names from a TypeScript binding name.
|
|
525
758
|
*
|
|
526
759
|
* @since 2.0.0
|
|
760
|
+
* @param name - Binding name node to flatten.
|
|
761
|
+
* @returns Identifier names from identifiers and destructuring patterns.
|
|
527
762
|
*/
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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;
|
|
763
|
+
function extract_binding_names(name) {
|
|
764
|
+
if (ts.isIdentifier(name)) return [name.text];
|
|
765
|
+
const result = [];
|
|
766
|
+
for (const element of name.elements) {
|
|
767
|
+
if (ts.isOmittedExpression(element)) continue;
|
|
768
|
+
result.push(...extract_binding_names(element.name));
|
|
551
769
|
}
|
|
552
|
-
|
|
770
|
+
return result;
|
|
771
|
+
}
|
|
553
772
|
//#endregion
|
|
554
773
|
//#region src/markup/transform/scan.ts
|
|
555
774
|
function sanitize_markup(content, filename) {
|
|
@@ -575,20 +794,20 @@ function sanitize_markup(content, filename) {
|
|
|
575
794
|
const trimmed = inner.trimStart();
|
|
576
795
|
const leading_ws = inner.length - trimmed.length;
|
|
577
796
|
const tag_info = get_tag_info(trimmed);
|
|
578
|
-
const
|
|
579
|
-
if (
|
|
580
|
-
for (const
|
|
797
|
+
const declaration_yields = collect_declaration_yield_expressions(content, open, leading_ws, trimmed);
|
|
798
|
+
if (declaration_yields.length > 0) {
|
|
799
|
+
for (const declaration_yield of declaration_yields) {
|
|
581
800
|
const placeholder = `__ser_markup_placeholder_${helper_index}`;
|
|
582
801
|
helper_index += 1;
|
|
583
802
|
candidates.push({
|
|
584
803
|
placeholder,
|
|
585
|
-
start:
|
|
586
|
-
end:
|
|
587
|
-
expr_text:
|
|
804
|
+
start: declaration_yield.start,
|
|
805
|
+
end: declaration_yield.end,
|
|
806
|
+
expr_text: declaration_yield.expr_text,
|
|
588
807
|
filename,
|
|
589
808
|
key: "plain"
|
|
590
809
|
});
|
|
591
|
-
magic.overwrite(
|
|
810
|
+
magic.overwrite(declaration_yield.start, declaration_yield.end, placeholder);
|
|
592
811
|
}
|
|
593
812
|
cursor = close + 1;
|
|
594
813
|
continue;
|
|
@@ -596,7 +815,7 @@ function sanitize_markup(content, filename) {
|
|
|
596
815
|
let expr_body = trimmed.slice(tag_info.prefix_length);
|
|
597
816
|
/** For @const, only use the RHS after `=` as the expression body. */
|
|
598
817
|
const equal_idx = tag_info.kind === "plain" && trimmed.startsWith("@const ") ? expr_body.indexOf("=") : -1;
|
|
599
|
-
if (!((
|
|
818
|
+
if (!((is_event_callback_expression(inner) ? analyze_event_yield(inner) : void 0)?.has_top_level_yield_star ?? contains_yield_star_in_text(expr_body))) {
|
|
600
819
|
cursor = close + 1;
|
|
601
820
|
continue;
|
|
602
821
|
}
|
|
@@ -742,35 +961,40 @@ function get_tag_info(trimmed) {
|
|
|
742
961
|
prefix_length: 0
|
|
743
962
|
};
|
|
744
963
|
}
|
|
745
|
-
function
|
|
964
|
+
function collect_declaration_yield_expressions(content, open, leading_ws, trimmed) {
|
|
746
965
|
if (!is_declaration_tag_text(trimmed)) return [];
|
|
747
966
|
const source_file = ts.createSourceFile("declaration-tag.ts", `${trimmed};`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
748
967
|
const stmt = source_file.statements[0];
|
|
749
968
|
if (!stmt || !ts.isVariableStatement(stmt)) return [];
|
|
750
969
|
const tag_start = open + 1 + leading_ws;
|
|
751
|
-
return stmt.declarationList.declarations.
|
|
970
|
+
return stmt.declarationList.declarations.flatMap((decl) => {
|
|
752
971
|
const initializer = decl.initializer;
|
|
753
|
-
|
|
754
|
-
const
|
|
755
|
-
|
|
756
|
-
start
|
|
757
|
-
end
|
|
758
|
-
expr_text
|
|
759
|
-
|
|
972
|
+
if (!initializer || !contains_top_level_yield_star(initializer)) return [];
|
|
973
|
+
const expressions = [];
|
|
974
|
+
collect_yield_star_nodes(initializer, (yield_node) => {
|
|
975
|
+
const start = tag_start + yield_node.getStart(source_file);
|
|
976
|
+
const end = tag_start + yield_node.end;
|
|
977
|
+
const expr_text = content.slice(start, end).trim();
|
|
978
|
+
expressions.push({
|
|
979
|
+
start,
|
|
980
|
+
end,
|
|
981
|
+
expr_text
|
|
982
|
+
});
|
|
983
|
+
});
|
|
984
|
+
return expressions;
|
|
760
985
|
});
|
|
761
986
|
}
|
|
762
987
|
function is_declaration_tag_text(trimmed) {
|
|
763
988
|
return /^(?:const|let)\s/.test(trimmed);
|
|
764
989
|
}
|
|
765
|
-
function
|
|
990
|
+
function is_event_callback_expression(inner) {
|
|
766
991
|
const trimmed = inner.trimStart();
|
|
767
|
-
return /^(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/.test(trimmed);
|
|
992
|
+
return /^(?:async\s+)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/.test(trimmed) || /^(?:async\s+)?function\b/.test(trimmed);
|
|
768
993
|
}
|
|
769
|
-
function analyze_event_yield(inner
|
|
994
|
+
function analyze_event_yield(inner) {
|
|
770
995
|
const event = strip_arrow_function(inner);
|
|
771
996
|
const analysis = analyze_event_body_yield_star(event.body);
|
|
772
|
-
|
|
773
|
-
return { has_top_level_yield_star: analysis.has_top_level_yield_star };
|
|
997
|
+
return { has_top_level_yield_star: analysis.has_top_level_yield_star || analysis.has_nested_invalid_yield_star || /\byield\s*\*/.test(event.body) };
|
|
774
998
|
}
|
|
775
999
|
function contains_yield_star_in_text(text) {
|
|
776
1000
|
if (!/\byield\s*\*/.test(text)) return false;
|
|
@@ -831,6 +1055,6 @@ function transform_markup_effect(content, filename) {
|
|
|
831
1055
|
};
|
|
832
1056
|
}
|
|
833
1057
|
//#endregion
|
|
834
|
-
export {
|
|
1058
|
+
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 };
|
|
835
1059
|
|
|
836
|
-
//# sourceMappingURL=transform-
|
|
1060
|
+
//# sourceMappingURL=transform-B4g76Ur4.js.map
|