Skip to content

feat(html): recognize Angular component templates#9658

Closed
raashish1601 wants to merge 2 commits intobiomejs:mainfrom
raashish1601:contributor-69/angular-component-templates
Closed

feat(html): recognize Angular component templates#9658
raashish1601 wants to merge 2 commits intobiomejs:mainfrom
raashish1601:contributor-69/angular-component-templates

Conversation

@raashish1601
Copy link
Copy Markdown

@raashish1601 raashish1601 commented Mar 29, 2026

Summary

  • recognize .component.html files and the angular language id as Angular HTML templates
  • enable Angular interpolation by default and treat Angular binding attributes as embedded JS snippets for formatting
  • add parser, file-source, and workspace coverage for Angular component templates

Testing

  • cargo fmt -p biome_html_syntax -p biome_html_parser -p biome_service
  • cargo test -p biome_html_syntax recognizes_angular --lib -j 1 blocked locally because the checkout ran out of disk space while building target
  • cargo test -p biome_html_parser parses_angular_component_templates --test spec_tests blocked locally because the checkout ran out of disk space while building target

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 29, 2026

⚠️ No Changeset found

Latest commit: 4694325

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions github-actions Bot added A-Project Area: project A-Parser Area: parser L-HTML Language: HTML and super languages labels Mar 29, 2026
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Mar 29, 2026

Merging this PR will not alter performance

✅ 67 untouched benchmarks
⏩ 161 skipped benchmarks1


Comparing raashish1601:contributor-69/angular-component-templates (4694325) with main (c17e08e)2

Open in CodSpeed

Footnotes

  1. 161 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (cc6f2f6) during the generation of this report, so c17e08e was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 29, 2026

Walkthrough

This PR adds support for Angular framework to Biome's HTML parser and formatter. Changes include introducing HtmlVariant::Angular to recognise Angular component files (.component.html), enabling double-quoted text expressions for Angular templates, and adding embed detection for Angular template bindings ([property], (event), [(ngModel)], *ngIf, etc.). Test coverage is added for parsing and formatting Angular components with embedded bindings.

Possibly related PRs

Suggested labels

A-Parser, A-Formatter, A-Project, L-HTML

Suggested reviewers

  • dyc3
  • ematipico
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: adding support for recognising Angular component templates as a new HTML variant.
Description check ✅ Passed The description clearly relates to the changeset, detailing support for Angular templates, interpolation, bindings, and test coverage across the affected crates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_html_parser/src/parser.rs`:
- Around line 180-182: HtmlVariant::Angular currently enables double-text
expression but doesn't mark the options as HTML, so parser.options().is_html()
remains false; update the Angular branch (where options =
options.with_double_text_expression() is called) to also set the HTML flag on
the parser options (e.g., call the options method that marks HTML, such as
options.with_is_html(true) or the equivalent setter) so Angular templates are
treated as HTML by parser.options().is_html().

In `@crates/biome_html_parser/tests/spec_tests.rs`:
- Around line 121-148: The test currently hardcodes HtmlFileSource::angular()
and doesn't exercise path-based detection or invalid fixtures; add a real spec
pair (one ok and one error) named with the .component.html suffix under the
parser spec runner (tests/specs/... for the Angular component group) where the
error case contains malformed Angular template to trigger diagnostics, then
update the gen_tests! glob/selector used to generate parser tests so it picks up
*.component.html files (or adjust the path-based detection logic used by
parse_html() to infer HtmlFileSource from filenames) and refactor the
parses_angular_component_templates test to load the generated spec cases instead
of calling HtmlFileSource::angular() directly (referencing parse_html,
HtmlFileSource::angular, and the gen_tests! macro to locate where to change).

In `@crates/biome_service/src/file_handlers/html.rs`:
- Around line 518-526: The Angular structural directive handling currently
treats any attribute starting with '*' as a JS embed; modify the logic around
HtmlAttribute::cast_ref / build_angular_binding_candidate /
EmbedDetectorsRegistry::detect_match / parse_matched_embed so you first validate
the candidate binding form and skip parsing when it is Angular microsyntax (e.g.
patterns containing "let", "of", "as", "else" or semicolon-separated microsyntax
like "cond; else tpl"); implement a small helper (e.g.
is_angular_microsyntax(&candidate) or validate_binding_form(&candidate)) that
returns true for microsyntax and short-circuits before calling
detect_match/parse_matched_embed, thereby narrowing the '*' predicate to only
run the JS embed path for genuine JS expressions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a2885f3d-9b9b-4357-b2f9-6c3bef98a893

📥 Commits

Reviewing files that changed from the base of the PR and between c17e08e and 570dcc7.

📒 Files selected for processing (6)
  • crates/biome_html_parser/src/parser.rs
  • crates/biome_html_parser/tests/spec_test.rs
  • crates/biome_html_parser/tests/spec_tests.rs
  • crates/biome_html_syntax/src/file_source.rs
  • crates/biome_service/src/file_handlers/html.rs
  • crates/biome_service/src/workspace/server.tests.rs

Comment on lines +180 to +182
HtmlVariant::Angular => {
options = options.with_double_text_expression();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Carry the HTML flag into Angular parser options.

Line 180 enables {{ ... }} but still leaves is_html at its default false. The parser uses p.options().is_html() when deciding whether uppercase tags are HTML or components, so Angular templates currently take the non-HTML path here.

🛠️ Possible fix
             HtmlVariant::Angular => {
-                options = options.with_double_text_expression();
+                options.is_html = true;
+                options = options.with_double_text_expression();
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_html_parser/src/parser.rs` around lines 180 - 182,
HtmlVariant::Angular currently enables double-text expression but doesn't mark
the options as HTML, so parser.options().is_html() remains false; update the
Angular branch (where options = options.with_double_text_expression() is called)
to also set the HTML flag on the parser options (e.g., call the options method
that marks HTML, such as options.with_is_html(true) or the equivalent setter) so
Angular templates are treated as HTML by parser.options().is_html().

