CaiWuXiTong/scripts/build-frontend.ps1

117 lines
4.0 KiB
PowerShell

[CmdletBinding()]
param(
[string]$NodeVersion = '20.20.2',
[int]$MaxOldSpaceSizeMB = 8192
)
$ErrorActionPreference = 'Stop'
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
$toolRoot = Join-Path $env:LOCALAPPDATA 'CaiWuXiTong\tools'
$nodeFolderName = "node-v$NodeVersion-win-x64"
$nodeRoot = Join-Path $toolRoot $nodeFolderName
$nodeExe = Join-Path $nodeRoot 'node.exe'
$npmCmd = Join-Path $nodeRoot 'npm.cmd'
$archiveName = "$nodeFolderName.zip"
$archivePath = Join-Path $toolRoot $archiveName
$checksumPath = Join-Path $toolRoot 'SHASUMS256.txt'
$trackedCssPath = Join-Path $repoRoot 'public/css/app.css'
$frontendOutputRoot = Join-Path $repoRoot '.caiwu-build/public'
function Invoke-Native {
param(
[Parameter(Mandatory = $true)]
[scriptblock]$Command,
[Parameter(Mandatory = $true)]
[string]$FailureMessage
)
& $Command
if ($LASTEXITCODE -ne 0) {
throw $FailureMessage
}
}
if (-not (Test-Path -LiteralPath $nodeExe)) {
New-Item -ItemType Directory -Force -Path $toolRoot | Out-Null
try {
$downloadRoot = "https://nodejs.org/dist/v$NodeVersion"
Invoke-WebRequest -Uri "$downloadRoot/$archiveName" -OutFile $archivePath
Invoke-WebRequest -Uri "$downloadRoot/SHASUMS256.txt" -OutFile $checksumPath
$checksumLine = Select-String -Path $checksumPath -Pattern ([regex]::Escape($archiveName) + '$') |
Select-Object -First 1
if (-not $checksumLine) {
throw 'Node 安装包缺少官方 SHA-256 校验值。'
}
$expectedHash = ($checksumLine.Line -split '\s+')[0].ToLowerInvariant()
$actualHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $archivePath).Hash.ToLowerInvariant()
if ($actualHash -ne $expectedHash) {
throw 'Node 安装包 SHA-256 校验失败。'
}
Expand-Archive -LiteralPath $archivePath -DestinationPath $toolRoot -Force
}
finally {
if (Test-Path -LiteralPath $archivePath) {
Remove-Item -LiteralPath $archivePath -Force
}
if (Test-Path -LiteralPath $checksumPath) {
Remove-Item -LiteralPath $checksumPath -Force
}
}
}
if (-not (Test-Path -LiteralPath $nodeExe) -or -not (Test-Path -LiteralPath $npmCmd)) {
throw "项目专用 Node $NodeVersion 安装不完整。"
}
$originalPath = $env:Path
$originalNodeOptions = $env:NODE_OPTIONS
$trackedCssBackup = if (Test-Path -LiteralPath $trackedCssPath) {
[IO.File]::ReadAllBytes($trackedCssPath)
}
else {
$null
}
Push-Location $repoRoot
try {
$env:Path = "$nodeRoot;$originalPath"
$env:NODE_OPTIONS = "--max-old-space-size=$MaxOldSpaceSizeMB"
Invoke-Native { & $nodeExe --version } '项目专用 Node 无法运行。'
Invoke-Native {
& $npmCmd ci --prefer-offline --no-audit --no-fund
} '前端依赖安装失败。'
Invoke-Native { & $npmCmd run production } '前端资源构建失败。'
$requiredOutputs = @(
'public/js/auth/common.min.js',
'public/css/app.css',
'public/mix-manifest.json'
)
foreach ($output in $requiredOutputs) {
if (-not (Test-Path -LiteralPath (Join-Path $repoRoot $output))) {
throw "前端构建缺少产物:$output"
}
}
if (Test-Path -LiteralPath $frontendOutputRoot) {
Remove-Item -LiteralPath $frontendOutputRoot -Recurse -Force
}
New-Item -ItemType Directory -Force -Path (Join-Path $frontendOutputRoot 'css') | Out-Null
Copy-Item -LiteralPath (Join-Path $repoRoot 'public/js') -Destination $frontendOutputRoot -Recurse
Copy-Item -LiteralPath (Join-Path $repoRoot 'public/css/app.css') -Destination (Join-Path $frontendOutputRoot 'css/app.css')
Copy-Item -LiteralPath (Join-Path $repoRoot 'public/mix-manifest.json') -Destination $frontendOutputRoot
}
finally {
Pop-Location
$env:Path = $originalPath
$env:NODE_OPTIONS = $originalNodeOptions
if ($null -ne $trackedCssBackup) {
[IO.File]::WriteAllBytes($trackedCssPath, $trackedCssBackup)
}
}