」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > C# .NET 程式碼庫中的 Bootstrap 現代化:來自 o 5 的 Python 支援的遷移

C# .NET 程式碼庫中的 Bootstrap 現代化:來自 o 5 的 Python 支援的遷移

發佈於2024-07-30
瀏覽:258

Modernizing Bootstrap in a C# .NET Codebase: A Python-Powered Migration from o 5

Introduction

As a developer, I recently found myself faced with an exciting challenge: modernizing a legacy C# .NET codebase that was still using Bootstrap 3. The goal was clear - bring the project up to speed with the latest Bootstrap 5. However, I quickly realized that making such a significant leap could be risky and time-consuming.

That's when I decided to take a phased approach:

  1. First, migrate from Bootstrap 3 to Bootstrap 4
  2. Then, once stable, make the jump from Bootstrap 4 to Bootstrap 5

This strategy would allow for a more manageable transition, easier debugging, and a smoother overall process. Today, I'm excited to share the first part of this journey - automating the migration from Bootstrap 3 to 4 using a Python script.

A Note on the Code

Before we dive in, it's important to note that the code presented here is a simplified version of the actual script used in the project. For obvious reasons, such as proprietary information and specific project requirements, I've streamlined the code for this blog post. However, the approach and core functionality remain very similar to what was implemented in the real-world scenario.

The Challenge

Migrating from Bootstrap 3 to 4 involves numerous class name changes and deprecated components. Manually updating these across an entire project can be time-consuming and error-prone. That's where our Python script comes in.

The Solution

Our script, which we'll call bootstrap_migrator.py, is designed to scan your project files and automatically update Bootstrap 3 class names to their Bootstrap 4 equivalents. It handles HTML, Razor (cshtml), and even JavaScript files, making it a comprehensive solution for your migration needs.

Breaking Down the Code

Let's dive into the details of our migration script and explain each part.

Importing Required Modules

import os
import re

We start by importing two essential Python modules:

  • os: This module provides a way to use operating system dependent functionality, like navigating the file system.
  • re: This module provides support for regular expressions in Python.

The Main Migration Function

def update_bootstrap_classes(content, file_type):
    class_mappings = {
    r'\bcol-xs-(\d )\b': r'col-\1',
    r'\bcol-sm-(\d )\b': r'col-sm-\1',
    r'\bcol-md-(\d )\b': r'col-md-\1',
    r'\bcol-lg-(\d )\b': r'col-lg-\1',
    r'\bcol-xl-(\d )\b': r'col-xl-\1',
    r'\bbtn-default\b': 'btn-secondary',
    r'\bimg-responsive\b': 'img-fluid',
    r'\bimg-circle\b': 'rounded-circle',
    r'\bimg-rounded\b': 'rounded',
    r'\bpanel\b': 'card',
    r'\bpanel-heading\b': 'card-header',
    r'\bpanel-title\b': 'card-title',
    r'\bpanel-body\b': 'card-body',
    r'\bpanel-footer\b': 'card-footer',
    r'\bpanel-primary\b': 'card bg-primary text-white',
    r'\bpanel-success\b': 'card bg-success text-white',
    r'\bpanel-info\b': 'card text-white bg-info',
    r'\bpanel-warning\b': 'card bg-warning',
    r'\bpanel-danger\b': 'card bg-danger text-white',
    r'\bwell\b': 'card card-body',
    r'\bthumbnail\b': 'card card-body',
    r'\blist-inline\s*>\s*li\b': 'list-inline-item',
    r'\bdropdown-menu\s*>\s*li\b': 'dropdown-item',
    r'\bnav\s navbar\s*>\s*li\b': 'nav-item',
    r'\bnav\s navbar\s*>\s*li\s*>\s*a\b': 'nav-link',
    r'\bnavbar-right\b': 'ml-auto',
    r'\bnavbar-btn\b': 'nav-item',
    r'\bnavbar-fixed-top\b': 'fixed-top',
    r'\bnav-stacked\b': 'flex-column',
    r'\bhidden-xs\b': 'd-none',
    r'\bhidden-sm\b': 'd-sm-none',
    r'\bhidden-md\b': 'd-md-none',
    r'\bhidden-lg\b': 'd-lg-none',
    r'\bvisible-xs\b': 'd-block d-sm-none',
    r'\bvisible-sm\b': 'd-none d-sm-block d-md-none',
    r'\bvisible-md\b': 'd-none d-md-block d-lg-none',
    r'\bvisible-lg\b': 'd-none d-lg-block d-xl-none',
    r'\bpull-right\b': 'float-right',
    r'\bpull-left\b': 'float-left',
    r'\bcenter-block\b': 'mx-auto d-block',
    r'\binput-lg\b': 'form-control-lg',
    r'\binput-sm\b': 'form-control-sm',
    r'\bcontrol-label\b': 'col-form-label',
    r'\btable-condensed\b': 'table-sm',
    r'\bpagination\s*>\s*li\b': 'page-item',
    r'\bpagination\s*>\s*li\s*>\s*a\b': 'page-link',
    r'\bitem\b': 'carousel-item',
    r'\bhelp-block\b': 'form-text',
    r'\blabel\b': 'badge',
    r'\bbadge\b': 'badge badge-pill'
}

