142 lines
4.8 KiB
Python
142 lines
4.8 KiB
Python
import re
|
|||
|
|
import os
|
||
|
|
import sys
|
||
|
|
|
||
|
|
def is_text_prop(tag_str):
|
||
|
|
"""Check if the NButton tag has the `text` prop (bare, :text, or v-bind:text)."""
|
||
|
|
# Remove attribute values to avoid false positives
|
||
|
|
cleaned = re.sub(r'"[^"]*"', '', tag_str)
|
||
|
|
cleaned = re.sub(r"'[^']*'", '', cleaned)
|
||
|
|
return bool(re.search(r'(?:^|[\s>])(?::text|v-bind:text|text)(?=[\s=>/]|$)', cleaned))
|
||
|
|
|
||
|
|
def has_dynamic_class(tag_str):
|
||
|
|
"""Check if the tag uses :class or v-bind:class (dynamic class binding)."""
|
||
|
|
cleaned = re.sub(r'"[^"]*"', '', tag_str)
|
||
|
|
cleaned = re.sub(r"'[^']*'", '', cleaned)
|
||
|
|
return bool(re.search(r'(?::class|v-bind:class)\s*=', cleaned))
|
||
|
|
|
||
|
|
def has_skybtn_class(tag_str):
|
||
|
|
"""Check if the tag already has sky-btn in its class attribute value."""
|
||
|
|
for m in re.finditer(r'class\s*=\s*"([^"]*)"', tag_str):
|
||
|
|
if 'sky-btn' in m.group(1):
|
||
|
|
return True
|
||
|
|
for m in re.finditer(r"class\s*=\s*'([^']*)'", tag_str):
|
||
|
|
if 'sky-btn' in m.group(1):
|
||
|
|
return True
|
||
|
|
return False
|
||
|
|
|
||
|
|
def add_skybtn_to_tag(tag_str):
|
||
|
|
"""Add class="sky-btn" or append sky-btn to existing class."""
|
||
|
|
# Check for existing class="..."
|
||
|
|
m = re.search(r'class\s*=\s*"([^"]*)"', tag_str)
|
||
|
|
if m:
|
||
|
|
old_class = m.group(1).strip()
|
||
|
|
new_class = (old_class + ' sky-btn') if old_class else 'sky-btn'
|
||
|
|
return tag_str[:m.start(1)] + new_class + tag_str[m.end(1):]
|
||
|
|
|
||
|
|
# Check for existing class='...'
|
||
|
|
m = re.search(r"class\s*=\s*'([^']*)'", tag_str)
|
||
|
|
if m:
|
||
|
|
old_class = m.group(1).strip()
|
||
|
|
new_class = (old_class + ' sky-btn') if old_class else 'sky-btn'
|
||
|
|
return tag_str[:m.start(1)] + new_class + tag_str[m.end(1):]
|
||
|
|
|
||
|
|
# No class attribute, add before closing >
|
||
|
|
# Find the outermost > that closes the tag
|
||
|
|
if tag_str.rstrip().endswith('/>'):
|
||
|
|
insert_pos = tag_str.rstrip().rfind('/>')
|
||
|
|
prefix = tag_str[:insert_pos].rstrip()
|
||
|
|
suffix = tag_str[insert_pos:]
|
||
|
|
return prefix + ' class="sky-btn" ' + suffix.lstrip()
|
||
|
|
else:
|
||
|
|
insert_pos = tag_str.rstrip().rfind('>')
|
||
|
|
prefix = tag_str[:insert_pos].rstrip()
|
||
|
|
suffix = tag_str[insert_pos:]
|
||
|
|
return prefix + ' class="sky-btn"' + suffix
|
||
|
|
|
||
|
|
def process_vue_file(filepath):
|
||
|
|
try:
|
||
|
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||
|
|
content = f.read()
|
||
|
|
except Exception as e:
|
||
|
|
print(f" Error reading {filepath}: {e}", file=sys.stderr)
|
||
|
|
return False
|
||
|
|
|
||
|
|
# Find the template section
|
||
|
|
template_start_match = re.search(r'<template>', content)
|
||
|
|
if not template_start_match:
|
||
|
|
return False
|
||
|
|
|
||
|
|
template_end_match = re.search(r'</template>', content)
|
||
|
|
if not template_end_match:
|
||
|
|
return False
|
||
|
|
|
||
|
|
template_start = template_start_match.start()
|
||
|
|
template_end = template_end_match.end()
|
||
|
|
|
||
|
|
template_section = content[template_start:template_end]
|
||
|
|
before = content[:template_start]
|
||
|
|
after = content[template_end:]
|
||
|
|
|
||
|
|
# Match NButton/n-button opening tags with attribute-aware regex
|
||
|
|
tag_pattern = r'<([Nn]-[Bb]utton)\b((?:[^>"\']|"[^"]*"|\'[^\']*\')*)\s*/?>'
|
||
|
|
|
||
|
|
modified_sections = []
|
||
|
|
last_end = 0
|
||
|
|
modified_count = 0
|
||
|
|
|
||
|
|
for m in re.finditer(tag_pattern, template_section):
|
||
|
|
modified_sections.append(template_section[last_end:m.start()])
|
||
|
|
full_match = m.group(0)
|
||
|
|
|
||
|
|
if is_text_prop(full_match):
|
||
|
|
modified_sections.append(full_match)
|
||
|
|
elif has_dynamic_class(full_match):
|
||
|
|
modified_sections.append(full_match)
|
||
|
|
elif has_skybtn_class(full_match):
|
||
|
|
modified_sections.append(full_match)
|
||
|
|
else:
|
||
|
|
new_tag = add_skybtn_to_tag(full_match)
|
||
|
|
modified_sections.append(new_tag)
|
||
|
|
modified_count += 1
|
||
|
|
|
||
|
|
last_end = m.end()
|
||
|
|
|
||
|
|
modified_sections.append(template_section[last_end:])
|
||
|
|
|
||
|
|
if modified_count > 0:
|
||
|
|
new_template_section = ''.join(modified_sections)
|
||
|
|
new_content = before + new_template_section + after
|
||
|
|
with open(filepath, 'w', encoding='utf-8') as f:
|
||
|
|
f.write(new_content)
|
||
|
|
print(f" Modified ({modified_count} changes): {os.path.relpath(filepath, os.getcwd())}")
|
||
|
|
return True
|
||
|
|
|
||
|
|
return False
|
||
|
|
|
||
|
|
def main():
|
||
|
|
cwd = os.getcwd()
|
||
|
|
print(f"Working directory: {cwd}")
|
||
|
|
|
||
|
|
vue_files = []
|
||
|
|
for root, dirs, files in os.walk(os.path.join(cwd, 'frontend', 'src')):
|
||
|
|
for f in files:
|
||
|
|
if f.endswith('.vue'):
|
||
|
|
vue_files.append(os.path.join(root, f))
|
||
|
|
|
||
|
|
print(f"Found {len(vue_files)} Vue files")
|
||
|
|
|
||
|
|
changed_files = []
|
||
|
|
for filepath in sorted(vue_files):
|
||
|
|
if process_vue_file(filepath):
|
||
|
|
changed_files.append(filepath)
|
||
|
|
|
||
|
|
print(f"\nTotal files modified: {len(changed_files)}")
|
||
|
|
for fp in changed_files:
|
||
|
|
print(f" {os.path.relpath(fp, cwd)}")
|
||
|
|
|
||
|
|
return len(changed_files)
|
||
|
|
|
||
|
|
if __name__ == '__main__':
|
||
|
|
main()
|