Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ export default tseslint.config([
],

'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/sort-type-constituents': 'error',
},
},
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
},
"scripts": {
"build": "nx run-many --target=build --exclude='@react-docgen-internal/*'",
"lint": "eslint . --flag v10_config_lookup_from_file --report-unused-disable-directives --max-warnings=0",
"fix": "eslint . --flag v10_config_lookup_from_file --fix --report-unused-disable-directives --max-warnings=0",
"lint": "eslint . --report-unused-disable-directives --max-warnings=0",
"fix": "eslint . --fix --report-unused-disable-directives --max-warnings=0",
"test": "nx run-many --target=test --exclude='@react-docgen-internal/*' --output-style=stream",
"copy:changelog:react-docgen": "cp ./packages/react-docgen/CHANGELOG.md ./packages/website/src/app/docs/release-notes/react-docgen/page.mdx",
"copy:changelog:cli": "cp ./packages/react-docgen-cli/CHANGELOG.md ./packages/website/src/app/docs/release-notes/cli/page.mdx",
Expand All @@ -19,11 +19,11 @@
"@changesets/changelog-github": "0.6.0",
"@changesets/cli": "2.30.0",
"@eslint/eslintrc": "3.3.5",
"@eslint/js": "9.39.4",
"@eslint/js": "10.0.1",
"@types/node": "20.19.37",
"@vitest/coverage-v8": "4.1.0",
"cpy": "13.2.1",
"eslint": "9.39.4",
"eslint": "10.0.3",
"eslint-config-next": "16.1.6",
"eslint-config-prettier": "10.1.8",
"eslint-plugin-prettier": "5.5.5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ export default async function loadReactDocgenPlugin<T>(
name: string,
builtins?: Record<string, T>,
): Promise<T> {
if (builtins?.[input]) {
return builtins[input]!;
const builtin = builtins?.[input];

if (builtin !== undefined) {
return builtin;
}

const path = resolve(process.cwd(), input);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,11 @@ export default async function loadResolvers(
});
}

return loadResolver(input[0]!);
const [resolver] = input;

if (resolver === undefined) {
return;
}

