Skip to content

Conversation

@weareoutman
Copy link
Member

@weareoutman weareoutman commented Sep 12, 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

  • 新功能
    • 片段/非页面渲染现已支持通过 FN 全局调用注入的函数;作用域与页面隔离,卸载时自动清理。
  • 公共 API
    • 上下文与相关工厂新增可选“隔离根”参数,用于区分渲染作用域,保持向后兼容。
  • 测试
    • 新增片段内函数调用的单元测试,覆盖渲染与卸载流程。

@coderabbitai
Copy link

coderabbitai bot commented Sep 12, 2025

Walkthrough

在运行时引入“隔离根”机制:为 RuntimeContext 增加可选的 isolatedRoot:symbol,并贯穿注册与解析流程。非 page 渲染创建并注册隔离函数,FN 全局在存在 isolatedRoot 时从隔离注册表读取。更新 StoryboardFunctionRegistry 工厂以接受并传递该参数,并补充相关单测与 API 声明。

Changes

Cohort / File(s) Summary
Public API 声明
etc/runtime.api.md
将 RuntimeContext 增加可选属性 isolatedRoot?: symbolStoryboardFunctionRegistryFactory 的参数对象新增可选 isolatedRoot?: symbol
Storyboard 函数注册工厂
packages/runtime/src/StoryboardFunctionRegistry.ts
工厂函数签名与实现接收并传递 isolatedRoot,转交给全局变量提供逻辑。
根创建与挂载流程
packages/runtime/src/createRoot.ts, packages/runtime/src/createRoot.spec.ts
非 page 渲染创建 isolatedRoot 并通过 registerIsolatedFunctions 注册函数;卸载时清理注册表。新增用例验证片段中 <% FN.testFn() %> 的函数调用。
隔离函数注册表(新增)
packages/runtime/src/internal/compute/IsolatedFunctions.ts
新增模块:导出 isolatedFunctionRegistry: Map<symbol, ReadonlyStoryboardFunctions>;提供 registerIsolatedFunctions(isolatedRoot, functions),内部通过工厂获取并注册函数,并缓存到注册表。
全局变量与求值路径
packages/runtime/src/internal/compute/evaluate.ts, packages/runtime/src/internal/compute/getGeneralGlobals.ts
isolatedRoot 传入全局变量构造;扩展选项接口,FN 全局在存在 isolatedRoot 时从 isolatedFunctionRegistry 获取,否则回退到原 storyboardFunctions
内部接口
packages/runtime/src/internal/interfaces.ts
RuntimeContext 新增可选属性 isolatedRoot?: symbol

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed 标题 "feat(): support FN in createRoot with scope fragment" 简洁且直接反映了变更的核心:在 createRoot 的 fragment(非 page)作用域中支持 FN(隔离函数/函数注册)功能,和变更中引入的 isolatedRoot/registerIsolatedFunctions 逻辑高度相关,因此可作为该 changeset 的单句摘要。该标题没有包含文件列表或无关噪音,便于团队在历史中快速识别主要改动。
Description Check ✅ Passed PR 描述遵循仓库提供的模板并已填写关键核对项:依赖检查选择“没有使用上游新特性”,新特性项标注为 feat 且已勾选单元测试与手动验证,破坏性变更项未被触发,因此满足模板要求并提供了审查所需的主要信息。

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch steve/v3-isolated-functions

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.

@weareoutman weareoutman force-pushed the steve/v3-isolated-functions branch from c6e9d17 to b5fb749 Compare September 12, 2025 07:26
@codecov
Copy link

codecov bot commented Sep 12, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.22%. Comparing base (f26358f) to head (b5fb749).
⚠️ Report is 2 commits behind head on v3.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##               v3    #4781   +/-   ##
=======================================
  Coverage   95.22%   95.22%           
=======================================
  Files         209      210    +1     
  Lines        9166     9175    +9     
  Branches     1764     1768    +4     
=======================================
+ Hits         8728     8737    +9     
  Misses        324      324           
  Partials      114      114           
Files with missing lines Coverage Δ
packages/runtime/src/StoryboardFunctionRegistry.ts 89.65% <ø> (ø)
packages/runtime/src/createRoot.ts 96.59% <100.00%> (+0.16%) ⬆️
.../runtime/src/internal/compute/IsolatedFunctions.ts 100.00% <100.00%> (ø)
packages/runtime/src/internal/compute/evaluate.ts 98.42% <ø> (ø)
.../runtime/src/internal/compute/getGeneralGlobals.ts 96.42% <100.00%> (ø)
🚀 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 Sep 12, 2025

