$ErrorActionPreference = 'Stop' $root = Split-Path -Parent $PSScriptRoot $coveragePath = Join-Path $PSScriptRoot 'responsive-layout-coverage.json' $allowlistPath = Join-Path $PSScriptRoot 'responsive-layout-allowlist.json' if (-not (Test-Path -LiteralPath $coveragePath)) { throw 'Missing responsive layout coverage owner' } if (-not (Test-Path -LiteralPath $allowlistPath)) { throw 'Missing responsive layout allowlist owner' } $coverage = Get-Content -LiteralPath $coveragePath -Raw -Encoding UTF8 | ConvertFrom-Json $allowlist = Get-Content -LiteralPath $allowlistPath -Raw -Encoding UTF8 | ConvertFrom-Json $requiredFoundation = @( 'components/AppButton.vue', 'components/AppDialog.vue' ) foreach ($required in $requiredFoundation) { if ($coverage.covered -notcontains $required) { throw "Responsive coverage cannot remove migrated foundation file: $required" } } function Normalize-Selector([string]$selector) { return (($selector -replace '\s+', ' ').Trim()) } function Test-Allowlisted([string]$path, [string]$selector, [string]$property) { $normalizedSelector = Normalize-Selector $selector foreach ($entry in $allowlist.exceptions) { if ($entry.path -eq $path -and (Normalize-Selector $entry.selector) -eq $normalizedSelector -and $entry.property -eq $property) { return $true } } return $false } $violations = [System.Collections.Generic.List[string]]::new() foreach ($relativePath in $coverage.covered) { $fullPath = Join-Path $root $relativePath if (-not (Test-Path -LiteralPath $fullPath)) { $violations.Add("missing covered file: $relativePath") continue } $source = Get-Content -LiteralPath $fullPath -Raw -Encoding UTF8 $styleMatches = [regex]::Matches($source, '(?is)]*>(?.*?)') foreach ($styleMatch in $styleMatches) { $css = $styleMatch.Groups['css'].Value $ruleMatches = [regex]::Matches($css, '(?ms)(?[^{}]+)\{(?[^{}]*)\}') foreach ($rule in $ruleMatches) { $selector = Normalize-Selector $rule.Groups['selector'].Value $body = $rule.Groups['body'].Value $checks = @( @{ Name = 'fixed-height'; Pattern = '(?im)(?