Rich Harris 8 months ago
parent 53b639de83
commit b0a08f5034

@ -265,7 +265,8 @@ export function analyze_module(ast, options) {
runes: true, runes: true,
immutable: true, immutable: true,
tracing: analysis.tracing, tracing: analysis.tracing,
async_deriveds: new Set() async_deriveds: new Set(),
blocking_awaits: new Set()
}; };
} }
@ -453,7 +454,8 @@ export function analyze_component(root, source, options) {
snippet_renderers: new Map(), snippet_renderers: new Map(),
snippets: new Set(), snippets: new Set(),
is_async: false, is_async: false,
async_deriveds: new Set() async_deriveds: new Set(),
blocking_awaits: new Set()
}; };
if (!runes) { if (!runes) {

@ -6,15 +6,22 @@
* @param {Context} context * @param {Context} context
*/ */
export function AwaitExpression(node, context) { export function AwaitExpression(node, context) {
const tla = context.state.ast_type === 'instance' && context.state.function_depth === 1;
const blocking = tla || !!context.state.expression;
if (blocking) {
if (!context.state.analysis.runes) { if (!context.state.analysis.runes) {
throw new Error('TODO runes mode only'); throw new Error('TODO runes mode only');
} }
context.state.analysis.blocking_awaits.add(node);
}
if (context.state.expression) { if (context.state.expression) {
context.state.expression.is_async = true; context.state.expression.is_async = true;
} }
if (context.state.ast_type === 'instance' && context.state.scope.function_depth === 1) { if (tla) {
context.state.analysis.is_async = true; context.state.analysis.is_async = true;
} }

@ -15,6 +15,7 @@ export function visit_function(node, context) {
context.next({ context.next({
...context.state, ...context.state,
function_depth: context.state.function_depth + 1 function_depth: context.state.function_depth + 1,
expression: null
}); });
} }

@ -7,12 +7,19 @@ import * as b from '../../../../utils/builders.js';
* @param {ComponentContext} context * @param {ComponentContext} context
*/ */
export function AwaitExpression(node, context) { export function AwaitExpression(node, context) {
if (!context.state.analysis.runes) {
return context.next();
}
const block = context.state.analysis.blocking_awaits.has(node);
return b.call( return b.call(
b.member( b.member(
b.await( b.await(
b.call( b.call(
'$.preserve_context', '$.preserve_context',
node.argument && /** @type {Expression} */ (context.visit(node.argument)) node.argument && /** @type {Expression} */ (context.visit(node.argument)),
block && b.true
) )
), ),
'read' 'read'

@ -1,5 +1,12 @@
import type { AST, Binding } from '#compiler'; import type { AST, Binding } from '#compiler';
import type { CallExpression, Identifier, LabeledStatement, Node, Program } from 'estree'; import type {
AwaitExpression,
CallExpression,
Identifier,
LabeledStatement,
Node,
Program
} from 'estree';
import type { Scope, ScopeRoot } from './scope.js'; import type { Scope, ScopeRoot } from './scope.js';
export interface Js { export interface Js {
@ -34,6 +41,9 @@ export interface Analysis {
/** A set of deriveds that contain `await` expressions */ /** A set of deriveds that contain `await` expressions */
async_deriveds: Set<CallExpression>; async_deriveds: Set<CallExpression>;
/** A set of `await` expressions that should trigger suspense */
blocking_awaits: Set<AwaitExpression>;
} }
export interface ComponentAnalysis extends Analysis { export interface ComponentAnalysis extends Analysis {

@ -247,14 +247,15 @@ export function trigger_async_boundary(effect, trigger) {
/** /**
* @template T * @template T
* @param {Promise<T>} promise * @param {Promise<T>} promise
* @param {boolean} block
* @returns {Promise<{ read: () => T }>} * @returns {Promise<{ read: () => T }>}
*/ */
export async function preserve_context(promise) { export function preserve_context(promise, block = false) {
var previous_effect = active_effect; var previous_effect = active_effect;
var previous_reaction = active_reaction; var previous_reaction = active_reaction;
var previous_component_context = component_context; var previous_component_context = component_context;
let boundary = active_effect; let boundary = block ? active_effect : null;
while (boundary !== null) { while (boundary !== null) {
if ((boundary.f & BOUNDARY_EFFECT) !== 0) { if ((boundary.f & BOUNDARY_EFFECT) !== 0) {
break; break;
@ -263,15 +264,14 @@ export async function preserve_context(promise) {
boundary = boundary.parent; boundary = boundary.parent;
} }
if (boundary === null) { if (block && boundary === null) {
throw new Error('cannot suspend outside a boundary'); throw new Error('cannot suspend outside a boundary');
} }
// @ts-ignore // @ts-ignore
boundary.fn(ASYNC_INCREMENT); boundary?.fn(ASYNC_INCREMENT);
const value = await promise;
return promise.then((value) => {
return { return {
read() { read() {
set_active_effect(previous_effect); set_active_effect(previous_effect);
@ -279,9 +279,10 @@ export async function preserve_context(promise) {
set_component_context(previous_component_context); set_component_context(previous_component_context);
// @ts-ignore // @ts-ignore
boundary.fn(ASYNC_DECREMENT); boundary?.fn(ASYNC_DECREMENT);
return value; return value;
} }
}; };
});
} }

Loading…
Cancel
Save