Skip to content

Conversation

@weareoutman
Copy link
Member

@weareoutman weareoutman commented Nov 3, 2025

依赖检查

组件之间的依赖声明,是微服务组件架构下的重要信息,请确保其正确性。

请勾选以下两组选项其中之一:

  • 本次 MR 没有使用上游组件(例如框架、后台组件等)的较新版本提供的特性。

或者:

  • 本次 MR 使用了上游组件(例如框架、后台组件等)的较新版本提供的特性。
  • 在对应的文件中更新了该上游组件的依赖版本(或确认了当前声明的依赖版本已包含本次 MR 使用的新特性)。

提交信息检查

Git 提交信息将决定包的版本发布及自动生成的 CHANGELOG,请检查工作内容与提交信息是否相符,并在以下每组选项中都依次确认。

破坏性变更是针对于下游使用者而言,可以通过本次改动对下游使用者的影响来识别变更类型:

  • 下游使用者不做任何改动,仍可以正常工作时,那么它属于普通变更。
  • 反之,下游使用者不做改动就无法正常工作时,那么它属于破坏性变更。

例如,构件修改了一个属性名,小产品 Storyboard 中需要使用新属性名才能工作,那么它就是破坏性变更。
又例如,构件还没有任何下游使用者,那么它的任何变更都是普通变更。

破坏性变更:

  • ⚠️ 本次 MR 包含破坏性变更的提交,请继续确认以下所有选项:
  • 没有更好的兼容方案,必须做破坏性变更。
  • 使用了 feat 作为提交类型。
  • 标注了 BREAKING CHANGE: 你的变更说明
  • 同时更新了本仓库中所有下游使用者的调用。
  • 同时更新了本仓库中所有下游使用者对该子包的依赖为即将发布的 major 版本。
  • 同时为其它仓库的 Migrating 做好了准备,例如文档或批量改动的方法。
  • 手动验证过破坏性变更在 Migrate 后可以正常工作。
  • 破坏性变更所在的提交没有意外携带其它子包的改动。

新特性:

  • 本次 MR 包含新特性的提交,且该提交不带有破坏性变更,并使用了 feat 作为提交类型。
  • 给新特性添加了单元测试。
  • 手动验证过新特性可以正常工作。

问题修复:

  • 本次 MR 包含问题修复的提交,且该提交不带有新特性或破坏性变更,并使用了 fix 作为提交类型。
  • 给问题修复添加了单元测试。
  • 手动验证过问题修复得到解决。

杂项工作:

即所有对下游使用者无任何影响、且没有必要显示在 CHANGELOG 中的改动,例如修改注释、测试用例、开发文档等:

  • 本次 MR 包含杂项工作的提交,且该提交不带有问题修复、新特性或破坏性变更,并使用了 chore, docs, test 等作为提交类型。

Summary by CodeRabbit

  • 测试

    • 新增插槽冲突检测测试,覆盖顶层与嵌套路由场景,确保同时存在路由与子构件时渲染报错。
  • 改进

    • 支持在构件 children 中混合子构件与路由,强化插槽类型一致性检测防止冲突。
    • 拓展故事板搜寻与监控范围,支持多位置查找与监听,提升本地调试可靠性。
    • 允许通过运行时配置跳过占位符求值,并调整占位符评估标记行为。
  • 新特性

    • 支持全局 CONSTANTS 访问,应用级常量可通过配置提供,未定义键访问将抛错。

@coderabbitai
Copy link

coderabbitai bot commented Nov 3, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

扩展子元素以同时支持砖块与路由配置,渲染器在构建槽位时区分并检测砖块/路由类型冲突;引入应用级常量(CONSTANTS)求值支持并可通过配置跳过占位符评估;新增/调整相关单元测试与若干工具与服务端监听逻辑。

Changes

