ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Halo 主题 UI 插件打包指南:为 `@halo-dev/ui-plugin-bundler-kit` 引入 theme provider 构建默认值

Halo 主题 UI 插件打包指南:为 `@halo-dev/ui-plugin-bundler-kit` 引入 theme provider 构建默认值 Halo 主题 UI 插件打包指南为halo-dev/ui-plugin-bundler-kit引入 theme provider 构建默认值【免费下载链接】haloHalo 是一款强大易用的开源建站工具从个人博客、知识库到企业官网、在线商城Halo 都能助您轻松实现一站式满足您的多样化建站需求。项目地址: https://gitcode.com/GitHub_Trending/ha/halo自 Halo 主题可以在运行时提供 Console管理后台与 User Center个人中心的 UI 插件资源后主题作者便拥有了与插件一致的前端扩展能力。但halo-dev/ui-plugin-bundler-kit长期只按插件项目产出构建配置主题作者必须手写 Vite/Rsbuild 细节才能让产物匹配主题运行时的资源契约。本文基于仓库中的变更提案 openspec/changes/archive/2026-06-08-support-theme-ui-plugin-bundling/proposal.md 及其设计文档、验收规格与源码实现完整讲解provider模型的引入、主题 provider 的各项目构建默认值、推荐的项目目录结构以及背后的运行时资源契约与测试验证。读完你可以在自己的主题仓库中直接落地一个可构建、可被 Halo 运行时加载的ui-plugin前端工程。背景主题提供 UI 插件需要一等公民的构建支持主题提供的 Console / UC UI 插件与插件的前端模块使用相同的PluginModule形态但二者的资源模型不同插件构建产物通过插件自身的静态资源映射加载主题构建产物的运行时资源位置固定为{themeRoot}/{themeName}/ui-plugin/dist/**资源通过以下地址对外暴露/themes/{themeName}/ui-plugin/assets/**Halo 运行时只读取主题包中ui-plugin/dist/**这一目录。旧版halo-dev/ui-plugin-bundler-kit假定项目一定是插件工程它读取插件清单plugin.yaml、按插件规则输出 bundle并把资源 public path 配置到插件静态目录。主题作者虽然可以手工绕过这些默认值但每次都手写 Vite/Rsbuild 的base、输出目录、模块名与 public path既容易出错也偏离了该工具包「即开即用」的定位。因此变更提案给出的方向是让同一套现代配置 API 同时服务插件与主题两类「provider」使主题作者也能用官方默认值生成与主题运行时资源契约完全匹配的 bundle而无需手写任何打包器细节。provider 模型一个配置 API两个提供方提案在halo-dev/ui-plugin-bundler-kit的现代 Vite 与 Rsbuild 配置辅助函数上新增一个可选配置项provider?: plugin | theme;该字段默认是plugin以保证既有插件调用完全不受影响// 等价于 provider: plugin已有调用保持原行为 viteConfig({ vite: {} }); rsbuildConfig({ rsbuild: {} });主题项目则显式选择provider: themeviteConfig({ provider: theme, vite: {} }); rsbuildConfig({ provider: theme, rsbuild: {} });在源码层面vite.ts 的实现 用getProvider()读取config?.provider || plugin再按 provider 分支选择清单路径。设计文档曾考虑过拆分成独立的themeViteConfig/themeRsbuildConfig导出但最终被否决插件与主题本质上是同一个前端模块契约的两个提供方用provider字段表达比拆分 API 更准确地反映这一点。为什么不自动探测 provider工具包不会因为目录下存在theme.yaml或plugin.yaml就自动切换行为。设计文档给出的理由很明确Auto-detection would make builds sensitive to directory layout and could surprise existing plugin projects.自动探测会让构建结果受目录布局影响并可能悄悄改变既有插件工程的输出因此显式选择provider能保证行为可预期、可复现。遗留 helper 不支持主题被标记为弃用的HaloUIPluginBundlerKit从 index.ts 导出实现位于legacy.ts不会获得主题支持。它仍承担旧版插件 IIFE 产物的生产兼容职责扩展它既会放大兼容矩阵也会诱导新主题工程采用已弃用的 API。现代工程应从构建系统专属入口导入import { viteConfig } from halo-dev/ui-plugin-bundler-kit/vite; import { rsbuildConfig } from halo-dev/ui-plugin-bundler-kit/rsbuild;自 2.26.0 起从包根入口导入的viteConfig/rsbuildConfig已被弃用并将于 2.27.0 移除。主题 provider 的构建默认值一览主题 provider 不去解析spec.requires的插件 bundle 位置兼容逻辑因为主题 UI 是一个只有单一资源位置ui-plugin/dist/**的新运行时特性。它在 utils/halo-plugin.ts 中只从主题清单解析必要的metadata.name再派生模块名与资源 public path。下表是 proposal.md、design.md 与 tasks.md 共同定义的默认值集合维度插件 provider保持原有行为主题 provider默认清单路径../src/main/resources/plugin.yaml../theme.yaml输出目录dev / prod由 bundle location 决定见下dist/dist模块global名清单metadata.nametheme:{metadata.name}Vitebase/ Rsbuild public path插件静态资源路径IIFE 时代留空/themes/{metadata.name}/ui-plugin/assets/主产物文件名IIFE 稳定main.jsstyle.cssIIFE 同样为main.jsstyle.cssESM 使用内容哈希名产物解析位置spec.requires 2.25.0时../build/resources/main/ui否则../build/resources/main/consoledevprod 为./build/distHalo 运行时固定读取ui-plugin/dist/**默认清单路径常量定义在 constants/halo-plugin.tsconst DEFAULT_PLUGIN_MANIFEST_PATH ../src/main/resources/plugin.yaml; const DEFAULT_THEME_MANIFEST_PATH ../theme.yaml;对于非标准目录布局两条路径均可用manifestPath显式覆盖例如viteConfig({ provider: theme, manifestPath: ../custom-theme.yaml, vite: {}, });推荐的 theme-root / ui-plugin 项目布局../theme.yaml这一相对默认值假定主题 UI 插件前端工程位于主题根的ui-plugin/子目录下theme-root/ ├── theme.yaml └── ui-plugin/ ├── package.json ├── src/index.ts └── vite.config.ts用 Rsbuild 时把vite.config.ts换成rsbuild.config.ts即可。theme.yaml中必须包含可供派生模块名与资源路径的metadata.name例如metadata.name: earth将得到模块名theme:earth与资源路径/themes/earth/ui-plugin/assets/。最小可用配置让主题 UI 插件真正构建起来在ui-plugin/前端工程中安装依赖后工具包本身见 ui/packages/ui-plugin-bundler-kit/README.mdVite 主题工程的最小vite.config.ts是import { viteConfig } from halo-dev/ui-plugin-bundler-kit/vite; export default viteConfig({ provider: theme, vite: {}, });Rsbuild 主题工程最小rsbuild.config.ts是import { rsbuildConfig } from halo-dev/ui-plugin-bundler-kit/rsbuild; export default rsbuildConfig({ provider: theme, rsbuild: {}, });工具包会自动完成四件事读取上一级../theme.yaml、把产物输出到dist、以theme:{metadata.name}注册模块、把资源 public path 指向/themes/{metadata.name}/ui-plugin/assets/。配套的package.json脚本建议如下{ scripts: { dev: vite dev --modedevelopment --watch, build: vite build } }Rsbuild 工程相应为rsbuild dev --env-modedevelopment --watch与rsbuild build。开发模式下 Vite 主题 provider 同样输出到dist开发构建采用--modedevelopment便于把dist整体作为ui-plugin/dist/**供给 Halo 运行时。从 vite.ts 的 createVitePresetsConfig 可以看到预设的本质先按 provider 取默认值、做格式选择、必要时挑选宿主运行时快照然后返回一个带base、内置 Vue 插件与build.outDir等预设的配置函数viteConfig最后通过mergeConfig(presetsConfig, userConfig)把用户配置合并到预设之后vite.ts因此任何自定义的resolve.alias、额外插件、outDir、public path 覆盖都能生效且采用打包器原生的合并语义——工具包不会因为覆盖与默认契约冲突而拒绝、改写或告警。提示在 README 与设计文档中均有明确警告——覆盖主题 public path 可能破坏动态 chunk 与静态资源的相对解析。预设覆盖后产物的正确性由开发者自负默认契约不再成立。输出格式自动选择IIFE 与 ESMviteConfig/rsbuildConfig还接收统一的format选项format?: auto | iife | esm; // 默认 auto主题与插件共享同一套格式选择语义实现在 utils/halo-plugin.ts 的 selectProviderFormatauto默认把清单spec.requires解析为简单稳定版本或MAJOR.MINOR.PATCH目标若最低版本不低于 Halo2.26.0源码中的ESM_PROVIDER_MIN_HALO_VERSION输出 ESM若缺失、通配、复合或不支持的版本范围则发出告警并回退到兼容的 IIFE 输出iife显式选择遗留 IIFE 输出保留旧的main.js、global 名、externals 与 bundle 位置兼容行为不需要解析spec.requiresesm显式强制 ESM。当无法从spec.requires推导出目标版本时必须额外提供targetHaloVersion否则构建直接报错若目标版本早于 ESM UI provider 支持的起点会输出强兼容性告警但不会静默改写为 IIFE。// 强制 ESM 且无法从 spec.requires 推导目标时 viteConfig({ format: esm, targetHaloVersion: 2.26.0, vite: {}, });选择结果会通过[ui-plugin-bundler-kit] Output: ESM (automatic; target Halo 2.26.0).之类的日志输出格式与目标版本对开发者透明可见。选择 ESM 后构建产物会额外生成保留文件名ui-plugin.jsonprovider manifest记录实际产出的入口与可选样式表路径该文件名被工具包保留不要手工创建或拷贝同名文件——缺少该文件的产物即便spec.requires支持 2.26仍按遗留产物处理。IIFE 输出则不会生成该 manifest避免 Halo 误判产物类型。ESM 产物要求入口默认导出标准PluginModule并使用内容哈希的入口与启动样式文件名。对主题而言ESM 下的异步资源 URL 由已加载入口的相对路径推导provider-root-safe因此产物既可用于插件也可用于主题甚至允许 Halo 通过遗留console目录回退发现完整 ESM 产物时仍能正确解析——运行时不会硬编码首选目录。ESM 预设默认对vue、vue-router、pinia、axios、formkit/vue、formkit/core、halo-dev/ui-shared、halo-dev/components、halo-dev/api-client、halo-dev/richtext-editor等共享包根做 external 化保持 Import Map 根规范器解析其余依赖打包进 provider 产物。复用 UI 插件的 externals 与 globals主题 UI bundle 与插件运行在同一个 Console / UC 宿主环境、使用同一份PluginModule契约因此应复用现有插件的 external 依赖与全局变量映射默认值而不是引入一套新的映射。design.md 的理由是避免 Vue/运行时被重复打进每个 bundle、保持产物体积与插件 UI bundle 一致。在 vite.ts 中IIFE 分支的external: EXTERNALS与output.globals: GLOBALS来自统一的constants/externals.ts主题与插件共用同一份配置。同理Vue 编译器也只由工具包内置一份Vite 内置单个vitejs/plugin-vue、Rsbuild 内置单个rsbuild/plugin-vue。需要自定义时通过顶层vue字段传参而不是再往plugins数组里追加一个 Vue 插件否则 SFC 转换会被执行两次// Vite viteConfig({ vue: { template: { compilerOptions: { isCustomElement: (tag) tag halo-app-card, }, }, }, vite: {}, }); // Rsbuild rsbuildConfig({ vue: { vueLoaderOptions: { compilerOptions: { isCustomElement: (tag) tag halo-app-card, }, }, }, rsbuild: {}, });源码与测试验证默认值如何被证明主题 provider 的默认值不是文档里的「口号」而是被单元测试逐条锁定的constants/halo-plugin.ts 定义两份默认清单路径utils/halo-plugin.ts 实现getHaloThemeModuleName拼出theme:{name}与getHaloThemeAssetPublicPath拼出/themes/{name}/ui-plugin/assets/主题清单使用精简的内部类型HaloThemeManifest只含metadata.name与可选spec.requires不依赖生成的完整ThemeAPI 模型输出目录在 vite.ts 中由getThemeProviderDefaults返回DEFAULT_THEME_OUT_DIRdev 与 prod 均为dist。任务清单 tasks.md 显示该变更已全部落地包括 package 级单元测试与文档更新。验收规格 specs/ui-plugin-bundler-provider/spec.md 用 WHEN/THEN 形式明确了各场景例如当metadata.name等于earth时全局模块名必须配置为theme:earth。对应的测试可以在以下文件中找到provider.spec.ts 中的主题 provider 测试断言viteConfig({ provider: theme, vite: {} })生成的config.base /themes/earth/ui-plugin/assets/、global 名为theme:earthRsbuild 侧同样断言 public path 与输出根目录halo-plugin.spec.ts与esm-build.spec.ts覆盖manifestPath覆盖、自定义主题名、ESM 主题输出的资源路径与相对 URL 推导等场景。任务的验证环节还要求运行pnpm -C ui build:packages校验包构建、pnpm -C ui typecheck校验工作区类型兼容说明该能力以 monorepoui/packages/ui-plugin-bundler-kit内的标准工具链为保障。风险与权衡变更提案在末尾也明确列出了几项被接受的风险public path 可被错误覆盖可能破坏动态 chunk 与产物相对资源对策是在文档中明确预期的主题 public path并用测试锁死默认值provider 默认值增加了配置分支把 provider 专属逻辑收敛到小工具函数中遗留 API 保持不动以控制复杂度主题清单解析可能放过畸形清单工具包只校验 bundler 所需的最小字段完整主题兼容性校验仍由 Halo 运行时负责package 级测试可能需要新增脚本直接复用工作区已有的 Vitest 工具链不引入新依赖。此外非目标同样值得注意不自动探测 provider、不支持任意自定义主题布局仅尊重既有 manifest 与 bundler 覆盖钩子、不改动运行时主题 UI 资源端点与前端模块契约、不新增任何依赖。变更提案明确声明该改动不涉及后端、数据库、OpenAPI 或生成的 API 客户端影响范围被严格限定在ui/packages/ui-plugin-bundler-kit一个包内。小结从「插件专属」到「插件与主题双 provider」provider: theme以最小侵入的方式补齐了主题作者的构建体验../theme.yaml自动读取、dist统一输出、theme:{name}模块注册、/themes/{name}/ui-plugin/assets/资源路径全部成为官方默认值Vite 与 Rsbuild 两条工具链行为对等产物与主题运行时契约天然匹配。对于希望在主题中提供 Console / User Center 扩展界面的开发者现在只需在theme-root/ui-plugin/下放置前端工程并显式选择provider: theme即可获得与插件开发完全一致的现代构建体验同时继续受 ESM/IIFE 格式选择、共享运行时 external 与严格单 Vue 编译器的既有保障。如果你正维护自己的 Halo 主题建议从 README 的主题 UI 插件章节出发参照theme-root/ui-plugin布局搭建最小工程并运行一次vite build或rsbuild build验证dist中是否生成了预期的入口、样式与ESM 时ui-plugin.json再将其纳入主题发布流程。【免费下载链接】haloHalo 是一款强大易用的开源建站工具从个人博客、知识库到企业官网、在线商城Halo 都能助您轻松实现一站式满足您的多样化建站需求。项目地址: https://gitcode.com/GitHub_Trending/ha/halo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表