Skip to content

Commit dfc1e6f

Browse files
committed
Add bitwarden compare
1 parent 6493f4e commit dfc1e6f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+3469
-437
lines changed

crates/oxc_angular_compiler/src/i18n/parser.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,8 @@ impl I18nMessageFactory {
294294
// If we found interpolations, return a Container; otherwise return single node
295295
if has_interpolation && nodes.len() > 1 {
296296
Node::Container(Container::new(nodes, text_span))
297-
} else if nodes.len() == 1 {
298-
nodes.pop().unwrap()
297+
} else if let Some(node) = nodes.pop() {
298+
node
299299
} else {
300300
Node::Text(Text::new(text.to_string(), text_span))
301301
}
@@ -425,9 +425,18 @@ impl I18nMessageFactory {
425425
// Build ICU cases (ordered for consistent serialization)
426426
let mut cases: IndexMap<String, Node> = IndexMap::default();
427427
for case in &expansion.cases {
428-
let case_nodes = self.visit_all(&case.expansion, context, visit_fn);
428+
let mut case_nodes = self.visit_all(&case.expansion, context, visit_fn);
429429
let case_node = if case_nodes.len() == 1 {
430-
case_nodes.into_iter().next().unwrap()
430+
case_nodes.pop().unwrap_or_else(|| {
431+
let case_span = ParseSourceSpan::from_offsets(
432+
&context.source_file,
433+
case.expansion_span.start,
434+
case.expansion_span.end,
435+
None,
436+
None,
437+
);
438+
Node::Container(Container::new(Vec::new(), case_span))
439+
})
431440
} else {
432441
let case_span = ParseSourceSpan::from_offsets(
433442
&context.source_file,

crates/oxc_angular_compiler/src/i18n/placeholder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ impl PlaceholderRegistry {
163163
attr_keys.sort();
164164
let str_attrs: String = attr_keys
165165
.iter()
166-
.map(|name| format!(" {}={}", name, attrs.get(*name).unwrap()))
166+
.filter_map(|name| attrs.get(*name).map(|value| format!(" {}={}", name, value)))
167167
.collect();
168168

169169
let end = if is_void { "/>".to_string() } else { format!("></{}>", tag) };

crates/oxc_angular_compiler/src/ir/expression.rs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,6 +1599,175 @@ pub fn transform_expressions_in_create_op<'a, F>(
15991599
}
16001600
}
16011601

1602+
/// Visit all expressions in an update operation (read-only).
1603+
pub fn visit_expressions_in_update_op<'a, F>(
1604+
op: &UpdateOp<'a>,
1605+
visitor: &F,
1606+
flags: VisitorContextFlag,
1607+
) where
1608+
F: Fn(&IrExpression<'a>, VisitorContextFlag),
1609+
{
1610+
match op {
1611+
UpdateOp::Property(op) => {
1612+
visit_expressions_in_expression(&op.expression, visitor, flags);
1613+
}
1614+
UpdateOp::StyleProp(op) => {
1615+
visit_expressions_in_expression(&op.expression, visitor, flags);
1616+
}
1617+
UpdateOp::ClassProp(op) => {
1618+
visit_expressions_in_expression(&op.expression, visitor, flags);
1619+
}
1620+
UpdateOp::StyleMap(op) => {
1621+
visit_expressions_in_expression(&op.expression, visitor, flags);
1622+
}
1623+
UpdateOp::ClassMap(op) => {
1624+
visit_expressions_in_expression(&op.expression, visitor, flags);
1625+
}
1626+
UpdateOp::Attribute(op) => {
1627+
visit_expressions_in_expression(&op.expression, visitor, flags);
1628+
}
1629+
UpdateOp::DomProperty(op) => {
1630+
visit_expressions_in_expression(&op.expression, visitor, flags);
1631+
}
1632+
UpdateOp::TwoWayProperty(op) => {
1633+
visit_expressions_in_expression(&op.expression, visitor, flags);
1634+
}
1635+
UpdateOp::Binding(op) => {
1636+
visit_expressions_in_expression(&op.expression, visitor, flags);
1637+
}
1638+
UpdateOp::InterpolateText(op) => {
1639+
visit_expressions_in_expression(&op.interpolation, visitor, flags);
1640+
}
1641+
UpdateOp::Variable(op) => {
1642+
visit_expressions_in_expression(&op.initializer, visitor, flags);
1643+
}
1644+
UpdateOp::StoreLet(op) => {
1645+
visit_expressions_in_expression(&op.value, visitor, flags);
1646+
}
1647+
UpdateOp::Conditional(op) => {
1648+
if let Some(ref test) = op.test {
1649+
visit_expressions_in_expression(test, visitor, flags);
1650+
}
1651+
for cond in op.conditions.iter() {
1652+
if let Some(ref expr) = cond.expr {
1653+
visit_expressions_in_expression(expr, visitor, flags);
1654+
}
1655+
}
1656+
if let Some(ref processed) = op.processed {
1657+
visit_expressions_in_expression(processed, visitor, flags);
1658+
}
1659+
if let Some(ref ctx) = op.context_value {
1660+
visit_expressions_in_expression(ctx, visitor, flags);
1661+
}
1662+
}
1663+
UpdateOp::Repeater(op) => {
1664+
visit_expressions_in_expression(&op.collection, visitor, flags);
1665+
}
1666+
UpdateOp::AnimationBinding(op) => {
1667+
visit_expressions_in_expression(&op.expression, visitor, flags);
1668+
}
1669+
UpdateOp::Control(op) => {
1670+
visit_expressions_in_expression(&op.expression, visitor, flags);
1671+
}
1672+
UpdateOp::I18nExpression(op) => {
1673+
visit_expressions_in_expression(&op.expression, visitor, flags);
1674+
}
1675+
UpdateOp::DeferWhen(op) => {
1676+
visit_expressions_in_expression(&op.condition, visitor, flags);
1677+
}
1678+
// Operations without expressions
1679+
UpdateOp::ListEnd(_)
1680+
| UpdateOp::Advance(_)
1681+
| UpdateOp::I18nApply(_)
1682+
| UpdateOp::Animation(_)
1683+
| UpdateOp::Statement(_) => {}
1684+
}
1685+
}
1686+
1687+
/// Visit all expressions in a create operation (read-only).
1688+
pub fn visit_expressions_in_create_op<'a, F>(
1689+
op: &CreateOp<'a>,
1690+
visitor: &F,
1691+
flags: VisitorContextFlag,
1692+
) where
1693+
F: Fn(&IrExpression<'a>, VisitorContextFlag),
1694+
{
1695+
match op {
1696+
CreateOp::Variable(op) => {
1697+
visit_expressions_in_expression(&op.initializer, visitor, flags);
1698+
}
1699+
CreateOp::Listener(op) => {
1700+
if let Some(handler_expr) = &op.handler_expression {
1701+
visit_expressions_in_expression(handler_expr, visitor, flags);
1702+
}
1703+
let child_flags = flags.union(VisitorContextFlag::IN_CHILD_OPERATION);
1704+
for handler_op in op.handler_ops.iter() {
1705+
visit_expressions_in_update_op(handler_op, visitor, child_flags);
1706+
}
1707+
}
1708+
CreateOp::TwoWayListener(op) => {
1709+
let child_flags = flags.union(VisitorContextFlag::IN_CHILD_OPERATION);
1710+
for handler_op in op.handler_ops.iter() {
1711+
visit_expressions_in_update_op(handler_op, visitor, child_flags);
1712+
}
1713+
}
1714+
CreateOp::AnimationListener(op) => {
1715+
let child_flags = flags.union(VisitorContextFlag::IN_CHILD_OPERATION);
1716+
for handler_op in op.handler_ops.iter() {
1717+
visit_expressions_in_update_op(handler_op, visitor, child_flags);
1718+
}
1719+
}
1720+
CreateOp::AnimationString(op) => {
1721+
visit_expressions_in_expression(&op.expression, visitor, flags);
1722+
}
1723+
CreateOp::ExtractedAttribute(op) => {
1724+
if let Some(ref value) = op.value {
1725+
visit_expressions_in_expression(value, visitor, flags);
1726+
}
1727+
}
1728+
CreateOp::DeferOn(op) => {
1729+
if let Some(ref options) = op.options {
1730+
visit_expressions_in_expression(options, visitor, flags);
1731+
}
1732+
}
1733+
CreateOp::RepeaterCreate(op) => {
1734+
visit_expressions_in_expression(&op.track, visitor, flags);
1735+
}
1736+
// Operations without expressions
1737+
CreateOp::Conditional(_)
1738+
| CreateOp::ListEnd(_)
1739+
| CreateOp::ElementStart(_)
1740+
| CreateOp::Element(_)
1741+
| CreateOp::ElementEnd(_)
1742+
| CreateOp::Template(_)
1743+
| CreateOp::ContainerStart(_)
1744+
| CreateOp::Container(_)
1745+
| CreateOp::ContainerEnd(_)
1746+
| CreateOp::DisableBindings(_)
1747+
| CreateOp::EnableBindings(_)
1748+
| CreateOp::Text(_)
1749+
| CreateOp::Pipe(_)
1750+
| CreateOp::Defer(_)
1751+
| CreateOp::I18nMessage(_)
1752+
| CreateOp::Namespace(_)
1753+
| CreateOp::ProjectionDef(_)
1754+
| CreateOp::Projection(_)
1755+
| CreateOp::DeclareLet(_)
1756+
| CreateOp::I18nStart(_)
1757+
| CreateOp::I18n(_)
1758+
| CreateOp::I18nEnd(_)
1759+
| CreateOp::IcuStart(_)
1760+
| CreateOp::IcuEnd(_)
1761+
| CreateOp::IcuPlaceholder(_)
1762+
| CreateOp::I18nContext(_)
1763+
| CreateOp::I18nAttributes(_)
1764+
| CreateOp::SourceLocation(_)
1765+
| CreateOp::ConditionalBranch(_)
1766+
| CreateOp::ControlCreate(_)
1767+
| CreateOp::Statement(_) => {}
1768+
}
1769+
}
1770+
16021771
// ============================================================================
16031772
// Angular Expression Cloning
16041773
// ============================================================================

crates/oxc_angular_compiler/src/ir/ops.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,9 @@ pub struct TemplateOp<'a> {
720720
pub embedded_view: XrefId,
721721
/// Assigned slot.
722722
pub slot: Option<SlotId>,
723+
/// HTML tag name for this template element.
724+
/// Used for content projection matching (e.g., `<ng-template>` vs `<div *ngIf="...">`).
725+
pub tag: Option<Atom<'a>>,
723726
/// Namespace.
724727
pub namespace: Namespace,
725728
/// Template kind.
@@ -733,8 +736,14 @@ pub struct TemplateOp<'a> {
733736
/// The number of binding variable slots used by this template.
734737
/// Set by var_counting phase.
735738
pub vars: Option<u32>,
739+
/// Index into the consts array for template attributes.
740+
/// Set by attribute extraction phase.
741+
pub attributes: Option<u32>,
736742
/// Local references on this template.
737743
pub local_refs: Vec<'a, LocalRef<'a>>,
744+
/// Index into the consts array for local refs.
745+
/// Set by local_refs extraction phase.
746+
pub local_refs_index: Option<u32>,
738747
/// I18n placeholder data (start_name and close_name for paired tags).
739748
pub i18n_placeholder: Option<I18nPlaceholder<'a>>,
740749
}
@@ -1114,6 +1123,10 @@ pub struct ProjectionOp<'a> {
11141123
pub fallback: Option<XrefId>,
11151124
/// I18n placeholder data for fallback view.
11161125
pub fallback_i18n_placeholder: Option<I18nPlaceholder<'a>>,
1126+
/// Attributes array expression (set by const_collection phase).
1127+
/// For ng-content, this contains the serialized attributes array directly,
1128+
/// unlike elements which use a const index.
1129+
pub attributes: Option<crate::output::ast::OutputExpression<'a>>,
11171130
}
11181131

11191132
/// Create a repeater instruction (@for).
@@ -1349,6 +1362,9 @@ pub struct VariableOp<'a> {
13491362
/// For Context: the view whose context is being stored.
13501363
/// For SavedView: the view being saved.
13511364
pub view: Option<XrefId>,
1365+
/// Whether this variable was declared locally within the same view (e.g., @let declarations).
1366+
/// Local variables take precedence during name resolution over non-local variables.
1367+
pub local: bool,
13521368
}
13531369

13541370
/// Extracted attribute for consts array.
@@ -1455,6 +1471,8 @@ pub struct PropertyOp<'a> {
14551471
pub i18n_context: Option<XrefId>,
14561472
/// I18n message.
14571473
pub i18n_message: Option<XrefId>,
1474+
/// Binding kind (for DomOnly mode and animation handling).
1475+
pub binding_kind: BindingKind,
14581476
}
14591477

