pull/17038/head
Rich Harris 4 weeks ago
parent c34348e44e
commit 795fa1ab25

@ -10,7 +10,7 @@ import { extract_identifiers, has_await_expression } from '../../utils/ast.js';
import * as b from '#compiler/builders';
import { Scope, ScopeRoot, create_scopes, get_rune, set_scope } from '../scope.js';
import check_graph_for_cycles from './utils/check_graph_for_cycles.js';
import { create_attribute, is_custom_element_node } from '../nodes.js';
import { create_attribute, create_expression_metadata, is_custom_element_node } from '../nodes.js';
import { analyze_css } from './css/css-analyze.js';
import { prune } from './css/css-prune.js';
import { hash, is_rune } from '../../../utils.js';
@ -131,7 +131,18 @@ const visitors = {
ignore_map.set(node, structuredClone(ignore_stack));
const scope = state.scopes.get(node);
next(scope !== undefined && scope !== state.scope ? { ...state, scope } : state);
const awaited = state.analysis.awaited_statements.get(node) ?? null;
if (awaited || (scope !== undefined && scope !== state.scope)) {
const child_state = { ...state };
if (awaited) child_state.expression = awaited.metadata;
if (scope !== undefined && scope !== state.scope) child_state.scope = scope;
next(child_state);
} else {
next();
}
if (ignores.length > 0) {
pop_ignore();
@ -707,12 +718,19 @@ export function analyze_component(root, source, options) {
const id = b.id(`$$${i++}`);
analysis.awaited_statements.set(declarator, {
id,
has_await,
metadata: create_expression_metadata()
});
for (const identifier of extract_identifiers(declarator.id)) {
analysis.awaited_declarations.set(identifier.name, {
id,
has_await,
pattern: declarator.id,
updated_by: new Set()
updated_by: new Set(),
metadata: create_expression_metadata()
});
}
}
@ -729,7 +747,8 @@ export function analyze_component(root, source, options) {
analysis.awaited_statements.set(node, {
id,
has_await
has_await,
metadata: create_expression_metadata()
});
}
}

@ -370,41 +370,22 @@ export function client_component(analysis, options) {
analysis.reactive_statements.size > 0 ||
component_returned_object.length > 0;
if (analysis.instance.has_await) {
if (should_inject_context && component_returned_object.length > 0) {
component_block.body.push(b.var('$$exports'));
}
const body = b.block([
...store_setup,
...state.instance_level_snippets,
.../** @type {ESTree.Statement[]} */ (instance.body),
...(should_inject_context && component_returned_object.length > 0
? [b.stmt(b.assignment('=', b.id('$$exports'), b.object(component_returned_object)))]
: []),
b.if(b.call('$.aborted'), b.return()),
.../** @type {ESTree.Statement[]} */ (template.body)
]);
component_block.body.push(
b.stmt(b.call(`$.async_body`, b.id('$$anchor'), b.arrow([b.id('$$anchor')], body, true)))
);
} else {
component_block.body.push(
...state.instance_level_snippets,
.../** @type {ESTree.Statement[]} */ (instance.body)
);
if (should_inject_context && component_returned_object.length > 0) {
component_block.body.push(b.var('$$exports', b.object(component_returned_object)));
}
component_block.body.unshift(...store_setup);
component_block.body.push(
...state.instance_level_snippets,
.../** @type {ESTree.Statement[]} */ (instance.body)
);
if (!analysis.runes && analysis.needs_context) {
component_block.body.push(b.stmt(b.call('$.init', analysis.immutable ? b.true : undefined)));
}
if (should_inject_context && component_returned_object.length > 0) {
component_block.body.push(b.var('$$exports', b.object(component_returned_object)));
}
component_block.body.unshift(...store_setup);
component_block.body.push(.../** @type {ESTree.Statement[]} */ (template.body));
if (!analysis.runes && analysis.needs_context) {
component_block.body.push(b.stmt(b.call('$.init', analysis.immutable ? b.true : undefined)));
}
component_block.body.push(.../** @type {ESTree.Statement[]} */ (template.body));
if (analysis.needs_mutation_validation) {
component_block.body.unshift(
b.var('$$ownership_validator', b.call('$.create_ownership_validator', b.id('$$props')))

@ -1,14 +1,14 @@
/** @import { Expression, ImportDeclaration, MemberExpression, Program } from 'estree' */
/** @import { Expression, Identifier, ImportDeclaration, MemberExpression, Program, Statement } from 'estree' */
/** @import { ComponentContext } from '../types' */
import { build_getter, is_prop_source } from '../utils.js';
import * as b from '#compiler/builders';
import { add_state_transformers } from './shared/declarations.js';
/**
* @param {Program} _
* @param {Program} node
* @param {ComponentContext} context
*/
export function Program(_, context) {
export function Program(node, context) {
if (!context.state.analysis.runes) {
context.state.transform['$$props'] = {
read: (node) => ({ ...node, name: '$$sanitized_props' })
@ -137,5 +137,115 @@ export function Program(_, context) {
add_state_transformers(context);
if (context.state.is_instance) {
return {
...node,
body: transform_body(node, context)
};
}
context.next();
}
/**
* @param {Program} program
* @param {ComponentContext} context
*/
function transform_body(program, context) {
/** @type {Statement[]} */
const out = [];
const { awaited_declarations, awaited_statements } = context.state.analysis;
/** @type {Identifier | null} */
let last = null;
for (let node of program.body) {
if (node.type === 'ImportDeclaration') {
// TODO we can get rid of the visitor
context.state.hoisted.push(node);
continue;
}
if (node.type === 'ExportDefaultDeclaration' || node.type === 'ExportAllDeclaration') {
// this can't happen, but it's useful for TypeScript to understand that
continue;
}
if (node.type === 'ExportNamedDeclaration') {
if (node.declaration) {
// TODO ditto — no visitor needed
node = node.declaration;
} else {
continue;
}
}
if (node.type === 'VariableDeclaration') {
for (const declarator of node.declarations) {
const awaited = awaited_statements.get(declarator);
if (awaited) {
// TODO dependencies
out.push(
b.var(
awaited.id,
b.call(
'$.run',
b.array([]),
b.arrow([], declarator.init ?? b.block([]), awaited.has_await)
)
)
);
last = awaited.id;
} else {
out.push(b.var(declarator.id, declarator.init));
}
}
} else if (node.type === 'ClassDeclaration' || node.type === 'FunctionDeclaration') {
// TODO
} else {
const awaited = awaited_statements.get(node);
if (awaited) {
const ids = new Set();
const patterns = new Set();
for (const binding of awaited.metadata.dependencies) {
const dep = awaited_declarations.get(binding.node.name);
if (dep && !ids.has(dep.id)) {
ids.add(dep.id);
patterns.add(dep.pattern);
}
}
if (last) {
ids.add(last);
}
// TODO dependencies
out.push(
b.var(
awaited.id,
b.call(
'$.run',
b.array([...ids]),
b.arrow(
[...patterns],
node.type === 'ExpressionStatement' ? node.expression : b.block([node]),
awaited.has_await
)
)
)
);
last = awaited.id;
} else {
out.push(node);
}
}
}
return out.map((node) => /** @type {Statement} */ (context.visit(node)));
}

@ -1,4 +1,4 @@
import type { AST, Binding, StateField } from '#compiler';
import type { AST, Binding, ExpressionMetadata, StateField } from '#compiler';
import type {
AwaitExpression,
CallExpression,
@ -8,7 +8,8 @@ import type {
ModuleDeclaration,
Pattern,
Program,
Statement
Statement,
VariableDeclarator
} from 'estree';
import type { Scope, ScopeRoot } from './scope.js';
@ -126,11 +127,20 @@ export interface ComponentAnalysis extends Analysis {
*/
awaited_declarations: Map<
string,
{ id: Identifier; has_await: boolean; pattern: Pattern; updated_by: Set<Identifier> }
{
id: Identifier;
has_await: boolean;
pattern: Pattern;
metadata: ExpressionMetadata;
updated_by: Set<Identifier>;
}
>;
/**
* Information about top-level instance statements that need to be transformed
* so that we can run the template synchronously
*/
awaited_statements: Map<Statement | ModuleDeclaration, { id: Identifier; has_await: boolean }>;
awaited_statements: Map<
Statement | ModuleDeclaration | VariableDeclarator,
{ id: Identifier; has_await: boolean; metadata: ExpressionMetadata }
>;
}

@ -1,4 +1,4 @@
/** @import { AST } from '#compiler' */
/** @import { AST, Scope } from '#compiler' */
/** @import * as ESTree from 'estree' */
import { walk } from 'zimmerframe';
import * as b from '#compiler/builders';

@ -184,7 +184,7 @@ export function declaration(kind, declarations) {
/**
* @param {ESTree.Pattern | string} pattern
* @param {ESTree.Expression} [init]
* @param {ESTree.Expression | null} [init]
* @returns {ESTree.VariableDeclarator}
*/
export function declarator(pattern, init) {
@ -520,7 +520,7 @@ const this_instance = {
/**
* @param {string | ESTree.Pattern} pattern
* @param { ESTree.Expression} [init]
* @param {ESTree.Expression | null} [init]
* @returns {ESTree.VariableDeclaration}
*/
function let_builder(pattern, init) {
@ -529,7 +529,7 @@ function let_builder(pattern, init) {
/**
* @param {string | ESTree.Pattern} pattern
* @param { ESTree.Expression} init
* @param {ESTree.Expression | null} init
* @returns {ESTree.VariableDeclaration}
*/
function const_builder(pattern, init) {
@ -538,7 +538,7 @@ function const_builder(pattern, init) {
/**
* @param {string | ESTree.Pattern} pattern
* @param { ESTree.Expression} [init]
* @param {ESTree.Expression | null} [init]
* @returns {ESTree.VariableDeclaration}
*/
function var_builder(pattern, init) {

Loading…
Cancel
Save