”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > C# .NET 代码库中的 Bootstrap 现代化:来自 o 5 的 Python 支持的迁移

C# .NET 代码库中的 Bootstrap 现代化:来自 o 5 的 Python 支持的迁移

发布于2024-07-30
浏览:442

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]删除
最新教程 更多>
  • 如何使用组在MySQL中旋转数据?
    如何使用组在MySQL中旋转数据?
    在关系数据库中使用mysql组使用mysql组来调整查询结果。在这里,我们面对一个共同的挑战:使用组的组将数据从基于行的基于列的基于列的转换。通过子句以及条件汇总函数,例如总和或情况。让我们考虑以下查询: select d.data_timestamp, sum(data_id = 1 tata...
    编程 发布于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_时 t_时...
    编程 发布于2025-02-07
  • 如何使用PHP从XML文件中有效地检索属性值?
    如何使用PHP从XML文件中有效地检索属性值?
    从php 您的目标可能是检索“ varnum”属性值,其中提取数据的传统方法可能会使您感到困惑。 - > attributes()为$ attributeName => $ attributeValue){ echo $ attributeName,'=“',$ at...
    编程 发布于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
  • 如何使用替换指令在GO MOD中解析模块路径差异?
    如何使用替换指令在GO MOD中解析模块路径差异?
    克服go mod中的模块路径差异 coreos/bbolt:github.com/coreos/ [email受保护]:解析go.mod:模块将其路径声明为:go.etcd.io/bbolt `要解决此问题,您可以在go.mod文件中使用替换指令。只需在go.mod的末尾添加以下行:[&& &...
    编程 发布于2025-02-07
  • 可以在纯CS中将多个粘性元素彼此堆叠在一起吗?
    可以在纯CS中将多个粘性元素彼此堆叠在一起吗?
    </main> <section> ,但无法使其正常工作,如您所见。任何洞察力都将不胜感激! display:grid; { position:sticky; top:1em; z-index:1 1 ; { { { pos...
    编程 发布于2025-02-07
  • Trie简介(前缀树)
    Trie简介(前缀树)
    Trie 是一种类似树的数据结构,用于有效存储和搜索字符串,尤其是在诸如AutoComplete,Spell Checking和IP路由之类的应用程序中。 Trie的关键特性: nodes :每个节点代表一个字符。 :root节点是空的,并用作起点。 :每个节点最多有26个孩...
    编程 发布于2025-02-07
  • 如何处理PHP MySQL中的分页和随机订购以避免重复和不一致?
    如何处理PHP MySQL中的分页和随机订购以避免重复和不一致?
    [ php mysql分页与随机订购:克服重复和页面一致性挑战 1。在后续页面上排除先前看到的结果,以防止先前显示的结果在随机订购时重新出现在随机订购时:在会话变量中存储ID作为逗号分隔的列表。 修改您的sql查询以排除这些IDS: 2。确保第一页上的不同结果随机订购将使很难在第一页上保证独特的结...
    编程 发布于2025-02-07
  • 哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    在Python 射线tracing方法Matplotlib's path.contains_points FunctionMatplotlib's path.contains_points function employs a路径对象表示多边形。它检查给定点是否位于定义路径内。 T...
    编程 发布于2025-02-07
  • 为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    [2明确担心Microsoft Visual C(MSVC)在正确实现两相模板实例化方面努力努力。该机制的哪些具体方面无法按预期运行?背景:说明:的初始Syntax检查在范围中受到限制。它未能检查是否存在声明名称的存在,导致名称缺乏正确的声明时会导致编译问题。为了说明这一点,请考虑以下示例:一个符合...
    编程 发布于2025-02-07
  • 版本5.6.5之前,使用current_timestamp与时间戳列的current_timestamp与时间戳列有什么限制?
    版本5.6.5之前,使用current_timestamp与时间戳列的current_timestamp与时间戳列有什么限制?
    在默认值中使用current_timestamp或mysql版本中的current_timestamp或在5.6.5 这种限制源于遗产实现的关注,这些限制需要为Current_timestamp功能提供特定的实现。消息和相关问题 `Productid` int(10)unsigned not ...
    编程 发布于2025-02-07

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3