Skip to content

Commit 166f6e2

Browse files
brandonrobertsclaudeCodex
authored
fix: pool ng-content attributes into the const pool (v22) (#340)
* fix: pool ng-content attributes into the const pool (v22) Angular 22.0.0 (rc.3, angular/angular@2891f7e) moves the `attrs` argument of `ɵɵprojection` out of the inline call and into the shared const pool, emitting `ɵɵprojection(slot, idx, _cN)` instead of an inline array. Mirror that in const_collection by routing projection attrs through `pool.get_const_literal(.., true)`, exactly as element attrs and the projectionDef/ngContentSelectors consts already do. This phase runs after generate_projection_def, so the projection attrs land at the next `_cN`, matching the goldens. Bump the conformance angular submodule rc.2 -> v22.0.0 so the suite guards this emit. Conformance stays at 1264/1264 (100%); update the three ng-content integration snapshots to the pooled form. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: reuse pooled projection attrs const per xref Co-authored-by: brandonroberts <42211+brandonroberts@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: openai-code-agent[bot] <242516109+Codex@users.noreply.github.com> Co-authored-by: brandonroberts <42211+brandonroberts@users.noreply.github.com>
1 parent 1c2da17 commit 166f6e2

5 files changed

Lines changed: 34 additions & 8 deletions

File tree

Submodule angular updated 177 files

crates/oxc_angular_compiler/src/pipeline/phases/const_collection.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -760,15 +760,29 @@ pub fn collect_element_consts(job: &mut ComponentCompilationJob<'_>) {
760760
// Second pass (2b): Assign const indices in collected order
761761
// This is where we actually call add_const, matching Angular's getConstIndex call order
762762
let mut element_const_indices: FxHashMap<XrefId, u32> = FxHashMap::default();
763-
let mut projection_attrs: FxHashMap<XrefId, OutputExpression<'_>> = FxHashMap::default();
763+
let mut projection_attrs: FxHashMap<XrefId, Ident<'_>> = FxHashMap::default();
764764

765765
for xref_item in &xrefs_to_assign {
766766
match xref_item {
767767
XrefToAssign::Projection(xref) => {
768768
if let Some(attrs) = all_element_attrs.get(xref) {
769769
if !attrs.is_empty() {
770770
let attr_array = serialize_attributes_to_array_expr(allocator, attrs);
771-
projection_attrs.insert(*xref, attr_array);
771+
// Angular v22 (rc.3+) pools projection attributes into the
772+
// shared const pool (`_cN`) instead of emitting them inline,
773+
// matching `getConstLiteral(attrArray, true)` in Angular's
774+
// const_collection.ts (angular/angular@2891f7e). This phase
775+
// runs after `generate_projection_def`, so the projectionDef
776+
// and ngContentSelectors consts are pooled first and the
777+
// projection attrs land at the next `_cN`, as in the goldens.
778+
let pooled = job.pool.get_const_literal(attr_array, true);
779+
let pooled_name = match pooled {
780+
OutputExpression::ReadVar(rv) => rv.name.clone(),
781+
_ => unreachable!(
782+
"ConstantPool::get_const_literal must return OutputExpression::ReadVar"
783+
),
784+
};
785+
projection_attrs.insert(*xref, pooled_name);
772786
}
773787
}
774788
}
@@ -794,8 +808,17 @@ pub fn collect_element_consts(job: &mut ComponentCompilationJob<'_>) {
794808
for op in view.create.iter_mut() {
795809
match op {
796810
CreateOp::Projection(proj) => {
797-
if let Some(attrs) = projection_attrs.remove(&proj.xref) {
798-
proj.attributes = Some(attrs);
811+
// It's possible for multiple projection ops to reference the same xref
812+
// (e.g. across conditional branches). Avoid consuming the pooled attrs so
813+
// every matching projection op receives the same const reference.
814+
if let Some(pooled_name) = projection_attrs.get(&proj.xref) {
815+
proj.attributes = Some(OutputExpression::ReadVar(Box::new_in(
816+
crate::output::ast::ReadVarExpr {
817+
name: pooled_name.clone(),
818+
source_span: None,
819+
},
820+
allocator,
821+
)));
799822
}
800823
}
801824
CreateOp::ElementStart(elem) => {

crates/oxc_angular_compiler/tests/snapshots/integration_test__ng_content_with_bound_select.snap

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ source: crates/oxc_angular_compiler/tests/integration_test.rs
33
expression: js
44
---
55
const _c0 = ["*"];
6+
const _c1 = ["[select]","'[slot=expanded-content]'"];
67
function TestComponent_Template(rf,ctx) {
78
if ((rf & 1)) {
89
i0.ɵɵprojectionDef();
9-
i0.ɵɵprojection(0,0,["[select]","'[slot=expanded-content]'"]);
10+
i0.ɵɵprojection(0,0,_c1);
1011
}
1112
}

crates/oxc_angular_compiler/tests/snapshots/integration_test__ng_content_with_ng_project_as.snap

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ source: crates/oxc_angular_compiler/tests/integration_test.rs
33
expression: js
44
---
55
const _c0 = ["*"];
6+
const _c1 = ["ngProjectAs","bit-label",5,["bit-label"]];
67
function TestComponent_Template(rf,ctx) {
78
if ((rf & 1)) {
89
i0.ɵɵprojectionDef();
9-
i0.ɵɵprojection(0,0,["ngProjectAs","bit-label",5,["bit-label"]]);
10+
i0.ɵɵprojection(0,0,_c1);
1011
}
1112
}

crates/oxc_angular_compiler/tests/snapshots/integration_test__ng_content_with_ng_project_as_attr_selector.snap

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ source: crates/oxc_angular_compiler/tests/integration_test.rs
33
expression: js
44
---
55
const _c0 = ["*"];
6+
const _c1 = ["ngProjectAs","[card-content]",5,["","card-content",""]];
67
function TestComponent_Template(rf,ctx) {
78
if ((rf & 1)) {
89
i0.ɵɵprojectionDef();
9-
i0.ɵɵprojection(0,0,["ngProjectAs","[card-content]",5,["","card-content",""]]);
10+
i0.ɵɵprojection(0,0,_c1);
1011
}
1112
}

0 commit comments

Comments
 (0)