This commit is contained in:
2026-07-08 13:46:27 -04:00
parent aca106bfe9
commit 41e737f3ec
2450 changed files with 73125 additions and 3 deletions
@@ -0,0 +1,388 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Converts C# files from block-scoped to file-scoped namespaces and fixes System namespace conflicts.
.DESCRIPTION
This script converts all C# files in MediaBrowser.Model from block-scoped namespaces
to file-scoped namespaces, and replaces System using directives with global::System
to resolve conflicts with MediaBrowser.Model.System namespace.
.PARAMETER Path
The root path to search for C# files. Defaults to "MediaBrowser.Model"
.PARAMETER DryRun
If specified, shows what would be changed without actually modifying files
.PARAMETER Verbose
Shows detailed processing information
.PARAMETER BackupFiles
Creates .bak files before conversion
.EXAMPLE
.\ConvertToFileScopedNamespaces.ps1 -DryRun
.EXAMPLE
.\ConvertToFileScopedNamespaces.ps1 -BackupFiles
.EXAMPLE
.\ConvertToFileScopedNamespaces.ps1 -Path "MediaBrowser.Model\Dlna" -Verbose
#>
param(
[string]$Path = "MediaBrowser.Model",
[switch]$DryRun,
[switch]$BackupFiles
)
$ErrorActionPreference = "Stop"
$VerbosePreference = if ($PSBoundParameters.ContainsKey('Verbose')) { 'Continue' } else { 'SilentlyContinue' }
function Get-IndentString {
param([string]$Line)
if ($Line -match '^(\s+)') {
return $Matches[1]
}
return ""
}
function Get-IndentLevel {
param([string]$IndentString)
# Count spaces (4 spaces = 1 level) or tabs (1 tab = 1 level)
$spaces = ($IndentString.ToCharArray() | Where-Object { $_ -eq ' ' }).Count
$tabs = ($IndentString.ToCharArray() | Where-Object { $_ -eq "`t" }).Count
return [Math]::Max($tabs, [Math]::Floor($spaces / 4))
}
function Remove-OneIndentLevel {
param([string]$Line, [ref]$IndentChar, [ref]$IndentSize)
if ([string]::IsNullOrWhiteSpace($Line)) {
return $Line
}
$indent = Get-IndentString -Line $Line
if ($indent.Length -eq 0) {
return $Line
}
# Detect indent character and size on first indented line
if ($IndentChar.Value -eq $null) {
if ($indent[0] -eq "`t") {
$IndentChar.Value = "`t"
$IndentSize.Value = 1
} else {
$IndentChar.Value = ' '
# Detect indent size (usually 4 spaces)
$IndentSize.Value = 4
if ($indent.Length -ge 2) {
# Try to detect actual indent size
for ($i = 1; $i -le 8; $i++) {
if ($indent.Length % $i -eq 0) {
$IndentSize.Value = $i
break
}
}
}
}
Write-Verbose "Detected indent: '$($IndentChar.Value)' x $($IndentSize.Value)"
}
# Remove one level of indentation
if ($IndentChar.Value -eq "`t") {
if ($Line -match '^\t(.*)') {
return $Matches[1]
}
} else {
$pattern = "^" + (' ' * $IndentSize.Value) + '(.*)'
if ($Line -match $pattern) {
return $Matches[1]
}
}
return $Line
}
function Test-FileScopedNamespace {
param([string]$Content)
# Check if already using file-scoped namespace (semicolon after namespace declaration)
return $Content -match 'namespace\s+[\w\.]+\s*;'
}
function Test-BlockScopedNamespace {
param([string]$Content)
# Check for block-scoped namespace
return $Content -match 'namespace\s+[\w\.]+\s*\r?\n?\s*\{'
}
function Add-GlobalPrefix {
param([string]$Content)
# System namespaces to prefix (ordered from most specific to least)
$systemNamespaces = @(
'System\.Xml\.Serialization',
'System\.ComponentModel\.DataAnnotations',
'System\.ComponentModel',
'System\.Collections\.Generic',
'System\.Collections\.Concurrent',
'System\.Collections',
'System\.Diagnostics\.CodeAnalysis',
'System\.Diagnostics',
'System\.Globalization',
'System\.Linq\.Expressions',
'System\.Linq',
'System\.Text\.Json\.Serialization',
'System\.Text\.Json',
'System\.Text\.RegularExpressions',
'System\.Text',
'System\.Threading\.Tasks',
'System\.Threading',
'System\.Runtime\.Serialization',
'System\.Runtime\.CompilerServices',
'System\.Runtime',
'System\.Net\.Http',
'System\.Net\.Sockets',
'System\.Net\.WebSockets',
'System\.Net\.Mime',
'System\.Net',
'System\.IO\.Compression',
'System\.IO',
'System\.Security',
'System' # Must be last to avoid partial matches
)
foreach ($ns in $systemNamespaces) {
# Don't add global:: if it's already there
# Match: "using System.X" but not "using global::System.X"
$pattern = '(\s+using\s+)(?!global::)(' + $ns + ')(\s|;)'
$replacement = '${1}global::${2}${3}'
$Content = $Content -replace $pattern, $replacement
}
return $Content
}
function Convert-ToFileScopedNamespace {
param(
[string]$FilePath,
[switch]$DryRun,
[switch]$CreateBackup
)
$content = Get-Content -Path $FilePath -Raw
$originalContent = $content
$modified = $false
# Check current state
$hasFileScopedNS = Test-FileScopedNamespace -Content $content
$hasBlockScopedNS = Test-BlockScopedNamespace -Content $content
if ($hasFileScopedNS) {
Write-Verbose " Already using file-scoped namespace"
# But still add global:: prefixes if needed
$contentWithGlobal = Add-GlobalPrefix -Content $content
if ($contentWithGlobal -ne $content) {
$content = $contentWithGlobal
$modified = $true
Write-Host " [UPDATED] Added global:: prefixes" -ForegroundColor Yellow
} else {
Write-Host " [SKIP] Already correct" -ForegroundColor Gray
return $false
}
}
elseif (-not $hasBlockScopedNS) {
Write-Host " [SKIP] No namespace found" -ForegroundColor Gray
return $false
}
else {
Write-Verbose " Converting block-scoped to file-scoped namespace"
# Extract namespace name and line
if ($content -match 'namespace\s+([\w\.]+)\s*\r?\n?\s*\{') {
$namespaceName = $Matches[1]
Write-Verbose " Namespace: $namespaceName"
} else {
Write-Warning " Could not parse namespace"
return $false
}
# Step 1: Convert namespace declaration to file-scoped
$content = $content -replace '(namespace\s+[\w\.]+)\s*\r?\n?\s*\{', '$1;'
$modified = $true
# Step 2: Add global:: prefixes to System using directives
$content = Add-GlobalPrefix -Content $content
# Step 3: Parse into lines for indentation removal
$lines = $content -split '\r?\n'
$hasWindowsLineEndings = $originalContent -match '\r\n'
# Find namespace declaration line
$namespaceLineIndex = -1
for ($i = 0; $i -lt $lines.Length; $i++) {
if ($lines[$i] -match '^namespace\s+[\w\.]+\s*;') {
$namespaceLineIndex = $i
break
}
}
if ($namespaceLineIndex -lt 0) {
Write-Warning " Could not find namespace declaration after conversion"
return $false
}
# Step 4: Remove one level of indentation from all lines after namespace
$indentChar = $null
$indentSize = 4
$indentCharRef = [ref]$indentChar
$indentSizeRef = [ref]$indentSize
for ($i = $namespaceLineIndex + 1; $i -lt $lines.Length; $i++) {
$lines[$i] = Remove-OneIndentLevel -Line $lines[$i] -IndentChar $indentCharRef -IndentSize $indentSizeRef
}
# Step 5: Remove the closing brace of the namespace
# Find the last non-empty, non-whitespace line that's just a closing brace
$closingBraceIndex = -1
for ($i = $lines.Length - 1; $i -gt $namespaceLineIndex; $i--) {
$trimmed = $lines[$i].Trim()
if ($trimmed -eq '}') {
$closingBraceIndex = $i
break
}
elseif ($trimmed -ne '' -and $trimmed -ne '}') {
# Found non-brace content, stop looking
break
}
}
if ($closingBraceIndex -ge 0) {
Write-Verbose " Removing closing brace at line $closingBraceIndex"
$lines = $lines[0..($closingBraceIndex-1)] + $lines[($closingBraceIndex+1)..($lines.Length-1)]
}
# Step 6: Rejoin lines
$lineEnding = if ($hasWindowsLineEndings) { "`r`n" } else { "`n" }
$content = ($lines | Where-Object { $_ -ne $null }) -join $lineEnding
# Step 7: Clean up trailing whitespace and ensure single newline at end
$content = $content.TrimEnd()
$content += $lineEnding
Write-Host " [CONVERTED] Block-scoped → File-scoped" -ForegroundColor Green
}
# Apply changes
if ($modified -and $content -ne $originalContent) {
if ($DryRun) {
Write-Host " [DRY-RUN] Would save changes" -ForegroundColor Cyan
return $true
}
if ($CreateBackup) {
$backupPath = "$FilePath.bak"
Copy-Item -Path $FilePath -Destination $backupPath -Force
Write-Verbose " Created backup: $backupPath"
}
# Write with correct encoding (UTF-8 without BOM for .cs files)
[System.IO.File]::WriteAllText($FilePath, $content, [System.Text.UTF8Encoding]::new($false))
return $true
}
return $false
}
# Main script
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host "C# File-Scoped Namespace Converter" -ForegroundColor Cyan
Write-Host "Fixes MediaBrowser.Model.System namespace conflicts" -ForegroundColor Cyan
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host
if ($DryRun) {
Write-Host "DRY RUN MODE - No files will be modified" -ForegroundColor Yellow
Write-Host
}
if ($BackupFiles) {
Write-Host "Backup mode enabled - .bak files will be created" -ForegroundColor Yellow
Write-Host
}
if (-not (Test-Path $Path)) {
Write-Error "❌ Path not found: $Path"
exit 1
}
# Find all C# files
$files = Get-ChildItem -Path $Path -Filter "*.cs" -Recurse | Where-Object {
$_.FullName -notmatch "\\obj\\" -and
$_.FullName -notmatch "\\bin\\" -and
$_.FullName -notmatch "\\Properties\\AssemblyInfo\.cs$"
}
Write-Host "Found $($files.Count) C# files in $Path" -ForegroundColor Cyan
Write-Host
$convertedCount = 0
$skippedCount = 0
$errorCount = 0
$updatedCount = 0
foreach ($file in $files) {
$relativePath = $file.FullName.Replace((Get-Location).Path, "").TrimStart([char]92, [char]47)
Write-Host "[Processing] $relativePath" -ForegroundColor White
try {
$result = Convert-ToFileScopedNamespace -FilePath $file.FullName -DryRun:$DryRun -CreateBackup:$BackupFiles
if ($result) {
if ($result -eq "updated") {
$updatedCount++
} else {
$convertedCount++
}
} else {
$skippedCount++
}
}
catch {
Write-Host " [ERROR] $_" -ForegroundColor Red
Write-Verbose $_.ScriptStackTrace
$errorCount++
}
}
Write-Host
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host "Summary:" -ForegroundColor Cyan
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host " Total files: $($files.Count)" -ForegroundColor White
Write-Host " Converted: $convertedCount" -ForegroundColor Green
Write-Host " Updated (global::): $updatedCount" -ForegroundColor Yellow
Write-Host " Skipped: $skippedCount" -ForegroundColor Gray
Write-Host " Errors: $errorCount" -ForegroundColor Red
if ($DryRun) {
Write-Host
Write-Host "This was a DRY RUN. Run without -DryRun to apply changes." -ForegroundColor Yellow
} elseif ($convertedCount -gt 0 -or $updatedCount -gt 0) {
Write-Host
Write-Host "Conversion complete! Next steps:" -ForegroundColor Green
Write-Host " 1. Review changes with: git diff" -ForegroundColor White
Write-Host " 2. Build project: dotnet build MediaBrowser.Model\MediaBrowser.Model.csproj" -ForegroundColor White
Write-Host " 3. If successful: git add . && git commit -m `"Convert to file-scoped namespaces`"" -ForegroundColor White
}
Write-Host ("=" * 80) -ForegroundColor Cyan
exit $errorCount