all in once

all in once

By medeb | personalblog | 4 Feb 2025


Automating Angular Component Styling with Python
Manually managing CSS styles for Angular components can be a tedious and error-prone process, especially when dealing with multiple projects and numerous files.  Repeatedly opening HTML files in Notepad++, extracting < style > blocks, creating or appending to CSS files, and then meticulously adding classes to HTML elements becomes a significant time investment.  This workflow is not only inefficient but also increases the risk of inconsistencies and mistakes across your codebase.  Imagine having to perform these steps for hundreds of components – the effort quickly becomes overwhelming.
Streamlining Workflow with Python Scripting
A more efficient approach is to leverage the power of Python scripting to automate this entire process.  By creating a Python script, you can effectively replace the manual steps with a single command.  The script can traverse your project directories, identify .component.html files, extract both inline and embedded styles, generate corresponding CSS files, and seamlessly inject the necessary classes into the HTML. This automation significantly reduces the time spent on styling tasks, allowing developers to focus on more critical aspects of project development.

Centralized Style Management and Enhanced Consistency
Using a Python script for CSS extraction and injection offers several key advantages. First, it centralizes the style management process. All the logic for handling styles resides in a single script, making it easier to maintain and update.  Second, it promotes consistency across projects.  The same script can be used for all your Angular projects, ensuring that styles are handled uniformly. Third, it eliminates human error.  By automating the extraction and injection of styles, the risk of typos, missed elements, or inconsistent class names is greatly reduced.  This leads to cleaner, more maintainable code and a more streamlined development workflow.

298b53f278bd13bb96eebe78dde86a83d3aff73f0c1bf06f351af62e3ef54e9e.png

import re
import os

# ... (save_to_file, extract_styles, flatten_css, extract_and_replace_inline_styles functions remain the same)
def extract_styles(html_content):
    # 1. Remove HTML comments:
    html_content = re.sub(r"(?=<!--)([\s\S]*?)-->", "", html_content, re.DOTALL)  # Correct regex for comments
    html_content = re.sub(r"//gs", "", html_content, re.DOTALL)

    # 2. Extract <style> tags (now that comments are gone):
    style_pattern = re.compile(r'<style[^>]*>(.*?)</style>', re.DOTALL | re.IGNORECASE)  # Case insensitive
    styles = style_pattern.findall(html_content)

    # 3. Remove <style> tags from the HTML content:
    cleaned_html = style_pattern.sub('', html_content)

    # Convert styles list to a dictionary with unique class names
    styles_dict = {}
    for i, style in enumerate(styles):
        class_name = "embedded-style-" + str(i + 1)
        styles_dict[class_name] = style.strip()  # Remove extra whitespace

    return cleaned_html, styles_dict

def flatten_css(styles_dict):
    flattened_css = []
    for cls, style in styles_dict.items():
        # Split the style into individual rules
        rules = style.split("}")
        for rule in rules:
            rule = rule.strip()
            if not rule:
                continue
            # Extract the selector and properties
            if "{" in rule:
                selector, properties = rule.split("{", 1)
                selector = selector.strip()
                properties = properties.strip()
                # Prepend the parent class to the selector
                flattened_css.append(".{} {} {{\n{}\n}}".format(cls, selector, properties))
    return "\n".join(flattened_css)

def extract_and_replace_inline_styles(html_content):
    inline_style_pattern = re.compile(r'style="([\s\S]*?)"', re.IGNORECASE)

    styles = {}
    cleaned_html = ""
    last_match_end = 0

    for match in inline_style_pattern.finditer(html_content):
        style_content = match.group(1)
        start, end = match.span()

        class_name = "inline-style-" + str(len(styles) + 1)
        styles[class_name] = style_content.strip()  # Remove extra whitespace

        # Find the opening tag more reliably (handles multi-line attributes)
        tag_start = -1
        for i in range(start - 1, -1, -1):  # Search backwards
            if html_content[i] == "<":
                tag_start = i
                break

        if tag_start != -1:
            tag_end = html_content.find(">", start)  # Find the closing >

            if tag_end != -1:
                tag = html_content[tag_start_end + 1]  # Extract the entire tag

                # Check if class attribute already exists
                if "class=" in tag:
                    new_tag = tag.replace('class="', 'class="{} '.format(class_name))  # Add class to existing
                else:
                    new_tag = tag.replace(">", ' class="{}">'.format(class_name))  # Add class attribute
                new_tag = new_tag.replace('style="{}"'.format(style_content), '')  # Remove the original style attribute
                cleaned_html += html_content[last_match_end_start] + new_tag
                last_match_end = tag_end + 1
            else:
                cleaned_html += html_content[last_match_end]  # Handle malformed tags
                last_match_end = end
        else:
            cleaned_html += html_content[last_match_end]  # Handle malformed tags
            last_match_end = end

    cleaned_html += html_content[last_match_end:]
    return cleaned_html, styles

def save_to_file(file_path, content, append=False): #Append parameter
    mode = 'a' if append else 'w' #Choose mode
    with open(file_path, mode, encoding='utf-8') as file:
        file.write(content)

def process_directory(directory):
    for root, _, files in os.walk(directory):
        for file in files:
            if file.endswith('.component.html'):
                html_file_path = os.path.join(root, file)
                css_file_path = os.path.join(root, file[:-5] + '.css')
                embedded_css_file_path = os.path.join(root, file[:-5] + '.css')

                try:
                    with open(html_file_path, 'r', encoding='utf-8') as html_file:
                        html_content = html_file.read()

                    cleaned_html1, styles1 = extract_styles(html_content)
                    cleaned_html, styles = extract_and_replace_inline_styles(cleaned_html1)

                    save_to_file(html_file_path, cleaned_html)  # Save HTML (overwrite)

                    if styles:
                        css_content = "\n".join([".{} {{ {} }}".format(cls, style) for cls, style in styles.items()])
                        save_to_file(css_file_path,"/*inlinestyle*/\n",append=os.path.exists(css_file_path)) # Append if file exists
                        save_to_file(css_file_path, css_content, append=os.path.exists(css_file_path)) # Append if file exists

                    if styles1:
                        css_content1 = flatten_css(styles1)
                        save_to_file(css_file_path,"/*embedded style*/\n",append=os.path.exists(css_file_path)) # Append if file exists
                        save_to_file(embedded_css_file_path, css_content1, append=os.path.exists(embedded_css_file_path)) # Append if file exists

                    print(f"Processed: {html_file_path}")

                except Exception as e:
                    print(f"Error processing {html_file_path}: {e}")

# ... (main function remains the same)


if __name__ == "__main__":
    app_directory = "src/app"  # Replace with your app directory
    process_directory(app_directory)
    print("Processing complete.")

How do you rate this article?

2



personalblog
personalblog

My daily experience in crypto world

Publish0x

Send a $0.01 microtip in crypto to the author, and earn yourself as you read!

20% to author / 80% to me.
We pay the tips from our rewards pool.