From fdc550197a75a6a1540f7da510f48e9bf3a596ce Mon Sep 17 00:00:00 2001 From: Adam Butterfield Date: Mon, 3 Jun 2024 13:11:06 +0900 Subject: [PATCH 1/3] add trimFileName cache --- src/trimFileName.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/trimFileName.ts b/src/trimFileName.ts index 011853e2..372d3f57 100644 --- a/src/trimFileName.ts +++ b/src/trimFileName.ts @@ -1,12 +1,13 @@ import * as path from 'path'; const slashRegex = /[\\/]/g; - +const fileNameCache = new Map(); 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; @@ -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; } From 4d140729d6c8a4d8ad8536dceae9cc0029510c59 Mon Sep 17 00:00:00 2001 From: Adam Butterfield Date: Mon, 3 Jun 2024 13:11:47 +0900 Subject: [PATCH 2/3] add propertiesOfProps cache --- src/parser.ts | 112 ++++++++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/src/parser.ts b/src/parser.ts index d21f31f9..87c3e8b3 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -225,6 +225,7 @@ export class Parser { private readonly savePropValueAsString: boolean; private readonly shouldIncludePropTagMap: boolean; private readonly shouldIncludeExpression: boolean; + private propertiesOfPropsCache: Map = new Map(); constructor(program: ts.Program, opts: ParserOptions) { const { @@ -701,60 +702,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')) { + this.propertiesOfPropsCache.set( + `${parent.fileName}_${propName}`, + propItem + ); + } + result[propName] = propItem; + } }); return result; From fea6da1dc3dc72481d82fdfe283f2188da84750b Mon Sep 17 00:00:00 2001 From: Adam Butterfield Date: Mon, 3 Jun 2024 13:13:26 +0900 Subject: [PATCH 3/3] add componentsInfo cache --- src/parser.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/parser.ts b/src/parser.ts index 87c3e8b3..9191b7fb 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -226,6 +226,7 @@ export class Parser { private readonly shouldIncludePropTagMap: boolean; private readonly shouldIncludeExpression: boolean; private propertiesOfPropsCache: Map = new Map(); + private componentsInfoCache: Map = new Map(); constructor(program: ts.Program, opts: ParserOptions) { const { @@ -300,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; @@ -347,6 +354,7 @@ export class Parser { (typeSymbol.getEscapedName() === 'Requireable' || typeSymbol.getEscapedName() === 'Validator') ) { + this.componentsInfoCache.set(cacheKey, null); return null; } @@ -366,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( @@ -405,6 +414,7 @@ export class Parser { result.rootExpression = exp; } + this.componentsInfoCache.set(cacheKey, result); return result; }