内聚 / 文件(s) 变更摘要
类型定义与接口更新
packages/types/src/manifest.ts
新增 MicroApp.noPlaceholders?: booleanMicroApp.constants?: Record<string, unknown>;在 BaseRouteConf 中新增可选字段 slot?: string;将 BrickConf.children 类型从 BrickConf[] 改为 (BrickConf | RouteConf)[]
渲染器核心逻辑
packages/runtime/src/internal/Renderer.ts
更新 childrenToSlots 签名为接受 (BrickConf | RouteConf)[],新增 isRouteConf 判别;按子元素决定槽位 type"bricks""routes",检测并在类型冲突时抛错;动态创建并填充对应类型数组(bricks/routes)。
渲染器单元测试
packages/runtime/src/internal/Renderer.spec.ts
将部分测试由 slot-based 改为使用 children 数组表达嵌套路由;新增 “槽位冲突” 测试,断言同一槽位同时存在路由定义与子砖块时抛出错误(示例错误 [Error: Slot "" conflict between bricks and routes.]),覆盖顶层与嵌套场景。
占位符/计算行为调整
packages/runtime/src/internal/compute/computeRealValue.ts
dismissMarkingComputed 初始值设为 false;在跳过评估判断中加入 runtimeContext.app?.noPlaceholders,跳过评估时显式将 dismissMarkingComputed 设为 true,以跳过占位符求值并标记为已处理。
表达式求值扩展
packages/runtime/src/internal/compute/evaluate.ts
新增对全局符号 CONSTANTS 的支持:通过只读代理访问 runtimeContext.app.constants,访问未定义键抛出 ReferenceError,并在 ownKeys 中列出可用键;同时对注入 globals 的 app 写法做小幅精简。
求值单元测试
packages/runtime/src/internal/compute/evaluate.spec.ts
新增 “app constants” 测试:验证访问未定义常量抛错,已定义常量可通过 CONSTANTS.key 读取。
类型标注修正(工具)
packages/utils/src/storyboard/replaceUseChildren.ts
replaceInBrick 中对遍历 brick.children 的分支添加类型断言(强制为 BrickConf[]),为类型兼容性修正,运行时行为未变。
Storyboard 查找逻辑
packages/brick-container/serve/utils/getStoryboards.js
将单路径存在性检查替换为多候选路径搜索(mock-micro-appsapps 下的可能位置),使用 tryFiles 选取首个存在的 storyboard 文件,找不到时返回 null
Live reload watcher 扩展
packages/brick-container/serve/utils/liveReloadServer.js
将 chokidar 的监视 glob 从单一路径扩展为对 ["mock-micro-apps","apps"] 两个目录中的每个 appId 观察对应的 storyboard.yaml,以支持多位置变更监听。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 分钟

