pull/16073/head
7nik 4 months ago
parent bcb6b72af2
commit ac9c130887

@ -12,14 +12,6 @@ export function AttachTag(node, context) {
const expression = context.state.analysis.runes const expression = context.state.analysis.runes
? /** @type {Expression} */ (context.visit(node.expression)) ? /** @type {Expression} */ (context.visit(node.expression))
: build_legacy_expression(node.expression, context); : build_legacy_expression(node.expression, context);
context.state.init.push( context.state.init.push(b.stmt(b.call('$.attach', context.state.node, b.thunk(expression))));
b.stmt(
b.call(
'$.attach',
context.state.node,
b.thunk(expression)
)
)
);
context.next(); context.next();
} }

@ -19,7 +19,7 @@ export function AwaitBlock(node, context) {
context.state.analysis.runes context.state.analysis.runes
? /** @type {Expression} */ (context.visit(node.expression)) ? /** @type {Expression} */ (context.visit(node.expression))
: build_legacy_expression(node.expression, context) : build_legacy_expression(node.expression, context)
); );
let then_block; let then_block;
let catch_block; let catch_block;

@ -19,15 +19,7 @@ export function ConstTag(node, context) {
const init = context.state.analysis.runes const init = context.state.analysis.runes
? /** @type {Expression} */ (context.visit(declaration.init)) ? /** @type {Expression} */ (context.visit(declaration.init))
: build_legacy_expression(declaration.init, context); : build_legacy_expression(declaration.init, context);
context.state.init.push( context.state.init.push(b.const(declaration.id, create_derived(context.state, b.thunk(init))));
b.const(
declaration.id,
create_derived(
context.state,
b.thunk(init)
)
)
);
context.state.transform[declaration.id.name] = { read: get_value }; context.state.transform[declaration.id.name] = { read: get_value };
@ -58,10 +50,7 @@ export function ConstTag(node, context) {
const fn = b.arrow( const fn = b.arrow(
[], [],
b.block([ b.block([
b.const( b.const(/** @type {Pattern} */ (context.visit(declaration.id, child_state)), init),
/** @type {Pattern} */ (context.visit(declaration.id, child_state)),
init,
),
b.return(b.object(identifiers.map((node) => b.prop('init', node, node)))) b.return(b.object(identifiers.map((node) => b.prop('init', node, node))))
]) ])
); );

@ -32,9 +32,9 @@ export function EachBlock(node, context) {
const collection = context.state.analysis.runes const collection = context.state.analysis.runes
? /** @type {Expression} */ (context.visit(node.expression, parent_scope_state)) ? /** @type {Expression} */ (context.visit(node.expression, parent_scope_state))
: build_legacy_expression(node.expression, { : build_legacy_expression(node.expression, {
...context, ...context,
state: parent_scope_state state: parent_scope_state
}); });
if (!each_node_meta.is_controlled) { if (!each_node_meta.is_controlled) {
context.state.template.push_comment(); context.state.template.push_comment();

@ -51,7 +51,6 @@ export function IfBlock(node, context) {
) )
]; ];
if (node.elseif) { if (node.elseif) {
// We treat this... // We treat this...
// //

@ -34,7 +34,7 @@ export function RenderTag(node, context) {
let snippet_function = context.state.analysis.runes let snippet_function = context.state.analysis.runes
? /** @type {Expression} */ (context.visit(callee)) ? /** @type {Expression} */ (context.visit(callee))
: build_legacy_expression(/** @type {Expression} */(callee), context); : build_legacy_expression(/** @type {Expression} */ (callee), context);
if (node.metadata.dynamic) { if (node.metadata.dynamic) {
// If we have a chain expression then ensure a nullish snippet function gets turned into an empty one // If we have a chain expression then ensure a nullish snippet function gets turned into an empty one

@ -370,41 +370,67 @@ function is_pure_expression(expression) {
// It's supposed that values do not have custom @@toPrimitive() or toString(), // It's supposed that values do not have custom @@toPrimitive() or toString(),
// which may be implicitly called in expressions like `a + b`, `a & b`, `+a`, `str: ${a}` // which may be implicitly called in expressions like `a + b`, `a & b`, `+a`, `str: ${a}`
switch (expression.type) { switch (expression.type) {
case "ArrayExpression": return expression.elements.every((element) => element == null || (element.type === "SpreadElement" ? false : is_pure_expression(element))); case 'ArrayExpression':
case "BinaryExpression": return expression.left.type !== "PrivateIdentifier" && is_pure_expression(expression.left) && is_pure_expression(expression.right); return expression.elements.every(
case "ConditionalExpression": return is_pure_expression(expression.test) && is_pure_expression(expression.consequent) && is_pure_expression(expression.alternate); (element) =>
case "Identifier": return true; element == null ||
case "Literal": return true; (element.type === 'SpreadElement' ? false : is_pure_expression(element))
case "LogicalExpression": return is_pure_expression(expression.left) && is_pure_expression(expression.right); );
case "MetaProperty": return true; // new.target case 'BinaryExpression':
case "ObjectExpression": return expression.properties.every((property) => return (
property.type !== "SpreadElement" expression.left.type !== 'PrivateIdentifier' &&
&& property.key.type !== "PrivateIdentifier" is_pure_expression(expression.left) &&
&& is_pure_expression(property.key) is_pure_expression(expression.right)
&& is_pure_expression(property.value) );
); case 'ConditionalExpression':
case "SequenceExpression": return expression.expressions.every(is_pure_expression); return (
case "TemplateLiteral": return expression.expressions.every(is_pure_expression); is_pure_expression(expression.test) &&
case "ThisExpression": return true; is_pure_expression(expression.consequent) &&
case "UnaryExpression": return is_pure_expression(expression.argument); is_pure_expression(expression.alternate)
case "YieldExpression": return expression.argument == null || is_pure_expression(expression.argument); );
case 'Identifier':
case "ArrayPattern": return true;
case "ArrowFunctionExpression": case 'Literal':
case "AssignmentExpression": return true;
case "AssignmentPattern": case 'LogicalExpression':
case "AwaitExpression": return is_pure_expression(expression.left) && is_pure_expression(expression.right);
case "CallExpression": case 'MetaProperty':
case "ChainExpression": return true; // new.target
case "ClassExpression": case 'ObjectExpression':
case "FunctionExpression": return expression.properties.every(
case "ImportExpression": (property) =>
case "MemberExpression": property.type !== 'SpreadElement' &&
case "NewExpression": property.key.type !== 'PrivateIdentifier' &&
case "ObjectPattern": is_pure_expression(property.key) &&
case "RestElement": is_pure_expression(property.value)
case "TaggedTemplateExpression": );
case "UpdateExpression": case 'SequenceExpression':
return expression.expressions.every(is_pure_expression);
case 'TemplateLiteral':
return expression.expressions.every(is_pure_expression);
case 'ThisExpression':
return true;
case 'UnaryExpression':
return is_pure_expression(expression.argument);
case 'YieldExpression':
return expression.argument == null || is_pure_expression(expression.argument);
case 'ArrayPattern':
case 'ArrowFunctionExpression':
case 'AssignmentExpression':
case 'AssignmentPattern':
case 'AwaitExpression':
case 'CallExpression':
case 'ChainExpression':
case 'ClassExpression':
case 'FunctionExpression':
case 'ImportExpression':
case 'MemberExpression':
case 'NewExpression':
case 'ObjectPattern':
case 'RestElement':
case 'TaggedTemplateExpression':
case 'UpdateExpression':
return false; return false;
} }
} }
@ -426,23 +452,27 @@ export function build_legacy_expression(expression, context) {
for (const [name, nodes] of context.state.scope.references) { for (const [name, nodes] of context.state.scope.references) {
const binding = context.state.scope.get(name); const binding = context.state.scope.get(name);
if (binding === null || binding.kind === 'normal' && binding.declaration_kind !== 'import') continue; if (binding === null || (binding.kind === 'normal' && binding.declaration_kind !== 'import'))
continue;
let used = false; let used = false;
for (const { node, path } of nodes) { for (const { node, path } of nodes) {
const expressionIdx = path.indexOf(expression); const expression_idx = path.indexOf(expression);
if (expressionIdx < 0) continue; if (expression_idx < 0) continue;
// in Svelte 4, #if, #each and #await copy context, so assignments // in Svelte 4, #if, #each and #await copy context, so assignments
// aren't propagated to the parent block / component root // aren't propagated to the parent block / component root
const track_assignment = !path.find((node, i) => const track_assignment = !path.find(
i < expressionIdx - 1 && ["IfBlock", "EachBlock", "AwaitBlock"].includes(node.type) (node, i) =>
) i < expression_idx - 1 && ['IfBlock', 'EachBlock', 'AwaitBlock'].includes(node.type)
);
if (track_assignment) { if (track_assignment) {
used = true; used = true;
break; break;
} }
const assignment = /** @type {AssignmentExpression|undefined} */(path.find((node, i) => i >= expressionIdx && node.type === "AssignmentExpression")); const assignment = /** @type {AssignmentExpression|undefined} */ (
if (!assignment || assignment.left !== node && !path.includes(assignment.left)) { path.find((node, i) => i >= expression_idx && node.type === 'AssignmentExpression')
);
if (!assignment || (assignment.left !== node && !path.includes(assignment.left))) {
used = true; used = true;
break; break;
} }
@ -461,4 +491,4 @@ export function build_legacy_expression(expression, context) {
} }
return b.sequence([...sequence, b.call('$.untrack', b.thunk(serialized_expression))]); return b.sequence([...sequence, b.call('$.untrack', b.thunk(serialized_expression))]);
} }

Loading…
Cancel
Save