weapp-vite 7.2.2 → 7.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -6
- package/dist/adapters-BwnMMIZR.mjs +2 -0
- package/dist/auto-routes.mjs +1 -1
- package/dist/cli.mjs +635 -644
- package/dist/{config-JdEU9lCP.d.mts → config-Dbvj5yK7.d.mts} +446 -15
- package/dist/config.d.mts +2 -2
- package/dist/constants-WOsG3Eqd.mjs +85 -0
- package/dist/{createContext-403iJEyy.mjs → createContext-UOEgeYnA.mjs} +9550 -5921
- package/dist/dashboard/vite.d.mts +9 -0
- package/dist/dashboard/vite.mjs +44 -0
- package/dist/dashboard-BzaVesmQ.mjs +558 -0
- package/dist/dashboard.d.mts +2 -0
- package/dist/dashboard.mjs +2 -0
- package/dist/docs/README.md +104 -6
- package/dist/docs/getting-started.md +50 -1
- package/dist/docs/index.md +3 -1
- package/dist/docs/upload.md +189 -0
- package/dist/docs/weapp-config.md +348 -2
- package/dist/docs/wevu-authoring.md +19 -0
- package/dist/file-D19nbDoN.mjs +2 -0
- package/dist/{file-CfgcKGLR.mjs → file-giE9UMZa.mjs} +3 -86
- package/dist/getInstance-CYF1vl5l.mjs +2 -0
- package/dist/index-DbqVupg0.d.mts +246 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/json.d.mts +1 -1
- package/dist/mcp.d.mts +1 -1
- package/dist/platform-DTWs5QRi.mjs +175 -0
- package/dist/test.mjs +1 -1
- package/dist/types.d.mts +2 -2
- package/dist/upload-D0RcEWrq.mjs +403 -0
- package/dist/upload-worker.d.mts +1 -0
- package/dist/upload-worker.mjs +28 -0
- package/package.json +47 -25
- package/dist/file-BRwj-D25.mjs +0 -2
- package/dist/getInstance-BhkSNqVQ.mjs +0 -2
|
@@ -24,6 +24,30 @@ export default defineConfig({
|
|
|
24
24
|
|
|
25
25
|
适合希望用约定生成页面路由的项目。启用后要保持 pages 目录与输出约定稳定。
|
|
26
26
|
|
|
27
|
+
### `multiPlatform.projectConfigs`
|
|
28
|
+
|
|
29
|
+
在一份配置中按 `weapp` / `alipay` / `tt` / `xhs` / `jd` / `swan` 提供原生项目字段,公共项用普通对象展开。没有显式 `targets` 时从映射键推导;完整示例见[上传速查](./upload.md#多平台与输出校验)。
|
|
30
|
+
|
|
31
|
+
各平台已知字段及嵌套设置提供智能提示;原生对象允许未知扩展字段,字符串选项允许新增取值,不需要 `as any`。独立映射推荐从 `weapp-vite/config` 导入 `MultiPlatformProjectConfigs` 并使用 `satisfies`,保留扩展字段推导。此开放能力不放宽平台名、AppID 类型和生成代码根限制。
|
|
32
|
+
|
|
33
|
+
`defineConfig` 的泛型不保证拒绝所有多余属性;需要静态检查平台名拼写时使用 `satisfies MultiPlatformProjectConfigs`,构建时仍拒绝不支持的平台。
|
|
34
|
+
|
|
35
|
+
标准项目 JSON 由打包器原生生成在代码目录内,默认 `dist/<平台>/dist/`,与 `app.json` 同级。SDK 代码根为 `.`;输入不能填写 `miniprogramRoot`、`srcMiniprogramRoot`、`smartProgramRoot`,修改目录用 `build.outDir`。这里只管理 IDE/SDK 项目配置,不代替业务 `app.json`。
|
|
36
|
+
|
|
37
|
+
不读取源码侧原生项目 JSON 或私有 JSON;缺少选中平台时直接报错,不回退到旧文件。不能同时指定 `projectConfigRoot` 或 `enabled: false`。独立插件仍使用原生文件模式;Web/组件库不生成项目 JSON。不写映射时保留原生文件模式。凭据始终走环境变量,不写入映射。
|
|
38
|
+
|
|
39
|
+
### `upload`
|
|
40
|
+
|
|
41
|
+
`weapp.upload: { version?: string; desc?: string }` 只设置显式 `wv build --upload` 和独立 `wv upload` 的默认参数,不是自动上传开关,也不支持凭据字段或 `bump`、`gitDesc`。版本优先级为 CLI `--uv` 或 `--bump` 生成值 > `weapp.upload.version` > `package.json.version`;说明优先级为 CLI `--desc` 或 `--git-desc` 生成值 > `weapp.upload.desc` > 项目名称与最终版本。值会去除首尾空白,显式空版本报错,空说明使用自动生成的说明。
|
|
42
|
+
|
|
43
|
+
普通 `build`、`dev/HMR` 不使用这组上传默认参数也不上传,`preview` 不使用该配置。配置文件本身仍会正常加载与合并,不保证其中的 JavaScript getter 延迟求值。`wv build --upload` 复用本次构建,等待所有选中的构建后端成功、产物校验通过后才调用平台工具;`wv build --upload --dry-run` 不校验凭据、不调用 SDK。
|
|
44
|
+
|
|
45
|
+
`build` 上的 `--uv`、`--desc`、`--bump`、`--git-desc`、`--dry-run` 必须与 `--upload` 一起使用;`--watch --upload`、仅 Web 的 `-p web --upload` 会报错。`build -p all --upload` 是“小程序 + Web”,两者都构建成功后只上传小程序;独立 `upload -p all` 则保持六端逐一构建上传。
|
|
46
|
+
|
|
47
|
+
默认不执行 Git 或 npm 版本操作。需要时显式使用 `wv upload -p xhs,tt --bump patch --git-desc`:`--bump patch|minor|major` 与 `--uv` 冲突,`--git-desc` 与 `--desc` 冲突,`preview` 不接受这两个选项。真实升版需要本机 npm;只有 Git 说明要求已有提交的 Git 仓库。版本只取命令根目录的应用清单,不向父目录查找;首次配置求值前准备一次,批量共用,不运行生命周期钩子、不 commit/tag/push。dry-run 不运行 npm、不修改版本或锁文件,直接导入清单的构建代码仍看到原始版本。升版后的失败不回滚,重试去掉 `--bump` 并复用同一版本。详见[内置本地自动版本](./upload.md#内置本地自动版本)。
|
|
48
|
+
|
|
49
|
+
CI 可在测试通过后显式执行 `wv build --upload -p weapp --uv 1.2.3 --desc "release"`。凭据仍通过环境变量提供;先读本地[上传与预览速查](./upload.md),完整的 AppID、私钥、支付宝 JSON 身份密钥、各端 Token、环境文件和 CI Secrets 示例见[分平台操作指南](https://vite.weapp.dev/guide/upload.html)。
|
|
50
|
+
|
|
27
51
|
### `buildScope`
|
|
28
52
|
|
|
29
53
|
用于只构建主包和指定分包。常用在大项目里只调试某几个业务分包:
|
|
@@ -180,11 +204,43 @@ export default defineConfig({
|
|
|
180
204
|
|
|
181
205
|
`inject: false` 只生成目标平台样式文件,适合由源码手动 `@import`。未显式配置 `include` 时仍默认排除 app,避免意外把共享样式提升为全局样式。独立分包不能依赖主包资源,不会收到 `weapp.styles` 的自动注入;需要在 `weapp.subPackages.<root>.styles` 中声明分包自己的入口。
|
|
182
206
|
|
|
207
|
+
### `compilerPlugins`
|
|
208
|
+
|
|
209
|
+
`compilerPlugins` 是底层源码编译器的扩展入口。第三方 provider 可以声明自己接管的源码,并参与 CSS、WXML、JavaScript、bundle 和 HMR 生命周期:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
import type { WeappCompilerPlugin } from 'weapp-vite/config'
|
|
213
|
+
import { defineConfig } from 'weapp-vite/config'
|
|
214
|
+
|
|
215
|
+
const compilerPlugin: WeappCompilerPlugin = {
|
|
216
|
+
name: 'example-compiler',
|
|
217
|
+
capabilities: { style: true, template: true, script: true, hmr: true },
|
|
218
|
+
create() {
|
|
219
|
+
return {
|
|
220
|
+
claimSource: ({ id }) => id.endsWith('.css'),
|
|
221
|
+
transformCss: ({ code }) => ({ code }),
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export default defineConfig({
|
|
227
|
+
weapp: {
|
|
228
|
+
compilerPlugins: [compilerPlugin],
|
|
229
|
+
},
|
|
230
|
+
})
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
provider 的状态由 provider 自己维护,host 负责插件顺序、源码所有权冲突、依赖监听和产物生命周期。`weapp.tailwindcss` 仍然是内置 Tailwind adapter 的兼容门面;UnoCSS 等实现可以独立包的形式提供同一协议。
|
|
234
|
+
|
|
235
|
+
微信状态保持 HMR 使用可选的 `controller.prepareHmr(request)` 协作:输入提供 `revision`、`changedFiles` 与固定的 `sources` 内容视图,返回本批次的 `assets`、`transformJavaScript`、依赖信息和资源释放方法。资产路径相对于输出目录;所有 Patch 共用该批次的编译状态。宿主通过 Vite/Rolldown 提交资产后才发布补丁,并在客户端执行回报后通知 DevEngine。开启 sourcemap 时,修改代码的转换必须同时返回映射。没有批次接口的内容 provider 使用完整构建回退。
|
|
236
|
+
|
|
237
|
+
共享协议由实验包 `@weapp-vite/hmr` 提供,Tailwind 控制器委托 `@weapp-vite/tailwindcss` 和 `weapp-tailwindcss/core`。两个包均不创建 DevEngine、watcher 或直接写出产物,框架与宿主运行时仍由适配器拥有;现有配置和 `prepareHmr` 类型兼容。Taro 的实验适配保留其持久发布与应用确认两阶段,不改变 weapp-vite 在应用确认后通知 DevEngine 的语义。
|
|
238
|
+
|
|
183
239
|
### `tailwindcss`
|
|
184
240
|
|
|
185
241
|
内置的 `weapp-tailwindcss` 集成支持显式配置和 Tailwind CSS v4 自动检测。显式配置优先级最高:设置为 `false` 会完全关闭(包括自动检测),设置为 `true` 或对象会按显式选项启用。未配置时,项目解析到 Tailwind CSS v4 且 CSS 模块实际包含 `@import "tailwindcss"`(也支持 `source(...)` 等合法参数)才会自动启用;Tailwind CSS v3、未安装或未引入该模块时不会生成 Tailwind CSS。
|
|
186
242
|
|
|
187
|
-
启用后,`weapp-vite`
|
|
243
|
+
启用后,`weapp-vite` 通过 `weapp-tailwindcss/core` compiler 处理 WXSS、WXML 和 JavaScript,通过 `compiler.generate()` 生成 Tailwind CSS,并将结果写入正常的样式产物。WXSS 最终化由 core 统一完成,Tailwind 构建阶段的 `@plugin`、`@source` 等指令不会泄漏到小程序产物:
|
|
188
244
|
|
|
189
245
|
```ts
|
|
190
246
|
import { defineConfig } from 'weapp-vite/config'
|
|
@@ -520,7 +576,7 @@ wv mcp doctor codex
|
|
|
520
576
|
|
|
521
577
|
```bash
|
|
522
578
|
weapp-vite build
|
|
523
|
-
weapp-vite preview --
|
|
579
|
+
weapp-vite preview -p weapp --mode test
|
|
524
580
|
weapp-vite ide preview --project ./dist/build/mp-weixin
|
|
525
581
|
```
|
|
526
582
|
|
|
@@ -529,3 +585,293 @@ weapp-vite ide preview --project ./dist/build/mp-weixin
|
|
|
529
585
|
- 项目结构与 `AGENTS.md`:[`project-structure.md`](./project-structure.md)
|
|
530
586
|
- wevu 运行时写法:[`wevu-authoring.md`](./wevu-authoring.md)
|
|
531
587
|
- Vue SFC 宏与模板:[`vue-sfc.md`](./vue-sfc.md)
|
|
588
|
+
|
|
589
|
+
## 函数式转换
|
|
590
|
+
|
|
591
|
+
`weapp.wxml.transform` 接收一个函数或函数数组,支持异步。每个函数收到当前模板源码;数组按声明顺序等待执行。返回 `null` / `undefined` 保持当前源码,返回空字符串清空模板。
|
|
592
|
+
|
|
593
|
+
```ts
|
|
594
|
+
import { defineConfig } from 'weapp-vite/config'
|
|
595
|
+
|
|
596
|
+
export default defineConfig({
|
|
597
|
+
weapp: {
|
|
598
|
+
wxml: {
|
|
599
|
+
transform: async (code, ctx) => {
|
|
600
|
+
if (ctx.isDev) {
|
|
601
|
+
return
|
|
602
|
+
}
|
|
603
|
+
return ctx.edit(code, (node) => {
|
|
604
|
+
if (node.tagName === 'view') {
|
|
605
|
+
node.removeAttribute('data-testid')
|
|
606
|
+
}
|
|
607
|
+
if (node.tagName === 'button') {
|
|
608
|
+
node.renameAttribute('data-track', 'data-analytics')
|
|
609
|
+
if (!node.hasAttribute('hover-class')) {
|
|
610
|
+
node.setAttribute('hover-class', 'none')
|
|
611
|
+
}
|
|
612
|
+
node.setAttribute('disabled', { expression: 'submitting' })
|
|
613
|
+
}
|
|
614
|
+
if (node.tagName === 'text' && node.hasAttribute('data-use-view')) {
|
|
615
|
+
node.renameTag('view')
|
|
616
|
+
node.removeAttribute('data-use-view')
|
|
617
|
+
}
|
|
618
|
+
})
|
|
619
|
+
},
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
})
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
匹配编译后的静态标签名,例如 Vue HTML 映射后的 `view`。第一版只转换最终模板:替换成自定义组件时,仍需通过既有 `usingComponents` 等配置注册组件;不会生成组件依赖、JS 或样式。
|
|
626
|
+
|
|
627
|
+
### 回调上下文
|
|
628
|
+
|
|
629
|
+
| 字段 / 方法 | 含义 |
|
|
630
|
+
| --- | --- |
|
|
631
|
+
| `fileName` | 相对输出根目录的 POSIX 文件名,包含分包目录 |
|
|
632
|
+
| `root` | 项目根目录 |
|
|
633
|
+
| `platform` / `mode` / `isDev` | 当前平台、模式及开发状态 |
|
|
634
|
+
| `subPackageRoot` | 独立分包构建的根目录;主包构建(含普通分包)为 `undefined` |
|
|
635
|
+
| `edit(code, visitor)` | 返回 `Promise<string>`,支持异步 visitor |
|
|
636
|
+
| `addWatchFile(path)` | 注册外部文件依赖,相对路径从项目根目录解析 |
|
|
637
|
+
| `warn(message)` / `error(message)` | 报告警告 / 中止本轮构建 |
|
|
638
|
+
|
|
639
|
+
`WxmlTransform`、`WxmlTransformResult`、`WxmlTransformContext`、`WxmlTransformVisitor`、`WxmlTransformNode`、`WxmlElementInfo`、`WxmlAttribute`、`WxmlAttributeValue`、`WxmlSourceLocation` 均可从 `weapp-vite/config` 和 `weapp-vite/types` 导入。回调执行于构建端,可以使用闭包、正则和异步读取;非法返回值或异常会中止输出,并报告文件名、回调序号及原始错误。
|
|
640
|
+
|
|
641
|
+
### 编辑节点与属性值
|
|
642
|
+
|
|
643
|
+
`ctx.edit` 每次只扫描一次,按源码顺序深度优先遍历标签。未修改的区间逐字保留;WXS 等脚本模块的原始内容不作为标签遍历。
|
|
644
|
+
|
|
645
|
+
- `tagName`、`attributes`、`parent`、`location` 为只读观察信息;位置包含从零开始的 `offset` 和从一开始的 `line` / `column`。父节点没有修改接口。
|
|
646
|
+
- `hasAttribute(name)` 和 `getAttribute(name)` 读取当前编辑状态。属性记录包含 `name`、原始 `rawValue` 与 `quote`,不求值动态或混合绑定;无值属性的 `rawValue` 为 `null`。
|
|
647
|
+
- `setAttribute(name, value)` 设置属性:字符串为字面量,数字和布尔值输出保持类型的绑定,`{ expression: 'submitting' }` 输出模板表达式。不要给表达式再包 `{{ }}`。
|
|
648
|
+
- `setBooleanAttribute(name)` 设置无值属性。`setAttribute(name, false)` 保留布尔 `false` 语义,不表示删除。
|
|
649
|
+
- `renameAttribute(from, to)` 保留原值,目标存在时抛错;源不存在时无操作。删除清除全部同名项,设置将重复属性收敛为一个。
|
|
650
|
+
- `renameTag(name)` 同时改开始和结束标签;`remove()` 删除整个子树并跳过子节点回调。
|
|
651
|
+
|
|
652
|
+
同一节点后续操作可以读取已修改状态。编辑句柄仅在当前 `edit` 会话内有效。不提供节点移动、插入或 unwrap;需要这些操作时可自行返回完整源码。
|
|
653
|
+
|
|
654
|
+
编辑工具沿用关键属性、结构标签及条件链保护。明确修改事件、平台指令或已识别的框架元数据会抛错并附带位置。直接返回源码允许完整控制,不提供上述语义保护;后续 `remove` 与宿主编译检查仍会执行。
|
|
655
|
+
|
|
656
|
+
### 子节点访问与子树编辑
|
|
657
|
+
|
|
658
|
+
`node.children` 返回按源码顺序排列的直接子标签;`await node.walk(visitor)` 深度优先遍历全部后代,不包含节点自身。下面的完整配置只处理带 `data-card` 的 `view`:
|
|
659
|
+
|
|
660
|
+
```ts
|
|
661
|
+
import { defineConfig } from 'weapp-vite/config'
|
|
662
|
+
|
|
663
|
+
export default defineConfig({
|
|
664
|
+
weapp: {
|
|
665
|
+
wxml: {
|
|
666
|
+
transform: (code, ctx) => ctx.edit(code, async (node) => {
|
|
667
|
+
if (node.tagName !== 'view' || !node.hasAttribute('data-card')) {
|
|
668
|
+
return
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
for (const child of node.children) {
|
|
672
|
+
if (child.tagName === 'text') {
|
|
673
|
+
child.setAttribute('selectable', true)
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
await node.walk(async (child) => {
|
|
678
|
+
if (child.hasAttribute('data-private-zone')) {
|
|
679
|
+
child.skipChildren()
|
|
680
|
+
return
|
|
681
|
+
}
|
|
682
|
+
if (child.tagName === 'button') {
|
|
683
|
+
child.setAttribute('hover-class', 'none')
|
|
684
|
+
}
|
|
685
|
+
if (child.hasAttribute('data-debug')) {
|
|
686
|
+
child.remove()
|
|
687
|
+
}
|
|
688
|
+
})
|
|
689
|
+
node.skipChildren()
|
|
690
|
+
}),
|
|
691
|
+
},
|
|
692
|
+
},
|
|
693
|
+
})
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
- 直接子 `text` 获得 `selectable="{{true}}"`;更深的 `text` 不会因直接子节点循环而改变。
|
|
697
|
+
- 后代 `button` 获得 `hover-class="none"`,带 `data-debug` 的普通标签及其子树删除。带 `data-private-zone` 的标签保留,其后代在这次手动遍历中跳过。
|
|
698
|
+
- 末尾的 `node.skipChildren()` 跳过外层自动遍历,避免同一子树再次被外层回调处理;因此示例中的 private zone 也不会再被外层自动遍历访问。删除该行时,外层仍会继续访问所有未删除后代。
|
|
699
|
+
|
|
700
|
+
| 接口 | 行为 |
|
|
701
|
+
| --- | --- |
|
|
702
|
+
| `children` | 当前未删除的直接子标签;只读数组,元素是可编辑句柄 |
|
|
703
|
+
| `walk(visitor)` | 返回 `Promise<void>`;同步或异步回调按源码顺序逐个等待,不包含自身 |
|
|
704
|
+
| `skipChildren()` | 仅跳过当前回调所属遍历的后代;不影响兄弟、其他遍历或输出 |
|
|
705
|
+
| `parent.children` | 只读观察节点,不允许借父节点修改祖先或兄弟 |
|
|
706
|
+
|
|
707
|
+
每次读取 `children` 得到一个数组快照;旧数组不会自动减少,删除节点及其全部后代的编辑句柄立即失效。子节点提前改名或修改属性后,后续自动回调与父节点观察到的是同一份新状态。源码位置始终对应本次 `edit` 输入,不因编辑重新计算。
|
|
708
|
+
|
|
709
|
+
手动 `walk` 每调用一次都会遍历一次,不会自动消费外层遍历;多层标记容器可能因此重复处理后代。若某个容器已负责全部后代处理,在它的外层回调中显式 `skipChildren()`。先 `skipChildren()` 再显式 `walk()` 也有效。内层回调中的跳过只属于内层遍历,不会跳过外层后续访问。
|
|
710
|
+
|
|
711
|
+
`skipChildren()` 只能对当前正在回调的节点调用;对 `children[0]`、其他保存的节点或回调结束后的句柄调用会报带源码位置的错误。嵌套遍历必须正确 `await`/`return`;同一编辑会话不支持 `Promise.all` 并发遍历或编辑,也不要发起未等待的异步任务或跨构建保存节点句柄。
|
|
712
|
+
|
|
713
|
+
`children` 与 `walk` 只识别本模板里的标签,不包含文本、注释、插值和 WXS/SJS 原始内容,不展开导入模板或自定义组件内部。事件、指令、结构标签、框架元数据与条件链保护在子树操作中同样生效。删除整个父节点会覆盖已记录的子节点编辑,并跳过全部相关后代回调。
|
|
714
|
+
|
|
715
|
+
`validate` 的 `node.children` 同样可用于结构检查,但元素只有只读观察接口;遍历仍用 `ctx.walk()`,没有节点编辑、`node.walk()` 或 `skipChildren()`:
|
|
716
|
+
|
|
717
|
+
```ts
|
|
718
|
+
import { defineConfig } from 'weapp-vite/config'
|
|
719
|
+
|
|
720
|
+
export default defineConfig({
|
|
721
|
+
weapp: {
|
|
722
|
+
wxml: {
|
|
723
|
+
validate: async (_code, ctx) => {
|
|
724
|
+
await ctx.walk((node) => {
|
|
725
|
+
if (node.hasAttribute('data-card')
|
|
726
|
+
&& !node.children.some(child => child.tagName === 'button')) {
|
|
727
|
+
ctx.report({
|
|
728
|
+
severity: 'warning',
|
|
729
|
+
code: 'card-button',
|
|
730
|
+
message: '卡片缺少直接子 button',
|
|
731
|
+
location: node.location,
|
|
732
|
+
})
|
|
733
|
+
}
|
|
734
|
+
})
|
|
735
|
+
},
|
|
736
|
+
},
|
|
737
|
+
},
|
|
738
|
+
})
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### 输出顺序与热更新
|
|
742
|
+
|
|
743
|
+
执行顺序:模板编译 / 平台归一化 / i18n → `transform` → `remove` → 既有 Tailwind 与 compiler-plugin 输出处理 → `validate` → HMR 内容比较 → bundler 输出。后续输出插件仍可修改模板。独立分包转换完成后汇入主包,不重复转换。
|
|
744
|
+
|
|
745
|
+
外部规则文件必须显式调用 `ctx.addWatchFile('rules.json')`,建议在读取前注册,以便文件缺失导致构建失败后仍能恢复。外部依赖修改、删除、恢复会触发完整模板重建,覆盖主包、普通和独立分包。同一文件跨模板注册会去重;不自动追踪任意文件读取、环境变量或网络请求,也不允许监听构建输出目录。
|
|
746
|
+
|
|
747
|
+
每轮从当前编译输入重新转换,避免连续 HMR 累加上轮结果。回调应根据输入与显式依赖生成结果;不保证跨文件、跨分包的全局调用顺序,也不保证整个开发会话只调用一次。未配置 `transform` 时不增加编辑扫描;只返回字符串而不调用 `edit` 时,也不会为了回调额外解析模板。
|
|
748
|
+
|
|
749
|
+
## 最终模板校验
|
|
750
|
+
|
|
751
|
+
`weapp.wxml.validate` 在 `transform`、`remove` 和框架管理的 Tailwind / compiler-plugin 输出处理完成后执行,在 HMR 内容比较和发布前检查模板。可用于禁止调试节点残留、检查指定标签的必填属性或验证项目约定;默认不启用任何规则。
|
|
752
|
+
|
|
753
|
+
```ts
|
|
754
|
+
import { defineConfig } from 'weapp-vite/config'
|
|
755
|
+
|
|
756
|
+
export default defineConfig({
|
|
757
|
+
weapp: {
|
|
758
|
+
wxml: {
|
|
759
|
+
remove: { attr: [{ tag: 'view', name: 'data-testid' }] },
|
|
760
|
+
validate: async (_code, ctx) => {
|
|
761
|
+
if (ctx.isDev) {
|
|
762
|
+
return
|
|
763
|
+
}
|
|
764
|
+
await ctx.walk((node) => {
|
|
765
|
+
if (node.tagName === 'debug-panel') {
|
|
766
|
+
ctx.report({
|
|
767
|
+
severity: 'error',
|
|
768
|
+
code: 'no-debug-panel',
|
|
769
|
+
message: '发布模板不能包含 debug-panel',
|
|
770
|
+
location: node.location,
|
|
771
|
+
})
|
|
772
|
+
}
|
|
773
|
+
if (node.tagName === 'button' && !node.hasAttribute('data-track')) {
|
|
774
|
+
ctx.report({
|
|
775
|
+
severity: 'warning',
|
|
776
|
+
code: 'button-tracking',
|
|
777
|
+
message: 'button 缺少 data-track',
|
|
778
|
+
location: node.location,
|
|
779
|
+
})
|
|
780
|
+
}
|
|
781
|
+
})
|
|
782
|
+
},
|
|
783
|
+
},
|
|
784
|
+
},
|
|
785
|
+
})
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
### 校验接口与诊断
|
|
789
|
+
|
|
790
|
+
```ts
|
|
791
|
+
type WxmlValidate = (
|
|
792
|
+
code: string,
|
|
793
|
+
ctx: WxmlValidationContext,
|
|
794
|
+
) => void | Promise<void>
|
|
795
|
+
|
|
796
|
+
interface WxmlValidationDiagnostic {
|
|
797
|
+
severity: 'warning' | 'error'
|
|
798
|
+
code?: string
|
|
799
|
+
message: string
|
|
800
|
+
location?: WxmlSourceLocation
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// weapp.wxml
|
|
804
|
+
// validate?: WxmlValidate | WxmlValidate[]
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
`WxmlValidate`、`WxmlValidationContext`、`WxmlValidationVisitor`、`WxmlValidationDiagnostic` 均从 `weapp-vite/config` 和 `weapp-vite/types` 导出。
|
|
808
|
+
|
|
809
|
+
- 单函数和函数数组都支持异步;数组按声明顺序等待,所有回调读取同一份模板。返回值必须为 `undefined`,返回字符串、`null`、`false` 等会报错;修改模板请使用 `transform`。
|
|
810
|
+
- `ctx.walk(visitor)` 返回 `Promise<void>`,须 `await` 或 `return`。同步/异步 visitor 按源码顺序深度优先访问节点;同一模板在本轮多个回调间复用一次扫描,只检查 `code` 而不遍历时不解析。
|
|
811
|
+
- 节点使用只读 `WxmlElementInfo`:`tagName`、`attributes`、`children`、`parent`、`location`、`hasAttribute`、`getAttribute`;没有 `setAttribute`、`renameTag` 或 `remove`。属性、父节点和位置不可改写。
|
|
812
|
+
- 属性 `rawValue` 不解码、不求值,`{{tracking}}` 不代表已知运行时值。静态值规则只比较可确定的字面内容,校验不能判断页面数据变化后的属性值。
|
|
813
|
+
- `ctx.report` 的 `warning` 允许输出,`error` 汇总当前构建内其他回调及模板的正常诊断后阻止输出。同轮同文件、回调、严重程度、代码、消息和位置均相同的诊断去重。
|
|
814
|
+
- 诊断包含输出相对路径、从 1 开始的回调序号、可选规则代码和行列;不传 `location` 时为文件级诊断。位置对应本轮最终模板,不映射回 `.vue` 源文件。
|
|
815
|
+
- 回调异常、扫描失败、非法返回值或诊断格式立即中止并保留原始错误;没有静默跳过或自动修复。失败不会推进该构建的成功输出缓存,独立分包失败会传递到主构建。
|
|
816
|
+
|
|
817
|
+
上下文提供与 `transform` 相同含义的 `fileName`、`root`、`platform`、`mode`、`isDev`、`subPackageRoot` 和 `addWatchFile`;不提供 `edit`,校验诊断统一通过 `report` 发出。
|
|
818
|
+
|
|
819
|
+
### 按范围校验与复用规则
|
|
820
|
+
|
|
821
|
+
下面把普通分包的静态属性检查拆成可复用函数;`fileName` 为输出相对 POSIX 路径,`subPackageRoot` 只标识独立分包构建,普通分包应按路径筛选。
|
|
822
|
+
|
|
823
|
+
```ts
|
|
824
|
+
import type { WxmlValidate } from 'weapp-vite/config'
|
|
825
|
+
import { defineConfig } from 'weapp-vite/config'
|
|
826
|
+
|
|
827
|
+
const checkCheckout: WxmlValidate = async (_code, ctx) => {
|
|
828
|
+
if (ctx.platform !== 'weapp' || !ctx.fileName.startsWith('packages/checkout/')) {
|
|
829
|
+
return
|
|
830
|
+
}
|
|
831
|
+
await ctx.walk((node) => {
|
|
832
|
+
if (node.tagName === 'button' && node.getAttribute('data-track')?.rawValue === '') {
|
|
833
|
+
ctx.report({ severity: 'error', message: '静态埋点属性不可为空字符串', location: node.location })
|
|
834
|
+
}
|
|
835
|
+
})
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
export default defineConfig({
|
|
839
|
+
weapp: { wxml: { validate: [checkCheckout] } },
|
|
840
|
+
})
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
校验按最终静态标签名匹配,因此 Vue HTML 映射后的 `div` 通常需要检查 `view`。已被 `remove` 删除的节点不会再命中;输出插件新添加的节点会被检查。独立分包在所属构建中校验一次,汇入主包不重复执行。
|
|
844
|
+
|
|
845
|
+
### 外部规则与 HMR
|
|
846
|
+
|
|
847
|
+
外部规则文件与转换阶段共用监听、分阶段记录依赖。在读取前登记文件,即使文件缺失导致失败,也能在恢复时触发重新构建:
|
|
848
|
+
|
|
849
|
+
```ts
|
|
850
|
+
import { readFile } from 'node:fs/promises'
|
|
851
|
+
import { resolve } from 'node:path'
|
|
852
|
+
import { defineConfig } from 'weapp-vite/config'
|
|
853
|
+
|
|
854
|
+
export default defineConfig({
|
|
855
|
+
weapp: {
|
|
856
|
+
wxml: {
|
|
857
|
+
validate: async (_code, ctx) => {
|
|
858
|
+
ctx.addWatchFile('wxml-policy.json')
|
|
859
|
+
const policy: unknown = JSON.parse(await readFile(resolve(ctx.root, 'wxml-policy.json'), 'utf8'))
|
|
860
|
+
if (!Array.isArray(policy) || !policy.every(tag => typeof tag === 'string')) {
|
|
861
|
+
throw new Error('wxml-policy.json 必须是禁用标签名的字符串数组')
|
|
862
|
+
}
|
|
863
|
+
const forbidden = new Set<string>(policy)
|
|
864
|
+
await ctx.walk((node) => {
|
|
865
|
+
if (forbidden.has(node.tagName)) {
|
|
866
|
+
ctx.report({ severity: 'error', message: `不允许使用 ${node.tagName}`, location: node.location })
|
|
867
|
+
}
|
|
868
|
+
})
|
|
869
|
+
},
|
|
870
|
+
},
|
|
871
|
+
},
|
|
872
|
+
})
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
`wxml-policy.json` 例如 `["debug-panel"]`。同一文件跨模板或阶段登记会去重;修改、删除、恢复触发完整模板重建与校验。局部构建保留未触及模板的依赖,成功完整构建清理失效登记。不要监听输出目录,也不要把一次读取的结果永久缓存而忽略规则更新。
|
|
876
|
+
|
|
877
|
+
“最终”指框架管理的输出链完成后的模板,不保证覆盖任意排在该阶段之后的第三方 Vite 插件修改。不保证跨文件/分包回调顺序,不追踪任意文件读取、环境变量或网络响应;第一版不提供内置业务规则、跨文件全局校验、独立 CLI 或报告文件,也不能替代宿主完整语法和运行时检查。
|
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
这个文档聚焦在 weapp-vite 项目里最常见的 wevu 编写约束。
|
|
4
4
|
|
|
5
|
+
## 自动裁剪
|
|
6
|
+
|
|
7
|
+
平台分支通过编译期常量 `import.meta.env.PLATFORM` 裁剪,取值为 `weapp`、`alipay`、`tt`、`swan`、`jd`、`xhs` 或 `web`。沿用 `--platform` / `weapp.platform` 选择目标,weapp-vite 会自动注入对应值,无需额外裁剪配置。Wevu 发布包保留平台表达式,由消费构建替换常量并移除非目标分支。
|
|
8
|
+
|
|
9
|
+
平台专属逻辑可直接判断该常量,例如构建支付宝目标时只保留下面的支付宝分支:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
if (import.meta.env.PLATFORM === 'alipay') {
|
|
13
|
+
console.info('支付宝目标')
|
|
14
|
+
}
|
|
15
|
+
else if (import.meta.env.PLATFORM === 'web') {
|
|
16
|
+
console.info('Web 目标')
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
能力裁剪取决于实际使用:从 `wevu` 使用具名导入,编译器根据 Binding Manifest 按需安装 JSX island、ref、插槽和 layout 能力;没有创建 router 时不加载首航 guard 状态机。这些能力无需额外用户开关,也不由平台常量决定是否启用。
|
|
21
|
+
|
|
22
|
+
公开动态工厂保留保守兼容安装;独立工具链未提供平台时保留动态宿主探测。未使用 API/fetch 时可整体移除,使用后保留动态跨平台 adapter。Web 的宿主桥接不等同于 Vue DOM runtime,原生 App 渲染不在本期范围。
|
|
23
|
+
|
|
5
24
|
## 页面与组件
|
|
6
25
|
|
|
7
26
|
优先保持小程序语义,不要默认把 Vue Web 习惯直接搬进来。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { a as supportedCssLangs, i as scriptExtensions, l as getSourceStyleExtensions, n as configExtensions, o as templateExtensions, p as isSourceTemplateExtension, s as vueExtensions, u as getSourceTemplateExtensions } from "./constants-WOsG3Eqd.mjs";
|
|
1
2
|
import { createRequire } from "node:module";
|
|
2
3
|
import path from "pathe";
|
|
3
|
-
import { MINI_PROGRAM_PLATFORM_DESCRIPTORS as MINI_PROGRAM_PLATFORM_ADAPTERS } from "@weapp-core/shared";
|
|
4
4
|
import { fs } from "@weapp-core/shared/fs";
|
|
5
5
|
import process from "node:process";
|
|
6
6
|
import { recursive } from "merge";
|
|
@@ -54,7 +54,7 @@ function resolveAutoRoutesMacroImportPath() {
|
|
|
54
54
|
}
|
|
55
55
|
async function resolveAutoRoutesInlineSnapshot() {
|
|
56
56
|
try {
|
|
57
|
-
const { getCompilerContext } = await import("./getInstance-
|
|
57
|
+
const { getCompilerContext } = await import("./getInstance-CYF1vl5l.mjs");
|
|
58
58
|
const compilerContext = getCompilerContext();
|
|
59
59
|
const service = compilerContext.autoRoutesService;
|
|
60
60
|
const reference = service?.getReference?.();
|
|
@@ -86,89 +86,6 @@ function inlineAutoRoutesImports(source, inlineRoutes) {
|
|
|
86
86
|
}).join("\n").replace(AUTO_ROUTES_DYNAMIC_IMPORT_RE, `Promise.resolve(${JSON.stringify(inlineRoutes)})`).replace(AUTO_ROUTES_SPECIFIER_RE, JSON.stringify(resolveAutoRoutesMacroImportPath()));
|
|
87
87
|
}
|
|
88
88
|
//#endregion
|
|
89
|
-
//#region src/platforms/sourceAssets.ts
|
|
90
|
-
const PORTABLE_TEMPLATE_EXTENSIONS = ["wxml", "html"];
|
|
91
|
-
const PORTABLE_STYLE_EXTENSIONS = [
|
|
92
|
-
"wxss",
|
|
93
|
-
"css",
|
|
94
|
-
"scss",
|
|
95
|
-
"less",
|
|
96
|
-
"sass",
|
|
97
|
-
"styl"
|
|
98
|
-
];
|
|
99
|
-
function uniqueExtensions(extensions) {
|
|
100
|
-
return Array.from(new Set(extensions.filter((extension) => Boolean(extension))));
|
|
101
|
-
}
|
|
102
|
-
const ALL_NATIVE_TEMPLATE_EXTENSIONS = uniqueExtensions(MINI_PROGRAM_PLATFORM_ADAPTERS.map((adapter) => adapter.outputExtensions.wxml));
|
|
103
|
-
const ALL_NATIVE_STYLE_EXTENSIONS = uniqueExtensions(MINI_PROGRAM_PLATFORM_ADAPTERS.map((adapter) => adapter.outputExtensions.wxss));
|
|
104
|
-
const ALL_NATIVE_STYLE_RESOLVER_EXTENSIONS = ALL_NATIVE_STYLE_EXTENSIONS.filter((extension) => extension !== "css");
|
|
105
|
-
const ALL_SOURCE_TEMPLATE_EXTENSIONS = uniqueExtensions([...PORTABLE_TEMPLATE_EXTENSIONS, ...ALL_NATIVE_TEMPLATE_EXTENSIONS]);
|
|
106
|
-
const ALL_SOURCE_STYLE_EXTENSIONS = uniqueExtensions([...PORTABLE_STYLE_EXTENSIONS, ...ALL_NATIVE_STYLE_EXTENSIONS]);
|
|
107
|
-
function getAdapter(platform) {
|
|
108
|
-
return platform ? MINI_PROGRAM_PLATFORM_ADAPTERS.find((adapter) => adapter.id === platform) : void 0;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* 返回当前平台的模板源码选择顺序。
|
|
112
|
-
* 原生平台后缀优先,便携源码后缀作为兼容回退。
|
|
113
|
-
*/
|
|
114
|
-
function getSourceTemplateExtensions(platform) {
|
|
115
|
-
const nativeExtension = getAdapter(platform)?.outputExtensions.wxml;
|
|
116
|
-
return platform ? uniqueExtensions([nativeExtension, ...PORTABLE_TEMPLATE_EXTENSIONS]) : ALL_SOURCE_TEMPLATE_EXTENSIONS;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* 返回当前平台的样式源码选择顺序。
|
|
120
|
-
* 原生平台后缀优先,预处理器和便携样式后缀继续可用。
|
|
121
|
-
*/
|
|
122
|
-
function getSourceStyleExtensions(platform) {
|
|
123
|
-
const nativeExtension = getAdapter(platform)?.outputExtensions.wxss;
|
|
124
|
-
return platform ? uniqueExtensions([nativeExtension, ...PORTABLE_STYLE_EXTENSIONS]) : ALL_SOURCE_STYLE_EXTENSIONS;
|
|
125
|
-
}
|
|
126
|
-
function isSourceTemplateExtension(extension) {
|
|
127
|
-
const normalized = extension.startsWith(".") ? extension.slice(1) : extension;
|
|
128
|
-
return getSourceTemplateExtensions().includes(normalized);
|
|
129
|
-
}
|
|
130
|
-
function isSourceStyleExtension(extension) {
|
|
131
|
-
const normalized = extension.startsWith(".") ? extension.slice(1) : extension;
|
|
132
|
-
return getSourceStyleExtensions().includes(normalized);
|
|
133
|
-
}
|
|
134
|
-
function isNativeTemplateSource(filePath, platform) {
|
|
135
|
-
const nativeExtension = getAdapter(platform)?.outputExtensions.wxml;
|
|
136
|
-
return Boolean(nativeExtension && filePath.endsWith(`.${nativeExtension}`));
|
|
137
|
-
}
|
|
138
|
-
//#endregion
|
|
139
|
-
//#region src/constants.ts
|
|
140
|
-
const VERSION = "__VERSION__";
|
|
141
|
-
/**
|
|
142
|
-
* 源代码支持的 js 文件格式
|
|
143
|
-
*/
|
|
144
|
-
const jsExtensions = ["ts", "js"];
|
|
145
|
-
const scriptExtensions = [
|
|
146
|
-
"ts",
|
|
147
|
-
"js",
|
|
148
|
-
"tsx",
|
|
149
|
-
"jsx"
|
|
150
|
-
];
|
|
151
|
-
/**
|
|
152
|
-
* 源代码支持的 vue 文件格式
|
|
153
|
-
*/
|
|
154
|
-
const vueExtensions = ["vue"];
|
|
155
|
-
/**
|
|
156
|
-
* 源代码支持的 json 文件格式
|
|
157
|
-
*/
|
|
158
|
-
const configExtensions = [
|
|
159
|
-
"jsonc",
|
|
160
|
-
"json",
|
|
161
|
-
...jsExtensions.map((x) => `json.${x}`)
|
|
162
|
-
];
|
|
163
|
-
/**
|
|
164
|
-
* 源代码支持的 css 文件格式
|
|
165
|
-
*/
|
|
166
|
-
const supportedCssLangs = ALL_SOURCE_STYLE_EXTENSIONS;
|
|
167
|
-
/**
|
|
168
|
-
* 源代码支持的 wxml 文件格式
|
|
169
|
-
*/
|
|
170
|
-
const templateExtensions = ALL_SOURCE_TEMPLATE_EXTENSIONS;
|
|
171
|
-
//#endregion
|
|
172
89
|
//#region src/utils/file/entry.ts
|
|
173
90
|
const pathExistsInFlight = /* @__PURE__ */ new Map();
|
|
174
91
|
const JS_OR_TS_RE = /\.[jt]s$/;
|
|
@@ -329,4 +246,4 @@ async function extractConfigFromVue(vueFilePath, options) {
|
|
|
329
246
|
}
|
|
330
247
|
}
|
|
331
248
|
//#endregion
|
|
332
|
-
export {
|
|
249
|
+
export { findJsonEntry as a, isJsOrTs as c, touch as d, getAutoRoutesMacroImportCandidates as f, resolveAutoRoutesInlineSnapshot as h, findJsEntry as i, isTemplate as l, inlineAutoRoutesImports as m, changeFileExtension as n, findTemplateEntry as o, hasAutoRoutesMacroImport as p, findCssEntry as r, findVueEntry as s, extractConfigFromVue as t, normalizeFileExtension as u };
|