mcpField
M
modernize-flp-sandbox

modernize-flp-sandbox

github.com/ui5开发44144 次安装1 周前更新
代码仓库

关于 modernize-flp-sandbox

面向 UI5 开发者的迁移技能,用于将应用从旧版 FLP sandbox 迁至 New Sandbox。它自动执行核心转换,检查测试基础设施,并单独报告相关问题。

主要功能

  1. 01分析旧版 sandbox 文件
  2. 02转换 HTML 与配置
  3. 03创建或重写钩子模块
  4. 04更新 OPA 测试配置
  5. 05报告迁移缺口

适用场景

  1. 01将 UI5 应用迁至 New Sandbox
  2. 02合并多个 sandbox 场景
  3. 03恢复 OPA 测试配置
  4. 04查找已弃用的 ushell 服务用法

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ui5/plugins-coding-agents --skill modernize-flp-sandbox

安装后在 Claude Code 中输入/modernize-flp-sandbox确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
native-replacements.md
operations.md
sandbox-config-surface.md

详细介绍

New Sandbox 迁移技能

将 UI5 应用从旧版 FLP sandbox 迁移到 New Sandbox。 你负责分析、转换、验证并报告。此技能会全自动完成核心迁移;对于相关事项(见下方范围),会先征求使用者意见再采取行动。

现在阅读 references/operations.md——这是所有 HTML / JSON / hook 模块转换的操作参考。

现在阅读 references/sandbox-config-surface.md——这是关于 New Sandbox 允许、阻止及静默覆盖内容的权威说明。 所有 §6 规则均引用此文件,而不是在正文中重复列出限制。

范围 — 三个层级

  1. 核心迁移 — 应用在 New Sandbox 下启动所需的操作。无条件执行;若无法完成,则迁移失败。触发条件:§6a、§6b、§6c、§6f。

  2. 测试基础设施 — 在不改变测试语义的前提下恢复测试套件运行。安全的重写无条件应用(资源根重绑定、OPA 引导代码提取)。任何涉及测试逻辑的操作:检测、报告,并在请求时提供选项。触发条件:§6d、§6e、§6g、§6h、§6j、§6l。

  3. 相关迁移与提示 — 由迁移引出但不属于迁移本身的事项。仅检测并报告;不应用重写。

    • QUnit 1.x → 2.x 模式(§6i)— 提示。
    • Shell 功能测试(§6j、§6l)— 提示。
    • 已弃用的 ushell 服务(§6k)— 在核心扫描中检测,并报告为“需要手动操作”(硬性运行时阻塞)。

1. 检测应用根目录

如果用户提供了路径参数,则使用该路径。否则,从 CWD 检测:

  • 在 CWD 及其父目录中查找 package.json + webapp/
  • 如果未找到,告知用户:“无法检测到 UI5 应用根目录。请在应用目录内运行,或提供路径:/modernize-flp-sandbox ~/path/to/app”

将 APP_ROOT 设为解析后的绝对路径。

2. 确保状态干净(迁移前)

修改任何文件之前,先确保有可回滚点。

如果检测到 git 仓库:

cd $APP_ROOT
git stash push -m "modernize-flp-sandbox: pre-migration backup $(date +%Y%m%d-%H%M%S)"

保存 stash 引用以供回滚。如果没有可 stash 的内容,则工作树已干净。

如果没有 git 仓库:

mkdir -p $APP_ROOT/webapp/test/.migration-backup
cp $APP_ROOT/webapp/test/flpSandbox*.html $APP_ROOT/webapp/test/.migration-backup/ 2>/dev/null
cp $APP_ROOT/webapp/test/fioriSandboxAppConfig.json $APP_ROOT/webapp/test/.migration-backup/ 2>/dev/null

3. 分析

读取 $APP_ROOT/webapp/test/ 下的所有 HTML 文件(递归),并构建模式清单。

检测旧版 sandbox HTML 文件 — 符合以下任一条件的文件即为旧版文件:

  • id="sap-ushell-bootstrap"(sandbox 引导脚本标签)
  • window["sap-ushell-config"] 赋值
  • src= 引用了 sandbox2.js(中间版本 New Sandbox 的旧式引导方式,早于 SandboxBootTask.js;与其他旧版模式同等处理)

