Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
9 changes: 5 additions & 4 deletions crates/oxc_angular_compiler/src/class_metadata/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crate::output::oxc_converter::convert_oxc_expression;
pub fn build_decorator_metadata_array<'a>(
allocator: &'a Allocator,
decorators: &[&Decorator<'a>],
source_text: Option<&'a str>,
) -> OutputExpression<'a> {
let mut decorator_entries = AllocVec::new_in(allocator);

Expand All @@ -38,7 +39,7 @@ pub fn build_decorator_metadata_array<'a>(
))),
Expression::StaticMemberExpression(member) => {
// Handle namespaced decorators like ng.Component
convert_oxc_expression(allocator, &member.object).map(|receiver| {
convert_oxc_expression(allocator, &member.object, source_text).map(|receiver| {
OutputExpression::ReadProp(Box::new_in(
ReadPropExpr {
receiver: Box::new_in(receiver, allocator),
Expand Down Expand Up @@ -77,7 +78,7 @@ pub fn build_decorator_metadata_array<'a>(
let mut args = AllocVec::new_in(allocator);
for arg in &call.arguments {
let expr = arg.to_expression();
if let Some(converted) = convert_oxc_expression(allocator, expr) {
if let Some(converted) = convert_oxc_expression(allocator, expr, source_text) {
args.push(converted);
}
}
Expand Down Expand Up @@ -163,7 +164,7 @@ pub fn build_ctor_params_metadata<'a>(
// Extract decorators from the parameter
let param_decorators = extract_angular_decorators_from_param(param);
if !param_decorators.is_empty() {
let decorators_array = build_decorator_metadata_array(allocator, &param_decorators);
let decorators_array = build_decorator_metadata_array(allocator, &param_decorators, None);
map_entries.push(LiteralMapEntry {
key: Ident::from("decorators"),
value: decorators_array,
Expand Down Expand Up @@ -251,7 +252,7 @@ pub fn build_prop_decorators_metadata<'a>(
}

// Build decorators array for this property
let decorators_array = build_decorator_metadata_array(allocator, &angular_decorators);
let decorators_array = build_decorator_metadata_array(allocator, &angular_decorators, None);

prop_entries.push(LiteralMapEntry {
key: prop_name,
Expand Down
23 changes: 16 additions & 7 deletions crates/oxc_angular_compiler/src/component/decorator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub fn extract_component_metadata<'a>(
class: &'a Class<'a>,
implicit_standalone: bool,
import_map: &ImportMap<'a>,
source_text: Option<&'a str>,
) -> Option<ComponentMetadata<'a>> {
// Get the class name
let class_name: Ident<'a> = class.id.as_ref()?.name.clone().into();
Expand Down Expand Up @@ -130,7 +131,8 @@ pub fn extract_component_metadata<'a>(
// 1. The identifier list for local analysis
metadata.imports = extract_identifier_array(allocator, &prop.value);
// 2. The raw expression to pass to ɵɵgetComponentDepsFactory in RuntimeResolved mode
metadata.raw_imports = convert_oxc_expression(allocator, &prop.value);
metadata.raw_imports =
convert_oxc_expression(allocator, &prop.value, source_text);
}
"exportAs" => {
// exportAs can be comma-separated: "foo, bar"
Expand All @@ -150,7 +152,8 @@ pub fn extract_component_metadata<'a>(
"animations" => {
// Extract animations expression as full OutputExpression
// Handles both identifier references and complex array expressions
metadata.animations = convert_oxc_expression(allocator, &prop.value);
metadata.animations =
convert_oxc_expression(allocator, &prop.value, source_text);
}
"schemas" => {
// Extract schemas identifiers (e.g., [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA])
Expand All @@ -159,11 +162,13 @@ pub fn extract_component_metadata<'a>(
"providers" => {
// Extract providers as full OutputExpression
// Handles complex expressions like [{provide: TOKEN, useFactory: Factory}]
metadata.providers = convert_oxc_expression(allocator, &prop.value);
metadata.providers =
convert_oxc_expression(allocator, &prop.value, source_text);
}
"viewProviders" => {
// Extract view providers as full OutputExpression
metadata.view_providers = convert_oxc_expression(allocator, &prop.value);
metadata.view_providers =
convert_oxc_expression(allocator, &prop.value, source_text);
}
"hostDirectives" => {
// Extract host directives array
Expand Down Expand Up @@ -1134,9 +1139,13 @@ mod tests {
};

if let Some(class) = class {
if let Some(metadata) =
extract_component_metadata(&allocator, class, implicit_standalone, &import_map)
{
if let Some(metadata) = extract_component_metadata(
&allocator,
class,
implicit_standalone,
&import_map,
Some(code),
) {
found_metadata = Some(metadata);
break;
}
Expand Down
31 changes: 20 additions & 11 deletions crates/oxc_angular_compiler/src/component/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1616,9 +1616,13 @@ pub fn transform_angular_file(
// Compute implicit_standalone based on Angular version
let implicit_standalone = options.implicit_standalone();

if let Some(mut metadata) =
extract_component_metadata(allocator, class, implicit_standalone, &import_map)
{
if let Some(mut metadata) = extract_component_metadata(
allocator,
class,
implicit_standalone,
&import_map,
Some(source),
) {
// 3. Resolve external styles and merge into metadata
resolve_styles(allocator, &mut metadata, resolved_resources);

Expand Down Expand Up @@ -1696,7 +1700,8 @@ pub fn transform_angular_file(

// Check if the class also has an @Injectable decorator.
// @Injectable is SHARED precedence and can coexist with @Component.
let has_injectable = extract_injectable_metadata(allocator, class);
let has_injectable =
extract_injectable_metadata(allocator, class, Some(source));
if let Some(injectable_metadata) = &has_injectable {
if let Some(span) = find_injectable_decorator_span(class) {
decorator_spans_to_remove.push(span);
Expand Down Expand Up @@ -1758,6 +1763,7 @@ pub fn transform_angular_file(
decorators: build_decorator_metadata_array(
allocator,
&[decorator],
Some(source),
),
ctor_parameters: build_ctor_params_metadata(
allocator,
Expand Down Expand Up @@ -1848,7 +1854,7 @@ pub fn transform_angular_file(
// the directive and creating conflicting property definitions (like
// ɵfac getters) that interfere with the AOT-compiled assignments.
if let Some(mut directive_metadata) =
extract_directive_metadata(allocator, class, implicit_standalone)
extract_directive_metadata(allocator, class, implicit_standalone, Some(source))
{
// Track decorator span for removal
if let Some(span) = find_directive_decorator_span(class) {
Expand Down Expand Up @@ -1906,7 +1912,8 @@ pub fn transform_angular_file(

// Check if the class also has an @Injectable decorator.
// @Injectable is SHARED precedence and can coexist with @Directive.
let has_injectable = extract_injectable_metadata(allocator, class);
let has_injectable =
extract_injectable_metadata(allocator, class, Some(source));
if let Some(injectable_metadata) = &has_injectable {
if let Some(span) = find_injectable_decorator_span(class) {
decorator_spans_to_remove.push(span);
Expand Down Expand Up @@ -1939,7 +1946,7 @@ pub fn transform_angular_file(
class_definitions
.insert(class_name, (property_assignments, String::new(), String::new()));
} else if let Some(mut pipe_metadata) =
extract_pipe_metadata(allocator, class, implicit_standalone)
extract_pipe_metadata(allocator, class, implicit_standalone, Some(source))
{
// Not a @Component or @Directive - check if it's a @Pipe (PRIMARY)
// We need to compile @Pipe classes to generate ɵpipe and ɵfac definitions.
Expand Down Expand Up @@ -1980,7 +1987,8 @@ pub fn transform_angular_file(

// Check if the class also has an @Injectable decorator (issue #65).
// @Injectable is SHARED precedence and can coexist with @Pipe.
let has_injectable = extract_injectable_metadata(allocator, class);
let has_injectable =
extract_injectable_metadata(allocator, class, Some(source));
if let Some(injectable_metadata) = &has_injectable {
if let Some(span) = find_injectable_decorator_span(class) {
decorator_spans_to_remove.push(span);
Expand Down Expand Up @@ -2017,7 +2025,7 @@ pub fn transform_angular_file(
);
}
} else if let Some(mut ng_module_metadata) =
extract_ng_module_metadata(allocator, class)
extract_ng_module_metadata(allocator, class, Some(source))
{
// Not a @Component, @Directive, @Injectable, or @Pipe - check if it's an @NgModule
// We need to compile @NgModule classes to generate ɵmod, ɵfac, and ɵinj definitions.
Expand Down Expand Up @@ -2061,7 +2069,8 @@ pub fn transform_angular_file(

// Check if the class also has an @Injectable decorator.
// @Injectable is SHARED precedence and can coexist with @NgModule.
let has_injectable = extract_injectable_metadata(allocator, class);
let has_injectable =
extract_injectable_metadata(allocator, class, Some(source));
if let Some(injectable_metadata) = &has_injectable {
if let Some(span) = find_injectable_decorator_span(class) {
decorator_spans_to_remove.push(span);
Expand Down Expand Up @@ -2108,7 +2117,7 @@ pub fn transform_angular_file(
);
}
} else if let Some(mut injectable_metadata) =
extract_injectable_metadata(allocator, class)
extract_injectable_metadata(allocator, class, Some(source))
{
// Standalone @Injectable (no PRIMARY decorator on the class)
// We need to compile @Injectable classes to generate ɵprov and ɵfac definitions.
Expand Down
16 changes: 11 additions & 5 deletions crates/oxc_angular_compiler/src/directive/decorator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub fn extract_directive_metadata<'a>(
allocator: &'a Allocator,
class: &'a Class<'a>,
implicit_standalone: bool,
source_text: Option<&'a str>,
) -> Option<R3DirectiveMetadata<'a>> {
// Get the class name
let class_name: Ident<'a> = class.id.as_ref()?.name.clone().into();
Expand Down Expand Up @@ -142,7 +143,9 @@ pub fn extract_directive_metadata<'a>(
}
}
"providers" => {
if let Some(providers) = convert_oxc_expression(allocator, &prop.value) {
if let Some(providers) =
convert_oxc_expression(allocator, &prop.value, source_text)
{
builder = builder.providers(providers);
}
}
Expand Down Expand Up @@ -180,7 +183,7 @@ pub fn extract_directive_metadata<'a>(
// Extract constructor dependencies for factory generation
// This enables proper DI for directive constructors
// See: packages/compiler-cli/src/ngtsc/annotations/common/src/di.ts
let constructor_deps = extract_constructor_deps(allocator, class, has_superclass);
let constructor_deps = extract_constructor_deps(allocator, class, has_superclass, source_text);
if let Some(deps) = constructor_deps {
builder = builder.deps(deps);
}
Expand Down Expand Up @@ -252,6 +255,7 @@ fn extract_constructor_deps<'a>(
allocator: &'a Allocator,
class: &'a Class<'a>,
has_superclass: bool,
source_text: Option<&'a str>,
) -> Option<Vec<'a, R3DependencyMetadata<'a>>> {
// Find the constructor method
let constructor = class.body.body.iter().find_map(|element| {
Expand All @@ -270,7 +274,7 @@ fn extract_constructor_deps<'a>(
let mut deps = Vec::with_capacity_in(params.items.len(), allocator);

for param in &params.items {
let dep = extract_param_dependency(allocator, param);
let dep = extract_param_dependency(allocator, param, source_text);
deps.push(dep);
}

Expand All @@ -290,6 +294,7 @@ fn extract_constructor_deps<'a>(
fn extract_param_dependency<'a>(
allocator: &'a Allocator,
param: &oxc_ast::ast::FormalParameter<'a>,
source_text: Option<&'a str>,
) -> R3DependencyMetadata<'a> {
// Extract flags and @Inject token from decorators
let mut optional = false;
Expand All @@ -306,7 +311,8 @@ fn extract_param_dependency<'a>(
// @Inject(TOKEN) - extract the token
if let Expression::CallExpression(call) = &decorator.expression {
if let Some(arg) = call.arguments.first() {
inject_token = convert_oxc_expression(allocator, arg.to_expression());
inject_token =
convert_oxc_expression(allocator, arg.to_expression(), source_text);
}
}
}
Expand Down Expand Up @@ -885,7 +891,7 @@ mod tests {

if let Some(class) = class {
if let Some(metadata) =
extract_directive_metadata(&allocator, class, implicit_standalone)
extract_directive_metadata(&allocator, class, implicit_standalone, Some(code))
{
found_metadata = Some(metadata);
break;
Expand Down
10 changes: 5 additions & 5 deletions crates/oxc_angular_compiler/src/directive/property_decorators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ fn parse_input_config<'a>(
config.required = extract_boolean_value(&prop.value).unwrap_or(false);
}
"transform" => {
config.transform = convert_oxc_expression(allocator, &prop.value);
config.transform = convert_oxc_expression(allocator, &prop.value, None);
}
_ => {}
}
Expand Down Expand Up @@ -779,7 +779,7 @@ fn parse_query_config<'a>(
let expr = first_arg.to_expression();
// Unwrap forwardRef if present - Angular doesn't include forwardRef in compiled output
let unwrapped_expr = try_unwrap_forward_ref(expr).unwrap_or(expr);
if let Some(output_expr) = convert_oxc_expression(allocator, unwrapped_expr) {
if let Some(output_expr) = convert_oxc_expression(allocator, unwrapped_expr, None) {
config.predicate = Some(QueryPredicate::Type(output_expr));
}
}
Expand All @@ -799,7 +799,7 @@ fn parse_query_config<'a>(
config.is_static = extract_boolean_value(&prop.value).unwrap_or(false);
}
"read" => {
config.read = convert_oxc_expression(allocator, &prop.value);
config.read = convert_oxc_expression(allocator, &prop.value, None);
}
"descendants" => {
// Use the decorator-specific default if not explicitly set
Expand Down Expand Up @@ -941,7 +941,7 @@ fn try_parse_signal_query<'a>(
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).unwrap_or(expr);
let output_expr = convert_oxc_expression(allocator, unwrapped_expr)?;
let output_expr = convert_oxc_expression(allocator, unwrapped_expr, None)?;
QueryPredicate::Type(output_expr)
}
};
Expand All @@ -960,7 +960,7 @@ fn try_parse_signal_query<'a>(

match key_name.as_str() {
"read" => {
read = convert_oxc_expression(allocator, &prop.value);
read = convert_oxc_expression(allocator, &prop.value, None);
}
"descendants" => {
descendants = extract_boolean_value(&prop.value).unwrap_or(descendants);
Expand Down
Loading
Loading