Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions crates/oxc_angular_compiler/src/component/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4427,10 +4427,16 @@ fn compile_component_partial<'a>(
allocator: &'a Allocator,
template: &'a str,
metadata: &ComponentMetadata<'a>,
view_queries: &[R3QueryMetadata<'a>],
content_queries: &[R3QueryMetadata<'a>],
pool_starting_index: u32,
) -> FullCompilationResult {
let inputs =
crate::partial::PartialComponentInputs { template, is_inline: metadata.template.is_some() };
let inputs = crate::partial::PartialComponentInputs {
template,
is_inline: metadata.template.is_some(),
content_queries,
view_queries,
};
let cmp_expr =
crate::partial::compile_declare_component_from_metadata(allocator, metadata, &inputs);
let fac_expr =
Expand Down Expand Up @@ -4481,7 +4487,14 @@ fn compile_component_full<'a>(
// Partial declarations carry the template as a verbatim string and
// let the linker re-parse at consumer build time.
if matches!(options.compilation_mode, crate::CompilationMode::Partial) {
return Ok(compile_component_partial(allocator, template, metadata, pool_starting_index));
return Ok(compile_component_partial(
allocator,
template,
metadata,
&view_queries,
&content_queries,
pool_starting_index,
));
}

let mut diagnostics = Vec::new();
Expand Down
8 changes: 8 additions & 0 deletions crates/oxc_angular_compiler/src/directive/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ pub struct R3QueryMetadata<'a> {

/// Whether the query is signal-based.
pub is_signal: bool,

/// Whether a `Type` predicate was written `forwardRef(() => X)` in the
/// source and unwrapped during extraction. The partial emitter re-wraps
/// it (`convertFromMaybeForwardRefExpression` in render3/util.ts); the
/// full emitter leaves it bare because query calls run after class
/// initialization.
pub is_forward_ref: bool,
}

/// Query predicate type.
Expand All @@ -118,6 +125,7 @@ impl<'a> R3QueryMetadata<'a> {
read: None,
is_static: false,
is_signal: false,
is_forward_ref: false,
}
}
}
Expand Down
36 changes: 30 additions & 6 deletions crates/oxc_angular_compiler/src/directive/property_decorators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,9 @@ struct QueryConfig<'a> {
descendants: bool,
/// Whether a `QueryList` only notifies when its contents change.
emit_distinct_changes_only: bool,
/// Whether the predicate was a `forwardRef(() => X)` that extraction
/// unwrapped. Kept so the partial emitter can re-wrap it.
is_forward_ref: bool,
}

impl<'a> Default for QueryConfig<'a> {
Expand All @@ -916,6 +919,7 @@ impl<'a> QueryConfig<'a> {
predicate: None,
is_static: false,
read: None,
is_forward_ref: false,
// For @ContentChildren, default is false; for all others, default is true
descendants: decorator_name != "ContentChildren",
emit_distinct_changes_only: true,
Expand Down Expand Up @@ -957,6 +961,7 @@ fn parse_query_config<'a>(
read: query.read,
descendants: query.descendants,
emit_distinct_changes_only: query.emit_distinct_changes_only,
is_forward_ref: query.is_forward_ref,
},
Err(_) => QueryConfig::default_for(decorator_name),
};
Expand All @@ -972,9 +977,12 @@ fn parse_query_config<'a>(

let mut config = QueryConfig::default_for(decorator_name);