OPA 测试页面(不会接收 SandboxBootTask / boot-manifest): 文件符合旧版检测条件,且同时包含以下所有内容:

  • QUnit.config 或 QUnit.config.autostart 赋值,或 window.QUnit = {
  • sap.ui.require(,其中包含来自 sap/ui/qunit/、sap/ui/test/ 或 sap/ui/thirdparty/qunit 的条目

这些文件不会作为 sandbox HTML 迁移。它们将执行:(a) §6g 将引导代码提取到同级 JS 文件;(b) §6i 检测 QUnit 1.x → 经用户同意后移交子技能。§6g 的 HTML 编辑包括:

  • 移除 <script src="../ushellConfig.js"> 和 <script src="...sandbox.js" id="sap-ushell-bootstrap">
  • 将内嵌 QUnit/OPA 引导代码替换为直接加载脚本标签(qunit-2.css、qunit-2.js、qunit-junit.js)+ <script src="AllJourneys.js">
  • 根据内嵌内容创建 AllJourneys.js(见 §6g)
  • 将 data-sap-ui-preload="async" 更新为 data-sap-ui-async="true",将 data-sap-ui-compatVersion 重命名为 data-sap-ui-compat-version,将 data-sap-ui-resourceroots 重命名为 data-sap-ui-resource-roots
  • 不要添加 SandboxBootTask.js、boot-manifest 或 <div id="canvas"> 外部化 ushell 配置检测: 对于 webapp/test/ 下任意 HTML 中的每个 <script src="...">,检查引用的文件是否包含对 ushell 配置对象的赋值,方式包括:
  • window["sap-ushell-config"] = … / globalThis[…] / self[…]
  • Object.assign(<global>["sap-ushell-config"], …)
  • <global>["sap-ushell-config"].<prop> = …(初始化后写入属性)

仅读取配置的文件不符合条件。将每个符合条件的文件记录为 externalUshellConfigFile。迁移时:与内联配置采用相同方式解析,从所有引用该文件的 HTML 中移除 <script> 引用,并在步骤 9 中删除该文件。

对于每个旧版 HTML 文件,记录:

  • 文件名、是否有模拟服务器、是否有 RTA/LREP、是否有自定义插件、是否有已弃用服务?
  • 是否有 locate-reuse-libs.js?(作为 Gap-Report 标记)
  • 现有的 data-sap-ui-libs、data-sap-ui-resourceroots/data-sap-ui-resource-roots、data-sap-ui-theme、data-sap-ui-language 值(保留,并重命名为 kebab-case)

检查是否存在 New Sandbox 构件:

  • fioriSandboxAppConfig.json — 是否存在?(将创建/覆盖)
  • sandbox/ 目录 — 对每个 *.js 文件分类:
    • legacy:包含 Container.createRendererInternal 或 attachRendererCreatedEvent,或者不包含 execute:。捕获以便原地重写(§6c)。将顶层命名空间声明捕获为 preservedDeclarations[](正则:^\s*sap(\.[a-zA-Z0-9_]+)+\s*\?\?=\s*\{\} 和 globalThis.sap/window.sap 链;后备方案:逐字复制,直到第一个 sap.ui.define( 或 IIFE)。
    • modern:导出 { execute: ... }。原样复用。
    • **多个旧版文件:**全部重写。仅有一个通过 beforeFlpStart 接入 — 优先选择名称中包含 Init(而非 Mock)的文件。生成 Gap-Report 说明。
  • FakeLREP 文件(webapp/test/ 下的 *Lrep*.json / *LRep*.json)— 记录准确文件名,以供 rta 引用。

检查 OPA 测试接线:

  • OPA 测试运行器 HTML(默认为 webapp/test/integration/opaTests.qunit.html;后备方案:webapp/test/integration/ 下任意引用旧版 mockserver HTML 的 *qunit.html)— 记录匹配 "../flpSandboxMockServer" 的值 → 触发 §6d。
  • OPA Common-helper 文件(默认为 webapp/test/integration/pages/Common.js;后备方案:webapp/test/integration/ 下任意包含 iStartMyAppInAFrame(/getFrameUrl( 和 sap.ui.require.toUrl( 的 *.js)— 触发 §6e。

扫描已弃用的 ushell 服务用法: 在 webapp/ 下搜索 *.js(排除 test/、resources/、*.min.js),查找 getService(<name>) / getServiceAsync(<name>),其中 <name> 是 references/sandbox-config-surface.md §5 中列出的 12 个已弃用服务之一。记录文件、行号、服务名称和绑定变量。在 Gap-Report(§8)中列出;后续处理见 §6k。

检查 UI5 框架版本: 读取 ui5.yaml 和 ui5-local.yaml 中的 framework.version。New Sandbox 要求版本 >= 1.147。如果版本低于此要求,自动升级:

  1. 查找 >= 1.147 的最新稳定 SAPUI5 版本
  2. 原地写入 ui5.yaml
  3. 记录:"Auto-bumped ui5.yaml framework version from <old> to <new> (New Sandbox requires >= 1.147)."
  4. 在 Gap-Report 中说明。

3a. Hook 依赖图扫描

识别从 beforeFlpStart hook 可达的传递 AMD 图中的急切 sap/ushell/* 导入(硬性约束 H2 — 参见 references/sandbox-config-surface.md §1)。详情和重写方案见 references/operations.md §6f。

**此步骤仅构建清单。**实际操作在 §6f 中进行(在 §6c 生成 hook 之后)。

记录:

  1. hookEntryModule — 从现有 fioriSandboxAppConfig.json 的 beforeFlpStart 解析,或从 §3 中的旧版 hook 检测(通常为 webapp/test/sandbox/fioriSandboxInit.js)。
  2. hookGraphRoots[] — hook 直接需要的其他模块(通过检查旧版 hook 主体确定)。

4. 场景向导(仅在发现多个 flp*.html 文件时)

如果只发现一个旧版沙箱 HTML 文件,则跳过。

新版沙箱使用单个 fioriSandboxAppConfig.json——多个 HTML 文件无法拥有独立配置。解决方案:合并为一个 HTML 文件,并通过 URL 参数在运行时选择场景。

**非交互模式:**检查 $APP_ROOT/webapp/test/wizard-answers.json。如果存在,直接使用其中的值(适用于 CI/脚本化迁移)。格式:

{
    "scenarios": [
        { "file": "flpSandbox.html", "id": "default", "description": "Live backend" },
        { "file": "flpSandboxMockServer.html", "id": "mockserver", "description": "Mock server" }
    ],
    "defaultScenario": "mockserver",
    "targetHtml": "flpSandbox.html",
    "paramName": "scenario"
}

询问用户:

  1. “每个文件代表什么场景?”(例如:“mockserver”、“cdm”、“default/no-mock”)
  2. “哪个场景应设为默认场景?”(建议:“mockserver”——可离线使用)
  3. “应保留哪个 HTML 文件名?”
  4. “URL 参数名称是什么?”(默认值:scenario;使用项目专属名称可避免冲突)

**记录:**目标 HTML 文件、场景列表、默认场景 ID、参数名称、废弃的 HTML 文件。

URL 约定:flpSandbox.html?scenario=mockserver。未提供参数 → 使用默认场景。在 README 中记录选定的名称。

5. 迁移前检查清单

写入文件前,确认:已设置 APP_ROOT(§1)、已确保可回滚(§2)、已建立清单(§3)、如有需要,已准备好向导答案(§4)。全部确认无误 → 继续执行 §6。

6. 迁移

执行所有转换。如果任何写入失败,跳转到回滚(§10)。

范围 — 修改的文件:

  • §3 中检测到的所有 flp*.html → 完整转换(§6a–§6c)
  • OPA 测试页 HTML 文件 → 提取引导代码(§6g)
  • OPA 测试运行器 HTML 文件 → 重新绑定资源根路径(§6d)
  • QUnit 测试套件索引 → 重写 journey URL(§6g)
  • OPA Common-helper → 注入 URL 参数(§6e)
  • webapp/test/fioriSandboxAppConfig.json → 创建/覆盖(§6b)
  • webapp/test/sandbox/*.js 旧版钩子 → 就地重写(§6c)
  • beforeFlpStart AMD 图中的文件 → 自动应用 references/native-replacements.md(§6f)
  • webapp/test/integration/ 下的 OPA 测试代码和应用代码 → §6h–§6l 的机械式重写

不在范围内:unitTests.qunit.html、unit/、*.qunit.js。

如果 opaTests.qunit.html 引用了 flpSandboxMockServer.html,请迁移它(§6a)并同时重新绑定 OPA 引用(§6d)。

以下每个小节均说明内容/时机/层级。完整算法见 references/operations.md。

6a. 就地转换旧版 HTML 文件

**层级:**核心。对每个旧版沙箱 HTML 文件无条件执行。

移除旧版 ushell-config 写入、sap-ushell-bootstrap 脚本标签及过时的引导属性。添加 SandboxBootTask.js(或 CDN 变体)、引导清单属性和 <div id="canvas">。合并各变体的资源根路径,并始终添加 sandbox 根路径。

→ references/operations.md §6a

6b. 创建或更新 fioriSandboxAppConfig.json

**层级:**核心。仅使用 references/sandbox-config-surface.md §2 中列出的键。

根据解析后的旧版配置构建 JSON。按第一个 - 将应用键拆分为 semanticObject + action。从 rootPath 中去除查询字符串,并确保以斜杠结尾。运行向导时,合并各变体中的磁贴。仅在有需要时添加 beforeFlpStart、rta、plugins。

→ references/operations.md §6b

6c. 创建或重写钩子模块

**层级:**核心。为 beforeFlpStart 提供支持,在 applyUshellConfig 之前运行。约定:不得触碰 globalThis["sap-ushell-config"],不得提前 require sap/ushell/*(参见 references/sandbox-config-surface.md §8)。

生成单场景钩子(仅模拟服务器)或多场景钩子(通过 URL 参数切换)。就地重写旧版钩子,并保留命名空间声明。如果不需要模拟服务器,则退回到最简无操作实现。

→ references/operations.md §6c

6d. 重新绑定 OPA 资源根路径

**层级:**测试基础设施。无条件执行(不改变语义)。

在 OPA 测试运行器 HTML 中,将 data-sap-ui-resource-roots 从指向旧版 mockserver HTML 改为指向迁移后的目标。

→ references/operations.md §6d

6e. 将场景 URL 参数注入 Common.js

**层级:**测试基础设施。仅当 Wizard 将多个 HTML 合并时执行。

在 # 哈希之前将场景参数注入 iframe URL(这样 window.location.search 才能获取该参数)。

→ references/operations.md §6e

6f. 解析 hook 图中的 ushell 依赖

**层级:**核心。硬运行时阻断项 H2——不得从 hook 的传递 AMD 图中急切加载任何 sap/ushell/*。

从 hookEntryModule(§3a)遍历 AMD 闭包。将每个 sap/ushell/* 引用分类为可直接替换/可通过架构调整迁移/需要手动处理。自动应用 references/native-replacements.md 中的方案,或生成 Gap-Report 条目。

→ references/operations.md §6f

6g. 将 OPA 引导代码提取到同级 JS 文件

**层级:**测试基础设施。机械式提取。

将内联 <script> 块提取到同级 .js 文件(默认文件名为 AllJourneys.js)。将 QUnit 1.x 全局变量和 jQuery.sap.getUriParameters 转换为现代等效用法。更新测试套件索引,使旅程 URL 使用 URLSearchParams 风格。

→ references/operations.md §6g

6h. 检测绑定到旧版 ushell-config 值的代码

**层级:**测试基础设施(可机械式自动修复的情况会自动修复,否则报告)。

识别硬编码值的代码:这些值在旧版配置中可自由指定,但在 New Sandbox 下的派生方式不同。类别:A 磁贴派生(自动修复)、B 用户配置(自动修复)、C 个性化设置(自动修复)、D 默认固定值(仅报告)。参见 references/sandbox-config-surface.md §3+§4。

→ references/operations.md §6h

6i. QUnit 1.x → 2.x 模式(移交)

**层级:**建议。实际重写由 modernize-flp-sandbox-qunit 子技能执行。

检测并报告,提供调用子技能的选项。之所以提示此项:§6a 会添加 data-sap-ui-async="true"——在此模式下,QUnit 1.x 全局变量(module、ok、equal)会抛出 ReferenceError。根本原因与 Sandbox 迁移无关;只是迁移使其显现出来。

→ references/operations.md §6i

6j. FLP 外壳控件检测(报告)

**层级:**测试基础设施(仅报告)。检测直接针对 FLP 外壳控件的 OPA 测试。较新的 UI5 版本通过 Web Components 渲染外壳,因此匹配 sap.m.* 外壳控件的测试会静默失效。

模式:S1 = sap.m 控件 + 仅限外壳的 id,S2 = type:"Back" 匹配器,S3 = 非 WebComponent 外壳页眉调用。

→ references/operations.md §6j

6k. 应用代码中的已弃用 ushell 服务(检测)

**层级:**核心检测。每个已弃用服务都会在调用 getService()/getServiceAsync() 时造成硬运行时阻断(参见 references/sandbox-config-surface.md §5)。应用将无法启动。

扫描应用代码并记录命中项,在 Gap-Report 中标记为“需要手动处理”。不会尝试重写。

→ references/operations.md §6k

6l. OPA 测试对 FLP-shell 功能的检测 + 询问

层: test-infra(检测 + 询问)。检测依赖 FLP shell 的 journey 测试。仅在存在可识别的 iframe-toggle 模式时才提供应用修复方案。未经同意绝不修改。

信号:shell-control 选择器(§6j)、已弃用的服务调用(§6k)、按约定命名的页面对象方法。 探测项:P1 = startMyAppInAFrame 标记,P2 = iStartMyAppInAFrame helper,P3 = 自定义 iframe URL 构造器。

→ references/operations.md §6l

8. 报告

始终输出 Gap-Report:

== New Sandbox Migration Report ==

App:    <app name from package.json>
Path:   <APP_ROOT>
Status: ✓ SUCCESSFUL  |  ✗ FAILED

── Automatically applied ──────────────────────────────────────────────
  ✓/✗  webapp/test/<filename>.html              (HTML transformed)
  ✓/✗  webapp/test/fioriSandboxAppConfig.json   (created)
  ✓/—  webapp/test/sandbox/<hookModule>.js      (created / reused / rewrote-legacy)
  ✓/—  rta: "<fakeLRep filename>"               (written to config)
  ✓/—  opaTests.qunit.html                      (resource root rebound)
  ✓/—  Common.js                                (scenario URL param injected)
  ✓/—  Hook-graph: <N> trivial replacement(s) auto-applied

── Manual action required ─────────────────────────────────────────────
  ✗  <ServiceName> (deprecated — hard runtime error): <file>:<line>
     → <successor from sandbox-config-surface.md §5>

  ✗  Custom FakeLrep connector at <path>
     → May have sap/ushell dependencies incompatible with beforeFlpStart.

  ✗  Hook-graph — architectural-relocatable: <module> uses <sap/ushell/X>
     → [diff sketch]

  ✗  Hook-graph — manual-required: <module> uses <sap/ushell/X> at load time
     → [textual sketch]

  ✗  <Layer-2 step> — pattern not recognised
     → [description]

── Advisory (no migration blocker) ───────────────────────────────────
  ℹ  <N> QUnit 1.x pattern(s) in <M> file(s)
  ℹ  Shell-control test patterns: <N> in <M> files (S1: ?, S2: ?, S3: ?)
  ℹ  locate-reuse-libs.js: script tag removed — deprecated pattern, safe.
  ℹ  <other non-standard patterns>

[On failure: "Rolled back. Partial attempt saved to webapp/test/.migration-attempt/"]

没有发现问题的部分将从报告中省略。

9. 清理旧文件(必需 — 不得跳过)

  • 未使用 Wizard: 旧 HTML 就地转换 — 无需删除其他文件。
  • 使用了 Wizard: 删除所有过时的 HTML 文件(目标文件除外)。
  • 始终: 删除每个 externalUshellConfigFile[]。
rm <obsolete-file-1> <obsolete-file-2> ...

验证: ls webapp/test/flp*.html — 仅保留迁移后的目标文件。

10. 回滚

§6 期间发生任何故障时:

保存部分尝试:

mkdir -p $APP_ROOT/webapp/test/.migration-attempt
cp $APP_ROOT/webapp/test/*.html $APP_ROOT/webapp/test/.migration-attempt/ 2>/dev/null
cp $APP_ROOT/webapp/test/fioriSandboxAppConfig.json $APP_ROOT/webapp/test/.migration-attempt/ 2>/dev/null

恢复:

  • Git:git checkout -- webapp/test/ && git stash pop
  • 无 Git:cp $APP_ROOT/webapp/test/.migration-backup/* $APP_ROOT/webapp/test/

输出状态为 FAILED 的 Gap-Report。

评论

支持 Markdown