fix(layout): page-header 注册补 isContainer: true,校验器不再对文档承诺的 children 写法报 not-a-container (#3900) - #3973
Merged
Conversation
…法报 not-a-container (#3900) PageHeader 一直有意把 schema.children 渲染进右侧动作槽(PageHeader.tsx:182), docs 把该槽的优先级写成公开契约,文档页唯一的 live demo 正是这个形状且实测正常 渲染;但 packages/layout/src/index.ts 的注册漏了 isContainer: true。 漏这个 flag 从来没挡住任何渲染 —— 渲染路径根本不读它,消费者是 sdui-parser 的 not-a-container 诊断、Studio 调色板、react-page 标签表。所以后果是校验器在说谎: 作者照文档写出能正常渲染的 schema 却拿到一条 warning,而会说谎的 warning 会训练 作者(尤其 AI 作者)连真实的 not-a-container 一起无视。 children 是 objectui JSON 协议里每个节点的基础属性(validate.ts 的 BASE_PROPS), 不是 PageHeaderProps 的键,所以补这个 flag 不在 spec 之外新开作者面 —— 维护者 2026-08-09 就 #3900 的 A/B 分叉按此理由裁定 A 案。 两个方向的钉子:文档 demo 走应用真实构建的 manifest 后不再产生 not-a-container (先断言无 unknown-component 且 demo 确实带 children,避免缺席假绿);阳性对照 navigation-renderer 带 children 时诊断照旧触发。 Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Collaborator
Author
|
PM 验收(session_01GTRjn8xBqp75dk7kFupVRt):通过,转 ready 并挂 auto-merge。#3900 按维护者 A 案落地。 核验记录(head
out-of-scope 两条处置得当:#3972(layout 声明面两处不符,同族合一单)由 PM 分诊;#3968(type-check 盲区 finding,本 PR 钉子恰在其中的弱点已如实披露)进分诊轮存量。 Generated by Claude Code |
yinlianghui
marked this pull request as ready for review
August 9, 2026 19:53
This was referenced Aug 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3900
按维护者 2026-08-09 的 A 案裁定(issue 评论 5230001265)执行:
packages/layout/src/index.ts的page-header注册补isContainer: true。为什么补 flag,而不是反过来退掉 children
PageHeader一直有意把schema.children渲染进右侧动作槽(packages/layout/src/PageHeader.tsx:182、:207),record:quick_actions嵌在page:header.children下就是靠它;content/docs/layout/page-header.mdx把该槽的优先级(action→ React children →actions→ schema children)写成公开契约;该文档页唯一的 live demo(examples/schema-catalog/src/schemas/layout-page-header/pageheader-with-actions.json)正是这个形状,而且实测正常渲染。漏掉
isContainer从来没有挡住任何渲染 —— 渲染路径根本不读它。它的消费者在别处:sdui-parser的not-a-container诊断、Studio 调色板元数据、react-page 标签表。所以真正的后果是校验器在说谎:作者照文档写出能正常渲染的 schema,却拿到一条 "page-header does not accept children";信了这条 warning 去掉 children,右槽就空掉。而会说谎的 warning 比缺一条更贵 —— 它训练作者(尤其 AI 作者)连真实的not-a-container(那些确实不收子节点的组件)一起无视。这不是在 spec 之外新开作者面:
children是 objectui JSON 协议里每个节点的基础属性(packages/sdui-parser/src/validate.ts的BASE_PROPS把它和type/id/className并列),不是PageHeaderProps的键。所以这个 flag 回答的是协议层面的「该节点是否接受子节点列表」,而对这个组件,答案一直是「是」。改动
packages/layout/src/index.ts:page-header注册补isContainer: true,并把该注册块的缩进/尾逗号对齐同文件其它容器组件(原来是 6 空格 / 10 空格,邻居都是 2 空格步进)。注释写清「为什么这不是扩 spec 面」,免得下一个读者把它当越界。examples/schema-catalog/test/pageheader-with-actions.test.tsx:模块头原来有一句「注册没有声明isContainer: true」—— 本 PR 把这句话变成假的,所以必须改;顺带在这里补上两个方向的钉子(见下)。packages/layout/src/__tests__/page-header-authorable-keys.test.tsx:声明面的注册级断言(bare 键与 namespaced 键各一条),与同文件既有的inputs收窄断言同处一室 —— 那条管「不要多声明」,这条管「不要否认」。examples/schema-catalog/package.json:新增 devDependency@object-ui/sdui-parser(测试要真的跑那条诊断)。@object-ui/layoutpatch。两个方向的钉子
not-a-container。 manifest 按packages/components/src/renderers/layout/page.tsx的getJsxManifest()同样的方式构造(getKnownTypes()+getMeta()),不是getAllConfigs()—— 后者的.type恒为 namespaced 形式,bare 的page-header标签根本不在 manifest 里,断言会以unknown-component的方式假绿。所以这条钉子先断言「没有unknown-component」且「demo 确实带 children」(诊断分支由node.children?.length守卫),再断言「没有not-a-container」—— 缺席只有在证明检查真的跑过之后才算证据。navigation-renderer(完全由items驱动,从不读schema.children)。少了这条,把诊断整个弄哑、或者让isContainer默认为真,钉子 1 都会继续绿 —— 那就等于什么都没量。未改动 / 已核对
page:header(@object-ui/components)刻意不动。 那个渲染器(containers.tsx的PageHeaderRenderer)完全不读schema.children(该范围内 grepchildren零命中),所以它没有isContainer是正确的,不是同一个 bug。而且它在PUBLIC_BLOCKS里,给它加 flag 会把它从每个kind:'react'页面的组件作用域里剔掉(react-page.tsx:66跳过容器)—— 那是另一件事,需要另一次裁定。packages/sdui-parser/scripts/gen-manifest.ts)只序列化getPublicConfigs(),即PUBLIC_BLOCKS加显式tier:'public'的集合;legacy kebab 键page-header两者都不属于(PUBLIC_BLOCKS里是page:header,注册也没有tier),而且sdui.manifest.json/sdui-intrinsics.d.ts/sdui-blocks.md三个产物本仓一个都没入库(git ls-files无命中),都是构建期写到 outDir。isContainer的读点:sdui-parser/src/validate.ts:89、components/.../react-page.tsx:66、components/.../page.tsx:464;app-shell的PageBlockCanvas.tsx:683是同名局部变量,与注册元数据无关。packages/components/src/__tests__/react-page-scope.test.tsx那条isContainer断言只覆盖list-view/object-form。packages/layout/src/__tests__/side-effects-manifest.test.ts用ComponentRegistry.register(的正则从源码抽注册键,register(那一行未动。PageHeader.tsx、不动文档、不动 spec(A 案下三者本已一致)。反向验证(先预判后跑)
预判:临时撤掉
isContainer: true—— 钉子 1 应精确翻红(多出一条且仅一条page-header的not-a-container),钉子 2 与四条既有的渲染/形状断言应全部保持绿(渲染路径不读该 flag,这正是本 issue 的不对称之处;渲染断言若跟着红,说明前提理解错了)。实测与预判一致:撤掉 flag 后
3 failed | 13 passed,红的恰好是本 PR 新增的三条(layout 两条expected undefined to be true,demo 一条多出),阳性对照与四条既有渲染/形状断言全绿。还原后与撤改前逐字节一致,同样三个文件重跑
16 passed。变异未提交。顺手发现(已另开单,不在本 PR 修)
inputs声明面还有两处与实现不符 ——page-header漏声明渲染器真读、spec 也声明的icon(所以本 PR 的实测输出里,这份 demo 今天就带一条unknown-prop,修 flag 前后都在),以及navigation-renderer把items声明成type: 'object'而 prop 是数组。钉子 1 刻意按 code 过滤、没把它们一起钉住,就是为了那单被修时本 PR 的钉子不会翻红。finding):examples/schema-catalog/test/的测试文件没有任何tsc编译过,而本该抓这件事的scripts/check-type-check-coverage.mjs因为countTestFiles只走src/而看不见它们 —— 本 PR 的钉子有一半正好落在这个盲区里。验证
pnpm exec turbo run type-check --concurrency=2(仓根):Tasks: 78 successful, 78 total,exit 0。pnpm exec vitest run packages/layout packages/sdui-parser examples/schema-catalog --maxWorkers=2(仓根):Test Files 16 passed (16) / Tests 1204 passed (1204)。node scripts/check-control-bytes.mjs:OK(3854 个文件)。check-changeset-presence/check-changeset-no-major:OK。Generated by Claude Code