From c01d1a00fdba76cea38eebc953fd3d2dd3f12fbd Mon Sep 17 00:00:00 2001 From: Daniel Tschinder <231804+danez@users.noreply.github.com> Date: Sun, 17 Sep 2023 17:54:00 +0200 Subject: [PATCH] Fix detecting super class in react class components --- .changeset/eight-coats-tie.md | 6 +++ .../__tests__/isReactComponentClass-test.ts | 37 +++++++++++++++++-- .../src/utils/isReactComponentClass.ts | 28 ++++++++------ 3 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 .changeset/eight-coats-tie.md diff --git a/.changeset/eight-coats-tie.md b/.changeset/eight-coats-tie.md new file mode 100644 index 00000000000..902c2245b07 --- /dev/null +++ b/.changeset/eight-coats-tie.md @@ -0,0 +1,6 @@ +--- +'react-docgen': patch +--- + +Fix detection of react class components when super class is imported via named +import. diff --git a/packages/react-docgen/src/utils/__tests__/isReactComponentClass-test.ts b/packages/react-docgen/src/utils/__tests__/isReactComponentClass-test.ts index 886ba6d8bb0..47d7c2b35af 100644 --- a/packages/react-docgen/src/utils/__tests__/isReactComponentClass-test.ts +++ b/packages/react-docgen/src/utils/__tests__/isReactComponentClass-test.ts @@ -31,23 +31,25 @@ describe('isReactComponentClass', () => { }); test('ignores static render methods', () => { - const def = parse.statement('class Foo { static render() {}}'); + const def = parse.statement('class Foo extends X { static render() {}}'); expect(isReactComponentClass(def)).toBe(false); }); test('ignores dynamic render methods', () => { - const def = parse.statement('class Foo { static [render]() {}}'); + const def = parse.statement( + 'class Foo extends X { static [render]() {}}', + ); expect(isReactComponentClass(def)).toBe(false); }); test('ignores getter or setter render methods', () => { - let def = parse.statement('class Foo { get render() {}}'); + let def = parse.statement('class Foo extends X { get render() {}}'); expect(isReactComponentClass(def)).toBe(false); - def = parse.statement('class Foo { set render(value) {}}'); + def = parse.statement('class Foo extends X { set render(value) {}}'); expect(isReactComponentClass(def)).toBe(false); }); }); @@ -86,6 +88,33 @@ describe('isReactComponentClass', () => { expect(isReactComponentClass(def)).toBe(true); }); + test('resolves the super class reference with named import', () => { + const def = parse.statementLast(` + import { Component } from 'react'; + class Foo extends Component {} + `); + + expect(isReactComponentClass(def)).toBe(true); + }); + + test('resolves the super class reference with default import', () => { + const def = parse.statementLast(` + import React from 'react'; + class Foo extends React.Component {} + `); + + expect(isReactComponentClass(def)).toBe(true); + }); + + test('resolves the super class reference with namespace import', () => { + const def = parse.statementLast(` + import * as React from 'react'; + class Foo extends React.Component {} + `); + + expect(isReactComponentClass(def)).toBe(true); + }); + test('resolves the super class reference with alias', () => { const def = parse.statementLast(` var { Component: C } = require('react'); diff --git a/packages/react-docgen/src/utils/isReactComponentClass.ts b/packages/react-docgen/src/utils/isReactComponentClass.ts index 0f954ad8999..114e2fc66b1 100644 --- a/packages/react-docgen/src/utils/isReactComponentClass.ts +++ b/packages/react-docgen/src/utils/isReactComponentClass.ts @@ -8,6 +8,7 @@ import isReactModuleName from './isReactModuleName.js'; import resolveToModule from './resolveToModule.js'; import resolveToValue from './resolveToValue.js'; import isDestructuringAssignment from './isDestructuringAssignment.js'; +import isImportSpecifier from './isImportSpecifier.js'; function isRenderMethod(path: NodePath): boolean { if ( @@ -35,19 +36,24 @@ function classExtendsReactComponent(path: NodePath): boolean { const property = path.get('property'); if ( - !property.isIdentifier({ name: 'Component' }) && - !property.isIdentifier({ name: 'PureComponent' }) + property.isIdentifier({ name: 'Component' }) || + property.isIdentifier({ name: 'PureComponent' }) ) { - return false; + return true; } } else if ( - !isDestructuringAssignment(path, 'Component') && - !isDestructuringAssignment(path, 'PureComponent') + isImportSpecifier(path, 'Component') || + isImportSpecifier(path, 'PureComponent') ) { - return false; + return true; + } else if ( + isDestructuringAssignment(path, 'Component') || + isDestructuringAssignment(path, 'PureComponent') + ) { + return true; } - return true; + return false; } /** @@ -61,11 +67,6 @@ export default function isReactComponentClass( return false; } - // extends something - if (!path.node.superClass) { - return false; - } - // React.Component or React.PureComponent const superClass = path.get('superClass'); @@ -79,6 +80,9 @@ export default function isReactComponentClass( return true; } } + } else { + // does not extend anything + return false; } // render method