diff --git a/.changeset/four-teachers-tan.md b/.changeset/four-teachers-tan.md new file mode 100644 index 00000000000..a33755e332a --- /dev/null +++ b/.changeset/four-teachers-tan.md @@ -0,0 +1,20 @@ +--- +'react-docgen': major +--- + +`resolveToValue` will not resolve to `ImportDeclaration` anymore but instead to +one of the possible specifiers (`ImportSpecifier`, `ImportDefaultSpecifier` or +`ImportNamespaceSpecifier`). This gives better understanding to which specifier +exactly `resolveToValue` did resolve a NodePath to. + +Here is a possible easy fix for this in a code snippet that uses +`resolveToValue` + +```diff +const resolved = resolveToValue(path); + +-if (resolved.isImportDeclaration()) { ++if (resolved.parentPath?.isImportDeclaration()) { + // do smth +} +``` diff --git a/packages/react-docgen/src/handlers/defaultPropsHandler.ts b/packages/react-docgen/src/handlers/defaultPropsHandler.ts index d1a88218d59..af6acac93c0 100644 --- a/packages/react-docgen/src/handlers/defaultPropsHandler.ts +++ b/packages/react-docgen/src/handlers/defaultPropsHandler.ts @@ -38,7 +38,7 @@ function getDefaultValue(path: NodePath): DefaultValueDescriptor | null { } else { resolvedPath = resolveToValue(path); } - if (resolvedPath.isImportDeclaration() && path.isIdentifier()) { + if (resolvedPath.parentPath?.isImportDeclaration() && path.isIdentifier()) { defaultValue = path.node.name; } else { valuePath = resolvedPath; diff --git a/packages/react-docgen/src/utils/__tests__/isUnreachableFlowType-test.ts b/packages/react-docgen/src/utils/__tests__/isUnreachableFlowType-test.ts index b37fda1f274..243bdd221fc 100644 --- a/packages/react-docgen/src/utils/__tests__/isUnreachableFlowType-test.ts +++ b/packages/react-docgen/src/utils/__tests__/isUnreachableFlowType-test.ts @@ -1,3 +1,4 @@ +import { ImportDeclaration } from '@babel/types'; import { parse } from '../../../tests/utils'; import isUnreachableFlowType from '../isUnreachableFlowType.js'; import { describe, expect, test } from 'vitest'; @@ -7,10 +8,14 @@ describe('isUnreachableFlowType', () => { expect(isUnreachableFlowType(parse.expression('foo'))).toBe(true); }); - test('considers ImportDeclaration as unreachable', () => { - expect(isUnreachableFlowType(parse.statement('import x from "";'))).toBe( - true, - ); + test('considers any ImportSpecifier as unreachable', () => { + expect( + isUnreachableFlowType( + parse + .statement('import x from "";') + .get('specifiers')[0], + ), + ).toBe(true); }); test('considers CallExpression as unreachable', () => { diff --git a/packages/react-docgen/src/utils/__tests__/resolveToValue-test.ts b/packages/react-docgen/src/utils/__tests__/resolveToValue-test.ts index 8d90298e4f5..1a00dde6fd6 100644 --- a/packages/react-docgen/src/utils/__tests__/resolveToValue-test.ts +++ b/packages/react-docgen/src/utils/__tests__/resolveToValue-test.ts @@ -157,7 +157,7 @@ describe('resolveToValue', () => { ); const value = resolveToValue(path); - expect(value.node.type).toBe('ImportDeclaration'); + expect(value.node.type).toBe('ImportDefaultSpecifier'); }); test('resolves unresolvable named import references to the import declaration', () => { @@ -166,7 +166,7 @@ describe('resolveToValue', () => { ); const value = resolveToValue(path); - expect(value.node.type).toBe('ImportDeclaration'); + expect(value.node.type).toBe('ImportSpecifier'); }); test('resolves unresolvable aliased import references to the import declaration', () => { @@ -175,7 +175,7 @@ describe('resolveToValue', () => { ); const value = resolveToValue(path); - expect(value.node.type).toBe('ImportDeclaration'); + expect(value.node.type).toBe('ImportSpecifier'); }); test('resolves unresolvable namespace import references to the import declaration', () => { @@ -184,7 +184,7 @@ describe('resolveToValue', () => { ); const value = resolveToValue(path); - expect(value.node.type).toBe('ImportDeclaration'); + expect(value.node.type).toBe('ImportNamespaceSpecifier'); }); test('resolves namespace import references to the import declaration', () => { diff --git a/packages/react-docgen/src/utils/getPropType.ts b/packages/react-docgen/src/utils/getPropType.ts index a118517854a..07b4ccfd0e6 100644 --- a/packages/react-docgen/src/utils/getPropType.ts +++ b/packages/react-docgen/src/utils/getPropType.ts @@ -37,7 +37,9 @@ function getEnumValuesFromArrayExpression( const value = resolveToValue(elementPath as NodePath); return values.push({ - value: printValue(value.isImportDeclaration() ? elementPath : value), + value: printValue( + value.parentPath?.isImportDeclaration() ? elementPath : value, + ), computed: !value.isLiteral(), }); }); diff --git a/packages/react-docgen/src/utils/isUnreachableFlowType.ts b/packages/react-docgen/src/utils/isUnreachableFlowType.ts index 61dc6d975d8..bfed1bfcc19 100644 --- a/packages/react-docgen/src/utils/isUnreachableFlowType.ts +++ b/packages/react-docgen/src/utils/isUnreachableFlowType.ts @@ -2,9 +2,12 @@ import type { NodePath } from '@babel/traverse'; /** * Returns true of the path is an unreachable TypePath + * This evaluates the NodePaths returned from resolveToValue */ export default (path: NodePath): boolean => { return ( - path.isIdentifier() || path.isImportDeclaration() || path.isCallExpression() + path.isIdentifier() || + path.parentPath?.isImportDeclaration() || + path.isCallExpression() ); }; diff --git a/packages/react-docgen/src/utils/resolveToModule.ts b/packages/react-docgen/src/utils/resolveToModule.ts index b1d55ccb7e4..a2be996c464 100644 --- a/packages/react-docgen/src/utils/resolveToModule.ts +++ b/packages/react-docgen/src/utils/resolveToModule.ts @@ -32,8 +32,8 @@ export default function resolveToModule(path: NodePath): string | null { } } else if (path.isObjectProperty() || path.isObjectPattern()) { return resolveToModule(path.parentPath); - } else if (path.isImportDeclaration()) { - return path.node.source.value; + } else if (path.parentPath?.isImportDeclaration()) { + return path.parentPath.node.source.value; } else if (path.isMemberExpression()) { path = getMemberExpressionRoot(path); diff --git a/packages/react-docgen/src/utils/resolveToValue.ts b/packages/react-docgen/src/utils/resolveToValue.ts index 8bdff855768..d296fa089f9 100644 --- a/packages/react-docgen/src/utils/resolveToValue.ts +++ b/packages/react-docgen/src/utils/resolveToValue.ts @@ -188,11 +188,17 @@ export default function resolveToValue(path: NodePath): NodePath { return resolveToValue(memberPath); } } - } else if (resolved.isImportDeclaration() && resolved.node.specifiers) { + } else if ( + resolved.isImportSpecifier() || + resolved.isImportDefaultSpecifier() || + resolved.isImportNamespaceSpecifier() + ) { + const declaration = resolved.parentPath as NodePath; + // Handle references to namespace imports, e.g. import * as foo from 'bar'. // Try to find a specifier that matches the root of the member expression, and // find the export that matches the property name. - for (const specifier of resolved.get('specifiers')) { + for (const specifier of declaration.get('specifiers')) { const property = path.get('property'); let propertyName: string | undefined; @@ -206,7 +212,7 @@ export default function resolveToValue(path: NodePath): NodePath { propertyName && specifier.node.local.name === root.node.name ) { - const resolvedPath = path.hub.import(resolved, propertyName); + const resolvedPath = path.hub.import(declaration, propertyName); if (resolvedPath) { return resolveToValue(resolvedPath); @@ -214,13 +220,6 @@ export default function resolveToValue(path: NodePath): NodePath { } } } - } else if ( - path.isImportDefaultSpecifier() || - path.isImportNamespaceSpecifier() || - path.isImportSpecifier() - ) { - // go up to the import declaration - return path.parentPath; } else if ( path.isTypeCastExpression() || path.isTSAsExpression() ||