docs + tidy

pull/17038/head
Simon Holthausen 3 weeks ago
parent 716640141e
commit b9a6a16269

@ -770,7 +770,7 @@ export function analyze_component(root, source, options) {
// for now, assume everything touched by the callee ends up mutating the object
// TODO optimise this better
// special case — no need to peek inside effects
// special case — no need to peek inside effects as they only run once async work has completed
const rune = get_rune(node, context.state.scope);
if (rune === '$effect') return;

@ -1,5 +1,5 @@
/** @import { Node, Program } from 'estree' */
/** @import { Context, ComponentContext } from '../types' */
/** @import { Context, ComponentServerTransformState } from '../types' */
import * as b from '#compiler/builders';
import { transform_body } from '../../shared/transform-async.js';
@ -9,13 +9,12 @@ import { transform_body } from '../../shared/transform-async.js';
*/
export function Program(node, context) {
if (context.state.is_instance) {
// @ts-ignore wtf
const c = /** @type {ComponentContext} */ (context);
const state = /** @type {ComponentServerTransformState} */ (context.state);
return {
...node,
body: transform_body(
c.state.analysis.instance_body,
state.analysis.instance_body,
b.id('$$renderer.run'),
(node) => /** @type {Node} */ (context.visit(node))
)

@ -1,5 +1,5 @@
/** @import { Expression, Identifier, Node, Statement, BlockStatement, ArrayExpression } from 'estree' */
/** @import { AST, Binding } from '#compiler' */
/** @import { AST } from '#compiler' */
/** @import { ComponentContext, ServerTransformState } from '../../types.js' */
import { escape_html } from '../../../../../../escaping.js';
@ -276,12 +276,17 @@ export function create_child_block(body, async) {
* @param {BlockStatement | Expression} body
* @param {ArrayExpression} blockers
* @param {boolean} has_await
* @param {boolean} markers
* @param {boolean} needs_hydration_markers
*/
export function create_async_block(body, blockers = b.array([]), has_await = true, markers = true) {
export function create_async_block(
body,
blockers = b.array([]),
has_await = true,
needs_hydration_markers = true
) {
return b.stmt(
b.call(
markers ? '$$renderer.async_block' : '$$renderer.async',
needs_hydration_markers ? '$$renderer.async_block' : '$$renderer.async',
blockers,
b.arrow([b.id('$$renderer')], body, has_await)
)
@ -291,17 +296,22 @@ export function create_async_block(body, blockers = b.array([]), has_await = tru
/**
* @param {Expression} expression
* @param {ExpressionMetadata} metadata
* @param {boolean} markers
* @param {boolean} needs_hydration_markers
* @returns {Expression | Statement}
*/
export function create_push(expression, metadata, markers = false) {
export function create_push(expression, metadata, needs_hydration_markers = false) {
if (metadata.is_async()) {
let statement = b.stmt(b.call('$$renderer.push', b.thunk(expression, metadata.has_await)));
const blockers = metadata.blockers();
if (blockers.elements.length > 0) {
statement = create_async_block(b.block([statement]), blockers, false, markers);
statement = create_async_block(
b.block([statement]),
blockers,
false,
needs_hydration_markers
);
}
return statement;
@ -321,6 +331,12 @@ export function call_component_renderer(body, component_fn_id) {
);
}
/**
* A utility for optimising promises in templates. Without it code like
* `<Component foo={await fetch()} bar={await other()} />` would be transformed
* into two blocking promises, with it it's using `Promise.all` to await them.
* It also keeps track of blocking promises, i.e. those that need to be resolved before continuing.
*/
export class PromiseOptimiser {
/** @type {Expression[]} */
expressions = [];

@ -3,16 +3,39 @@
import * as b from '#compiler/builders';
/**
* Transforms the body of the instance script in such a way that await expressions are made non-blocking as much as possible.
*
* Example Transformation:
* ```js
* let x = 1;
* let data = await fetch('/api');
* let y = data.value;
* ```
* becomes:
* ```js
* let x = 1;
* var data, y;
* var $$promises = $.run([
* () => data = await fetch('/api'),
* () => y = data.value
* ]);
* ```
* where `$$promises` is an array of promises that are resolved in the order they are declared,
* and which expressions in the template can await on like `await $$promises[0]` which means they
* wouldn't have to wait for e.g. `$$promises[1]` to resolve.
*
* @param {ComponentAnalysis['instance_body']} instance_body
* @param {ESTree.Expression} runner
* @param {(node: ESTree.Node) => ESTree.Node} transform
* @returns {Array<ESTree.Statement | ESTree.VariableDeclaration>}
*/
export function transform_body(instance_body, runner, transform) {
// Any sync statements before the first await expression
const statements = instance_body.sync.map(
(node) => /** @type {ESTree.Statement | ESTree.VariableDeclaration} */ (transform(node))
);
// Declarations for the await expressions (they will asign to them; need to be hoisted to be available in whole instance scope)
if (instance_body.declarations.length > 0) {
statements.push(
b.declaration(
@ -22,6 +45,7 @@ export function transform_body(instance_body, runner, transform) {
);
}
// Thunks for the await expressions
if (instance_body.async.length > 0) {
const thunks = instance_body.async.map((s) => {
if (s.node.type === 'VariableDeclarator') {

@ -6,8 +6,7 @@ import {
read_hydration_instruction,
skip_nodes,
set_hydrate_node,
set_hydrating,
hydrate_node
set_hydrating
} from '../hydration.js';
import { block } from '../../reactivity/effects.js';
import { HYDRATION_START_ELSE } from '../../../../constants.js';

@ -480,10 +480,10 @@ function set_attributes(
/**
* @param {Element & ElementCSSInlineStyle} element
* @param {Array<Promise<void>>} blockers
* @param {(...expressions: any) => Record<string | symbol, any>} fn
* @param {Array<() => any>} sync
* @param {Array<() => Promise<any>>} async
* @param {Array<Promise<void>>} blockers
* @param {string} [css_hash]
* @param {boolean} [should_remove_defaults]
* @param {boolean} [skip_warning]

@ -25,14 +25,7 @@ import {
set_from_async_derived
} from './deriveds.js';
import { aborted } from './effects.js';
import {
hydrate_next,
hydrate_node,
hydrating,
set_hydrate_node,
set_hydrating,
skip_nodes
} from '../dom/hydration.js';
import { hydrate_next, hydrating, set_hydrate_node, skip_nodes } from '../dom/hydration.js';
/**
* @param {Array<Promise<void>>} blockers

@ -34,7 +34,7 @@ import * as e from '../errors.js';
import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js';
import { get_stack, tag_proxy } from '../dev/tracing.js';
import { component_context, is_runes } from '../context.js';
import { Batch, current_batch, eager_block_effects, schedule_effect } from './batch.js';
import { Batch, eager_block_effects, schedule_effect } from './batch.js';
import { proxy } from '../proxy.js';
import { execute_derived } from './deriveds.js';

Loading…
Cancel
Save