Uh oh!
There was an error while loading. Please reload this page.
fix(components): span 废弃提示改为每模块加载一条,且只对 JSON 作者面开火 (#4917) - #5029
Conversation
… authors (#4917) `SpanRenderer` was two rulings behind `div`. It still `console.warn`ed on every render, and it still fired at nodes the `kind:'html'` tier's own parser had emitted -- the two defects ruled on for `div` in #3965 (PR #3998, which named `span` as the follow-up and left this shape untouched) and #4000 (PR #4916, which built the provenance mechanism and exported `isHtmlTierNode`). This is that follow-up; it copies the shape now in `basic/div.tsx` rather than inventing a second one. The notice is a property of the deprecated TYPE, not of each node, so repeating it per render only buries the page's real console errors. And an author writing the plain inline tag in a `kind:'html'` page gets a node this deprecated renderer serves, then is told to migrate to `badge` / `text` -- neither of which exists in that tier's vocabulary, with nothing they could write to make it stop. `basic/html-elements.tsx` deliberately leaves the tag out of its own TAGS list precisely because this module registers it. Order is load-bearing and pinned: the html-tier exemption is checked BEFORE the warn-once set is marked, so an html-tier node rendering first cannot swallow the notice a JSON-authored node earns later on the same page. Provenance is established by the producer (the parser stamps what it emits), not guessed from the node's shape here. The migration guidance is byte-for-byte unchanged; the notice only gained the sentence naming which surface it means. The provenance test deliberately does NOT copy the sibling `div` test's `textContent` control assertion. The inline tag's own text never arrives, because this renderer reads `schema.body` while the parser emits `children` -- a separate rendering-path defect, filed as #5027 with its own ruling to make on which key is canonical, and deliberately not patched here behind a lenient `||`. The control instead reads a sibling paragraph's text plus the presence of the inline element itself: together they prove the page compiled AND that the node reached this renderer, so the observed silence is an exemption rather than a missing node. Both assertions still hold once #5027 is fixed. Co-authored-by: Claude <noreply@anthropic.com>
yinlianghui
commented
Aug 17, 2026
PM 验收 ✅ ACCEPT(#4917,批次 21) 实物核验(merge-base CI 亲读:20/20 check runs completed,零失败(两项 path-filter skipped 计绿)。 反向验证读数:①撤豁免 → 双红且第二条正是顺序钉开火(豁免位置承重的直接证据);②只撤去重 → 4 红 3 绿的分池证明两个守卫独立而非一体;③空绿检查按 #4916 家族陷阱做成配对(删对照→假绿实测坐实、留对照→红)—— 对照承重被实测而非声称。 deviation 1 的判断是本单亮点:不照抄 div 的 textContent 对照 —— 因为 span.tsx 读 #5027 已由 PM 定级 Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#4917
背景:同一个渲染器落后
div两步span的废弃提示身上压着两条已经裁定过的纪律,而div两条都补上了,span一条没补:div组件类型,docs 站每张缩略图都刷一条 deprecation warning #3965 / PR fix(components): div 废弃提示每次模块加载只报一次;catalog 换类型那一半升级待定夺 (#3965) #3998 把div从「每次渲染 warn 一条」改成「每模块加载一条」。该 PR 的「边界」一节写明span「该照抄本 PR 的守卫形状,留作后续」—— 至今没有后续。div的废弃警告会对kind: 'html'tier 自己解析出的节点开火 —— 作者无法消除,且意味着div结构上退不掉 #4000 / PR fix(components): div 废弃提示按 provenance 收窄到 JSON 作者面 (#4000) #4916 裁定kind:'html'tier 自己的解析器产出的节点豁免废弃提示,并把机制落在生产者侧、从@object-ui/sdui-parser包入口导出isHtmlTierNode(Symbol.for注册键,对 JSON / DOM 都不可见)。span的判断面一字未动。两条叠加的后果与
div当初一致:提示对引擎自己解析器的输出开火,而且不去重。basic/html-elements.tsx的 TAGS 列表故意把这个标签排除在外(注释写明「已在别处注册」),正是因为它由basic/span.tsx注册 —— 所以 html tier 页面里写这个内联标签,编译出的节点必然落到这个废弃渲染器上,作者收到一条建议换badge/text的提示,而那两个都不是 html tier 里的等价标签,作者写什么都消不掉。本 PR 就是那个「后续」:照抄
div.tsx现在的形状,不另发明一套。改动
packages/components/src/renderers/basic/span.tsx:_warnedDeprecationsSet +warnDeprecatedOnce()—— 提示是类型的属性而不是节点的属性,每次渲染重复一遍只会把页面真正的报错埋掉。dev 构建照旧报,恰好一条。isHtmlTierNode(schema),provenance 由生产者建立(parser 给自己产出的节点打标),不是在这里猜节点形状。warnDeprecatedOnce内部「production 提前 return 在标记之前」是同一条纪律。content/docs/components/basic/span.mdx补一段作用域说明,与div.mdx同形。测试
两个新钉子,照抄
div-deprecation-*.test.tsx的形状:span-deprecation-warn-once.test.tsx—— production 静默用例必须跑在最前(Set 会 latch 整个模块实例的生命周期),它同时钉住守卫的顺序:production 渲染不得吞掉之后的 dev 提示。七次 SpanRenderer 调用(含三层嵌套)只收一条。span-deprecation-provenance.test.tsx—— html-tier 用例跑在最前、对着一个未被污染的 Set,所以「零提示」不能用「之前某次渲染已经 latch 了守卫」解释;紧接着的 JSON 作者面用例观察到恰好一条,只有在前一个用例没标记 Set 时才可能。两个用例互相钉住。一处刻意的偏离,连带发现 #5027
provenance 测试没有照抄
div那条textContent对照断言。原因是这个内联标签在 html tier 里的文字压根不会渲染:该渲染器读schema.body,而 parser 产出的是children(div.tsx读的是schema.children || schema.body,所以div没事)。实测:这是渲染路径的独立缺陷,不是提示作用域的缺陷,而且它需要单独裁定「规范的子节点键是
children还是body」—— 按 Commandment #0.1 contract-first,这该在类型/规范侧定,不该由渲染器顺手加一个宽容的||糊掉。已单独立 #5027,本 PR 不碰。对照断言因此改用「同页 p 标签的文字」+「内联元素本身存在(带 html 源码里写的 class)」:两者合起来证明页面确实编译并渲染了、且节点确实落到了这个废弃渲染器上,所以观察到的静默是豁免生效,而不是节点根本没出现。两条断言在 #5027 修好之后依然成立。
反向验证(先书面预判,再变异)
isHtmlTierNode豁免判断expected [ Array(1) ] to have a length of +0 but got 1/expected [] to have a length of 1 but got +0has/add),保留 production 提前 returntoHaveLength(0)在页面根本没渲染时免费通过);③b 保留对照断言 → 红在not.toContain('failed to compile')Tests 4 passed(绿,且是错误的绿);③bexpected 'HTML page failed to compile (2)…' not to contain 'failed to compile'变异 ① 的第二处失败是顺序钉子在响:没有豁免时,html-tier 节点会把 JSON 作者应得的那一条吞掉 —— 这恰好是把豁免放在 Set 标记之前所要防的事。
变异 ② 的判别项(用例 1 保持绿)证明 production 提前 return 与去重是两道独立的守卫,不是一道。
变异 ③ 是对对照断言本身的验证,回答「这条
toHaveLength(0)会不会因为整页编译失败而免费通过」:③a 证明危险真实存在,③b 证明本 PR 的对照断言确实拦得住。还原一律
git checkout --指定路径,未使用git stash(共享栈)。Generated by Claude Code