test(scripts): 仓级 sideEffects 声明与加载期副作用一致性门 (#3943) - #4907
Merged
Conversation
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Collaborator
Author
|
PM 验收:ACCEPT(#3943,批次 16,PM 会话 实物核验(已过):diff 与报告逐字对账(+1234/−382);标识 0;releases 0。两处声明外文件的编辑抽查为实: 门的设计
有据扩界(接受):扫描面从字面「barrel」放宽到「barrel + 相对导入可达闭包」—— 反向验证:四变异,M1/M3/M4 预判逐字命中;M2 预判 3 红实测 4 红 —— 多出的红是防空集 floor 按设计触发,dev 按「欠预判」如实报告而非追认,纪律正确。中途自纠 附带产出:新 finding #4902(scripts-type-check 把注释当导入);#4540 追加现状而非立孪生卡。 CI(亲读终态):20 项全 completed,18 success + 2 skipped,零失败。 → undraft + auto-merge (SQUASH)。 Generated by Claude Code |
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 #3943
按 2026-08-17 晋级评论的 scope 实施:在
scripts/__tests__/落一条派生式仓级门,形状照同目录package-files-exist.test.ts先例。前提验证(先行,
origin/main@97da1b0d6)卡面的两条前提都复核为成立:
components/fields为true(保守claim,不在门的判定面内),core/i18n/react-runtime/sdui-parser/types为false,layout为数组(PR fix(layout): declare the load-time registration insideEffects(#3899) #3940 修正后)。false包自 barrel 可达的全部 157 个模块,顶层副作用语句 0 命中;layout 扫出且仅扫出src/index.ts:285的try { registerLayout(); } catch {},即 packages/layout:sideEffects: falsecontradicts the load-timeregisterLayout()— a side-effect-only import can be tree-shaken away #3899 的原始标本。门落地即绿,没有跑出新的活缺陷。逐包判定表
@object-ui/corefalsedist/index.js,src/index.ts@object-ui/i18nfalsedist/index.js,src/index.ts@object-ui/react-runtimefalsedist/index.js,src/index.tsx@object-ui/sdui-parserfalsedist/index.js,src/index.ts@object-ui/typesfalseexports子路径 +dist/index.cjs+src/index.ts)@object-ui/layoutdist/index.js,dist/index.umd.cjs,src/index.tssrc/index.ts确有顶层副作用@object-ui/componentstruetrue是最保守 claim,不会丢注册)@object-ui/fieldstruereact-runtime的index.tsx是卡面点名「别被index.ts的 glob 漏掉」的那一处 —— 门不靠文件名约定猜,而是从 alias 表解析目录到 barrel,.tsx与.ts走同一条代码路径,并有一条断言与一组探针分别钉住。门做什么
两个方向都判,因为声明可以朝两边说谎:
sideEffects: false+ 加载期副作用 = packages/layout:sideEffects: falsecontradicts the load-timeregisterLayout()— a side-effect-only import can be tree-shaken away #3899 那个谎(注册被整块丢掉,0 字节、退出码 0、无警告)。扫描面按可达性派生,不按文件 glob。
sideEffects: false授权打包器丢掉该包任何导出未被用到的模块,不只是 barrel,所以诚实的扫描面是 barrel + 从它经相对 import 可达的全部模块 —— 恰好就是打包器能丢的那一集。这样还免掉了一份测试文件排除名单:src/**glob 会把同目录的*.test.ts一并扫进来(光packages/core/src就有 28 个顶层describe(...),个个是顶层调用、个个不是打包风险),而按文件名排除等于在门上开一个文件名形状的洞。入口形态含 workspace alias 入口。 仓内有三张打包器 alias 表把
@object-ui/*指到包的src(apps/console、examples/console-starter,以及此前未被点名的packages/fields/vite.config.ts),打包器对这些源文件读的是同一份package.json—— PR #3940 实测过只声明dist/*时 console 的 alias 形态照样 0 字节。三张表都是从配置源文本解析的(照vitest-config-alias-targets-3944.test.ts的理由:只要一串字符串,不该把整个 Vite 插件面 import 起来),新增一张表当天即被覆盖。根vitest.config.mts的第四张表故意不读:Vitest 转译并执行模块,不做 tree-shaking,不是打包面。真打包器只用在静态分析答不了的那一问上。 写门之前先实测过「能不能拿打包器当副作用探测器」——把各包真实源码在镜像的
sideEffects: true下打包(强制保留模块,存活下来的就是打包器认定的不纯部分):rollup 的纯度分析是刻意保守的,「打包器留了东西」≠「这个包有对外可见的副作用」——照这个读法,门第一天就会在三个诚实的包上变红。所以静态判据只问那个可判定的窄问题(裸调用 / 经成员表达式的写 / 纯副作用 import),打包器则负责回答「声明到底改不改变产物」,且每条探针都配
sideEffects: false对照。反向验证(各面先书面预判再跑,变异前已 commit,还原一律
git checkout)false包 barrel(packages/core/src/index.ts)注入顶层裸调用registerEverything();1 failed / 27 passed,报@object-ui/core declares "sideEffects": false, but packages/core/src/index.ts:86 is a top-level call: registerEverything();"./src/index.ts"(#3940 实测过的坑)src/index.ts的 marker 正向探针、真源码 specimen 探针false翻成数组形态(对照的对照)falsebarrel 对照),其余不动8 failed / 20 passed,不多不少正是那 8 条try { registerLayout(); } catch {}(反向/空头声明方向)2 failed / 26 passed② 的预判少了一条,如实记下而不是回头改预判: 第 4 条红是
every declared source entry really does have a load-time side effect里的反空绿地板。抠掉./src/index.ts后,数组里就没有任何源入口了,那条空头声明检查会在空集合上走完并报绿 —— 地板正是为此而设,它按设计先炸。这是地板在干活,不是误报。另外 ② 的 specimen 探针给出的现场是
expected '' to contain 'page-header'—— 字面上的 0 字节 bundle,#3899 那个数字在门里被原样复现。layout 包内 pin 的收敛方式与理由
删除并入(
packages/layout/src/__tests__/side-effects-manifest.test.ts→ 仓级门),理由:该文件 6 条断言里 5 条都是可派生的,并入后自动适用于所有包,而不再只护 layout 一个包——declaresLoadTimeRegistrationsideEffects: false对照最后一条是唯一不可泛化的:marker 探针只能证明「每个声明路径上有某个模块存活」,而 layout 的实际载荷是一组注册键,只有打包真源码才能看到那些具体的键存活 —— 这正是 #3787 需要的、OBJUI-001 报告其缺失的那条断言,因此逐字搬运而非折成 marker。仓级门的反空绿断言按名点住
@object-ui/layout且要求它仍是数组形态,所以 layout 掉出扫描面本身就会变红。一处顺带修正:built-vs-unbuilt 分歧(写门途中实测到)
「哪些入口形态是源码」最初按
fs.existsSync判定。跑turbo type-check(dependsOn: ^build)时packages/layout/dist被生成出来,该判据当场在同一棵树上翻面 ——dist/index.js会被算进静态扫描面,而 CI 的全新 clone 里不会。改为按 git index 判定:git ls-files不会因为你跑了pnpm build而变。这与package-files-exist.test.ts在 #4059 学到的是同一课(它的 producibility 检查最初也写在onDisk上,并在它本该拒绝的状态上报了绿),注释里已互相指认。改完在已 built 的树上重跑 ④,仍只报src/index.ts,不报dist/index.js。相邻缺陷(不扩围,已按纪律处理)
finding,未指派):scripts-type-check.test.ts的 workspace-import 检查按文本 grep,注释里的 import 示例会被判成真 import,且失败信息指引「移动 ci.yml 步骤 / 删掉这条 import」——而现场根本没有 import。本 PR 在check-package-self-import.test.ts里踩到过一次(改写措辞绕开,并留注释指向该卡)。packages/plugin-editor/vite.config.ts.timestamp-*.mjs的.gitignore规则已补上(:99),但文件仍被跟踪。本门因此把打包器配置的匹配收严为 basename 正则/^vite\.config\.(ts|mts|cts|js|mjs|cjs)$/,免得把这份 vite 5 时代的 CI 快照当成第 4 张 alias 表读进来。验证
新门本身 28 tests / 2.3s(9 条静态 + 17 条探针 + 2 条 specimen;探针数与派生出的入口形态数逐条对得上,所以没有探针被静默吞掉)。
changeset 照
.changeset/app-shell-docs-nav-examples.md的 test-only 先例,空 frontmatter、不 bump 任何包(未触碰任何包的运行时源码)。Generated by Claude Code