Skip to content

feat(scripts): en 文案改动必须由九个译文包同批跟改的门禁 (#3650) - #3659

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-3650-en-drift-gate
Aug 7, 2026
Merged

feat(scripts): en 文案改动必须由九个译文包同批跟改的门禁 (#3650)#3659
yinlianghui merged 1 commit into
mainfrom
claude/issue-3650-en-drift-gate

Conversation

@yinlianghui

@yinlianghui yinlianghui commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Fixes #3650

新增 scripts/check-i18n-en-drift.mjs:以 git merge-base HEAD origin/main 为基线比对十个语言包的,en 某 key 的值变了而其余九包该 key 的值没变即失败并逐 key 点名。挂账文件 scripts/i18n-en-drift-baseline.json 落地为空。

未碰语言包本体、scripts/check-doc-links.mjs 及其测试(在途 #3622)、既有守卫的任何行为(只加了两处头注互认注释)。

为什么是「事件」而不是「状态」

#3625 才是定设计的那一案。八个包存的是地道译文(ja 是日文、ru 是西里尔、ar 是阿拉伯文),互不相同、文字系统正确、键集合完整、占位符一致 —— 关于那个值本身的每一条机械判据都是绿的,而且会一直绿。没有门禁能判断一句日文是否还等于英文现在的意思,那是在要一个译者。

能判的是事件:这个 PR 把 en 的这个 key 改了,九个译文没跟。这条不变量只在en 的那个 PR 里可判 —— 那时作者还知道这句话该说什么;晚一天就是考古。

需要斟酌的三点(实测后裁,取舍如下)

1. en 纯排版不豁免

SaveSave. 与整句重写同等对待。理由按顺序:

  1. 「这次改动没改变含义」没有诚实的机械判据。标点白名单是一条披着正则外衣的语义主张,而 view.readonlyTooltip 在 8 个语言包里译的是「duplicate to customize」这句已废弃语义 —— 因为它是真译文,任何「值是英文」判据都看不见它 #3625 正是这类主张的代价样本。
  2. 挂账机制本身就是豁免通道,而且它把这个主张变成人签字而不是脚本猜 —— 一条纯排版改动只需一条挂账。

先量后裁:把门禁回放到 en.ts 进入本仓以来的全部 21 个 commit,只有 1 个 commit 改过 en 的值(0e50440e8,3 个 key),它的 27 个包/key 对全部跟改了 —— 绿,且正确。也就是说在这段历史上,「不豁免」的可观测代价是 0 个 commit:根本没有纯排版的抖动需要被豁免。

2. 粒度:key 级

不比语义、不按命名空间、不按文件。两条推论都是有意的:

  • en 新增/删除 key 不归本门禁,那是键集合的事,all-locales-key-parity.test.ts 双向都会红。在这里再报一遍等于一个缺陷两处报、两套说辞。
  • 某包没有这个 key,该包该 key 跳过(计数并打印)。四个 console.ai.* 出站消息 key 就是有意只存在于 en/zh;要求另外八包「跟改」一个它们不该定义的 key,就是本门禁与管键集合的那道门禁直接打架

3. 与 all-locales-key-parity 的分工写进两者头注(互相指认)

三道门禁各自的盲区已写进 check-i18n-call-site-keys.mjsall-locales-key-parity.test.ts 的头注:

门禁 判什么 看不见什么
all-locales-key-parity.test.ts 包 vs 包键集合 + 占位符形状 值说了什么
check-i18n-call-site-keys.mjs(#3530) 调用点 key 在 en 里存不存在 同上
check-i18n-en-drift.mjs(本单) en 变了,九包跟没跟 值是否译对(没有门禁能判)

parity 头注里同时写明:它在 #3582/#3625 上全绿是正确的,不该试图让它变红 —— 那是在要一个键集合测试去判含义。

落地即绿 —— 为什么不是空绿(正文第 5 点的诚实答复)

正文第 5 点要求「先全量扫一遍确认没有第三案存量」。这条对本门禁不适用,原因要写清楚:

本门禁判的是 diff 里的事件,不是树里的状态。它没有「存量」可扫 —— 「今天 ja 那句话是否还等于今天 en 那句话」正是那个没有门禁能回答的语义问题,而假装能回答正是 #3625 得以全绿的原因。所以挂账文件落地为空,不是「债还没算」,是这道门禁在构造上就没有债可记

能做的那部分我做了:把门禁回放到历史上

21 commits replayed (en.ts 进入本仓以来的全部改动)
0 red, 21 green
typography-only findings: 0

为什么全绿 —— 这是本 PR 最该被读到的一条实测:两案的肇事 commit 根本不在本仓历史里。十个语言包是在 30ac2e1ee 一次性整体进仓的(11 files, +30270),而就在那个 commit 上:

en @30ac2e1ee  readonlyTooltip: 'System view — defined in code, read-only.'
zh @30ac2e1ee  readonlyTooltip: '系统视图 — 由代码定义,只读。'
ja @30ac2e1ee  readonlyTooltip: "システムビュー — 複製してカスタマイズしてください。"
de @30ac2e1ee  readonlyTooltip: "Systemansicht — zum Anpassen duplizieren."

en @30ac2e1ee  systemViewReadonly: 'System view defined in code — read-only.'
ja @30ac2e1ee  systemViewReadonly: 'System view defined in code - duplicate to customize.'

两案都是带着漂移进的仓 —— 改 en 的那次动作发生在这些文件被搬进来之前。这恰恰是这一族缺陷只能靠人肉逐 key 读邻行才被发现的原因,也说明本门禁不可能被历史验证

因此验收语料是植入式的,不是观察来的:scripts/__tests__/check-i18n-en-drift.test.ts 在一次性 git 仓里重建 #3625 那个本应存在的 commit(真实的十句译文、真实的八个包),断言门禁点名 view.readonlyTooltip 与全部八个包。这一条,加上下面的退出码验证,才是「落地即绿是一次测量而不是空比较」的证据。

挂账机制:为什么它不会退化成 allowlist

条目形如:

"view.readonlyTooltip": {
  "en": "System view — defined in code, read-only.",
  "reason": "",
  "issue": "objectui#1234"
}

逐字转写新 en 文案是整个机制,不是手续:

  • 只有已经做出那次改动的人写得出来(得把新句子抄进挂账,评审在 reason 旁边就读到它);
  • 它只豁免那一句;
  • 门禁在每次运行都拿这段文字与 en 现值核对(与 diff 无关),所以该 key 下次再改时条目自动失配 → 红,直到有人删掉或按新句子续签。挂账自己会过期,不会沉淀成 allowlist。

有意不做的一条:「本次没命中任何 finding 的挂账」不算错。挂账 PR 合入后,之后每一次运行看到的都是空 diff、都是这个形状;若判红,等于把一个 PR 的逃生口变成后面所有 PR 的红灯。数量方向改由测试里的 WAIVER_CEILING 钉住 —— 加一条挂账就得同时抬那个数字,评审看得见。

逆向验证(先写预测,后跑;六条)

预测在跑之前就写死在这里,其中第 6 条预测的是「不红」,如实记

# 植入的形状 预测 实测
a en 改值,九包不动 ,点名 key + 九个包 ✅ exit=1,view.readonlyTooltip / unchanged in: zh, ja, ko, de, fr, es, pt, ru, ar
b 同一 key 十包同批改 绿 ✅ exit=0,9 pack value(s) followed
c a + 挂账逐字转写新 en 文案 绿,挂账 diff 可见 ✅ exit=0,1 finding(s) waived by the ledger
d1 挂账指向 en 里不存在的 key stale-waiver ✅ exit=1,view.noSuchKey: this key no longer exists in the en pack
d2 挂账转写差一个词 stale-waiver,且不豁免原 finding ✅ exit=1,两条同时报
e c 之后 en 再改一次 ,上个 PR 的挂账失效 ✅ exit=1,the en text this waiver covers is gone. Ledger says "…read-only.", en now says "…. Read-only."

(a) 的真实输出:

1 en string(s) changed without their translations:

  view.readonlyTooltip
      en was:  "System view — duplicate to customize."
      en now:  "System view — defined in code, read-only."
      unchanged in: zh, ja, ko, de, fr, es, pt, ru, ar

第 6 条:棘轮 —— 派发口径那一句需要拆成三种形状

派发口径写的是「临时增一条无对应 en 变更的挂账 → 棘轮测试红」。结论成立,但抓住它的机制随形状而不同,照单一句写会把读者引偏,所以拆开如实记:

形状 脚本 棘轮测试
d1 挂账的 key 不在 en (stale-waiver)
d2 挂账转写与 en 现值不符 (stale-waiver)
d3 挂账 key 真实、转写等于 en 现值、本次 diff 无漂移 绿 —— 这是有意的 (且只有它红)

d3 是惰性挂账:脚本判绿是设计,不是漏 —— 挂账 PR 合入后每一次运行都是这个形状。而它永远不可能豁免未来的改动:该 key 下次一改,转写就与 en 现值不符 → stale-waiver → 红(即第 e 条)。在静态时抓住它的是测试里的数量天花板,不是脚本。

实测(把一条 d3 形状的挂账临时塞进真挂账文件):

# 脚本(预测绿)
node scripts/check-i18n-en-drift.mjs
  → No en value changed in this range.   exit=0

# 棘轮测试(预测红)
× the shipped ledger : holds at most 0 waiver(s) — a ratchet, and this number only goes down
  AssertionError: expected [ 'wizard.missingRequired' ] to have a length of +0 but got 1
  Tests  1 failed | 31 passed (32)

只有那一条红,方向与预测一致。植入项已还原(waivers: {})。

实现取舍

解析方式:TypeScript AST(不是 import,也不是正则)

「改动前」那一侧是 git 里的 blob,不是磁盘上的文件,十个包又是 1.5 MB TypeScript。用 git show REF:PATH 拿到文本,TS AST 转成 keystring 的映射:不需要构建、不需要 loader、不需要临时文件 —— 与 check-i18n-call-site-keys.mjscollectEnKeys 同一招、同一理由。正则在多行字符串/转义/嵌套花括号上不可靠,不考虑。

解析器读不懂的形态一律 throw,不跳过 —— 悄悄丢掉一棵子树会让门禁在它不再读的那些 key 上恰好全绿。这条第一次运行就兑现了:它当场炸在 objectActions.resetPackageSetConfirm,那句 en 是 '…' + '…' 跨行拼接(全仓仅此一处)。i18next 服务的是拼接后的字符串,所以比较的也必须是拼接后的字符串;已加折叠并钉了测试。(隔壁 collectEnKeys 把这个节点当作不透明叶子,对它是对的 —— 它只要 key。)

CI 挂接:ci.yml 的 type-check 作业(与 #3547 同款)

#3547check:i18n-keys 就挂在这个作业里,本门禁紧挨其后 —— 复用同一次 install(它 import typescript),不需要构建。

⚠️ 两处失明风险已实测排除:

  1. checkout 深度。本仓没有既有的 diff-based 门禁可循(changeset-guard / control-bytes / check-doc-links 全是在 HEAD 上全树扫描),所以按派发口径「没有先例就 fetch」办:给该作业的 checkout 加 fetch-depth: 0。actions/checkout 默认深度 1,git merge-base 在浅克隆里什么也找不到 —— 而门禁把「基线解析不出来」判为硬失败而不是跳过(diff 门禁找不到 diff 却报绿,正是这一族门禁存在的理由),所以这一行掉了是响亮的红,不是静悄悄的过。测试钉住了这一行。
  2. paths 过滤ci.ymlpaths-ignore**/*.mdcontent/**docs/**apps/site/**.changeset/** —— 没有一条能匹配 packages/i18n/src/locales/*.ts 或挂账文件,所以改 en 文案的 PR 一定会启动这个 workflow。测试把这条也钉住了:任何新增的 paths-ignore 条目只要触及 packages / scripts / locales 就红。

基线解析顺序

--baseOS_I18N_DRIFT_BASEmerge-base with origin/$GITHUB_BASE_REForigin/mainmain。用 merge-base 而不是 origin/main 顶端:一个分支只该为它自己改的 en 负责,不该为它分叉之后落到 main 上的改动负责。

--root / --head / --min-keys 三个开关都有注释说明用途:--root 指向别的 checkout(worktree,或测试建的一次性仓),--min-keys 是「防空比较」下限(默认 2000,CI 从不传;测试里有一条故意不传、驱动 CLI 去证明这道下限还在响)。

测试

新文件 scripts/__tests__/check-i18n-en-drift.test.ts,32 条。

pnpm exec vitest run --maxWorkers=2 scripts/__tests__/ packages/i18n/src/__tests__/all-locales-key-parity.test.ts
  Test Files  18 passed (18)
       Tests  345 passed (345)

pnpm type-check:scripts                → tsc -p tsconfig.scripts.json  exit 0(无输出)
node scripts/check-i18n-en-drift.mjs   → No en value changed in this range.  exit 0
node scripts/check-i18n-call-site-keys.mjs
  → Every in-scope call-site key resolves against the en pack (2736 keys).
node scripts/check-control-bytes.mjs   → OK (3661 tracked text files; 85 binary)
node scripts/check-changeset-no-major.mjs / check-changeset-fixed.mjs  → 均 ✅
eslint(4 个改动/新增文件)             → 0 errors 0 warnings
控制字符自查 grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]'(7 个文件,超出门禁扫描面)→ 无匹配

18 个文件里包含 ci-cd-pipeline-doc.test.ts(证明新增 CI 步骤没有破坏那页的钉子)、check-i18n-call-site-keys.test.ts(头注改动安全)、scripts-type-check.test.ts

测试自身的两处防空绿:

  • 解析器保真测试比对的是 vitest 真正求值的 en 模块,而且逐值比对而不只是逐键 —— 只比键的话,一个把文本读错的解析器会去比两个错字符串,漂移可以照样漏掉。
  • 下限测试故意不传 --min-keys 跑一次 CLI,让「包小得不可能是真包」这条守卫真的被执行过,而不是一行没人碰的代码。

无 changeset

纯 CI 门禁改动,不改任何 npm 包的运行时行为、非用户可见特性 —— 与 PR #3589(check-doc-links 扫描面扩张)同款判断。未触碰 content/docs/releases/

顺带发现,未在本 PR 修改(越界)

#3653(observation-class,finding 标签,未指派):content/docs/guide/ci-cd-pipeline.md:69type-check 作业行漏了 pnpm check:i18n-keys(#3547 落的那一步),而 ci-cd-pipeline-doc.test.ts 的判定粒度是「作业」不是「步骤」 —— 它双向钉住 job 表与每个 workflow 的小节,但没有任何一条读「一个作业里跑了哪些步骤」,所以往既有作业里加 run: 步骤而不改文档,全套钉子照绿。本 PR 会让这一栏再少报一步(check:i18n-drift),有意未改 —— 派发口径的文件面不含 content/docs/,且要把这行改对必须连带补上 #3547 欠的那一步(不是本单的账)。修 #3653 时两步一起补即可。

范围

  • 新增:scripts/check-i18n-en-drift.mjsscripts/i18n-en-drift-baseline.json(空)、scripts/__tests__/check-i18n-en-drift.test.ts
  • :.github/workflows/ci.yml(fetch-depth: 0 + 一个步骤)、package.json(一条 script)、check-i18n-call-site-keys.mjsall-locales-key-parity.test.ts头注分工互认(纯注释)
  • 未改:任何语言包、scripts/check-doc-links.mjs 及其测试、既有守卫的任何行为、content/docs/content/docs/releases/

🤖 Generated with Claude Code

https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt

新增 scripts/check-i18n-en-drift.mjs:以 merge-base 为基线比对十个语言包的
值,en 某 key 的值变了而其余九包该 key 的值没变即失败并逐 key 点名。这是
#3582/#3625 一族缺陷缺的那道不变量 —— 现有三道门禁全部只读键集合,对「值
的语义漂移」天生失明,而 #3625 证明连「值是英文」「非拉丁包里有 ASCII」这
类判据也看不见它(八包存的是地道译文,只是译了一句 en 已废弃的话)。

挂账文件 scripts/i18n-en-drift-baseline.json 落地为空,条目须逐字转写新的
en 文案 —— 因此只能由已经做出该改动的人写,只豁免那一句,且下次该 key 再
变时自动过期。数量方向由测试里的 WAIVER_CEILING 钉住。

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

vercel Bot commented Aug 7, 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)
objectui Ignored Ignored Aug 7, 2026 5:36pm

Request Review

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.1 KB 350 KB
Entry file index-DchYFue2.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.66KB 3.13KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 480.72KB 105.64KB
core (index.js) 2.96KB 1.13KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 137.51KB 35.11KB
fields (index.js) 230.87KB 56.83KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.65KB 1.06KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 9.48KB 3.27KB
i18n (useObjectLabel.js) 26.14KB 6.07KB
i18n (useSafeTranslation.js) 4.52KB 1.96KB
layout (index.js) 38.53KB 10.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 44.98KB 12.37KB
plugin-charts (index.js) 61.04KB 17.31KB
plugin-chatbot (index.js) 180.09KB 42.72KB
plugin-dashboard (index.js) 115.50KB 29.96KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 232.79KB 57.42KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 112.10KB 27.10KB
plugin-gantt (index.js) 162.55KB 39.57KB
plugin-grid (index.js) 186.61KB 49.34KB
plugin-kanban (index.js) 48.30KB 13.28KB
plugin-list (index.js) 105.12KB 25.48KB
plugin-map (index.js) 16.81KB 5.24KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.58KB 10.58KB
plugin-timeline (index.js) 25.76KB 7.33KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.03KB 20.55KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 19.28KB 6.38KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 2.71KB 1.34KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

i18n 门禁:en 文案变更时其余九包必须同批跟改(或显式挂账)——#3582/#3625 族缺陷缺的那道不变量

2 participants