Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b8e341d
fix(vite): link excluded deps in builds and embed the built index.htm…
Brooooooklyn Nov 9, 2026
f556ead
fix(dts): name classes in .d.ts members like ngtsc
Brooooooklyn Nov 9, 2026
30a8867
fix(dts): keep the declaration map in sync when injecting Ivy members
Brooooooklyn Nov 9, 2026
69dbc37
fix(host): order [@trigger] like ngtsc and compile [animate.*] host b…
Brooooooklyn Nov 9, 2026
252ee0e
fix(partial): align host maps, __proto__ and @HostBinding values with…
Brooooooklyn Nov 9, 2026
68ccb31
fix(vite): add Ivy members to each .d.ts from its own source
Brooooooklyn Nov 9, 2026
c68939c
Merge branch 'fix686/g2-host-animations' into fix/issues-686-696
Brooooooklyn Nov 9, 2026
c82df68
Merge branch 'fix686/g3b-dts-typeof' into fix/issues-686-696
Brooooooklyn Nov 9, 2026
175e2ec
Merge branch 'fix686/g4-vite-dts' into fix/issues-686-696
Brooooooklyn Nov 9, 2026
0506cdc
Merge branch 'fix686/g5-vite-ssr-linker' into fix/issues-686-696
Brooooooklyn Nov 9, 2026
596b546
test(linker): close the #690 member access test after the merge
Brooooooklyn Nov 9, 2026
93ac784
fix(vite): warn about a stale SSR page in watch mode, only when it is…
Brooooooklyn Nov 9, 2026
966d01e
fix(host): gate animate.* host bindings on v20.2 and link static ones
Brooooooklyn Nov 9, 2026
0628bfd
fix(dts): keep the first class's JSDoc, linear map shift, string expo…
Brooooooklyn Nov 9, 2026
1d31255
fix(vite): match written .d.ts files to their source exactly
Brooooooklyn Nov 9, 2026
607c6e1
Merge branch 'fix686/r2-dts-inject' into fix/issues-686-696
Brooooooklyn Nov 9, 2026
092ee8b
Merge branch 'fix686/j1-dts-plugin' into fix/issues-686-696
Brooooooklyn Nov 9, 2026
7c722a3
Merge branch 'fix686/j2-ssr-html' into fix/issues-686-696
Brooooooklyn Nov 9, 2026
5d378dd
fix(host): add ɵɵAnimationsFeature for animate.leave on 20.2.0-20.3.0
Brooooooklyn Nov 9, 2026
3395397
fix(dts): put the import above every comment but /// directives, like…
Brooooooklyn Nov 9, 2026
9d6fa01
fix(vite): no .d.ts guess on a layout tie, compare real paths for maps
Brooooooklyn Nov 9, 2026
b91ef14
fix(dts): import a module named only through a string export
Brooooooklyn Nov 9, 2026
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
6 changes: 3 additions & 3 deletions crates/oxc_angular_compiler/src/component/decorator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2466,7 +2466,7 @@ mod tests {
assert_eq!(host.properties.len(), 1);
// Keys are wrapped with brackets: "class.active" -> "[class.active]"
assert_eq!(host.properties[0].0.as_str(), "[class.active]");
assert_eq!(host.properties[0].1.as_str(), "isActive");
assert_eq!(host.properties[0].1.as_str(), "this.isActive");
});
}

Expand All @@ -2487,7 +2487,7 @@ mod tests {
assert_eq!(host.properties.len(), 1);
// When no name is provided, uses the property name, wrapped in brackets
assert_eq!(host.properties[0].0.as_str(), "[title]");
assert_eq!(host.properties[0].1.as_str(), "title");
assert_eq!(host.properties[0].1.as_str(), "this.title");
});
}

Expand Down Expand Up @@ -2623,7 +2623,7 @@ mod tests {
assert_eq!(host.properties.len(), 1);
// Keys are wrapped with brackets
assert_eq!(host.properties[0].0.as_str(), "[style.color]");
assert_eq!(host.properties[0].1.as_str(), "textColor");
assert_eq!(host.properties[0].1.as_str(), "this.textColor");
});
}

Expand Down
78 changes: 61 additions & 17 deletions crates/oxc_angular_compiler/src/component/definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,13 @@ fn generate_cmp_definition<'a>(

// 13. features: [...] - component features like providers, lifecycle hooks, inheritance
// See: packages/compiler/src/render3/view/compiler.ts:119-161
if let Some(features) =
generate_features_array(allocator, core_namespace, metadata, namespace_registry)
{
if let Some(features) = generate_features_array(
allocator,
core_namespace,
metadata,
namespace_registry,
options.angular_version,
) {
entries.push(LiteralMapEntry::new(Ident::from("features"), features, false));
}

Expand Down Expand Up @@ -1000,6 +1004,7 @@ fn generate_features_array<'a>(
core_namespace: &'a str,
metadata: &ComponentMetadata<'a>,
namespace_registry: &mut NamespaceRegistry<'a>,
angular_version: Option<crate::AngularVersion>,
) -> Option<OutputExpression<'a>> {
let mut features: OxcVec<'a, OutputExpression<'a>> = OxcVec::new_in(&allocator);

Expand Down Expand Up @@ -1051,6 +1056,31 @@ fn generate_features_array<'a>(
features.push(external_styles_feature);
}

