Skip to content
Open
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
12 changes: 6 additions & 6 deletions crates/oxc_angular_compiler/src/component/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use crate::class_metadata::{
use crate::directive::collect_string_consts;
use crate::directive::{
R3QueryMetadata, create_content_queries_function, create_view_queries_function,
decorator_io_errors, extract_content_queries, extract_directive_metadata, extract_view_queries,
decorator_io_errors, extract_class_queries, extract_directive_metadata,
find_directive_decorator, find_directive_decorator_span, generate_directive_definitions,
};
use crate::dts;
Expand Down Expand Up @@ -2634,11 +2634,11 @@ pub fn transform_angular_file(
let template = allocator.alloc_str(&template_string);
// 4.5 Extract view queries from the class (for @ViewChild/@ViewChildren)
// These need to be passed to compile_component_full so predicates can be pooled
let view_queries = extract_view_queries(allocator, class, Some(source));

// 4.6 Extract content queries from the class (for @ContentChild/@ContentChildren)
// Signal-based queries (contentChild(), contentChildren()) are also detected here
let content_queries = extract_content_queries(allocator, class, Some(source));
// 4.5/4.6 View and content queries: member decorators and signal
// queries, then `queries:` in the decorator, as ngtsc orders them.
// Predicates are pooled by compile_component_full.
let (view_queries, content_queries) =
extract_class_queries(allocator, class, Some(source), &string_consts);

// Collect content query property names for .d.ts generation
// (before content_queries is moved into compile_component_full)
Expand Down
29 changes: 24 additions & 5 deletions crates/oxc_angular_compiler/src/directive/decorator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ pub fn extract_directive_metadata<'a>(
}

// Extract @Input/@Output/@HostBinding/@HostListener from class members
builder = builder.extract_from_class(allocator, class, source_text);
builder = builder.extract_from_class_in(allocator, class, source_text, Some(consts));

// Detect if ngOnChanges lifecycle hook is implemented
// Similar to Angular's: const usesOnChanges = members.some(member => ...)
Expand All @@ -206,6 +206,20 @@ pub fn extract_directive_metadata<'a>(
// The builder already has host data from extract_from_class, we need to merge the decorator host
let mut metadata = builder.build()?;

// `queries:` from the decorator come after the member queries, as in ngtsc.
if let Some(config) = config_obj {
let queries = super::parse_decorator_queries(
allocator,
config,
class,
source_text,
consts,
"Directive",
);
metadata.view_queries.extend(queries.view);
metadata.queries.extend(queries.content);
}

if let Some(io) = io {
let fields = std::mem::replace(&mut metadata.inputs, Vec::new_in(&allocator));
metadata.inputs =
Expand Down Expand Up @@ -910,10 +924,10 @@ pub(crate) fn angular_decorator_config<'a>(
Some((config, name))
}

/// The first error ngtsc raises for the inputs and outputs of a `@Component` /
/// `@Directive` on `class`, in the order it checks them
/// (`extractDirectiveMetadata`): `inputs:`, `@Input` members, `outputs:`, then
/// output members. ngtsc stops at the first one.
/// The first error ngtsc raises for the inputs, outputs and queries of a
/// `@Component` / `@Directive` on `class`, in the order it checks them
/// (`extractDirectiveMetadata`): `inputs:`, `@Input` members, `outputs:`,
/// output members, then `queries:`. ngtsc stops at the first one.
pub fn decorator_io_errors<'a>(
allocator: &'a Allocator,
class: &'a Class<'a>,
Expand Down Expand Up @@ -980,12 +994,17 @@ pub fn decorator_io_errors<'a>(
})
})
};
let queries = || {
let config = config?;
super::parse_decorator_queries(allocator, config, class, None, consts, decorator_name).error
};

io.as_ref()
.and_then(|io| io.input_error.clone())
.or_else(input_members)
.or_else(|| io.as_ref().and_then(|io| io.output_error.clone()))
.or_else(output_members)
.or_else(queries)
.into_iter()
.collect()
}
Expand Down
11 changes: 9 additions & 2 deletions crates/oxc_angular_compiler/src/directive/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ pub(crate) struct FileScope<'a> {
types: HashSet<&'a str>,
}