14601478
/// Bind to a style property.
@@ -1564,6 +1582,8 @@ pub struct DomPropertyOp<'a> {
15641582
pub security_context: SecurityContext,
15651583
/// Sanitizer function.
15661584
pub sanitizer: Option<Atom<'a>>,
1585+
/// Binding kind (for animation handling).
1586+
pub binding_kind: BindingKind,
15671587
}
15681588

15691589
/// Update a repeater.
@@ -1826,6 +1846,9 @@ pub struct UpdateVariableOp<'a> {
18261846
/// For Context: the view whose context is being stored.
18271847
/// For SavedView: the view being saved.
18281848
pub view: Option<XrefId>,
1849+
/// Whether this variable was declared locally within the same view (e.g., @let declarations).
1850+
/// Local variables take precedence during name resolution over non-local variables.
1851+
pub local: bool,
18291852
}
18301853

18311854
/// Control binding.

crates/oxc_angular_compiler/src/pipeline/conversion.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,11 @@ impl<'a> ConvertedExpression<'a> {
7575
}
7676
}
7777

78-
/// Unwraps to an IR expression, panicking if this is an Output expression.
78+
/// Unwraps to an IR expression, returning `None` if this is an Output expression.
7979
///
80-
/// # Panics
81-
/// Panics if this is an Output expression.
82-
#[track_caller]
83-
pub fn unwrap_ir(self) -> IrExpression<'a> {
84-
self.try_unwrap_ir().expect("Expected IR expression, got Output expression")
80+
/// This is an alias for [`try_unwrap_ir`](Self::try_unwrap_ir).
81+
pub fn unwrap_ir(self) -> Option<IrExpression<'a>> {
82+
self.try_unwrap_ir()
8583
}
8684