// 6. AnimationsFeature - v20.2.0 to v20.3.0, for a host `animate.leave`
// Format: ɵɵAnimationsFeature()
if angular_version.is_some_and(|v| v.needs_animations_feature())
&& metadata.host.as_ref().is_some_and(|host| {
crate::directive::has_animate_leave_host_binding(
host.attributes.iter().map(|(key, _)| key.as_str()),
host.properties.iter().map(|(key, _)| key.as_str()),
host.listeners.iter().map(|(key, _)| key.as_str()),
)
})
{
let feature =
create_angular_fn_ref(allocator, core_namespace, Identifiers::ANIMATIONS_FEATURE);
features.push(OutputExpression::InvokeFunction(Box::new_in(
InvokeFunctionExpr {
fn_expr: Box::new_in(feature, &allocator),
args: OxcVec::new_in(&allocator),
pure: false,
optional: false,
source_span: None,
},
&allocator,
)));
}

// Only return the array if there are features
if features.is_empty() {
None
Expand Down Expand Up @@ -1661,7 +1691,8 @@ mod tests {
let metadata = create_test_metadata(&allocator);
let mut namespace_registry = NamespaceRegistry::new(&allocator);

let result = generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry);
let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None);
assert!(result.is_none(), "Should return None when no features are needed");
}

Expand Down Expand Up @@ -1693,7 +1724,8 @@ mod tests {
Some(create_test_providers_array(&allocator, &["ServiceA", "ServiceB"]));

let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry).unwrap();
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None)
.unwrap();

let emitter = JsEmitter::new();
let js = emitter.emit_expression(&result);
Expand All @@ -1712,7 +1744,8 @@ mod tests {
metadata.view_providers = Some(create_test_providers_array(&allocator, &["ViewService"]));

let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry).unwrap();
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None)
.unwrap();

let emitter = JsEmitter::new();
let js = emitter.emit_expression(&result);
Expand All @@ -1731,7 +1764,8 @@ mod tests {
metadata.view_providers = Some(create_test_providers_array(&allocator, &["ViewService"]));

let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry).unwrap();
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None)
.unwrap();

let emitter = JsEmitter::new();
let js = emitter.emit_expression(&result);
Expand All @@ -1749,7 +1783,8 @@ mod tests {
metadata.uses_inheritance = true;

let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry).unwrap();
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None)
.unwrap();

let emitter = JsEmitter::new();
let js = emitter.emit_expression(&result);
Expand All @@ -1767,7 +1802,8 @@ mod tests {
metadata.lifecycle = LifecycleMetadata { uses_on_changes: true };

let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry).unwrap();
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None)
.unwrap();

let emitter = JsEmitter::new();
let js = emitter.emit_expression(&result);
Expand All @@ -1786,7 +1822,8 @@ mod tests {
metadata.external_styles.push(Ident::from("./theme.css"));

let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry).unwrap();
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None)
.unwrap();

let emitter = JsEmitter::new();
let js = emitter.emit_expression(&result);
Expand All @@ -1806,7 +1843,8 @@ mod tests {
metadata.host_directives.push(directive);

let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry).unwrap();
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None)
.unwrap();

let emitter = JsEmitter::new();
let js = emitter.emit_expression(&result);
Expand All @@ -1827,7 +1865,8 @@ mod tests {
metadata.host_directives.push(directive);

let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry).unwrap();
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None)
.unwrap();

let emitter = JsEmitter::new();
let js = emitter.emit_expression(&result);
Expand All @@ -1851,7 +1890,8 @@ mod tests {
metadata.host_directives.push(directive);

let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry).unwrap();
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None)
.unwrap();

let emitter = JsEmitter::new();
let js = emitter.emit_expression(&result);
Expand All @@ -1874,7 +1914,8 @@ mod tests {
metadata.host_directives.push(directive);

let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry).unwrap();
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None)
.unwrap();

let emitter = JsEmitter::new();
let js = emitter.emit_expression(&result);
Expand Down Expand Up @@ -1907,7 +1948,8 @@ mod tests {
metadata.host_directives.push(imported_directive);

let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry).unwrap();
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None)
.unwrap();

let emitter = JsEmitter::new();
let js = emitter.emit_expression(&result);
Expand Down Expand Up @@ -1937,7 +1979,8 @@ mod tests {
metadata.lifecycle = LifecycleMetadata { uses_on_changes: true };

let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry).unwrap();
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None)
.unwrap();

let emitter = JsEmitter::new();
let js = emitter.emit_expression(&result);
Expand Down Expand Up @@ -2231,7 +2274,8 @@ mod tests {
metadata.host_directives.push(directive);

let result =
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry).unwrap();
generate_features_array(&allocator, "i0", &metadata, &mut namespace_registry, None)
.unwrap();

let emitter = JsEmitter::new();
let js = emitter.emit_expression(&result);
Expand Down
16 changes: 16 additions & 0 deletions crates/oxc_angular_compiler/src/component/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,22 @@ impl AngularVersion {
self.major >= 20
}

/// Check if this version supports `ɵɵanimateEnter`/`ɵɵanimateLeave` (v20.2.0+).
///
/// Angular v20.2 added the `animate.enter` / `animate.leave` bindings. Before
/// that, a host `[animate.*]` binding is a plain DOM property.
pub fn supports_animate_instructions(&self) -> bool {
self.major > 20 || (self.major == 20 && self.minor >= 2)
}

/// Check if `animate.leave` needs `ɵɵAnimationsFeature()` (v20.2.0 to v20.3.0).
///
/// In those versions only that feature sets up the registry `ɵɵanimateLeave`
/// writes to, so the compiler adds it (`addFeatures`). v20.3.1 dropped both.
pub fn needs_animations_feature(&self) -> bool {
self.major == 20 && (self.minor == 2 || (self.minor == 3 && self.patch == 0))
}

/// Check if this version supports the `@Service` decorator (v22.0.0+).
///
/// Angular v22 introduced the `@Service` decorator as a lighter alternative to
Expand Down
Loading
Loading