@uni-helper/vite-plugin-uni-manifest
v0.6.0
Published
Use TypeScript to write manifest.json of uni-app
Downloads
9,048
Readme
@uni-helper/vite-plugin-uni-manifest
使用 TypeScript 编写 uni-app 的 manifest.json。
不想看文档?直接问 AI 🤖
请考虑持续赞助以维持该项目的持续健康发展,非常感谢!🙏
安装
pnpm i -D @uni-helper/vite-plugin-uni-manifest使用
// vite.config.mts
import Uni from '@uni-helper/plugin-uni'
// 或者
// import dcloudioUni from '@dcloudio/vite-plugin-uni'
// const Uni = dcloudioUni.default || dcloudioUni
import UniManifest from '@uni-helper/vite-plugin-uni-manifest'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
UniManifest(), // 需要在 Uni() 之前调用
Uni(),
],
})创建 manifest.config.(ts|mts|cts|js|cjs|mjs|json),然后用 TypeScript 编写你的 manifest.json。👉 manifest.config.ts 示例
// manifest.config.ts
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
export default defineManifestConfig({
// 属性参考 manifest.json,理论上一比一对齐
// 如果发现没有对齐,请提交 issue,谢谢 🙏
// https://uniapp.dcloud.net.cn/collocation/manifest.html
name: 'my-project',
})插件配置
UniManifest() 支持以下选项定义行为:
interface UserOptions {
/**
* 是否压缩生成的 manifest.json
* @default false
* @since 0.1.3
*/
minify?: boolean
/**
* 是否在 manifest.json 末尾插入换行
* @default false
* @since 0.2.9
*/
insertFinalNewline?: boolean
/**
* 生成的 manifest.json 的缩进
* 接受空格数量或字符串(如 `'\t'`)
* 当 `minify` 为 `true` 时被忽略
* @default 2
* @since 0.5.2
*/
indent?: number | string
/**
* 生成的 manifest.json 的换行符
* @default '\n'
* @since 0.5.2
*/
eol?: '\n' | '\r\n'
/**
* 解析配置的工作目录
* 插件会从该目录查找 `manifest.config.(ts|mts|cts|js|cjs|mjs|json)` 文件
* 未设置该环境变量时回退到 `process.cwd()`
* @default process.env.VITE_ROOT_DIR
* @since 0.2.12
*/
cwd?: string
/**
* 生成 `manifest.json` 的输出目录。
* 未设置时使用 uni-app 的 `UNI_INPUT_DIR`(或 `cwd/src`)。
* 相对路径会基于 `process.cwd()` 解析。
* @default undefined
* @since 0.5.1
*/
outDir?: string
/**
* 是否启用调试日志
* 设为 `true` 启用全部类别,指定字符串则只启用单一类别(如 `'writer'`)
* 可选类别:`options`(选项解析)/ `config`(配置加载与变更检测)/ `writer`(文件写入)
* 等效于 `DEBUG=vite-plugin-uni-manifest:*` 环境变量
* @default false
* @since 0.5.7
*/
debug?: boolean | DebugType
}FAQ
这个插件写入配置晚于 uni-app 读取配置,导致无法正常运行
@dcloudio/vite-plugin-uni 在 Vite 的 config 钩子里通过 parseManifestJsonOnce 读取 manifest.json,而本插件在更晚的 configResolved 钩子里才写入。
config 早于 configResolved,所以即便本插件设置了 enforce: 'pre',也只能在 configResolved 内部抢先,无法早于 config 钩子。parseManifestJsonOnce 结果被 once 缓存,首次读取后即固定,后续写入对 uni-app 无效。
核心矛盾是时序:必须在 uni-app 进程启动前把 manifest.json 生成好。以下按推荐度排序给出方案。
方案一(推荐):使用 @uni-helper/unh
unh 在调用 uni dev/build 前用 unconfig 加载 manifest.config.ts 并写盘,再 spawn 子进程,天然解决时序问题。
// package.json
{
"scripts": {
"dev": "unh dev",
"build": "unh build"
}
}// unh.config.ts
import { defineConfig } from '@uni-helper/unh'
export default defineConfig({
autoGenerate: {
manifest: true, // 在 dev/build 前自动生成 manifest.json
},
})方案二:自行编写脚本,在 uni 命令前生成
用一个独立脚本加载 manifest.config.ts、写入 src/manifest.json,再用 && 串联到 uni 命令前。脚本在 uni 进程之外运行,与 Vite 钩子时序无关。
pnpm i -D c12 tsximport { writeFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
// scripts/generate-manifest.ts
import { loadConfig } from 'c12'
// 与本插件内部实现保持一致:c12 + defaultConfig 合并
const defaultManifestConfig = defineManifestConfig({
// ...参考本插件 defaultManifestConfig,或按需精简
})
const { config } = await loadConfig({
cwd: process.cwd(),
name: 'manifest',
defaultConfig: defaultManifestConfig,
rcFile: false,
packageJson: false,
})
// UNI_INPUT_DIR 由 vite-plugin-uni 注入,独立脚本中不可用,这里硬编码默认输入目录
// monorepo 或自定义 input dir 时改为对应路径
const outPath = resolve(process.cwd(), 'src/manifest.json')
writeFileSync(outPath, JSON.stringify(config, null, 2))// package.json
{
"scripts": {
"dev:h5": "tsx scripts/generate-manifest.ts && uni",
"build:mp-weixin": "tsx scripts/generate-manifest.ts && uni build -p mp-weixin"
}
}说明:
manifest.config.ts中的热更新监听在本方案下不生效。脚本只生成一次。开发期间修改manifest.config.ts需重新执行脚本(或重启 dev server)。本插件仍可作为 Vite 插件保留,用于处理manifest.config.ts的运行时变更;首次启动的正确性由本脚本兜底。
方案三:用 npm predev/prebuild 钩子
// package.json
{
"scripts": {
"predev": "tsx scripts/generate-manifest.ts",
"prebuild": "tsx scripts/generate-manifest.ts",
"dev": "uni",
"build": "uni build"
}
}说明:npm 会在执行
dev/build前自动运行同名pre*脚本。pnpm 10 同样会运行用户自定义的predev/prebuild(注意enable-pre-post-scripts只影响 install 阶段的生命周期脚本,不影响run时的pre*/post*)。比方案二的&&串联更隐式,开发者可能意识不到predev被自动触发,排查问题时需额外留意,故排序靠后。
修改 manifest.config.ts 后,需要重启 dev server 才能生效
Vite 只会因 vite.config、.env 文件变更而自动重启服务;而 manifest.json 不在 Vite 的模块图中(uni-app 直接从磁盘读取它),变更既不会触发 HMR,也不会触发整页刷新。更关键的是,uni-app 只在启动时读取一次 manifest.json 并做进程级缓存(见上一节),即便是 vite build --watch 触发的重建,使用的仍是旧配置。因此修改配置后请重启 dev server(或重新构建)。
如需自动化,可以用文件监听工具监视生成的 manifest.json,在变化时自动重启或重新构建 uni 进程。新进程会重新读取 manifest,不存在缓存问题。
使用 nodemon 自动重启 dev server
pnpm i -D nodemon// package.json
{
"scripts": {
// 只监视生成的 manifest.json,变化时重启 uni dev 进程
"dev:h5": "nodemon -e json --watch src/manifest.json --exec \"uni\"",
"dev:mp-weixin": "nodemon -e json --watch src/manifest.json --exec \"uni -p mp-weixin\""
}
}说明:
--watch限定只监视manifest.json,其他源码变更仍走 Vite 自身 HMR,不会被 nodemon 重启;-e json确保.json后缀被 nodemon 识别。若重启过于频繁,可加--delay 1防抖。nodemon 重启前会先终止旧进程,适合常驻的 dev server。
使用 chokidar-cli 监视重新构建
pnpm i -D chokidar-cli// package.json
{
"scripts": {
// 启动时先构建一次(--initial),之后 manifest.json 每次变化都重新构建
"build:mp-weixin:watch": "chokidar \"src/manifest.json\" --initial -c \"uni build -p mp-weixin\""
}
}说明:chokidar-cli 的
-c只会在每次事件时执行命令、不会终止之前的进程,因此适合有限的构建命令(每次构建都是新进程,天然规避缓存问题);常驻 dev server 请用 nodemon。若你的输入目录不是src,请相应调整监视路径;需要防抖时可加-d 100(毫秒)。
支持 monorepo 吗?
支持。在 monorepo 场景下,uni-app 应用通常位于某个子包目录(如 packages/app)中,而命令可能从仓库根目录执行。此时可以通过两个选项调整本插件的路径解析:
cwd:指定插件查找manifest.config.*的目录。默认为process.env.VITE_ROOT_DIR(由@dcloudio/vite-plugin-uni注入),该环境变量不存在时回退到process.cwd()。若配置文件不在默认目录,显式指向应用所在目录即可。outDir:指定manifest.json的输出目录。默认写入 uni-app 的输入目录(UNI_INPUT_DIR,通常是应用的src/)。相对路径基于process.cwd()解析。
// vite.config.mts
import UniManifest from '@uni-helper/vite-plugin-uni-manifest'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
UniManifest({
cwd: resolve(__dirname, 'packages/app'), // 从该目录查找 manifest.config.ts
// 如需把 manifest.json 输出到其他位置,再配置 outDir
// outDir: resolve(__dirname, 'packages/app/src'),
}),
],
})注意:uni-app 在运行时会从其默认输入目录读取
manifest.json,自定义outDir后请确保后续流程能正确读取到该文件,否则可能导致 uni-app 无法解析 manifest。
开发
请参考仓库根目录的 CONTRIBUTING.md。
架构
插件围绕一个深模块(watcher.ts)构建,对外暴露极简 interface,内部吸收所有编排逻辑:
index.ts Vite 插件入口,调用 createManifestWatcher
watcher.ts 深模块 — 选项解析 + c12 配置监听 + 文件写入
writer.ts 文件 I/O — writeManifestJson / ensureManifestJsonExists
paths.ts 路径解析 — resolveManifestJsonPath
defaults.ts 静态数据 — 默认 manifest 配置
config.ts defineManifestConfig 辅助函数 + 类型重导出
logger.ts 分类调试日志(debug 包)
types.ts 公共类型定义(Options / UserOptions / ResolvedOptions / DebugType)模块依赖关系
index.ts
└─ watcher.ts(深模块)
├─ writer.ts ── paths.ts
├─ defaults.ts
└─ logger.ts(writer.ts 同样使用)插件生命周期
插件通过 Vite 的生命周期钩子驱动,顺序如下:
configResolved(异步)— 调用createManifestWatcher(userOptions):启动 c12 监听 → 执行首次写入(manifest.json不存在时在此创建,配置加载失败则不会写入占位文件)- 运行时 — c12 检测到
manifest.config.ts变更 →onUpdate回调 →writeManifestJson()写入文件 buildEnd— 调用watcher.unwatch()停止 c12 监听
关键设计决策
- 深模块设计:
createManifestWatcher()是唯一的核心 interface,选项解析、配置监听、文件写入全部作为 implementation 细节隐藏其后。测试直接通过这一个 interface 验证端到端行为。 - 无导入时副作用:所有文件系统操作(路径解析、文件写入)都在插件生命周期内执行,而非模块导入时。这使得模块可独立测试。
- 路径解析为函数:
resolveManifestJsonPath()每次调用重新计算路径,依赖process.env.UNI_INPUT_DIR(由@dcloudio/vite-plugin-uni注入),不缓存。 - c12 配置加载:通过
c12的watchConfig实现manifest.config.ts的监听和热更新,支持.ts、.mts、.js、.json等格式。 - 幂等写入:
writeManifestJson在内容未变化时跳过写入,避免触发下游不必要的重编译。 - 分类调试日志:通过
debug选项或DEBUG=vite-plugin-uni-manifest:*环境变量按类别(options/config/writer)输出日志,默认关闭,不影响正常输出。
关联项目
- @uni-helper/vite-plugin-uni-manifest - 使用 TypeScript 编写
uni-app的manifest.json - @uni-helper/manifest-json-schema - 为 uni-app 的 manifest.json 提供 schema
- @uni-helper/uni-manifest-types - 为 uni-app 的 manifest.json 提供 TypeScript 类型
- uni-helper/vite-plugin-uni-pages - 为 Vite 下的 uni-app 提供基于文件系统的路由
- uni-helper/vite-plugin-uni-platform - 基于文件名 (.<h5|mp-weixin|app>.) 的按平台编译插件
- uni-helper/vite-plugin-uni-platform-modifier - 为属性、指令提供平台修饰符并按需编译
- uni-helper/vite-plugin-uni-layouts - 为 Vite 下的 uni-app 提供类 nuxt 的 layouts 系统
- uni-ku/root - 解决 uni-app 无法使用根部组件问题
