UNPKG

@compodoc/compodoc

Version:

The missing documentation tool for your Angular application

407 lines (358 loc) 14.2 kB
import { SyntaxKind, ts } from 'ts-morph'; import { detectIndent, getSubstringFromMultilineString } from '../../../../../utils'; import { ClassHelper } from './class-helper'; import { IParseDeepIdentifierResult, SymbolHelper } from './symbol-helper'; export class ComponentHelper { constructor( private classHelper: ClassHelper, private symbolHelper: SymbolHelper = new SymbolHelper() ) {} public getComponentChangeDetection( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string { return this.symbolHelper.getSymbolDeps(props, 'changeDetection', srcFile).pop(); } public getComponentEncapsulation( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): Array<string> { return this.symbolHelper.getSymbolDeps(props, 'encapsulation', srcFile); } public getComponentPure( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string { return this.symbolHelper.getSymbolDeps(props, 'pure', srcFile).pop(); } public getComponentName( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string { return this.symbolHelper.getSymbolDeps(props, 'name', srcFile).pop(); } public getComponentExportAs( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string { return this.symbolHelper.getSymbolDeps(props, 'exportAs', srcFile).pop(); } public getComponentHostDirectives( props: ReadonlyArray<ts.ObjectLiteralElementLike> ): Array<any> { const hostDirectiveSymbolParsed = this.symbolHelper.getSymbolDepsRaw( props, 'hostDirectives' ); let hostDirectiveSymbol = null; if (hostDirectiveSymbolParsed.length > 0) { hostDirectiveSymbol = hostDirectiveSymbolParsed.pop(); } const result = []; if ( hostDirectiveSymbol && hostDirectiveSymbol.initializer && hostDirectiveSymbol.initializer.elements && hostDirectiveSymbol.initializer.elements.length > 0 ) { hostDirectiveSymbol.initializer.elements.forEach(element => { if (element.kind === SyntaxKind.Identifier) { result.push({ name: element.escapedText }); } else if ( element.kind === SyntaxKind.ObjectLiteralExpression && element.properties && element.properties.length > 0 ) { const parsedDirective: any = { name: '', inputs: [], outputs: [] }; element.properties.forEach(property => { if (property.name.escapedText === 'directive') { parsedDirective.name = property.initializer.escapedText; } else if (property.name.escapedText === 'inputs') { if ( property.initializer && property.initializer.elements && property.initializer.elements.length > 0 ) { property.initializer.elements.forEach(propertyElement => { parsedDirective.inputs.push(propertyElement.text); }); } } else if (property.name.escapedText === 'outputs') { if ( property.initializer && property.initializer.elements && property.initializer.elements.length > 0 ) { property.initializer.elements.forEach(propertyElement => { parsedDirective.outputs.push(propertyElement.text); }); } } }); result.push(parsedDirective); } }); } return result; } public getComponentHost( props: ReadonlyArray<ts.ObjectLiteralElementLike> ): Map<string, string> { return this.getSymbolDepsObject(props, 'host'); } public getComponentTag( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string { return this.symbolHelper.getSymbolDeps(props, 'tag', srcFile).pop(); } public getComponentInputsMetadata( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): Array<string> { return this.symbolHelper.getSymbolDeps(props, 'inputs', srcFile); } public getInputSignals(props) { let inputSignals = []; props?.forEach((prop, i) => { const regexpInput = /input(?:\.(required))?(?:<([\w-]+)>)?\(([\w-]+)?\)/; const resInput = regexpInput.exec(prop.defaultValue); if (resInput) { const newInput = prop; newInput.defaultValue = resInput[resInput.length - 1]; newInput.required = resInput[0]?.includes('.required') ?? false; inputSignals.push(newInput); } else { const regexpModel = /model(?:\.(required))?(?:<([\w-]+)>)?\(([\w-]+)?\)/; const resModel = regexpModel.exec(prop.defaultValue); if (resModel) { const newInput = prop; newInput.defaultValue = resModel[resModel.length - 1]; newInput.required = resModel[0]?.includes('.required') ?? false; inputSignals.push(newInput); } } }); return inputSignals; } public getOutputSignals(props) { let outputSignals = []; props?.forEach((prop, i) => { const regexp = /output(?:\.(required))?(?:<([\w-]+)>)?\(([\w-]+)?\)/; const res = regexp.exec(prop.defaultValue); if (res) { const newOutput = prop; newOutput.defaultValue = res[res.length - 1]; newOutput.required = res[0]?.includes('.required') ?? false; outputSignals.push(newOutput); } }); return outputSignals; } public getComponentStandalone( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): boolean { let result = null; const parsedData = this.symbolHelper.getSymbolDeps(props, 'standalone', srcFile); if (parsedData.length === 1) { result = JSON.parse(parsedData[0]); } return result; } public getComponentTemplate( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string { let t = this.symbolHelper.getSymbolDeps(props, 'template', srcFile, true).pop(); if (t) { t = detectIndent(t, 0); t = t.replace(/\n/, ''); t = t.replace(/ +$/gm, ''); } return t; } public getComponentStyleUrls( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string[] { return this.symbolHelper.getSymbolDeps(props, 'styleUrls', srcFile); } public getComponentStyleUrl( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string { return this.symbolHelper.getSymbolDeps(props, 'styleUrl', srcFile).pop(); } public getComponentShadow( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string { return this.symbolHelper.getSymbolDeps(props, 'shadow', srcFile).pop(); } public getComponentScoped( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string { return this.symbolHelper.getSymbolDeps(props, 'scoped', srcFile).pop(); } public getComponentAssetsDir( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string { return this.symbolHelper.getSymbolDeps(props, 'assetsDir', srcFile).pop(); } public getComponentAssetsDirs( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string[] { return this.sanitizeUrls(this.symbolHelper.getSymbolDeps(props, 'assetsDir', srcFile)); } public getComponentStyles( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string[] { return this.symbolHelper.getSymbolDeps(props, 'styles', srcFile); } public getComponentModuleId( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string { return this.symbolHelper.getSymbolDeps(props, 'moduleId', srcFile).pop(); } public getComponentOutputs( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string[] { return this.symbolHelper.getSymbolDeps(props, 'outputs', srcFile); } public getComponentProviders( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): Array<IParseDeepIdentifierResult> { return this.symbolHelper .getSymbolDeps(props, 'providers', srcFile) .map(name => this.symbolHelper.parseDeepIndentifier(name)); } public getComponentImports( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): Array<IParseDeepIdentifierResult> { return this.symbolHelper .getSymbolDeps(props, 'imports', srcFile) .map(name => this.symbolHelper.parseDeepIndentifier(name)); } public getComponentEntryComponents( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): Array<IParseDeepIdentifierResult> { return this.symbolHelper .getSymbolDeps(props, 'entryComponents', srcFile) .map(name => this.symbolHelper.parseDeepIndentifier(name)); } public getComponentViewProviders( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): Array<IParseDeepIdentifierResult> { return this.symbolHelper .getSymbolDeps(props, 'viewProviders', srcFile) .map(name => this.symbolHelper.parseDeepIndentifier(name)); } public getComponentTemplateUrl( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): Array<string> { return this.symbolHelper.getSymbolDeps(props, 'templateUrl', srcFile); } public getComponentExampleUrls(text: string): Array<string> | undefined { let exampleUrlsMatches = text.match(/<example-url>(.*?)<\/example-url>/g); let exampleUrls = undefined; if (exampleUrlsMatches && exampleUrlsMatches.length) { exampleUrls = exampleUrlsMatches.map(function (val) { return val.replace(/<\/?example-url>/g, ''); }); } return exampleUrls; } public getComponentPreserveWhitespaces( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string { return this.symbolHelper.getSymbolDeps(props, 'preserveWhitespaces', srcFile).pop(); } public getComponentSelector( props: ReadonlyArray<ts.ObjectLiteralElementLike>, srcFile: ts.SourceFile ): string { return this.symbolHelper.getSymbolDeps(props, 'selector', srcFile).pop(); } private parseProperties(node: ReadonlyArray<ts.ObjectLiteralElementLike>): Map<string, string> { let obj = new Map<string, string>(); let properties = node.initializer.properties || []; properties.forEach(prop => { obj.set(prop.name.text, prop.initializer.text); }); return obj; } public getSymbolDepsObject( props: ReadonlyArray<ts.ObjectLiteralElementLike>, type: string, multiLine?: boolean ): Map<string, string> { let i = 0, len = props.length, filteredProps = []; for (i; i < len; i++) { if (props[i].name && props[i].name.text === type) { filteredProps.push(props[i]); } } return filteredProps.map(x => this.parseProperties(x)).pop(); } public getComponentIO( filename: string, sourceFile: ts.SourceFile, node: ts.Node, fileBody, astFile: ts.SourceFile ): any { /** * Copyright https://github.com/ng-bootstrap/ng-bootstrap */ let reducedSource = fileBody ? fileBody.statements : sourceFile.statements; let res = reducedSource.reduce((directive, statement) => { if (ts.isClassDeclaration(statement)) { if (statement.pos === node.pos && statement.end === node.end) { return directive.concat( this.classHelper.visitClassDeclaration( filename, statement, sourceFile, astFile ) ); } } return directive; }, []); return res[0] || {}; } private sanitizeUrls(urls: Array<string>): Array<string> { return urls.map(url => url.replace('./', '')); } } export class ComponentCache { private cache: Map<string, any> = new Map(); public get(key: string): any { return this.cache.get(key); } public set(key: string, value: any): void { this.cache.set(key, value); } }