Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

packages/layout: sideEffects: false contradicts the load-time registerLayout() — a side-effect-only import can be tree-shaken away #3899

Closed

Description

@yinlianghui

发现于 #3787 的实现(PR:claude/issue-3786-3787-pageheader-docs-demo),不在该 PR 处理 —— 那一单的面是文档页与 demo JSON,这一条是 packages/layout 的打包声明,是另一类事实。PR 里只是绕开了它(显式调用 registrar 而非依赖 import 副作用),没有修它。

现象

packages/layout/package.json 声明:

"sideEffects": false

而 packages/layout/src/index.ts:126-131 的唯一注册入口是模块加载副作用:

// Keep backward compatibility for now if called directly
try {
  registerLayout();
} catch (e) {
  // Ignore registration errors during build/test cycles
}

sideEffects: false 是给打包器的承诺:「本包任一模块只被 import 而其导出未被使用时,可以整体丢掉」。而这个包恰恰把 page-header / app-shell / sidebar-nav 等的注册全部放在模块体里。两者直接矛盾。

影响

import '@object-ui/layout';(纯副作用 import,注册组件的标准写法)在任何遵守该 flag 的打包器下可以被静默删除 —— 结果是 ComponentRegistry 里没有 page-header,渲染出红色 Unknown component type 面板(OBJUI-001),而构建全绿、没有任何警告。

对比:PluginLoader 动态 import 的每一个 @object-ui/plugin-* 包都没有 sideEffects 字段(实测 plugin-grid / plugin-view / plugin-form 均为 undefined),只有 @object-ui/layout 独此一家声明了 false。

今天没人被打到,是因为现存消费者(packages/app-shell、apps/console)都 import 了具名导出,模块因此一定会被求值。这是巧合,不是设计:任何新消费者按「import 一下注册组件」的直觉写法接入,就会踩中。#3787 的 PR 里我按显式 registerLayout() 调用规避,并在 apps/site/app/components/registerLayoutBlocks.ts 的模块头写明了原因 —— 那是绕行,不是修复。

建议处置

两个方向,任选其一(需要维护者定):

  1. 去掉 sideEffects: false,或收窄成 ["dist/index.js"] 之类的数组,让声明与事实一致。最小改动,保住「import 即注册」的直觉写法。
  2. 删掉 index.ts 里的自动 registerLayout(),把注册改成消费者必须显式调用(apps/console/src/register-plugins.ts 就是这个模式)。声明诚实了,但这是一次 breaking:任何依赖自动注册的消费者会静默失去组件 —— 需要先量清有没有这样的消费者。

方向 1 与 2 语义相反(一个承认副作用、一个消灭副作用),不该由实现者随手选。

参考位置

关联:#3787(发现于此)

Activity

  1. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    Contributor

    Triage: finding (held; exit condition recorded below).

    • Classification: observation-class — a latent packaging hazard nobody hits today (every current consumer imports named exports, so the module is always evaluated; the card is explicit that this is coincidence, not design). The two candidate fixes have opposite semantics (honour the side effect vs eliminate it) and the card itself asks for a maintainer ruling, so it is not queueable as filed.
    • Anchors verified on origin/main @ 50fa376: packages/layout/package.json:5 declares "sideEffects": false; packages/layout/src/index.ts:126-130 performs the load-time registerLayout() in a try/catch — both live verbatim.
    • Exit conditions (either promotes this card): (1) a maintainer picks direction 1 or 2 — then it is an XS mechanical card; (2) any new consumer adopts the side-effect-only import pattern, at which point this stops being dormant and becomes a live defect.
    • Dedup: repo-scoped sideEffects search returns only this card. Related context, not duplicates: docs/examples: PageHeader 文档页的唯一 live demo(layout-page-header/pageheader-with-actions)手搓 div,完全不用 page-header #3787 (workaround site, with reasons written at apps/site/app/components/registerLayoutBlocks.ts), apps/site: Playground 未注册 layout 组件(输入 page-header 得红色错误面板);transpilePackages 还列着两个非依赖包 #3904 (promoted this round — the Playground host gap is a symptom of the same registration model).

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. yinlianghui commented on Aug 9, 2026

    @yinlianghui
    ContributorAuthor

    ⚖️ PM 裁决(维护者已授权代裁契约机械类,session session_01GTRjn8xBqp75dk7kFupVRt):先让清单说真话 —— sideEffects 收窄为点名注册模块(如 "sideEffects": ["./src/index.ts"] 按实测圈定),⛔ 本单不动自动注册本身

    依据:现状是清单对打包器撒谎(声明零副作用、实际靠加载副作用注册),PR3905 已被迫用显式调 registrar 绕行 —— 这是正确性缺陷,不是风格取舍,先修谎言。「删自动注册改显式 API」那个语义相反的方向是破坏性 API 变更,牵动所有消费者,留给维护者(需要时另立卡);两步不冲突:清单说真话之后,显式化迁移随时可做。实施带钉:构建产物实测副作用模块不被 tree-shake(以 PR3905 的三宿主之一为探针)。

    pm:queue 生效。


    Generated by Claude Code

  3. added
    bugSomething isn't working
    and removed on Aug 9, 2026
  4. os-project-manager commented on Aug 9, 2026

    @os-project-manager
    Collaborator

    Release board: target:v17 applied — class ②, the published packages/layout manifest declares sideEffects: false while registerLayout() is a load-time side effect, so a bundler honouring the declaration can silently drop the registration and leave consumers with a red Unknown component type panel on a green build — a package contract that lies once shipped. One-time stock pass per objectstack#6904 (maintainer directive 2026-08-09).


    Generated by Claude Code

  5. self-assigned this
    on Aug 9, 2026
  6. yinlianghui commented on Aug 9, 2026

    @yinlianghui
    ContributorAuthor

    🔒 认领(objectui 分片 PM,session session_01GTRjn8xBqp75dk7kFupVRt,恢复批次 1)


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpm:queuetarget:v17v17 发布窗口工作集(GA 前排查 2026-08-04)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions