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
52 changes: 34 additions & 18 deletions crates/oxc_angular_compiler/src/ir/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1264,8 +1264,8 @@ pub struct I18nStartOp<'a> {
pub slot: Option<SlotId>,
/// I18n context.
pub context: Option<XrefId>,
/// Message.
pub message: Option<XrefId>,
/// Message instance ID for metadata lookup.
pub message: Option<u32>,
/// I18n placeholder data (start_name and close_name for i18n blocks).
pub i18n_placeholder: Option<I18nPlaceholder<'a>>,
/// Sub-template index for nested templates inside i18n blocks.
Expand All @@ -1289,8 +1289,8 @@ pub struct I18nOp<'a> {
pub slot: Option<SlotId>,
/// I18n context.
pub context: Option<XrefId>,
/// Message.
pub message: Option<XrefId>,
/// Message instance ID for metadata lookup.
pub message: Option<u32>,
/// I18n placeholder data (start_name and close_name for i18n blocks).
pub i18n_placeholder: Option<I18nPlaceholder<'a>>,
/// Sub-template index for nested templates inside i18n blocks.
Expand Down Expand Up @@ -1321,8 +1321,8 @@ pub struct IcuStartOp<'a> {
pub xref: XrefId,
/// I18n context.
pub context: Option<XrefId>,
/// Message.
pub message: Option<XrefId>,
/// Message instance ID for metadata lookup.
pub message: Option<u32>,
/// ICU placeholder.
pub icu_placeholder: Option<Atom<'a>>,
}
Expand Down Expand Up @@ -1365,8 +1365,11 @@ pub struct I18nContextOp<'a> {
/// Maps ICU placeholder names to their formatted string values.
/// These are string literals like "Hello ${�0�}!" generated from IcuPlaceholderOp.
pub icu_placeholder_literals: oxc_allocator::HashMap<'a, Atom<'a>, Atom<'a>>,
/// Message reference.
pub message: Option<XrefId>,
/// Message instance ID reference (for metadata lookup).
///
/// Stores the i18n message's instance_id (not an XrefId) to look up metadata
/// in the job's i18n_message_metadata map.
pub message: Option<u32>,
}