This function is the heart of our script. It takes two parameters:

  • content: The content of the file we're updating.
  • file_type: The type of file we're dealing with (HTML, JS, etc.).

The class_mappings dictionary is crucial. It maps Bootstrap 3 class patterns (as regex) to their Bootstrap 4 equivalents. For example, col-xs-* becomes just col-* in Bootstrap 4.

Replacing Classes in HTML and Razor Files

def replace_class(match):
    classes = match.group(1).split()
    updated_classes = []
    for cls in classes:
        replaced = False
        for pattern, replacement in class_mappings.items():
            if re.fullmatch(pattern, cls):
                updated_cls = re.sub(pattern, replacement, cls)
                updated_classes.append(updated_cls)
                replaced = True
                break
        if not replaced:
            updated_classes.append(cls)
    return f'class="{" ".join(updated_classes)}"'

if file_type in ['cshtml', 'html']:
    return re.sub(r'class="([^"]*)"', replace_class, content)

This part handles the replacement of classes in HTML and Razor files:

  1. It finds all class attributes in the HTML.
  2. For each class found, it checks if it matches any of our Bootstrap 3 patterns.
  3. If a match is found, it replaces the class with its Bootstrap 4 equivalent.
  4. Classes that don't match any patterns are left unchanged.

Updating JavaScript Selectors

    def replace_js_selectors(match):
        full_match = match.group(0)
        method = match.group(1)
        selector = match.group(2)

        classes = re.findall(r'\.[-\w] ', selector)

        for i, cls in enumerate(classes):
            cls = cls[1:]  
            for pattern, replacement in class_mappings.items():
                if re.fullmatch(pattern, cls):
                    new_cls = re.sub(pattern, replacement, cls)
                    classes[i] = f'.{new_cls}'
                    break

        updated_selector = selector
        for old_cls, new_cls in zip(re.findall(r'\.[-\w] ', selector), classes):
            updated_selector = updated_selector.replace(old_cls, new_cls)

        return f"{method}('{updated_selector}')"

    if file_type == 'js':
        js_jquery_methods = [
            'querySelector', 'querySelectorAll', 'getElementById', 'getElementsByClassName',
            '$', 'jQuery', 'find', 'children', 'siblings', 'parent', 'closest', 'next', 'prev',
            'addClass', 'removeClass', 'toggleClass', 'hasClass'
        ]

        method_pattern = '|'.join(map(re.escape, js_jquery_methods))
        content = re.sub(rf"({method_pattern})\s*\(\s*['\"]([^'\"] )['\"]\s*\)", replace_js_selectors, content)

        return content

This section handles updating class names in JavaScript files:

  1. It defines a list of common JavaScript and jQuery methods that might use class selectors.
  2. It then uses regex to find these method calls and updates the class names in their selectors.
  3. It also updates class names used in jQuery's .css() method calls.

Processing Individual Files

def process_file(file_path):
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            content = file.read()

        file_type = file_path.split('.')[-1].lower()
        updated_content = update_bootstrap_classes(content, file_type)

        if content != updated_content:
            with open(file_path, 'w', encoding='utf-8') as file:
                file.write(updated_content)
            print(f"Updated: {file_path}")
        else:
            print(f"No changes: {file_path}")
    except Exception as e:
        print(f"Error processing {file_path}: {str(e)}")

This function handles the processing of individual files:

  1. It reads the content of the file.
  2. Determines the file type based on its extension.
  3. Calls update_bootstrap_classes to update the content.
  4. If changes were made, it writes the updated content back to the file.
  5. It also handles exceptions and provides feedback on the process.

The Main Function

