Skip to content

fix(runner): 把 vite 别名表补成工作区 import 的完整传递闭包 - #3599

Merged
yinlianghui merged 2 commits into
mainfrom
claude/issue-3575-runner-alias-closure
Aug 7, 2026
Merged

fix(runner): 把 vite 别名表补成工作区 import 的完整传递闭包#3599
yinlianghui merged 2 commits into
mainfrom
claude/issue-3575-runner-alias-closure

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #3575

问题

packages/runner/vite.config.ts 只把 7 个 @object-ui/* specifier 别名到 packages/*/src,但这些包的 src 自己又 import 了另外一批工作区包。没进别名表的 specifier 退回 Node 解析、落到 packages/PKG/dist —— 而 dist 只在构建之后存在。于是在只跑过 pnpm install 的干净 checkout 里,content/docs/utilities/runner.mdx 的 "From Source"(install → dev,没有 build 步骤)必然失败。

按 PM 分诊裁定走方向 1:别名表就是本仓已选定的「runner 在 monorepo 内不依赖 dist 即跑」机制,表不完整是该机制自身不变量的破缺。不动发布依赖面(方向 2),不让文档为机制的洞背锅(方向 3)。

闭包计算(不是只补第一层)

packages/runner/src 出发,穿过每个被别名包的 src,迭代到不动点。扫描前先剥掉注释 —— 本仓源码里大量在文档注释中引用 @object-ui/...,不剥注释会把 app-shell / plugin-grid / plugin-map 等误判进来(实测三处均为注释)。

闭包共 14 个包,其中 6 个已在表内(components / core / react / types / plugin-kanban / plugin-charts),新增 8 个:

新增包 首次被谁拉进来
@object-ui/i18n react/src/index.tscomponents/src/lib/close-label.tsx 1
@object-ui/sdui-parser components/src/renderers/layout/page.tsx 1
@object-ui/react-runtime components/src/renderers/layout/react-page.tsx 1
@object-ui/fields plugin-kanban/src/ObjectKanban.tsx 1
@object-ui/plugin-detail plugin-kanban/src/ObjectKanban.tsx 1
@object-ui/providers fields/src/widgets/FileField.tsxImageField.tsx 2
@object-ui/permissions plugin-detail/src/DetailView.tsx 等 6 个文件 2
@object-ui/data-objectstack react/src/context/AppShellContext.tsx 类型级
  • 第 2 层(providers / permissions)只有在第 1 层解析到 src 之后才可达 —— issue 里列的 5 个是第一层,只补它们会把洞往下挪一层。
  • data-objectstackimport type,esbuild 会抹除,所以依赖扫描从来不报它;它按不变量属于闭包,提前别名,免得哪天该 import 变成值导入时 dev server 才炸。
  • 无深路径子导入(@object-ui/x/y),无工作区包的真实 CSS @import —— 三处疑似命中经核实全在注释里。

表旁留了可复跑的闭包重导命令

一条 comm -13 单行命令,拿别名表自身当输入,再跑一遍即可确认是否还有缺口。已实测:

  • 当前表下输出为空(到达不动点);
  • 人为把 fields / i18n 两条从表里去掉,能准确报出这两个缺口。

注释里逐条写明三个过滤器各自压掉的真实误报(@import 文档注释、JSDoc 里的 import() 示例、以及指向已不存在目录的表项)。

验证(方向在跑之前先定)

干净 worktree,只 pnpm install, build,自选空闲端口 --strictPort

BEFORE(预测红,实测红)

(!) Failed to run dependency scan. Skipping dependency pre-bundling. Error: The following dependencies are imported but could not be resolved:
  @object-ui/i18n (imported by .../packages/components/src/lib/close-label.tsx)
  @object-ui/fields (imported by .../packages/plugin-kanban/src/ObjectKanban.tsx)
  @object-ui/plugin-detail (imported by .../packages/plugin-kanban/src/ObjectKanban.tsx)
  @object-ui/sdui-parser (imported by .../packages/components/src/renderers/layout/page.tsx)
  @object-ui/react-runtime (imported by .../packages/components/src/renderers/layout/react-page.tsx)

issue 里那条 @fs 探针连同另外 5 个文件全部 500:Failed to resolve import "@object-ui/i18n" from "../components/src/renderers/basic/elements.tsx"

顺带测到:pnpm --filter @object-ui/runner build 在同样状态下也是失败的 —— 这个洞不止影响 dev:

Error: [vite]: Rolldown failed to resolve import "@object-ui/i18n" from ".../packages/react/src/index.ts".

AFTER(预测绿,实测绿)

依赖扫描零 unresolved(日志里 could not be resolved / Failed to run dependency scan 出现 0 次)。没有停在点探针上 —— 直接从 /src/main.tsx 沿 vite 改写后的 import URL 爬了整张模块图:

crawled 2381 modules from /src/main.tsx
workspace packages served FROM SRC (13): components, core, fields, i18n, permissions,
  plugin-charts, plugin-detail, plugin-kanban, providers, react, react-runtime, sdui-parser, types
served from dist / node_modules/@object-ui (0): (none)

NON-200 responses: 0

13 而非 14,差的正是类型级的 data-objectstack(被 esbuild 抹除,本就不进模块图)—— 与分析自洽。issue 原探针 elements.tsx 500 → 200

@object-ui/react@object-ui/react-runtime 的前缀,但 rollup alias 要求匹配 pattern + "/",故未被吞掉 —— react-page.tsxreact-runtime/src/index.ts 均 200,已实测钉住。

未跑无头浏览器:容器内没装 playwright 浏览器(~/.cache/ms-playwright 不存在),所以没有白屏/渲染截图这一层,只有 HTTP 模块图这一层。