/// I18n attributes on an element.
Expand Down Expand Up @@ -1439,8 +1442,13 @@ pub struct ExtractedAttributeOp<'a> {
pub security_context: SecurityContext,
/// Whether expression is truthy.
pub truthy_expression: bool,
/// i18n message (for i18n attributes).
pub i18n_message: Option<XrefId>,
/// i18n message instance ID (for i18n attributes).
///
/// This stores the i18n message's instance_id rather than an XrefId to avoid
/// allocating xrefs during ingest. Angular TS stores a direct object reference
/// to the i18n.Message; we use the instance_id as a dedup key instead.
/// The actual xref for the i18n context is allocated later in create_i18n_contexts.
pub i18n_message: Option<u32>,
/// i18n context.
pub i18n_context: Option<XrefId>,
/// Trusted value function for security-sensitive constant attributes.
Expand Down Expand Up @@ -1522,8 +1530,10 @@ pub struct PropertyOp<'a> {
pub is_structural: bool,
/// I18n context.
pub i18n_context: Option<XrefId>,
/// I18n message.
pub i18n_message: Option<XrefId>,
/// I18n message instance ID.
///
/// Stores the i18n message's instance_id for dedup, not an XrefId.
pub i18n_message: Option<u32>,
/// Binding kind (for DomOnly mode and animation handling).
pub binding_kind: BindingKind,
}
Expand Down Expand Up @@ -1608,8 +1618,10 @@ pub struct AttributeOp<'a> {
pub sanitizer: Option<Atom<'a>>,
/// I18n context.
pub i18n_context: Option<XrefId>,
/// I18n message.
pub i18n_message: Option<XrefId>,
/// I18n message instance ID.
///
/// Stores the i18n message's instance_id for dedup, not an XrefId.
pub i18n_message: Option<u32>,
/// Whether this is a text attribute (static attribute from template).
///
/// Text attributes are extractable to the consts array and don't need
Expand Down Expand Up @@ -1811,8 +1823,10 @@ pub struct BindingOp<'a> {
pub unit: Option<Atom<'a>>,
/// Security context.
pub security_context: SecurityContext,
/// I18n message.
pub i18n_message: Option<XrefId>,
/// I18n message instance ID.
///
/// Stores the i18n message's instance_id for dedup, not an XrefId.
pub i18n_message: Option<u32>,
/// Whether this binding came from a text attribute (e.g., `class="cls"` vs `[class]="expr"`).
///
/// This is used for compatibility with TemplateDefinitionBuilder which treats
Expand Down Expand Up @@ -1843,8 +1857,10 @@ pub struct AnimationOp<'a> {
pub handler_ops: Vec<'a, UpdateOp<'a>>,
/// Function name for the handler.
pub handler_fn_name: Option<Atom<'a>>,
/// I18n message.
pub i18n_message: Option<XrefId>,
/// I18n message instance ID.
///
/// Stores the i18n message's instance_id for dedup, not an XrefId.
pub i18n_message: Option<u32>,
/// Security context.
pub security_context: SecurityContext,
/// Sanitizer function.
Expand Down
32 changes: 5 additions & 27 deletions crates/oxc_angular_compiler/src/pipeline/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,12 @@ pub struct ComponentCompilationJob<'a> {
pub mode: TemplateCompilationMode,
/// Whether this is for an i18n template.
pub is_i18n_template: bool,
/// Metadata for i18n messages keyed by xref.
pub i18n_message_metadata: FxHashMap<XrefId, I18nMessageMetadata<'a>>,
/// Cache of i18n xrefs keyed by message identity (custom_id or computed id).
/// Metadata for i18n messages keyed by instance_id.
///
/// This ensures that when the same i18n message is encountered multiple times
/// (e.g., when copying attributes from an element to its conditional), they
/// share the same xref. This is crucial for correct const deduplication because
/// the i18n_const_collection phase assigns i18n variable names (I18N_0, I18N_1)
/// based on the context xref, which in turn depends on the message xref.
pub i18n_xref_by_message_key: FxHashMap<String, XrefId>,
/// The instance_id is a unique u32 assigned to each i18n message during parsing.
/// This avoids allocating xrefs during ingest for i18n messages on attribute bindings,
/// matching Angular TS which stores direct object references on BindingOp.i18nMessage.
pub i18n_message_metadata: FxHashMap<u32, I18nMessageMetadata<'a>>,
/// Whether to use external message IDs in Closure Compiler variable names.
///
/// When true, generates variable names like `MSG_EXTERNAL_abc123$$SUFFIX`.
Expand Down Expand Up @@ -227,7 +223,6 @@ impl<'a> ComponentCompilationJob<'a> {
mode: TemplateCompilationMode::default(),
is_i18n_template: false,
i18n_message_metadata: FxHashMap::default(),
i18n_xref_by_message_key: FxHashMap::default(),
i18n_use_external_ids: true, // Default matches Angular's JIT behavior
relative_context_file_path: None,
relocation_entries: Vec::new_in(allocator),
Expand Down Expand Up @@ -257,23 +252,6 @@ impl<'a> ComponentCompilationJob<'a> {
id
}

/// Gets or creates an i18n xref for a message with the given key.
///
/// This ensures that when the same i18n message is encountered multiple times
/// (e.g., when copying attributes from an element to its conditional wrapper),
/// they share the same xref. This is crucial for correct const deduplication.
///
/// The key should be derived from the message's identity (custom_id or computed id).
pub fn get_or_create_i18n_xref(&mut self, message_key: String) -> XrefId {
if let Some(&xref) = self.i18n_xref_by_message_key.get(&message_key) {
xref
} else {
let xref = self.allocate_xref_id();
self.i18n_xref_by_message_key.insert(message_key, xref);
xref
}
}

/// Stores an expression and returns its ID.
///
/// Use this instead of inline expressions to avoid cloning.
Expand Down
Loading
Loading