Skip to content

fix(lint): translation-target-unknown 读取视图容器的默认 form.sections (#5415) - #5422

Merged
baozhoutao merged 3 commits into
mainfrom
claude/issue-5415-lint-container-form-sections
Aug 5, 2026
Merged

fix(lint): translation-target-unknown 读取视图容器的默认 form.sections (#5415)#5422
baozhoutao merged 3 commits into
mainfrom
claude/issue-5415-lint-container-form-sections

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #5415

前提复核(改之前先证伪)

按正文在 origin/main(切 worktree 时 9fad07f66,issue 钉的是 b4872a868,两者之间未触 packages/lint)重跑了 showcase 复现:直接 import 真实的 examples/app-showcase/src/ui/views/contact.view.ts,把正确翻译 showcase_contact._sections.contact 的 bundle 喂给 validateTranslationReferences,输出与 issue 正文逐字一致:

rule:     translation-target-unknown
severity: warning
where:    locale "zh-CN" · object "showcase_contact" · section "contact"
message:  Translations are keyed to section "contact", which nothing on object
          "showcase_contact" declares — no `fieldGroups[].key`, no named form-view
          section, no named `record:details` section. ...
hint:     ... Object "showcase_contact" declares no named section at all.

前提成立。

改了什么

collectViewRecord 原本从四个锚点收集对象的合法 _sections 名:fieldGroups[].keylistViews.* / formViews.* 的具名段落、页面 record:details 的具名段落、以及视图记录自身的 sections。容器的默认 form(defineView({ list, form, formViews }) 里那个、没有指定具名表单视图时 ObjectForm 渲染的那个)哪一条都不是 —— 循环写死 ['listViews', 'formViews'],而它既不在其中、也不是记录自身的 sections,于是 view.form.sections[].name 对事实集零贡献

现在默认 form 走与 formViews.* 完全相同的段落收集路径,绑定取 bindingOf(view.form) ?? listBinding —— 即先 form.data.object、再记录级对象、最后旁边那个 list 的绑定 —— 与 CLI i18n walker 的 viewObjectNameview.form.data.object 的解析对齐。这正是 #5405 要建立的集合关系(覆盖走查器要求的 key ⊆ 引用校验器接受的 key)在 lint 侧的那一半。

顺带把四处段落收集收敛成一个 addSections 收集器:锚点清单从此是「一串调用点」,而不是四份会各自漂移的循环副本。

没有任何收紧:无名段落依旧不可翻译(它没有可查的稳定 key),任何锚点都没声明的 _sections key 依旧上报 —— 只是 hint 现在会列出真实存在的锚点,而不再断言「declares no named section at all」。

未触 _views 族(#5164 领地),未触 packages/lint/src/index.ts(PR #5416 在队列中触该文件)。

测试

packages/lint/src/validate-translation-references.test.ts 新增 4 例:

  1. 默认 form 的具名段落可解析(合成最小形状)。
  2. 默认 form 按自己的 data 绑定,而不是旁边的 list —— 一个 stack 里同时断言两个方向。只断言「crm_lead 仍被上报」是不可证伪的:在修复前它同样通过,因为默认 form 什么都没贡献(空集合让断言"通过"的经典陷阱);是 crm_contact 那一半让这一对有了方向。
      1. 真实 showcase 元数据(import { Contact } + import { ContactViews })。选真实 import 而不是手工缩写:缺陷本身就是「锚点清单漏了一项」,手写 fixture 只能钉住作者记得的锚点。而这个面恰好是暴露它的形状 —— 对象只声明 field.group、没有 fieldGroups[],所以默认 form 是它唯一的段落锚点。一例钉四个具名段落全部被接受;一例是防过度放宽的反例:contract(contact 的错拼)与 who_is_this(formViews.create 那个无名段落的 label)两个 key 仍各上报一条,且 hint 改口列出 Declared sections: contact, notes, status, work

按车道口径,两条真实元数据 import 的用例显式 }, 60_000)

反向验证(方向先预判,再跑)

预判:删掉新加的这一条 limb,4 条新用例应全红(这是纯放宽,不存在 #5018 那种反转,也不存在 #5046 那种「计数从 1 变 0 反而多出诊断」)。实测一致:

× resolves a section named on the container default `form` 12ms
× binds the default `form` by its OWN data, not by the list beside it 2ms
× accepts every section the default form names 3ms
× still reports a section name nothing declares, and names the real ones 5ms
AssertionError: expected [ { severity: 'warning', …(5) } ] to deeply equal []
AssertionError: expected 'Sections are translatable only throug…' to contain 'Declared sections: contact, notes, st…'
Tests  4 failed | 24 passed (28)

恢复 limb 后:

pnpm --filter @objectstack/lint test        →  Test Files 57 passed | Tests 1202 passed (1202)
pnpm --filter @objectstack/lint typecheck   →  tsc --noEmit (clean)
pnpm --filter @objectstack/lint build       →  DTS Build success
npx eslint <两个改动文件> --no-inline-config  →  clean
node scripts/check-nul-bytes.mjs            →  OK (5429 files)

消费半径已按规则的调用点(而非改动包)扫过:translation-target-unknown / validateTranslationReferences 仅由 packages/lint 内的 reference-integrity-suite + index.ts 消费,全仓 _sections 的 fixture 只出现在本规则的测试与 packages/spec 的 translation schema 测试里 —— 没有 #5046 那种跨包 fixture 需要同步。

关于 #5416

切 worktree 时 PR #5416(#5405)尚未合入 main,故按分诊口径跳过「走查器要求 ⊆ 校验器接受」的集合断言,不为它引入跨分支依赖;本修复不依赖 #5416#5416 落地后可在其上补该断言。

越界发现(已另开 issue,未在本 PR 修)

🤖 Generated with Claude Code

https://claude.ai/code/session_016FNvXhtSdnEGEfLEsMmvxh


Generated by Claude Code

claude added 2 commits August 5, 2026 10:57
…ult `form.sections` (#5415)

`collectViewRecord` derived an object's legal `_sections` names from
`fieldGroups[].key`, the named sections on `listViews.*` / `formViews.*`, a
page's named `record:details` sections, and the view record's own `sections`.
The view CONTAINER's default `form` — what `defineView({ list, form })`
declares and `ObjectForm` renders when no named form view is asked for — was in
none of those, so `view.form.sections[].name` contributed nothing: a bundle
correctly translating a heading that DOES render was reported as keyed to a
section "nothing declares", with a hint advising the author to delete it.

The default form now feeds the same section collector as `formViews.*`, bound
by `bindingOf(view.form) ?? listBinding` — the resolution the CLI i18n walker
performs on `view.form.data.object` — so the rule that demands a key and the
rule that accepts one agree on which object a heading belongs to. Every anchor
is now a call into one collector instead of its own copy of the loop.

Tests import the real showcase contact metadata (`Contact` +`ContactViews`):
its object declares `field.group` and no `fieldGroups[]`, so the default form
is its ONLY section anchor, which is exactly what a hand-reduced fixture could
not have pinned. Over-widening controls kept: an unnamed section is still
untranslatable, an undeclared key is still reported, and the default form is
bound by its own `data`, not by the list beside it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016FNvXhtSdnEGEfLEsMmvxh
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 5, 2026 11:03am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/hook-bodies.mdx (via @objectstack/lint)
  • content/docs/permissions/authorization.mdx (via @objectstack/lint)
  • content/docs/releases/v17.mdx (via @objectstack/lint)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint: translation-target-unknown misses a view container's DEFAULT form.sections — correctly translating a rendered heading is reported as a stale key

2 participants