Bug description
When a class with a PascalCase name is exported via a named export list (export { ClassName }), the plugin incorrectly treats it as a React component. This causes all other non-component exports in the same file to be flagged with the namedExport warning.
Minimal reproduction
// helpers.jsx
class TimeObject {
constructor() {
this.years = {};
this.total = 0;
}
}
const calcAmount = () => { /* ... */ };
export { calcAmount, TimeObject };
// ^^^^^^^^^^ incorrectly flagged
// TimeObject is treated as a component, triggering the rule on calcAmount
Root cause
In handleExportIdentifier, when called without initParam (i.e. from an export { Name } statement rather than an inline export declaration), the plugin falls back to name-only detection:
if (!initParam) {
if (reactComponentNameRE.test(identifierNode.name)) {
hasReactExport = true; // PascalCase → assumed to be a component
} else {
nonComponentExports.push(identifierNode);
}
return;
}
TimeObject matches reactComponentNameRE, so hasReactExport becomes true, which causes calcAmount to be reported as a non-component export mixed with a component export.
Why the inline form works correctly
export class TimeObject {} goes through handleExportDeclaration → ClassDeclaration branch, which correctly checks for superClass !== null and a render method before considering it a React component. The export { } form bypasses this check entirely.
Relation to previous fixes
This is similar to #71 (PascalCase constant treated as component). The fix in #75 addressed the case of local VariableDeclaration by adding a canBeReactFunctionComponent check, but the export { Name } path (no initParam) was not updated and still relies solely on the name.
Expected behavior
export { TimeObject } where TimeObject is a class (not a function returning JSX) should be treated as a non-component export, consistent with how export class TimeObject {} is handled.
Workarounds
- Rename the file from
.jsx to .js (suppresses the rule entirely)
- Use
export class TimeObject {} inline instead of export { TimeObject }
Version
eslint-plugin-react-refresh@0.5.2
Bug description
When a class with a PascalCase name is exported via a named export list (
export { ClassName }), the plugin incorrectly treats it as a React component. This causes all other non-component exports in the same file to be flagged with thenamedExportwarning.Minimal reproduction
Root cause
In
handleExportIdentifier, when called withoutinitParam(i.e. from anexport { Name }statement rather than an inline export declaration), the plugin falls back to name-only detection:TimeObjectmatchesreactComponentNameRE, sohasReactExportbecomestrue, which causescalcAmountto be reported as a non-component export mixed with a component export.Why the inline form works correctly
export class TimeObject {}goes throughhandleExportDeclaration→ClassDeclarationbranch, which correctly checks forsuperClass !== nulland arendermethod before considering it a React component. Theexport { }form bypasses this check entirely.Relation to previous fixes
This is similar to #71 (PascalCase constant treated as component). The fix in #75 addressed the case of local
VariableDeclarationby adding acanBeReactFunctionComponentcheck, but theexport { Name }path (noinitParam) was not updated and still relies solely on the name.Expected behavior
export { TimeObject }whereTimeObjectis a class (not a function returning JSX) should be treated as a non-component export, consistent with howexport class TimeObject {}is handled.Workarounds
.jsxto.js(suppresses the rule entirely)export class TimeObject {}inline instead ofexport { TimeObject }Version
eslint-plugin-react-refresh@0.5.2