Uh oh!
There was an error while loading. Please reload this page.
fix(cli): derive the temp app's workspace alias table from pnpm-workspace.yaml (#3890) - #4922
Conversation
…pace.yaml (#3890) Inside a pnpm workspace the generated temp app installs nothing and the repo root declares no `@object-ui/*`, so a Vite alias table is the only thing that resolves a platform package there. `dev` kept that table by hand — eleven names — while the property it needed was the transitive import graph of every package already in it. Measured on the reported commit: the generated entry closes over 21 packages, ten were unlisted, and 8 of the first 400 modules a browser walk reaches answered 500. Vite's dependency scan named only four of the ten, because a scan stops at the first layer it cannot resolve, so a backfill of "the missing four" would have left six behind. The table is now derived from the workspace manifest: every scoped package that exposes a source barrel, aliased at its `src` directory. `serve` and `build` had no workspace branch at all — no aliases, plus an unconditional `npm install` against a manifest that is empty here — and now share the same helper, so the three cannot drift apart again. The `lucide-react` entry moves from the entry file `require.resolve` reports to the package root. An alias replaces the matched prefix and keeps the rest, so the old target rewrote every subpath import of it into `<entry-file>/<subpath>`; with the platform packages finally reachable, `components/src/lib/lazy-icon.tsx` answered 500 for exactly that reason. Measured after, same repro: 0 of 2498 modules non-200, `objectui build` produces a bundle in-workspace for the first time, and the page renders its schema. Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
commented
Aug 17, 2026
PM 验收:ACCEPT(#3890,批次 17,PM 会话 路线裁量(B,派生式)—— 以实测定案而非偏好:卡面点名的 4 个缺失包只是 Vite 依赖扫描在第一层失败处能看见的;静态量出传递 value-import 闭包 = 21 包、手表 11 条、实缺 10 —— 路线 A(回填 4 条)当场就是不完整修法。派生规则读 复现读数(实物级):BEFORE 依赖扫描报错 + 模块图 8×500(ObjectGrid/ObjectForm/MasterDetailForm 等);AFTER 全图 2498 模块零非 200,真 chromium 页面渲染出 schema 文本; 实物核验(已过):按真实 merge-base 对账 —— 本 PR 自身改动恰为 6 文件 +659/−122,全在 反向验证:三变异全部预判先行 —— M1 抠 fields:3 红 11 绿,其中一条超预判的红(.tsx barrel 样本恰是 fields)按「方向一致、数量欠预判」如实记录;「目标都是目录」钉如预判保持绿(空产出假绿形状,依赖对账钉互补);M2 lucide 还原:2 红且失败信息打印出被改写的不存在路径;M3 serve 整体还原:2 红点名 serve.ts。 有据偏离(全部接受):①lucide-react 条目同表同修 —— 非相邻缺陷而是本卡验收状态的必要条件(平台包通了它才暴露,不修页面仍不渲染),单列论证 + 两钉(子路径从真实 importer 读出);②build 改动前态未实跑(无条件 npm install 联网慢),以静态零命中 + M3 红代偿,「未做的事」如实列明 —— 不假称实测,正确。 CI(亲读终态):20 项全 completed,18 success + 2 skipped,零失败。 附带产出:#4923(serve/build 项目根定位与 dev 分叉,待分诊)、#4924(serve 无 --no-open 观察卡)、#4925(console 34 条手表同类风险记录)。 → undraft + auto-merge (SQUASH)。 Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
…ld (objectstack-ai#4923) (objectstack-ai#5038) * fix(cli): resolve the project root the same way in dev, serve and build (objectstack-ai#4923) For a project with an app config and a `pages/` directory beside it, the three commands answered one invocation two different ways. `dev` anchored on the schema argument: `dirname(<schema>)` is the project root, `pages/` beside it means file-system routing, the named file is the app config. `serve` and `build` looked for a `pages/` directory in the current working directory and nowhere else, so from any directory above the project they fell through to single-schema mode and handed the app config to the renderer as if it were a page. Measured on the reported fixture (`<root>/app.json` + `<root>/pages/index.json`, invoked from the directory above): `dev` reported the project and one route, `serve` reported `Loading schema: <root>/app.json`, and `build` did the same and exited 0 — the emitted bundle embedded the app config as the page schema and contained no page from `pages/` at all. A wrong artifact, produced silently. PR objectstack-ai#4922 made `serve`/`build` able to resolve modules in a workspace, which is what turned this from "it could not run anyway" into a reachable wrong output. Detection now lives in one helper the three share (`utils/project-source.ts`), resolving in a fixed order: a `pages/` directory beside the schema argument, else one under the current directory, else single-schema mode. The reporting is split out beside it, so a reader comparing two commands' output is comparing one function with itself. `serve` and `build` also pass the resolved app config to the routed generator, which they never did — a routed project kept its routes but lost its layout otherwise. No documented intent was found for `serve`/`build` to be single-schema commands; the documentation says the opposite in three places (`cli.ts` calls `dev` an "alias for serve", the README and the docs page call `serve` a "legacy alias of `dev`", and the routing section promises file-system routing whenever "the project contains a `pages/` directory"). The docs now also say WHICH directory that is. A lone schema file with no `pages/` beside it is unchanged and pinned: it is the fallback, not a casualty. Co-authored-by: Claude <noreply@anthropic.com> * test(cli): pin the shared detection by its call site, not the identifier (objectstack-ai#4923) Reverse verification exposed the gap: reverting serve.ts to the cwd-only reading while leaving its now-unused import behind kept the bare-name assertion green. The pin now names the call form, so an import alone cannot satisfy it. Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes#3890
前提验证(先于实现)
卡面前提在
origin/main=5ffcc1432上完全成立,并且实测发现缺失面比卡面记的更大:commands/dev.ts:228-240的别名表仍是 11 个包的手工清单,@object-ui/fields零命中(反证:同文件里别名表本身找得到)。serve.ts/build.ts的isMonorepo零命中,vite 配置里没有resolve.alias。dogfood/app.json+pages/index.json,node packages/cli/dist/cli.js dev … --port 5893 --no-open):Vite 依赖扫描报出卡面点名的那 4 个包,页面模块 500。但缺的不是 4 个,是 10 个。 生成 app 入口静态 import 9 个平台包,其传递 value-import 闭包是 21 个包,表里只有 11 个:
@object-ui/fields、@object-ui/permissions、@object-ui/mobile、@object-ui/plugin-detail@object-ui/data-objectstack、@object-ui/i18n、@object-ui/plugin-map、@object-ui/providers、@object-ui/react-runtime、@object-ui/sdui-parserVite 的依赖扫描在第一层解析不了的地方就停,所以它只报得出前 4 个。这条实测直接决定了路线。
路线选择:B(从
pnpm-workspace.yaml派生)A(回填 4 条)在本仓当场就是不完整的修法 —— 补完卡面点名的 4 个之后还剩 6 个继续 500,页面照旧空白。而"手工清单必须追传递导入图"这个结构本身才是产生本卡的原因:清单要维护的不是"app import 了什么",而是"app 传递 import 了什么",即表内每个包每个源文件的导入图,没有任何门禁钉住它。
新增
packages/cli/src/utils/workspace-vite.ts,三命令共用:pnpm-workspace.yaml的packages:模式(用已是 cli 依赖的glob展开,!排除也支持),取@object-ui/scope 且有 src barrel 的成员,别名指向其src目录。resolve.extensions顺序探测index.*。仓内.ts/.tsx两种拼法都在(corevsfields),按解析器的顺序探测不是猜测,而是解析器自己会给的答案。apps/console/vite.config.ts:117-136);更重要的是别名替换的是匹配到的前缀并保留其余部分,文件目标会把子路径 import 改写成一个不可能存在的路径。pnpm-workspace.yaml对账的测试兜住"不能少列"。serve/build补上 monorepo 分支:workspace 内跳过npm install(那份清单在 workspace 下本就是空的),并共用同一份 vite 覆盖。一处顺带必须修的:
lucide-react别名它和平台包在同一张表里,而且是平台包解析通了之后才暴露出来的:原来的目标是
require.resolve给的入口文件,于是每个lucide-react/…子路径 import 都被改写成"入口文件路径 + 子路径",packages/components/src/lib/lazy-icon.tsx因此 500(它 import 该库的 dynamic-icon 子路径)。改成指向包根目录后,裸 specifier 与子路径落在同一个目录上,入口交给包自己的exports决定。不修它本卡的验收状态(页面能渲染)达不到,故在本 PR 内一并改,并有专门的钉。复现前后读数(同一脚本、同一 fixture,仓根)
按浏览器的方式走模块图(取回一个模块 → 解析它被改写后的 import URL → 继续取回):
5ffcc1432,端口 5893)Failed to run dependency scan,报 4 个包document.body.innerText)hello from the dogfood app;失败响应 0objectui build(workspace 内)index.html+ assetsobjectui serve(workspace 内)npm install500 的 8 个模块:
plugin-grid/src/ObjectGrid.tsx、plugin-form/src/{ObjectForm,ModalForm,MasterDetailForm,LineItemsPanel,sectionFields}、plugin-kanban/src/ObjectKanban.tsx、components/src/lib/lazy-icon.tsx(最后一个即上节的 lucide 子路径)。反向验证(先书面预判,后跑;变异前已 commit,还原用
git checkout)三次变异,方向都先写后跑:
@object-ui/fields)。预判:与 manifest 对账的钉必须红;"别名目标都是目录"那条保持绿(它只遍历表里产出的东西 —— 正是"产出为空所以绿"的形状,也正是对账钉存在的理由)。实测:3 红 11 绿,对账钉与"卡面点名的 4 个"钉如预判红;第 3 条超出预判 ——resolves barrels spelled .ts and .tsx alike恰好拿fields当.tsx样本,一起红了。方向一致,数量我预判少了一条,如实记录。"目标都是目录"确实保持绿。…/dist/cjs/lucide-react.js/dynamic.mjs),即缺陷的活体。serve.ts整个还原到改动前。预判:三命令一致性的两条钉红。实测:2 红,都点名 serve.ts。还原后全量重跑复绿(147/147)。
测试
新增
packages/cli/src/__tests__/workspace-vite.test.ts(14 条):与pnpm-workspace.yaml独立走一遍的对账钉(被测函数用glob展开,测试用自己的readdirSync;用被测函数重算期望值只能证明它是确定性的)、卡面点名的 4 个 + 扫描看不到的 6 个、.ts/.tsxbarrel 推断、别名目标必须是目录、无 barrel 的包必须被排除(@object-ui/runner)、lucide 指向包根且子路径可解析(子路径从真实 importer 里读出来而不是写死)、临时 fixture workspace 上的派生规则自测(含"barrel 一没就掉出表"的植入缺陷方向)、以及三命令一致性(都走同一 helper、都有 workspace 分支、都不许再出现手写别名条目)。sideEffects 门(#3943)按 PM 提示单独确认:它只扫 workspace 包目录下 basename 匹配
vite.config.*的文件,本改动落在packages/cli/src/,不在其扫描面内,实测同批绿。未做的事(如实记录)
objectui build的改动前状态没有实测跑:那条路径会先无条件npm install把整套已发布平台包装进.objectui-tmp(联网、慢),不划算。改动前的判据用的是origin/main上的静态事实(serve.ts/build.ts里isMonorepo与resolve.alias皆零命中)+ 变异 3 的红。改动后的构建成功是实测的。changeset:
.changeset/cli-workspace-alias-derivation-3890.md(patch)。Generated by Claude Code