// The predicate: a string selector or a type/token.
// forwardRef isn't included in compiled output.
let node = try_unwrap_forward_ref(first_arg, None).unwrap_or(first_arg);
// The predicate: a string selector or a type/token. `forwardRef` is
// unwrapped for evaluation; `is_forward_ref` lets the partial emitter
// re-wrap it (upstream convertFromMaybeForwardRefExpression).
let unwrapped = try_unwrap_forward_ref(first_arg, None);
config.is_forward_ref = unwrapped.is_some();
let node = unwrapped.unwrap_or(first_arg);
config.predicate = match node {
Expression::StringLiteral(lit) => {
let mut list = Vec::new_in(&allocator);
Expand Down Expand Up @@ -1091,6 +1099,7 @@ fn try_parse_signal_query<'a>(

// Parse the predicate from the first argument
let predicate_arg = call_expr.arguments.first()?;
let mut is_forward_ref = false;
let predicate = match predicate_arg {
// String selector: viewChild('myRef')
Argument::StringLiteral(lit) => {
Expand All @@ -1101,8 +1110,11 @@ fn try_parse_signal_query<'a>(
// Type predicate: viewChild(TemplateRef) or viewChild(forwardRef(() => MyClass))
_ => {
let expr = predicate_arg.to_expression();
// Unwrap forwardRef if present - Angular doesn't include forwardRef in compiled output
let unwrapped_expr = try_unwrap_forward_ref(expr, consts).unwrap_or(expr);
// Unwrap forwardRef for evaluation; `is_forward_ref` records it so
// the partial emitter can re-wrap the predicate.
let unwrapped = try_unwrap_forward_ref(expr, consts);
is_forward_ref = unwrapped.is_some();
let unwrapped_expr = unwrapped.unwrap_or(expr);
// ngtsc emits a non-string locator as written (`WrappedNodeExpr`),
// so fall back to the source text for what can't be converted.
let output_expr = convert_oxc_expression(allocator, unwrapped_expr, source_text)
Expand Down Expand Up @@ -1150,6 +1162,7 @@ fn try_parse_signal_query<'a>(
read,
is_static: false, // Signal queries are never static
is_signal: true,
is_forward_ref,
},
))
}
Expand Down Expand Up @@ -1244,6 +1257,7 @@ pub(crate) fn extract_view_queries_in<'a>(
read: config.read,
is_static: config.is_static,
is_signal: false,
is_forward_ref: config.is_forward_ref,
});
}
}
Expand All @@ -1268,6 +1282,7 @@ pub(crate) fn extract_view_queries_in<'a>(
read: config.read,
is_static: config.is_static,
is_signal: false,
is_forward_ref: config.is_forward_ref,
});
}
}
Expand Down Expand Up @@ -1299,6 +1314,7 @@ pub(crate) fn extract_view_queries_in<'a>(
read: config.read,
is_static: config.is_static,
is_signal: false,
is_forward_ref: config.is_forward_ref,
});
}
}
Expand All @@ -1323,6 +1339,7 @@ pub(crate) fn extract_view_queries_in<'a>(
read: config.read,
is_static: config.is_static,
is_signal: false,
is_forward_ref: config.is_forward_ref,
});
}
}
Expand Down Expand Up @@ -1432,6 +1449,7 @@ pub(crate) fn extract_content_queries_in<'a>(
read: config.read,
is_static: config.is_static,
is_signal: false,
is_forward_ref: config.is_forward_ref,
});
}
}
Expand All @@ -1456,6 +1474,7 @@ pub(crate) fn extract_content_queries_in<'a>(
read: config.read,
is_static: config.is_static,
is_signal: false,
is_forward_ref: config.is_forward_ref,
});
}
}
Expand Down Expand Up @@ -1487,6 +1506,7 @@ pub(crate) fn extract_content_queries_in<'a>(
read: config.read,
is_static: config.is_static,
is_signal: false,
is_forward_ref: config.is_forward_ref,
});
}
}
Expand All @@ -1511,6 +1531,7 @@ pub(crate) fn extract_content_queries_in<'a>(
read: config.read,
is_static: config.is_static,
is_signal: false,
is_forward_ref: config.is_forward_ref,
});
}
}
Expand Down Expand Up @@ -2068,7 +2089,9 @@ fn decorator_query<'a>(
let Some(first) = args.first().and_then(Argument::as_expression) else {
return Err((format!("@{name} must have arguments"), span));
};
let node = try_unwrap_forward_ref(first, Some(consts)).unwrap_or(first);
let unwrapped = try_unwrap_forward_ref(first, Some(consts));
let is_forward_ref = unwrapped.is_some();
let node = unwrapped.unwrap_or(first);
let at_node = |message: String| (message, node.span());
let predicate = match evaluator.evaluate(node) {
// Like ngtsc, a reference or a value that can't be statically
Expand Down Expand Up @@ -2156,6 +2179,7 @@ fn decorator_query<'a>(
read: config.read,
is_static: config.is_static,
is_signal: false,
is_forward_ref,
})
}

Expand Down
9 changes: 9 additions & 0 deletions crates/oxc_angular_compiler/src/directive/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,7 @@ mod tests {
emit_distinct_changes_only: false,
is_static: false,
is_signal: true,
is_forward_ref: false,
read: None,
};

Expand Down Expand Up @@ -967,6 +968,7 @@ mod tests {
emit_distinct_changes_only: false,
is_static: false,
is_signal: true,
is_forward_ref: false,
read: None,
};

Expand Down Expand Up @@ -1017,6 +1019,7 @@ mod tests {
emit_distinct_changes_only: false,
is_static: false,
is_signal: true,
is_forward_ref: false,
read: None,
};

Expand All @@ -1031,6 +1034,7 @@ mod tests {
emit_distinct_changes_only: false,
is_static: false,
is_signal: true,
is_forward_ref: false,
read: None,
};

Expand Down Expand Up @@ -1088,6 +1092,7 @@ mod tests {
emit_distinct_changes_only: true,
is_static: false,
is_signal: false,
is_forward_ref: false,
read: None,
};

Expand All @@ -1102,6 +1107,7 @@ mod tests {
emit_distinct_changes_only: true,
is_static: false,
is_signal: false,
is_forward_ref: false,
read: None,
};

Expand Down Expand Up @@ -1151,6 +1157,7 @@ mod tests {
emit_distinct_changes_only: true,
is_static: false,
is_signal: false,
is_forward_ref: false,
read: None,
};

Expand All @@ -1165,6 +1172,7 @@ mod tests {
emit_distinct_changes_only: true,
is_static: false,
is_signal: false,
is_forward_ref: false,
read: None,
};

Expand Down Expand Up @@ -1215,6 +1223,7 @@ mod tests {
emit_distinct_changes_only: false,
is_static: false,
is_signal: true,
is_forward_ref: false,
read: None,
};

Expand Down
11 changes: 11 additions & 0 deletions crates/oxc_angular_compiler/src/output/oxc_converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,17 @@ fn convert_arrow_function_expression<'a>(
arrow: &oxc_ast::ast::ArrowFunctionExpression<'a>,
source_text: Option<&'a str>,
) -> Option<OutputExpression<'a>> {
// `ArrowFunctionExpr`/`FnParam` can't express `async`, default values,
// or a rest parameter — emit the source verbatim (types stripped), as
// for destructured params and non-arrow function expressions. Dropping
// any of these would change runtime behavior (issue #512).
if arrow.r#async
|| arrow.params.rest.is_some()
|| arrow.params.items.iter().any(|p| p.initializer.is_some())
{
return make_raw_source(allocator, source_text, arrow.span);
}

// Convert parameters
let mut params = OxcVec::with_capacity_in(arrow.params.items.len(), &allocator);
for param in &arrow.params.items {
Expand Down
Loading
Loading