next-core    Run #11654

Run Properties:  status check passed Passed #11654  •  git commit ee2c6059f0 ℹ️: Merge b5fb749d05aec7018aff16795a7f1c606f6becd5 into f26358f18cb64fd9e3bff3c2fb41...
Project next-core
Branch Review steve/v3-isolated-functions
Run status status check passed Passed #11654
Run duration 00m 23s
Commit git commit ee2c6059f0 ℹ️: Merge b5fb749d05aec7018aff16795a7f1c606f6becd5 into f26358f18cb64fd9e3bff3c2fb41...
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 September 12, 2025 07:35
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 implements support for isolated function scopes in fragment-scoped rendering contexts, allowing FN calls to be isolated per render root to prevent interference between different rendering instances.

  • Adds isolated function registry for non-page root contexts
  • Integrates isolated root support throughout the evaluation pipeline
  • Implements automatic cleanup of isolated state on unmount

Reviewed Changes

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

Show a summary per file
File Description
packages/runtime/src/internal/interfaces.ts Adds isolatedRoot symbol to RuntimeContext
packages/runtime/src/internal/compute/getGeneralGlobals.ts Updates FN global to use isolated functions when available
packages/runtime/src/internal/compute/evaluate.ts Passes isolatedRoot to general globals
packages/runtime/src/internal/compute/IsolatedFunctions.ts New module implementing isolated function registry
packages/runtime/src/createRoot.ts Integrates isolated root creation and function registration
packages/runtime/src/createRoot.spec.ts Adds test for functions in scope fragment
packages/runtime/src/StoryboardFunctionRegistry.ts Updates factory to support isolatedRoot parameter
etc/runtime.api.md Updates API documentation to reflect new isolatedRoot parameter

Tip: Customize your code reviews with copilot-instructions.md. Create the file or 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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/runtime/src/StoryboardFunctionRegistry.ts (1)

149-163: 修复 FN 分支:当 isolatedRoot 存在但注册表未命中时回退到全局 FN,并补充测试

  • 验证:GeneralGlobalsOptions 已声明 isolatedRoot?: symbol(packages/runtime/src/internal/compute/getGeneralGlobals.ts)。
  • 问题:getGeneralGlobals 中 "FN" 分支当前为 isolatedRoot ? isolatedFunctionRegistry.get(isolatedRoot) : storyboardFunctions —— 若 isolatedRoot 存在但 registry.get 返回 undefined,会导致 FN 返回 undefined 且不会回退到 storyboardFunctions(packages/runtime/src/internal/compute/getGeneralGlobals.ts)。
  • 覆盖缺失:未发现针对 registerIsolatedFunctions / 未注册回退 的单元测试(createRoot.spec.ts 有 FN 使用案例,但未覆盖“隔离覆盖全局”和“未注册回退”两条路径)。

建议:

  • 在 packages/runtime/src/internal/compute/getGeneralGlobals.ts 将 "FN" 分支改为带回退的形式:isolatedRoot ? (isolatedFunctionRegistry.get(isolatedRoot) ?? storyboardFunctions) : storyboardFunctions
  • 补充单元测试:一)注册隔离函数时 FN 使用隔离实现;二)isolatedRoot 存在但未注册时 FN 回退到全局 storyboardFunctions(可在 createRoot.spec.ts 或新增 getGeneralGlobals.spec.ts 实现)。
🧹 Nitpick comments (13)
etc/runtime.api.md (2)

461-463: 新增 RuntimeContext.isolatedRoot?: symbol 定义正确,但建议补充文档注释与稳定性声明

建议在源码处为该字段添加 TSDoc,描述“仅在 fragment/非 page 根下使用,用于隔离 FN 作用域;page 下应为 undefined”。便于后续维护者理解语义。


629-629: API Extractor 警告:FunctionCoverageSettings 未从入口导出

建议在入口 index.ts/d.ts 导出该类型,避免消费者无法引用并清理 AE 警告。

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

33-34: 为 RuntimeContext 新增 isolatedRoot?: symbol,建议补充 TSDoc

类型选择 symbol 合理。建议增加简短注释以阐明仅 fragment 使用、page 为空。

   appendI18nNamespace?: string;
-  isolatedRoot?: symbol;
+  /** 标识当前渲染根的隔离作用域:fragment 下为唯一 symbol;page 场景为 undefined */
+  isolatedRoot?: symbol;
packages/runtime/src/createRoot.spec.ts (1)

