Skip to content

Commit

Permalink
resolveToValue resolves to ImportSpecifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
danez committed Sep 17, 2023
1 parent 0641700 commit 62e692f
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 23 deletions.
20 changes: 20 additions & 0 deletions .changeset/four-teachers-tan.md
Original file line number Diff line number Diff line change
@@ -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
}
```
2 changes: 1 addition & 1 deletion packages/react-docgen/src/handlers/defaultPropsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<ImportDeclaration>('import x from "";')
.get('specifiers')[0],
),
).toBe(true);
});

test('considers CallExpression as unreachable', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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', () => {
Expand All @@ -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', () => {
Expand All @@ -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', () => {
Expand Down
4 changes: 3 additions & 1 deletion packages/react-docgen/src/utils/getPropType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ function getEnumValuesFromArrayExpression(
const value = resolveToValue(elementPath as NodePath<Expression>);

return values.push({
value: printValue(value.isImportDeclaration() ? elementPath : value),
value: printValue(
value.parentPath?.isImportDeclaration() ? elementPath : value,
),
computed: !value.isLiteral(),
});
});
Expand Down
5 changes: 4 additions & 1 deletion packages/react-docgen/src/utils/isUnreachableFlowType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
);
};
4 changes: 2 additions & 2 deletions packages/react-docgen/src/utils/resolveToModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
19 changes: 9 additions & 10 deletions packages/react-docgen/src/utils/resolveToValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ImportDeclaration>;

// 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;

Expand All @@ -206,21 +212,14 @@ 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);
}
}
}
}
} else if (
path.isImportDefaultSpecifier() ||
path.isImportNamespaceSpecifier() ||
path.isImportSpecifier()
) {
// go up to the import declaration
return path.parentPath;
} else if (
path.isTypeCastExpression() ||
path.isTSAsExpression() ||
Expand Down

0 comments on commit 62e692f

Please sign in to comment.