fix(cli): 生成的临时 app 补齐 lucide-react 与七个 plugin 声明,两处清单区间锚到仓内 - #3854
Conversation
`createTempAppWithRouting` 生成的 layout 两处 import lucide-react(`import * as
LucideIcons` 喂 DynamicIcon 与四个 LucideIcons.* 图标,具名 `{ Moon, Sun }` 渲染主题
开关)—— 两处都活,但生成的 package.json 从未声明它。核实时又量到:两个生成器的
src/App.tsx 都以副作用 import 拉七个 @object-ui/plugin-*,同样从未声明;未声明集合是
八个包,不是 issue 报的一个。
@object-ui/* 的区间改为由本 CLI 自身版本推导:.changeset/config.json 把 @object-ui/cli
与全部平台包放在同一 fixed 组,永远同版本发布,故 `^<自身版本>` 既是当前值又保证在
registry 上存在。原先的 `^0.1.0` 对已发布到 17.x 的包解析不到任何版本(registry 无
0.1.0),而写死字面量本身就是化石发生器 —— fixed 组每次发布都换版本。
工具链区间锚到仓内实测值:vite ^5.0.0 -> ^8.2.0、typescript ~5.7.3 -> ^6.0.3、
@vitejs/plugin-react ^4.2.1 -> ^6.0.5、react/react-dom ^18.3.1 -> 19.2.8(@types/* 同
步)、react-router-dom ^7.12.0 -> ^7.18.2、postcss ^8.5.6 -> ^8.5.26、autoprefixer
^10.4.23 -> ^10.5.4。tailwindcss 有意留在 ^3.4.19 并钉成显式 deferred:生成的
index.css/postcss.config.js/tailwind.config.js 是 v3 全套,只抬区间会得到装得上但无样式
的 app,已另开 #3852。
两个生成器改为先产出文件映射再由 writer 落盘,测试因此断言在 CLI 真正写出的产物上。
移植三条结构性闸门(每条都配"种回缺陷"自检):bare import 必须被声明、不得声明无人
import 的 versioned 依赖、src/** 不得存在从 src/main.tsx 不可达的文件。移植提示:
create-plugin 的 import 扫描只认单引号,而本处模板混用引号,照抄会正好漏掉
`from "lucide-react"`。
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
|
补一条本体之外的验证:已构建产物(dist 布局)里的版本推导也核过了。
于是直接调构建后的产物,并且把 cwd 放在仓库之外: 两点由此坐实:dist 与 src 深度不同但推导结果一致;仓外 cwd 也照样拿到 顺带说明该函数在解析不到时的取向:抛错,不猜。写出一份区间是编的清单,正是本单要修的那类缺陷,所以宁可让命令响亮地失败,也不静默产出一份装不上的 Generated by Claude Code Generated by Claude Code |
|
✅ 验收(PM,session 实物核验: 裁定要点:
转 ready 并挂 auto-merge。越界三单 #3852/#3853/#3855 已立,归分诊席。 Generated by Claude Code |
Fixes #3827
方向 1(PM 缺省)。前置核实先做,结论决定了 lucide 的处置方向与 #3755 相反。
1. LucideIcons 活性核实:两处 import 都活,故补声明而非删 import
issue 方向 3 问
import * as LucideIcons(:591)与具名{ Moon, Sun }(:592)是否真被生成代码用到。逐一核到使用点(行号为生成的src/Layout.tsx内偏移):import * as LucideIconsLucideIcons[name](DynamicIcon 动态查表)、LucideIcons.ChevronsUpDown、LucideIcons.Sun、LucideIcons.Moon、LucideIcons.Monitorimport { Moon, Sun }<Sun /> 与<Moon />(两个 className 带 rotate/scale 过渡)所以这里与 #3755 是相反的处置:那单删掉了一条无人 import 的
lucide-react声明,本单补上一条被 import 却没声明的。同一条规则(声明面与 import 面必须对齐)在两个方向上的两种落法 —— 这点写进了测试注释,避免下一个 agent 误以为"lucide 一律该删"。commands/dev.ts里本来有一段消费侧兜底:把lucide-reactalias 到从packages/components解析出的路径,注释原话是 "avoid dependency not found in temp app"。但那段只在 monorepo 分支跑,serve.ts/build.ts完全没有。按 contract-first,缺声明的账要记在生产者(生成的清单)上,alias 退回成 workspace 便利。2. 越界量到的:未声明集合是八个包,不是一个
src/App.tsx(两个生成器都有)以副作用 import 拉七个插件注册组件:@object-ui/plugin-charts/-editor/-kanban/-markdown/-form/-grid/-view,两处清单都从未声明。issue 正文说"作者核对过一次 import 与声明的对应关系,只漏掉紧邻下一行的 lucide-react" —— 实测这个判断偏乐观:漏的是八个。这不算扩面:同一个函数、同一张清单、同一类缺陷,而且移植进来的闸门在这七个包还没声明时根本无法变绿(见下方反向验证输出)。修一个留七个会让新闸门只能靠豁免名单活着。
3. 锚定对照表(生成值 ↔ 仓内锚源)
锚源均为实测,扫 46 个仓内 manifest 的
dependencies+devDependencies(排除 peer —— peer 说的是"能接受什么",与"本仓实际装哪个"是两回事):@object-ui/react等 9 个平台包^0.1.0(仅 2 个被声明)^17.3.0lucide-react^1.28.0react/react-dom^18.3.119.2.8@types/react^18.3.1219.2.18@types/react-dom^18.3.119.2.4react-router-dom^7.12.0^7.18.2vite^5.0.0^8.2.0typescript~5.7.3^6.0.3@vitejs/plugin-react^4.2.1^6.0.5postcss^8.5.6^8.5.26autoprefixer^10.4.23^10.5.4tailwindcss^3.4.19^3.4.19(有意不动)为什么
@object-ui/*改成推导而不是写死字面量。.changeset/config.json把@object-ui/cli与全部平台包放进同一个fixed组,永远同版本发布;于是^<自身版本>既是当前值,又保证在 registry 上存在(用户手里的 cli 是哪版,兄弟包就在那版一起发过)。反过来说,写死字面量在这里不只是"可能变化石",而是化石发生器 —— fixed 组每次发布都换版本,任何硬编码区间隔天就旧。这正是^0.1.0能落后 16 个 major 的机制。registry 实测:@object-ui/reactlatest=17.3.0、76 个版本、没有 0.1.0,所以^0.1.0不是"落后",是解析不到任何已发布版本 —— issue 的判断在这点上完全站得住。推导的前提(fixed 组不被拆)被单独断言,不是默认成立:见测试
pins the release lockstep that lets the platform range be derived,同时校验各包当前版本确实等于 cli 版本(配置写了 ≠ 事实如此)。tailwindcss有意留在 v3 并钉成显式 deferred。 仓内是 Tailwind 4(根^4.3.3,6 个postcss.config.*全用@tailwindcss/postcss),而@object-ui/components的 peer 写tailwindcss: ^4.2.1—— 所以^3.4.19不只是落后,是冲突。但抬区间不是版本编辑:生成的index.css是 v3 指令、postcss.config.js用的是 v4 已迁走的tailwindcss插件键、tailwind.config.js是 v3 config。只抬区间会得到"装得上、渲染不出样式"的 app,比现状更糟(看起来像修好了)。已带实测证据另开 #3852(含"仓内objectui dev今天就静默无样式"的复现),本 PR 把它钉成TAILWIND_V3_DEFERRED显式记账,并断言 components 的 peer 值,免得读成漏改。4. 两条规则的移植与自检
生成器改为先产出
Record<路径, 内容>再由 writer 落盘,测试因此断言在 CLI 真正写出的产物上(#3733/#3826 定的形状)。三条闸门,每条都配"种回缺陷"自检:src/**)src/**不得从 entry 不可达App.tsx的./Layoutimport → 必须点名src/Layout.tsx;另加"孤立 schema JSON"用例移植时踩到的一处真陷阱(写下来给下一个搬这套规则的人):
create-plugin的 import 扫描正则只认单引号,因为它守的模板全是单引号。本处模板混用 —— 生成的 layout 写from "lucide-react"、src/theme-provider.tsx写from "react"。照抄那个正则,闸门会正好瞎在 issue 报的两行之一(:592)。所以importedPackagesOf做成引号无关,并有一条测试专门钉住它能看见双引号形式与副作用形式。unreachableGeneratedFiles判src/**全部文件而非只判.tsx?:生成的 schema JSON 写出来却没有对应 import,等于路由里不存在的页面 —— 这也是一类死产物。今天没有产物触发它,所以配了自检。5. 反向验证(方向先判后跑)
预判(跑之前写下):把源码里的清单还原成 pre-#3827 → ① import 闸门红、点名 lucide-react + 7 插件;② 锚表完整性红(8 个键不再被声明);③ 区间锚定与落盘断言红;④ "不得声明无人 import 的依赖"保持绿 —— pre-fix 缺陷是"import 了没声明的",不是"声明了没人 import 的",这条守的是反方向;⑤ 两条可达性闸门保持绿 —— 改清单动不了模块图。
实跑结果:五项全部与预判一致,
4 failed | 15 passed:第一轮反向验证时这条红只点了 7 个(plain 的断言先失败,vitest 就地停,routed 那条没跑到,lucide 只在锚表 diff 里露出来)。这是诊断质量问题而非闸门问题,已把三种形态合成一次断言,现在红会直接点名 8 个 —— 上面的输出就是改完之后重跑的。
6. 重构等价性:逐字节 before/after
先产出映射再落盘是重构,风险是悄悄改了生成内容。用同一份输入在改动前后各生成三种形态(plain / routed 无 appConfig / routed 有 appConfig)× 两种 cwd(仓内 monorepo、仓外),逐文件比对:除
package.json外全部字节一致,package.json的 diff 恰好就是上面那张锚定表。顺带删掉
createTempApp里一个死变量(第一份tailwindConfig被后面的finalTailwindConfig完全遮蔽,从未写盘),对产物零影响 —— 由上面的字节比对背书。7. 生成产物冒烟
用真实入口
createTempApp/createTempAppWithRouting生成到mkdtempSync目录,断言盘上每个文件与映射逐字节相等且没有多余文件,含嵌套src/schemas/page0.json真落地;finally里rmSync清干净。故意不装依赖:仓内 hoist 会兜住缺声明,装成功什么也证明不了 —— 这正是本单的教训,所以断言面是清单结构,不是安装成功。验证
pnpm exec vitest run packages/cli/→ Test Files 3 passed, Tests 57 passed(新增文件 19 条)pnpm exec turbo run type-check --concurrency=2(仓根全量,CI 同命令)→ 78 successful, 78 totalpnpm exec eslint两个改动文件 → 0 error;1 warning 是(appConfig as any)?.title,与origin/main同,非本 PR 引入node scripts/check-control-bytes.mjs→ OK(3758 个文件);另做闸门外自扫grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]'两个改动文件 → 零命中越界发现(均已单独立单,未在本 PR 修)
^4.2.1peer 冲突;dev.ts的编程式 postcss 在 v4 下抛异常被 try/catch 吞成一行 warn,仓内objectui dev今天就静默无样式。具体缺陷,未打finding。tsconfig.json开了strict+noUnusedLocals,但产物过不了它(未用 import 5 处、隐式 any 7 处,已附 tsc 输出),而且没有任何命令会跑 tsc。今天不红,打了finding。Generated by Claude Code