@lark-apaas/coding-steering 0.1.69 → 0.1.70-alpha.20261021140057

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 CHANGED
@@ -26,9 +26,9 @@ vice versa). Current state:
26
26
 
27
27
  | stackId (directory) | What it is | `tech.md` | `skills/` | template package |
28
28
  |---|---|---|---|---|
29
- | `vite-react` | React + Vite SPA | ✅ | `plugin-guide`, `react-three-fiber` | [`coding-template-vite-react`](../../templates/vite-react) |
29
+ | `vite-react` | React + Vite SPA | ✅ | `plugin-guide`, `react-three-fiber`, `browser-compat` | [`coding-template-vite-react`](../../templates/vite-react) |
30
30
  | `html` | 妙搭 html,带 devserver | ✅ | `rich-interactive-design` | [`coding-template-html`](../../templates/html) |
31
- | `nestjs-react-fullstack` | NestJS + React 全栈 | — | `authn`/`authz`/`feishu`/`plugin`/`devops`/`trigger`/`user-*` … + `skills_local/` (`code-fix`, `coding-guide`) | [`coding-template-nestjs-react-fullstack`](../../templates/nestjs-react-fullstack) |
31
+ | `nestjs-react-fullstack` | NestJS + React 全栈 | — | `authn`/`authz`/`feishu`/`plugin`/`devops`/`trigger`/`user-*`/`browser-compat` … + `skills_local/` (`code-fix`, `coding-guide`) | [`coding-template-nestjs-react-fullstack`](../../templates/nestjs-react-fullstack) |
32
32
  | `design-html` | **无构建**纯静态 HTML 托管(源码即产物) | — | _(占位,待补)_ | [`coding-template-design-html`](../../templates/design-html) |
33
33
  | `design-stack` | **带构建**的 design / 创意栈(有工具链,区别于 buildless 的 `design-html`) | — | _(占位,待补)_ | _(暂无)_ |
34
34
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/coding-steering",
3
- "version": "0.1.69",
3
+ "version": "0.1.70-alpha.20261021140057",
4
4
  "description": "Stack-specific steering content for miaoda-coding templates",
5
5
  "type": "module",
6
6
  "files": [
@@ -10,6 +10,7 @@
10
10
  "lint:md": "markdownlint 'steering/**/*.md' --ignore 'steering/**/skills/**' --ignore 'steering/**/skills_common/**' --ignore 'steering/**/skills_local/**'"
11
11
  },
12
12
  "devDependencies": {
13
+ "@mdn/browser-compat-data": "^8",
13
14
  "markdownlint-cli": "^0.47.0"
14
15
  },
