接口开始5%

This commit is contained in:
rain
2026-07-22 17:31:33 +08:00
parent eced3d1e6e
commit 9b0ad62df4
426 changed files with 20111 additions and 28472 deletions
+104
View File
@@ -0,0 +1,104 @@
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
$manifest = 'design-pipeline/manifests/runtime-assets.json'
$validator = 'design-pipeline/scripts/validate-runtime-asset-inventory.mjs'
foreach ($relativePath in @($manifest, $validator)) {
if (-not (Test-Path -LiteralPath (Join-Path $root $relativePath) -PathType Leaf)) {
throw "缺少运行时资产合同文件:$relativePath"
}
}
# 注册表是正式资产物理规格的唯一入口。递归验证同时拒绝缺失、循环、重复导入、重复 id 和重复 output。
$json = & node (Join-Path $root $validator) $manifest
if ($LASTEXITCODE -ne 0) { throw '运行时资产注册表验证失败' }
$inventory = $json | ConvertFrom-Json
$owned = @{}
foreach ($asset in $inventory.assets) {
if ($owned.ContainsKey($asset.output)) { throw "运行时资产存在重复所有者:$($asset.output)" }
$owned[$asset.output] = $asset
}
# static/assets 不允许存在“先留着再说”的文件:每个物理文件都必须有 owner,每个 owner 也必须有真实输出。
$physicalAssets = @(
Get-ChildItem -LiteralPath (Join-Path $root 'static/assets') -Recurse -File |
ForEach-Object { $_.FullName.Substring($root.Length + 1).Replace('\', '/') }
)
$unownedFiles = @($physicalAssets | Where-Object { -not $owned.ContainsKey($_) })
$missingFiles = @($owned.Keys | Where-Object { $_ -notin $physicalAssets })
if ($unownedFiles.Count -gt 0) { throw "存在未登记的 static/assets 文件:$($unownedFiles -join ', ')" }
if ($missingFiles.Count -gt 0) { throw "资产 owner 指向不存在的输出:$($missingFiles -join ', ')" }
Add-Type -AssemblyName System.Drawing
foreach ($asset in $inventory.assets) {
$absolutePath = Join-Path $root $asset.output
$image = [System.Drawing.Image]::FromFile($absolutePath)
try {
if ($image.Width -ne $asset.width -or $image.Height -ne $asset.height) {
throw "运行时资产尺寸漂移:$($asset.output)"
}
$hasAlpha = [System.Drawing.Image]::IsAlphaPixelFormat($image.PixelFormat)
if ($hasAlpha -ne [bool]$asset.alpha) {
throw "运行时资产透明通道漂移:$($asset.output)"
}
} finally {
$image.Dispose()
}
if ($asset.sha256) {
$actualBytes = (Get-Item -LiteralPath $absolutePath).Length
$actualHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $absolutePath).Hash.ToLowerInvariant()
if ($actualBytes -ne $asset.bytes -or $actualHash -ne $asset.sha256) {
throw "直接提交的运行时资产发生漂移:$($asset.output)"
}
} elseif ((Get-Item -LiteralPath $absolutePath).Length -gt $asset.maxBytes) {
throw "清单生成资产超过体积上限:$($asset.output)"
}
}
# 消费者只从运行时代码中提取;清单、测试和文档本身不能替孤立输出制造虚假引用。
$referenceFiles = Get-ChildItem -LiteralPath @(
(Join-Path $root 'pages'),
(Join-Path $root 'components'),
(Join-Path $root 'styles'),
(Join-Path $root 'data'),
(Join-Path $root 'utils')
) -Recurse -File | Where-Object { $_.Extension -in '.vue', '.scss', '.js' }
$referenceCounts = @{}
foreach ($output in $owned.Keys) { $referenceCounts[$output] = 0 }
foreach ($file in $referenceFiles) {
$content = Get-Content -Raw -Encoding UTF8 -LiteralPath $file.FullName
foreach ($match in [regex]::Matches($content, '/?static/assets/[A-Za-z0-9_./-]+\.(?:png|jpe?g|webp|svg)')) {
$assetPath = $match.Value.TrimStart('/')
if (-not $owned.ContainsKey($assetPath)) {
throw "运行时代码引用了无 owner 的资产:$assetPath$($file.FullName.Substring($root.Length + 1))"
}
$referenceCounts[$assetPath]++
}
}
# ModulePage 的两个路径由四个受控模块名动态展开;这里验证模板和有限集合,禁止扫描器漏掉动态消费者。
$modulePagePath = Join-Path $root 'components/ModulePage.vue'
$modulePage = Get-Content -Raw -Encoding UTF8 -LiteralPath $modulePagePath
foreach ($template in @(
'/static/assets/modules/${assetModule.value}/transparent/module-content-frame.png',
'/static/assets/modules/${assetModule.value}/transparent/module-field-frame.png'
)) {
if (-not $modulePage.Contains($template)) { throw "ModulePage 动态资产模板漂移:$template" }
}
foreach ($module in @('family', 'records', 'notification', 'profile')) {
foreach ($fileName in @('module-content-frame.png', 'module-field-frame.png')) {
$assetPath = "static/assets/modules/$module/transparent/$fileName"
if (-not $owned.ContainsKey($assetPath)) { throw "ModulePage 动态资产没有 owner$assetPath" }
$referenceCounts[$assetPath]++
}
}
$orphanedOutputs = @($referenceCounts.Keys | Where-Object { $referenceCounts[$_] -eq 0 })
if ($orphanedOutputs.Count -gt 0) {
throw "资产清单保留了没有运行时消费者的输出:$($orphanedOutputs -join ', ')"
}
Write-Output "RUNTIME-ASSETS-CONTRACT PASS ($($owned.Count) assets)"