refactor: Common.psm1 拆成 BakNRet/{Public,Private},一函数一文件 + 薄加载器

3152 行、66 个函数的单文件模块拆成:
  BakNRet\BakNRet.psd1   模块清单:FunctionsToExport 是显式白名单(62 个名字)
  BakNRet\BakNRet.psm1   加载器:点源顺序的唯一一处声明
  BakNRet\Public\*.ps1   62 个对外函数,一函数一文件,文件名 = 函数名
  BakNRet\Private\*.ps1  4 个内部函数 + State.ps1(模块级状态集中一处)

为什么是一函数一文件:这是社区里脚本模块的主流形态(调研实测:winutil 79 个、
Terminal-Icons 24 个、ModuleBuilder 23 个,全部如此)。收益是改动落在小文件里、diff 按职责
可读、模块级状态有唯一去处。

为什么这不算"打散":模块内 dot-source 的文件共享同一个模块作用域(实测确认),所以
"按顺序点源 67 个文件"与"点源一个大文件"在语义上等价;顺序只在加载器里出现一次,
tools\Build-BakNRetModule.ps1 从那里读出顺序就能拼回单文件 —— 本次产物 dist\BakNRet.psm1
3240 行、两个版本都解析零错。

新增一条断言把这条承诺钉住:加载器点源的文件集合必须与磁盘一致、导出名单必须与
Public\ 一一对应。漏一个文件或漏一个名字就是静默少一个函数 —— 而那种错在运行时只表现为
"找不到命令"。

引用更新:11 个文件里的 Common.psm1 改成 BakNRet\BakNRet.psd1(走清单导入,
FunctionsToExport 才真的说了算);Common.psm1 直接删除,不留转发垫片。

验收:test.ps1 9/9 全绿(7 与 5.1),98 个文件两版解析零错,276 个断言原样通过 ——
这次搬家没有改变任何可观察行为。
This commit is contained in:
Shuery committed 2026-09-27 09:34:10 +08:00
1 parent 78f43c8cc9
commit 2ad7987ffe
83 files changed
+3547 -3310

No files matched your search

