Angular Ivy Plugin: Directive Transformation
Angular's Ivy compiler provides a powerful plugin system that allows developers to hook into the compilation process and transform Angular code before it's rendered. This challenge focuses on implementing a basic Ivy plugin to automatically transform directives based on specific criteria.
This task will help you understand the fundamentals of Angular Ivy plugins, how to leverage the compiler's API for code transformation, and how to inject custom logic into the Angular build pipeline.
Problem Description
Your goal is to create an Angular Ivy compiler plugin that automatically adds a specific directive (e.g., [appHighlight]) to any component's template that doesn't already have it. This plugin should inspect component templates and, if the directive is missing, inject it into the relevant elements.
Key Requirements:
- Plugin Registration: The plugin needs to be correctly registered within the Angular build process. This typically involves configuring your
angular.jsonfile. - Template Analysis: The plugin must be able to parse and analyze component templates (HTML files).
- Directive Detection: It needs to detect if the
[appHighlight]directive is already present on elements within a template. - Directive Injection: If
[appHighlight]is not found, the plugin should inject it into the top-level element of the component's template. - No Infinite Loops: The plugin should not introduce infinite loops or cause compilation errors. It should be intelligent enough to avoid re-injecting the directive if it's already present.
- Targeted Injection: For simplicity, assume you only need to inject into the root element of a component's template. You don't need to analyze deeply nested elements or complex conditional rendering for this challenge.
Expected Behavior:
Given an Angular project with a component like:
<!-- app.component.html -->
<div>
<h1>Welcome to my app!</h1>
</div>
After the Ivy plugin is applied, the compiled output for this component's template should effectively be:
<!-- Transformed Output -->
<div appHighlight>
<h1>Welcome to my app!</h1>
</div>
If the directive is already present:
<!-- app.component.html -->
<div appHighlight>
<h1>Welcome to my app!</h1>
</div>
The output should remain unchanged.
Edge Cases to Consider:
- Components with empty templates.
- Components with only comments or whitespace.
- Templates that already contain
appHighlighton multiple elements. - The impact of the plugin on other template syntax (e.g., structural directives, attribute bindings).
Examples
Example 1:
Input Component Template (src/app/my-component/my-component.component.html):
<section>
<h2>My Section</h2>
<p>Some content.</p>
</section>
Output of Plugin (effectively transforming the AST):
The compiler will process the template. The plugin should identify that appHighlight is missing and inject it into the <section> element. The final rendered HTML after compilation and rendering would reflect this.
Explanation: The plugin scans the template, finds no appHighlight directive, and adds it to the root element (<section>) of the component's template.
Example 2:
Input Component Template (src/app/another-component/another-component.component.html):
<div appHighlight>
<span>Already highlighted!</span>
</div>
Output of Plugin (effectively transforming the AST):
The plugin scans the template, finds appHighlight on the <div> element, and therefore makes no changes.
Explanation: The directive is already present, so the plugin correctly leaves the template as is.
Constraints
- The plugin must be implemented using TypeScript.
- The plugin should integrate with the Angular CLI build process via
angular.json. - Focus on transforming component templates (
.htmlfiles). - Assume the
appHighlightdirective is a simple attribute directive. - For this challenge, you do not need to handle dynamic directive creation or complex Angular compiler internals beyond AST manipulation.
Notes
- You'll need to research Angular Ivy's plugin architecture and how to hook into its transformation pipeline. Look for concepts like
compiler_pluginand AST (Abstract Syntax Tree) manipulation. - Libraries like
@angular/compilerand its associated AST types will be crucial. - Consider how to identify the root element of a template.
- The plugin will likely operate by transforming the Abstract Syntax Tree (AST) of the HTML template. You'll need to traverse the AST, find relevant nodes, and modify them.
- Think about how to register your plugin within the
angular.jsonfile. This might involve creating a separate build target or modifying an existing one.