def main():
    project_dir = input("Enter the path to your project directory: ")
    print(f"Scanning directory: {project_dir}")

    if not os.path.exists(project_dir):
        print(f"The directory {project_dir} does not exist.")
        return

    files_found = False
    for root, dirs, files in os.walk(project_dir):
        for file in files:
            if file.endswith(('.cshtml', '.html', '.js')):
                files_found = True
                file_path = os.path.join(root, file)
                print(f"Processing file: {file_path}")
                process_file(file_path)

    if not files_found:
        print("No .cshtml, .html, or .js files found in the specified directory.")

if __name__ == "__main__":
    main()

The main function ties everything together:

  1. It prompts the user for the project directory.
  2. It then walks through the directory, finding all relevant files (.cshtml, .html, .js).
  3. For each file found, it calls process_file to update its content.
  4. It provides feedback on the process, including if no relevant files were found.

Key Features

  • Comprehensive Class Updates: From grid classes to component-specific classes, the script covers a wide range of Bootstrap changes.
  • JavaScript Support: It updates class names in various JavaScript and jQuery selectors, ensuring your dynamic content doesn't break.
  • Flexibility: The script can be easily extended to include more class mappings or file types.
  • Non-Destructive: It only modifies files where changes are necessary, leaving others untouched.

Using the Script

To use the script, simply run it and provide the path to your project directory when prompted. It will then process all relevant files, updating them as necessary.

python bootstrap_migrator.py

Limitations and Considerations

While this script automates a significant portion of the migration process, it's important to note that it's not a complete solution. You should still:

  1. Thoroughly test your application after running the script.
  2. Be aware of Bootstrap 4's new components and features that may require manual implementation.
  3. Review your custom CSS and JavaScript that might interact with Bootstrap classes.

Conclusion

This script provides a powerful, automated way to handle a large part of the Bootstrap 3 to 4 migration process, saving developers significant time and reducing the chance of manual errors. It represents the first step in our journey to modernize our legacy C# .NET codebase. Once we've successfully migrated to Bootstrap 4 and ensured stability, we'll tackle the next phase: moving from Bootstrap 4 to 5.

Remember, while automation is incredibly helpful, it's not a substitute for understanding the changes between Bootstrap versions. Use this script as a powerful aid in your migration process, but always couple it with your expertise and thorough testing.

Happy migrating!

