关于 react-19-plugin-migration
帮助 Grafana 插件在不升级 React 的情况下做好 React 19 兼容准备,面向插件开发者,涵盖工具链、依赖、源码和 CI 调整。
主要功能
- 01扫描 React 19 兼容性问题
- 02更新 create-plugin 脚手架
- 03外置 JSX runtime
- 04升级 Grafana 与 React 类型依赖
- 05修复弃用的 React 写法并验证构建
适用场景
- 01为 Grafana 13 做插件准备
- 02查找插件源码中的不兼容 API
- 03更新构建和 lint 配置
- 04通过构建、类型检查和测试验证
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ grafana/ skills --skill react-19-plugin-migration安装后在 Claude Code 中输入/react-19-plugin-migration确认已加载。
附带文件references/known-issues.md
详细介绍
将 Grafana 插件迁移到 React 19
Grafana 13(2026 年 4 月)将从 React 18 迁移到 React 19。不兼容的插件将无法运行。 不要将 React 升级到 19——只进行向前兼容的更改。
所有更改都放在一个 PR 中。按顺序执行步骤。切勿手动编辑 yarn.lock。
步骤 1:检测插件上下文
PLUGIN_JSON=$([ -f src/plugin.json ] && echo "src/plugin.json" \
|| ([ -f plugin/src/plugin.json ] && echo "plugin/src/plugin.json" || echo ""))
PKG_JSON=$([ -f package.json ] && echo "package.json" \
|| ([ -f plugin/package.json ] && echo "plugin/package.json" || echo ""))
PLUGIN_ID=$(jq -r '.id' $PLUGIN_JSON 2>/dev/null)
[ -f yarn.lock ] && PM="yarn" || ([ -f pnpm-lock.yaml ] && PM="pnpm" || PM="npm")
CP_VERSION=$(jq -r '.version' .config/.cprc.json 2>/dev/null)
echo "PLUGIN_ID=$PLUGIN_ID PM=$PM CP=$CP_VERSION"
如果 PLUGIN_ID 为空,请向用户询问插件根目录路径。
步骤 2:扫描兼容性问题
构建插件并运行 React 19 兼容性扫描器:
npm run build 2>&1 | tail -5
npx -y @grafana/react-detect@latest 2>&1
保存输出。扫描器会标记:
jsxRuntimeImport/__SECRET_INTERNALS→ 步骤 4 修复defaultProps/propTypes/ReactDOM.render→ 步骤 8(源代码修复)findDOMNode→ 步骤 6(依赖项升级)或步骤 8(源代码修复)
如果构建失败(插件此前从未构建过),请跳过此步骤,并在步骤 9 后运行 react-detect。如果输出显示“No breaking changes detected”,仍需继续——jsx-runtime 外部化和 grafanaDependency 升级始终是必需的。
步骤 9 后重新运行 react-detect,确认所有问题均已解决。
步骤 3:更新 @grafana/create-plugin
脚手架更新会引入 React 19 所需的 externals 提取、jest 模拟、Docker 修复和 webpack 改进。务必在 add externalize-jsx-runtime 之前完成此操作。
要求 Git 工作区干净。如果尚未创建功能分支,请先创建。
运行更新
npx @grafana/create-plugin@latest update 2>&1
如果 yarn install 因“engine is incompatible”而失败
更新过程会运行一个不带 --ignore-engines 的中间 yarn install。请手动完成:
yarn install --ignore-scripts --ignore-engines 2>&1 | tail -10
提交中间状态,然后重新运行:
git add -A && git commit -m "chore: intermediate create-plugin update" --no-verify
npx @grafana/create-plugin@latest update 2>&1
如果 ESLint 9 迁移(004)因解析器错误而失败
对于 ESLint 配置复杂的插件,自动迁移可能会生成无效的 JS。 不要跳过——提交已成功的更改,然后手动完成 ESLint 9 迁移:
git add -A && git commit -m "chore: update create-plugin (ESLint 9 migration manual)" --no-verify
然后按照下方的“完成 ESLint 9 迁移”部分完成操作。
更新后
务必运行安装并进行验证:
yarn install --ignore-scripts --ignore-engines 2>&1 | tail -10
cat .config/.cprc.json
如果有更改,请提交:
git add -A && git diff --cached --quiet || git commit -m "chore: update create-plugin scaffolding" --no-verify
步骤 3b:完成 ESLint 9 迁移
create-plugin update 会将 ESLint 升级到 v9,这需要使用扁平配置(eslint.config.js),而不是 .eslintrc。无论自动迁移(004)成功、部分成功还是失败,都必须确保 ESLint 正常工作后才能继续。
检查当前状态
ls eslint.config.js .eslintrc* .config/.eslintrc* 2>/dev/null
npx eslint --version 2>&1
有以下三种情况:
A)eslint.config.js 存在且 yarn lint 通过——自动迁移成功。继续下一步。
B)eslint.config.js 存在,但 yarn lint 失败——迁移不完整。请修复问题:
yarn lint 2>&1 | head -30
常见修复方法:
Invalid option '--ignore-path'或Invalid option '--ext'→ 从package.json的lint脚本中移除这些标志。在 ESLint v9 扁平配置中,忽略项和文件匹配规则应在eslint.config.js中配置,而不是通过 CLI 标志配置。更新为:eslint --cache .Cannot find module 'eslint-plugin-deprecation'→ 从eslint.config.js中移除该导入/引用(已由@typescript-eslint/no-deprecated替代)- 其他无效的插件导入 → 如果对应的包已被移除,请从配置中删除这些导入
C)不存在 eslint.config.js——自动迁移失败。请手动创建:
ls node_modules/@grafana/eslint-config/flat.js 2>/dev/null
如果存在 flat.js,请以它为基础创建 eslint.config.js:
import grafanaConfig from '@grafana/eslint-config/flat';
export default [
...grafanaConfig,
{
ignores: ['**/dist/', '**/node_modules/', '**/.config/', '**/coverage/'],
},
];
然后将旧 .eslintrc 中的自定义规则迁移到数组中的其他配置对象。
创建扁平配置后:
- 更新
lint脚本:"lint": "eslint --cache ." - 删除根目录下的
.eslintrc(保留.config/.eslintrc——它由脚手架生成,不会造成问题)
验证 lint 是否正常工作
yarn lint 2>&1 | tail -20
使用 yarn lint --fix 修复可自动修复的问题。提交更改:
git add -A && git diff --cached --quiet || git commit -m "chore: complete ESLint 9 flat config migration" --no-verify
步骤 4:外置 jsx-runtime
始终使用 create-plugin add 命令。 要求 Git 工作树干净。
npx @grafana/create-plugin@latest add externalize-jsx-runtime 2>&1
验证:
grep "jsx-runtime" .config/bundler/externals.ts 2>/dev/null
- 找到 → 提交并继续。
- 未找到 → 命令失败。只有在这种情况下,才手动将 externals 添加到根目录的
webpack.config.ts:
externals: ['react/jsx-runtime', 'react/jsx-dev-runtime'],
提交:
git add -A && git diff --cached --quiet || git commit -m "feat: externalize jsx-runtime" --no-verify
步骤 5:升级 grafanaDependency
jq -r '.dependencies.grafanaDependency' $PLUGIN_JSON
如果尚未设为 >=12.3.0,请更新。步骤 3 中的 create-plugin add 可能已完成此操作。
步骤 6:升级依赖项
Faro(如果存在)
grep '"@grafana/faro' $PKG_JSON
| Package | Target |
|---|---|
@grafana/faro-react | ^2.2.3 |
@grafana/faro-web-sdk | ^2.2.3 |
@grafana/faro-web-tracing | ^2.0.0 |
Grafana 包
grep '"@grafana/' $PKG_JSON | grep -v faro | grep -v create-plugin
将 @grafana/data、@grafana/runtime、@grafana/schema、@grafana/ui 升级到 ^12.2.0 或更高版本。
如果插件使用翻译,或 @grafana/scenes 有此要求,则添加 @grafana/i18n@^12.2.0。
React 类型
将 react 和 react-dom 升级到 ^18.3.0(尽早暴露 React 19 问题)。
如果缺少 @types/react@^18.3.0 和 @types/react-dom@^18.3.0,请将它们添加到 devDependencies。
移除已弃用的包
如果存在,请从 devDependencies 中移除:
eslint-plugin-deprecation(已由@typescript-eslint/no-deprecated替代)@types/testing-library__jest-dom(已由setupTests.d.ts替代)
损坏的传递依赖项
如果 yarn install 因过时的 Git 引用而失败,不要编辑 yarn.lock。请添加 resolutions 条目:
"resolutions": {
"<package-name>": "<working-version-or-git-ref>"
}
然后删除 yarn.lock 和 node_modules,再重新安装:
rm -rf node_modules yarn.lock
yarn install --ignore-engines 2>&1 | tail -10
步骤 7:修复未满足的 @openfeature/web-sdk 对等依赖
@grafana/runtime 依赖 @openfeature/react-sdk,后者将 @openfeature/web-sdk 作为
对等依赖。Yarn v1(classic)不会自动安装对等依赖。
检查插件是否使用 yarn classic:
yarn --version 2>&1 | head -1
如果版本以 1. 开头,请检查警告:
yarn install --ignore-engines 2>&1 | grep "unmet peer dependency.*openfeature/web-sdk"
如果发现警告:
yarn add -D @openfeature/web-sdk @openfeature/core --ignore-engines
跳过条件: Yarn v2+ 或 npm v7+(会自动安装对等依赖)。
步骤 8:修复源代码问题
grep -rn "ReactDOM\.render\|ReactDOM\.unmountComponentAtNode\|ReactDOM\.findDOMNode" src/ --include="*.tsx" --include="*.ts"
grep -rn "\.defaultProps\s*=" src/ --include="*.tsx" --include="*.ts"
grep -rn "\.propTypes\s*=" src/ --include="*.tsx" --include="*.ts"
grep -rn "contextTypes\|getChildContext" src/ --include="*.tsx" --include="*.ts"
grep -rn "createFactory" src/ --include="*.tsx" --include="*.ts"
grep -rn "ChangeEvent<HTMLInputElement>" src/ --include="*.tsx" --include="*.ts"
| Pattern | Fix |
|---|---|
ReactDOM.render() | createRoot(container).render(element) |
函数组件上的 defaultProps | 移至解构参数默认值 |
类组件上的 defaultProps | 保留 — 仍然有效 |
propTypes | 移除 |
contextTypes / getChildContext | 使用 React.createContext() + useContext() |
createFactory | 使用 JSX 或 createElement() |
复选框上的 ChangeEvent<HTMLInputElement> | 改为 FormEvent<HTMLInputElement> |
步骤 9:构建、类型检查、测试
rm -rf node_modules dist
yarn install --ignore-engines 2>&1 | tail -10
yarn build 2>&1 | tail -10
yarn typecheck 2>&1 | tail -10
yarn test --watchAll=false 2>&1 | tail -10
| Error | Fix |
|---|---|
Cannot find module 'react/jsx-runtime' | 未应用步骤 4 — 重新运行 create-plugin add |
Cannot find module '@openfeature/web-sdk' | 步骤 7 — yarn add -D @openfeature/web-sdk @openfeature/core |
Can't resolve '@grafana/i18n' | yarn add @grafana/i18n@^12.2.0 |
Cannot read properties of undefined (reading 'ReactCurrentOwner') | 升级 @grafana-cloud/* 包 — 参见步骤 6 |
仅图标的 Button 缺少 aria-label | 添加 aria-label 属性(较新版 @grafana/ui 要求此属性) |
yarn.lock 中的 Git 哈希过时 | 在 package.json 中添加 resolutions,删除锁文件并重新安装 |
有关已知问题的详细信息(i18n 崩溃、@grafana/schema 类型中断、publicPath 不匹配),请参阅
references/known-issues.md。
步骤 10:更新 CI(如适用)
grep -rn "plugin-ci-workflows\|e2e-version" .github/workflows/ 2>/dev/null
plugin-ci-workflows@main或 >= 6.0.0 → 已测试 React 19。无需更改。plugin-actions/e2e-version→ 添加skip-grafana-react-19-preview-image: false。- 两者均未找到 → 使用
GRAFANA_VERSION=dev-preview-react19 docker compose up --build手动测试。
步骤 11:压缩提交并推送
git reset --soft origin/main
git add -A
git commit -m "fix: Prepare plugin for React 19 compatibility"
提交消息正文应列出:create-plugin 版本变更、迁移到 ESLint 9、关键依赖项升级,以及任何源代码修复。
参考资料
- 迁移指南
- 面向插件开发者的 React 19 博文
- React 19 更新日志
- grafana-collector-app #1337 — 完整迁移,包括 create-plugin 更新和源码修复
- grafana/scenes 问题 — 上游 i18n 跟踪