Comment on lines +121 to +148
#[test]
fn parses_angular_component_templates() {
let html = r#"
<input
[value]="value"
(input)="handleInput($event)"
[(ngModel)]="model"
*ngIf="visible"
/>
<p>{{ theme }}</p>
"#;
let syntax = parse_html(html, HtmlParserOptions::from(&HtmlFileSource::angular()));

assert!(
syntax.diagnostics().is_empty(),
"expected Angular component template sample to parse without diagnostics: {:?}",
syntax.diagnostics()
);
assert!(
syntax
.tree()
.syntax()
.descendants()
.find_map(HtmlDoubleTextExpression::cast)
.is_some(),
"expected Angular component template sample to contain a double text expression"
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Please add a real .component.html spec pair.

This test is useful, but Line 132 hardcodes HtmlFileSource::angular(), so it still sidesteps the path-based branch, and there is no invalid Angular fixture exercising diagnostics. I'd add at least one ok and one error *.component.html case under the spec runner, then teach the gen_tests! glob to pick them up.

As per coding guidelines, "All code changes MUST include appropriate tests: lint rules require snapshot tests in 'tests/specs/{group}/{rule}/', formatters require snapshot tests with valid/invalid cases, parsers require test files covering valid and error cases, and bug fixes require tests that reproduce and validate the fix."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_html_parser/tests/spec_tests.rs` around lines 121 - 148, The
test currently hardcodes HtmlFileSource::angular() and doesn't exercise
path-based detection or invalid fixtures; add a real spec pair (one ok and one
error) named with the .component.html suffix under the parser spec runner
(tests/specs/... for the Angular component group) where the error case contains
malformed Angular template to trigger diagnostics, then update the gen_tests!
glob/selector used to generate parser tests so it picks up *.component.html
files (or adjust the path-based detection logic used by parse_html() to infer
HtmlFileSource from filenames) and refactor the
parses_angular_component_templates test to load the generated spec cases instead
of calling HtmlFileSource::angular() directly (referencing parse_html,
HtmlFileSource::angular, and the gen_tests! macro to locate where to change).

Comment on lines +518 to +526
if let Some(attribute) = HtmlAttribute::cast_ref(&element)
&& let Some(candidate) = build_angular_binding_candidate(&attribute)
&& let Some(embed_match) = EmbedDetectorsRegistry::detect_match(
HostLanguage::Html,
&candidate,
&doc_file_source,
)
&& let Some(parsed) =
parse_matched_embed(&candidate, &embed_match, &mut ctx, None)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Angular templates, are structural directive values such as *ngFor="let item of items"and*ngIf="cond; else tpl" valid JavaScript expressions, or do they use Angular microsyntax?

💡 Result:

In Angular templates, structural directive values such as *ngFor="let item of items" and ngIf="cond; else tpl" use Angular microsyntax, not valid standalone JavaScript expressions. The asterisk () is syntactic sugar that Angular desugars into an with the directive applied. The string after the = (e.g., let item of items or cond; else tpl) follows Angular's structural directive microsyntax grammar, which includes special keywords like let, of, as, and else separated by semicolons (;). This microsyntax is parsed by Angular to generate bindings like [ngForOf]="items" let-item="$implicit", which are not valid JavaScript. While parts like conditions in *ngIf="cond" are Angular template expressions (a subset of JavaScript), the full directive syntax with microsyntax elements is Angular-specific and not executable JavaScript.

Citations:


The * predicate will incorrectly classify Angular structural directive microsyntax as JavaScript.

Lines 1118–1121 match any attribute starting with *, and the new Angular flow (lines 518–526) will attempt to parse structural directive values like *ngFor="let item of items" or *ngIf="cond; else tpl" as JavaScript. These expressions use Angular's microsyntax with keywords like let, of, as, and else, and are not valid JavaScript. Narrow the * case to exclude directives with microsyntax, or validate the binding form before treating it as a JS embed.

Also applies to: 1117–1122

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_service/src/file_handlers/html.rs` around lines 518 - 526, The
Angular structural directive handling currently treats any attribute starting
with '*' as a JS embed; modify the logic around HtmlAttribute::cast_ref /
build_angular_binding_candidate / EmbedDetectorsRegistry::detect_match /
parse_matched_embed so you first validate the candidate binding form and skip
parsing when it is Angular microsyntax (e.g. patterns containing "let", "of",
"as", "else" or semicolon-separated microsyntax like "cond; else tpl");
implement a small helper (e.g. is_angular_microsyntax(&candidate) or
validate_binding_form(&candidate)) that returns true for microsyntax and
short-circuits before calling detect_match/parse_matched_embed, thereby
narrowing the '*' predicate to only run the JS embed path for genuine JS
expressions.

@Conaclos Conaclos added the M-Likely Agent Meta: this was likely an automated PR without a human in the loop label Mar 29, 2026
@ematipico ematipico closed this Mar 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Parser Area: parser A-Project Area: project L-HTML Language: HTML and super languages M-Likely Agent Meta: this was likely an automated PR without a human in the loop

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants