Skip to content

React19: Add a validator to check plugin compatibility#552

Open
leventebalogh wants to merge 1 commit intomainfrom
leventebalogh/react19-compat-check
Open

React19: Add a validator to check plugin compatibility#552
leventebalogh wants to merge 1 commit intomainfrom
leventebalogh/react19-compat-check

Conversation

@leventebalogh
Copy link
Copy Markdown
Contributor

@leventebalogh leventebalogh commented Mar 30, 2026

What changed?

This PR is trying to add a new validator that is checking if the plugin is compatible with React 19.
It checks:

  • removed PropTypes/defaultProps
  • legacy context API
  • string refs
  • ReactDOM.render/findDOMNode
  • legacy lifecycle methods (componentWillMount etc.)
  • React.createFactory

The rules emit warnings (not errors) so the check is advisory at this point.

Scan plugin module.js bundles for patterns that indicate incompatibility
with React 19: removed PropTypes/defaultProps, legacy context, string
refs, ReactDOM.render, ReactDOM.findDOMNode, legacy lifecycle methods,
and removed React.createFactory.
@cla-assistant
Copy link
Copy Markdown

cla-assistant bot commented Mar 30, 2026

CLA assistant check
All committers have signed the CLA.

@leventebalogh leventebalogh changed the title feat: add React 19 compatibility checker for plugins React19: Add a validator to check plugin compatibility Mar 30, 2026
@leventebalogh leventebalogh self-assigned this Mar 30, 2026
@leventebalogh leventebalogh moved this from 📬 Triage to 🔬 In review in Grafana Catalog Team Mar 30, 2026
Comment on lines +83 to +144
var reactPatterns = []reactPattern{
{
rule: react19PropTypes,
title: "module.js: Uses removed React API propTypes or defaultProps",
description: "Detected usage of '%s'. propTypes and defaultProps on function components were removed in React 19.",
detectors: []detector{
&containsBytesDetector{pattern: []byte(".propTypes=")},
&containsBytesDetector{pattern: []byte(".defaultProps=")},
},
},
{
rule: react19LegacyContext,
title: "module.js: Uses removed React legacy context API",
description: "Detected usage of '%s'. contextTypes, childContextTypes, and getChildContext were removed in React 19.",
detectors: []detector{
&containsBytesDetector{pattern: []byte(".contextTypes=")},
&containsBytesDetector{pattern: []byte(".childContextTypes=")},
&containsBytesDetector{pattern: []byte("getChildContext")},
},
},
{
rule: react19StringRefs,
title: "module.js: Uses removed React string refs",
description: "Detected usage of '%s'. String refs were removed in React 19. Use callback refs or React.createRef() instead.",
detectors: []detector{
&regexDetector{regex: regexp.MustCompile(`ref:"[^"]+?"`)},
&regexDetector{regex: regexp.MustCompile(`ref:'[^']+'`)},
},
},
{
rule: react19CreateFactory,
title: "module.js: Uses removed React.createFactory",
description: "Detected usage of '%s'. React.createFactory was removed in React 19. Use JSX instead.",
detectors: []detector{
&containsBytesDetector{pattern: []byte("createFactory(")},
},
},
{
rule: react19FindDOMNode,
title: "module.js: Uses removed ReactDOM.findDOMNode",
description: "Detected usage of '%s'. ReactDOM.findDOMNode was removed in React 19. Use DOM refs instead.",
detectors: []detector{
&containsBytesDetector{pattern: []byte("findDOMNode(")},
},
},
{
rule: react19LegacyRender,
title: "module.js: Uses removed ReactDOM.render or unmountComponentAtNode",
description: "Detected usage of '%s'. ReactDOM.render and unmountComponentAtNode were removed in React 19. Use createRoot instead.",
detectors: []detector{
&containsBytesDetector{pattern: []byte("ReactDOM.render(")},
&containsBytesDetector{pattern: []byte("unmountComponentAtNode(")},
},
},
{
rule: react19SecretInternals,
title: "module.js: Uses React internal __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED",
description: "Detected usage of '%s'. This internal was removed in React 19.",
detectors: []detector{
&containsBytesDetector{pattern: []byte("__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED")},
},
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As far as I can see, these could be semgrep rules? If that's the case there is no need to create a new rule

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

+1 all this can work with semgrep and we even have already one for this case

- pattern-regex: __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED

Copy link
Copy Markdown
Collaborator

@academo academo left a comment

Choose a reason for hiding this comment

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

I think most if not all of this check can be a semgrep rule.

@leventebalogh
Copy link
Copy Markdown
Contributor Author

That's a totally good point, thanks guys, I'll run another circle with this 👍

Copy link
Copy Markdown

@sunker sunker left a comment

Choose a reason for hiding this comment

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

Just want to flag that Jack's @grafana/react-detect package does exactly this kind of analysis but with a few extras rules. It's published to npm and can be invoked with npx @grafana/react-detect. It also has a --json output mode that could work well for feeding results back into the validator. I think we should use this instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 🔬 In review

Development

Successfully merging this pull request may close these issues.

4 participants