8785
/// Tries to unwrap to an Output expression, returning `None` if this is an IR expression.
@@ -92,13 +90,11 @@ impl<'a> ConvertedExpression<'a> {
9290
}
9391
}
9492

95-
/// Unwraps to an Output expression, panicking if this is an IR expression.
93+
/// Unwraps to an Output expression, returning `None` if this is an IR expression.
9694
///
97-
/// # Panics
98-
/// Panics if this is an IR expression.
99-
#[track_caller]
100-
pub fn unwrap_output(self) -> OutputExpression<'a> {
101-
self.try_unwrap_output().expect("Expected Output expression, got IR expression")
95+
/// This is an alias for [`try_unwrap_output`](Self::try_unwrap_output).
96+
pub fn unwrap_output(self) -> Option<OutputExpression<'a>> {
97+
self.try_unwrap_output()
10298
}
10399

104100
/// Converts to an Output expression.

crates/oxc_angular_compiler/src/pipeline/emit.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,9 +1035,13 @@ fn convert_pure_function_body<'a>(
10351035
// Only constant expressions should appear here - convert them to output expressions
10361036
IrExpression::Ast(ast) => convert_ast_for_pure_function_body(allocator, ast, params),
10371037
IrExpression::ExpressionRef(_) => {
1038-
panic!(
1039-
"AssertionError: ExpressionRef should be resolved before pure function extraction"
1040-
)
1038+
// ExpressionRef should be resolved before pure function extraction.
1039+
// If we reach here, it indicates a bug in the pipeline phases.
1040+
// Return undefined as a fallback to avoid runtime panic.
1041+
OutputExpression::Literal(Box::new_in(
1042+
LiteralExpr { value: LiteralValue::Undefined, source_span: None },
1043+
allocator,
1044+
))
10411045
}
10421046

