diff --git a/.eslintrc.js b/.eslintrc.js index d7953bb2a36..fd34209758c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,7 @@ // This is a workaround for https://github.com/eslint/eslint/issues/3458 -require("@rushstack/eslint-config/patch-eslint6"); +require('@rushstack/eslint-config/patch-eslint6'); module.exports = { - extends: [ "@rushstack/eslint-config" ], - parserOptions: { tsconfigRootDir: __dirname }, + extends: ['@rushstack/eslint-config'], + parserOptions: { tsconfigRootDir: __dirname } }; diff --git a/config/api-extractor.json b/config/api-extractor.json index 3e8571b570b..aa9d8f810fd 100644 --- a/config/api-extractor.json +++ b/config/api-extractor.json @@ -15,6 +15,6 @@ "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/dist/rollup.d.ts", + "untrimmedFilePath": "/dist/rollup.d.ts" } } diff --git a/config/jest.json b/config/jest.json index b4a7ec97a56..902b00ea176 100644 --- a/config/jest.json +++ b/config/jest.json @@ -1,3 +1,3 @@ { "isEnabled": true -} \ No newline at end of file +} diff --git a/src/aedoc/PackageDocComment.ts b/src/aedoc/PackageDocComment.ts index c6bc4360ef0..ea0ea4d9def 100644 --- a/src/aedoc/PackageDocComment.ts +++ b/src/aedoc/PackageDocComment.ts @@ -9,9 +9,10 @@ export class PackageDocComment { /** * For the given source file, see if it starts with a TSDoc comment containing the `@packageDocumentation` tag. */ - public static tryFindInSourceFile(sourceFile: ts.SourceFile, - collector: Collector): ts.TextRange | undefined { - + public static tryFindInSourceFile( + sourceFile: ts.SourceFile, + collector: Collector + ): ts.TextRange | undefined { // The @packageDocumentation comment is special because it is not attached to an AST // definition. Instead, it is part of the "trivia" tokens that the compiler treats // as irrelevant white space. @@ -47,8 +48,8 @@ export class PackageDocComment { // wrong place? This sanity check helps people to figure out why there comment isn't working. for (const statement of sourceFile.statements) { const ranges: ts.CommentRange[] = []; - ranges.push(...ts.getLeadingCommentRanges(sourceFile.text, statement.getFullStart()) || []); - ranges.push(...ts.getTrailingCommentRanges(sourceFile.text, statement.getEnd()) || []); + ranges.push(...(ts.getLeadingCommentRanges(sourceFile.text, statement.getFullStart()) || [])); + ranges.push(...(ts.getTrailingCommentRanges(sourceFile.text, statement.getEnd()) || [])); for (const commentRange of ranges) { const commentBody: string = sourceFile.text.substring(commentRange.pos, commentRange.end); @@ -57,7 +58,8 @@ export class PackageDocComment { collector.messageRouter.addAnalyzerIssueForPosition( ExtractorMessageId.MisplacedPackageTag, 'The @packageDocumentation comment must appear at the top of entry point *.d.ts file', - sourceFile, commentRange.pos + sourceFile, + commentRange.pos ); break; } @@ -67,5 +69,4 @@ export class PackageDocComment { return packageCommentRange; } - } diff --git a/src/analyzer/AstDeclaration.ts b/src/analyzer/AstDeclaration.ts index 48a83380a40..bdaa01aa161 100644 --- a/src/analyzer/AstDeclaration.ts +++ b/src/analyzer/AstDeclaration.ts @@ -238,21 +238,21 @@ export class AstDeclaration { switch (kind) { case ts.SyntaxKind.CallSignature: case ts.SyntaxKind.ClassDeclaration: - case ts.SyntaxKind.ConstructSignature: // Example: "new(x: number): IMyClass" - case ts.SyntaxKind.Constructor: // Example: "constructor(x: number)" + case ts.SyntaxKind.ConstructSignature: // Example: "new(x: number): IMyClass" + case ts.SyntaxKind.Constructor: // Example: "constructor(x: number)" case ts.SyntaxKind.EnumDeclaration: case ts.SyntaxKind.EnumMember: - case ts.SyntaxKind.FunctionDeclaration: // Example: "(x: number): number" + case ts.SyntaxKind.FunctionDeclaration: // Example: "(x: number): number" case ts.SyntaxKind.GetAccessor: case ts.SyntaxKind.SetAccessor: - case ts.SyntaxKind.IndexSignature: // Example: "[key: string]: string" + case ts.SyntaxKind.IndexSignature: // Example: "[key: string]: string" case ts.SyntaxKind.InterfaceDeclaration: case ts.SyntaxKind.MethodDeclaration: case ts.SyntaxKind.MethodSignature: - case ts.SyntaxKind.ModuleDeclaration: // Used for both "module" and "namespace" declarations + case ts.SyntaxKind.ModuleDeclaration: // Used for both "module" and "namespace" declarations case ts.SyntaxKind.PropertyDeclaration: case ts.SyntaxKind.PropertySignature: - case ts.SyntaxKind.TypeAliasDeclaration: // Example: "type Shape = Circle | Square" + case ts.SyntaxKind.TypeAliasDeclaration: // Example: "type Shape = Circle | Square" case ts.SyntaxKind.VariableDeclaration: return true; @@ -268,5 +268,4 @@ export class AstDeclaration { return false; } - } diff --git a/src/analyzer/AstEntity.ts b/src/analyzer/AstEntity.ts index 5178bde5f92..c1887b08d30 100644 --- a/src/analyzer/AstEntity.ts +++ b/src/analyzer/AstEntity.ts @@ -43,5 +43,4 @@ export abstract class AstEntity { * - `AstImport` * - `AstImportAsModule` */ -export abstract class AstSyntheticEntity extends AstEntity { -} +export abstract class AstSyntheticEntity extends AstEntity {} diff --git a/src/analyzer/AstImport.ts b/src/analyzer/AstImport.ts index 9919f8341b0..281188af19d 100644 --- a/src/analyzer/AstImport.ts +++ b/src/analyzer/AstImport.ts @@ -109,7 +109,8 @@ export class AstImport extends AstSyntheticEntity { } /** {@inheritdoc} */ - public get localName(): string { // abstract + public get localName(): string { + // abstract return this.exportName; } diff --git a/src/analyzer/AstImportAsModule.ts b/src/analyzer/AstImportAsModule.ts index b4ef385eada..4d0226da289 100644 --- a/src/analyzer/AstImportAsModule.ts +++ b/src/analyzer/AstImportAsModule.ts @@ -47,7 +47,8 @@ export class AstImportAsModule extends AstSyntheticEntity { } /** {@inheritdoc} */ - public get localName(): string { // abstract + public get localName(): string { + // abstract return this.exportName; } -} \ No newline at end of file +} diff --git a/src/analyzer/AstReferenceResolver.ts b/src/analyzer/AstReferenceResolver.ts index 675c3c9aa81..0632b3bd62e 100644 --- a/src/analyzer/AstReferenceResolver.ts +++ b/src/analyzer/AstReferenceResolver.ts @@ -53,8 +53,10 @@ export class AstReferenceResolver { public resolve(declarationReference: tsdoc.DocDeclarationReference): AstDeclaration | ResolverFailure { // Is it referring to the working package? - if (declarationReference.packageName !== undefined - && declarationReference.packageName !== this._workingPackage.name) { + if ( + declarationReference.packageName !== undefined && + declarationReference.packageName !== this._workingPackage.name + ) { return new ResolverFailure('External package references are not supported'); } @@ -64,7 +66,8 @@ export class AstReferenceResolver { } const astModule: AstModule = this._astSymbolTable.fetchAstModuleFromWorkingPackage( - this._workingPackage.entryPointSourceFile); + this._workingPackage.entryPointSourceFile + ); if (declarationReference.memberReferences.length === 0) { return new ResolverFailure('Package references are not supported'); @@ -78,18 +81,25 @@ export class AstReferenceResolver { } const rootAstEntity: AstEntity | undefined = this._astSymbolTable.tryGetExportOfAstModule( - exportName, astModule); + exportName, + astModule + ); if (rootAstEntity === undefined) { - return new ResolverFailure(`The package "${this._workingPackage.name}" does not have an export "${exportName}"`); + return new ResolverFailure( + `The package "${this._workingPackage.name}" does not have an export "${exportName}"` + ); } if (!(rootAstEntity instanceof AstSymbol)) { return new ResolverFailure('This type of declaration is not supported yet by the resolver'); } - let currentDeclaration: AstDeclaration | ResolverFailure = this._selectDeclaration(rootAstEntity.astDeclarations, - rootMemberReference, rootAstEntity.localName); + let currentDeclaration: AstDeclaration | ResolverFailure = this._selectDeclaration( + rootAstEntity.astDeclarations, + rootMemberReference, + rootAstEntity.localName + ); if (currentDeclaration instanceof ResolverFailure) { return currentDeclaration; @@ -103,13 +113,18 @@ export class AstReferenceResolver { return memberName; } - const matchingChildren: ReadonlyArray = currentDeclaration.findChildrenWithName(memberName); + const matchingChildren: ReadonlyArray = currentDeclaration.findChildrenWithName( + memberName + ); if (matchingChildren.length === 0) { return new ResolverFailure(`No member was found with name "${memberName}"`); } - const selectedDeclaration: AstDeclaration | ResolverFailure = this._selectDeclaration(matchingChildren, - memberReference, memberName); + const selectedDeclaration: AstDeclaration | ResolverFailure = this._selectDeclaration( + matchingChildren, + memberReference, + memberName + ); if (selectedDeclaration instanceof ResolverFailure) { return selectedDeclaration; @@ -131,9 +146,11 @@ export class AstReferenceResolver { return memberReference.memberIdentifier.identifier; } - private _selectDeclaration(astDeclarations: ReadonlyArray, - memberReference: tsdoc.DocMemberReference, astSymbolName: string): AstDeclaration | ResolverFailure { - + private _selectDeclaration( + astDeclarations: ReadonlyArray, + memberReference: tsdoc.DocMemberReference, + astSymbolName: string + ): AstDeclaration | ResolverFailure { const memberSelector: tsdoc.DocMemberSelector | undefined = memberReference.selector; if (memberSelector === undefined) { @@ -142,13 +159,17 @@ export class AstReferenceResolver { } else { // If we found multiple matches, but the extra ones are all ancillary declarations, // then return the main declaration. - const nonAncillaryMatch: AstDeclaration | undefined = this._tryDisambiguateAncillaryMatches(astDeclarations); + const nonAncillaryMatch: AstDeclaration | undefined = this._tryDisambiguateAncillaryMatches( + astDeclarations + ); if (nonAncillaryMatch) { return nonAncillaryMatch; } - return new ResolverFailure(`The reference is ambiguous because "${astSymbolName}"` - + ` has more than one declaration; you need to add a TSDoc member reference selector`); + return new ResolverFailure( + `The reference is ambiguous because "${astSymbolName}"` + + ` has more than one declaration; you need to add a TSDoc member reference selector` + ); } } @@ -162,9 +183,11 @@ export class AstReferenceResolver { return new ResolverFailure(`The selector "${memberSelector.selector}" is not a supported selector type`); } - private _selectUsingSystemSelector(astDeclarations: ReadonlyArray, - memberSelector: tsdoc.DocMemberSelector, astSymbolName: string): AstDeclaration | ResolverFailure { - + private _selectUsingSystemSelector( + astDeclarations: ReadonlyArray, + memberSelector: tsdoc.DocMemberSelector, + astSymbolName: string + ): AstDeclaration | ResolverFailure { const selectorName: string = memberSelector.selector; let selectorSyntaxKind: ts.SyntaxKind; @@ -195,10 +218,14 @@ export class AstReferenceResolver { return new ResolverFailure(`Unsupported system selector "${selectorName}"`); } - const matches: AstDeclaration[] = astDeclarations.filter(x => x.declaration.kind === selectorSyntaxKind); + const matches: AstDeclaration[] = astDeclarations.filter( + (x) => x.declaration.kind === selectorSyntaxKind + ); if (matches.length === 0) { - return new ResolverFailure(`A declaration for "${astSymbolName}" was not found that matches the` - + ` TSDoc selector "${selectorName}"`); + return new ResolverFailure( + `A declaration for "${astSymbolName}" was not found that matches the` + + ` TSDoc selector "${selectorName}"` + ); } if (matches.length > 1) { // If we found multiple matches, but the extra ones are all ancillary declarations, @@ -208,15 +235,18 @@ export class AstReferenceResolver { return nonAncillaryMatch; } - return new ResolverFailure(`More than one declaration "${astSymbolName}" matches the` - + ` TSDoc selector "${selectorName}"`); + return new ResolverFailure( + `More than one declaration "${astSymbolName}" matches the TSDoc selector "${selectorName}"` + ); } return matches[0]; } - private _selectUsingIndexSelector(astDeclarations: ReadonlyArray, - memberSelector: tsdoc.DocMemberSelector, astSymbolName: string): AstDeclaration | ResolverFailure { - + private _selectUsingIndexSelector( + astDeclarations: ReadonlyArray, + memberSelector: tsdoc.DocMemberSelector, + astSymbolName: string + ): AstDeclaration | ResolverFailure { const selectorOverloadIndex: number = parseInt(memberSelector.selector); const matches: AstDeclaration[] = []; @@ -228,8 +258,10 @@ export class AstReferenceResolver { } if (matches.length === 0) { - return new ResolverFailure(`An overload for "${astSymbolName}" was not found that matches the` - + ` TSDoc selector ":${selectorOverloadIndex}"`); + return new ResolverFailure( + `An overload for "${astSymbolName}" was not found that matches the` + + ` TSDoc selector ":${selectorOverloadIndex}"` + ); } if (matches.length > 1) { // If we found multiple matches, but the extra ones are all ancillary declarations, @@ -239,8 +271,10 @@ export class AstReferenceResolver { return nonAncillaryMatch; } - return new ResolverFailure(`More than one declaration for "${astSymbolName}" matches the` - + ` TSDoc selector ":${selectorOverloadIndex}"`); + return new ResolverFailure( + `More than one declaration for "${astSymbolName}" matches the` + + ` TSDoc selector ":${selectorOverloadIndex}"` + ); } return matches[0]; } @@ -249,7 +283,9 @@ export class AstReferenceResolver { * This resolves an ambiguous match in the case where the extra matches are all ancillary declarations, * except for one match that is the main declaration. */ - private _tryDisambiguateAncillaryMatches(matches: ReadonlyArray): AstDeclaration | undefined { + private _tryDisambiguateAncillaryMatches( + matches: ReadonlyArray + ): AstDeclaration | undefined { let result: AstDeclaration | undefined = undefined; for (const match of matches) { diff --git a/src/analyzer/AstSymbolTable.ts b/src/analyzer/AstSymbolTable.ts index fb6e5852026..2c4568acbe7 100644 --- a/src/analyzer/AstSymbolTable.ts +++ b/src/analyzer/AstSymbolTable.ts @@ -79,32 +79,35 @@ export class AstSymbolTable { /** * A mapping from ts.Declaration --> AstDeclaration */ - private readonly _astDeclarationsByDeclaration: Map - = new Map(); + private readonly _astDeclarationsByDeclaration: Map = new Map< + ts.Node, + AstDeclaration + >(); // Note that this is a mapping from specific AST nodes that we analyzed, based on the underlying symbol // for that node. - private readonly _entitiesByIdentifierNode: Map - = new Map(); - - public constructor(program: ts.Program, typeChecker: ts.TypeChecker, packageJsonLookup: PackageJsonLookup, - bundledPackageNames: Set, messageRouter: MessageRouter) { - + private readonly _entitiesByIdentifierNode: Map = new Map< + ts.Identifier, + AstEntity | undefined + >(); + + public constructor( + program: ts.Program, + typeChecker: ts.TypeChecker, + packageJsonLookup: PackageJsonLookup, + bundledPackageNames: Set, + messageRouter: MessageRouter + ) { this._program = program; this._typeChecker = typeChecker; this._messageRouter = messageRouter; this._globalVariableAnalyzer = TypeScriptInternals.getGlobalVariableAnalyzer(program); this._packageMetadataManager = new PackageMetadataManager(packageJsonLookup, messageRouter); - this._exportAnalyzer = new ExportAnalyzer( - this._program, - this._typeChecker, - bundledPackageNames, - { - analyze: this.analyze.bind(this), - fetchAstSymbol: this._fetchAstSymbol.bind(this) - } - ); + this._exportAnalyzer = new ExportAnalyzer(this._program, this._typeChecker, bundledPackageNames, { + analyze: this.analyze.bind(this), + fetchAstSymbol: this._fetchAstSymbol.bind(this) + }); this._alreadyWarnedGlobalNames = new Set(); } @@ -207,7 +210,9 @@ export class AstSymbolTable { public static getLocalNameForSymbol(symbol: ts.Symbol): string { // TypeScript binds well-known ECMAScript symbols like "[Symbol.iterator]" as "__@iterator". // Decode it back into "[Symbol.iterator]". - const wellKnownSymbolName: string | undefined = TypeScriptHelpers.tryDecodeWellKnownSymbolName(symbol.escapedName); + const wellKnownSymbolName: string | undefined = TypeScriptHelpers.tryDecodeWellKnownSymbolName( + symbol.escapedName + ); if (wellKnownSymbolName) { return wellKnownSymbolName; } @@ -269,7 +274,6 @@ export class AstSymbolTable { return unquotedName; } - private _analyzeAstImportAsModule(astImportAsModule: AstImportAsModule): void { if (astImportAsModule.analyzed) { return; @@ -278,15 +282,17 @@ export class AstSymbolTable { // mark before actual analyzing, to handle module cyclic reexport astImportAsModule.analyzed = true; - this.fetchAstModuleExportInfo(astImportAsModule.astModule).exportedLocalEntities.forEach(exportedEntity => { - if (exportedEntity instanceof AstImportAsModule) { - this._analyzeAstImportAsModule(exportedEntity); - } + this.fetchAstModuleExportInfo(astImportAsModule.astModule).exportedLocalEntities.forEach( + (exportedEntity) => { + if (exportedEntity instanceof AstImportAsModule) { + this._analyzeAstImportAsModule(exportedEntity); + } - if (exportedEntity instanceof AstSymbol) { - this._analyzeAstSymbol(exportedEntity); + if (exportedEntity instanceof AstSymbol) { + this._analyzeAstSymbol(exportedEntity); + } } - }); + ); } private _analyzeAstSymbol(astSymbol: AstSymbol): void { @@ -316,7 +322,6 @@ export class AstSymbolTable { // get analyzed. rootAstSymbol.forEachDeclarationRecursive((astDeclaration: AstDeclaration) => { for (const referencedAstEntity of astDeclaration.referencedAstEntities) { - // Walk up to the root of the tree, looking for any imports along the way if (referencedAstEntity instanceof AstSymbol) { if (!referencedAstEntity.isExternal) { @@ -326,7 +331,7 @@ export class AstSymbolTable { if (referencedAstEntity instanceof AstImportAsModule) { if (!referencedAstEntity.astModule.isExternal) { - this._analyzeAstImportAsModule(referencedAstEntity) + this._analyzeAstImportAsModule(referencedAstEntity); } } } @@ -345,17 +350,21 @@ export class AstSymbolTable { // Is this a reference to another AstSymbol? case ts.SyntaxKind.TypeReference: // general type references case ts.SyntaxKind.ExpressionWithTypeArguments: // special case for e.g. the "extends" keyword - case ts.SyntaxKind.ComputedPropertyName: // used for EcmaScript "symbols", e.g. "[toPrimitive]". + case ts.SyntaxKind.ComputedPropertyName: // used for EcmaScript "symbols", e.g. "[toPrimitive]". case ts.SyntaxKind.TypeQuery: // represents for "typeof X" as a type { // Sometimes the type reference will involve multiple identifiers, e.g. "a.b.C". // In this case, we only need to worry about importing the first identifier, // so do a depth-first search for it: const identifierNode: ts.Identifier | undefined = TypeScriptHelpers.findFirstChildNode( - node, ts.SyntaxKind.Identifier); + node, + ts.SyntaxKind.Identifier + ); if (identifierNode) { - let referencedAstEntity: AstEntity | undefined = this._entitiesByIdentifierNode.get(identifierNode); + let referencedAstEntity: AstEntity | undefined = this._entitiesByIdentifierNode.get( + identifierNode + ); if (!referencedAstEntity) { const symbol: ts.Symbol | undefined = this._typeChecker.getSymbolAtLocation(identifierNode); if (!symbol) { @@ -388,8 +397,11 @@ export class AstSymbolTable { if (this._messageRouter.showDiagnostics) { if (!this._alreadyWarnedGlobalNames.has(identifierNode.text)) { this._alreadyWarnedGlobalNames.add(identifierNode.text); - this._messageRouter.logDiagnostic(`Ignoring reference to global variable "${identifierNode.text}"` - + ` in ` + SourceFileLocationFormatter.formatDeclaration(identifierNode)); + this._messageRouter.logDiagnostic( + `Ignoring reference to global variable "${identifierNode.text}"` + + ` in ` + + SourceFileLocationFormatter.formatDeclaration(identifierNode) + ); } } } else { @@ -398,8 +410,10 @@ export class AstSymbolTable { throw new InternalError(`Unable to follow symbol for "${identifierNode.text}"`); } } else { - referencedAstEntity = this._exportAnalyzer.fetchReferencedAstEntity(symbol, - governingAstDeclaration.astSymbol.isExternal); + referencedAstEntity = this._exportAnalyzer.fetchReferencedAstEntity( + symbol, + governingAstDeclaration.astSymbol.isExternal + ); this._entitiesByIdentifierNode.set(identifierNode, referencedAstEntity); } @@ -422,7 +436,10 @@ export class AstSymbolTable { let referencedAstEntity: AstEntity | undefined = undefined; if (symbol === governingAstDeclaration.astSymbol.followedSymbol) { - referencedAstEntity = this._fetchEntityForIdentifierNode(identifierNode, governingAstDeclaration); + referencedAstEntity = this._fetchEntityForIdentifierNode( + identifierNode, + governingAstDeclaration + ); } this._entitiesByIdentifierNode.set(identifierNode, referencedAstEntity); @@ -432,17 +449,20 @@ export class AstSymbolTable { } // Is this node declaring a new AstSymbol? - const newGoverningAstDeclaration: AstDeclaration | undefined = this._fetchAstDeclaration(node, - governingAstDeclaration.astSymbol.isExternal); + const newGoverningAstDeclaration: AstDeclaration | undefined = this._fetchAstDeclaration( + node, + governingAstDeclaration.astSymbol.isExternal + ); for (const childNode of node.getChildren()) { this._analyzeChildTree(childNode, newGoverningAstDeclaration || governingAstDeclaration); } } - private _fetchEntityForIdentifierNode(identifierNode: ts.Identifier, - governingAstDeclaration: AstDeclaration): AstEntity | undefined { - + private _fetchEntityForIdentifierNode( + identifierNode: ts.Identifier, + governingAstDeclaration: AstDeclaration + ): AstEntity | undefined { let referencedAstEntity: AstEntity | undefined = this._entitiesByIdentifierNode.get(identifierNode); if (!referencedAstEntity) { const symbol: ts.Symbol | undefined = this._typeChecker.getSymbolAtLocation(identifierNode); @@ -450,8 +470,10 @@ export class AstSymbolTable { throw new Error('Symbol not found for identifier: ' + identifierNode.getText()); } - referencedAstEntity = this._exportAnalyzer.fetchReferencedAstEntity(symbol, - governingAstDeclaration.astSymbol.isExternal); + referencedAstEntity = this._exportAnalyzer.fetchReferencedAstEntity( + symbol, + governingAstDeclaration.astSymbol.isExternal + ); this._entitiesByIdentifierNode.set(identifierNode, referencedAstEntity); } @@ -463,8 +485,10 @@ export class AstSymbolTable { return undefined; } - const symbol: ts.Symbol | undefined = TypeScriptHelpers.getSymbolForDeclaration(node as ts.Declaration, - this._typeChecker); + const symbol: ts.Symbol | undefined = TypeScriptHelpers.getSymbolForDeclaration( + node as ts.Declaration, + this._typeChecker + ); if (!symbol) { throw new InternalError('Unable to find symbol for node'); } @@ -500,8 +524,11 @@ export class AstSymbolTable { const arbitraryDeclaration: ts.Declaration = followedSymbol.declarations[0]; // eslint-disable-next-line no-bitwise - if (followedSymbol.flags & (ts.SymbolFlags.TypeParameter | ts.SymbolFlags.TypeLiteral | ts.SymbolFlags.Transient) - && !TypeScriptInternals.isLateBoundSymbol(followedSymbol)) { + if ( + followedSymbol.flags & + (ts.SymbolFlags.TypeParameter | ts.SymbolFlags.TypeLiteral | ts.SymbolFlags.Transient) && + !TypeScriptInternals.isLateBoundSymbol(followedSymbol) + ) { return undefined; } @@ -546,9 +573,11 @@ export class AstSymbolTable { if (!nominalAnalysis) { for (const declaration of followedSymbol.declarations || []) { if (!AstDeclaration.isSupportedSyntaxKind(declaration.kind)) { - throw new InternalError(`The "${followedSymbol.name}" symbol has a` - + ` ts.SyntaxKind.${ts.SyntaxKind[declaration.kind]} declaration which is not (yet?)` - + ` supported by API Extractor`); + throw new InternalError( + `The "${followedSymbol.name}" symbol has a` + + ` ts.SyntaxKind.${ts.SyntaxKind[declaration.kind]} declaration which is not (yet?)` + + ` supported by API Extractor` + ); } } @@ -564,13 +593,15 @@ export class AstSymbolTable { // - but P1 and P2 may be different (e.g. merged namespaces containing merged interfaces) // Is there a parent AstSymbol? First we check to see if there is a parent declaration: - const arbitraryParentDeclaration: ts.Node | undefined - = this._tryFindFirstAstDeclarationParent(followedSymbol.declarations[0]); + const arbitraryParentDeclaration: ts.Node | undefined = this._tryFindFirstAstDeclarationParent( + followedSymbol.declarations[0] + ); if (arbitraryParentDeclaration) { const parentSymbol: ts.Symbol = TypeScriptHelpers.getSymbolForDeclaration( arbitraryParentDeclaration as ts.Declaration, - this._typeChecker); + this._typeChecker + ); parentAstSymbol = this._fetchAstSymbol({ followedSymbol: parentSymbol, @@ -579,13 +610,13 @@ export class AstSymbolTable { addIfMissing: true }); if (!parentAstSymbol) { - throw new InternalError('Unable to construct a parent AstSymbol for ' - + followedSymbol.name); + throw new InternalError('Unable to construct a parent AstSymbol for ' + followedSymbol.name); } } } - const localName: string | undefined = options.localName || AstSymbolTable.getLocalNameForSymbol(followedSymbol); + const localName: string | undefined = + options.localName || AstSymbolTable.getLocalNameForSymbol(followedSymbol); astSymbol = new AstSymbol({ followedSymbol: followedSymbol, @@ -601,7 +632,6 @@ export class AstSymbolTable { // Okay, now while creating the declarations we will wire them up to the // their corresponding parent declarations for (const declaration of followedSymbol.declarations || []) { - let parentAstDeclaration: AstDeclaration | undefined = undefined; if (parentAstSymbol) { const parentDeclaration: ts.Node | undefined = this._tryFindFirstAstDeclarationParent(declaration); @@ -617,16 +647,21 @@ export class AstSymbolTable { } const astDeclaration: AstDeclaration = new AstDeclaration({ - declaration, astSymbol, parent: parentAstDeclaration}); + declaration, + astSymbol, + parent: parentAstDeclaration + }); this._astDeclarationsByDeclaration.set(declaration, astDeclaration); } } if (options.isExternal !== astSymbol.isExternal) { - throw new InternalError(`Cannot assign isExternal=${options.isExternal} for` - + ` the symbol ${astSymbol.localName} because it was previously registered` - + ` with isExternal=${astSymbol.isExternal}`); + throw new InternalError( + `Cannot assign isExternal=${options.isExternal} for` + + ` the symbol ${astSymbol.localName} because it was previously registered` + + ` with isExternal=${astSymbol.isExternal}` + ); } return astSymbol; diff --git a/src/analyzer/ExportAnalyzer.ts b/src/analyzer/ExportAnalyzer.ts index 9bf7d371fb5..e0bb130aefa 100644 --- a/src/analyzer/ExportAnalyzer.ts +++ b/src/analyzer/ExportAnalyzer.ts @@ -68,17 +68,23 @@ export class ExportAnalyzer { private readonly _bundledPackageNames: Set; private readonly _astSymbolTable: IAstSymbolTable; - private readonly _astModulesByModuleSymbol: Map - = new Map(); + private readonly _astModulesByModuleSymbol: Map = new Map(); // Used with isImportableAmbientSourceFile() private readonly _importableAmbientSourceFiles: Set = new Set(); private readonly _astImportsByKey: Map = new Map(); - private readonly _astImportAsModuleByModule: Map = new Map(); - - public constructor(program: ts.Program, typeChecker: ts.TypeChecker, bundledPackageNames: Set, - astSymbolTable: IAstSymbolTable) { + private readonly _astImportAsModuleByModule: Map = new Map< + AstModule, + AstImportAsModule + >(); + + public constructor( + program: ts.Program, + typeChecker: ts.TypeChecker, + bundledPackageNames: Set, + astSymbolTable: IAstSymbolTable + ) { this._program = program; this._typeChecker = typeChecker; this._bundledPackageNames = bundledPackageNames; @@ -91,9 +97,10 @@ export class ExportAnalyzer { * @param moduleReference - contextual information about the import statement that took us to this source file. * or `undefined` if this source file is the initial entry point */ - public fetchAstModuleFromSourceFile(sourceFile: ts.SourceFile, - moduleReference: IAstModuleReference | undefined): AstModule { - + public fetchAstModuleFromSourceFile( + sourceFile: ts.SourceFile, + moduleReference: IAstModuleReference | undefined + ): AstModule { const moduleSymbol: ts.Symbol = this._getModuleSymbolFromSourceFile(sourceFile, moduleReference); // Don't traverse into a module that we already processed before: @@ -101,7 +108,6 @@ export class ExportAnalyzer { // even if m2 and m3 both have "export * from 'm4'". let astModule: AstModule | undefined = this._astModulesByModuleSymbol.get(moduleSymbol); if (!astModule) { - // (If moduleReference === undefined, then this is the entry point of the local project being analyzed.) let externalModulePath: string | undefined = undefined; if (moduleReference !== undefined) { @@ -112,19 +118,23 @@ export class ExportAnalyzer { } } - astModule = new AstModule({ sourceFile, moduleSymbol, externalModulePath }); + astModule = new AstModule({ sourceFile, moduleSymbol, externalModulePath }); this._astModulesByModuleSymbol.set(moduleSymbol, astModule); if (astModule.isExternal) { // It's an external package, so do the special simplified analysis that doesn't crawl into referenced modules for (const exportedSymbol of this._typeChecker.getExportsOfModule(moduleSymbol)) { - if (externalModulePath === undefined) { - throw new InternalError('Failed assertion: externalModulePath=undefined but astModule.isExternal=true'); + throw new InternalError( + 'Failed assertion: externalModulePath=undefined but astModule.isExternal=true' + ); } - const followedSymbol: ts.Symbol = TypeScriptHelpers.followAliases(exportedSymbol, this._typeChecker); + const followedSymbol: ts.Symbol = TypeScriptHelpers.followAliases( + exportedSymbol, + this._typeChecker + ); // Ignore virtual symbols that don't have any declarations if (TypeScriptHelpers.hasAnyDeclarations(followedSymbol)) { @@ -136,8 +146,10 @@ export class ExportAnalyzer { }); if (!astSymbol) { - throw new Error(`Unsupported export ${JSON.stringify(exportedSymbol.name)}:\n` - + SourceFileLocationFormatter.formatDeclaration(followedSymbol.declarations[0])); + throw new Error( + `Unsupported export ${JSON.stringify(exportedSymbol.name)}:\n` + + SourceFileLocationFormatter.formatDeclaration(followedSymbol.declarations[0]) + ); } astModule.cachedExportedEntities.set(exportedSymbol.name, astSymbol); @@ -149,13 +161,16 @@ export class ExportAnalyzer { if (moduleSymbol.exports) { // The "export * from 'module-name';" declarations are all attached to a single virtual symbol // whose name is InternalSymbolName.ExportStar - const exportStarSymbol: ts.Symbol | undefined = moduleSymbol.exports.get(ts.InternalSymbolName.ExportStar); + const exportStarSymbol: ts.Symbol | undefined = moduleSymbol.exports.get( + ts.InternalSymbolName.ExportStar + ); if (exportStarSymbol) { for (const exportStarDeclaration of exportStarSymbol.getDeclarations() || []) { if (ts.isExportDeclaration(exportStarDeclaration)) { - - const starExportedModule: AstModule | undefined = this._fetchSpecifierAstModule(exportStarDeclaration, - exportStarSymbol); + const starExportedModule: AstModule | undefined = this._fetchSpecifierAstModule( + exportStarDeclaration, + exportStarSymbol + ); if (starExportedModule !== undefined) { astModule.starExportedModules.add(starExportedModule); @@ -167,7 +182,6 @@ export class ExportAnalyzer { } } } - } } @@ -182,11 +196,14 @@ export class ExportAnalyzer { * (This is a deprecated construct and mainly used for typings such as `@types/node`.) In this situation, * `moduleReference` helps us to fish out the correct module symbol. */ - private _getModuleSymbolFromSourceFile(sourceFile: ts.SourceFile, - moduleReference: IAstModuleReference | undefined): ts.Symbol { - - const moduleSymbol: ts.Symbol | undefined = TypeScriptInternals.tryGetSymbolForDeclaration(sourceFile, - this._typeChecker); + private _getModuleSymbolFromSourceFile( + sourceFile: ts.SourceFile, + moduleReference: IAstModuleReference | undefined + ): ts.Symbol { + const moduleSymbol: ts.Symbol | undefined = TypeScriptInternals.tryGetSymbolForDeclaration( + sourceFile, + this._typeChecker + ); if (moduleSymbol !== undefined) { // This is the normal case. The SourceFile acts is a module and has a symbol. return moduleSymbol; @@ -200,7 +217,9 @@ export class ExportAnalyzer { if ((moduleReference.moduleSpecifierSymbol.flags & ts.SymbolFlags.Alias) !== 0) { // Follow the import/export declaration to one hop the exported item inside the target module let followedSymbol: ts.Symbol | undefined = TypeScriptInternals.getImmediateAliasedSymbol( - moduleReference.moduleSpecifierSymbol, this._typeChecker); + moduleReference.moduleSpecifierSymbol, + this._typeChecker + ); if (followedSymbol === undefined) { // This is a workaround for a compiler bug where getImmediateAliasedSymbol() sometimes returns undefined @@ -237,8 +256,7 @@ export class ExportAnalyzer { if (entryPointAstModule.astModuleExportInfo === undefined) { const astModuleExportInfo: AstModuleExportInfo = new AstModuleExportInfo(); - this._collectAllExportsRecursive(astModuleExportInfo, entryPointAstModule, - new Set()); + this._collectAllExportsRecursive(astModuleExportInfo, entryPointAstModule, new Set()); entryPointAstModule.astModuleExportInfo = astModuleExportInfo; } @@ -283,9 +301,11 @@ export class ExportAnalyzer { return this._importableAmbientSourceFiles.has(sourceFile); } - private _collectAllExportsRecursive(astModuleExportInfo: AstModuleExportInfo, astModule: AstModule, - visitedAstModules: Set): void { - + private _collectAllExportsRecursive( + astModuleExportInfo: AstModuleExportInfo, + astModule: AstModule, + visitedAstModules: Set + ): void { if (visitedAstModules.has(astModule)) { return; } @@ -334,16 +354,18 @@ export class ExportAnalyzer { * refers to. For example, if a particular interface describes the return value of a function, this API can help * us determine a TSDoc declaration reference for that symbol (if the symbol is exported). */ - public fetchReferencedAstEntity(symbol: ts.Symbol, referringModuleIsExternal: boolean): AstEntity | undefined { + public fetchReferencedAstEntity( + symbol: ts.Symbol, + referringModuleIsExternal: boolean + ): AstEntity | undefined { let current: ts.Symbol = symbol; if (referringModuleIsExternal) { current = TypeScriptHelpers.followAliases(symbol, this._typeChecker); } else { - for (; ;) { + for (;;) { // Is this symbol an import/export that we need to follow to find the real declaration? for (const declaration of current.declarations || []) { - let matchedAstEntity: AstEntity | undefined; matchedAstEntity = this._tryMatchExportDeclaration(declaration, current); if (matchedAstEntity !== undefined) { @@ -355,11 +377,15 @@ export class ExportAnalyzer { } } - if (!(current.flags & ts.SymbolFlags.Alias)) { // eslint-disable-line no-bitwise + if (!(current.flags & ts.SymbolFlags.Alias)) { + // eslint-disable-line no-bitwise break; } - const currentAlias: ts.Symbol = TypeScriptInternals.getImmediateAliasedSymbol(current, this._typeChecker); + const currentAlias: ts.Symbol = TypeScriptInternals.getImmediateAliasedSymbol( + current, + this._typeChecker + ); // Stop if we reach the end of the chain if (!currentAlias || currentAlias === current) { break; @@ -380,9 +406,13 @@ export class ExportAnalyzer { return astSymbol; } - private _tryMatchExportDeclaration(declaration: ts.Declaration, declarationSymbol: ts.Symbol): AstEntity | undefined { - const exportDeclaration: ts.ExportDeclaration | undefined - = TypeScriptHelpers.findFirstParent(declaration, ts.SyntaxKind.ExportDeclaration); + private _tryMatchExportDeclaration( + declaration: ts.Declaration, + declarationSymbol: ts.Symbol + ): AstEntity | undefined { + const exportDeclaration: ts.ExportDeclaration | undefined = TypeScriptHelpers.findFirstParent< + ts.ExportDeclaration + >(declaration, ts.SyntaxKind.ExportDeclaration); if (exportDeclaration) { let exportName: string | undefined = undefined; @@ -412,8 +442,10 @@ export class ExportAnalyzer { // Ignore "export { A }" without a module specifier if (exportDeclaration.moduleSpecifier) { - const externalModulePath: string | undefined = this._tryGetExternalModulePath(exportDeclaration, - declarationSymbol); + const externalModulePath: string | undefined = this._tryGetExternalModulePath( + exportDeclaration, + declarationSymbol + ); if (externalModulePath !== undefined) { return this._fetchAstImport(declarationSymbol, { @@ -430,13 +462,19 @@ export class ExportAnalyzer { return undefined; } - private _tryMatchImportDeclaration(declaration: ts.Declaration, declarationSymbol: ts.Symbol): AstEntity | undefined { - const importDeclaration: ts.ImportDeclaration | undefined - = TypeScriptHelpers.findFirstParent(declaration, ts.SyntaxKind.ImportDeclaration); + private _tryMatchImportDeclaration( + declaration: ts.Declaration, + declarationSymbol: ts.Symbol + ): AstEntity | undefined { + const importDeclaration: ts.ImportDeclaration | undefined = TypeScriptHelpers.findFirstParent< + ts.ImportDeclaration + >(declaration, ts.SyntaxKind.ImportDeclaration); if (importDeclaration) { - const externalModulePath: string | undefined = this._tryGetExternalModulePath(importDeclaration, - declarationSymbol); + const externalModulePath: string | undefined = this._tryGetExternalModulePath( + importDeclaration, + declarationSymbol + ); if (declaration.kind === ts.SyntaxKind.NamespaceImport) { // EXAMPLE: @@ -528,8 +566,9 @@ export class ExportAnalyzer { // SemicolonToken: pre=[;] const importClause: ts.ImportClause = declaration as ts.ImportClause; - const exportName: string = importClause.name ? - importClause.name.getText().trim() : ts.InternalSymbolName.Default; + const exportName: string = importClause.name + ? importClause.name.getText().trim() + : ts.InternalSymbolName.Default; if (externalModulePath !== undefined) { return this._fetchAstImport(declarationSymbol, { @@ -539,7 +578,11 @@ export class ExportAnalyzer { }); } - return this._getExportOfSpecifierAstModule(ts.InternalSymbolName.Default, importDeclaration, declarationSymbol); + return this._getExportOfSpecifierAstModule( + ts.InternalSymbolName.Default, + importDeclaration, + declarationSymbol + ); } else { throw new InternalError('Unimplemented import declaration kind: ' + declaration.getText()); } @@ -561,10 +604,10 @@ export class ExportAnalyzer { // SemicolonToken: pre=[;] if (ts.isExternalModuleReference(declaration.moduleReference)) { if (ts.isStringLiteralLike(declaration.moduleReference.expression)) { - const variableName: string = TypeScriptInternals.getTextOfIdentifierOrLiteral( - declaration.name); + const variableName: string = TypeScriptInternals.getTextOfIdentifierOrLiteral(declaration.name); const externalModuleName: string = TypeScriptInternals.getTextOfIdentifierOrLiteral( - declaration.moduleReference.expression); + declaration.moduleReference.expression + ); return this._fetchAstImport(declarationSymbol, { importKind: AstImportKind.EqualsImport, @@ -575,33 +618,44 @@ export class ExportAnalyzer { } } - const importTypeNode: ts.Node | undefined - = TypeScriptHelpers.findFirstChildNode(declaration, ts.SyntaxKind.ImportType); + const importTypeNode: ts.Node | undefined = TypeScriptHelpers.findFirstChildNode( + declaration, + ts.SyntaxKind.ImportType + ); if (importTypeNode) { - throw new Error('The expression contains an import() type, which is not yet supported by API Extractor:\n' - + SourceFileLocationFormatter.formatDeclaration(importTypeNode)); + throw new Error( + 'The expression contains an import() type, which is not yet supported by API Extractor:\n' + + SourceFileLocationFormatter.formatDeclaration(importTypeNode) + ); } return undefined; } - private _getExportOfSpecifierAstModule(exportName: string, + private _getExportOfSpecifierAstModule( + exportName: string, importOrExportDeclaration: ts.ImportDeclaration | ts.ExportDeclaration, - exportSymbol: ts.Symbol): AstEntity { - - const specifierAstModule: AstModule = this._fetchSpecifierAstModule(importOrExportDeclaration, exportSymbol); + exportSymbol: ts.Symbol + ): AstEntity { + const specifierAstModule: AstModule = this._fetchSpecifierAstModule( + importOrExportDeclaration, + exportSymbol + ); const astEntity: AstEntity = this._getExportOfAstModule(exportName, specifierAstModule); return astEntity; } private _getExportOfAstModule(exportName: string, astModule: AstModule): AstEntity { - const visitedAstModules: Set = new Set(); - const astEntity: AstEntity | undefined = this._tryGetExportOfAstModule(exportName, astModule, - visitedAstModules); + const astEntity: AstEntity | undefined = this._tryGetExportOfAstModule( + exportName, + astModule, + visitedAstModules + ); if (astEntity === undefined) { - throw new InternalError(`Unable to analyze the export ${JSON.stringify(exportName)} in\n` - + astModule.sourceFile.fileName); + throw new InternalError( + `Unable to analyze the export ${JSON.stringify(exportName)} in\n` + astModule.sourceFile.fileName + ); } return astEntity; } @@ -611,13 +665,14 @@ export class ExportAnalyzer { */ public tryGetExportOfAstModule(exportName: string, astModule: AstModule): AstEntity | undefined { const visitedAstModules: Set = new Set(); - return this._tryGetExportOfAstModule(exportName, astModule, - visitedAstModules); + return this._tryGetExportOfAstModule(exportName, astModule, visitedAstModules); } - private _tryGetExportOfAstModule(exportName: string, astModule: AstModule, - visitedAstModules: Set): AstEntity | undefined { - + private _tryGetExportOfAstModule( + exportName: string, + astModule: AstModule, + visitedAstModules: Set + ): AstEntity | undefined { if (visitedAstModules.has(astModule)) { return undefined; } @@ -647,7 +702,6 @@ export class ExportAnalyzer { astEntity = this._tryGetExportOfAstModule(exportName, starExportedModule, visitedAstModules); if (astEntity !== undefined) { - if (starExportedModule.externalModulePath !== undefined) { // This entity was obtained from an external module, so return an AstImport instead const astSymbol: AstSymbol = astEntity as AstSymbol; @@ -665,11 +719,14 @@ export class ExportAnalyzer { return undefined; } - private _tryGetExternalModulePath(importOrExportDeclaration: ts.ImportDeclaration | ts.ExportDeclaration, - exportSymbol: ts.Symbol): string | undefined { - - // The name of the module, which could be like "./SomeLocalFile' or like 'external-package/entry/point' - const moduleSpecifier: string | undefined = TypeScriptHelpers.getModuleSpecifier(importOrExportDeclaration); + private _tryGetExternalModulePath( + importOrExportDeclaration: ts.ImportDeclaration | ts.ExportDeclaration, + exportSymbol: ts.Symbol + ): string | undefined { + // The name of the module, which could be like "./SomeLocalFile' or like 'external-package/entry/point' + const moduleSpecifier: string | undefined = TypeScriptHelpers.getModuleSpecifier( + importOrExportDeclaration + ); if (!moduleSpecifier) { throw new InternalError('Unable to parse module specifier'); } @@ -687,38 +744,52 @@ export class ExportAnalyzer { * Given an ImportDeclaration of the form `export { X } from "___";`, this interprets the module specifier (`"___"`) * and fetches the corresponding AstModule object. */ - private _fetchSpecifierAstModule(importOrExportDeclaration: ts.ImportDeclaration | ts.ExportDeclaration, - exportSymbol: ts.Symbol): AstModule { - + private _fetchSpecifierAstModule( + importOrExportDeclaration: ts.ImportDeclaration | ts.ExportDeclaration, + exportSymbol: ts.Symbol + ): AstModule { // The name of the module, which could be like "./SomeLocalFile' or like 'external-package/entry/point' - const moduleSpecifier: string | undefined = TypeScriptHelpers.getModuleSpecifier(importOrExportDeclaration); + const moduleSpecifier: string | undefined = TypeScriptHelpers.getModuleSpecifier( + importOrExportDeclaration + ); if (!moduleSpecifier) { throw new InternalError('Unable to parse module specifier'); } const resolvedModule: ts.ResolvedModuleFull | undefined = TypeScriptInternals.getResolvedModule( - importOrExportDeclaration.getSourceFile(), moduleSpecifier); + importOrExportDeclaration.getSourceFile(), + moduleSpecifier + ); if (resolvedModule === undefined) { // This should not happen, since getResolvedModule() specifically looks up names that the compiler // found in export declarations for this source file - throw new InternalError('getResolvedModule() could not resolve module name ' + JSON.stringify(moduleSpecifier)); + throw new InternalError( + 'getResolvedModule() could not resolve module name ' + JSON.stringify(moduleSpecifier) + ); } // Map the filename back to the corresponding SourceFile. This circuitous approach is needed because // we have no way to access the compiler's internal resolveExternalModuleName() function - const moduleSourceFile: ts.SourceFile | undefined = this._program.getSourceFile(resolvedModule.resolvedFileName); + const moduleSourceFile: ts.SourceFile | undefined = this._program.getSourceFile( + resolvedModule.resolvedFileName + ); if (!moduleSourceFile) { // This should not happen, since getResolvedModule() specifically looks up names that the compiler // found in export declarations for this source file - throw new InternalError('getSourceFile() failed to locate ' + JSON.stringify(resolvedModule.resolvedFileName)); + throw new InternalError( + 'getSourceFile() failed to locate ' + JSON.stringify(resolvedModule.resolvedFileName) + ); } const moduleReference: IAstModuleReference = { moduleSpecifier: moduleSpecifier, moduleSpecifierSymbol: exportSymbol }; - const specifierAstModule: AstModule = this.fetchAstModuleFromSourceFile(moduleSourceFile, moduleReference); + const specifierAstModule: AstModule = this.fetchAstModuleFromSourceFile( + moduleSourceFile, + moduleReference + ); return specifierAstModule; } diff --git a/src/analyzer/PackageMetadataManager.ts b/src/analyzer/PackageMetadataManager.ts index 8de8edc14cf..b463cab4d7c 100644 --- a/src/analyzer/PackageMetadataManager.ts +++ b/src/analyzer/PackageMetadataManager.ts @@ -60,8 +60,10 @@ export class PackageMetadataManager { private readonly _packageJsonLookup: PackageJsonLookup; private readonly _messageRouter: MessageRouter; - private readonly _packageMetadataByPackageJsonPath: Map - = new Map(); + private readonly _packageMetadataByPackageJsonPath: Map = new Map< + string, + PackageMetadata + >(); public constructor(packageJsonLookup: PackageJsonLookup, messageRouter: MessageRouter) { this._packageJsonLookup = packageJsonLookup; @@ -70,9 +72,10 @@ export class PackageMetadataManager { // This feature is still being standardized: https://github.com/microsoft/tsdoc/issues/7 // In the future we will use the @microsoft/tsdoc library to read this file. - private static _resolveTsdocMetadataPathFromPackageJson(packageFolder: string, - packageJson: INodePackageJson): string { - + private static _resolveTsdocMetadataPathFromPackageJson( + packageFolder: string, + packageJson: INodePackageJson + ): string { const tsdocMetadataFilename: string = PackageMetadataManager.tsdocMetadataFilename; let tsdocMetadataRelativePath: string; @@ -85,17 +88,11 @@ export class PackageMetadataManager { } else if (packageJson.typings) { // 2. If package.json contains a field such as "typings": "./path1/path2/index.d.ts", then we look // for the file under "./path1/path2/tsdoc-metadata.json" - tsdocMetadataRelativePath = path.join( - path.dirname(packageJson.typings), - tsdocMetadataFilename - ); + tsdocMetadataRelativePath = path.join(path.dirname(packageJson.typings), tsdocMetadataFilename); } else if (packageJson.main) { // 3. If package.json contains a field such as "main": "./path1/path2/index.js", then we look for // the file under "./path1/path2/tsdoc-metadata.json" - tsdocMetadataRelativePath = path.join( - path.dirname(packageJson.main), - tsdocMetadataFilename - ); + tsdocMetadataRelativePath = path.join(path.dirname(packageJson.main), tsdocMetadataFilename); } else { // 4. If none of the above rules apply, then by default we look for the file under "./tsdoc-metadata.json" // since the default entry point is "./index.js" @@ -103,10 +100,7 @@ export class PackageMetadataManager { } // Always resolve relative to the package folder. - const tsdocMetadataPath: string = path.resolve( - packageFolder, - tsdocMetadataRelativePath - ); + const tsdocMetadataPath: string = path.resolve(packageFolder, tsdocMetadataRelativePath); return tsdocMetadataPath; } @@ -123,10 +117,7 @@ export class PackageMetadataManager { if (tsdocMetadataPath) { return path.resolve(packageFolder, tsdocMetadataPath); } - return PackageMetadataManager._resolveTsdocMetadataPathFromPackageJson( - packageFolder, - packageJson - ); + return PackageMetadataManager._resolveTsdocMetadataPathFromPackageJson(packageFolder, packageJson); } /** @@ -160,13 +151,15 @@ export class PackageMetadataManager { * is returned. The results are cached. */ public tryFetchPackageMetadata(sourceFilePath: string): PackageMetadata | undefined { - const packageJsonFilePath: string | undefined - = this._packageJsonLookup.tryGetPackageJsonFilePathFor(sourceFilePath); + const packageJsonFilePath: string | undefined = this._packageJsonLookup.tryGetPackageJsonFilePathFor( + sourceFilePath + ); if (!packageJsonFilePath) { return undefined; } - let packageMetadata: PackageMetadata | undefined - = this._packageMetadataByPackageJsonPath.get(packageJsonFilePath); + let packageMetadata: PackageMetadata | undefined = this._packageMetadataByPackageJsonPath.get( + packageJsonFilePath + ); if (!packageMetadata) { const packageJson: INodePackageJson = this._packageJsonLookup.loadNodePackageJson(packageJsonFilePath); @@ -181,7 +174,10 @@ export class PackageMetadataManager { ); if (FileSystem.exists(tsdocMetadataPath)) { - this._messageRouter.logVerbose(ConsoleMessageId.FoundTSDocMetadata, 'Found metadata in ' + tsdocMetadataPath); + this._messageRouter.logVerbose( + ConsoleMessageId.FoundTSDocMetadata, + 'Found metadata in ' + tsdocMetadataPath + ); // If the file exists at all, assume it was written by API Extractor aedocSupported = true; } diff --git a/src/analyzer/SourceFileLocationFormatter.ts b/src/analyzer/SourceFileLocationFormatter.ts index b2fab5b5204..3521334e915 100644 --- a/src/analyzer/SourceFileLocationFormatter.ts +++ b/src/analyzer/SourceFileLocationFormatter.ts @@ -8,7 +8,7 @@ import { Path, Text } from '@rushstack/node-core-library'; export interface ISourceFileLocationFormatOptions { sourceFileLine?: number; sourceFileColumn?: number; - workingPackageFolderPath?: string + workingPackageFolderPath?: string; } export class SourceFileLocationFormatter { @@ -29,7 +29,7 @@ export class SourceFileLocationFormatter { public static formatPath(sourceFilePath: string, options?: ISourceFileLocationFormatOptions): string { if (!options) { - options = { }; + options = {}; } let result: string = ''; diff --git a/src/analyzer/Span.ts b/src/analyzer/Span.ts index da41839e55e..c702c312a6f 100644 --- a/src/analyzer/Span.ts +++ b/src/analyzer/Span.ts @@ -403,20 +403,18 @@ export class Span { const childCount: number = this.children.length; if (!this.modification.omitChildren) { - if (this.modification.sortChildren && childCount > 1) { // We will only sort the items with a sortKey - const sortedSubset: Span[] = this.children.filter(x => x.modification.sortKey !== undefined); + const sortedSubset: Span[] = this.children.filter((x) => x.modification.sortKey !== undefined); const sortedSubsetCount: number = sortedSubset.length; // Is there at least one of them? if (sortedSubsetCount > 1) { - // Remember the separator for the first and last ones const firstSeparator: string = sortedSubset[0].getLastInnerSeparator(); const lastSeparator: string = sortedSubset[sortedSubsetCount - 1].getLastInnerSeparator(); - Sort.sortBy(sortedSubset, x => x.modification.sortKey); + Sort.sortBy(sortedSubset, (x) => x.modification.sortKey); const childOptions: IWriteModifiedTextOptions = { ...options }; @@ -456,9 +454,9 @@ export class Span { if ( // Only the last child inherits the separatorOverride, because only it can contain // the "last inner separator" span - i < childCount - 1 + i < childCount - 1 || // If this.separator is specified, then we will write separatorOverride below, so don't pass it along - || this.separator + this.separator ) { const childOptions: IWriteModifiedTextOptions = { ...options }; childOptions.separatorOverride = undefined; @@ -508,4 +506,4 @@ export class Span { interface IWriteModifiedTextOptions { output: StringBuilder; separatorOverride: string | undefined; -} \ No newline at end of file +} diff --git a/src/analyzer/StringChecks.ts b/src/analyzer/StringChecks.ts index eec6f81a795..0bad57345c7 100644 --- a/src/analyzer/StringChecks.ts +++ b/src/analyzer/StringChecks.ts @@ -15,16 +15,16 @@ export class StringChecks { * * ```ts * class X { - * public okay: number = 1; - * public "not okay!": number = 2; - * } - * ``` - * - * A precise check is extremely complicated and highly dependent on the ECMAScript standard version - * and how faithfully the interpreter implements it. To keep things simple, `isSafeUnquotedMemberIdentifier()` - * conservatively accepts any identifier that would be valid with ECMAScript 5, and returns false otherwise. - */ - public static isSafeUnquotedMemberIdentifier(identifier: string): boolean { + * public okay: number = 1; + * public "not okay!": number = 2; + * } + * ``` + * + * A precise check is extremely complicated and highly dependent on the ECMAScript standard version + * and how faithfully the interpreter implements it. To keep things simple, `isSafeUnquotedMemberIdentifier()` + * conservatively accepts any identifier that would be valid with ECMAScript 5, and returns false otherwise. + */ + public static isSafeUnquotedMemberIdentifier(identifier: string): boolean { if (identifier.length === 0) { return false; // cannot be empty } diff --git a/src/analyzer/TypeScriptHelpers.ts b/src/analyzer/TypeScriptHelpers.ts index b3759a21878..8e0198b2beb 100644 --- a/src/analyzer/TypeScriptHelpers.ts +++ b/src/analyzer/TypeScriptHelpers.ts @@ -80,8 +80,10 @@ export class TypeScriptHelpers { const firstDeclaration: ts.Declaration = followedSymbol.declarations[0]; // Test 1: Are we inside the sinister "declare global {" construct? - const highestModuleDeclaration: ts.ModuleDeclaration | undefined - = TypeScriptHelpers.findHighestParent(firstDeclaration, ts.SyntaxKind.ModuleDeclaration); + const highestModuleDeclaration: ts.ModuleDeclaration | undefined = TypeScriptHelpers.findHighestParent( + firstDeclaration, + ts.SyntaxKind.ModuleDeclaration + ); if (highestModuleDeclaration) { if (highestModuleDeclaration.name.getText().trim() === 'global') { return true; @@ -106,20 +108,27 @@ export class TypeScriptHelpers { * cannot be found. */ public static getSymbolForDeclaration(declaration: ts.Declaration, checker: ts.TypeChecker): ts.Symbol { - const symbol: ts.Symbol | undefined = TypeScriptInternals.tryGetSymbolForDeclaration(declaration, checker); + const symbol: ts.Symbol | undefined = TypeScriptInternals.tryGetSymbolForDeclaration( + declaration, + checker + ); if (!symbol) { - throw new InternalError('Unable to determine semantic information for declaration:\n' - + SourceFileLocationFormatter.formatDeclaration(declaration)); + throw new InternalError( + 'Unable to determine semantic information for declaration:\n' + + SourceFileLocationFormatter.formatDeclaration(declaration) + ); } return symbol; } // Return name of the module, which could be like "./SomeLocalFile' or like 'external-package/entry/point' - public static getModuleSpecifier(declarationWithModuleSpecifier: ts.ImportDeclaration - | ts.ExportDeclaration): string | undefined { - - if (declarationWithModuleSpecifier.moduleSpecifier - && ts.isStringLiteralLike(declarationWithModuleSpecifier.moduleSpecifier)) { + public static getModuleSpecifier( + declarationWithModuleSpecifier: ts.ImportDeclaration | ts.ExportDeclaration + ): string | undefined { + if ( + declarationWithModuleSpecifier.moduleSpecifier && + ts.isStringLiteralLike(declarationWithModuleSpecifier.moduleSpecifier) + ) { return TypeScriptInternals.getTextOfIdentifierOrLiteral(declarationWithModuleSpecifier.moduleSpecifier); } @@ -139,7 +148,10 @@ export class TypeScriptHelpers { * * Calling _matchAncestor(C, [ExportDeclaration]) would return C. */ - public static matchAncestor(node: ts.Node, kindsToMatch: ts.SyntaxKind[]): T | undefined { + public static matchAncestor( + node: ts.Node, + kindsToMatch: ts.SyntaxKind[] + ): T | undefined { // (slice(0) clones an array) const reversedParentKinds: ts.SyntaxKind[] = kindsToMatch.slice(0).reverse(); @@ -168,7 +180,10 @@ export class TypeScriptHelpers { * Does a depth-first search of the children of the specified node. Returns the first child * with the specified kind, or undefined if there is no match. */ - public static findFirstChildNode(node: ts.Node, kindToMatch: ts.SyntaxKind): T | undefined { + public static findFirstChildNode( + node: ts.Node, + kindToMatch: ts.SyntaxKind + ): T | undefined { for (const child of node.getChildren()) { if (child.kind === kindToMatch) { return child as T; @@ -204,7 +219,10 @@ export class TypeScriptHelpers { * @remarks * Whereas findFirstParent() returns the first match, findHighestParent() returns the last match. */ - public static findHighestParent(node: ts.Node, kindToMatch: ts.SyntaxKind): T | undefined { + public static findHighestParent( + node: ts.Node, + kindToMatch: ts.SyntaxKind + ): T | undefined { let current: ts.Node | undefined = node; let highest: T | undefined = undefined; @@ -249,15 +267,21 @@ export class TypeScriptHelpers { public static tryGetLateBoundName(declarationName: ts.ComputedPropertyName): string | undefined { // Create a node printer that ignores comments and indentation that we can use to convert // declarationName to a string. - const printer: ts.Printer = ts.createPrinter({ removeComments: true }, { - onEmitNode(hint: ts.EmitHint, node: ts.Node | undefined, - emit: (hint: ts.EmitHint, node: ts.Node | undefined) => void): void { - if (node) { - ts.setEmitFlags(declarationName, ts.EmitFlags.NoIndentation | ts.EmitFlags.SingleLine); + const printer: ts.Printer = ts.createPrinter( + { removeComments: true }, + { + onEmitNode( + hint: ts.EmitHint, + node: ts.Node | undefined, + emit: (hint: ts.EmitHint, node: ts.Node | undefined) => void + ): void { + if (node) { + ts.setEmitFlags(declarationName, ts.EmitFlags.NoIndentation | ts.EmitFlags.SingleLine); + } + emit(hint, node); } - emit(hint, node); } - }); + ); const sourceFile: ts.SourceFile = declarationName.getSourceFile(); const text: string = printer.printNode(ts.EmitHint.Unspecified, declarationName, sourceFile); // clean up any emit flags we've set on any nodes in the tree. diff --git a/src/analyzer/TypeScriptInternals.ts b/src/analyzer/TypeScriptInternals.ts index e177c5c792b..63d46424466 100644 --- a/src/analyzer/TypeScriptInternals.ts +++ b/src/analyzer/TypeScriptInternals.ts @@ -14,7 +14,6 @@ export interface IGlobalVariableAnalyzer { } export class TypeScriptInternals { - public static getImmediateAliasedSymbol(symbol: ts.Symbol, typeChecker: ts.TypeChecker): ts.Symbol { // Compiler internal: // https://github.com/microsoft/TypeScript/blob/v3.2.2/src/compiler/checker.ts @@ -29,12 +28,14 @@ export class TypeScriptInternals { * @returns The associated Symbol. If there is no semantic information (e.g. if the * declaration is an extra semicolon somewhere), then "undefined" is returned. */ - public static tryGetSymbolForDeclaration(declaration: ts.Declaration, checker: ts.TypeChecker): ts.Symbol - | undefined { + public static tryGetSymbolForDeclaration( + declaration: ts.Declaration, + checker: ts.TypeChecker + ): ts.Symbol | undefined { let symbol: ts.Symbol | undefined = (declaration as any).symbol; if (symbol && symbol.escapedName === ts.InternalSymbolName.Computed) { const name: ts.DeclarationName | undefined = ts.getNameOfDeclaration(declaration); - symbol = name && checker.getSymbolAtLocation(name) || symbol; + symbol = (name && checker.getSymbolAtLocation(name)) || symbol; } return symbol; } @@ -44,9 +45,11 @@ export class TypeScriptInternals { * for a computed property based on its type, rather than by the Binder). */ public static isLateBoundSymbol(symbol: ts.Symbol): boolean { - // eslint-disable-next-line no-bitwise - if (symbol.flags & ts.SymbolFlags.Transient && - (symbol as any).checkFlags === (ts as any).CheckFlags.Late) { + if ( + // eslint-disable-next-line no-bitwise + symbol.flags & ts.SymbolFlags.Transient && + (symbol as any).checkFlags === (ts as any).CheckFlags.Late + ) { return true; } return false; @@ -65,7 +68,9 @@ export class TypeScriptInternals { /** * Retrieves the (unescaped) value of an string literal, numeric literal, or identifier. */ - public static getTextOfIdentifierOrLiteral(node: ts.Identifier | ts.StringLiteralLike | ts.NumericLiteral): string { + public static getTextOfIdentifierOrLiteral( + node: ts.Identifier | ts.StringLiteralLike | ts.NumericLiteral + ): string { // Compiler internal: // https://github.com/microsoft/TypeScript/blob/v3.2.2/src/compiler/utilities.ts#L2721 @@ -76,9 +81,10 @@ export class TypeScriptInternals { * Retrieves the (cached) module resolution information for a module name that was exported from a SourceFile. * The compiler populates this cache as part of analyzing the source file. */ - public static getResolvedModule(sourceFile: ts.SourceFile, moduleNameText: string): ts.ResolvedModuleFull - | undefined { - + public static getResolvedModule( + sourceFile: ts.SourceFile, + moduleNameText: string + ): ts.ResolvedModuleFull | undefined { // Compiler internal: // https://github.com/microsoft/TypeScript/blob/v3.2.2/src/compiler/utilities.ts#L218 diff --git a/src/analyzer/test/PackageMetadataManager.test.ts b/src/analyzer/test/PackageMetadataManager.test.ts index 3bb872cb81d..59815374c3c 100644 --- a/src/analyzer/test/PackageMetadataManager.test.ts +++ b/src/analyzer/test/PackageMetadataManager.test.ts @@ -1,4 +1,3 @@ - import * as path from 'path'; import { PackageMetadataManager } from '../PackageMetadataManager'; import { FileSystem, PackageJsonLookup, INodePackageJson, NewlineKind } from '@rushstack/node-core-library'; @@ -9,7 +8,9 @@ function resolveInTestPackage(testPackageName: string, ...args: string[]): strin return path.resolve(__dirname, 'test-data/tsdoc-metadata-path-inference', testPackageName, ...args); } -function getPackageMetadata(testPackageName: string): { packageFolder: string, packageJson: INodePackageJson } { +function getPackageMetadata( + testPackageName: string +): { packageFolder: string; packageJson: INodePackageJson } { const packageFolder: string = resolveInTestPackage(testPackageName); const packageJson: INodePackageJson | undefined = packageJsonLookup.tryLoadPackageJsonFor(packageFolder); if (!packageJson) { @@ -50,42 +51,34 @@ describe('PackageMetadataManager', () => { const tsdocMetadataPath: string = ''; describe('given a package.json where the field "tsdocMetadata" is defined', () => { it('outputs the tsdoc metadata path as given by "tsdocMetadata" relative to the folder of package.json', () => { - const { - packageFolder, - packageJson - } = getPackageMetadata('package-inferred-from-tsdoc-metadata'); - expect(PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath)) - .toBe(path.resolve(packageFolder, packageJson.tsdocMetadata as string)); + const { packageFolder, packageJson } = getPackageMetadata('package-inferred-from-tsdoc-metadata'); + expect( + PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) + ).toBe(path.resolve(packageFolder, packageJson.tsdocMetadata as string)); }); }); describe('given a package.json where the field "typings" is defined and "tsdocMetadata" is not defined', () => { it('outputs the tsdoc metadata file "tsdoc-metadata.json" in the same folder as the path of "typings"', () => { - const { - packageFolder, - packageJson - } = getPackageMetadata('package-inferred-from-typings'); - expect(PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath)) - .toBe(path.resolve(packageFolder, path.dirname(packageJson.typings!), 'tsdoc-metadata.json')); + const { packageFolder, packageJson } = getPackageMetadata('package-inferred-from-typings'); + expect( + PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) + ).toBe(path.resolve(packageFolder, path.dirname(packageJson.typings!), 'tsdoc-metadata.json')); }); }); describe('given a package.json where the field "main" is defined but not "typings" nor "tsdocMetadata"', () => { it('outputs the tsdoc metadata file "tsdoc-metadata.json" in the same folder as the path of "main"', () => { - const { - packageFolder, - packageJson - } = getPackageMetadata('package-inferred-from-main'); - expect(PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath)) - .toBe(path.resolve(packageFolder, path.dirname(packageJson.main!), 'tsdoc-metadata.json')); + const { packageFolder, packageJson } = getPackageMetadata('package-inferred-from-main'); + expect( + PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) + ).toBe(path.resolve(packageFolder, path.dirname(packageJson.main!), 'tsdoc-metadata.json')); }); }); describe('given a package.json where the fields "main", "typings" and "tsdocMetadata" are not defined', () => { it('outputs the tsdoc metadata file "tsdoc-metadata.json" in the folder where package.json is located', () => { - const { - packageFolder, - packageJson - } = getPackageMetadata('package-default'); - expect(PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath)) - .toBe(path.resolve(packageFolder, 'tsdoc-metadata.json')); + const { packageFolder, packageJson } = getPackageMetadata('package-default'); + expect( + PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) + ).toBe(path.resolve(packageFolder, 'tsdoc-metadata.json')); }); }); }); @@ -93,42 +86,34 @@ describe('PackageMetadataManager', () => { const tsdocMetadataPath: string = 'path/to/custom-tsdoc-metadata.json'; describe('given a package.json where the field "tsdocMetadata" is defined', () => { it('outputs the tsdoc metadata file at the provided path in the folder where package.json is located', () => { - const { - packageFolder, - packageJson - } = getPackageMetadata('package-inferred-from-tsdocMetadata'); - expect(PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath)) - .toBe(path.resolve(packageFolder, tsdocMetadataPath)); + const { packageFolder, packageJson } = getPackageMetadata('package-inferred-from-tsdocMetadata'); + expect( + PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) + ).toBe(path.resolve(packageFolder, tsdocMetadataPath)); }); }); describe('given a package.json where the field "typings" is defined and "tsdocMetadata" is not defined', () => { it('outputs the tsdoc metadata file at the provided path in the folder where package.json is located', () => { - const { - packageFolder, - packageJson - } = getPackageMetadata('package-inferred-from-typings'); - expect(PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath)) - .toBe(path.resolve(packageFolder, tsdocMetadataPath)); + const { packageFolder, packageJson } = getPackageMetadata('package-inferred-from-typings'); + expect( + PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) + ).toBe(path.resolve(packageFolder, tsdocMetadataPath)); }); }); describe('given a package.json where the field "main" is defined but not "typings" nor "tsdocMetadata"', () => { it('outputs the tsdoc metadata file at the provided path in the folder where package.json is located', () => { - const { - packageFolder, - packageJson - } = getPackageMetadata('package-inferred-from-main'); - expect(PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath)) - .toBe(path.resolve(packageFolder, tsdocMetadataPath)); + const { packageFolder, packageJson } = getPackageMetadata('package-inferred-from-main'); + expect( + PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) + ).toBe(path.resolve(packageFolder, tsdocMetadataPath)); }); }); describe('given a package.json where the fields "main", "typings" and "tsdocMetadata" are not defined', () => { it('outputs the tsdoc metadata file at the provided path in the folder where package.json is located', () => { - const { - packageFolder, - packageJson - } = getPackageMetadata('package-default'); - expect(PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath)) - .toBe(path.resolve(packageFolder, tsdocMetadataPath)); + const { packageFolder, packageJson } = getPackageMetadata('package-default'); + expect( + PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) + ).toBe(path.resolve(packageFolder, tsdocMetadataPath)); }); }); }); diff --git a/src/api/CompilerState.ts b/src/api/CompilerState.ts index e56bff07b8d..b1f438bc91a 100644 --- a/src/api/CompilerState.ts +++ b/src/api/CompilerState.ts @@ -5,9 +5,7 @@ import * as path from 'path'; import * as ts from 'typescript'; import colors = require('colors'); -import { - JsonFile -} from '@rushstack/node-core-library'; +import { JsonFile } from '@rushstack/node-core-library'; import { ExtractorConfig } from './ExtractorConfig'; import { IExtractorInvokeOptions } from './Extractor'; @@ -45,8 +43,10 @@ export class CompilerState { /** * Create a compiler state for use with the specified `IExtractorInvokeOptions`. */ - public static create(extractorConfig: ExtractorConfig, options?: ICompilerStateCreateOptions): CompilerState { - + public static create( + extractorConfig: ExtractorConfig, + options?: ICompilerStateCreateOptions + ): CompilerState { let tsconfig: {} | undefined = extractorConfig.overrideTsconfig; let configBasePath: string = extractorConfig.projectFolder; if (!tsconfig) { @@ -55,18 +55,16 @@ export class CompilerState { configBasePath = path.resolve(path.dirname(extractorConfig.tsconfigFilePath)); } - const commandLine: ts.ParsedCommandLine = ts.parseJsonConfigFileContent( - tsconfig, - ts.sys, - configBasePath - ); + const commandLine: ts.ParsedCommandLine = ts.parseJsonConfigFileContent(tsconfig, ts.sys, configBasePath); if (!commandLine.options.skipLibCheck && extractorConfig.skipLibCheck) { commandLine.options.skipLibCheck = true; - console.log(colors.cyan( - 'API Extractor was invoked with skipLibCheck. This is not recommended and may cause ' + - 'incorrect type analysis.' - )); + console.log( + colors.cyan( + 'API Extractor was invoked with skipLibCheck. This is not recommended and may cause ' + + 'incorrect type analysis.' + ) + ); } const inputFilePaths: string[] = commandLine.fileNames.concat(extractorConfig.mainEntryPointFilePath); @@ -91,7 +89,7 @@ export class CompilerState { }); } - /** + /** * Given a list of absolute file paths, return a list containing only the declaration * files. Duplicates are also eliminated. * @@ -128,9 +126,10 @@ export class CompilerState { return analysisFilePaths; } - private static _createCompilerHost(commandLine: ts.ParsedCommandLine, - options: IExtractorInvokeOptions | undefined): ts.CompilerHost { - + private static _createCompilerHost( + commandLine: ts.ParsedCommandLine, + options: IExtractorInvokeOptions | undefined + ): ts.CompilerHost { // Create a default CompilerHost that we will override const compilerHost: ts.CompilerHost = ts.createCompilerHost(commandLine.options); diff --git a/src/api/Extractor.ts b/src/api/Extractor.ts index d25c412babf..2ad06db17ae 100644 --- a/src/api/Extractor.ts +++ b/src/api/Extractor.ts @@ -3,12 +3,7 @@ import * as path from 'path'; import * as ts from 'typescript'; -import { - FileSystem, - NewlineKind, - PackageJsonLookup, - IPackageJson -} from '@rushstack/node-core-library'; +import { FileSystem, NewlineKind, PackageJsonLookup, IPackageJson } from '@rushstack/node-core-library'; import { ExtractorConfig } from './ExtractorConfig'; import { Collector } from '../collector/Collector'; @@ -171,7 +166,10 @@ export class Extractor { /** * Load the api-extractor.json config file from the specified path, and then invoke API Extractor. */ - public static loadConfigAndInvoke(configFilePath: string, options?: IExtractorInvokeOptions): ExtractorResult { + public static loadConfigAndInvoke( + configFilePath: string, + options?: IExtractorInvokeOptions + ): ExtractorResult { const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare(configFilePath); return Extractor.invoke(extractorConfig, options); @@ -181,9 +179,8 @@ export class Extractor { * Invoke API Extractor using an already prepared `ExtractorConfig` object. */ public static invoke(extractorConfig: ExtractorConfig, options?: IExtractorInvokeOptions): ExtractorResult { - if (!options) { - options = { }; + options = {}; } const localBuild: boolean = options.localBuild || false; @@ -198,7 +195,7 @@ export class Extractor { const messageRouter: MessageRouter = new MessageRouter({ workingPackageFolder: extractorConfig.packageFolder, messageCallback: options.messageCallback, - messagesConfig: extractorConfig.messages || { }, + messagesConfig: extractorConfig.messages || {}, showVerboseMessages: !!options.showVerboseMessages, showDiagnostics: !!options.showDiagnostics }); @@ -209,7 +206,9 @@ export class Extractor { messageRouter.logDiagnosticFooter(); messageRouter.logDiagnosticHeader('Compiler options'); - const serializedOptions: object = MessageRouter.buildJsonDumpObject((compilerState.program as ts.Program).getCompilerOptions()); + const serializedOptions: object = MessageRouter.buildJsonDumpObject( + (compilerState.program as ts.Program).getCompilerOptions() + ); messageRouter.logDiagnostic(JSON.stringify(serializedOptions, undefined, 2)); messageRouter.logDiagnosticFooter(); } @@ -233,7 +232,10 @@ export class Extractor { } if (extractorConfig.docModelEnabled) { - messageRouter.logVerbose(ConsoleMessageId.WritingDocModelFile, 'Writing: ' + extractorConfig.apiJsonFilePath); + messageRouter.logVerbose( + ConsoleMessageId.WritingDocModelFile, + 'Writing: ' + extractorConfig.apiJsonFilePath + ); apiPackage.saveToJsonFile(extractorConfig.apiJsonFilePath, { toolPackage: Extractor.packageName, toolVersion: Extractor.version, @@ -248,10 +250,14 @@ export class Extractor { if (extractorConfig.apiReportEnabled) { const actualApiReportPath: string = extractorConfig.reportTempFilePath; - const actualApiReportShortPath: string = extractorConfig._getShortFilePath(extractorConfig.reportTempFilePath); + const actualApiReportShortPath: string = extractorConfig._getShortFilePath( + extractorConfig.reportTempFilePath + ); const expectedApiReportPath: string = extractorConfig.reportFilePath; - const expectedApiReportShortPath: string = extractorConfig._getShortFilePath(extractorConfig.reportFilePath); + const expectedApiReportShortPath: string = extractorConfig._getShortFilePath( + extractorConfig.reportFilePath + ); const actualApiReportContent: string = ApiReportGenerator.generateReviewFileContent(collector); @@ -265,30 +271,38 @@ export class Extractor { if (FileSystem.exists(expectedApiReportPath)) { const expectedApiReportContent: string = FileSystem.readFile(expectedApiReportPath); - if (!ApiReportGenerator.areEquivalentApiFileContents(actualApiReportContent, expectedApiReportContent)) { + if ( + !ApiReportGenerator.areEquivalentApiFileContents(actualApiReportContent, expectedApiReportContent) + ) { apiReportChanged = true; if (!localBuild) { // For a production build, issue a warning that will break the CI build. - messageRouter.logWarning(ConsoleMessageId.ApiReportNotCopied, - 'You have changed the public API signature for this project.' - + ` Please copy the file "${actualApiReportShortPath}" to "${expectedApiReportShortPath}",` - + ` or perform a local build (which does this automatically).` - + ` See the Git repo documentation for more info.`); + messageRouter.logWarning( + ConsoleMessageId.ApiReportNotCopied, + 'You have changed the public API signature for this project.' + + ` Please copy the file "${actualApiReportShortPath}" to "${expectedApiReportShortPath}",` + + ` or perform a local build (which does this automatically).` + + ` See the Git repo documentation for more info.` + ); } else { // For a local build, just copy the file automatically. - messageRouter.logWarning(ConsoleMessageId.ApiReportCopied, - 'You have changed the public API signature for this project.' - + ` Updating ${expectedApiReportShortPath}`); + messageRouter.logWarning( + ConsoleMessageId.ApiReportCopied, + 'You have changed the public API signature for this project.' + + ` Updating ${expectedApiReportShortPath}` + ); FileSystem.writeFile(expectedApiReportPath, actualApiReportContent, { ensureFolderExists: true, convertLineEndings: extractorConfig.newlineKind }); } - } else { - messageRouter.logVerbose(ConsoleMessageId.ApiReportUnchanged, - `The API report is up to date: ${actualApiReportShortPath}`); + } else { + messageRouter.logVerbose( + ConsoleMessageId.ApiReportUnchanged, + `The API report is up to date: ${actualApiReportShortPath}` + ); } } else { // The target file does not exist, so we are setting up the API review file for the first time. @@ -300,25 +314,29 @@ export class Extractor { if (!localBuild) { // For a production build, issue a warning that will break the CI build. - messageRouter.logWarning(ConsoleMessageId.ApiReportNotCopied, - 'The API report file is missing.' - + ` Please copy the file "${actualApiReportShortPath}" to "${expectedApiReportShortPath}",` - + ` or perform a local build (which does this automatically).` - + ` See the Git repo documentation for more info.`); + messageRouter.logWarning( + ConsoleMessageId.ApiReportNotCopied, + 'The API report file is missing.' + + ` Please copy the file "${actualApiReportShortPath}" to "${expectedApiReportShortPath}",` + + ` or perform a local build (which does this automatically).` + + ` See the Git repo documentation for more info.` + ); } else { const expectedApiReportFolder: string = path.dirname(expectedApiReportPath); if (!FileSystem.exists(expectedApiReportFolder)) { - messageRouter.logError(ConsoleMessageId.ApiReportFolderMissing, - 'Unable to create the API report file. Please make sure the target folder exists:\n' - + expectedApiReportFolder + messageRouter.logError( + ConsoleMessageId.ApiReportFolderMissing, + 'Unable to create the API report file. Please make sure the target folder exists:\n' + + expectedApiReportFolder ); } else { FileSystem.writeFile(expectedApiReportPath, actualApiReportContent, { convertLineEndings: extractorConfig.newlineKind }); - messageRouter.logWarning(ConsoleMessageId.ApiReportCreated, - 'The API report file was missing, so a new file was created. Please add this file to Git:\n' - + expectedApiReportPath + messageRouter.logWarning( + ConsoleMessageId.ApiReportCreated, + 'The API report file was missing, so a new file was created. Please add this file to Git:\n' + + expectedApiReportPath ); } } @@ -327,17 +345,31 @@ export class Extractor { if (extractorConfig.rollupEnabled) { Extractor._generateRollupDtsFile( - collector, extractorConfig.publicTrimmedFilePath, DtsRollupKind.PublicRelease, extractorConfig.newlineKind); + collector, + extractorConfig.publicTrimmedFilePath, + DtsRollupKind.PublicRelease, + extractorConfig.newlineKind + ); Extractor._generateRollupDtsFile( - collector, extractorConfig.betaTrimmedFilePath, DtsRollupKind.BetaRelease, extractorConfig.newlineKind); + collector, + extractorConfig.betaTrimmedFilePath, + DtsRollupKind.BetaRelease, + extractorConfig.newlineKind + ); Extractor._generateRollupDtsFile( - collector, extractorConfig.untrimmedFilePath, DtsRollupKind.InternalRelease, extractorConfig.newlineKind); + collector, + extractorConfig.untrimmedFilePath, + DtsRollupKind.InternalRelease, + extractorConfig.newlineKind + ); } if (extractorConfig.tsdocMetadataEnabled) { // Write the tsdoc-metadata.json file for this project PackageMetadataManager.writeTsdocMetadataFile( - extractorConfig.tsdocMetadataFilePath, extractorConfig.newlineKind); + extractorConfig.tsdocMetadataFilePath, + extractorConfig.newlineKind + ); } // Show all the messages that we collected during analysis @@ -364,10 +396,16 @@ export class Extractor { } private static _generateRollupDtsFile( - collector: Collector, outputPath: string, dtsKind: DtsRollupKind, newlineKind: NewlineKind + collector: Collector, + outputPath: string, + dtsKind: DtsRollupKind, + newlineKind: NewlineKind ): void { if (outputPath !== '') { - collector.messageRouter.logVerbose(ConsoleMessageId.WritingDtsRollup, `Writing package typings: ${outputPath}`); + collector.messageRouter.logVerbose( + ConsoleMessageId.WritingDtsRollup, + `Writing package typings: ${outputPath}` + ); DtsRollupGenerator.writeTypingsFile(collector, outputPath, dtsKind, newlineKind); } } diff --git a/src/api/ExtractorConfig.ts b/src/api/ExtractorConfig.ts index 5c3010653bf..ad67b2703e2 100644 --- a/src/api/ExtractorConfig.ts +++ b/src/api/ExtractorConfig.ts @@ -17,10 +17,7 @@ import { Path, NewlineKind } from '@rushstack/node-core-library'; -import { - IConfigFile, - IExtractorMessagesConfig -} from './IConfigFile'; +import { IConfigFile, IExtractorMessagesConfig } from './IConfigFile'; import { PackageMetadataManager } from '../analyzer/PackageMetadataManager'; import { MessageRouter } from '../collector/MessageRouter'; @@ -98,7 +95,7 @@ interface IExtractorConfigParameters { mainEntryPointFilePath: string; bundledPackages: string[]; tsconfigFilePath: string; - overrideTsconfig: { } | undefined; + overrideTsconfig: {} | undefined; skipLibCheck: boolean; apiReportEnabled: boolean; reportFilePath: string; @@ -126,15 +123,17 @@ export class ExtractorConfig { * The JSON Schema for API Extractor config file (api-extractor.schema.json). */ public static readonly jsonSchema: JsonSchema = JsonSchema.fromFile( - path.join(__dirname, '../schemas/api-extractor.schema.json')); + path.join(__dirname, '../schemas/api-extractor.schema.json') + ); /** * The config file name "api-extractor.json". */ public static readonly FILENAME: string = 'api-extractor.json'; - private static readonly _defaultConfig: Partial = JsonFile.load(path.join(__dirname, - '../schemas/api-extractor-defaults.json')); + private static readonly _defaultConfig: Partial = JsonFile.load( + path.join(__dirname, '../schemas/api-extractor-defaults.json') + ); private static readonly _declarationFileExtensionRegExp: RegExp = /\.d\.ts$/i; @@ -163,7 +162,7 @@ export class ExtractorConfig { public readonly tsconfigFilePath: string; /** {@inheritDoc IConfigCompiler.overrideTsconfig} */ - public readonly overrideTsconfig: { } | undefined; + public readonly overrideTsconfig: {} | undefined; /** {@inheritDoc IConfigCompiler.skipLibCheck} */ public readonly skipLibCheck: boolean; @@ -277,7 +276,8 @@ export class ExtractorConfig { const packageJsonLookup: PackageJsonLookup = new PackageJsonLookup(); const packageJsonFullPath: string | undefined = packageJsonLookup.tryGetPackageJsonFilePathFor( - configObjectFullPath); + configObjectFullPath + ); const extractorConfig: ExtractorConfig = ExtractorConfig.prepare({ configObject, @@ -301,14 +301,16 @@ export class ExtractorConfig { const visitedPaths: Set = new Set(); let currentConfigFilePath: string = path.resolve(process.cwd(), jsonFilePath); - let configObject: Partial = { }; + let configObject: Partial = {}; try { do { // Check if this file was already processed. if (visitedPaths.has(currentConfigFilePath)) { - throw new Error(`The API Extractor "extends" setting contains a cycle.` - + ` This file is included twice: "${currentConfigFilePath}"`); + throw new Error( + `The API Extractor "extends" setting contains a cycle.` + + ` This file is included twice: "${currentConfigFilePath}"` + ); } visitedPaths.add(currentConfigFilePath); @@ -331,12 +333,9 @@ export class ExtractorConfig { // // Resolve "my-package" from the perspective of the current folder. try { - extendsField = resolve.sync( - extendsField, - { - basedir: currentConfigFolderPath - } - ); + extendsField = resolve.sync(extendsField, { + basedir: currentConfigFolderPath + }); } catch (e) { throw new Error(`Error resolving NodeJS path "${extendsField}": ${e.message}`); } @@ -353,7 +352,6 @@ export class ExtractorConfig { currentConfigFilePath = extendsField; } while (currentConfigFilePath); - } catch (e) { throw new Error(`Error loading ${currentConfigFilePath}:\n` + e.message); } @@ -367,69 +365,103 @@ export class ExtractorConfig { return configObject as IConfigFile; } - private static _resolveConfigFileRelativePaths(configFile: IConfigFile, currentConfigFolderPath: string): void { - + private static _resolveConfigFileRelativePaths( + configFile: IConfigFile, + currentConfigFolderPath: string + ): void { if (configFile.projectFolder) { configFile.projectFolder = ExtractorConfig._resolveConfigFileRelativePath( - 'projectFolder', configFile.projectFolder, currentConfigFolderPath); + 'projectFolder', + configFile.projectFolder, + currentConfigFolderPath + ); } if (configFile.mainEntryPointFilePath) { configFile.mainEntryPointFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'mainEntryPointFilePath', configFile.mainEntryPointFilePath, currentConfigFolderPath); + 'mainEntryPointFilePath', + configFile.mainEntryPointFilePath, + currentConfigFolderPath + ); } if (configFile.compiler) { if (configFile.compiler.tsconfigFilePath) { configFile.compiler.tsconfigFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'tsconfigFilePath', configFile.compiler.tsconfigFilePath, currentConfigFolderPath); + 'tsconfigFilePath', + configFile.compiler.tsconfigFilePath, + currentConfigFolderPath + ); } } if (configFile.apiReport) { if (configFile.apiReport.reportFolder) { configFile.apiReport.reportFolder = ExtractorConfig._resolveConfigFileRelativePath( - 'reportFolder', configFile.apiReport.reportFolder, currentConfigFolderPath); + 'reportFolder', + configFile.apiReport.reportFolder, + currentConfigFolderPath + ); } if (configFile.apiReport.reportTempFolder) { configFile.apiReport.reportTempFolder = ExtractorConfig._resolveConfigFileRelativePath( - 'reportTempFolder', configFile.apiReport.reportTempFolder, currentConfigFolderPath); + 'reportTempFolder', + configFile.apiReport.reportTempFolder, + currentConfigFolderPath + ); } } if (configFile.docModel) { if (configFile.docModel.apiJsonFilePath) { configFile.docModel.apiJsonFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'apiJsonFilePath', configFile.docModel.apiJsonFilePath, currentConfigFolderPath); + 'apiJsonFilePath', + configFile.docModel.apiJsonFilePath, + currentConfigFolderPath + ); } } if (configFile.dtsRollup) { if (configFile.dtsRollup.untrimmedFilePath) { configFile.dtsRollup.untrimmedFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'untrimmedFilePath', configFile.dtsRollup.untrimmedFilePath, currentConfigFolderPath); + 'untrimmedFilePath', + configFile.dtsRollup.untrimmedFilePath, + currentConfigFolderPath + ); } if (configFile.dtsRollup.betaTrimmedFilePath) { configFile.dtsRollup.betaTrimmedFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'betaTrimmedFilePath', configFile.dtsRollup.betaTrimmedFilePath, currentConfigFolderPath); + 'betaTrimmedFilePath', + configFile.dtsRollup.betaTrimmedFilePath, + currentConfigFolderPath + ); } if (configFile.dtsRollup.publicTrimmedFilePath) { configFile.dtsRollup.publicTrimmedFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'publicTrimmedFilePath', configFile.dtsRollup.publicTrimmedFilePath, currentConfigFolderPath); + 'publicTrimmedFilePath', + configFile.dtsRollup.publicTrimmedFilePath, + currentConfigFolderPath + ); } } if (configFile.tsdocMetadata) { if (configFile.tsdocMetadata.tsdocMetadataFilePath) { configFile.tsdocMetadata.tsdocMetadataFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'tsdocMetadataFilePath', configFile.tsdocMetadata.tsdocMetadataFilePath, currentConfigFolderPath); + 'tsdocMetadataFilePath', + configFile.tsdocMetadata.tsdocMetadataFilePath, + currentConfigFolderPath + ); } } } - private static _resolveConfigFileRelativePath(fieldName: string, fieldValue: string, - currentConfigFolderPath: string): string { - + private static _resolveConfigFileRelativePath( + fieldName: string, + fieldValue: string, + currentConfigFolderPath: string + ): string { if (!path.isAbsolute(fieldValue)) { if (fieldValue.indexOf('') !== 0) { // If the path is not absolute and does not start with "", then resolve it relative @@ -451,7 +483,9 @@ export class ExtractorConfig { const configObject: Partial = options.configObject; if (configObject.extends) { - throw new Error('The IConfigFile.extends field must be expanded before calling ExtractorConfig.prepare()'); + throw new Error( + 'The IConfigFile.extends field must be expanded before calling ExtractorConfig.prepare()' + ); } if (options.configObjectFullPath) { @@ -486,7 +520,6 @@ export class ExtractorConfig { } try { - if (!configObject.compiler) { // A merged configuration should have this throw new Error('The "compiler" section is missing'); @@ -500,8 +533,10 @@ export class ExtractorConfig { let projectFolder: string; if (configObject.projectFolder.trim() === '') { if (!options.configObjectFullPath) { - throw new Error('The "projectFolder" setting uses the "" token, but it cannot be expanded because' - + ' the "configObjectFullPath" setting was not specified'); + throw new Error( + 'The "projectFolder" setting uses the "" token, but it cannot be expanded because' + + ' the "configObjectFullPath" setting was not specified' + ); } // "The default value for `projectFolder` is the token ``, which means the folder is determined @@ -510,7 +545,7 @@ export class ExtractorConfig { // this way, then an error will be reported." let currentFolder: string = path.dirname(options.configObjectFullPath); - for (; ; ) { + for (;;) { const tsconfigPath: string = path.join(currentFolder, 'tsconfig.json'); if (FileSystem.exists(tsconfigPath)) { projectFolder = currentFolder; @@ -518,8 +553,10 @@ export class ExtractorConfig { } const parentFolder: string = path.dirname(currentFolder); if (parentFolder === '' || parentFolder === currentFolder) { - throw new Error('The "projectFolder" setting uses the "" token, but a tsconfig.json file cannot be' - + ' found in this folder or any parent folder.'); + throw new Error( + 'The "projectFolder" setting uses the "" token, but a tsconfig.json file cannot be' + + ' found in this folder or any parent folder.' + ); } currentFolder = parentFolder; } @@ -548,11 +585,16 @@ export class ExtractorConfig { // A merged configuration should have this throw new Error('The "mainEntryPointFilePath" setting is missing'); } - const mainEntryPointFilePath: string = ExtractorConfig._resolvePathWithTokens('mainEntryPointFilePath', - configObject.mainEntryPointFilePath, tokenContext); + const mainEntryPointFilePath: string = ExtractorConfig._resolvePathWithTokens( + 'mainEntryPointFilePath', + configObject.mainEntryPointFilePath, + tokenContext + ); if (!ExtractorConfig.hasDtsFileExtension(mainEntryPointFilePath)) { - throw new Error('The "mainEntryPointFilePath" value is not a declaration file: ' + mainEntryPointFilePath); + throw new Error( + 'The "mainEntryPointFilePath" value is not a declaration file: ' + mainEntryPointFilePath + ); } if (!FileSystem.exists(mainEntryPointFilePath)) { @@ -566,8 +608,11 @@ export class ExtractorConfig { } } - const tsconfigFilePath: string = ExtractorConfig._resolvePathWithTokens('tsconfigFilePath', - configObject.compiler.tsconfigFilePath, tokenContext); + const tsconfigFilePath: string = ExtractorConfig._resolvePathWithTokens( + 'tsconfigFilePath', + configObject.compiler.tsconfigFilePath, + tokenContext + ); if (configObject.compiler.overrideTsconfig === undefined) { if (!tsconfigFilePath) { @@ -584,8 +629,11 @@ export class ExtractorConfig { if (configObject.apiReport) { apiReportEnabled = !!configObject.apiReport.enabled; - const reportFilename: string = ExtractorConfig._expandStringWithTokens('reportFileName', - configObject.apiReport.reportFileName || '', tokenContext); + const reportFilename: string = ExtractorConfig._expandStringWithTokens( + 'reportFileName', + configObject.apiReport.reportFileName || '', + tokenContext + ); if (!reportFilename) { // A merged configuration should have this @@ -596,10 +644,16 @@ export class ExtractorConfig { throw new Error(`The "reportFilename" setting contains invalid characters: "${reportFilename}"`); } - const reportFolder: string = ExtractorConfig._resolvePathWithTokens('reportFolder', - configObject.apiReport.reportFolder, tokenContext); - const reportTempFolder: string = ExtractorConfig._resolvePathWithTokens('reportTempFolder', - configObject.apiReport.reportTempFolder, tokenContext); + const reportFolder: string = ExtractorConfig._resolvePathWithTokens( + 'reportFolder', + configObject.apiReport.reportFolder, + tokenContext + ); + const reportTempFolder: string = ExtractorConfig._resolvePathWithTokens( + 'reportTempFolder', + configObject.apiReport.reportTempFolder, + tokenContext + ); reportFilePath = path.join(reportFolder, reportFilename); reportTempFilePath = path.join(reportTempFolder, reportFilename); @@ -609,8 +663,11 @@ export class ExtractorConfig { let apiJsonFilePath: string = ''; if (configObject.docModel) { docModelEnabled = !!configObject.docModel.enabled; - apiJsonFilePath = ExtractorConfig._resolvePathWithTokens('apiJsonFilePath', - configObject.docModel.apiJsonFilePath, tokenContext); + apiJsonFilePath = ExtractorConfig._resolvePathWithTokens( + 'apiJsonFilePath', + configObject.docModel.apiJsonFilePath, + tokenContext + ); } let tsdocMetadataEnabled: boolean = false; @@ -623,25 +680,34 @@ export class ExtractorConfig { if (tsdocMetadataFilePath.trim() === '') { if (!packageJson) { - throw new Error('The "" token cannot be used with the "tsdocMetadataFilePath" setting because' - + ' the "packageJson" option was not provided'); + throw new Error( + 'The "" token cannot be used with the "tsdocMetadataFilePath" setting because' + + ' the "packageJson" option was not provided' + ); } if (!packageJsonFullPath) { - throw new Error('The "" token cannot be used with "tsdocMetadataFilePath" because' - + 'the "packageJsonFullPath" option was not provided'); + throw new Error( + 'The "" token cannot be used with "tsdocMetadataFilePath" because' + + 'the "packageJsonFullPath" option was not provided' + ); } tsdocMetadataFilePath = PackageMetadataManager.resolveTsdocMetadataPath( path.dirname(packageJsonFullPath), packageJson ); } else { - tsdocMetadataFilePath = ExtractorConfig._resolvePathWithTokens('tsdocMetadataFilePath', - configObject.tsdocMetadata.tsdocMetadataFilePath, tokenContext); + tsdocMetadataFilePath = ExtractorConfig._resolvePathWithTokens( + 'tsdocMetadataFilePath', + configObject.tsdocMetadata.tsdocMetadataFilePath, + tokenContext + ); } if (!tsdocMetadataFilePath) { - throw new Error('The "tsdocMetadata.enabled" setting is enabled,' - + ' but "tsdocMetadataFilePath" is not specified'); + throw new Error( + 'The "tsdocMetadata.enabled" setting is enabled,' + + ' but "tsdocMetadataFilePath" is not specified' + ); } } } @@ -654,12 +720,21 @@ export class ExtractorConfig { if (configObject.dtsRollup) { rollupEnabled = !!configObject.dtsRollup.enabled; - untrimmedFilePath = ExtractorConfig._resolvePathWithTokens('untrimmedFilePath', - configObject.dtsRollup.untrimmedFilePath, tokenContext); - betaTrimmedFilePath = ExtractorConfig._resolvePathWithTokens('betaTrimmedFilePath', - configObject.dtsRollup.betaTrimmedFilePath, tokenContext); - publicTrimmedFilePath = ExtractorConfig._resolvePathWithTokens('publicTrimmedFilePath', - configObject.dtsRollup.publicTrimmedFilePath, tokenContext); + untrimmedFilePath = ExtractorConfig._resolvePathWithTokens( + 'untrimmedFilePath', + configObject.dtsRollup.untrimmedFilePath, + tokenContext + ); + betaTrimmedFilePath = ExtractorConfig._resolvePathWithTokens( + 'betaTrimmedFilePath', + configObject.dtsRollup.betaTrimmedFilePath, + tokenContext + ); + publicTrimmedFilePath = ExtractorConfig._resolvePathWithTokens( + 'publicTrimmedFilePath', + configObject.dtsRollup.publicTrimmedFilePath, + tokenContext + ); omitTrimmingComments = !!configObject.dtsRollup.omitTrimmingComments; } @@ -698,18 +773,19 @@ export class ExtractorConfig { tsdocMetadataEnabled, tsdocMetadataFilePath, newlineKind, - messages: configObject.messages || { }, + messages: configObject.messages || {}, testMode: !!configObject.testMode }); - } catch (e) { throw new Error(`Error parsing ${filenameForErrors}:\n` + e.message); } } - private static _resolvePathWithTokens(fieldName: string, value: string | undefined, - tokenContext: IExtractorConfigTokenContext): string { - + private static _resolvePathWithTokens( + fieldName: string, + value: string | undefined, + tokenContext: IExtractorConfigTokenContext + ): string { value = ExtractorConfig._expandStringWithTokens(fieldName, value, tokenContext); if (value !== '') { value = path.resolve(tokenContext.projectFolder, value); @@ -717,8 +793,11 @@ export class ExtractorConfig { return value; } - private static _expandStringWithTokens(fieldName: string, value: string | undefined, - tokenContext: IExtractorConfigTokenContext): string { + private static _expandStringWithTokens( + fieldName: string, + value: string | undefined, + tokenContext: IExtractorConfigTokenContext + ): string { value = value ? value.trim() : ''; if (value !== '') { value = Text.replaceAll(value, '', tokenContext.unscopedPackageName); @@ -732,8 +811,10 @@ export class ExtractorConfig { if (value.indexOf(projectFolderToken) >= 0) { // If after all replacements, "" appears somewhere in the string, report an error - throw new Error(`The "${fieldName}" value incorrectly uses the "" token.` - + ` It must appear at the start of the string.`); + throw new Error( + `The "${fieldName}" value incorrectly uses the "" token.` + + ` It must appear at the start of the string.` + ); } if (value.indexOf('') >= 0) { diff --git a/src/api/ExtractorMessage.ts b/src/api/ExtractorMessage.ts index be5e8d37e57..4e64d31cdba 100644 --- a/src/api/ExtractorMessage.ts +++ b/src/api/ExtractorMessage.ts @@ -13,7 +13,6 @@ import { SourceFileLocationFormatter } from '../analyzer/SourceFileLocationForma * @public */ export interface IExtractorMessageProperties { - /** * A declaration can have multiple names if it is exported more than once. * If an `ExtractorMessage` applies to a specific export name, this property can indicate that. @@ -138,7 +137,7 @@ export class ExtractorMessage { this.sourceFilePath = options.sourceFilePath; this.sourceFileLine = options.sourceFileLine; this.sourceFileColumn = options.sourceFileColumn; - this.properties = options.properties || { }; + this.properties = options.properties || {}; this._handled = false; this._logLevel = options.logLevel || ExtractorLogLevel.None; @@ -162,7 +161,9 @@ export class ExtractorMessage { public set handled(value: boolean) { if (this._handled && !value) { - throw new Error('One a message has been marked as handled, the "handled" property cannot be set to false'); + throw new Error( + 'One a message has been marked as handled, the "handled" property cannot be set to false' + ); } this._handled = value; } diff --git a/src/api/ExtractorMessageId.ts b/src/api/ExtractorMessageId.ts index 6caf70a2496..bb63cfb84c6 100644 --- a/src/api/ExtractorMessageId.ts +++ b/src/api/ExtractorMessageId.ts @@ -95,7 +95,7 @@ export const enum ExtractorMessageId { /** * "The property ___ has a setter but no getter." */ - MissingGetter = 'ae-missing-getter', + MissingGetter = 'ae-missing-getter' } export const allExtractorMessageIds: Set = new Set([ diff --git a/src/api/IConfigFile.ts b/src/api/IConfigFile.ts index e37281b3879..3eee451e1c5 100644 --- a/src/api/IConfigFile.ts +++ b/src/api/IConfigFile.ts @@ -33,7 +33,7 @@ export interface IConfigCompiler { * * If omitted, then the tsconfig.json file will instead be read from the projectFolder. */ - overrideTsconfig?: { }; + overrideTsconfig?: {}; /** * This option causes the compiler to be invoked with the `--skipLibCheck` option. diff --git a/src/cli/ApiExtractorCommandLine.ts b/src/cli/ApiExtractorCommandLine.ts index 60e04681d43..1a11b335fbd 100644 --- a/src/cli/ApiExtractorCommandLine.ts +++ b/src/cli/ApiExtractorCommandLine.ts @@ -16,16 +16,18 @@ export class ApiExtractorCommandLine extends CommandLineParser { public constructor() { super({ toolFilename: 'api-extractor', - toolDescription: 'API Extractor helps you build better TypeScript libraries. It analyzes the main entry' - + ' point for your package, collects the inventory of exported declarations, and then generates three kinds' - + ' of output: an API report file (.api.md) to facilitate reviews, a declaration rollup (.d.ts) to be' - + ' published with your NPM package, and a doc model file (.api.json) to be used with a documentation' - + ' tool such as api-documenter. For details, please visit the web site.' + toolDescription: + 'API Extractor helps you build better TypeScript libraries. It analyzes the main entry' + + ' point for your package, collects the inventory of exported declarations, and then generates three kinds' + + ' of output: an API report file (.api.md) to facilitate reviews, a declaration rollup (.d.ts) to be' + + ' published with your NPM package, and a doc model file (.api.json) to be used with a documentation' + + ' tool such as api-documenter. For details, please visit the web site.' }); this._populateActions(); } - protected onDefineParameters(): void { // override + protected onDefineParameters(): void { + // override this._debugParameter = this.defineFlagParameter({ parameterLongName: '--debug', parameterShortName: '-d', @@ -33,13 +35,13 @@ export class ApiExtractorCommandLine extends CommandLineParser { }); } - protected onExecute(): Promise { // override + protected onExecute(): Promise { + // override if (this._debugParameter.value) { InternalError.breakInDebugger = true; } return super.onExecute().catch((error) => { - if (this._debugParameter.value) { console.error(os.EOL + error.stack); } else { diff --git a/src/cli/InitAction.ts b/src/cli/InitAction.ts index 120918d1a2f..30228b68fe6 100644 --- a/src/cli/InitAction.ts +++ b/src/cli/InitAction.ts @@ -10,22 +10,24 @@ import { ApiExtractorCommandLine } from './ApiExtractorCommandLine'; import { ExtractorConfig } from '../api/ExtractorConfig'; export class InitAction extends CommandLineAction { - public constructor(parser: ApiExtractorCommandLine) { super({ actionName: 'init', summary: `Create an ${ExtractorConfig.FILENAME} config file`, - documentation: `Use this command when setting up API Extractor for a new project. It writes an` - + ` ${ExtractorConfig.FILENAME} config file template with code comments that describe all the settings.` - + ` The file will be written in the current directory.` + documentation: + `Use this command when setting up API Extractor for a new project. It writes an` + + ` ${ExtractorConfig.FILENAME} config file template with code comments that describe all the settings.` + + ` The file will be written in the current directory.` }); } - protected onDefineParameters(): void { // override + protected onDefineParameters(): void { + // override // No parameters yet } - protected onExecute(): Promise { // override + protected onExecute(): Promise { + // override const inputFilePath: string = path.resolve(__dirname, '../schemas/api-extractor-template.json'); const outputFilePath: string = path.resolve(ExtractorConfig.FILENAME); @@ -41,8 +43,10 @@ export class InitAction extends CommandLineAction { destinationPath: outputFilePath }); - console.log('\nThe recommended location for this file is in the project\'s "config" subfolder,\n' - + 'or else in the top-level folder with package.json.'); + console.log( + '\nThe recommended location for this file is in the project\'s "config" subfolder,\n' + + 'or else in the top-level folder with package.json.' + ); return Promise.resolve(); } diff --git a/src/cli/RunAction.ts b/src/cli/RunAction.ts index 7866391c531..85f971814ac 100644 --- a/src/cli/RunAction.ts +++ b/src/cli/RunAction.ts @@ -4,11 +4,7 @@ import * as colors from 'colors'; import * as os from 'os'; import * as path from 'path'; -import { - PackageJsonLookup, - FileSystem, - IPackageJson -} from '@rushstack/node-core-library'; +import { PackageJsonLookup, FileSystem, IPackageJson } from '@rushstack/node-core-library'; import { CommandLineAction, @@ -37,7 +33,8 @@ export class RunAction extends CommandLineAction { }); } - protected onDefineParameters(): void { // override + protected onDefineParameters(): void { + // override this._configFileParameter = this.defineStringParameter({ parameterLongName: '--config', parameterShortName: '-c', @@ -48,10 +45,11 @@ export class RunAction extends CommandLineAction { this._localParameter = this.defineFlagParameter({ parameterLongName: '--local', parameterShortName: '-l', - description: 'Indicates that API Extractor is running as part of a local build,' - + ' e.g. on a developer\'s machine. This disables certain validation that would' - + ' normally be performed for a ship/production build. For example, the *.api.md' - + ' report file is automatically copied in a local build.' + description: + 'Indicates that API Extractor is running as part of a local build,' + + " e.g. on a developer's machine. This disables certain validation that would" + + ' normally be performed for a ship/production build. For example, the *.api.md' + + ' report file is automatically copied in a local build.' }); this._verboseParameter = this.defineFlagParameter({ @@ -62,22 +60,25 @@ export class RunAction extends CommandLineAction { this._diagnosticsParameter = this.defineFlagParameter({ parameterLongName: '--diagnostics', - description: 'Show diagnostic messages used for troubleshooting problems with API Extractor.' - + ' This flag also enables the "--verbose" flag.' + description: + 'Show diagnostic messages used for troubleshooting problems with API Extractor.' + + ' This flag also enables the "--verbose" flag.' }); this._typescriptCompilerFolder = this.defineStringParameter({ parameterLongName: '--typescript-compiler-folder', argumentName: 'PATH', - description: 'API Extractor uses its own TypeScript compiler engine to analyze your project. If your project' - + ' is built with a significantly different TypeScript version, sometimes API Extractor may report compilation' - + ' errors due to differences in the system typings (e.g. lib.dom.d.ts). You can use the' - + ' "--typescriptCompilerFolder" option to specify the folder path where you installed the TypeScript package,' - + ' and API Extractor\'s compiler will use those system typings instead.' + description: + 'API Extractor uses its own TypeScript compiler engine to analyze your project. If your project' + + ' is built with a significantly different TypeScript version, sometimes API Extractor may report compilation' + + ' errors due to differences in the system typings (e.g. lib.dom.d.ts). You can use the' + + ' "--typescriptCompilerFolder" option to specify the folder path where you installed the TypeScript package,' + + " and API Extractor's compiler will use those system typings instead." }); } - protected onExecute(): Promise { // override + protected onExecute(): Promise { + // override const lookup: PackageJsonLookup = new PackageJsonLookup(); let configFilename: string; @@ -96,8 +97,8 @@ export class RunAction extends CommandLineAction { ); } else if (typescriptCompilerPackageJson.name !== 'typescript') { throw new Error( - `The path specified in the ${this._typescriptCompilerFolder.longName} parameter is not a TypeScript` - + ' compiler package.' + `The path specified in the ${this._typescriptCompilerFolder.longName} parameter is not a TypeScript` + + ' compiler package.' ); } } else { @@ -124,7 +125,9 @@ export class RunAction extends CommandLineAction { configFilename = path.join(baseFolder, 'config', ExtractorConfig.FILENAME); if (FileSystem.exists(configFilename)) { if (FileSystem.exists(path.join(baseFolder, ExtractorConfig.FILENAME))) { - throw new Error(`Found conflicting ${ExtractorConfig.FILENAME} files in "." and "./config" folders`); + throw new Error( + `Found conflicting ${ExtractorConfig.FILENAME} files in "." and "./config" folders` + ); } } else { // Otherwise try the top-level folder @@ -147,14 +150,12 @@ export class RunAction extends CommandLineAction { packageJsonFullPath: lookup.tryGetPackageJsonFilePathFor(configObjectFullPath) }); - const extractorResult: ExtractorResult = Extractor.invoke(extractorConfig, - { - localBuild: this._localParameter.value, - showVerboseMessages: this._verboseParameter.value, - showDiagnostics: this._diagnosticsParameter.value, - typescriptCompilerFolder: typescriptCompilerFolder - } - ); + const extractorResult: ExtractorResult = Extractor.invoke(extractorConfig, { + localBuild: this._localParameter.value, + showVerboseMessages: this._verboseParameter.value, + showDiagnostics: this._diagnosticsParameter.value, + typescriptCompilerFolder: typescriptCompilerFolder + }); if (extractorResult.succeeded) { console.log(os.EOL + 'API Extractor completed successfully'); diff --git a/src/collector/Collector.ts b/src/collector/Collector.ts index 1d5d119956b..74372c234a1 100644 --- a/src/collector/Collector.ts +++ b/src/collector/Collector.ts @@ -3,20 +3,13 @@ import * as ts from 'typescript'; import * as tsdoc from '@microsoft/tsdoc'; -import { - PackageJsonLookup, - Sort, - InternalError -} from '@rushstack/node-core-library'; -import { - ReleaseTag, - AedocDefinitions -} from '@microsoft/api-extractor-model'; +import { PackageJsonLookup, Sort, InternalError } from '@rushstack/node-core-library'; +import { ReleaseTag, AedocDefinitions } from '@microsoft/api-extractor-model'; import { ExtractorMessageId } from '../api/ExtractorMessageId'; import { CollectorEntity } from './CollectorEntity'; -import { AstSymbolTable, } from '../analyzer/AstSymbolTable'; +import { AstSymbolTable } from '../analyzer/AstSymbolTable'; import { AstEntity } from '../analyzer/AstEntity'; import { AstModule, AstModuleExportInfo } from '../analyzer/AstModule'; import { AstSymbol } from '../analyzer/AstSymbol'; @@ -80,7 +73,10 @@ export class Collector { private _astEntryPoint: AstModule | undefined; private readonly _entities: CollectorEntity[] = []; - private readonly _entitiesByAstEntity: Map = new Map(); + private readonly _entitiesByAstEntity: Map = new Map< + AstEntity, + CollectorEntity + >(); private readonly _starExportedExternalModulePaths: string[] = []; @@ -97,7 +93,8 @@ export class Collector { this.extractorConfig = options.extractorConfig; const entryPointSourceFile: ts.SourceFile | undefined = options.program.getSourceFile( - this.extractorConfig.mainEntryPointFilePath); + this.extractorConfig.mainEntryPointFilePath + ); if (!entryPointSourceFile) { throw new Error('Unable to load file: ' + this.extractorConfig.mainEntryPointFilePath); @@ -125,8 +122,13 @@ export class Collector { const bundledPackageNames: Set = new Set(this.extractorConfig.bundledPackages); - this.astSymbolTable = new AstSymbolTable(this.program, this.typeChecker, this.packageJsonLookup, - bundledPackageNames, this.messageRouter); + this.astSymbolTable = new AstSymbolTable( + this.program, + this.typeChecker, + this.packageJsonLookup, + bundledPackageNames, + this.messageRouter + ); this.astReferenceResolver = new AstReferenceResolver(this); this._cachedOverloadIndexesByDeclaration = new Map(); @@ -199,20 +201,25 @@ export class Collector { const entryPointSourceFile: ts.SourceFile = this.workingPackage.entryPointSourceFile; const astEntryPoint: AstModule = this.astSymbolTable.fetchAstModuleFromWorkingPackage( - entryPointSourceFile); + entryPointSourceFile + ); this._astEntryPoint = astEntryPoint; const packageDocCommentTextRange: ts.TextRange | undefined = PackageDocComment.tryFindInSourceFile( - entryPointSourceFile, this); + entryPointSourceFile, + this + ); if (packageDocCommentTextRange) { - const range: tsdoc.TextRange = tsdoc.TextRange.fromStringRange(entryPointSourceFile.text, - packageDocCommentTextRange.pos, packageDocCommentTextRange.end); + const range: tsdoc.TextRange = tsdoc.TextRange.fromStringRange( + entryPointSourceFile.text, + packageDocCommentTextRange.pos, + packageDocCommentTextRange.end + ); this.workingPackage.tsdocParserContext = this._tsdocParser.parseRange(range); - this.messageRouter.addTsdocMessages(this.workingPackage.tsdocParserContext, - entryPointSourceFile); + this.messageRouter.addTsdocMessages(this.workingPackage.tsdocParserContext, entryPointSourceFile); this.workingPackage.tsdocComment = this.workingPackage.tsdocParserContext!.docComment; } @@ -221,7 +228,9 @@ export class Collector { // Create a CollectorEntity for each top-level export - const astModuleExportInfo: AstModuleExportInfo = this.astSymbolTable.fetchAstModuleExportInfo(astEntryPoint); + const astModuleExportInfo: AstModuleExportInfo = this.astSymbolTable.fetchAstModuleExportInfo( + astEntryPoint + ); for (const [exportName, astEntity] of astModuleExportInfo.exportedLocalEntities) { this._createCollectorEntity(astEntity, exportName); @@ -248,7 +257,7 @@ export class Collector { } } - Sort.sortBy(this._entities, x => x.getSortKey()); + Sort.sortBy(this._entities, (x) => x.getSortKey()); Sort.sortSet(this._dtsTypeReferenceDirectives); Sort.sortSet(this._dtsLibReferenceDirectives); this._starExportedExternalModulePaths.sort(); @@ -397,7 +406,10 @@ export class Collector { } } - private _createEntityForIndirectReferences(astEntity: AstEntity, alreadySeenAstEntities: Set): void { + private _createEntityForIndirectReferences( + astEntity: AstEntity, + alreadySeenAstEntities: Set + ): void { if (alreadySeenAstEntities.has(astEntity)) { return; } @@ -423,11 +435,13 @@ export class Collector { } if (astEntity instanceof AstImportAsModule) { - this.astSymbolTable.fetchAstModuleExportInfo(astEntity.astModule).exportedLocalEntities.forEach((exportedEntity: AstEntity) => { - // Create a CollectorEntity for each top-level export of AstImportInternal entity - this._createCollectorEntity(exportedEntity, undefined); - this._createEntityForIndirectReferences(exportedEntity, alreadySeenAstEntities); // TODO- create entity for module export - }); + this.astSymbolTable + .fetchAstModuleExportInfo(astEntity.astModule) + .exportedLocalEntities.forEach((exportedEntity: AstEntity) => { + // Create a CollectorEntity for each top-level export of AstImportInternal entity + this._createCollectorEntity(exportedEntity, undefined); + this._createEntityForIndirectReferences(exportedEntity, alreadySeenAstEntities); // TODO- create entity for module export + }); } } @@ -469,12 +483,14 @@ export class Collector { // Ensure that each entity has a unique nameForEmit for (const entity of this._entities) { - // What name would we ideally want to emit it as? let idealNameForEmit: string; // If this entity is exported exactly once, then we prefer the exported name - if (entity.singleExportName !== undefined && entity.singleExportName !== ts.InternalSymbolName.Default) { + if ( + entity.singleExportName !== undefined && + entity.singleExportName !== ts.InternalSymbolName.Default + ) { idealNameForEmit = entity.singleExportName; } else { // otherwise use the local name @@ -546,7 +562,9 @@ export class Collector { // Initialize DeclarationMetadata for each declaration for (const astDeclaration of astSymbol.astDeclarations) { if (astDeclaration.declarationMetadata) { - throw new InternalError('AstDeclaration.declarationMetadata is not expected to have been initialized yet'); + throw new InternalError( + 'AstDeclaration.declarationMetadata is not expected to have been initialized yet' + ); } const metadata: InternalDeclarationMetadata = new InternalDeclarationMetadata(); @@ -557,7 +575,6 @@ export class Collector { // Detect ancillary declarations for (const astDeclaration of astSymbol.astDeclarations) { - // For a getter/setter pair, make the setter ancillary to the getter if (astDeclaration.declaration.kind === ts.SyntaxKind.SetAccessor) { let foundGetter: boolean = false; @@ -574,38 +591,49 @@ export class Collector { this.messageRouter.addAnalyzerIssue( ExtractorMessageId.MissingGetter, `The property "${astDeclaration.astSymbol.localName}" has a setter but no getter.`, - astDeclaration); + astDeclaration + ); } } - } } - private _addAncillaryDeclaration(mainAstDeclaration: AstDeclaration, ancillaryAstDeclaration: AstDeclaration): void { + private _addAncillaryDeclaration( + mainAstDeclaration: AstDeclaration, + ancillaryAstDeclaration: AstDeclaration + ): void { const mainMetadata: InternalDeclarationMetadata = mainAstDeclaration.declarationMetadata as InternalDeclarationMetadata; const ancillaryMetadata: InternalDeclarationMetadata = ancillaryAstDeclaration.declarationMetadata as InternalDeclarationMetadata; if (mainMetadata.ancillaryDeclarations.indexOf(ancillaryAstDeclaration) >= 0) { - return; // already added + return; // already added } if (mainAstDeclaration.astSymbol !== ancillaryAstDeclaration.astSymbol) { - throw new InternalError('Invalid call to _addAncillaryDeclaration() because declarations do not' - + ' belong to the same symbol'); + throw new InternalError( + 'Invalid call to _addAncillaryDeclaration() because declarations do not' + + ' belong to the same symbol' + ); } if (mainMetadata.isAncillary) { - throw new InternalError('Invalid call to _addAncillaryDeclaration() because the target is ancillary itself'); + throw new InternalError( + 'Invalid call to _addAncillaryDeclaration() because the target is ancillary itself' + ); } if (ancillaryMetadata.isAncillary) { - throw new InternalError('Invalid call to _addAncillaryDeclaration() because source is already ancillary' - + ' to another declaration'); + throw new InternalError( + 'Invalid call to _addAncillaryDeclaration() because source is already ancillary' + + ' to another declaration' + ); } if (mainAstDeclaration.apiItemMetadata || ancillaryAstDeclaration.apiItemMetadata) { - throw new InternalError('Invalid call to _addAncillaryDeclaration() because the API item metadata' - + ' has already been constructed'); + throw new InternalError( + 'Invalid call to _addAncillaryDeclaration() because the API item metadata' + + ' has already been constructed' + ); } ancillaryMetadata.isAncillary = true; @@ -615,13 +643,14 @@ export class Collector { private _calculateApiItemMetadata(astDeclaration: AstDeclaration): void { const declarationMetadata: InternalDeclarationMetadata = astDeclaration.declarationMetadata as InternalDeclarationMetadata; if (declarationMetadata.isAncillary) { - if (astDeclaration.declaration.kind === ts.SyntaxKind.SetAccessor) { if (declarationMetadata.tsdocParserContext) { - this.messageRouter.addAnalyzerIssue(ExtractorMessageId.SetterWithDocs, - `The doc comment for the property "${astDeclaration.astSymbol.localName}"` - + ` must appear on the getter, not the setter.`, - astDeclaration); + this.messageRouter.addAnalyzerIssue( + ExtractorMessageId.SetterWithDocs, + `The doc comment for the property "${astDeclaration.astSymbol.localName}"` + + ` must appear on the getter, not the setter.`, + astDeclaration + ); } } @@ -674,11 +703,13 @@ export class Collector { } if (extraReleaseTags) { - if (!astDeclaration.astSymbol.isExternal) { // for now, don't report errors for external code + if (!astDeclaration.astSymbol.isExternal) { + // for now, don't report errors for external code this.messageRouter.addAnalyzerIssue( ExtractorMessageId.ExtraReleaseTag, 'The doc comment should not contain more than one release tag', - astDeclaration); + astDeclaration + ); } } @@ -701,8 +732,8 @@ export class Collector { } else { this.messageRouter.addAnalyzerIssue( ExtractorMessageId.PreapprovedBadReleaseTag, - `The @preapproved tag cannot be applied to "${astDeclaration.astSymbol.localName}"` - + ` without an @internal release tag`, + `The @preapproved tag cannot be applied to "${astDeclaration.astSymbol.localName}"` + + ` without an @internal release tag`, astDeclaration ); } @@ -710,8 +741,8 @@ export class Collector { default: this.messageRouter.addAnalyzerIssue( ExtractorMessageId.PreapprovedUnsupportedType, - `The @preapproved tag cannot be applied to "${astDeclaration.astSymbol.localName}"` - + ` because it is not a supported declaration type`, + `The @preapproved tag cannot be applied to "${astDeclaration.astSymbol.localName}"` + + ` because it is not a supported declaration type`, astDeclaration ); break; @@ -722,18 +753,20 @@ export class Collector { // This needs to be set regardless of whether or not a parserContext exists if (astDeclaration.parent) { const parentApiItemMetadata: ApiItemMetadata = this.fetchApiItemMetadata(astDeclaration.parent); - options.effectiveReleaseTag = options.declaredReleaseTag === ReleaseTag.None - ? parentApiItemMetadata.effectiveReleaseTag - : options.declaredReleaseTag; + options.effectiveReleaseTag = + options.declaredReleaseTag === ReleaseTag.None + ? parentApiItemMetadata.effectiveReleaseTag + : options.declaredReleaseTag; - options.releaseTagSameAsParent = + options.releaseTagSameAsParent = parentApiItemMetadata.effectiveReleaseTag === options.effectiveReleaseTag; } else { options.effectiveReleaseTag = options.declaredReleaseTag; } if (options.effectiveReleaseTag === ReleaseTag.None) { - if (!astDeclaration.astSymbol.isExternal) { // for now, don't report errors for external code + if (!astDeclaration.astSymbol.isExternal) { + // for now, don't report errors for external code // Don't report missing release tags for forgotten exports const astSymbol: AstSymbol = astDeclaration.astSymbol; const entity: CollectorEntity | undefined = this._entitiesByAstEntity.get(astSymbol.rootAstSymbol); @@ -743,8 +776,8 @@ export class Collector { if (astSymbol.rootAstSymbol.localName !== '_default') { this.messageRouter.addAnalyzerIssue( ExtractorMessageId.MissingReleaseTag, - `"${entity.astEntity.localName}" is exported by the package, but it is missing ` - + `a release tag (@alpha, @beta, @public, or @internal)`, + `"${entity.astEntity.localName}" is exported by the package, but it is missing ` + + `a release tag (@alpha, @beta, @public, or @internal)`, astSymbol ); } @@ -783,8 +816,10 @@ export class Collector { // // But _getReleaseTagForDeclaration() still receives a node corresponding to "x", so we need to walk upwards // and find the containing statement in order for getJSDocCommentRanges() to read the comment that we expect. - const statement: ts.VariableStatement | undefined = TypeScriptHelpers.findFirstParent(declaration, - ts.SyntaxKind.VariableStatement) as ts.VariableStatement | undefined; + const statement: ts.VariableStatement | undefined = TypeScriptHelpers.findFirstParent( + declaration, + ts.SyntaxKind.VariableStatement + ) as ts.VariableStatement | undefined; if (statement !== undefined) { // For a compound declaration, fall back to looking for C instead of A if (statement.declarationList.declarations.length === 1) { @@ -794,7 +829,8 @@ export class Collector { } const sourceFileText: string = declaration.getSourceFile().text; - const ranges: ts.CommentRange[] = TypeScriptInternals.getJSDocCommentRanges(nodeForComment, sourceFileText) || []; + const ranges: ts.CommentRange[] = + TypeScriptInternals.getJSDocCommentRanges(nodeForComment, sourceFileText) || []; if (ranges.length === 0) { return undefined; @@ -804,8 +840,11 @@ export class Collector { // the last one preceding it const range: ts.TextRange = ranges[ranges.length - 1]; - const tsdocTextRange: tsdoc.TextRange = tsdoc.TextRange.fromStringRange(sourceFileText, - range.pos, range.end); + const tsdocTextRange: tsdoc.TextRange = tsdoc.TextRange.fromStringRange( + sourceFileText, + range.pos, + range.end + ); const parserContext: tsdoc.ParserContext = this._tsdocParser.parseRange(tsdocTextRange); @@ -820,8 +859,9 @@ export class Collector { private _collectReferenceDirectives(astEntity: AstEntity): void { if (astEntity instanceof AstSymbol) { - const sourceFiles: ts.SourceFile[] = astEntity.astDeclarations.map(astDeclaration => - astDeclaration.declaration.getSourceFile()); + const sourceFiles: ts.SourceFile[] = astEntity.astDeclarations.map((astDeclaration) => + astDeclaration.declaration.getSourceFile() + ); return this._collectReferenceDirectivesFromSourceFiles(sourceFiles); } @@ -840,15 +880,20 @@ export class Collector { seenFilenames.add(sourceFile.fileName); for (const typeReferenceDirective of sourceFile.typeReferenceDirectives) { - const name: string = sourceFile.text.substring(typeReferenceDirective.pos, typeReferenceDirective.end); + const name: string = sourceFile.text.substring( + typeReferenceDirective.pos, + typeReferenceDirective.end + ); this._dtsTypeReferenceDirectives.add(name); } for (const libReferenceDirective of sourceFile.libReferenceDirectives) { - const name: string = sourceFile.text.substring(libReferenceDirective.pos, libReferenceDirective.end); + const name: string = sourceFile.text.substring( + libReferenceDirective.pos, + libReferenceDirective.end + ); this._dtsLibReferenceDirectives.add(name); } - } } } diff --git a/src/collector/CollectorEntity.ts b/src/collector/CollectorEntity.ts index 5ab4e75fea9..8a7834e33dc 100644 --- a/src/collector/CollectorEntity.ts +++ b/src/collector/CollectorEntity.ts @@ -73,7 +73,7 @@ export class CollectorEntity { * In all other cases, it is undefined. */ public get singleExportName(): string | undefined { - return this._singleExportName; + return this._singleExportName; } /** diff --git a/src/collector/MessageRouter.ts b/src/collector/MessageRouter.ts index 864986dc86b..cc0b7cf581c 100644 --- a/src/collector/MessageRouter.ts +++ b/src/collector/MessageRouter.ts @@ -16,10 +16,7 @@ import { IExtractorMessageProperties } from '../api/ExtractorMessage'; import { ExtractorMessageId, allExtractorMessageIds } from '../api/ExtractorMessageId'; -import { - IExtractorMessagesConfig, - IConfigMessageReportingRule -} from '../api/IConfigFile'; +import { IExtractorMessagesConfig, IConfigMessageReportingRule } from '../api/IConfigFile'; import { SourceMapper } from './SourceMapper'; import { ExtractorLogLevel } from '../api/ExtractorLogLevel'; import { ConsoleMessageId } from '../api/ConsoleMessageId'; @@ -38,7 +35,8 @@ export interface IMessageRouterOptions { } export class MessageRouter { - public static readonly DIAGNOSTICS_LINE: string = '============================================================'; + public static readonly DIAGNOSTICS_LINE: string = + '============================================================'; private readonly _workingPackageFolder: string | undefined; private readonly _messageCallback: ((message: ExtractorMessage) => void) | undefined; @@ -53,12 +51,15 @@ export class MessageRouter { // Normalized representation of the routing rules from api-extractor.json private _reportingRuleByMessageId: Map = new Map(); - private _compilerDefaultRule: IReportingRule = { logLevel: ExtractorLogLevel.None, - addToApiReportFile: false }; - private _extractorDefaultRule: IReportingRule = { logLevel: ExtractorLogLevel.None, - addToApiReportFile: false }; - private _tsdocDefaultRule: IReportingRule = { logLevel: ExtractorLogLevel.None, - addToApiReportFile: false }; + private _compilerDefaultRule: IReportingRule = { + logLevel: ExtractorLogLevel.None, + addToApiReportFile: false + }; + private _extractorDefaultRule: IReportingRule = { + logLevel: ExtractorLogLevel.None, + addToApiReportFile: false + }; + private _tsdocDefaultRule: IReportingRule = { logLevel: ExtractorLogLevel.None, addToApiReportFile: false }; public errorCount: number = 0; public warningCount: number = 0; @@ -95,13 +96,16 @@ export class MessageRouter { if (messagesConfig.compilerMessageReporting) { for (const messageId of Object.getOwnPropertyNames(messagesConfig.compilerMessageReporting)) { const reportingRule: IReportingRule = MessageRouter._getNormalizedRule( - messagesConfig.compilerMessageReporting[messageId]); + messagesConfig.compilerMessageReporting[messageId] + ); if (messageId === 'default') { this._compilerDefaultRule = reportingRule; } else if (!/^TS[0-9]+$/.test(messageId)) { - throw new Error(`Error in API Extractor config: The messages.compilerMessageReporting table contains` - + ` an invalid entry "${messageId}". The identifier format is "TS" followed by an integer.`); + throw new Error( + `Error in API Extractor config: The messages.compilerMessageReporting table contains` + + ` an invalid entry "${messageId}". The identifier format is "TS" followed by an integer.` + ); } else { this._reportingRuleByMessageId.set(messageId, reportingRule); } @@ -111,16 +115,21 @@ export class MessageRouter { if (messagesConfig.extractorMessageReporting) { for (const messageId of Object.getOwnPropertyNames(messagesConfig.extractorMessageReporting)) { const reportingRule: IReportingRule = MessageRouter._getNormalizedRule( - messagesConfig.extractorMessageReporting[messageId]); + messagesConfig.extractorMessageReporting[messageId] + ); if (messageId === 'default') { this._extractorDefaultRule = reportingRule; } else if (!/^ae-/.test(messageId)) { - throw new Error(`Error in API Extractor config: The messages.extractorMessageReporting table contains` - + ` an invalid entry "${messageId}". The name should begin with the "ae-" prefix.`); + throw new Error( + `Error in API Extractor config: The messages.extractorMessageReporting table contains` + + ` an invalid entry "${messageId}". The name should begin with the "ae-" prefix.` + ); } else if (!allExtractorMessageIds.has(messageId)) { - throw new Error(`Error in API Extractor config: The messages.extractorMessageReporting table contains` - + ` an unrecognized identifier "${messageId}". Is it spelled correctly?`); + throw new Error( + `Error in API Extractor config: The messages.extractorMessageReporting table contains` + + ` an unrecognized identifier "${messageId}". Is it spelled correctly?` + ); } else { this._reportingRuleByMessageId.set(messageId, reportingRule); } @@ -130,16 +139,21 @@ export class MessageRouter { if (messagesConfig.tsdocMessageReporting) { for (const messageId of Object.getOwnPropertyNames(messagesConfig.tsdocMessageReporting)) { const reportingRule: IReportingRule = MessageRouter._getNormalizedRule( - messagesConfig.tsdocMessageReporting[messageId]); + messagesConfig.tsdocMessageReporting[messageId] + ); if (messageId === 'default') { this._tsdocDefaultRule = reportingRule; } else if (!/^tsdoc-/.test(messageId)) { - throw new Error(`Error in API Extractor config: The messages.tsdocMessageReporting table contains` - + ` an invalid entry "${messageId}". The name should begin with the "tsdoc-" prefix.`); + throw new Error( + `Error in API Extractor config: The messages.tsdocMessageReporting table contains` + + ` an invalid entry "${messageId}". The name should begin with the "tsdoc-" prefix.` + ); } else if (!AedocDefinitions.tsdocConfiguration.isKnownMessageId(messageId)) { - throw new Error(`Error in API Extractor config: The messages.tsdocMessageReporting table contains` - + ` an unrecognized identifier "${messageId}". Is it spelled correctly?`); + throw new Error( + `Error in API Extractor config: The messages.tsdocMessageReporting table contains` + + ` an unrecognized identifier "${messageId}". Is it spelled correctly?` + ); } else { this._reportingRuleByMessageId.set(messageId, reportingRule); } @@ -165,7 +179,7 @@ export class MessageRouter { switch (diagnostic.category) { case ts.DiagnosticCategory.Suggestion: case ts.DiagnosticCategory.Message: - return; // ignore noise + return; // ignore noise } const messageText: string = `${diagnostic.messageText}`; @@ -178,7 +192,8 @@ export class MessageRouter { if (diagnostic.file) { const sourceFile: ts.SourceFile = diagnostic.file; const lineAndCharacter: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition( - diagnostic.start || 0); + diagnostic.start || 0 + ); options.sourceFilePath = sourceFile.fileName; options.sourceFileLine = lineAndCharacter.line + 1; @@ -193,9 +208,12 @@ export class MessageRouter { /** * Add a message from the API Extractor analysis */ - public addAnalyzerIssue(messageId: ExtractorMessageId, messageText: string, - astDeclarationOrSymbol: AstDeclaration | AstSymbol, properties?: IExtractorMessageProperties): void { - + public addAnalyzerIssue( + messageId: ExtractorMessageId, + messageText: string, + astDeclarationOrSymbol: AstDeclaration | AstSymbol, + properties?: IExtractorMessageProperties + ): void { let astDeclaration: AstDeclaration; if (astDeclarationOrSymbol instanceof AstDeclaration) { astDeclaration = astDeclarationOrSymbol; @@ -204,8 +222,12 @@ export class MessageRouter { } const extractorMessage: ExtractorMessage = this.addAnalyzerIssueForPosition( - messageId, messageText, astDeclaration.declaration.getSourceFile(), - astDeclaration.declaration.getStart(), properties); + messageId, + messageText, + astDeclaration.declaration.getSourceFile(), + astDeclaration.declaration.getStart(), + properties + ); this._associateMessageWithAstDeclaration(extractorMessage, astDeclaration); } @@ -214,12 +236,15 @@ export class MessageRouter { * Add all messages produced from an invocation of the TSDoc parser, assuming they refer to * code in the specified source file. */ - public addTsdocMessages(parserContext: tsdoc.ParserContext, sourceFile: ts.SourceFile, - astDeclaration?: AstDeclaration): void { - + public addTsdocMessages( + parserContext: tsdoc.ParserContext, + sourceFile: ts.SourceFile, + astDeclaration?: AstDeclaration + ): void { for (const message of parserContext.log.messages) { const lineAndCharacter: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition( - message.textRange.pos); + message.textRange.pos + ); const options: IExtractorMessageOptions = { category: ExtractorMessageCategory.TSDoc, @@ -274,7 +299,7 @@ export class MessageRouter { return outputArray; } - const outputObject: object = { }; + const outputObject: object = {}; for (const key of Object.getOwnPropertyNames(input)) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const value: any = input[key]; @@ -295,11 +320,13 @@ export class MessageRouter { /** * Record this message in _associatedMessagesForAstDeclaration */ - private _associateMessageWithAstDeclaration(extractorMessage: ExtractorMessage, - astDeclaration: AstDeclaration): void { - - let associatedMessages: ExtractorMessage[] | undefined - = this._associatedMessagesForAstDeclaration.get(astDeclaration); + private _associateMessageWithAstDeclaration( + extractorMessage: ExtractorMessage, + astDeclaration: AstDeclaration + ): void { + let associatedMessages: ExtractorMessage[] | undefined = this._associatedMessagesForAstDeclaration.get( + astDeclaration + ); if (!associatedMessages) { associatedMessages = []; @@ -311,11 +338,14 @@ export class MessageRouter { /** * Add a message for a location in an arbitrary source file. */ - public addAnalyzerIssueForPosition(messageId: ExtractorMessageId, messageText: string, - sourceFile: ts.SourceFile, pos: number, properties?: IExtractorMessageProperties): ExtractorMessage { - - const lineAndCharacter: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition( - pos); + public addAnalyzerIssueForPosition( + messageId: ExtractorMessageId, + messageText: string, + sourceFile: ts.SourceFile, + pos: number, + properties?: IExtractorMessageProperties + ): ExtractorMessage { + const lineAndCharacter: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition(pos); const options: IExtractorMessageOptions = { category: ExtractorMessageCategory.Extractor, @@ -342,22 +372,19 @@ export class MessageRouter { public fetchAssociatedMessagesForReviewFile(astDeclaration: AstDeclaration): ExtractorMessage[] { const messagesForApiReportFile: ExtractorMessage[] = []; - const associatedMessages: ExtractorMessage[] = this._associatedMessagesForAstDeclaration.get(astDeclaration) || []; + const associatedMessages: ExtractorMessage[] = + this._associatedMessagesForAstDeclaration.get(astDeclaration) || []; for (const associatedMessage of associatedMessages) { - // Make sure we didn't already report this message for some reason if (!associatedMessage.handled) { - // Is this message type configured to go in the API report file? const reportingRule: IReportingRule = this._getRuleForMessage(associatedMessage); if (reportingRule.addToApiReportFile) { - // Include it in the result, and record that it went to the API report file messagesForApiReportFile.push(associatedMessage); associatedMessage.handled = true; } } - } this._sortMessagesForOutput(messagesForApiReportFile); @@ -372,20 +399,16 @@ export class MessageRouter { const messagesForApiReportFile: ExtractorMessage[] = []; for (const unassociatedMessage of this.messages) { - // Make sure we didn't already report this message for some reason if (!unassociatedMessage.handled) { - // Is this message type configured to go in the API report file? const reportingRule: IReportingRule = this._getRuleForMessage(unassociatedMessage); if (reportingRule.addToApiReportFile) { - // Include it in the result, and record that it went to the API report file messagesForApiReportFile.push(unassociatedMessage); unassociatedMessage.handled = true; } } - } this._sortMessagesForOutput(messagesForApiReportFile); @@ -414,44 +437,68 @@ export class MessageRouter { } } - public logError(messageId: ConsoleMessageId, message: string, properties?: IExtractorMessageProperties): void { - this._handleMessage(new ExtractorMessage({ - category: ExtractorMessageCategory.Console, - messageId, - text: message, - properties, - logLevel: ExtractorLogLevel.Error - })); + public logError( + messageId: ConsoleMessageId, + message: string, + properties?: IExtractorMessageProperties + ): void { + this._handleMessage( + new ExtractorMessage({ + category: ExtractorMessageCategory.Console, + messageId, + text: message, + properties, + logLevel: ExtractorLogLevel.Error + }) + ); } - public logWarning(messageId: ConsoleMessageId, message: string, properties?: IExtractorMessageProperties): void { - this._handleMessage(new ExtractorMessage({ - category: ExtractorMessageCategory.Console, - messageId, - text: message, - properties, - logLevel: ExtractorLogLevel.Warning - })); + public logWarning( + messageId: ConsoleMessageId, + message: string, + properties?: IExtractorMessageProperties + ): void { + this._handleMessage( + new ExtractorMessage({ + category: ExtractorMessageCategory.Console, + messageId, + text: message, + properties, + logLevel: ExtractorLogLevel.Warning + }) + ); } - public logInfo(messageId: ConsoleMessageId, message: string, properties?: IExtractorMessageProperties): void { - this._handleMessage(new ExtractorMessage({ - category: ExtractorMessageCategory.Console, - messageId, - text: message, - properties, - logLevel: ExtractorLogLevel.Info - })); + public logInfo( + messageId: ConsoleMessageId, + message: string, + properties?: IExtractorMessageProperties + ): void { + this._handleMessage( + new ExtractorMessage({ + category: ExtractorMessageCategory.Console, + messageId, + text: message, + properties, + logLevel: ExtractorLogLevel.Info + }) + ); } - public logVerbose(messageId: ConsoleMessageId, message: string, properties?: IExtractorMessageProperties): void { - this._handleMessage(new ExtractorMessage({ - category: ExtractorMessageCategory.Console, - messageId, - text: message, - properties, - logLevel: ExtractorLogLevel.Verbose - })); + public logVerbose( + messageId: ConsoleMessageId, + message: string, + properties?: IExtractorMessageProperties + ): void { + this._handleMessage( + new ExtractorMessage({ + category: ExtractorMessageCategory.Console, + messageId, + text: message, + properties, + logLevel: ExtractorLogLevel.Verbose + }) + ); } public logDiagnosticHeader(title: string): void { diff --git a/src/collector/SourceMapper.ts b/src/collector/SourceMapper.ts index 58ba28d2246..ac1669c50da 100644 --- a/src/collector/SourceMapper.ts +++ b/src/collector/SourceMapper.ts @@ -26,8 +26,7 @@ interface IOriginalFileInfo { export class SourceMapper { // Map from .d.ts file path --> ISourceMap if a source map was found, or null if not found - private _sourceMapByFilePath: Map - = new Map(); + private _sourceMapByFilePath: Map = new Map(); // Cache the FileSystem.exists() result for mapped .ts files private _originalFileInfoByPath: Map = new Map(); @@ -81,7 +80,7 @@ export class SourceMapper { SourceMapConsumer.GENERATED_ORDER ); - sourceMap = { sourceMapConsumer, mappingItems}; + sourceMap = { sourceMapConsumer, mappingItems }; } else { // No source map for this filename sourceMap = null; // eslint-disable-line @rushstack/no-null @@ -108,7 +107,8 @@ export class SourceMapper { options.sourceFileColumn = 1; } - const nearestMappingItem: MappingItem | undefined = SourceMapper._findNearestMappingItem(sourceMap.mappingItems, + const nearestMappingItem: MappingItem | undefined = SourceMapper._findNearestMappingItem( + sourceMap.mappingItems, { line: options.sourceFileLine, column: options.sourceFileColumn @@ -120,7 +120,10 @@ export class SourceMapper { return; } - const mappedFilePath: string = path.resolve(path.dirname(options.sourceFilePath), nearestMappingItem.source); + const mappedFilePath: string = path.resolve( + path.dirname(options.sourceFilePath), + nearestMappingItem.source + ); // Does the mapped filename exist? Use a cache to remember the answer. let originalFileInfo: IOriginalFileInfo | undefined = this._originalFileInfoByPath.get(mappedFilePath); @@ -132,11 +135,12 @@ export class SourceMapper { if (originalFileInfo.fileExists) { // Read the file and measure the length of each line - originalFileInfo.maxColumnForLine = - FileSystem.readFile(mappedFilePath, { convertLineEndings: NewlineKind.Lf }) + originalFileInfo.maxColumnForLine = FileSystem.readFile(mappedFilePath, { + convertLineEndings: NewlineKind.Lf + }) .split('\n') - .map(x => x.length + 1); // +1 since columns are 1-based - originalFileInfo.maxColumnForLine.unshift(0); // Extra item since lines are 1-based + .map((x) => x.length + 1); // +1 since columns are 1-based + originalFileInfo.maxColumnForLine.unshift(0); // Extra item since lines are 1-based } this._originalFileInfoByPath.set(mappedFilePath, originalFileInfo); @@ -151,15 +155,17 @@ export class SourceMapper { // the delta and apply it to the original position. const guessedPosition: Position = { line: nearestMappingItem.originalLine + options.sourceFileLine - nearestMappingItem.generatedLine, - column: nearestMappingItem.originalColumn + options.sourceFileColumn - nearestMappingItem.generatedColumn + column: + nearestMappingItem.originalColumn + options.sourceFileColumn - nearestMappingItem.generatedColumn }; // Verify that the result is not out of bounds, in cause our heuristic failed - if (guessedPosition.line >= 1 - && guessedPosition.line < originalFileInfo.maxColumnForLine.length - && guessedPosition.column >= 1 - && guessedPosition.column <= originalFileInfo.maxColumnForLine[guessedPosition.line]) { - + if ( + guessedPosition.line >= 1 && + guessedPosition.line < originalFileInfo.maxColumnForLine.length && + guessedPosition.column >= 1 && + guessedPosition.column <= originalFileInfo.maxColumnForLine[guessedPosition.line] + ) { options.sourceFilePath = mappedFilePath; options.sourceFileLine = guessedPosition.line; options.sourceFileColumn = guessedPosition.column; @@ -174,7 +180,10 @@ export class SourceMapper { // The `mappingItems` array is sorted by generatedLine/generatedColumn (GENERATED_ORDER). // The _findNearestMappingItem() lookup is a simple binary search that returns the previous item // if there is no exact match. - private static _findNearestMappingItem(mappingItems: MappingItem[], position: Position): MappingItem | undefined { + private static _findNearestMappingItem( + mappingItems: MappingItem[], + position: Position + ): MappingItem | undefined { if (mappingItems.length === 0) { return undefined; } diff --git a/src/collector/SymbolMetadata.ts b/src/collector/SymbolMetadata.ts index ee942f80fab..d28d731cc4d 100644 --- a/src/collector/SymbolMetadata.ts +++ b/src/collector/SymbolMetadata.ts @@ -19,7 +19,7 @@ export class SymbolMetadata { // `ApiItemMetadata.effectiveReleaseTag` value that is most public. public readonly maxEffectiveReleaseTag: ReleaseTag; - public constructor (options: ISymbolMetadataOptions) { + public constructor(options: ISymbolMetadataOptions) { this.maxEffectiveReleaseTag = options.maxEffectiveReleaseTag; } } diff --git a/src/collector/WorkingPackage.ts b/src/collector/WorkingPackage.ts index 3ae47076705..7cd76fad9c8 100644 --- a/src/collector/WorkingPackage.ts +++ b/src/collector/WorkingPackage.ts @@ -4,9 +4,7 @@ import * as ts from 'typescript'; import * as tsdoc from '@microsoft/tsdoc'; -import { - INodePackageJson -} from '@rushstack/node-core-library'; +import { INodePackageJson } from '@rushstack/node-core-library'; /** * Constructor options for WorkingPackage diff --git a/src/enhancers/DocCommentEnhancer.ts b/src/enhancers/DocCommentEnhancer.ts index f25afd093a9..75e6863ef48 100644 --- a/src/enhancers/DocCommentEnhancer.ts +++ b/src/enhancers/DocCommentEnhancer.ts @@ -65,7 +65,6 @@ export class DocCommentEnhancer { } private _analyzeNeedsDocumentation(astDeclaration: AstDeclaration, metadata: ApiItemMetadata): void { - if (astDeclaration.declaration.kind === ts.SyntaxKind.Constructor) { // Constructors always do pretty much the same thing, so it's annoying to require people to write // descriptions for them. Instead, if the constructor lacks a TSDoc summary, then API Extractor @@ -115,8 +114,9 @@ export class DocCommentEnhancer { new tsdoc.DocParagraph({ configuration }, [ new tsdoc.DocPlainText({ configuration, - text: `The constructor for this class is marked as internal. Third-party code should not` - + ` call the constructor directly or create subclasses that extend the ` + text: + `The constructor for this class is marked as internal. Third-party code should not` + + ` call the constructor directly or create subclasses that extend the ` }), new tsdoc.DocCodeSpan({ configuration, @@ -125,7 +125,6 @@ export class DocCommentEnhancer { new tsdoc.DocPlainText({ configuration, text: ' class.' }) ]) ); - } return; } @@ -133,7 +132,9 @@ export class DocCommentEnhancer { if (metadata.tsdocComment) { // Require the summary to contain at least 10 non-spacing characters metadata.needsDocumentation = !tsdoc.PlainTextEmitter.hasAnyTextContent( - metadata.tsdocComment.summarySection, 10); + metadata.tsdocComment.summarySection, + 10 + ); } else { metadata.needsDocumentation = true; } @@ -149,22 +150,24 @@ export class DocCommentEnhancer { private _checkForBrokenLinksRecursive(astDeclaration: AstDeclaration, node: tsdoc.DocNode): void { if (node instanceof tsdoc.DocLinkTag) { if (node.codeDestination) { - // Is it referring to the working package? If not, we don't do any link validation, because // AstReferenceResolver doesn't support it yet (but ModelReferenceResolver does of course). // Tracked by: https://github.com/microsoft/rushstack/issues/1195 - if (node.codeDestination.packageName === undefined - || node.codeDestination.packageName === this._collector.workingPackage.name) { - - const referencedAstDeclaration: AstDeclaration | ResolverFailure = this._collector.astReferenceResolver - .resolve(node.codeDestination); + if ( + node.codeDestination.packageName === undefined || + node.codeDestination.packageName === this._collector.workingPackage.name + ) { + const referencedAstDeclaration: + | AstDeclaration + | ResolverFailure = this._collector.astReferenceResolver.resolve(node.codeDestination); if (referencedAstDeclaration instanceof ResolverFailure) { - this._collector.messageRouter.addAnalyzerIssue(ExtractorMessageId.UnresolvedLink, + this._collector.messageRouter.addAnalyzerIssue( + ExtractorMessageId.UnresolvedLink, 'The @link reference could not be resolved: ' + referencedAstDeclaration.reason, - astDeclaration); + astDeclaration + ); } - } } } @@ -176,38 +179,51 @@ export class DocCommentEnhancer { /** * Follow an `{@inheritDoc ___}` reference and copy the content that we find in the referenced comment. */ - private _applyInheritDoc(astDeclaration: AstDeclaration, docComment: tsdoc.DocComment, - inheritDocTag: tsdoc.DocInheritDocTag): void { - + private _applyInheritDoc( + astDeclaration: AstDeclaration, + docComment: tsdoc.DocComment, + inheritDocTag: tsdoc.DocInheritDocTag + ): void { if (!inheritDocTag.declarationReference) { - this._collector.messageRouter.addAnalyzerIssue(ExtractorMessageId.UnresolvedInheritDocBase, + this._collector.messageRouter.addAnalyzerIssue( + ExtractorMessageId.UnresolvedInheritDocBase, 'The @inheritDoc tag needs a TSDoc declaration reference; signature matching is not supported yet', - astDeclaration); + astDeclaration + ); return; } // Is it referring to the working package? - if (!(inheritDocTag.declarationReference.packageName === undefined - || inheritDocTag.declarationReference.packageName === this._collector.workingPackage.name)) { - + if ( + !( + inheritDocTag.declarationReference.packageName === undefined || + inheritDocTag.declarationReference.packageName === this._collector.workingPackage.name + ) + ) { // It's referencing an external package, so skip this inheritDoc tag, since AstReferenceResolver doesn't // support it yet. As a workaround, this tag will get handled later by api-documenter. // Tracked by: https://github.com/microsoft/rushstack/issues/1195 return; } - const referencedAstDeclaration: AstDeclaration | ResolverFailure = this._collector.astReferenceResolver - .resolve(inheritDocTag.declarationReference); + const referencedAstDeclaration: + | AstDeclaration + | ResolverFailure = this._collector.astReferenceResolver.resolve(inheritDocTag.declarationReference); if (referencedAstDeclaration instanceof ResolverFailure) { - this._collector.messageRouter.addAnalyzerIssue(ExtractorMessageId.UnresolvedInheritDocReference, - 'The @inheritDoc reference could not be resolved: ' + referencedAstDeclaration.reason, astDeclaration); + this._collector.messageRouter.addAnalyzerIssue( + ExtractorMessageId.UnresolvedInheritDocReference, + 'The @inheritDoc reference could not be resolved: ' + referencedAstDeclaration.reason, + astDeclaration + ); return; } this._analyzeApiItem(referencedAstDeclaration); - const referencedMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(referencedAstDeclaration); + const referencedMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata( + referencedAstDeclaration + ); if (referencedMetadata.tsdocComment) { this._copyInheritedDocs(docComment, referencedMetadata.tsdocComment); @@ -232,5 +248,4 @@ export class DocCommentEnhancer { targetDocComment.inheritDocTag = undefined; } - } diff --git a/src/enhancers/ValidationEnhancer.ts b/src/enhancers/ValidationEnhancer.ts index 1abc37f04cd..6c5b377db58 100644 --- a/src/enhancers/ValidationEnhancer.ts +++ b/src/enhancers/ValidationEnhancer.ts @@ -15,7 +15,6 @@ import { ReleaseTag } from '@microsoft/api-extractor-model'; import { AstImportAsModule } from '../analyzer/AstImportAsModule'; export class ValidationEnhancer { - public static analyze(collector: Collector): void { const alreadyWarnedSymbols: Set = new Set(); @@ -44,7 +43,6 @@ export class ValidationEnhancer { astSymbol: AstSymbol, symbolMetadata: SymbolMetadata ): void { - let needsUnderscore: boolean = false; if (symbolMetadata.maxEffectiveReleaseTag === ReleaseTag.Internal) { @@ -87,8 +85,8 @@ export class ValidationEnhancer { if (exportName[0] !== '_') { collector.messageRouter.addAnalyzerIssue( ExtractorMessageId.InternalMissingUnderscore, - `The name "${exportName}" should be prefixed with an underscore` - + ` because the declaration is marked as @internal`, + `The name "${exportName}" should be prefixed with an underscore` + + ` because the declaration is marked as @internal`, astSymbol, { exportName } ); @@ -155,7 +153,7 @@ export class ValidationEnhancer { collector.messageRouter.addAnalyzerIssue( ExtractorMessageId.InternalMixedReleaseTag, `Mixed release tags are not allowed for "${astSymbol.localName}" because one of its declarations` + - ` is marked as @internal`, + ` is marked as @internal`, astSymbol ); } @@ -171,7 +169,6 @@ export class ValidationEnhancer { const declarationReleaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; for (const referencedEntity of astDeclaration.referencedAstEntities) { - if (referencedEntity instanceof AstSymbol) { // If this is e.g. a member of a namespace, then we need to be checking the top-level scope to see // whether it's exported. @@ -188,15 +185,19 @@ export class ValidationEnhancer { const referencedReleaseTag: ReleaseTag = referencedMetadata.maxEffectiveReleaseTag; if (ReleaseTag.compare(declarationReleaseTag, referencedReleaseTag) > 0) { - collector.messageRouter.addAnalyzerIssue(ExtractorMessageId.IncompatibleReleaseTags, - `The symbol "${astDeclaration.astSymbol.localName}"` - + ` is marked as ${ReleaseTag.getTagName(declarationReleaseTag)},` - + ` but its signature references "${referencedEntity.localName}"` - + ` which is marked as ${ReleaseTag.getTagName(referencedReleaseTag)}`, - astDeclaration); + collector.messageRouter.addAnalyzerIssue( + ExtractorMessageId.IncompatibleReleaseTags, + `The symbol "${astDeclaration.astSymbol.localName}"` + + ` is marked as ${ReleaseTag.getTagName(declarationReleaseTag)},` + + ` but its signature references "${referencedEntity.localName}"` + + ` which is marked as ${ReleaseTag.getTagName(referencedReleaseTag)}`, + astDeclaration + ); } } else { - const entryPointFilename: string = path.basename(collector.workingPackage.entryPointSourceFile.fileName); + const entryPointFilename: string = path.basename( + collector.workingPackage.entryPointSourceFile.fileName + ); if (!alreadyWarnedSymbols.has(referencedEntity)) { alreadyWarnedSymbols.add(referencedEntity); @@ -204,15 +205,14 @@ export class ValidationEnhancer { // The main usage scenario for ECMAScript symbols is to attach private data to a JavaScript object, // so as a special case, we do NOT report them as forgotten exports. if (!ValidationEnhancer._isEcmaScriptSymbol(referencedEntity)) { - - collector.messageRouter.addAnalyzerIssue(ExtractorMessageId.ForgottenExport, - `The symbol "${rootSymbol.localName}" needs to be exported` - + ` by the entry point ${entryPointFilename}`, - astDeclaration); + collector.messageRouter.addAnalyzerIssue( + ExtractorMessageId.ForgottenExport, + `The symbol "${rootSymbol.localName}" needs to be exported` + + ` by the entry point ${entryPointFilename}`, + astDeclaration + ); } - } - } } } @@ -253,5 +253,4 @@ export class ValidationEnhancer { return false; } - } diff --git a/src/generators/ApiModelGenerator.ts b/src/generators/ApiModelGenerator.ts index 9fa54f39414..546cf43a367 100644 --- a/src/generators/ApiModelGenerator.ts +++ b/src/generators/ApiModelGenerator.ts @@ -56,7 +56,8 @@ export class ApiModelGenerator { collector.packageJsonLookup, collector.workingPackage.name, collector.program, - collector.typeChecker); + collector.typeChecker + ); } public get apiModel(): ApiModel { @@ -85,9 +86,11 @@ export class ApiModelGenerator { return apiPackage; } - private _processAstEntity(astEntity: AstEntity, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processAstEntity( + astEntity: AstEntity, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { if (astEntity instanceof AstSymbol) { // Skip ancillary declarations; we will process them with the main declaration for (const astDeclaration of this._collector.getNonAncillaryDeclarations(astEntity)) { @@ -106,27 +109,40 @@ export class ApiModelGenerator { // form "export { X } from 'external-package'". We can also use this to solve GitHub issue #950. } - private _processAstModule(astModule: AstModule, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processAstModule( + astModule: AstModule, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const name: string = exportedName ? exportedName : astModule.moduleSymbol.name; const containerKey: string = ApiNamespace.getContainerKey(name); - let apiNamespace: ApiNamespace | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiNamespace; + let apiNamespace: ApiNamespace | undefined = parentApiItem.tryGetMemberByKey( + containerKey + ) as ApiNamespace; if (apiNamespace === undefined) { - apiNamespace = new ApiNamespace({ name, docComment: undefined, releaseTag: ReleaseTag.None, excerptTokens: [] }); + apiNamespace = new ApiNamespace({ + name, + docComment: undefined, + releaseTag: ReleaseTag.None, + excerptTokens: [] + }); parentApiItem.addMember(apiNamespace); } - astModule.astModuleExportInfo!.exportedLocalEntities.forEach((exportedEntity: AstEntity, exportedName: string) => { - this._processAstEntity(exportedEntity, exportedName, apiNamespace!); - }); + astModule.astModuleExportInfo!.exportedLocalEntities.forEach( + (exportedEntity: AstEntity, exportedName: string) => { + this._processAstEntity(exportedEntity, exportedName, apiNamespace!); + } + ); } - private _processDeclaration(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processDeclaration( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { if ((astDeclaration.modifierFlags & ts.ModifierFlags.Private) !== 0) { return; // trim out private declarations } @@ -207,25 +223,31 @@ export class ApiModelGenerator { break; default: - // ignore unknown types + // ignore unknown types } } - private _processChildDeclarations(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { + private _processChildDeclarations( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { for (const childDeclaration of astDeclaration.children) { this._processDeclaration(childDeclaration, undefined, parentApiItem); } } - private _processApiCallSignature(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiCallSignature( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration); const containerKey: string = ApiCallSignature.getContainerKey(overloadIndex); - let apiCallSignature: ApiCallSignature | undefined = parentApiItem.tryGetMemberByKey(containerKey) as - ApiCallSignature; + let apiCallSignature: ApiCallSignature | undefined = parentApiItem.tryGetMemberByKey( + containerKey + ) as ApiCallSignature; if (apiCallSignature === undefined) { const callSignature: ts.CallSignatureDeclaration = astDeclaration.declaration as ts.CallSignatureDeclaration; @@ -235,10 +257,15 @@ export class ApiModelGenerator { const returnTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); nodesToCapture.push({ node: callSignature.type, tokenRange: returnTypeTokenRange }); - const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters(nodesToCapture, - callSignature.typeParameters); + const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( + nodesToCapture, + callSignature.typeParameters + ); - const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, callSignature.parameters); + const parameters: IApiParameterOptions[] = this._captureParameters( + nodesToCapture, + callSignature.parameters + ); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); @@ -259,21 +286,27 @@ export class ApiModelGenerator { } } - private _processApiConstructor(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiConstructor( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration); const containerKey: string = ApiConstructor.getContainerKey(overloadIndex); - let apiConstructor: ApiConstructor | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiConstructor; + let apiConstructor: ApiConstructor | undefined = parentApiItem.tryGetMemberByKey( + containerKey + ) as ApiConstructor; if (apiConstructor === undefined) { const constructorDeclaration: ts.ConstructorDeclaration = astDeclaration.declaration as ts.ConstructorDeclaration; const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; - const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, - constructorDeclaration.parameters); + const parameters: IApiParameterOptions[] = this._captureParameters( + nodesToCapture, + constructorDeclaration.parameters + ); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); @@ -292,9 +325,11 @@ export class ApiModelGenerator { } } - private _processApiClass(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiClass( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const name: string = exportedName ? exportedName : astDeclaration.astSymbol.localName; const containerKey: string = ApiClass.getContainerKey(name); @@ -305,8 +340,10 @@ export class ApiModelGenerator { const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; - const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters(nodesToCapture, - classDeclaration.typeParameters); + const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( + nodesToCapture, + classDeclaration.typeParameters + ); let extendsTokenRange: IExcerptTokenRange | undefined = undefined; const implementsTokenRanges: IExcerptTokenRange[] = []; @@ -315,13 +352,13 @@ export class ApiModelGenerator { if (heritageClause.token === ts.SyntaxKind.ExtendsKeyword) { extendsTokenRange = ExcerptBuilder.createEmptyTokenRange(); if (heritageClause.types.length > 0) { - nodesToCapture.push({ node: heritageClause.types[0], tokenRange: extendsTokenRange}); + nodesToCapture.push({ node: heritageClause.types[0], tokenRange: extendsTokenRange }); } } else if (heritageClause.token === ts.SyntaxKind.ImplementsKeyword) { for (const heritageType of heritageClause.types) { const implementsTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); implementsTokenRanges.push(implementsTokenRange); - nodesToCapture.push({ node: heritageType, tokenRange: implementsTokenRange}); + nodesToCapture.push({ node: heritageType, tokenRange: implementsTokenRange }); } } } @@ -347,28 +384,35 @@ export class ApiModelGenerator { this._processChildDeclarations(astDeclaration, exportedName, apiClass); } - private _processApiConstructSignature(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiConstructSignature( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration); const containerKey: string = ApiConstructSignature.getContainerKey(overloadIndex); - let apiConstructSignature: ApiConstructSignature | undefined = parentApiItem.tryGetMemberByKey(containerKey) as - ApiConstructSignature; + let apiConstructSignature: ApiConstructSignature | undefined = parentApiItem.tryGetMemberByKey( + containerKey + ) as ApiConstructSignature; if (apiConstructSignature === undefined) { - const constructSignature: ts.ConstructSignatureDeclaration = astDeclaration.declaration as - ts.ConstructSignatureDeclaration; + const constructSignature: ts.ConstructSignatureDeclaration = astDeclaration.declaration as ts.ConstructSignatureDeclaration; const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; const returnTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); nodesToCapture.push({ node: constructSignature.type, tokenRange: returnTypeTokenRange }); - const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters(nodesToCapture, - constructSignature.typeParameters); + const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( + nodesToCapture, + constructSignature.typeParameters + ); - const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, constructSignature.parameters); + const parameters: IApiParameterOptions[] = this._captureParameters( + nodesToCapture, + constructSignature.parameters + ); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); @@ -389,9 +433,11 @@ export class ApiModelGenerator { } } - private _processApiEnum(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiEnum( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const name: string = exportedName ? exportedName : astDeclaration.astSymbol.localName; const containerKey: string = ApiEnum.getContainerKey(name); @@ -410,13 +456,17 @@ export class ApiModelGenerator { this._processChildDeclarations(astDeclaration, exportedName, apiEnum); } - private _processApiEnumMember(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiEnumMember( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const name: string = exportedName ? exportedName : astDeclaration.astSymbol.localName; const containerKey: string = ApiEnumMember.getContainerKey(name); - let apiEnumMember: ApiEnumMember | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiEnumMember; + let apiEnumMember: ApiEnumMember | undefined = parentApiItem.tryGetMemberByKey( + containerKey + ) as ApiEnumMember; if (apiEnumMember === undefined) { const enumMember: ts.EnumMember = astDeclaration.declaration as ts.EnumMember; @@ -443,16 +493,17 @@ export class ApiModelGenerator { } } - private _processApiFunction(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiFunction( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const name: string = exportedName ? exportedName : astDeclaration.astSymbol.localName; const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration); const containerKey: string = ApiFunction.getContainerKey(name, overloadIndex); - let apiFunction: ApiFunction | undefined = parentApiItem.tryGetMemberByKey(containerKey) as - ApiFunction; + let apiFunction: ApiFunction | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiFunction; if (apiFunction === undefined) { const functionDeclaration: ts.FunctionDeclaration = astDeclaration.declaration as ts.FunctionDeclaration; @@ -462,11 +513,15 @@ export class ApiModelGenerator { const returnTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); nodesToCapture.push({ node: functionDeclaration.type, tokenRange: returnTypeTokenRange }); - const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters(nodesToCapture, - functionDeclaration.typeParameters); + const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( + nodesToCapture, + functionDeclaration.typeParameters + ); - const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, - functionDeclaration.parameters); + const parameters: IApiParameterOptions[] = this._captureParameters( + nodesToCapture, + functionDeclaration.parameters + ); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); @@ -491,14 +546,17 @@ export class ApiModelGenerator { } } - private _processApiIndexSignature(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiIndexSignature( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration); const containerKey: string = ApiIndexSignature.getContainerKey(overloadIndex); - let apiIndexSignature: ApiIndexSignature | undefined = parentApiItem.tryGetMemberByKey(containerKey) as - ApiIndexSignature; + let apiIndexSignature: ApiIndexSignature | undefined = parentApiItem.tryGetMemberByKey( + containerKey + ) as ApiIndexSignature; if (apiIndexSignature === undefined) { const indexSignature: ts.IndexSignatureDeclaration = astDeclaration.declaration as ts.IndexSignatureDeclaration; @@ -508,7 +566,10 @@ export class ApiModelGenerator { const returnTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); nodesToCapture.push({ node: indexSignature.type, tokenRange: returnTypeTokenRange }); - const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, indexSignature.parameters); + const parameters: IApiParameterOptions[] = this._captureParameters( + nodesToCapture, + indexSignature.parameters + ); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); @@ -528,21 +589,27 @@ export class ApiModelGenerator { } } - private _processApiInterface(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiInterface( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const name: string = exportedName ? exportedName : astDeclaration.astSymbol.localName; const containerKey: string = ApiInterface.getContainerKey(name); - let apiInterface: ApiInterface | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiInterface; + let apiInterface: ApiInterface | undefined = parentApiItem.tryGetMemberByKey( + containerKey + ) as ApiInterface; if (apiInterface === undefined) { const interfaceDeclaration: ts.InterfaceDeclaration = astDeclaration.declaration as ts.InterfaceDeclaration; const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; - const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters(nodesToCapture, - interfaceDeclaration.typeParameters); + const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( + nodesToCapture, + interfaceDeclaration.typeParameters + ); const extendsTokenRanges: IExcerptTokenRange[] = []; @@ -551,7 +618,7 @@ export class ApiModelGenerator { for (const heritageType of heritageClause.types) { const extendsTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); extendsTokenRanges.push(extendsTokenRange); - nodesToCapture.push({ node: heritageType, tokenRange: extendsTokenRange}); + nodesToCapture.push({ node: heritageType, tokenRange: extendsTokenRange }); } } } @@ -576,9 +643,11 @@ export class ApiModelGenerator { this._processChildDeclarations(astDeclaration, exportedName, apiInterface); } - private _processApiMethod(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiMethod( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const name: string = exportedName ? exportedName : astDeclaration.astSymbol.localName; const isStatic: boolean = (astDeclaration.modifierFlags & ts.ModifierFlags.Static) !== 0; @@ -595,10 +664,15 @@ export class ApiModelGenerator { const returnTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); nodesToCapture.push({ node: methodDeclaration.type, tokenRange: returnTypeTokenRange }); - const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters(nodesToCapture, - methodDeclaration.typeParameters); + const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( + nodesToCapture, + methodDeclaration.typeParameters + ); - const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, methodDeclaration.parameters); + const parameters: IApiParameterOptions[] = this._captureParameters( + nodesToCapture, + methodDeclaration.parameters + ); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); @@ -624,16 +698,19 @@ export class ApiModelGenerator { } } - private _processApiMethodSignature(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiMethodSignature( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const name: string = exportedName ? exportedName : astDeclaration.astSymbol.localName; const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration); const containerKey: string = ApiMethodSignature.getContainerKey(name, overloadIndex); - let apiMethodSignature: ApiMethodSignature | undefined = parentApiItem.tryGetMemberByKey(containerKey) as - ApiMethodSignature; + let apiMethodSignature: ApiMethodSignature | undefined = parentApiItem.tryGetMemberByKey( + containerKey + ) as ApiMethodSignature; if (apiMethodSignature === undefined) { const methodSignature: ts.MethodSignature = astDeclaration.declaration as ts.MethodSignature; @@ -643,10 +720,15 @@ export class ApiModelGenerator { const returnTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); nodesToCapture.push({ node: methodSignature.type, tokenRange: returnTypeTokenRange }); - const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters(nodesToCapture, - methodSignature.typeParameters); + const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( + nodesToCapture, + methodSignature.typeParameters + ); - const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, methodSignature.parameters); + const parameters: IApiParameterOptions[] = this._captureParameters( + nodesToCapture, + methodSignature.parameters + ); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); @@ -668,13 +750,17 @@ export class ApiModelGenerator { } } - private _processApiNamespace(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiNamespace( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const name: string = exportedName ? exportedName : astDeclaration.astSymbol.localName; const containerKey: string = ApiNamespace.getContainerKey(name); - let apiNamespace: ApiNamespace | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiNamespace; + let apiNamespace: ApiNamespace | undefined = parentApiItem.tryGetMemberByKey( + containerKey + ) as ApiNamespace; if (apiNamespace === undefined) { const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, []); @@ -689,17 +775,18 @@ export class ApiModelGenerator { this._processChildDeclarations(astDeclaration, exportedName, apiNamespace); } - private _processApiProperty(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiProperty( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const name: string = exportedName ? exportedName : astDeclaration.astSymbol.localName; const isStatic: boolean = (astDeclaration.modifierFlags & ts.ModifierFlags.Static) !== 0; const containerKey: string = ApiProperty.getContainerKey(name, isStatic); - let apiProperty: ApiProperty | undefined - = parentApiItem.tryGetMemberByKey(containerKey) as ApiProperty; + let apiProperty: ApiProperty | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiProperty; if (apiProperty === undefined) { const propertyDeclaration: ts.PropertyDeclaration = astDeclaration.declaration as ts.PropertyDeclaration; @@ -714,7 +801,14 @@ export class ApiModelGenerator { const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; - apiProperty = new ApiProperty({ name, docComment, releaseTag, isStatic, excerptTokens, propertyTypeTokenRange }); + apiProperty = new ApiProperty({ + name, + docComment, + releaseTag, + isStatic, + excerptTokens, + propertyTypeTokenRange + }); parentApiItem.addMember(apiProperty); } else { // If the property was already declared before (via a merged interface declaration), @@ -722,14 +816,17 @@ export class ApiModelGenerator { } } - private _processApiPropertySignature(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiPropertySignature( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const name: string = exportedName ? exportedName : astDeclaration.astSymbol.localName; const containerKey: string = ApiPropertySignature.getContainerKey(name); - let apiPropertySignature: ApiPropertySignature | undefined - = parentApiItem.tryGetMemberByKey(containerKey) as ApiPropertySignature; + let apiPropertySignature: ApiPropertySignature | undefined = parentApiItem.tryGetMemberByKey( + containerKey + ) as ApiPropertySignature; if (apiPropertySignature === undefined) { const propertySignature: ts.PropertySignature = astDeclaration.declaration as ts.PropertySignature; @@ -759,23 +856,28 @@ export class ApiModelGenerator { } } - private _processApiTypeAlias(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiTypeAlias( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const name: string = exportedName ? exportedName : astDeclaration.astSymbol.localName; const containerKey: string = ApiTypeAlias.getContainerKey(name); - let apiTypeAlias: ApiTypeAlias | undefined = parentApiItem.tryGetMemberByKey(containerKey) as - ApiTypeAlias; + let apiTypeAlias: ApiTypeAlias | undefined = parentApiItem.tryGetMemberByKey( + containerKey + ) as ApiTypeAlias; if (apiTypeAlias === undefined) { const typeAliasDeclaration: ts.TypeAliasDeclaration = astDeclaration.declaration as ts.TypeAliasDeclaration; const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; - const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters(nodesToCapture, - typeAliasDeclaration.typeParameters); + const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( + nodesToCapture, + typeAliasDeclaration.typeParameters + ); const typeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); nodesToCapture.push({ node: typeAliasDeclaration.type, tokenRange: typeTokenRange }); @@ -798,15 +900,16 @@ export class ApiModelGenerator { } } - private _processApiVariable(astDeclaration: AstDeclaration, exportedName: string | undefined, - parentApiItem: ApiItemContainerMixin): void { - + private _processApiVariable( + astDeclaration: AstDeclaration, + exportedName: string | undefined, + parentApiItem: ApiItemContainerMixin + ): void { const name: string = exportedName ? exportedName : astDeclaration.astSymbol.localName; const containerKey: string = ApiVariable.getContainerKey(name); - let apiVariable: ApiVariable | undefined = parentApiItem.tryGetMemberByKey(containerKey) as - ApiVariable; + let apiVariable: ApiVariable | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiVariable; if (apiVariable === undefined) { const variableDeclaration: ts.VariableDeclaration = astDeclaration.declaration as ts.VariableDeclaration; @@ -830,9 +933,10 @@ export class ApiModelGenerator { /** * @param nodesToCapture - A list of child nodes whose token ranges we want to capture */ - private _buildExcerptTokens(astDeclaration: AstDeclaration, - nodesToCapture: IExcerptBuilderNodeToCapture[]): IExcerptToken[] { - + private _buildExcerptTokens( + astDeclaration: AstDeclaration, + nodesToCapture: IExcerptBuilderNodeToCapture[] + ): IExcerptToken[] { const excerptTokens: IExcerptToken[] = []; // Build the main declaration @@ -843,15 +947,21 @@ export class ApiModelGenerator { // Add any ancillary declarations for (const ancillaryDeclaration of declarationMetadata.ancillaryDeclarations) { ExcerptBuilder.addBlankLine(excerptTokens); - ExcerptBuilder.addDeclaration(excerptTokens, ancillaryDeclaration, nodesToCapture, this._referenceGenerator); + ExcerptBuilder.addDeclaration( + excerptTokens, + ancillaryDeclaration, + nodesToCapture, + this._referenceGenerator + ); } return excerptTokens; } - private _captureTypeParameters(nodesToCapture: IExcerptBuilderNodeToCapture[], typeParameterNodes: - ts.NodeArray | undefined): IApiTypeParameterOptions[] { - + private _captureTypeParameters( + nodesToCapture: IExcerptBuilderNodeToCapture[], + typeParameterNodes: ts.NodeArray | undefined + ): IApiTypeParameterOptions[] { const typeParameters: IApiTypeParameterOptions[] = []; if (typeParameterNodes) { for (const typeParameter of typeParameterNodes) { @@ -871,9 +981,10 @@ export class ApiModelGenerator { return typeParameters; } - private _captureParameters(nodesToCapture: IExcerptBuilderNodeToCapture[], - parameterNodes: ts.NodeArray): IApiParameterOptions[] { - + private _captureParameters( + nodesToCapture: IExcerptBuilderNodeToCapture[], + parameterNodes: ts.NodeArray + ): IApiParameterOptions[] { const parameters: IApiParameterOptions[] = []; for (const parameter of parameterNodes) { const parameterTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); diff --git a/src/generators/ApiReportGenerator.ts b/src/generators/ApiReportGenerator.ts index 8f00c63fb8c..41ecb8ed631 100644 --- a/src/generators/ApiReportGenerator.ts +++ b/src/generators/ApiReportGenerator.ts @@ -28,7 +28,10 @@ export class ApiReportGenerator { * might be introduced by a tool, e.g. Git newline normalization or an editor that strips * whitespace when saving. */ - public static areEquivalentApiFileContents(actualFileContent: string, expectedFileContent: string): boolean { + public static areEquivalentApiFileContents( + actualFileContent: string, + expectedFileContent: string + ): boolean { // NOTE: "\s" also matches "\r" and "\n" const normalizedActual: string = actualFileContent.replace(/[\s]+/g, ' '); const normalizedExpected: string = expectedFileContent.replace(/[\s]+/g, ' '); @@ -38,12 +41,14 @@ export class ApiReportGenerator { public static generateReviewFileContent(collector: Collector): string { const stringWriter: StringWriter = new StringWriter(); - stringWriter.writeLine([ - `## API Report File for "${collector.workingPackage.name}"`, - ``, - `> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).`, - `` - ].join('\n')); + stringWriter.writeLine( + [ + `## API Report File for "${collector.workingPackage.name}"`, + ``, + `> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).`, + `` + ].join('\n') + ); // Write the opening delimiter for the Markdown code fence stringWriter.writeLine('```ts\n'); @@ -64,7 +69,6 @@ export class ApiReportGenerator { // Emit the regular declarations for (const entity of collector.entities) { if (entity.exported) { - // First, collect the list of export names for this symbol. When reporting messages with // ExtractorMessage.properties.exportName, this will enable us to emit the warning comments alongside // the associated export statement. @@ -83,10 +87,10 @@ export class ApiReportGenerator { if (entity.astEntity instanceof AstSymbol) { // Emit all the declarations for this entity for (const astDeclaration of entity.astEntity.astDeclarations || []) { - // Get the messages associated with this declaration - const fetchedMessages: ExtractorMessage[] = collector.messageRouter - .fetchAssociatedMessagesForReviewFile(astDeclaration); + const fetchedMessages: ExtractorMessage[] = collector.messageRouter.fetchAssociatedMessagesForReviewFile( + astDeclaration + ); // Peel off the messages associated with an export statement and store them // in IExportToEmit.associatedMessages (to be processed later). The remaining messages will @@ -94,7 +98,9 @@ export class ApiReportGenerator { const messagesToReport: ExtractorMessage[] = []; for (const message of fetchedMessages) { if (message.properties.exportName) { - const exportToEmit: IExportToEmit | undefined = exportsToEmit.get(message.properties.exportName); + const exportToEmit: IExportToEmit | undefined = exportsToEmit.get( + message.properties.exportName + ); if (exportToEmit) { exportToEmit.associatedMessages.push(message); continue; @@ -103,7 +109,9 @@ export class ApiReportGenerator { messagesToReport.push(message); } - stringWriter.write(ApiReportGenerator._getAedocSynopsis(collector, astDeclaration, messagesToReport)); + stringWriter.write( + ApiReportGenerator._getAedocSynopsis(collector, astDeclaration, messagesToReport) + ); const span: Span = new Span(astDeclaration.declaration); @@ -120,16 +128,21 @@ export class ApiReportGenerator { } if (entity.astEntity instanceof AstImportAsModule) { - throw new Error('"import * as ___ from ___;" is not supported for local files when generating report.' - + '\nFailure in: ' + ApiReportGenerator._getSourceFilePath(entity.astEntity)); + throw new Error( + '"import * as ___ from ___;" is not supported for local files when generating report.' + + '\nFailure in: ' + + ApiReportGenerator._getSourceFilePath(entity.astEntity) + ); } // Now emit the export statements for this entity. for (const exportToEmit of exportsToEmit.values()) { // Write any associated messages for (const message of exportToEmit.associatedMessages) { - ApiReportGenerator._writeLineAsComments(stringWriter, - 'Warning: ' + message.formatMessageWithoutLocation()); + ApiReportGenerator._writeLineAsComments( + stringWriter, + 'Warning: ' + message.formatMessageWithoutLocation() + ); } DtsEmitHelpers.emitNamedExport(stringWriter, exportToEmit.exportName, entity); @@ -141,22 +154,25 @@ export class ApiReportGenerator { DtsEmitHelpers.emitStarExports(stringWriter, collector); // Write the unassociated warnings at the bottom of the file - const unassociatedMessages: ExtractorMessage[] = collector.messageRouter - .fetchUnassociatedMessagesForReviewFile(); + const unassociatedMessages: ExtractorMessage[] = collector.messageRouter.fetchUnassociatedMessagesForReviewFile(); if (unassociatedMessages.length > 0) { stringWriter.writeLine(); ApiReportGenerator._writeLineAsComments(stringWriter, 'Warnings were encountered during analysis:'); ApiReportGenerator._writeLineAsComments(stringWriter, ''); for (const unassociatedMessage of unassociatedMessages) { - ApiReportGenerator._writeLineAsComments(stringWriter, unassociatedMessage.formatMessageWithLocation( - collector.workingPackage.packageFolder - )); + ApiReportGenerator._writeLineAsComments( + stringWriter, + unassociatedMessage.formatMessageWithLocation(collector.workingPackage.packageFolder) + ); } } if (collector.workingPackage.tsdocComment === undefined) { stringWriter.writeLine(); - ApiReportGenerator._writeLineAsComments(stringWriter, '(No @packageDocumentation comment for this package)'); + ApiReportGenerator._writeLineAsComments( + stringWriter, + '(No @packageDocumentation comment for this package)' + ); } // Write the closing delimiter for the Markdown code fence @@ -181,11 +197,16 @@ export class ApiReportGenerator { /** * Before writing out a declaration, _modifySpan() applies various fixups to make it nice. */ - private static _modifySpan(collector: Collector, span: Span, entity: CollectorEntity, - astDeclaration: AstDeclaration, insideTypeLiteral: boolean): void { - + private static _modifySpan( + collector: Collector, + span: Span, + entity: CollectorEntity, + astDeclaration: AstDeclaration, + insideTypeLiteral: boolean + ): void { // Should we process this declaration at all? - if ((astDeclaration.modifierFlags & ts.ModifierFlags.Private) !== 0) { // eslint-disable-line no-bitwise + // eslint-disable-next-line no-bitwise + if ((astDeclaration.modifierFlags & ts.ModifierFlags.Private) !== 0) { span.modification.skipAll(); return; } @@ -246,7 +267,7 @@ export class ApiReportGenerator { } break; - case ts.SyntaxKind.VariableDeclaration: + case ts.SyntaxKind.VariableDeclaration: if (!span.parent) { // The VariableDeclaration node is part of a VariableDeclarationList, however // the Entry.followedSymbol points to the VariableDeclaration part because @@ -255,14 +276,17 @@ export class ApiReportGenerator { // Since we are emitting a separate declaration for each one, we need to look upwards // in the ts.Node tree and write a copy of the enclosing VariableDeclarationList // content (e.g. "var" from "var x=1, y=2"). - const list: ts.VariableDeclarationList | undefined = TypeScriptHelpers.matchAncestor(span.node, - [ts.SyntaxKind.VariableDeclarationList, ts.SyntaxKind.VariableDeclaration]); + const list: ts.VariableDeclarationList | undefined = TypeScriptHelpers.matchAncestor(span.node, [ + ts.SyntaxKind.VariableDeclarationList, + ts.SyntaxKind.VariableDeclaration + ]); if (!list) { // This should not happen unless the compiler API changes somehow throw new InternalError('Unsupported variable declaration'); } - const listPrefix: string = list.getSourceFile().text - .substring(list.getStart(), list.declarations[0].getStart()); + const listPrefix: string = list + .getSourceFile() + .text.substring(list.getStart(), list.declarations[0].getStart()); span.modification.prefix = listPrefix + span.modification.prefix; span.modification.suffix = ';'; @@ -303,21 +327,31 @@ export class ApiReportGenerator { let childAstDeclaration: AstDeclaration = astDeclaration; if (AstDeclaration.isSupportedSyntaxKind(child.kind)) { - childAstDeclaration = collector.astSymbolTable.getChildAstDeclarationByNode(child.node, astDeclaration); + childAstDeclaration = collector.astSymbolTable.getChildAstDeclarationByNode( + child.node, + astDeclaration + ); if (sortChildren) { span.modification.sortChildren = true; child.modification.sortKey = Collector.getSortKeyIgnoringUnderscore( - childAstDeclaration.astSymbol.localName); + childAstDeclaration.astSymbol.localName + ); } if (!insideTypeLiteral) { - const messagesToReport: ExtractorMessage[] = collector.messageRouter - .fetchAssociatedMessagesForReviewFile(childAstDeclaration); - const aedocSynopsis: string = ApiReportGenerator._getAedocSynopsis(collector, childAstDeclaration, - messagesToReport); - const indentedAedocSynopsis: string = ApiReportGenerator._addIndentAfterNewlines(aedocSynopsis, - child.getIndent()); + const messagesToReport: ExtractorMessage[] = collector.messageRouter.fetchAssociatedMessagesForReviewFile( + childAstDeclaration + ); + const aedocSynopsis: string = ApiReportGenerator._getAedocSynopsis( + collector, + childAstDeclaration, + messagesToReport + ); + const indentedAedocSynopsis: string = ApiReportGenerator._addIndentAfterNewlines( + aedocSynopsis, + child.getIndent() + ); child.modification.prefix = indentedAedocSynopsis + child.modification.prefix; } @@ -366,9 +400,7 @@ export class ApiReportGenerator { let skipRest: boolean = false; for (const child of span.children) { - if (skipRest - || child.kind === ts.SyntaxKind.SyntaxList - || child.kind === ts.SyntaxKind.JSDocComment) { + if (skipRest || child.kind === ts.SyntaxKind.SyntaxList || child.kind === ts.SyntaxKind.JSDocComment) { child.modification.skipAll(); } if (child.kind === ts.SyntaxKind.Identifier) { @@ -384,12 +416,18 @@ export class ApiReportGenerator { * whether the item has been documented, and any warnings that were detected * by the analysis. */ - private static _getAedocSynopsis(collector: Collector, astDeclaration: AstDeclaration, - messagesToReport: ExtractorMessage[]): string { + private static _getAedocSynopsis( + collector: Collector, + astDeclaration: AstDeclaration, + messagesToReport: ExtractorMessage[] + ): string { const stringWriter: StringWriter = new StringWriter(); for (const message of messagesToReport) { - ApiReportGenerator._writeLineAsComments(stringWriter, 'Warning: ' + message.formatMessageWithoutLocation()); + ApiReportGenerator._writeLineAsComments( + stringWriter, + 'Warning: ' + message.formatMessageWithoutLocation() + ); } if (!collector.isAncillaryDeclaration(astDeclaration)) { @@ -454,5 +492,4 @@ export class ApiReportGenerator { } return Text.replaceAll(text, '\n', '\n' + indent); } - } diff --git a/src/generators/DeclarationReferenceGenerator.ts b/src/generators/DeclarationReferenceGenerator.ts index 77b2b8383f2..ee12897318d 100644 --- a/src/generators/DeclarationReferenceGenerator.ts +++ b/src/generators/DeclarationReferenceGenerator.ts @@ -22,9 +22,12 @@ export class DeclarationReferenceGenerator { private _program: ts.Program; private _typeChecker: ts.TypeChecker; - public constructor(packageJsonLookup: PackageJsonLookup, workingPackageName: string, program: ts.Program, - typeChecker: ts.TypeChecker) { - + public constructor( + packageJsonLookup: PackageJsonLookup, + workingPackageName: string, + program: ts.Program, + typeChecker: ts.TypeChecker + ) { this._packageJsonLookup = packageJsonLookup; this._workingPackageName = workingPackageName; this._program = program; @@ -38,50 +41,81 @@ export class DeclarationReferenceGenerator { const symbol: ts.Symbol | undefined = this._typeChecker.getSymbolAtLocation(node); if (symbol !== undefined) { const isExpression: boolean = DeclarationReferenceGenerator._isInExpressionContext(node); - return this.getDeclarationReferenceForSymbol(symbol, isExpression ? ts.SymbolFlags.Value : ts.SymbolFlags.Type) - || this.getDeclarationReferenceForSymbol(symbol, isExpression ? ts.SymbolFlags.Type : ts.SymbolFlags.Value) - || this.getDeclarationReferenceForSymbol(symbol, ts.SymbolFlags.Namespace); + return ( + this.getDeclarationReferenceForSymbol( + symbol, + isExpression ? ts.SymbolFlags.Value : ts.SymbolFlags.Type + ) || + this.getDeclarationReferenceForSymbol( + symbol, + isExpression ? ts.SymbolFlags.Type : ts.SymbolFlags.Value + ) || + this.getDeclarationReferenceForSymbol(symbol, ts.SymbolFlags.Namespace) + ); } } /** * Gets the DeclarationReference for a TypeScript Symbol for a given meaning. */ - public getDeclarationReferenceForSymbol(symbol: ts.Symbol, meaning: ts.SymbolFlags - ): DeclarationReference | undefined { + public getDeclarationReferenceForSymbol( + symbol: ts.Symbol, + meaning: ts.SymbolFlags + ): DeclarationReference | undefined { return this._symbolToDeclarationReference(symbol, meaning, /*includeModuleSymbols*/ false); } private static _isInExpressionContext(node: ts.Node): boolean { switch (node.parent.kind) { - case ts.SyntaxKind.TypeQuery: return true; - case ts.SyntaxKind.QualifiedName: return DeclarationReferenceGenerator._isInExpressionContext(node.parent); - default: return false; + case ts.SyntaxKind.TypeQuery: + return true; + case ts.SyntaxKind.QualifiedName: + return DeclarationReferenceGenerator._isInExpressionContext(node.parent); + default: + return false; } } private static _isExternalModuleSymbol(symbol: ts.Symbol): boolean { - return !!(symbol.flags & ts.SymbolFlags.ValueModule) - && symbol.valueDeclaration !== undefined - && ts.isSourceFile(symbol.valueDeclaration); + return ( + !!(symbol.flags & ts.SymbolFlags.ValueModule) && + symbol.valueDeclaration !== undefined && + ts.isSourceFile(symbol.valueDeclaration) + ); } private static _isSameSymbol(left: ts.Symbol | undefined, right: ts.Symbol): boolean { - return left === right - || !!(left && left.valueDeclaration && right.valueDeclaration && left.valueDeclaration === right.valueDeclaration); + return ( + left === right || + !!( + left && + left.valueDeclaration && + right.valueDeclaration && + left.valueDeclaration === right.valueDeclaration + ) + ); } private static _getNavigationToSymbol(symbol: ts.Symbol): Navigation | 'global' { const parent: ts.Symbol | undefined = TypeScriptInternals.getSymbolParent(symbol); // First, try to determine navigation to symbol via its parent. if (parent) { - if (parent.exports && DeclarationReferenceGenerator._isSameSymbol(parent.exports.get(symbol.escapedName), symbol)) { + if ( + parent.exports && + DeclarationReferenceGenerator._isSameSymbol(parent.exports.get(symbol.escapedName), symbol) + ) { return Navigation.Exports; } - if (parent.members && DeclarationReferenceGenerator._isSameSymbol(parent.members.get(symbol.escapedName), symbol)) { + if ( + parent.members && + DeclarationReferenceGenerator._isSameSymbol(parent.members.get(symbol.escapedName), symbol) + ) { return Navigation.Members; } - if (parent.globalExports && DeclarationReferenceGenerator._isSameSymbol(parent.globalExports.get(symbol.escapedName), symbol)) { + if ( + parent.globalExports && + DeclarationReferenceGenerator._isSameSymbol(parent.globalExports.get(symbol.escapedName), symbol) + ) { return 'global'; } } @@ -105,11 +139,12 @@ export class DeclarationReferenceGenerator { // enum members are exports return Navigation.Exports; } - if (ts.isExportSpecifier(declaration) - || ts.isExportAssignment(declaration) - || ts.isExportSpecifier(declaration) - || ts.isExportDeclaration(declaration) - || ts.isNamedExports(declaration) + if ( + ts.isExportSpecifier(declaration) || + ts.isExportAssignment(declaration) || + ts.isExportSpecifier(declaration) || + ts.isExportDeclaration(declaration) || + ts.isNamedExports(declaration) ) { return Navigation.Exports; } @@ -175,9 +210,11 @@ export class DeclarationReferenceGenerator { return undefined; } - private _symbolToDeclarationReference(symbol: ts.Symbol, meaning: ts.SymbolFlags, includeModuleSymbols: boolean - ): DeclarationReference | undefined { - + private _symbolToDeclarationReference( + symbol: ts.Symbol, + meaning: ts.SymbolFlags, + includeModuleSymbols: boolean + ): DeclarationReference | undefined { let followedSymbol: ts.Symbol = symbol; if (followedSymbol.flags & ts.SymbolFlags.ExportValue) { followedSymbol = this._typeChecker.getExportSymbolOfSymbol(followedSymbol); @@ -191,9 +228,9 @@ export class DeclarationReferenceGenerator { return undefined; } const sourceFile: ts.SourceFile | undefined = - followedSymbol.declarations - && followedSymbol.declarations[0] - && followedSymbol.declarations[0].getSourceFile(); + followedSymbol.declarations && + followedSymbol.declarations[0] && + followedSymbol.declarations[0].getSourceFile(); return new DeclarationReference(this._sourceFileToModuleSource(sourceFile)); } @@ -205,13 +242,17 @@ export class DeclarationReferenceGenerator { const parent: ts.Symbol | undefined = TypeScriptInternals.getSymbolParent(followedSymbol); let parentRef: DeclarationReference | undefined; if (parent) { - parentRef = this._symbolToDeclarationReference(parent, ts.SymbolFlags.Namespace, /*includeModuleSymbols*/ true); + parentRef = this._symbolToDeclarationReference( + parent, + ts.SymbolFlags.Namespace, + /*includeModuleSymbols*/ true + ); } else { // this may be a local symbol in a module... const sourceFile: ts.SourceFile | undefined = - followedSymbol.declarations - && followedSymbol.declarations[0] - && followedSymbol.declarations[0].getSourceFile(); + followedSymbol.declarations && + followedSymbol.declarations[0] && + followedSymbol.declarations[0].getSourceFile(); if (sourceFile && ts.isExternalModule(sourceFile)) { parentRef = new DeclarationReference(this._sourceFileToModuleSource(sourceFile)); } else { @@ -227,7 +268,9 @@ export class DeclarationReferenceGenerator { if (followedSymbol.escapedName === ts.InternalSymbolName.Constructor) { localName = 'constructor'; } else { - const wellKnownName: string | undefined = TypeScriptHelpers.tryDecodeWellKnownSymbolName(followedSymbol.escapedName); + const wellKnownName: string | undefined = TypeScriptHelpers.tryDecodeWellKnownSymbolName( + followedSymbol.escapedName + ); if (wellKnownName) { // TypeScript binds well-known ECMAScript symbols like 'Symbol.iterator' as '__@iterator'. // This converts a string like '__@iterator' into the property name '[Symbol.iterator]'. @@ -246,7 +289,9 @@ export class DeclarationReferenceGenerator { } } - let navigation: Navigation | 'global' = DeclarationReferenceGenerator._getNavigationToSymbol(followedSymbol); + let navigation: Navigation | 'global' = DeclarationReferenceGenerator._getNavigationToSymbol( + followedSymbol + ); if (navigation === 'global') { if (parentRef.source !== GlobalSource.instance) { parentRef = new DeclarationReference(GlobalSource.instance); @@ -261,8 +306,9 @@ export class DeclarationReferenceGenerator { private _getPackageName(sourceFile: ts.SourceFile): string { if (this._program.isSourceFileFromExternalLibrary(sourceFile)) { - const packageJson: INodePackageJson | undefined = this._packageJsonLookup - .tryLoadNodePackageJsonFor(sourceFile.fileName); + const packageJson: INodePackageJson | undefined = this._packageJsonLookup.tryLoadNodePackageJsonFor( + sourceFile.fileName + ); if (packageJson && packageJson.name) { return packageJson.name; @@ -279,4 +325,3 @@ export class DeclarationReferenceGenerator { return GlobalSource.instance; } } - diff --git a/src/generators/DtsEmitHelpers.ts b/src/generators/DtsEmitHelpers.ts index e4621ca4eee..fe05d8fc057 100644 --- a/src/generators/DtsEmitHelpers.ts +++ b/src/generators/DtsEmitHelpers.ts @@ -13,7 +13,11 @@ import { Collector } from '../collector/Collector'; * Some common code shared between DtsRollupGenerator and ApiReportGenerator. */ export class DtsEmitHelpers { - public static emitImport(stringWriter: StringWriter, collectorEntity: CollectorEntity, astImport: AstImport): void { + public static emitImport( + stringWriter: StringWriter, + collectorEntity: CollectorEntity, + astImport: AstImport + ): void { switch (astImport.importKind) { case AstImportKind.DefaultImport: if (collectorEntity.nameForEmit !== astImport.exportName) { @@ -42,9 +46,11 @@ export class DtsEmitHelpers { } } - public static emitNamedExport(stringWriter: StringWriter, exportName: string, - collectorEntity: CollectorEntity): void { - + public static emitNamedExport( + stringWriter: StringWriter, + exportName: string, + collectorEntity: CollectorEntity + ): void { if (exportName === ts.InternalSymbolName.Default) { stringWriter.writeLine(`export default ${collectorEntity.nameForEmit};`); } else if (collectorEntity.nameForEmit !== exportName) { diff --git a/src/generators/DtsRollupGenerator.ts b/src/generators/DtsRollupGenerator.ts index 034345aa9bd..b996200db0f 100644 --- a/src/generators/DtsRollupGenerator.ts +++ b/src/generators/DtsRollupGenerator.ts @@ -54,7 +54,10 @@ export class DtsRollupGenerator { * @param dtsFilename - The *.d.ts output filename */ public static writeTypingsFile( - collector: Collector, dtsFilename: string, dtsKind: DtsRollupKind, newlineKind: NewlineKind + collector: Collector, + dtsFilename: string, + dtsKind: DtsRollupKind, + newlineKind: NewlineKind ): void { const stringWriter: StringWriter = new StringWriter(); @@ -66,9 +69,11 @@ export class DtsRollupGenerator { }); } - private static _generateTypingsFileContent(collector: Collector, stringWriter: StringWriter, - dtsKind: DtsRollupKind): void { - + private static _generateTypingsFileContent( + collector: Collector, + stringWriter: StringWriter, + dtsKind: DtsRollupKind + ): void { if (collector.workingPackage.tsdocParserContext) { stringWriter.writeLine(collector.workingPackage.tsdocParserContext.sourceRange.toString()); stringWriter.writeLine(); @@ -93,7 +98,8 @@ export class DtsRollupGenerator { // and it was marked as `@internal`, then don't emit it. const symbolMetadata: SymbolMetadata | undefined = collector.tryFetchMetadataForAstEntity(astImport); const maxEffectiveReleaseTag: ReleaseTag = symbolMetadata - ? symbolMetadata.maxEffectiveReleaseTag : ReleaseTag.None; + ? symbolMetadata.maxEffectiveReleaseTag + : ReleaseTag.None; if (this._shouldIncludeReleaseTag(maxEffectiveReleaseTag, dtsKind)) { DtsEmitHelpers.emitImport(stringWriter, entity, astImport); @@ -103,9 +109,12 @@ export class DtsRollupGenerator { // Emit the regular declarations for (const entity of collector.entities) { - const symbolMetadata: SymbolMetadata | undefined = collector.tryFetchMetadataForAstEntity(entity.astEntity); + const symbolMetadata: SymbolMetadata | undefined = collector.tryFetchMetadataForAstEntity( + entity.astEntity + ); const maxEffectiveReleaseTag: ReleaseTag = symbolMetadata - ? symbolMetadata.maxEffectiveReleaseTag : ReleaseTag.None; + ? symbolMetadata.maxEffectiveReleaseTag + : ReleaseTag.None; if (!this._shouldIncludeReleaseTag(maxEffectiveReleaseTag, dtsKind)) { if (!collector.extractorConfig.omitTrimmingComments) { @@ -123,7 +132,9 @@ export class DtsRollupGenerator { if (!this._shouldIncludeReleaseTag(apiItemMetadata.effectiveReleaseTag, dtsKind)) { if (!collector.extractorConfig.omitTrimmingComments) { stringWriter.writeLine(); - stringWriter.writeLine(`/* Excluded declaration from this release type: ${entity.nameForEmit} */`); + stringWriter.writeLine( + `/* Excluded declaration from this release type: ${entity.nameForEmit} */` + ); } continue; } else { @@ -136,7 +147,9 @@ export class DtsRollupGenerator { } if (entity.astEntity instanceof AstImportAsModule) { - const astModuleExportInfo: AstModuleExportInfo = collector.astSymbolTable.fetchAstModuleExportInfo(entity.astEntity.astModule); + const astModuleExportInfo: AstModuleExportInfo = collector.astSymbolTable.fetchAstModuleExportInfo( + entity.astEntity.astModule + ); if (entity.nameForEmit === undefined) { // This should never happen @@ -152,11 +165,15 @@ export class DtsRollupGenerator { // all local exports of local imported module are just references to top-level declarations stringWriter.writeLine(' export {'); astModuleExportInfo.exportedLocalEntities.forEach((exportedEntity, exportedName) => { - const collectorEntity: CollectorEntity | undefined = collector.tryGetCollectorEntity(exportedEntity); + const collectorEntity: CollectorEntity | undefined = collector.tryGetCollectorEntity( + exportedEntity + ); if (collectorEntity === undefined) { // This should never happen // top-level exports of local imported module should be added as collector entities before - throw new InternalError(`Cannot find collector entity for ${entity.nameForEmit}.${exportedEntity.localName}`); + throw new InternalError( + `Cannot find collector entity for ${entity.nameForEmit}.${exportedEntity.localName}` + ); } if (collectorEntity.nameForEmit === exportedName) { stringWriter.writeLine(` ${collectorEntity.nameForEmit},`); @@ -168,7 +185,9 @@ export class DtsRollupGenerator { if (astModuleExportInfo.starExportedExternalModules.size > 0) { // TODO [MA]: write a better error message. - throw new Error(`Unsupported star export of external module inside namespace imported module: ${entity.nameForEmit}`); + throw new Error( + `Unsupported star export of external module inside namespace imported module: ${entity.nameForEmit}` + ); } stringWriter.writeLine('}'); // end of "declare namespace { ... }" @@ -192,9 +211,13 @@ export class DtsRollupGenerator { /** * Before writing out a declaration, _modifySpan() applies various fixups to make it nice. */ - private static _modifySpan(collector: Collector, span: Span, entity: CollectorEntity, - astDeclaration: AstDeclaration, dtsKind: DtsRollupKind): void { - + private static _modifySpan( + collector: Collector, + span: Span, + entity: CollectorEntity, + astDeclaration: AstDeclaration, + dtsKind: DtsRollupKind + ): void { const previousSpan: Span | undefined = span.previousSibling; let recurseChildren: boolean = true; @@ -258,14 +281,17 @@ export class DtsRollupGenerator { // Since we are emitting a separate declaration for each one, we need to look upwards // in the ts.Node tree and write a copy of the enclosing VariableDeclarationList // content (e.g. "var" from "var x=1, y=2"). - const list: ts.VariableDeclarationList | undefined = TypeScriptHelpers.matchAncestor(span.node, - [ts.SyntaxKind.VariableDeclarationList, ts.SyntaxKind.VariableDeclaration]); + const list: ts.VariableDeclarationList | undefined = TypeScriptHelpers.matchAncestor(span.node, [ + ts.SyntaxKind.VariableDeclarationList, + ts.SyntaxKind.VariableDeclaration + ]); if (!list) { // This should not happen unless the compiler API changes somehow throw new InternalError('Unsupported variable declaration'); } - const listPrefix: string = list.getSourceFile().text - .substring(list.getStart(), list.declarations[0].getStart()); + const listPrefix: string = list + .getSourceFile() + .text.substring(list.getStart(), list.declarations[0].getStart()); span.modification.prefix = 'declare ' + listPrefix + span.modification.prefix; span.modification.suffix = ';'; @@ -316,8 +342,12 @@ export class DtsRollupGenerator { // Should we trim this node? let trimmed: boolean = false; if (AstDeclaration.isSupportedSyntaxKind(child.kind)) { - childAstDeclaration = collector.astSymbolTable.getChildAstDeclarationByNode(child.node, astDeclaration); - const releaseTag: ReleaseTag = collector.fetchApiItemMetadata(childAstDeclaration).effectiveReleaseTag; + childAstDeclaration = collector.astSymbolTable.getChildAstDeclarationByNode( + child.node, + astDeclaration + ); + const releaseTag: ReleaseTag = collector.fetchApiItemMetadata(childAstDeclaration) + .effectiveReleaseTag; if (!this._shouldIncludeReleaseTag(releaseTag, dtsKind)) { let nodeToTrim: Span = child; @@ -325,8 +355,9 @@ export class DtsRollupGenerator { // If we are trimming a variable statement, then we need to trim the outer VariableDeclarationList // as well. if (child.kind === ts.SyntaxKind.VariableDeclaration) { - const variableStatement: Span | undefined - = child.findFirstParent(ts.SyntaxKind.VariableStatement); + const variableStatement: Span | undefined = child.findFirstParent( + ts.SyntaxKind.VariableStatement + ); if (variableStatement !== undefined) { nodeToTrim = variableStatement; } @@ -373,13 +404,14 @@ export class DtsRollupGenerator { } private static _shouldIncludeReleaseTag(releaseTag: ReleaseTag, dtsKind: DtsRollupKind): boolean { - switch (dtsKind) { case DtsRollupKind.InternalRelease: return true; case DtsRollupKind.BetaRelease: // NOTE: If the release tag is "None", then we don't have enough information to trim it - return releaseTag === ReleaseTag.Beta || releaseTag === ReleaseTag.Public || releaseTag === ReleaseTag.None; + return ( + releaseTag === ReleaseTag.Beta || releaseTag === ReleaseTag.Public || releaseTag === ReleaseTag.None + ); case DtsRollupKind.PublicRelease: return releaseTag === ReleaseTag.Public || releaseTag === ReleaseTag.None; } diff --git a/src/generators/ExcerptBuilder.ts b/src/generators/ExcerptBuilder.ts index 1266d70e63a..e2f02620f6a 100644 --- a/src/generators/ExcerptBuilder.ts +++ b/src/generators/ExcerptBuilder.ts @@ -3,11 +3,7 @@ import * as ts from 'typescript'; import { DeclarationReference } from '@microsoft/tsdoc/lib/beta/DeclarationReference'; -import { - ExcerptTokenKind, - IExcerptToken, - IExcerptTokenRange -} from '@microsoft/api-extractor-model'; +import { ExcerptTokenKind, IExcerptToken, IExcerptTokenRange } from '@microsoft/api-extractor-model'; import { Span } from '../analyzer/Span'; import { DeclarationReferenceGenerator } from './DeclarationReferenceGenerator'; @@ -82,9 +78,12 @@ export class ExcerptBuilder { * @param excerptTokens - The target token list to append to * @param nodesToCapture - A list of child nodes whose token ranges we want to capture */ - public static addDeclaration(excerptTokens: IExcerptToken[], astDeclaration: AstDeclaration, - nodesToCapture: IExcerptBuilderNodeToCapture[], referenceGenerator: DeclarationReferenceGenerator): void { - + public static addDeclaration( + excerptTokens: IExcerptToken[], + astDeclaration: AstDeclaration, + nodesToCapture: IExcerptBuilderNodeToCapture[], + referenceGenerator: DeclarationReferenceGenerator + ): void { let stopBeforeChildKind: ts.SyntaxKind | undefined = undefined; switch (astDeclaration.declaration.kind) { @@ -149,11 +148,15 @@ export class ExcerptBuilder { } if (canonicalReference) { - ExcerptBuilder._appendToken(excerptTokens, ExcerptTokenKind.Reference, - span.prefix, state, canonicalReference); + ExcerptBuilder._appendToken( + excerptTokens, + ExcerptTokenKind.Reference, + span.prefix, + state, + canonicalReference + ); } else { - ExcerptBuilder._appendToken(excerptTokens, ExcerptTokenKind.Content, - span.prefix, state); + ExcerptBuilder._appendToken(excerptTokens, ExcerptTokenKind.Content, span.prefix, state); } } @@ -190,24 +193,32 @@ export class ExcerptBuilder { return true; } - private static _appendToken(excerptTokens: IExcerptToken[], excerptTokenKind: ExcerptTokenKind, - text: string, state: IBuildSpanState, canonicalReference?: DeclarationReference): void { - + private static _appendToken( + excerptTokens: IExcerptToken[], + excerptTokenKind: ExcerptTokenKind, + text: string, + state: IBuildSpanState, + canonicalReference?: DeclarationReference + ): void { if (text.length === 0) { return; } if (excerptTokenKind !== ExcerptTokenKind.Content) { - if (excerptTokenKind === ExcerptTokenKind.Reference && excerptTokens.length > 1 - && !state.disableMergingForNextToken) { + if ( + excerptTokenKind === ExcerptTokenKind.Reference && + excerptTokens.length > 1 && + !state.disableMergingForNextToken + ) { // If the previous two tokens were a Reference and a '.', then concatenate // all three tokens as a qualified name Reference. const previousTokenM1: IExcerptToken = excerptTokens[excerptTokens.length - 1]; const previousTokenM2: IExcerptToken = excerptTokens[excerptTokens.length - 2]; - if (previousTokenM1.kind === ExcerptTokenKind.Content - && previousTokenM1.text.trim() === '.' - && previousTokenM2.kind === ExcerptTokenKind.Reference) { - + if ( + previousTokenM1.kind === ExcerptTokenKind.Content && + previousTokenM1.text.trim() === '.' && + previousTokenM2.kind === ExcerptTokenKind.Reference + ) { previousTokenM2.text += '.' + text; if (canonicalReference !== undefined) { previousTokenM2.canonicalReference = canonicalReference.toString(); @@ -266,5 +277,4 @@ export class ExcerptBuilder { return false; } } - } diff --git a/src/index.ts b/src/index.ts index e6d162ea6e9..3e64e505a1e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,16 +13,9 @@ export { ConsoleMessageId } from './api/ConsoleMessageId'; export { CompilerState, ICompilerStateCreateOptions } from './api/CompilerState'; -export { - Extractor, - IExtractorInvokeOptions, - ExtractorResult -} from './api/Extractor'; +export { Extractor, IExtractorInvokeOptions, ExtractorResult } from './api/Extractor'; -export { - IExtractorConfigPrepareOptions, - ExtractorConfig -} from './api/ExtractorConfig'; +export { IExtractorConfigPrepareOptions, ExtractorConfig } from './api/ExtractorConfig'; export { ExtractorLogLevel } from './api/ExtractorLogLevel'; diff --git a/src/schemas/api-extractor-defaults.json b/src/schemas/api-extractor-defaults.json index d732795edbf..22358fe1b34 100644 --- a/src/schemas/api-extractor-defaults.json +++ b/src/schemas/api-extractor-defaults.json @@ -3,7 +3,7 @@ // ("mainEntryPointFilePath" is required) - "bundledPackages": [ ], + "bundledPackages": [], "newlineKind": "crlf", diff --git a/src/schemas/api-extractor-template.json b/src/schemas/api-extractor-template.json index edd97d910bb..e47c7f6d7d7 100644 --- a/src/schemas/api-extractor-template.json +++ b/src/schemas/api-extractor-template.json @@ -60,7 +60,7 @@ * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been * local files for library1. */ - "bundledPackages": [ ], + "bundledPackages": [], /** * Determines how the TypeScript compiler engine will be invoked by API Extractor. @@ -78,7 +78,6 @@ * DEFAULT VALUE: "/tsconfig.json" */ // "tsconfigFilePath": "/tsconfig.json", - /** * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. * The object must conform to the TypeScript tsconfig schema: @@ -92,7 +91,6 @@ // "overrideTsconfig": { // . . . // } - /** * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when @@ -111,7 +109,7 @@ /** * (REQUIRED) Whether to generate an API report. */ - "enabled": true, + "enabled": true /** * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce @@ -162,7 +160,7 @@ /** * (REQUIRED) Whether to generate a doc model file. */ - "enabled": true, + "enabled": true /** * The output path for the doc model file. The file extension should be ".api.json". @@ -183,7 +181,7 @@ /** * (REQUIRED) Whether to generate the .d.ts rollup file. */ - "enabled": true, + "enabled": true /** * Specifies the output path for a .d.ts rollup file to be generated without any trimming. @@ -211,7 +209,6 @@ */ // "betaTrimmedFilePath": "/dist/-beta.d.ts", - /** * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. * This file will include only declarations that are marked as "@public". @@ -246,7 +243,6 @@ * DEFAULT VALUE: true */ // "enabled": true, - /** * Specifies where the TSDoc metadata file should be written. * @@ -303,7 +299,7 @@ * * DEFAULT VALUE: "warning" */ - "logLevel": "warning", + "logLevel": "warning" /** * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), @@ -313,7 +309,7 @@ * DEFAULT VALUE: false */ // "addToApiReportFile": false - }, + } // "TS2551": { // "logLevel": "warning", @@ -332,9 +328,9 @@ */ "extractorMessageReporting": { "default": { - "logLevel": "warning", + "logLevel": "warning" // "addToApiReportFile": false - }, + } // "ae-extra-release-tag": { // "logLevel": "warning", @@ -353,7 +349,7 @@ */ "tsdocMessageReporting": { "default": { - "logLevel": "warning", + "logLevel": "warning" // "addToApiReportFile": false } @@ -365,5 +361,4 @@ // . . . } } - } diff --git a/src/schemas/api-extractor.schema.json b/src/schemas/api-extractor.schema.json index 153b3739000..3bf3914a9c5 100644 --- a/src/schemas/api-extractor.schema.json +++ b/src/schemas/api-extractor.schema.json @@ -74,9 +74,8 @@ "description": "Specifies the folder where the temporary report file is written. The file name portion is determined by the \"reportFileName\" setting. After the temporary file is written to disk, it is compared with the file in the \"reportFolder\". If they are different, a production build will fail. The path is resolved relative to the folder of the config file that contains the setting; to change this, prepend a folder token such as \"\".", "type": "string" } - }, - "required": [ "enabled" ], + "required": ["enabled"], "additionalProperties": false }, @@ -93,7 +92,7 @@ "type": "string" } }, - "required": [ "enabled" ], + "required": ["enabled"], "additionalProperties": false }, @@ -122,7 +121,7 @@ "type": "boolean" } }, - "required": [ "enabled" ], + "required": ["enabled"], "additionalProperties": false }, @@ -174,12 +173,12 @@ "type": "boolean" } }, - "required": [ "mainEntryPointFilePath" ], + "required": ["mainEntryPointFilePath"], "additionalProperties": false, "definitions": { "extractorMessageReportingTable": { - "type":"object", + "type": "object", "description": "Specifies a table of reporting rules for different message identifiers, and also the default rule used for identifiers that do not appear in the table. The key is a message identifier for the associated type of message, or \"default\" to specify the default policy. For example, the key might be \"TS2551\" (a compiler message), \"tsdoc-link-tag-unescaped-text\" (a TSDOc message), or \"ae-extra-release-tag\" (a message related to the API Extractor analysis).", "patternProperties": { ".+": { @@ -189,7 +188,7 @@ "logLevel": { "type": "string", "description": "Specifies whether the message should be written to the the tool's output log. Note that the \"addToApiReportFile\" property may supersede this option.", - "enum": [ "error", "warning", "none" ] + "enum": ["error", "warning", "none"] }, "addToApiReportFile": { "type": "boolean", @@ -197,7 +196,7 @@ } }, "additionalProperties": false, - "required": [ "logLevel" ] + "required": ["logLevel"] } }, "additionalProperties": false diff --git a/src/start.ts b/src/start.ts index 8fce9a0b64a..4cb57170a6e 100644 --- a/src/start.ts +++ b/src/start.ts @@ -7,8 +7,10 @@ import * as colors from 'colors'; import { ApiExtractorCommandLine } from './cli/ApiExtractorCommandLine'; import { Extractor } from './api/Extractor'; -console.log(os.EOL + colors.bold(`api-extractor ${Extractor.version} ` - + colors.cyan(' - https://api-extractor.com/') + os.EOL)); +console.log( + os.EOL + + colors.bold(`api-extractor ${Extractor.version} ` + colors.cyan(' - https://api-extractor.com/') + os.EOL) +); const parser: ApiExtractorCommandLine = new ApiExtractorCommandLine(); diff --git a/tsconfig.json b/tsconfig.json index 824a88b71e5..46bc07fdda9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,9 +2,6 @@ "extends": "./node_modules/@microsoft/rush-stack-compiler-3.5/includes/tsconfig-node.json", "compilerOptions": { - "types": [ - "jest", - "node" - ] + "types": ["jest", "node"] } }