版本聲明 本文轉載於:https://dev.to/stokry/modernizing-bootstrap-in-a-c-net-codebase-a-python-powered-migration-from-3-to-5-49ej?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • 如何在保持鼠標輪和箭頭鑰匙滾動的同時隱藏滾動條?
    如何在保持鼠標輪和箭頭鑰匙滾動的同時隱藏滾動條?
    在通過鼠標輪和箭頭鍵啟用滾動時隱藏scrollbars A:要完成此操作,請按照以下步驟:使用CSSS屬性溢出:隱藏在目標div或身體上隱藏滾動條。 模仿鼠標輪滾動: javaScript或jquery。 在函數中,修改目標div的scrolltop屬性以仿真滾動。 : 綁定鍵盤事件(而不是...
    程式設計 發佈於2025-02-07
  • Java是否允許多種返回類型:仔細研究通用方法?
    Java是否允許多種返回類型:仔細研究通用方法?
    在java中的多個返回類型:一個誤解介紹,其中foo是自定義類。該方法聲明似乎擁有兩種返回類型:列表和E。但是,情況確實如此嗎? 通用方法:拆開神秘 [方法僅具有單一的返回類型。相反,它採用機制,如鑽石符號“ ”。 分解方法簽名: :本節定義了一個通用類型參數,E。它表示該方法接受了擴展foo類...
    程式設計 發佈於2025-02-07
  • 如何使用組在MySQL中旋轉數據?
    如何使用組在MySQL中旋轉數據?
    在關係數據庫中使用mysql組使用mysql組來調整查詢結果。在這裡,我們面對一個共同的挑戰:使用組的組將數據從基於行的基於列的基於列的轉換。通過子句以及條件匯總函數,例如總和或情況。讓我們考慮以下查詢: select d.data_timestamp, sum(data_id = 1 tata...
    程式設計 發佈於2025-02-07
  • 如何檢查對像是否具有Python中的特定屬性?
    如何檢查對像是否具有Python中的特定屬性?
    方法來確定對象屬性存在尋求一種方法來驗證對像中特定屬性的存在。考慮以下示例,其中嘗試訪問不確定屬性會引起錯誤: >>> a = someClass() >>> A.property Trackback(最近的最新電話): 文件“ ”,第1行, AttributeError:SomeClass實...
    程式設計 發佈於2025-02-07
  • 我可以在CSS中使用SVG作為偽元素嗎?
    我可以在CSS中使用SVG作為偽元素嗎?
    使用svgs用作pseudo-element content css content properts允許在使用元素之前或之後使用元素插入各種類型的內容偽元素,例如::之前和::之後。但是,對可以包括哪些內容有限制。 可以將svgs用作pseudo-element Content? ,現在可以使...
    程式設計 發佈於2025-02-07
  • 如何從PHP服務器發送文件?
    如何從PHP服務器發送文件?
    將文件發送到user
    程式設計 發佈於2025-02-07
  • 為什麼儘管有效代碼,為什麼在PHP中捕獲輸入?
    為什麼儘管有效代碼,為什麼在PHP中捕獲輸入?
    [2 _post ['ss'];? > 的目的是從單擊提交按鈕時,文本框並顯示。但是,輸出保持空白。當方法=“ get”無縫工作時,方法=“ post”構成問題。 檢查action屬性:如果您正在刷新頁面,請將操作屬性設置為空字符串,例如] action ='&...
    程式設計 發佈於2025-02-07
  • 可以在純CS中將多個粘性元素彼此堆疊在一起嗎?
    可以在純CS中將多個粘性元素彼此堆疊在一起嗎?
    </main> <section> ,但无法使其正常工作,如您所见。任何洞察力都将不胜感激! display:grid; { position:sticky; top:1em; z-index:1 1 ; { { { pos...
    程式設計 發佈於2025-02-07
  • 'exec()
    'exec()
    Exec對本地變量的影響: exec function,python staple,用於動態代碼執行的python staple,提出一個有趣的Query:它可以在函數中更新局部變量嗎? python 3 Dialemma 在Python 3中,以下代碼shippet無法更新本地變量,因為人...
    程式設計 發佈於2025-02-07
  • 如何干淨地刪除匿名JavaScript事件處理程序?
    如何干淨地刪除匿名JavaScript事件處理程序?
    在這里工作/},false); 不幸的是,答案是否。除非在Creation中存儲對處理程序的引用。 要解決此問題,請考慮將事件處理程序存儲在中心位置,例如頁面的主要對象,請考慮將事件處理程序存儲在中心位置,否則無法清理匿名事件處理程序。 。這允許在需要時輕鬆迭代和清潔處理程序。
    程式設計 發佈於2025-02-07
  • 如何在整個HTML文檔中設計特定元素類型的第一個實例?
    如何在整個HTML文檔中設計特定元素類型的第一個實例?
    [2單獨使用CSS,整個HTML文檔可能是一個挑戰。 the:第一型偽級僅限於與其父元素中類型的第一個元素匹配。 以下CSS將使用添加的類樣式的第一個段落: }
    程式設計 發佈於2025-02-07
  • 如何在JavaScript對像中動態設置鍵?
    如何在JavaScript對像中動態設置鍵?
    如何為JavaScript對像變量創建動態鍵,嘗試為JavaScript對象創建動態鍵,使用此Syntax jsObj['key' i] = 'example' 1;將不起作用。正確的方法採用方括號:他們維持一個長度屬性,該屬性反映了數字屬性(索引)和一個數字屬性的數量。標準對像沒有模仿這...
    程式設計 發佈於2025-02-07
  • \“(1)vs.(;;):編譯器優化是否消除了性能差異?\”
    \“(1)vs.(;;):編譯器優化是否消除了性能差異?\”
    使用(1)而不是(;;)會導致無限循環的性能差異? 現代編譯器,(1)和(;;)之間沒有性能差異。 是如何實現這些循環的技術分析在編譯器中: perl: S-> 7 8 unstack v-> 4 -e語法ok 在GCC中,兩者都循環到相同的彙編代碼中,如下所示:。 globl t_時 ...
    程式設計 發佈於2025-02-07
  • 如何使用PHP從XML文件中有效地檢索屬性值?
    如何使用PHP從XML文件中有效地檢索屬性值?
    從php 您的目標可能是檢索“ varnum”屬性值,其中提取數據的傳統方法可能會使您感到困惑。 - > attributes()為$ attributeName => $ attributeValue){ echo $ attributeName,'=“',$ a...
    程式設計 發佈於2025-02-07
  • 為什麼使用Firefox後退按鈕時JavaScript執行停止?
    為什麼使用Firefox後退按鈕時JavaScript執行停止?
    導航歷史記錄問題:JavaScript使用Firefox Back Back 此行為是由瀏覽器緩存JavaScript資源引起的。 To resolve this issue and ensure scripts execute on subsequent page visits, Firefox...
    程式設計 發佈於2025-02-07

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3