Skip to content

Commit 7828f59

Browse files
Brooooooklynclaude
andauthored
fix(angular): use regex-based else-if pattern matching to match Angular's ELSE_IF_PATTERN (#36)
Angular uses the regex `/^else[^\S\r\n]+if/` to detect "else if" connected blocks, which means block names like "else ifx" also match as chained else-if branches. Our parser was using exact string matching ("else if"), causing names like "else ifx" to fall through to the default BlockType::If and emit two independent conditionals instead of a single chained one. Fix by reusing `is_else_if_pattern()` (made public) in the parser's block type classification, matching Angular's regex-based connected-block detection. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9feb2a8 commit 7828f59

3 files changed

Lines changed: 27 additions & 3 deletions

File tree

crates/oxc_angular_compiler/src/parser/html/parser.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::ast::html::{
1515
InterpolatedToken, InterpolatedTokenType,
1616
};
1717
use crate::parser::expression::BindingParser;
18+
use crate::transform::control_flow::is_else_if_pattern;
1819
use crate::util::{ParseError, ParseLocation, ParseSourceFile, ParseSourceSpan};
1920

2021
use super::entities::decode_entities_in_string;
@@ -1501,7 +1502,11 @@ impl<'a> HtmlParser<'a> {
15011502
let block_type = match name.as_str() {
15021503
"if" => BlockType::If,
15031504
"else" => BlockType::Else,
1504-
"else if" => BlockType::ElseIf,
1505+
// Match Angular's ELSE_IF_PATTERN: /^else[^\S\r\n]+if/
1506+
// Any block name starting with "else " followed by "if" (e.g. "else if",
1507+
// "else ifx") is classified as ElseIf, matching Angular's regex-based
1508+
// connected-block detection.
1509+
_ if is_else_if_pattern(&name) => BlockType::ElseIf,
15051510
"for" => BlockType::For,
15061511
"empty" => BlockType::Empty,
15071512
"switch" => BlockType::Switch,

crates/oxc_angular_compiler/src/transform/control_flow.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,12 @@ fn parse_as_alias(s: &str) -> Option<&str> {
3636
Some(after_as.trim_start())
3737
}
3838

39-
/// Check if string matches `^else\s+if` pattern.
40-
fn is_else_if_pattern(s: &str) -> bool {
39+
/// Check if string matches Angular's `ELSE_IF_PATTERN`: `/^else[^\S\r\n]+if/`.
40+
///
41+
/// Any name starting with "else" followed by at least one whitespace character
42+
/// and then "if" is treated as an else-if block. This means names like
43+
/// "else ifx" also match, which is intentional to mirror Angular's behavior.
44+
pub fn is_else_if_pattern(s: &str) -> bool {
4145
if !s.starts_with("else") {
4246
return false;
4347
}

crates/oxc_angular_compiler/tests/r3_template_transform_test.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,21 @@ mod if_blocks {
10701070
assert_eq!(result[1], h!["IfBlockBranch", "cond1"]);
10711071
assert_eq!(result[3], h!["IfBlockBranch", "cond2"]);
10721072
}
1073+
1074+
/// Angular uses the regex `/^else[^\S\r\n]+if/` to detect "else if" blocks,
1075+
/// which means block names like "else ifx" still match as connected else-if
1076+
/// branches. Our parser must replicate this behavior to avoid emitting two
1077+
/// independent conditionals instead of a single chained one.
1078+
#[test]
1079+
fn should_treat_else_if_prefix_as_connected_block() {
1080+
// "else ifx" should still be chained as a connected else-if branch,
1081+
// matching Angular's regex-based ELSE_IF_PATTERN: /^else[^\S\r\n]+if/
1082+
let result = humanize_ignore_errors("@if (cond1) { a } @else ifx (cond2) { b }");
1083+
// Should produce a single IfBlock with two branches, not two independent blocks
1084+
assert_eq!(result[0], h!["IfBlock"]);
1085+
assert_eq!(result[1], h!["IfBlockBranch", "cond1"]);
1086+
assert_eq!(result[3], h!["IfBlockBranch", "cond2"]);
1087+
}
10731088
}
10741089

10751090
// ============================================================================

0 commit comments

Comments
 (0)