return loadResolver(resolver);
}
13 changes: 9 additions & 4 deletions packages/react-docgen/src/handlers/componentDocblockHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ function getDocblockFromComponent(path: NodePath): string | null {
// If we have a class declaration or expression, then the comment might be
// attached to the last decorator instead as trailing comment.
if (decorators && decorators.length > 0) {
description = getDocblock(decorators[decorators.length - 1]!, true);
const lastDecorator = decorators[decorators.length - 1];

if (lastDecorator) {
description = getDocblock(lastDecorator, true);
}
}
}
if (description == null) {
Expand All @@ -41,9 +45,10 @@ function getDocblockFromComponent(path: NodePath): string | null {
}
}
if (!description) {
const searchPath = isReactForwardRefCall(path)
? path.get('arguments')[0]!
: path;
const [forwardRefArgument] = isReactForwardRefCall(path)
? path.get('arguments')
: [];
const searchPath = forwardRefArgument ?? path;
const inner = resolveToValue(searchPath);

if (inner.node !== path.node) {
Expand Down
8 changes: 6 additions & 2 deletions packages/react-docgen/src/handlers/componentMethodsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,13 @@ function findStatelessComponentBody(
return body;
}
} else if (isReactForwardRefCall(componentDefinition)) {
const inner = resolveToValue(componentDefinition.get('arguments')[0]!);
const [forwardRefArgument] = componentDefinition.get('arguments');

return findStatelessComponentBody(inner);
if (forwardRefArgument) {
const inner = resolveToValue(forwardRefArgument);

return findStatelessComponentBody(inner);
}
}

return undefined;
Expand Down
11 changes: 9 additions & 2 deletions packages/react-docgen/src/handlers/defaultPropsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import type { Handler } from './index.js';

function getDefaultValue(path: NodePath): DefaultValueDescriptor | null {
let defaultValue: string | undefined;
let resolvedPath = path;
let valuePath = path;

if (path.isBooleanLiteral()) {
Expand All @@ -33,6 +32,8 @@ function getDefaultValue(path: NodePath): DefaultValueDescriptor | null {
} else if (path.isLiteral()) {
defaultValue = path.node.extra?.raw as string;
} else {
let resolvedPath: NodePath;

if (path.isAssignmentPattern()) {
resolvedPath = resolveToValue(path.get('right'));
} else {
Expand Down Expand Up @@ -64,7 +65,13 @@ function getStatelessPropsPath(
let value: NodePath = componentDefinition;

if (isReactForwardRefCall(componentDefinition)) {
value = resolveToValue(componentDefinition.get('arguments')[0]!);
const [forwardRefArgument] = componentDefinition.get('arguments');

if (!forwardRefArgument) {
return undefined;
}

value = resolveToValue(forwardRefArgument);
}

if (!value.isFunction()) {
Expand Down
12 changes: 10 additions & 2 deletions packages/react-docgen/src/utils/docblock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ export function getDocblock(path: NodePath, trailing = false): string | null {
}

if (comments.length > 0) {
return parseDocblock(comments[comments.length - 1]!.value);
const lastComment = comments[comments.length - 1];

if (lastComment) {
return parseDocblock(lastComment.value);
}
}

return null;
Expand All @@ -50,7 +54,11 @@ export function getDoclets(str: string): Record<string, string> {
let match: RegExpExecArray | null;

while ((match = DOCLET_PATTERN.exec(str))) {
doclets[match[1]!] = match[2] || true;
const [, name, value] = match;

if (name) {
doclets[name] = value || true;
}
}

return doclets;
Expand Down
8 changes: 7 additions & 1 deletion packages/react-docgen/src/utils/findComponentDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ function resolveComponentDefinition(
): NodePath<ComponentNode> | null {
if (isReactCreateClassCall(definition)) {
// return argument
const resolvedPath = resolveToValue(definition.get('arguments')[0]!);
const [argument] = definition.get('arguments');

if (!argument) {
return null;
}

const resolvedPath = resolveToValue(argument);

if (resolvedPath.isObjectExpression()) {
return resolvedPath;
Expand Down
24 changes: 14 additions & 10 deletions packages/react-docgen/src/utils/getFlowType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,15 +182,13 @@ function handleGenericTypeAnnotation(
);
}

if (
typeParams &&
typeParams[type.name] &&
typeParams[type.name]!.isGenericTypeAnnotation()
) {
const resolvedTypeParam = typeParams?.[type.name];

if (resolvedTypeParam?.isGenericTypeAnnotation()) {
return type;
}

if (typeParams && typeParams[type.name]) {
if (resolvedTypeParam) {
type = getFlowTypeWithResolvedTypes(
resolvedPath as NodePath<FlowType>,
typeParams,
Expand Down Expand Up @@ -482,8 +480,10 @@ function getFlowTypeWithResolvedTypes(
visitedTypes[parent.node.id.name] = true;
}

if (path.node.type in flowTypes) {
type = { name: flowTypes[path.node.type]! };
const primitiveFlowType = flowTypes[path.node.type];

if (primitiveFlowType) {
type = { name: primitiveFlowType };
} else if (path.node.type in flowLiteralTypes) {
type = {
name: 'literal',
Expand All @@ -499,8 +499,12 @@ function getFlowTypeWithResolvedTypes(
).node.value
}`,
};
} else if (path.node.type in namedTypes) {
type = namedTypes[path.node.type]!(path, typeParams);
} else {
const typeHandler = namedTypes[path.node.type];

if (typeHandler) {
type = typeHandler(path, typeParams);
}
}

if (!type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ function resolveName(path: NodePath): string | undefined {
);
}
// VariableDeclarator always has at least one declaration, hence the non-null-assertion
const id = declarations[0]!.get('id');
const [declaration] = declarations;

if (!declaration) {
return;
}

const id = declaration.get('id');

if (id.isIdentifier()) {
return id.node.name;
Expand Down
14 changes: 10 additions & 4 deletions packages/react-docgen/src/utils/getTSType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -526,10 +526,16 @@ function getTSTypeWithResolvedTypes(
visitedTypes[typeAliasName] = true;
}

if (node.type in tsTypes) {
type = { name: tsTypes[node.type]! };
} else if (node.type in namedTypes) {
type = namedTypes[node.type]!(path, typeParams);
const primitiveTSType = tsTypes[node.type];

if (primitiveTSType) {
type = { name: primitiveTSType };
} else {
const typeHandler = namedTypes[node.type];

if (typeHandler) {
type = typeHandler(path, typeParams);
}
}

if (!type) {
Expand Down
14 changes: 10 additions & 4 deletions packages/react-docgen/src/utils/getTypeFromReactComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ export default (componentDefinition: NodePath): NodePath[] => {
const params = superTypes.get('params');

if (params.length >= 1) {
typePaths.push(params[params.length === 3 ? 1 : 0]!);
const propsTypePath = params[params.length === 3 ? 1 : 0];

if (propsTypePath) {
typePaths.push(propsTypePath);
}
}
} else {
const propsMemberPath = getMemberValuePath(componentDefinition, 'props');
Expand All @@ -122,9 +126,11 @@ export default (componentDefinition: NodePath): NodePath[] => {
typePaths.push(genericTypeAnnotation);
}

componentDefinition = resolveToValue(
componentDefinition.get('arguments')[0]!,
);
const [forwardRefArgument] = componentDefinition.get('arguments');

if (forwardRefArgument) {
componentDefinition = resolveToValue(forwardRefArgument);
}
}

const propsParam = getStatelessPropsPath(componentDefinition);
Expand Down
13 changes: 6 additions & 7 deletions packages/react-docgen/src/utils/getTypeParameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,12 @@ export default function getTypeParameters(
typeName = resolvedTypePath.get('id');
}

if (
typeName &&
inputParams &&
typeName.isIdentifier() &&
inputParams[typeName.node.name]
) {
resolvedTypePath = inputParams[typeName.node.name]!;
if (typeName && inputParams && typeName.isIdentifier()) {
const inputParam = inputParams[typeName.node.name];

if (inputParam) {
resolvedTypePath = inputParam;
}
}

params[key] = resolvedTypePath;
Expand Down
37 changes: 20 additions & 17 deletions packages/react-docgen/src/utils/resolveHOC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,30 @@ export default function resolveHOC(path: NodePath): NodePath {
!isReactCreateClassCall(path) &&
!isReactForwardRefCall(path)
) {
const node = path.node;
const argumentLength = node.arguments.length;
const args = path.get('arguments');
const [firstArg] = args;

if (argumentLength && argumentLength > 0) {
const args = path.get('arguments');
const firstArg = args[0]!;
if (!firstArg) {
return path;
}

// If the first argument is one of these types then the component might be the last argument
// If there are all identifiers then we cannot figure out exactly and have to assume it is the first
if (
argumentLength > 1 &&
(firstArg.isLiteral() ||
firstArg.isObjectExpression() ||
firstArg.isArrayExpression() ||
firstArg.isSpreadElement())
) {
return resolveHOC(resolveToValue(args[argumentLength - 1]!));
}
// If the first argument is one of these types then the component might be the last argument
// If there are all identifiers then we cannot figure out exactly and have to assume it is the first
if (
args.length > 1 &&
(firstArg.isLiteral() ||
firstArg.isObjectExpression() ||
firstArg.isArrayExpression() ||
firstArg.isSpreadElement())
) {
const lastArg = args[args.length - 1];

return resolveHOC(resolveToValue(firstArg));
if (lastArg) {
return resolveHOC(resolveToValue(lastArg));
}
}

return resolveHOC(resolveToValue(firstArg));
}

return path;
Expand Down
12 changes: 7 additions & 5 deletions packages/react-docgen/src/utils/resolveObjectKeysToArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,13 @@ export default function resolveObjectKeysToArray(
path: NodePath,
): string[] | null {
if (isObjectKeysCall(path)) {
const argument = path.get('arguments')[0];
const objectExpression = resolveToValue(
// isObjectKeysCall already asserts that there is at least one argument, hence the non-null-assertion
argument!,
);
const [argument] = path.get('arguments');

if (!argument) {
return null;
}

const objectExpression = resolveToValue(argument);
const values = resolveObjectToNameArray(objectExpression);

if (values) {
Expand Down
12 changes: 7 additions & 5 deletions packages/react-docgen/src/utils/resolveObjectValuesToArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,13 @@ export default function resolveObjectValuesToArray(
path: NodePath,
): string[] | null {
if (isObjectValuesCall(path)) {
const argument = path.get('arguments')[0];
const objectExpression = resolveToValue(
// isObjectValuesCall already asserts that there is at least one argument, hence the non-null-assertion
argument!,
);
const [argument] = path.get('arguments');

if (!argument) {
return null;
}

const objectExpression = resolveToValue(argument);
const values = resolveObjectToPropMap(objectExpression);

if (values) {
Expand Down
Loading
Loading