15
16
  "publishConfig": {
@@ -0,0 +1,168 @@
1
+ ---
2
+ name: browser-compat
3
+ description: "Use when 应用在部分手机、部分浏览器或旧版本系统上白屏、打不开、样式错乱,而在电脑或其他设备上正常;或用户要求确认应用能否在某个浏览器版本上运行、调整打包目标 build.target。触发词:手机白屏, 部分手机打不开, 安卓打不开, 苹果手机打不开, 旧手机打不开, 旧手机样式错乱, 电脑正常手机白屏, 某些手机报 SyntaxError, 低版本浏览器, 浏览器兼容, 浏览器版本, iOS 15, Chrome 93, build.target, blank page on some phones, browser compatibility, old browser"
4
+ steering: true
5
+ steering-topic: browser_compat
6
+ match-template-name: nestjs-react-fullstack
7
+ ---
8
+
9
+ # 浏览器兼容排查与修复
10
+
11
+ 部分设备白屏或样式错乱、其他设备正常时,用「构建产物 + 出问题的浏览器版本」做证据,判断是不是浏览器兼容问题。JS 语法不兼容就调低打包目标,复验通过后交给用户发布;打包配置修不了的问题(样式、运行时正则、平台脚本)要如实告诉用户。
12
+
13
+ **适用前提**:同一个页面在部分设备或浏览器上异常、在其他设备上正常,或用户明确要求确认对某个浏览器版本的支持。所有设备都白屏、开发预览本身就白屏,多半是代码或构建问题,按常规方式看控制台和构建报错,不要走本流程。
14
+
15
+ ## 为什么要这样做
16
+
17
+ - 旧浏览器遇到不认识的 JS 语法(例如 `class` 里的 `static {}` 静态块),会在**解析阶段**直接抛 SyntaxError,整个页面脚本都不执行,表现就是白屏。电脑和新手机的内核新,所以正常。
18
+ - 样式是另一层:浏览器不认识的 CSS 规则会被整块丢弃。模板用的 Tailwind v4 几乎所有样式都包在 `@layer` 里,不支持 `@layer` 的浏览器(Chrome 99、iOS 15.4 以下)页面能跑,但样式会大面积缺失。这一层 `build.target` 修不了。
19
+ - 产物里有哪些语法,取决于依赖(包括平台 SDK)**当前版本**的代码,会随升级变化。不要凭记忆判断「某个 SDK 支持到哪个浏览器版本」,每次都用脚本扫描实际产物。
20
+ - 白屏的原因很多(运行时报错、接口失败、线上还是旧版本……)。没有证据就改打包配置,会白白浪费一次发布,问题却还在。
21
+
22
+ ## Quick Reference
23
+
24
+ | 步骤 | 做什么 | 通过标准 |
25
+ | ---- | ------ | -------- |
26
+ | 1. 拿版本 | 问用户出问题环境的浏览器版本或 UA(方法见下文) | 拿到 UA,或「浏览器 + 版本」 |
27
+ | 2. 构建 | 构建到 `node_modules/.cache/browser-compat/dist`,用 `run_in_background` | 构建日志里有 `built in` |
28
+ | 3. 验证 | `node <本 skill 目录>/scripts/check-browser-compat.mjs --browser "<UA>" --dir node_modules/.cache/browser-compat/dist` | 退出码 0 兼容 / 1 JS 无法解析 / 3 打包配置修不了的问题 / 2 无法判断 |
29
+ | 4. 修复 | `vite.config.ts` 加 `build.target`,重复第 2、3 步 | 复验退出码 0;或 3 / 剩下的都是 `build.target` 管不到的(如实告知) |
30
+ | 5. 交付 | 提交 → 请用户发布 → 在原设备复测 | 用户确认能打开 |
31
+
32
+ 本 skill 在沙箱里的目录是 `.agent/skills/steering/nestjs-react-fullstack/skills/browser-compat/`;如果路径不同,以本文件所在目录为准。
33
+
34
+ ## 第 1 步:拿到出问题环境的浏览器版本
35
+
36
+ 后面的验证全靠这个版本号。用户还没给版本时,**先问,不要猜,也不要先改配置**。一次性把获取方法告诉用户:
37
+
38
+ - **安卓上的 Chrome 或 Chrome 内核浏览器**:在出问题的浏览器地址栏打开 `chrome://version`,复制「用户代理」那一整行;或者在「设置 → 关于 Chrome」里看版本号。
39
+ - **iPhone / iPad**:「设置 → 通用 → 关于本机」里的 iOS 版本。iOS 上所有浏览器和 App 内页面都用系统 WebKit 内核,系统版本就是浏览器版本。
40
+ - **在飞书、豆包、微信等 App 里打开**:App 内核和手机自带浏览器的版本可能不同,以出问题的那个环境为准。拿不到时,请用户在该环境里打开一个能显示 UA 的页面并截图。
41
+ - 机型和安卓系统版本**不能代替**浏览器版本:同一台手机里,不同浏览器的内核版本可以相差很多。
42
+
43
+ 脚本 `--browser` 接受完整 UA,或这些简写:`chrome 93`、`android 93`(安卓 Chrome 内核)、`webview 93`、`ios 15.4`、`safari 16`、`firefox 92`、`edge 90`、`samsung 14`。UA 里没有 `Chrome/` 字样的安卓浏览器(老系统自带浏览器等)判断不了内核版本,脚本会拒绝,如实告诉用户这类浏览器无法验证、建议换用 Chrome 内核浏览器。
44
+
45
+ ## 第 2 步:构建到隔离目录
46
+
47
+ ```bash
48
+ mkdir -p node_modules/.cache/browser-compat && NODE_ENV=production npx vite build --config vite.config.ts --outDir node_modules/.cache/browser-compat/dist --emptyOutDir --sourcemap false > node_modules/.cache/browser-compat/build.log 2>&1
49
+ ```
50
+
51
+ - 用 `run_in_background: true` 执行:构建可能超过前台单次调用的时间上限。之后用 `tail -n 20 node_modules/.cache/browser-compat/build.log` 确认出现 `built in`。
52
+ - 为什么不直接 `npm run build`、不输出到 `dist/`:全栈应用开发态的页面 HTML 就写在 `dist/client/index.html`,由 NestJS 读取返回,覆盖或清空它会让开发预览直接挂掉;`npm run build` 还会构建服务端,又慢又用不上。`node_modules/.cache` 被 git 忽略,验证产物不会混进提交。`--sourcemap false` 同理:验证用不到 sourcemap,平台配置可能把它写到 `outDir` 之外。
53
+ - 构建失败时,先按报错修好构建:没有成功的产物就没法验证。
54
+
55
+ ## 第 3 步:用脚本验证
56
+
57
+ ```bash
58
+ node .agent/skills/steering/nestjs-react-fullstack/skills/browser-compat/scripts/check-browser-compat.mjs --browser "<第 1 步拿到的 UA 或简写>" --dir node_modules/.cache/browser-compat/dist
59
+ ```
60
+
61
+ 脚本做两件事,浏览器支持数据都运行时现查 MDN 的浏览器兼容数据(`@mdn/browser-compat-data`):首次运行装到 `node_modules/.cache/browser-compat/bcd`(需要能访问 npm 源,几秒内完成;不改项目的 `package.json` 和 lockfile),之后复用。
62
+
63
+ - 【JS】用项目自带的 Vite 解析每个脚本,找出该浏览器不认识的语法。不认识的语法会让所在脚本整份解析失败。
64
+ - 【CSS】从 MDN 列出该浏览器不支持的 @规则、属性、选择器、函数,再到样式里找,估算会被丢弃的样式比例。写在 `@supports` 里、或前面已有兜底写法的,不算。
65
+
66
+ 按退出码处理:
67
+
68
+ - **0「兼容」**:JS 都能解析,样式没有大面积缺失。**不要改打包配置**,转到下文「验证兼容但仍白屏」。
69
+ - **1「JS 无法解析」**:输出列出每种语法(MDN 条目名和说明)、需要的版本和所在文件,做第 4 步。两种特殊情况:
70
+ - 「在模块顶层构造的正则」:脚本一加载就执行这个 `RegExp(…)`,效果和解析失败一样。`build.target` 修不了,要改写正则或换依赖。
71
+ - 「该浏览器不支持 ES 模块」:Vite 产物在这个浏览器上整体不会执行,`build.target` 也修不了。如实告诉用户需要升级浏览器。
72
+ - **3「JS 都能解析,但有打包配置修不了的问题」**:结论会写明是哪一种,都**不要**在应用里自己写插件、补丁去改:
73
+ - 函数里构造的正则:`RegExp(…)` 里用了该浏览器不支持的正则语法,执行到这段代码时报错。如果这个函数在页面加载过程中就会被调用(比如组件渲染、依赖初始化),页面仍会白屏,所以**不能**据此说白屏已修好。按文件和代码片段找到它属于哪个功能:在用户自己的代码里,就改写这个正则,改完复验到 0;在依赖里(常见于 Markdown 渲染一类的库),如实告诉用户哪个功能会出错、页面可能仍白屏,必须在原设备复测确认。
74
+ - 样式会被丢弃:超过一成时结论为 3,常见原因是 Tailwind v4 的样式几乎都包在 `@layer` 里。如实告诉用户这台设备上样式会大面积缺失,并给出输出里「样式基本可用」和「全部生效」两个版本(「全部生效」不含该浏览器任何版本都不支持的个别特性,这些会单独列出)。
75
+
76
+ 如果第一次检查就是 3,说明白屏不是 JS 解析失败造成的,按「验证兼容但仍白屏」继续排查。
77
+ - **2「无法判断」**:看报错处理,都**不要盲改配置**。
78
+ - 浏览器无法识别 → 回第 1 步换一种写法。提示「看起来是安卓系统版本」→ 请用户提供 Chrome 内核版本。提示 UA 里没有 Chrome 版本号 → 这类浏览器无法验证,如实告知。
79
+ - 产物目录不存在 → 回第 2 步。
80
+ - 无法获取 MDN 兼容数据 → 网络问题,稍后重试。
81
+ - 提示 vite 不提供 parseAst → 项目的 Vite 版本太旧(Vite 7、8 已验证可用)。
82
+ - 「无法完整判断」(有脚本解析失败),或提示 MDN 数据里没有某个条目 → 脚本本身需要维护。
83
+
84
+ 后几种重试无效时,如实告诉用户这次无法验证。
85
+
86
+ 另外几类输出:
87
+
88
+ - 「JS 能解析所需的最低版本」「样式基本可用 / 全部生效需要的版本」:回复用户时都带上。说「升级到某版本就完全正常」时,只能用「全部生效」那个版本。
89
+ - 「写在 try 块里的 RegExp」:多为特性检测(先试一下浏览器支不支持),没有计入结论,排查运行时报错时再看。
90
+ - 「⚠ 无法解析」:这些脚本或样式没有参与判断。结论是 1 或 3 时照常处理,并在回复里说明。
91
+
92
+ ## 第 4 步:修复——在 vite.config.ts 设置 build.target
93
+
94
+ 在 `vite.config.ts` 传给 `defineConfig` 的对象里加 `build.target`。preset 会把这份配置合并进最终配置,所以加上就生效。`defineConfig` 的导入保持原样不动:当前模板从 `@lark-apaas/coding-preset-vite-react` 导入,较早创建的应用可能从 `@lark-apaas/fullstack-vite-preset` 导入,都不要改;其他原有配置也保持不动。
95
+
96
+ ```ts
97
+ export default defineConfig({
98
+ build: {
99
+ target: ['chrome87', 'edge88', 'firefox78', 'safari14'],
100
+ },
101
+ // 原有的 resolve 等配置保持不变
102
+ });
103
+ ```
104
+
105
+ 取值说明:
106
+
107
+ - **默认用上面这组**(约等于 ES2020)。它让 JS 语法在 2020 年以后的主流浏览器上都能解析,修好报障设备的同时也照顾到其他旧设备,产物体积大约只增加 1%。它只管打包产物里的 JS:样式和打包之外的脚本有各自的最低版本,以脚本输出为准。
108
+ - **四个浏览器家族都要写**。打包器只产出一份代码,要满足 `target` 里列出的所有浏览器。只写一个家族(比如 `['chrome93']`)时,这个家族已支持、其他家族还不支持的语法会原样保留(例如 Safari 比 Chrome 晚支持的私有方法),对应的旧设备仍可能白屏。
109
+ - 用户浏览器比默认值还旧(比如 Chrome 80、iOS 13):把对应家族调到用户的版本,例如 `'chrome80'`、`'safari13'`。
110
+ - 已经有 `build.target`:只下调需要的家族,不要覆盖其他 `build` 配置。
111
+
112
+ 改完用**同一个浏览器版本**重复第 2、3 步:
113
+
114
+ - **退出码 0**:修好了。
115
+ - **退出码 3**:JS 已经都能解析,剩下的按第 3 步处理。正则字面量降级后会被打包器改写成 `RegExp(…)`,可能从 1 变成 3(在函数里)或仍是 1(在模块顶层):这只是把报错从解析期推迟到执行期,正则本身还是不被支持。用户自己代码里的正则要改写到复验为 0。
116
+ - **仍然是 1**:对比修复前后的输出,修复后还在的问题,`build.target` 管不到,不要继续往下调:
117
+ - 在 `public/` 拷贝来的文件、或你写在 `index.html` 的内联脚本里:改对应源文件的写法。
118
+ - 在平台单独构建的文件(例如 `polyfills.js`)或平台注入的内联脚本里:应用里改不了,如实告诉用户并转平台处理。单独用 `<script>` 引入、不被别的脚本 `import` 的文件解析失败只影响它自己(比如 polyfills 失效),不一定白屏。
119
+ - 在打包产物里还在,或者是「在模块顶层构造的正则」:打包器降不了,要改源码写法;在依赖里自己改不了时,如实告诉用户并转人工处理。
120
+
121
+ 剩下的都属于这几类时,可以交付,但要在回复里逐条说明。
122
+
123
+ 语法问题不要靠安装 `@vitejs/plugin-legacy` 或额外的 polyfill 来解决:polyfill 补不了语法;plugin-legacy 只给不支持 ES 模块的浏览器生成兼容包,Chrome 93、iOS 15 这类浏览器支持 ES 模块,仍然会加载原来的产物。
124
+
125
+ ## 第 5 步:交付
126
+
127
+ 1. 可以清理验证产物:`rm -rf node_modules/.cache/browser-compat`。
128
+ 2. 提交 `vite.config.ts` 的改动,提交说明写清目的,例如「降低打包目标以兼容 Chrome 93」。
129
+ 3. 发布会影响线上用户,**不要自行发布**。告诉用户已经修好并验证通过,请其点击发布;发布后在原设备上重新打开页面(App 内页面要完全关闭再打开,避免看到缓存的旧页面)。
130
+
131
+ 回复用户时说清五件事:
132
+
133
+ - 原因:哪个语法、出现在哪个文件、需要的版本与用户版本的对比;
134
+ - 改了什么;
135
+ - 修复前后的验证结论;
136
+ - 剩下的问题:复验不是 0 时,明确说明「白屏已修好,但这台设备上……」(样式会大面积缺失、某个功能执行时会报错、某个平台脚本无法执行),以及输出里对应的版本。**不要**笼统地说「修好了」;
137
+ - 下一步:发布并在原设备复测。
138
+
139
+ ## 验证兼容但仍白屏
140
+
141
+ 按顺序排查,**都不要改打包配置**:
142
+
143
+ 1. **线上可能还是旧版本**:确认最近一次发布是否包含当前代码。没有发布过,就请用户发布后再复测。
144
+ 2. **运行时报错**:请用户提供控制台报错。安卓可以在电脑 Chrome 打开 `chrome://inspect` 远程调试,iPhone 可以用 Mac Safari 的「开发」菜单。脚本输出里「写在 try 块里的 RegExp」和运行时正则也是可疑点。
145
+ 3. **内置 API 缺失**:脚本只检查语法和样式,不检查内置 API(例如 `Promise.withResolvers` 在 iOS 17.4 以下没有、`Array.prototype.at` 在 iOS 15.4 以下没有),这类问题在执行时报错,同样可能白屏。看到 `xxx is not a function` 一类的报错时优先怀疑它。
146
+ 4. **其他原因**(接口失败、权限、网络):按常规方式排查。
147
+
148
+ ## Common Mistakes
149
+
150
+ | 错误 | 正确做法 |
151
+ | ---- | -------- |
152
+ | 所有设备都白屏,也套用本流程 | 只有部分设备异常时才适用;否则按常规排查 |
153
+ | 没拿到浏览器版本就改配置 | 先问版本;没有验证证据不改 |
154
+ | 把机型或安卓系统版本当浏览器版本 | 要浏览器内核版本;iOS 用系统版本 |
155
+ | 直接 `npm run build` 或输出到 `dist/` | 输出到 `node_modules/.cache/browser-compat/dist` |
156
+ | 构建放前台执行,超时被杀 | 用 `run_in_background: true` |
157
+ | `target` 只写报障浏览器那一个家族 | 四个家族都写 |
158
+ | 凭记忆判断某个 SDK 支持到哪个版本 | 每次都扫描实际产物 |
159
+ | 为语法问题安装 plugin-legacy 或 polyfill | 语法问题靠 `build.target` 解决 |
160
+ | 运行时正则的问题也靠下调 `build.target`,或说「白屏已修好」 | 自己的代码就改写到复验为 0;依赖里的如实告知风险并要求原设备复测 |
161
+ | 降了 `build.target` 仍是 1,继续往下调 | 修复后还在的问题 `build.target` 管不到,按第 4 步分类处理 |
162
+ | 改完不复验就让用户发布 | 复验后再交付,剩下的问题逐条说明 |
163
+ | 复验不是 0 时笼统地说「修好了」 | 如实说明这台设备上还会有什么问题 |
164
+ | 说「升级到某版本就完全正常」 | 只用输出里「全部生效」的版本 |
165
+ | 在应用里手写插件改写 `@layer` 或平台脚本 | 如实告知,转平台处理或请用户升级浏览器 |
166
+ | 退出码 2 时自己推断结论 | 如实说明这次无法验证 |
167
+ | 自己发布 | 请用户确认后发布 |
168
+ | 用 `run_skill` 调脚本 | 脚本没有可执行权限,用 `node <路径>` 运行 |