commit changes to project file
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user