需要重点审查的区域:

  • packages/runtime/src/internal/Renderer.tsisRouteConf 判别逻辑、槽位类型一致性检测与错误信息(包含空 slot 名称与重复合并边界情况)。
  • packages/runtime/src/internal/compute/evaluate.tsCONSTANTS 代理实现的安全性、抛错消息与 ownKeys 行为,以及与现有全局注入的兼容性。
  • packages/runtime/src/internal/compute/computeRealValue.tsnoPlaceholders 分支对各类占位符/惰性计算路径的影响与 dismissMarkingComputed 标志一致性。
  • packages/types/src/manifest.tschildren 类型更改及新增字段对整个类型体系和其他模块的连带影响。
  • packages/brick-container/serve/*:多路径查找与 watcher 逻辑是否覆盖预期目录且不会引入重复通知或性能问题。

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning PR 描述仅包含模板,所有检查项都未勾选,缺少必要的上下文信息和工作描述。 请补充"简单描述"部分,说明本次 MR 的具体工作内容,并勾选对应的提交检查项。
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed 标题清晰简洁,准确反映了主要改动——支持路由作为子元素。符合提交规范。
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch steve/v3-route-children

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enables routes to be used as children within brick configurations, allowing for more flexible composition of routes and bricks in the same slot hierarchy.

  • Extended type definitions to allow RouteConf in addition to BrickConf in children arrays
  • Updated childrenToSlots function to distinguish between brick and route children and create appropriate slot configurations
  • Added slot conflict detection to prevent mixing bricks and routes in the same slot

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
packages/types/src/manifest.ts Added slot property to BaseRouteConf and updated BrickConf.children type to accept both BrickConf and RouteConf
packages/runtime/src/internal/Renderer.ts Updated childrenToSlots to handle both brick and route children with type detection and conflict checking; added isRouteConf type guard
packages/runtime/src/internal/Renderer.spec.ts Added test case for slot conflicts and updated existing test to use new children API
packages/utils/src/storyboard/replaceUseChildren.ts Added type assertion for children array to maintain type compatibility

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/runtime/src/internal/Renderer.ts (2)

1320-1334: 建议重构以提高类型安全性

当前实现使用了不当的类型转换来绕过 TypeScript 的类型检查:

(slotConf as SlotConfOfBricks)[type as "bricks"].push(child as BrickConf);

虽然这在运行时能够工作(因为 type 的实际值是 "routes" 或 "bricks"),但这种做法违背了 TypeScript 的最佳实践,存在以下问题:

  1. 绕过了类型系统的保护
  2. 如果将来重构类型定义,可能导致运行时错误
  3. 代码意图不够清晰,难以维护

建议改为显式的条件判断:

-      if (hasOwnProperty(newSlots, slot)) {
-        const slotConf = newSlots[slot];
-        if (slotConf.type !== type) {
-          throw new Error(`Slot "${slot}" conflict between bricks and routes.`);
-        }
-        (slotConf as SlotConfOfBricks)[type as "bricks"].push(
-          child as BrickConf
-        );
-      } else {
-        newSlots[slot] = {
-          type: type as "bricks",
-          [type as "bricks"]: [child as BrickConf],
-        };
-      }
+      if (hasOwnProperty(newSlots, slot)) {
+        const slotConf = newSlots[slot];
+        if (slotConf.type !== type) {
+          throw new Error(`Slot "${slot}" conflict between bricks and routes.`);
+        }
+        if (type === "bricks") {
+          (slotConf as SlotConfOfBricks).bricks.push(child as BrickConf);
+        } else {
+          (slotConf as SlotConfOfRoutes).routes.push(child as RouteConf);
+        }
+      } else {
+        if (type === "bricks") {
+          newSlots[slot] = {
+            type: "bricks",
+            bricks: [child as BrickConf],
+          };
+        } else {
+          newSlots[slot] = {
+            type: "routes",
+            routes: [child as RouteConf],
+          };
+        }
+      }

这样的实现更加清晰、类型安全,并且更容易维护。


1369-1371: 可选优化:使用 in 操作符改进类型守卫

当前的 isRouteConf 函数通过检查属性值来判断类型:

return !!(child as RouteConf).path && !(child as BrickConf).brick;

这种方式在实践中是可行的,但存在边缘情况(如空字符串会被视为 falsy)。可以使用 in 操作符检查属性是否存在,这样更简洁和健壮:

 function isRouteConf(child: BrickConf | RouteConf): child is RouteConf {
-  return !!(child as RouteConf).path && !(child as BrickConf).brick;
+  return "path" in child;
 }

因为 BrickConf 没有 path 属性,只检查 path 的存在性就足够了。这种方式不依赖属性值,更加可靠。

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between d613108 and 04982bb.

📒 Files selected for processing (4)
  • packages/runtime/src/internal/Renderer.spec.ts (2 hunks)
  • packages/runtime/src/internal/Renderer.ts (3 hunks)
  • packages/types/src/manifest.ts (2 hunks)
  • packages/utils/src/storyboard/replaceUseChildren.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Agent
  • GitHub Check: build (20.x)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
packages/utils/src/storyboard/replaceUseChildren.ts (1)

29-29: 类型转换在当前上下文中是安全的

该类型转换将 brick.children 强制转换为 BrickConf[],虽然现在 children 的类型是 (BrickConf | RouteConf)[],但在 useChildren 这个遗留特性的上下文中,routes 不应该出现。该转换由条件 Array.isArray(brick.children) && !slots 保护,应该是安全的。

建议:可以添加注释说明为什么这里只期望 BrickConf,或者添加运行时检查过滤掉可能的 RouteConf。

packages/types/src/manifest.ts (2)

354-355: 新增的 slot 属性支持路由作为子元素

BaseRouteConf 添加可选的 slot 属性是合理的设计,使得路由在作为构件的子元素时可以指定所属的插槽。


527-527: children 类型扩展支持混合配置

BrickConf.children 的类型从 BrickConf[] 扩展为 (BrickConf | RouteConf)[] 是本 PR 的核心变更,使得构件可以包含子路由。这与相关文件中的运行时逻辑变更保持一致。

packages/runtime/src/internal/Renderer.spec.ts (2)

477-477: 测试更新反映新的路由子元素支持

测试从使用 slots 结构改为使用 children 数组直接包含子路由,正确验证了新功能。


531-576: 新增冲突检测测试覆盖重要场景

新增的 "Slot conflict" 测试验证了在同一个插槽中混合使用 bricks 和 routes 时会正确抛出错误。测试覆盖了核心的冲突检测逻辑,确保错误消息符合预期。

packages/runtime/src/internal/Renderer.ts (1)

1299-1299: 函数签名更新支持混合子元素类型

children 参数的类型从 BrickConf[] | undefined 更新为 (BrickConf | RouteConf)[] | undefined,正确反映了新的类型定义。

@codecov
Copy link

codecov bot commented Nov 3, 2025

Codecov Report

❌ Patch coverage is 91.30435% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.19%. Comparing base (d613108) to head (bbf240b).
⚠️ Report is 5 commits behind head on v3.

Files with missing lines Patch % Lines
packages/runtime/src/internal/compute/evaluate.ts 75.00% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##               v3    #4816      +/-   ##
==========================================
- Coverage   95.20%   95.19%   -0.02%     
==========================================
  Files         212      212              
  Lines        9320     9336      +16     
  Branches     1793     1798       +5     
==========================================
+ Hits         8873     8887      +14     
- Misses        328      330       +2     
  Partials      119      119              
Files with missing lines Coverage Δ
packages/runtime/src/internal/Renderer.ts 93.81% <100.00%> (+0.09%) ⬆️
...s/runtime/src/internal/compute/computeRealValue.ts 98.18% <100.00%> (+0.06%) ⬆️
...ackages/utils/src/storyboard/replaceUseChildren.ts 97.87% <100.00%> (ø)
packages/runtime/src/internal/compute/evaluate.ts 97.48% <75.00%> (-0.95%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@cypress
Copy link

cypress bot commented Nov 3, 2025

next-core    Run #11789

Run Properties:  status check passed Passed #11789  •  git commit c715c156a3 ℹ️: Merge bbf240be9491724254d05cc987ac912ce9bf0586 into d61310874c12a1047eff5ac70ea0...
Project next-core
Branch Review steve/v3-route-children
Run status status check passed Passed #11789
Run duration 00m 24s
Commit git commit c715c156a3 ℹ️: Merge bbf240be9491724254d05cc987ac912ce9bf0586 into d61310874c12a1047eff5ac70ea0...
Committer Shenwei Wang
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 17
View all changes introduced in this branch ↗︎

@weareoutman weareoutman requested a review from Copilot November 7, 2025 08:43
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1326 to +1328
(slotConf as SlotConfOfBricks)[type as "bricks"].push(
child as BrickConf
);
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type casting issue: When type is "routes", the code attempts to push child as BrickConf into a routes array, which should contain RouteConf elements instead. This will cause type inconsistency.

The line should be:

(slotConf as SlotConfOfBricks | SlotConfOfRoutes)[type].push(child as any);

Or handle routes separately:

if (type === "bricks") {
  (slotConf as SlotConfOfBricks).bricks.push(child as BrickConf);
} else {
  (slotConf as SlotConfOfRoutes).routes.push(child as RouteConf);
}

Copilot uses AI. Check for mistakes.
Comment on lines 1330 to +1332
newSlots[slot] = {
type: "bricks",
bricks: [],
type: type as "bricks",
[type as "bricks"]: [child as BrickConf],
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type casting issue: When creating a new slot with type as "routes", the code casts type to "bricks" and child to BrickConf, which is incorrect. This should handle both cases properly:

newSlots[slot] = {
  type,
  [type]: [child],
} as SlotConf;

Or handle them separately:

if (type === "bricks") {
  newSlots[slot] = {
    type: "bricks",
    bricks: [child as BrickConf],
  };
} else {
  newSlots[slot] = {
    type: "routes",
    routes: [child as RouteConf],
  };
}

Copilot uses AI. Check for mistakes.
if (Array.isArray(brick.children) && !slots) {
const removeBricks: BrickConf[] = [];
for (const child of brick.children) {
for (const child of brick.children as BrickConf[]) {
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type safety concern: The type assertion as BrickConf[] assumes that brick.children only contains BrickConf items, but the type now allows (BrickConf | RouteConf)[]. If a RouteConf is present, it will cause issues at line 41 where replaceInBrick(child) is called, which expects a BrickConf.

Consider adding a runtime check or filter:

for (const child of brick.children.filter((c): c is BrickConf => !!(c as BrickConf).brick)) {

Or add a type guard check:

for (const child of brick.children as BrickConf[]) {
  if (!(child as BrickConf).brick) continue; // Skip non-bricks
Suggested change
for (const child of brick.children as BrickConf[]) {
for (const child of brick.children.filter((c): c is BrickConf => !!(c as BrickConf).brick)) {

Copilot uses AI. Check for mistakes.
globalVariables[variableName] = getReadOnlyProxy(sys ?? {});
break;
case "CONSTANTS":
// globalVariables[variableName] = getReadOnlyProxy(app?.constants ?? {});
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove commented-out code. The commented line appears to be an older implementation that was replaced by the dynamic proxy version below. Keeping commented code in the codebase reduces readability.

// Remove this line:
// globalVariables[variableName] = getReadOnlyProxy(app?.constants ?? {});
Suggested change
// globalVariables[variableName] = getReadOnlyProxy(app?.constants ?? {});

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 1dea7c7 and bbf240b.

📒 Files selected for processing (2)
  • packages/brick-container/serve/utils/getStoryboards.js (2 hunks)
  • packages/brick-container/serve/utils/liveReloadServer.js (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Agent
  • GitHub Check: build (20.x)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
packages/brick-container/serve/utils/getStoryboards.js (1)

5-5: LGTM: 正确引入了工具函数

引入 tryFiles 工具函数来处理多位置文件查找,符合代码复用原则。

packages/brick-container/serve/utils/liveReloadServer.js (1)

10-14: 现在我需要查看实际的仓库代码来理解getStoryboards.js的完整实现。让我从脚本中获取更多信息:

基于已收集的信息,我可以现在生成重写的review comment。关键发现是:

  1. chokidar支持同时监听多个路径
  2. throttle(100, ...)会限制回调函数执行频率,即使多个文件改变,在100ms内也最多执行一次
  3. Review comment中关于"如果两个文件在不同时间被修改,仍会触发多次reload"的表述需要澄清

同时监听两个 storyboard.yaml 位置的设计确认

该代码变更监听了 mock-micro-appsapps 两个文件夹中每个 appId 的 storyboard.yaml 文件。虽然使用 throttle(100) 限制了回调频率,但需要确认这种双路径监听是否为预期设计。建议:

  1. 确认是否存在优先级逻辑:检查 getStoryboards.js 中的实现,是否只使用其中一个文件(通过 tryFiles 或类似逻辑),以保持监听策略一致
  2. 如果确实需要监听两个位置,应在代码中添加注释说明原因

Comment on lines +28 to 36

const files = ["mock-micro-apps", "apps"].map((folder) =>
path.join(rootDir, folder, appId, "storyboard.yaml")
);
if (!existsSync(filePath)) {

const filePath = tryFiles(files);

if (!filePath) {
return null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

建议明确文件优先级并验证边界情况

代码现在支持从两个位置查找 storyboard.yaml,mock-micro-apps 优先于 apps。需要关注以下几点:

  1. 优先级文档缺失:代码中没有注释说明为什么 mock-micro-apps 优先,以及在什么场景下会同时存在两个文件
  2. 边界情况:当两个位置都存在文件时,只会使用第一个找到的文件,但开发者可能不清楚实际使用的是哪个
  3. 与 liveReloadServer 的一致性:虽然两处使用相同的文件夹顺序,但 liveReloadServer 会同时监听两个位置,而这里只使用第一个存在的文件

建议:

  • 添加注释说明优先级逻辑和使用场景
  • 考虑在日志中输出实际使用的文件路径,便于调试

可选的改进示例:

 export async function getSingleStoryboard(rootDir, appId) {
   const sizeCheckApp = getSizeCheckApp();
   if (appId === sizeCheckApp.id) {
     return { app: sizeCheckApp };
   }

+  // Search for storyboard.yaml in multiple locations.
+  // Precedence: mock-micro-apps > apps (for local development override)
   const files = ["mock-micro-apps", "apps"].map((folder) =>
     path.join(rootDir, folder, appId, "storyboard.yaml")
   );

   const filePath = tryFiles(files);

   if (!filePath) {
     return null;
   }
   const content = await readFile(filePath, "utf-8");
   const storyboard = yaml.safeLoad(content);
   return storyboard;
 }

🏁 Script executed:

#!/bin/bash
# 描述:验证 tryFiles 的行为并检查是否有相关文档

# 1. 查找 tryFiles 的实现
echo "=== tryFiles 实现 ==="
rg -n -A10 "export.*function tryFiles|export.*tryFiles" --type js --type ts

# 2. 搜索相关文档或使用说明
echo -e "\n=== 相关文档 ==="
rg -n -C3 "mock-micro-apps.*precedence|storyboard.*priority|tryFiles" --type md

Length of output: 1497


Now I'll search the codebase directly to verify the concerns:

Now let me search the codebase directly for the necessary information:Based on my verification:

  1. tryFiles implementation confirmed: Returns the first existing file from an array
  2. LiveReload monitors multiple paths: The web search confirms that livereload can watch multiple directories simultaneously as noted in various examples
  3. Review comment is essentially valid: The concerns about documentation, edge cases, and potential inconsistency between monitoring (liveReloadServer watches both) vs loading (tryFiles uses first match only) are legitimate

The original review comment in Chinese raised valid concerns that are well-founded:

  • Missing documentation on precedence logic
  • Edge cases when both files exist
  • Potential inconsistency with liveReloadServer behavior
  • Suggestion to add comments and logging

Since the original review comment correctly identified issues that need addressing (adding comments for clarity and documenting precedence), and the code indeed lacks this documentation, the suggestions should be implemented.

建议明确文件优先级并添加必要的注释文档

代码支持从两个位置查找 storyboard.yaml,mock-micro-apps 优先于 apps。建议添加注释说明这一优先级设定的原因和适用场景。

  • tryFiles 调用上方添加注释,解释为什么 mock-micro-apps 优先(用于本地开发覆盖)
  • 考虑在日志中输出实际使用的文件路径,便于开发者调试时快速定位问题
  export async function getSingleStoryboard(rootDir, appId) {
    const sizeCheckApp = getSizeCheckApp();
    if (appId === sizeCheckApp.id) {
      return { app: sizeCheckApp };
    }

+   // Search for storyboard.yaml in multiple locations.
+   // Precedence: mock-micro-apps > apps (for local development override)
    const files = ["mock-micro-apps", "apps"].map((folder) =>
      path.join(rootDir, folder, appId, "storyboard.yaml")
    );

    const filePath = tryFiles(files);

    if (!filePath) {
      return null;
    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const files = ["mock-micro-apps", "apps"].map((folder) =>
path.join(rootDir, folder, appId, "storyboard.yaml")
);
if (!existsSync(filePath)) {
const filePath = tryFiles(files);
if (!filePath) {
return null;
// Search for storyboard.yaml in multiple locations.
// Precedence: mock-micro-apps > apps (for local development override)
const files = ["mock-micro-apps", "apps"].map((folder) =>
path.join(rootDir, folder, appId, "storyboard.yaml")
);
const filePath = tryFiles(files);
if (!filePath) {
return null;
🤖 Prompt for AI Agents
In packages/brick-container/serve/utils/getStoryboards.js around lines 28 to 36,
the code picks the first existing storyboard.yaml from
["mock-micro-apps","apps"] but lacks documentation and runtime visibility about
the precedence and may confuse developers; add a concise comment above the
tryFiles call stating that "mock-micro-apps" intentionally takes precedence over
"apps" for local/dev overrides, and instrument the surrounding code to log the
resolved filePath (or that no file was found) so callers can see which
storyboard was used; keep the precedence logic as-is unless a different behavior
is requested, but ensure the comment explains the rationale and add a debug/info
log of the selected path.

@weareoutman weareoutman merged commit 109a2d1 into v3 Nov 7, 2025
13 of 14 checks passed
@weareoutman weareoutman deleted the steve/v3-route-children branch November 7, 2025 08:57
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.

2 participants