/// An import binding: unless it's a namespace import, the name it's exported under.
/// An import binding: the module it comes from and, unless it's a namespace
/// import, the name it's exported under.
#[derive(Clone, Copy)]
pub(crate) struct Import<'a> {
pub module: &'a str,
pub imported: Option<&'a str>,
}

Expand All @@ -49,6 +51,7 @@ impl<'a> FileScope<'a> {
for stmt in &program.body {
match stmt {
Statement::ImportDeclaration(import) => {
let module = import.source.value.as_str();
for spec in import.specifiers.iter().flatten() {
let (local, imported) = match spec {
ImportDeclarationSpecifier::ImportSpecifier(s) => {
Expand All @@ -66,7 +69,7 @@ impl<'a> FileScope<'a> {
(s.local.name.as_str(), None)
}
};
scope.imports.insert(local, Import { imported });
scope.imports.insert(local, Import { module, imported });
}
}
Statement::ExportDeclaration(export) => {
Expand Down Expand Up @@ -167,6 +170,10 @@ impl<'a> FileScope<'a> {
self.exported.insert(id);
}
}

pub(crate) fn import(&self, name: &str) -> Option<Import<'a>> {
self.imports.get(name).copied()
}
}

/// A function definition ngtsc's `getDefinitionOfFunction` accepts.
Expand Down
28 changes: 24 additions & 4 deletions crates/oxc_angular_compiler/src/directive/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,10 +411,22 @@ impl<'a> R3DirectiveMetadataBuilder<'a> {
/// # Returns
/// The builder with all extracted metadata added.
pub fn extract_from_class(
self,
allocator: &'a Allocator,
class: &'a Class<'a>,
source_text: Option<&'a str>,
) -> Self {
self.extract_from_class_in(allocator, class, source_text, None)
}

/// [`Self::extract_from_class`], resolving query predicates that reference
/// same-file consts (`@ViewChild(SELECTOR)`) the way ngtsc does.
pub(crate) fn extract_from_class_in(
mut self,
allocator: &'a Allocator,
class: &'a Class<'a>,
source_text: Option<&'a str>,
consts: Option<&super::StringConsts<'a>>,
) -> Self {
// Extract inputs from @Input decorators
let inputs =
Expand All @@ -430,15 +442,23 @@ impl<'a> R3DirectiveMetadataBuilder<'a> {
}

// Extract view queries from @ViewChild/@ViewChildren
let view_queries =
super::property_decorators::extract_view_queries(allocator, class, source_text);
let view_queries = super::property_decorators::extract_view_queries_in(
allocator,
class,
source_text,
consts,
);
for query in view_queries {
self = self.add_view_query(query);
}

// Extract content queries from @ContentChild/@ContentChildren
let content_queries =
super::property_decorators::extract_content_queries(allocator, class, source_text);
let content_queries = super::property_decorators::extract_content_queries_in(
allocator,
class,
source_text,
consts,
);
for query in content_queries {
self = self.add_query(query);
}
Expand Down
12 changes: 7 additions & 5 deletions crates/oxc_angular_compiler/src/directive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ pub use decorator::{
StringConsts, collect_string_consts, decorator_io_errors, extract_directive_metadata,
find_directive_decorator_span,
};
pub(crate) use decorator::{extract_string_value, resolve_template_literal};
pub(crate) use decorator::{
angular_decorator_config, extract_string_value, resolve_template_literal,
};
pub(crate) use decorator::{
merge_by_class_property, parse_decorator_io, resolve_member_transforms,
};
Expand All @@ -40,11 +42,11 @@ pub use metadata::{
R3HostMetadata, R3InputMetadata, R3QueryMetadata,
};
pub use property_decorators::{
extract_content_queries, extract_host_bindings, extract_host_listeners, extract_input_metadata,
extract_output_metadata, extract_view_queries,
extract_class_queries, extract_content_queries, extract_host_bindings, extract_host_listeners,
extract_input_metadata, extract_output_metadata, extract_view_queries,
};
pub(crate) use property_decorators::{
try_parse_signal_input, try_parse_signal_model, try_parse_signal_output,
unwrap_initializer_api_expr,
parse_decorator_queries, try_parse_signal_input, try_parse_signal_model,
try_parse_signal_output, unwrap_initializer_api_expr,
};
pub use query::{create_content_queries_function, create_view_queries_function};
Loading