10431047
// View-related expressions that shouldn't appear in pure function bodies

crates/oxc_angular_compiler/src/pipeline/ingest.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,13 +922,16 @@ fn ingest_template<'a>(
922922
xref,
923923
embedded_view: embedded_xref,
924924
slot: None,
925+
tag: None, // Will be set by structural directive processing
925926
namespace: Namespace::Html,
926927
template_kind: TemplateKind::NgTemplate,
927928
fn_name_suffix: None,
928929
block: None,
929930
decl_count: None,
930931
vars: None,
932+
attributes: None, // Set by attribute extraction phase
931933
local_refs,
934+
local_refs_index: None, // Set by local_refs extraction phase
932935
i18n_placeholder: None,
933936
});
934937

@@ -958,6 +961,7 @@ fn ingest_variable<'a>(
958961
initializer,
959962
flags: crate::ir::enums::VariableFlags::NONE,
960963
view: Some(view_xref),
964+
local: false,
961965
});
962966

963967
if let Some(view) = job.view_mut(view_xref) {
@@ -982,6 +986,7 @@ fn ingest_content<'a>(
982986
selector: Some(content.selector),
983987
fallback: None,
984988
fallback_i18n_placeholder: None,
989+
attributes: None, // Set by const_collection phase
985990
});
986991

987992
if let Some(view) = job.view_mut(view_xref) {

0 commit comments

Comments
 (0)