构建路径的影响 + changeset 决策

别名表没有command 分档,因此同样作用于 vite build。这是刻意的,与 apps/console 一致(其注释写明:插件包解析到 src 才能保证 ComponentRegistry 单例,从各自 dist 引入则不保证,否则会出 "Unknown component type")。

把依赖都构建好(等价 CI 状态)后对比:

BEFORE AFTER(仅补别名) FINAL(+ modulePreload: false)
build
asset 数 10 1776 1776
index.html 546 B 145595 B 542 B
rel="modulepreload" 条数 1 1765 0
首屏急切载荷 4231003 B 591795 B(-86%)
dist 总大小 5064294 B 5670299 B 5343175 B(+5.5%)

@object-ui/fields 等从 src 打包后,components/src/lib/lazy-icon.tsxlucide-react/dynamic.mjs 产生的逐图标 chunk 不再被内联,产物变成约 1761 个 2KB 以下的图标微 chunk。这与 console 的形状一致 —— console 的 build 配置注释里就写着 "1700+ icon chunks"。

但 vite 默认 modulePreload: true 会给每个 chunk 发一条 preload,首屏就把图标全拉下来,懒加载反成劣化。console 正是为此关掉它,runner 同样需要,故本 PR 一并关掉;这是本次改动自身引入的回归,不是顺手修的无关问题。关掉后首屏急切载荷从 4.2 MB 降到 0.59 MB。

产物可见变化(runner 已发布,filesdist),故补了 patch changeset —— 与派单里「若影响发布产物解析则需 patch changeset」的判据一致。

其它

  • pnpm --filter @object-ui/runner type-check / lint / test:全绿(9 tests passed;lint 23 条既有 warning、0 error)。node scripts/check-control-bytes.mjs OK,并对改动文件另做了控制字符自扫。
  • 注意 vite.config.ts 不在 runner 的 tsc program 里(include: ["src"]),所以 type-check 覆盖不到本文件;真正的验收证据是上面的 dev/build 实测。
  • 表里 @object-ui/data-objectql 指向的 packages/data-objectql/ 目录并不存在,且该 specifier 全仓仅此一处引用。属同一张表另一个方向的破缺,是独立缺陷,本 PR 未动,已按 Prime Directive [WIP] Enhance every detail of the designer #10 另开 packages/runner/vite.config.ts 的 @object-ui/data-objectql 别名指向不存在的包目录(全仓唯一引用) #3593(observation-class,休眠:无人 import,故今天不影响任何人)。本 PR 的重导命令用 2>/dev/null 容忍它并在注释里点名了该单。

Generated by Claude Code

claude added 2 commits August 7, 2026 14:59
`packages/runner/vite.config.ts` 只别名了 7 个 `@object-ui/*`,但这些包的
`src` 自己又 import 了另外 8 个工作区包。没被别名的 specifier 退回 Node 解析、
落到 `packages/<pkg>/dist` —— 而 `dist` 只在构建之后存在,所以在只跑过
`pnpm install` 的干净 checkout 里,vite 报 "Failed to run dependency scan",
整条 import 链上的模块全部 500。这让 `content/docs/utilities/runner.mdx` 的
"From Source"(install → dev,没有 build 步骤)按文档操作必然失败。

别名表就是本仓选定的「runner 在 monorepo 内不依赖 dist 即跑」机制,表不完整
是该机制自身不变量的破缺。这里按传递闭包一次补齐,不是只补第一层:

  第一层(#3575 已列):i18n / sdui-parser / react-runtime / fields / plugin-detail
  第二层(补齐第一层后才可达):providers(<- fields)、permissions(<- plugin-detail)
  类型级:data-objectstack(<- react/src/context/AppShellContext.tsx,`import type`,
          被 esbuild 抹除,所以依赖扫描从来不报它,但它在闭包里)

表旁留了一行可复跑的闭包重导命令(已验证:当前表下输出为空即到达不动点;
人为拿掉两条时能准确报出缺口),并写明三个过滤器各自压掉的真实误报。

Fixes #3575

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
别名表没有按 `command` 分档,所以同样作用于 `vite build`(这是刻意的,与
apps/console 一致:插件包解析到 src 才能保证 ComponentRegistry 单例,
从各自 dist 引入则不保证)。补全闭包后 `@object-ui/fields` 等从 src 打包,
`components/src/lib/lazy-icon.tsx` 经 `lucide-react/dynamic.mjs` 产生的
逐图标 chunk 不再被内联 —— 产物从 10 个 asset 变成 1776 个,其中约 1761 个
是 2KB 以下的图标微 chunk。这与 apps/console 的形状一致(其 build 配置注释
里就写着 "1700+ icon chunks")。

问题在于 vite 默认 `modulePreload: true` 会给每个 chunk 发一条 preload:
实测 index.html 从 546 B 涨到 145 KB、含 1765 条 `rel="modulepreload"`,
首屏就把所有图标 chunk 拉下来,懒加载反成劣化。console 正是为此关掉它,
runner 同样需要。

关掉之后实测:
  index.html      546 B  ->  542 B(1 条 preload -> 0 条)
  首屏急切载荷  4231003 B -> 591795 B(-86%)
  dist 总大小   5064294 B -> 5343175 B(+5.5%,原先内联的图标变成独立文件)

产物可见变化,故补 patch changeset(runner 已发布,files 含 dist)。

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 3:15pm

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-CVz6ARkO.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.05KB 1.53KB
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) 112.03KB 28.88KB
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.54KB 1.30KB
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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

packages/runner 按 runner.mdx 的「From Source」步骤起不来:vite 别名表漏了 5 个源码实际 import 的工作区包

2 participants