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
142 changes: 75 additions & 67 deletions crates/oxc_angular_compiler/src/class_metadata/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,73 +119,7 @@ pub fn compile_component_metadata_async_resolver<'a>(
let mut dynamic_imports = Vec::new_in(&allocator);

for dep in dependencies {
// Create: (m) => m.CmpA (or m.default for default imports)
let mut inner_params = Vec::new_in(&allocator);
inner_params.push(FnParam { name: Ident::from("m") });

let prop_name =
if dep.is_default_import { Ident::from("default") } else { dep.export_name.clone() };

let inner_body = OutputExpression::ReadProp(Box::new_in(
ReadPropExpr {
receiver: Box::new_in(
OutputExpression::ReadVar(Box::new_in(
ReadVarExpr { name: Ident::from("m"), source_span: None },
&allocator,
)),
&allocator,
),
name: prop_name,
optional: false,
source_span: None,
},
&allocator,
));

let inner_fn = OutputExpression::ArrowFunction(Box::new_in(
ArrowFunctionExpr {
params: inner_params,
body: ArrowFunctionBody::Expression(Box::new_in(inner_body, &allocator)),
source_span: None,
},
&allocator,
));

// Create: import('./cmp-a').then((m) => m.CmpA)
let dynamic_import = OutputExpression::DynamicImport(Box::new_in(
DynamicImportExpr {
url: DynamicImportUrl::String(dep.import_path.clone()),
url_comment: None,
source_span: None,
},
&allocator,
));

let then_prop = OutputExpression::ReadProp(Box::new_in(
ReadPropExpr {
receiver: Box::new_in(dynamic_import, &allocator),
name: Ident::from("then"),
optional: false,
source_span: None,
},
&allocator,
));

let mut then_args = Vec::new_in(&allocator);
then_args.push(inner_fn);

let then_call = OutputExpression::InvokeFunction(Box::new_in(
InvokeFunctionExpr {
fn_expr: Box::new_in(then_prop, &allocator),
args: then_args,
pure: false,
optional: false,
source_span: None,
},
&allocator,
));

dynamic_imports.push(then_call);
dynamic_imports.push(compile_deferred_dependency_import(allocator, dep));
}

// Create: () => [...]
Expand All @@ -205,6 +139,80 @@ pub fn compile_component_metadata_async_resolver<'a>(
))
}

/// One entry of a dependency resolver function:
/// `import('./cmp-a').then((m) => m.CmpA)`, or `m.default` for a default
/// import (the `isDeferrable` branch of Angular's
/// `compileDeferResolverFunction`).
pub fn compile_deferred_dependency_import<'a>(
allocator: &'a Allocator,
dep: &R3DeferPerComponentDependency<'a>,
) -> OutputExpression<'a> {
// Create: (m) => m.CmpA (or m.default for default imports)
let mut inner_params = Vec::new_in(&allocator);
inner_params.push(FnParam { name: Ident::from("m") });

let prop_name = if dep.is_default_import { Ident::from("default") } else { dep.export_name };

let inner_body = OutputExpression::ReadProp(Box::new_in(
ReadPropExpr {
receiver: Box::new_in(
OutputExpression::ReadVar(Box::new_in(
ReadVarExpr { name: Ident::from("m"), source_span: None },
&allocator,
)),
&allocator,
),
name: prop_name,
optional: false,
source_span: None,
},
&allocator,
));

let inner_fn = OutputExpression::ArrowFunction(Box::new_in(
ArrowFunctionExpr {
params: inner_params,
body: ArrowFunctionBody::Expression(Box::new_in(inner_body, &allocator)),
source_span: None,
},
&allocator,
));

// Create: import('./cmp-a').then((m) => m.CmpA)
let dynamic_import = OutputExpression::DynamicImport(Box::new_in(
DynamicImportExpr {
url: DynamicImportUrl::String(dep.import_path),
url_comment: None,
source_span: None,
},
&allocator,
));

let then_prop = OutputExpression::ReadProp(Box::new_in(
ReadPropExpr {
receiver: Box::new_in(dynamic_import, &allocator),
name: Ident::from("then"),
optional: false,
source_span: None,
},
&allocator,
));

let mut then_args = Vec::new_in(&allocator);
then_args.push(inner_fn);

OutputExpression::InvokeFunction(Box::new_in(
InvokeFunctionExpr {
fn_expr: Box::new_in(then_prop, &allocator),
args: then_args,
pure: false,
optional: false,
source_span: None,
},
&allocator,
))
}

// ============================================================================
// Internal helpers
// ============================================================================
Expand Down
3 changes: 2 additions & 1 deletion crates/oxc_angular_compiler/src/class_metadata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub use builders::{
pub(crate) use builders::{entity_path, param_type_reference, type_param_names_of};
pub use compiler::{
compile_class_metadata, compile_component_class_metadata,
compile_component_metadata_async_resolver, compile_opaque_async_class_metadata,
compile_component_metadata_async_resolver, compile_deferred_dependency_import,
compile_opaque_async_class_metadata,
};
pub use metadata::{R3ClassMetadata, R3DeferPerComponentDependency};
37 changes: 37 additions & 0 deletions crates/oxc_angular_compiler/src/component/cross_file_elision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,44 @@ fn standalone_class_selector<'a>(
}
_ => None,
})?;
decorated_class_selector(class, consts, implicit_standalone)
}

/// The selector of the top-level class `local` declares in `program`,
/// exported or not, when it is a standalone `@Component` or `@Directive` whose
/// `selector` and `standalone` are literals: an `imports` entry that names a
/// class of the file being compiled. `consts` are the program's.
pub fn local_standalone_class_selector<'a>(
program: &'a Program<'a>,
consts: &StringConsts<'a>,
local: &str,
implicit_standalone: bool,
) -> Option<String> {
let is_local = |class: &Class| class.id.as_ref().is_some_and(|id| id.name == local);
let class = program.body.iter().find_map(|stmt| match stmt {
Statement::ClassDeclaration(class) if is_local(class) => Some(&**class),
Statement::ExportDeclaration(decl) => match &decl.declaration {
Declaration::ClassDeclaration(class) if is_local(class) => Some(&**class),
_ => None,
},
Statement::ExportDefaultDeclaration(decl) => match &decl.declaration {
ExportDefaultDeclarationKind::ClassDeclaration(class) if is_local(class) => {
Some(&**class)
}
_ => None,
},
_ => None,
})?;
decorated_class_selector(class, consts, implicit_standalone)
}

/// The selector of `class` when it is a standalone `@Component` or
/// `@Directive` whose `selector` and `standalone` are literals.
fn decorated_class_selector<'a>(
class: &Class<'a>,
consts: &StringConsts<'a>,
implicit_standalone: bool,
) -> Option<String> {
let decorator = ["Component", "Directive"].iter().find_map(|name| {
crate::directive::find_angular_class_decorator(&class.decorators, name, consts)
})?;
Expand Down
Loading
Loading