+1 -1
View File
@@ -30,7 +30,7 @@ BeforeAll {
$script:RestoreScript = Join-Path $script:ProjectRoot 'Restore.ps1'
$script:SevenZip = Get-Command 7z -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Source
Import-Module (Join-Path $script:ProjectRoot 'Common.psm1') -Force
Import-Module (Join-Path $script:ProjectRoot 'BakNRet\BakNRet.psd1') -Force
$script:Sandbox = Join-Path $env:TEMP ('baknret-formats-' + [guid]::NewGuid().ToString('N').Substring(0, 8))
New-Item -ItemType Directory -Path $script:Sandbox -Force | Out-Null
+1 -1
View File
@@ -28,7 +28,7 @@ BeforeAll {
$script:BackupScript = Join-Path $script:ProjectRoot 'Backup.ps1'
$script:RestoreScript = Join-Path $script:ProjectRoot 'Restore.ps1'
Import-Module (Join-Path $script:ProjectRoot 'Common.psm1') -Force
Import-Module (Join-Path $script:ProjectRoot 'BakNRet\BakNRet.psd1') -Force
$script:Sandbox = Join-Path $env:TEMP ('baknret-acl-' + [guid]::NewGuid().ToString('N').Substring(0, 8))
New-Item -ItemType Directory -Path $script:Sandbox -Force | Out-Null
+1 -1
View File
@@ -41,7 +41,7 @@ BeforeAll {
$script:RestoreScript = Join-Path $script:ProjectRoot 'Restore.ps1'
$script:SevenZip = Get-Command 7z -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Source
Import-Module (Join-Path $script:ProjectRoot 'Common.psm1') -Force
Import-Module (Join-Path $script:ProjectRoot 'BakNRet\BakNRet.psd1') -Force
$script:Sandbox = Join-Path $env:TEMP ('baknret-pester-' + [guid]::NewGuid().ToString('N').Substring(0, 8))
New-Item -ItemType Directory -Path $script:Sandbox -Force | Out-Null
+1 -1
View File
@@ -75,7 +75,7 @@ if (-not $ConfigPath) { $ConfigPath = Join-Path $projectRoot 'BackupConfig.psd1'
$restoreScript = Join-Path $projectRoot 'Restore.ps1'
Import-Module (Join-Path $projectRoot 'Common.psm1') -Force
Import-Module (Join-Path $projectRoot 'BakNRet\BakNRet.psd1') -Force
if (-not (Test-Path -LiteralPath $restoreScript)) {
Write-Error "找不到 Restore.ps1:$restoreScript"
+1 -1
View File
@@ -37,7 +37,7 @@ Import-Module (Join-Path $PSScriptRoot 'TestHelpers.psm1') -Force
$projectRoot = Split-Path -Parent $PSScriptRoot
$backupScript = Join-Path $projectRoot 'Backup.ps1'
$restoreScript = Join-Path $projectRoot 'Restore.ps1'
Import-Module (Join-Path $projectRoot 'Common.psm1') -Force
Import-Module (Join-Path $projectRoot 'BakNRet\BakNRet.psd1') -Force
Reset-TestResult
+1 -1
View File
@@ -37,7 +37,7 @@ param(
$ErrorActionPreference = 'Stop'
$projectRoot = Split-Path -Parent $PSScriptRoot
if (-not $ModulePath) { $ModulePath = Join-Path $projectRoot 'Common.psm1' }
if (-not $ModulePath) { $ModulePath = Join-Path $projectRoot 'BakNRet\BakNRet.psd1' }
Import-Module $ModulePath -Force
Import-Module (Join-Path $PSScriptRoot 'TestHelpers.psm1') -Force
+42 -1
View File
@@ -35,7 +35,7 @@ $ErrorActionPreference = 'Stop'
# 参数绑定阶段还没有给 $PSScriptRoot 赋值,默认值表达式会拿到空串(实测:带
# [CmdletBinding()] -> 空串,不带 -> 正常;PowerShell 7 两种都正常)。所以默认值
# 一律在这里补 —— 这也是本仓库对 -BackupDir / -ConfigPath 一直在用的写法。
if (-not $ModulePath) { $ModulePath = Join-Path (Split-Path -Parent $PSScriptRoot) 'Common.psm1' }
if (-not $ModulePath) { $ModulePath = Join-Path (Split-Path -Parent $PSScriptRoot) 'BakNRet\BakNRet.psd1' }
Import-Module $ModulePath -Force
Import-Module (Join-Path $PSScriptRoot 'TestHelpers.psm1') -Force
@@ -1314,6 +1314,47 @@ Test-Case '运行锁:释放之后可以重新取得' {
}
}
Test-Case '模块可以合回单文件:加载器的顺序声明完整、导出名单与 Public\ 一一对应' {
# 这是"拆分可逆"那条承诺的守卫。顺序只在加载器里出现一次、导出面只在清单里出现一次,
# 两者都必须与磁盘上的文件对得上 —— 否则哪天有人加了文件却忘了点源,工具照样能跑,
# 而"合回单文件"会静默少一个函数。
$repoRoot = Split-Path -Parent $PSScriptRoot
$moduleRoot = Join-Path $repoRoot 'BakNRet'
$loaderPath = Join-Path $moduleRoot 'BakNRet.psm1'
Assert-FileExists $loaderPath
$loader = @(Get-Content -Encoding UTF8 -LiteralPath $loaderPath)
# 用 Split 取路径,不用正则:这一行里同时有引号与反斜杠,正则嵌进测试文件之后
# 多一层转义,很容易取到 0 个(第一版就是这样,而手工核对是 67 个)。
$sources = @()
foreach ($line in $loader) {
if ($line -like '*Join-Path*' -and $line -like "*.ps1')*") {
$quoted = $line.Split("'")
if ($quoted.Count -ge 3) { $sources += $quoted[1] }
}
}
Assert-True ($sources.Count -ge 60) "加载器里只点源了 $($sources.Count) 个文件,太少"
foreach ($rel in $sources) { Assert-FileExists (Join-Path $moduleRoot ($rel -replace '/', '\')) }
# 磁盘上的每个函数文件都必须被点源到 —— 漏掉一个就是静默少一个函数
$onDisk = @(Get-ChildItem -LiteralPath (Join-Path $moduleRoot 'Public'), (Join-Path $moduleRoot 'Private') -Filter *.ps1 |
ForEach-Object { "$($_.Directory.Name)\$($_.Name)" })
$missingFromLoader = @($onDisk | Where-Object { $sources -notcontains $_ })
Assert-Equal 0 $missingFromLoader.Count ("这些文件没被加载器点源:" + ($missingFromLoader -join '、'))
# 导出名单必须与 Public\ 下的文件一一对应(少一个就是静默不导出)
$exported = @()
foreach ($line in $loader) {
$trimmed = $line.Trim()
if ($trimmed.StartsWith("'")) { $exported += $trimmed.TrimStart("'").TrimEnd("'", ',') }
}
$publicNames = @(Get-ChildItem -LiteralPath (Join-Path $moduleRoot 'Public') -Filter *.ps1 | ForEach-Object { $_.BaseName })
$notExported = @($publicNames | Where-Object { $exported -notcontains $_ })
Assert-Equal 0 $notExported.Count ("Public\ 下有文件没写进导出名单:" + ($notExported -join '、'))
$noFile = @($exported | Where-Object { $publicNames -notcontains $_ })
Assert-Equal 0 $noFile.Count ("导出名单里有名字没有对应文件:" + ($noFile -join '、'))
}
# ============================================================================
Remove-Item -LiteralPath $sandbox -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $itemSandbox -Recurse -Force -ErrorAction SilentlyContinue