322-351: 新增“functions in scope fragment”单测覆盖核心路径,LGTM;建议补一条多根隔离性回归

当前用例验证 fragment 下 FN 注入与调用。建议再补一个“两个 fragment 根互不干扰”的用例,确保函数不会串根泄漏。

可参考:

test("functions isolation across two fragment roots", async () => {
  const r1 = unstable_createRoot(document.createElement("div"));
  const r2 = unstable_createRoot(document.createElement("div"));

  await r1.render([{ brick: "p", properties: { textContent: "<% FN.a() %>" } }], {
    functions: [{ name: "a", source: "function a(){return 'A'}" }],
  });
  await r2.render([{ brick: "p", properties: { textContent: "<% FN.b() %>" } }], {
    functions: [{ name: "b", source: "function b(){return 'B'}" }],
  });

  expect(r1Container.innerHTML).toBe("<p>A</p>");
  expect(r2Container.innerHTML).toBe("<p>B</p>");

  r1.unmount();
  r2.unmount();
});
packages/runtime/src/internal/compute/getGeneralGlobals.ts (2)

10-10: 为 GeneralGlobals 引入 isolatedRoot 选项并导入注册表:方向正确,建议细化类型

  • 引入 isolatedRoot 与注册表符合隔离设计。
  • GeneralGlobalsOptions.storyboardFunctions 建议从 unknown 收紧为具体的 ReadonlyStoryboardFunctions 类型,减少调用端断言。
