Rich Harris 8 months ago
parent 53b639de83
commit b0a08f5034

@ -265,7 +265,8 @@ export function analyze_module(ast, options) {
runes: true,
immutable: true,
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(),
snippets: new Set(),
is_async: false,
async_deriveds: new Set()
async_deriveds: new Set(),
blocking_awaits: new Set()
};
if (!runes) {

@ -6,15 +6,22 @@
* @param {Context} context
*/
export function AwaitExpression(node, context) {
if (!context.state.analysis.runes) {
throw new Error('TODO runes mode only');
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) {
throw new Error('TODO runes mode only');
}
context.state.analysis.blocking_awaits.add(node);
}
if (context.state.expression) {
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;
}

@ -15,6 +15,7 @@ export function visit_function(node, context) {
context.next({
...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
*/
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(
b.member(
b.await(
b.call(
'$.preserve_context',
node.argument && /** @type {Expression} */ (context.visit(node.argument))
node.argument && /** @type {Expression} */ (context.visit(node.argument)),
block && b.true
)
),
'read'

@ -1,5 +1,12 @@
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';
export interface Js {
@ -34,6 +41,9 @@ export interface Analysis {
/** A set of deriveds that contain `await` expressions */
async_deriveds: Set<CallExpression>;
/** A set of `await` expressions that should trigger suspense */
blocking_awaits: Set<AwaitExpression>;
}
export interface ComponentAnalysis extends Analysis {

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

Loading…
Cancel
Save