Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perf improvements #504

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
122 changes: 73 additions & 49 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ export class Parser {
private readonly savePropValueAsString: boolean;
private readonly shouldIncludePropTagMap: boolean;
private readonly shouldIncludeExpression: boolean;
private propertiesOfPropsCache: Map<string, PropItem> = new Map();
private componentsInfoCache: Map<string, ComponentDoc | null> = new Map();

constructor(program: ts.Program, opts: ParserOptions) {
const {
Expand Down Expand Up @@ -299,11 +301,17 @@ export class Parser {
const typeSymbol = type.symbol || type.aliasSymbol;
const originalName = rootExp.getName();
const filePath = source.fileName;
const cacheKey = `${filePath}_${originalName}`;

if (this.componentsInfoCache.has(cacheKey)) {
return this.componentsInfoCache.get(cacheKey) as ComponentDoc | null;
}

if (!rootExp.valueDeclaration) {
if (!typeSymbol && (rootExp.flags & ts.SymbolFlags.Alias) !== 0) {
commentSource = this.checker.getAliasedSymbol(commentSource);
} else if (!typeSymbol) {
this.componentsInfoCache.set(cacheKey, null);
return null;
} else {
rootExp = typeSymbol;
Expand Down Expand Up @@ -346,6 +354,7 @@ export class Parser {
(typeSymbol.getEscapedName() === 'Requireable' ||
typeSymbol.getEscapedName() === 'Validator')
) {
this.componentsInfoCache.set(cacheKey, null);
return null;
}

Expand All @@ -365,6 +374,7 @@ export class Parser {
let result: ComponentDoc | null = null;
if (propsType) {
if (!commentSource.valueDeclaration) {
this.componentsInfoCache.set(cacheKey, null);
return null;
}
const defaultProps = this.extractDefaultPropsFromComponent(
Expand Down Expand Up @@ -404,6 +414,7 @@ export class Parser {
result.rootExpression = exp;
}

this.componentsInfoCache.set(cacheKey, result);
return result;
}

Expand Down Expand Up @@ -701,60 +712,73 @@ export class Parser {

propertiesOfProps.forEach(prop => {
const propName = prop.getName();
const parent = getParentType(prop);
const cacheKey = `${parent?.fileName}_${propName}`;
if (this.propertiesOfPropsCache.has(cacheKey)) {
result[propName] = this.propertiesOfPropsCache.get(
cacheKey
) as PropItem;
} else {
// Find type of prop by looking in context of the props object itself.
const propType = this.checker.getTypeOfSymbolAtLocation(
prop,
propsObj.valueDeclaration!
);

// Find type of prop by looking in context of the props object itself.
const propType = this.checker.getTypeOfSymbolAtLocation(
prop,
propsObj.valueDeclaration!
);
const jsDocComment = this.findDocComment(prop);
const hasCodeBasedDefault = defaultProps[propName] !== undefined;

const jsDocComment = this.findDocComment(prop);
const hasCodeBasedDefault = defaultProps[propName] !== undefined;
let defaultValue: { value: any } | null = null;

let defaultValue: { value: any } | null = null;
if (hasCodeBasedDefault) {
defaultValue = { value: defaultProps[propName] };
} else if (jsDocComment.tags.default) {
defaultValue = { value: jsDocComment.tags.default };
}

if (hasCodeBasedDefault) {
defaultValue = { value: defaultProps[propName] };
} else if (jsDocComment.tags.default) {
defaultValue = { value: jsDocComment.tags.default };
}
const parents = getDeclarations(prop);
const declarations = prop.declarations || [];
const baseProp = baseProps.find(p => p.getName() === propName);

const parent = getParentType(prop);
const parents = getDeclarations(prop);
const declarations = prop.declarations || [];
const baseProp = baseProps.find(p => p.getName() === propName);

const required =
!isOptional(prop) &&
!hasCodeBasedDefault &&
// If in a intersection or union check original declaration for "?"
// @ts-ignore
declarations.every(d => !d.questionToken) &&
(!baseProp || !isOptional(baseProp));

const type = jsDocComment.tags.type
? {
name: jsDocComment.tags.type
}
: this.getDocgenType(propType, required);

const propTags = this.shouldIncludePropTagMap
? { tags: jsDocComment.tags }
: {};
const description = this.shouldIncludePropTagMap
? jsDocComment.description.replace(/\r\n/g, '\n')
: jsDocComment.fullComment.replace(/\r\n/g, '\n');

result[propName] = {
defaultValue,
description: description,
name: propName,
parent,
declarations: parents,
required,
type,
...propTags
};
const required =
!isOptional(prop) &&
!hasCodeBasedDefault &&
// If in a intersection or union check original declaration for "?"
// @ts-ignore
declarations.every(d => !d.questionToken) &&
(!baseProp || !isOptional(baseProp));

const type = jsDocComment.tags.type
? {
name: jsDocComment.tags.type
}
: this.getDocgenType(propType, required);

const propTags = this.shouldIncludePropTagMap
? { tags: jsDocComment.tags }
: {};
const description = this.shouldIncludePropTagMap
? jsDocComment.description.replace(/\r\n/g, '\n')
: jsDocComment.fullComment.replace(/\r\n/g, '\n');

const propItem: PropItem = {
defaultValue,
description: description,
name: propName,
parent,
declarations: parents,
required,
type,
...propTags
};
if (parent?.fileName.includes('node_modules')) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put it like this at first, because I saw a ton of types coming from @types/react for all the native props. But I guess we could also cache user defined types in here as well.

this.propertiesOfPropsCache.set(
`${parent.fileName}_${propName}`,
propItem
);
}
result[propName] = propItem;
}
});

return result;
Expand Down
20 changes: 11 additions & 9 deletions src/trimFileName.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as path from 'path';

const slashRegex = /[\\/]/g;

const fileNameCache = new Map<string, string>();
export function trimFileName(
fileName: string,
cwd: string = process.cwd(),
platform?: 'posix' | 'win32'
) {
): string {
if (fileNameCache.has(fileName)) return fileNameCache.get(fileName) as string;
// This allows tests to run regardless of current platform
const pathLib = platform ? path[platform] : path;

Expand All @@ -24,17 +25,18 @@ export function trimFileName(
let parent = cwd;
do {
if (normalizedFileName.startsWith(parent)) {
return (
pathLib
// Preserve the parent directory name to match existing behavior
.relative(pathLib.dirname(parent), normalizedFileName)
// Restore original type of slashes
.replace(slashRegex, originalSep)
);
const finalPathName = pathLib
// Preserve the parent directory name to match existing behavior
.relative(pathLib.dirname(parent), normalizedFileName)
// Restore original type of slashes
.replace(slashRegex, originalSep);
fileNameCache.set(fileName, finalPathName);
return finalPathName;
}
parent = pathLib.dirname(parent);
} while (parent !== root);

fileNameCache.set(fileName, fileName);
// No common ancestor, so return the path as-is
return fileName;
}