diff --git a/docs/modules.md b/docs/modules.md index 54dea92..91df110 100644 --- a/docs/modules.md +++ b/docs/modules.md @@ -119,6 +119,29 @@ powershell tools/package-module.ps1 -Project "src/YourModule/YourModule.csproj" > 说明:该脚本依赖 Docker(会拉取/使用 `mcr.microsoft.com/dotnet/sdk:8.0` 镜像)。首次执行会比较慢属正常现象。 +### 轻量打包(推荐) + +模块运行时会与宿主共享一批“边界程序集”(例如 `TelegramPanel.*`、`Microsoft.Extensions.*`、`Microsoft.AspNetCore.*`、`MudBlazor` 等)。 +这些程序集即使被打进模块包里,宿主也会强制从 Default ALC 解析(避免类型身份不一致),因此**携带它们只会徒增包体积**。 + +打包时可加 `-Slim` 开关自动剔除这类共享程序集: + +```powershell +powershell tools/package-module.ps1 -Project "src/YourModule/YourModule.csproj" -Manifest "src/YourModule/manifest.json" -Slim +``` + +### 更激进的轻量打包(仅限 TelegramPanel 宿主) + +如果确定目标宿主就是 TelegramPanel 主程序(必带 EFCore/Sqlite/WTelegramClient 等依赖),并且你希望把模块包做到尽可能小,可以使用 `-SlimHost`: + +- 额外剔除:`Microsoft.EntityFrameworkCore*`、`Microsoft.Data.Sqlite`、`SQLitePCLRaw*`、`WTelegramClient`、`SixLabors.ImageSharp`、`PhoneNumbers` 等宿主内置依赖 +- 剔除 `runtimes/`(多平台 SQLite native,体积占比很高) +- 剔除 `wwwroot/_content/MudBlazor`(静态资源由宿主提供) + +```powershell +powershell tools/package-module.ps1 -Project "src/YourModule/YourModule.csproj" -Manifest "src/YourModule/manifest.json" -SlimHost +``` + ## manifest.json(示例) ```json diff --git a/src/TelegramPanel.Web/Modules/ModuleLoadContext.cs b/src/TelegramPanel.Web/Modules/ModuleLoadContext.cs index de1e646..35023fe 100644 --- a/src/TelegramPanel.Web/Modules/ModuleLoadContext.cs +++ b/src/TelegramPanel.Web/Modules/ModuleLoadContext.cs @@ -32,7 +32,14 @@ public sealed class ModuleLoadContext : AssemblyLoadContext || name.StartsWith("Microsoft.AspNetCore.", StringComparison.OrdinalIgnoreCase) || name.StartsWith("Microsoft.JSInterop", StringComparison.OrdinalIgnoreCase) || name.StartsWith("TelegramPanel.", StringComparison.OrdinalIgnoreCase) - || string.Equals(name, "MudBlazor", StringComparison.OrdinalIgnoreCase)) + || string.Equals(name, "MudBlazor", StringComparison.OrdinalIgnoreCase) + // TelegramPanel 宿主内置依赖:模块可直接复用宿主版本,避免重复携带导致包体积膨胀。 + || string.Equals(name, "WTelegramClient", StringComparison.OrdinalIgnoreCase) + || string.Equals(name, "PhoneNumbers", StringComparison.OrdinalIgnoreCase) + || string.Equals(name, "SixLabors.ImageSharp", StringComparison.OrdinalIgnoreCase) + || string.Equals(name, "Microsoft.Data.Sqlite", StringComparison.OrdinalIgnoreCase) + || name.StartsWith("Microsoft.EntityFrameworkCore", StringComparison.OrdinalIgnoreCase) + || name.StartsWith("SQLitePCLRaw", StringComparison.OrdinalIgnoreCase)) return null; // 2) 其次:如果宿主已经加载过同名程序集,则同样交给 Default ALC(避免重复加载)。 diff --git a/tools/package-module.ps1 b/tools/package-module.ps1 index 77cc204..98c30d0 100644 --- a/tools/package-module.ps1 +++ b/tools/package-module.ps1 @@ -6,7 +6,17 @@ param( [string]$Manifest, [Parameter(Mandatory = $false)] - [string]$OutDir = "artifacts/modules" + [string]$OutDir = "artifacts/modules", + + # 轻量打包:剔除会由宿主共享加载的程序集(减少包体积)。 + # 说明:宿主的 ModuleLoadContext 会强制让这些程序集由 Default ALC 解析,模块包携带它们只会徒增体积。 + [Parameter(Mandatory = $false)] + [switch]$Slim, + + # 更激进的轻量打包:额外剔除宿主已内置的第三方依赖(EFCore/Sqlite/WTelegramClient 等)与多平台 native runtimes。 + # 适用场景:目标宿主就是 TelegramPanel 主程序(必带这些依赖),并且部署环境固定(例如 Docker linux-x64)。 + [Parameter(Mandatory = $false)] + [switch]$SlimHost ) $ErrorActionPreference = "Stop" @@ -28,10 +38,18 @@ $moduleId = $moduleId.Trim() $version = $version.Trim() if ([string]::IsNullOrWhiteSpace($moduleId)) { throw "manifest.json 缺少 id" } if ([string]::IsNullOrWhiteSpace($version)) { throw "manifest.json 缺少 version" } +$entryAssembly = "" +try { + if ($null -ne $manifestObj.entry -and $null -ne $manifestObj.entry.assembly) { $entryAssembly = [string]$manifestObj.entry.assembly } +} catch { } +$entryAssembly = [string]$entryAssembly +$entryAssembly = $entryAssembly.Trim() +if ([string]::IsNullOrWhiteSpace($entryAssembly)) { throw "manifest.json 缺少 entry.assembly" } $buildRootRel = "artifacts/_modulebuild/$moduleId/$version" -$publishRel = "$buildRootRel/publish" -$stagingRel = "$buildRootRel/staging" +$runId = ([Guid]::NewGuid().ToString("N")) +$publishRel = "$buildRootRel/publish/$runId" +$stagingRel = "$buildRootRel/staging/$runId" $publishHost = Join-Path $repoRoot $publishRel $stagingHost = Join-Path $repoRoot $stagingRel @@ -56,6 +74,72 @@ New-Item -ItemType Directory -Force -Path $stagingLib | Out-Null Copy-Item -Path $manifestPath -Destination (Join-Path $stagingHost "manifest.json") -Force Copy-Item -Path (Join-Path $publishHost "*") -Destination $stagingLib -Recurse -Force +if ($Slim -or $SlimHost) +{ + # 与宿主共享的“边界程序集”(见 src/TelegramPanel.Web/Modules/ModuleLoadContext.cs)。 + # 模块包携带这些 dll 会导致体积膨胀(同时也容易造成类型身份不一致的风险),因此在打包阶段直接剔除。 + $sharedPatterns = @( + "Microsoft.AspNetCore.*.dll", + "Microsoft.Extensions.*.dll", + "Microsoft.JSInterop*.dll", + "TelegramPanel.*.dll", + "MudBlazor*.dll" + ) + + foreach ($pattern in $sharedPatterns) + { + Get-ChildItem -Path $stagingLib -Filter $pattern -File -ErrorAction SilentlyContinue | ForEach-Object { + if ($_.Name -ieq $entryAssembly) { return } + try { Remove-Item -Path $_.FullName -Force -ErrorAction Stop } catch { } + } + + $pdbPattern = [System.IO.Path]::ChangeExtension($pattern, ".pdb") + Get-ChildItem -Path $stagingLib -Filter $pdbPattern -File -ErrorAction SilentlyContinue | ForEach-Object { + try { Remove-Item -Path $_.FullName -Force -ErrorAction Stop } catch { } + } + } +} + +if ($SlimHost) +{ + # 宿主已内置且通常在启动早期加载的依赖:把这些也剔除可大幅减小体积(尤其是 SQLitePCL 的多平台 runtimes/native)。 + $hostBuiltInPatterns = @( + "Microsoft.EntityFrameworkCore*.dll", + "Microsoft.Data.Sqlite*.dll", + "SQLitePCLRaw*.dll", + "WTelegramClient*.dll", + "SixLabors.ImageSharp*.dll", + "PhoneNumbers*.dll" + ) + + foreach ($pattern in $hostBuiltInPatterns) + { + Get-ChildItem -Path $stagingLib -Filter $pattern -File -ErrorAction SilentlyContinue | ForEach-Object { + if ($_.Name -ieq $entryAssembly) { return } + try { Remove-Item -Path $_.FullName -Force -ErrorAction Stop } catch { } + } + + $pdbPattern = [System.IO.Path]::ChangeExtension($pattern, ".pdb") + Get-ChildItem -Path $stagingLib -Filter $pdbPattern -File -ErrorAction SilentlyContinue | ForEach-Object { + try { Remove-Item -Path $_.FullName -Force -ErrorAction Stop } catch { } + } + } + + # 多平台 native runtimes(SQLite)体积很大;TelegramPanel 宿主自身已携带,这里直接剔除。 + $runtimesDir = Join-Path $stagingLib "runtimes" + if (Test-Path -Path $runtimesDir) + { + try { Remove-Item -Path $runtimesDir -Recurse -Force -ErrorAction Stop } catch { } + } + + # MudBlazor 静态资源由宿主提供;模块包里携带的这份没有实际用途,顺手剔除。 + $mudWwwroot = Join-Path $stagingLib "wwwroot/_content/MudBlazor" + if (Test-Path -Path $mudWwwroot) + { + try { Remove-Item -Path $mudWwwroot -Recurse -Force -ErrorAction Stop } catch { } + } +} + New-Item -ItemType Directory -Force -Path $outRoot | Out-Null $dest = Join-Path $outRoot "$moduleId-$version.tpm" if (Test-Path -Path $dest) { Remove-Item -Path $dest -Force }