-export interface GeneralGlobalsOptions {
+export interface GeneralGlobalsOptions {
   collectCoverage?: unknown;
   widgetId?: string;
   widgetVersion?: string;
   isolatedRoot?: symbol;
   app?: PartialMicroApp;
   appendI18nNamespace?: string;
-  storyboardFunctions?: unknown;
+  storyboardFunctions?: Readonly<Record<string, Function>>;
   isStoryboardFunction?: boolean;
 }

Also applies to: 12-21, 41-53


58-61: FN 解析优先使用 isolatedRoot:建议增加安全兜底,避免未注册时抛 ReferenceError

在极端情况下(例如 registerIsolatedFunctions 未写入或写入前的早期求值),isolatedFunctionRegistry.get(isolatedRoot) 可能为 undefined,从而导致 FN 缺失。建议兜底为空对象(或受控的只读空表),提升健壮性。

-    case "FN":
-      return isolatedRoot
-        ? isolatedFunctionRegistry.get(isolatedRoot)
-        : storyboardFunctions;
+    case "FN":
+      return isolatedRoot
+        ? isolatedFunctionRegistry.get(isolatedRoot) ?? Object.freeze({})
+        : storyboardFunctions;

请同时确认 registerIsolatedFunctions 在传入空数组时,确实会为该 isolatedRoot 写入一个空只读对象,而不是跳过写入。

packages/runtime/src/createRoot.ts (3)

105-106: 每个非 page 根使用唯一 Symbol 作为隔离标识:实现简洁,但建议加注释说明生命周期

建议注明:该 symbol 在 root 生命周期内保持不变,用于跨多次 render 共享同一隔离命名空间。

-  const isolatedRoot = scope === "page" ? undefined : Symbol("IsolatedRoot");
+  // 每个 fragment 根唯一的隔离标识;在该根的整个生命周期内保持不变
+  const isolatedRoot = scope === "page" ? undefined : Symbol("IsolatedRoot");

199-201: fragment 下注册隔离函数:确认覆盖“重复 render 时更新/清空”的语义

目前对 undefined 使用空数组兜底是对的。请确认 registerIsolatedFunctions 是“替换”而非“增量合并”,以避免旧函数遗留。

可在 registerIsolatedFunctions 内显式清空旧映射后再写入,或在此处 render 前先 isolatedFunctionRegistry.set(isolatedRoot!, Object.freeze({})) 再注册。


273-276: unmount 时清理注册表,避免泄漏:LGTM;建议补充防御日志(可选)

可在开发模式下 console.debug 一条,用于排查未清理导致的潜在冲突。

packages/runtime/src/StoryboardFunctionRegistry.ts (1)

78-88: 为 isolatedRoot 参数补充简短 JSDoc 并标注可见性(@internal

建议在工厂函数参数处对 isolatedRoot 的用途做一句话说明(用于在 getGeneralGlobals 中解析隔离作用域的 FN),并标注 @internal 以避免被误用为公共 API。

packages/runtime/src/internal/compute/IsolatedFunctions.ts (3)

1-1: 将类型导入标记为 type-only 以避免多余运行时依赖

仅在类型位置使用的导入应使用 import type,减少编译产物中的无用引入。

-import { StoryboardFunction } from "@next-core/types";
+import type { StoryboardFunction } from "@next-core/types";

12-20: 注册顺序建议:先注册后暴露,消除潜在读到“半初始化”视图的窗口

虽然当前调用序在同一 tick 内几乎没有风险,但更稳妥的顺序是先完成 register,再写入 registry,可避免外部在极端时序下读取到尚未注册完的 Proxy。

   const { storyboardFunctions, registerStoryboardFunctions } =
     StoryboardFunctionRegistryFactory({ isolatedRoot });
-  isolatedFunctionRegistry.set(isolatedRoot, storyboardFunctions);
-  registerStoryboardFunctions(functions);
+  registerStoryboardFunctions(functions);
+  isolatedFunctionRegistry.set(isolatedRoot, storyboardFunctions);

12-20: 提供对称的注销 API,集中清理职责

createRoot 层据称会在卸载时删除映射,但在此模块提供一个轻量的 unregisterIsolatedFunctions 能让职责更集中、调用更直观。

 export function registerIsolatedFunctions(
   isolatedRoot: symbol,
   functions: StoryboardFunction[]
 ): void {
   const { storyboardFunctions, registerStoryboardFunctions } =
     StoryboardFunctionRegistryFactory({ isolatedRoot });
   isolatedFunctionRegistry.set(isolatedRoot, storyboardFunctions);
   registerStoryboardFunctions(functions);
 }
+
+export function unregisterIsolatedFunctions(isolatedRoot: symbol): void {
+  isolatedFunctionRegistry.delete(isolatedRoot);
+}
📜 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 f26358f and b5fb749.

📒 Files selected for processing (8)
  • etc/runtime.api.md (3 hunks)
  • packages/runtime/src/StoryboardFunctionRegistry.ts (2 hunks)
  • packages/runtime/src/createRoot.spec.ts (1 hunks)
  • packages/runtime/src/createRoot.ts (5 hunks)
  • packages/runtime/src/internal/compute/IsolatedFunctions.ts (1 hunks)
  • packages/runtime/src/internal/compute/evaluate.ts (1 hunks)
  • packages/runtime/src/internal/compute/getGeneralGlobals.ts (3 hunks)
  • packages/runtime/src/internal/interfaces.ts (1 hunks)
🔇 Additional comments (5)
etc/runtime.api.md (1)

575-579: 确认:isolatedRoot 已正确透传且调用方兼容

验证结果:StoryboardFunctionRegistryFactory 在对 getGeneralGlobals 的调用中传入了 isolatedRoot(packages/runtime/src/StoryboardFunctionRegistry.ts),主要调用点已兼容 — IsolatedFunctions 显式传入 isolatedRoot、WidgetFunctions 以具名对象传入 widgetId/widgetVersion、StoryboardFunctions 使用默认调用,测试文件也已适应。

packages/runtime/src/internal/compute/evaluate.ts (1)

518-526: 在 getGeneralGlobals 传入 isolatedRoot:实现对 fragment 根的 FN 隔离支持,LGTM

改动小而关键,能在不影响 page 场景下启用隔离根的 FN 解析。

packages/runtime/src/createRoot.ts (2)

31-34: 引入 IsolatedFunctions 注册与注册表:LGTM


143-151: 将 isolatedRoot 写入 runtimeContext:LGTM

这使 evaluate/getGeneralGlobals 能感知隔离根。

packages/runtime/src/internal/compute/IsolatedFunctions.ts (1)

12-20: 已验证:卸载时会清理 isolatedFunctionRegistry 映射

createRoot.ts 在卸载路径调用了 isolatedFunctionRegistry.delete(isolatedRoot)(packages/runtime/src/createRoot.ts:273-274);registerIsolatedFunctions 在创建时注册映射(packages/runtime/src/createRoot.ts:200),isolatedRoot 在创建时生成(packages/runtime/src/createRoot.ts:105)。

@qiaofengxi qiaofengxi merged commit 421d161 into v3 Sep 12, 2025
8 checks passed
@qiaofengxi qiaofengxi deleted the steve/v3-isolated-functions branch September 12, 2025 07:59
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.

3 participants