Repository navigation
[api-extractor] Add a "--diagnostics" command-line option #1359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0f4b5c1
08e0424
c94da5f
3839e1e
10aec7b
0675833
72798d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,8 +30,18 @@ interface IReportingRule { | |
| addToApiReportFile: boolean; | ||
| } | ||
|
|
||
| export interface IMessageRouterOptions { | ||
| workingPackageFolder: string | undefined; | ||
| messageCallback: ((message: ExtractorMessage) => void) | undefined; | ||
| messagesConfig: IExtractorMessagesConfig; | ||
| showVerboseMessages: boolean; | ||
| showDiagnostics: boolean; | ||
| } | ||
|
|
||
| export class MessageRouter { | ||
| private readonly _workingPackageFolder: string; | ||
| public static readonly DIAGNOSTICS_LINE: string = '============================================================'; | ||
|
|
||
| private readonly _workingPackageFolder: string | undefined; | ||
| private readonly _messageCallback: ((message: ExtractorMessage) => void) | undefined; | ||
|
|
||
| // All messages | ||
|
|
@@ -53,20 +63,30 @@ export class MessageRouter { | |
|
|
||
| public errorCount: number = 0; | ||
| public warningCount: number = 0; | ||
| public showVerboseMessages: boolean = false; | ||
|
|
||
| public constructor(workingPackageFolder: string, | ||
| messageCallback: ((message: ExtractorMessage) => void) | undefined, | ||
| messagesConfig: IExtractorMessagesConfig) { | ||
| /** | ||
| * See {@link IExtractorInvokeOptions.showVerboseMessages} | ||
| */ | ||
| public readonly showVerboseMessages: boolean; | ||
|
|
||
| /** | ||
| * See {@link IExtractorInvokeOptions.showDiagnostics} | ||
| */ | ||
| public readonly showDiagnostics: boolean; | ||
|
|
||
| this._workingPackageFolder = workingPackageFolder; | ||
| this._messageCallback = messageCallback; | ||
| public constructor(options: IMessageRouterOptions) { | ||
| this._workingPackageFolder = options.workingPackageFolder; | ||
| this._messageCallback = options.messageCallback; | ||
|
|
||
| this._messages = []; | ||
| this._associatedMessagesForAstDeclaration = new Map<AstDeclaration, ExtractorMessage[]>(); | ||
| this._sourceMapper = new SourceMapper(); | ||
|
|
||
| this._applyMessagesConfig(messagesConfig); | ||
| // showDiagnostics implies showVerboseMessages | ||
| this.showVerboseMessages = options.showVerboseMessages || options.showDiagnostics; | ||
| this.showDiagnostics = options.showDiagnostics; | ||
|
|
||
| this._applyMessagesConfig(options.messagesConfig); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -222,6 +242,57 @@ export class MessageRouter { | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Recursively collects the primitive members (numbers, strings, arrays, etc) into an object that | ||
| * is JSON serializable. This is used by the "--diagnostics" feature to dump the state of configuration objects. | ||
| * | ||
| * @returns a JSON serializable object (possibly including `null` values) | ||
| * or `undefined` if the input cannot be represented as JSON | ||
| */ | ||
| // tslint:disable-next-line:no-any | ||
| public static buildJsonDumpObject(input: any): any | undefined { | ||
| if (input === null || input === undefined) { | ||
| // tslint:disable-next-line:no-null-keyword | ||
| return null; // JSON uses null instead of undefined | ||
| } | ||
|
|
||
| switch (typeof input) { | ||
| case 'boolean': | ||
| case 'number': | ||
| case 'string': | ||
| return input; | ||
| case 'object': | ||
| if (Array.isArray(input)) { | ||
| // tslint:disable-next-line:no-any | ||
| const outputArray: any[] = []; | ||
| for (const element of input) { | ||
| // tslint:disable-next-line:no-any | ||
| const serializedElement: any = MessageRouter.buildJsonDumpObject(element); | ||
| if (serializedElement !== undefined) { | ||
| outputArray.push(serializedElement); | ||
| } | ||
| } | ||
| return outputArray; | ||
| } | ||
|
|
||
| const outputObject: object = { }; | ||
| for (const key of Object.getOwnPropertyNames(input)) { | ||
| // tslint:disable-next-line:no-any | ||
| const value: any = input[key]; | ||
|
|
||
| // tslint:disable-next-line:no-any | ||
| const serializedValue: any = MessageRouter.buildJsonDumpObject(value); | ||
|
|
||
| if (serializedValue !== undefined) { | ||
| outputObject[key] = serializedValue; | ||
| } | ||
| } | ||
| return outputObject; | ||
| } | ||
|
|
||
| return undefined; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you want to return something more useful if the parameter is a function. Maybe
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really. The idea is that this function crawls through an object and picks out the printable stuff, ignoring any other members. Ideally I would have just used |
||
| } | ||
|
|
||
| /** | ||
| * Record this message in _associatedMessagesForAstDeclaration | ||
| */ | ||
|
|
@@ -384,6 +455,20 @@ export class MessageRouter { | |
| })); | ||
| } | ||
|
|
||
| public logDiagnosticHeader(title: string): void { | ||
| this.logDiagnostic(MessageRouter.DIAGNOSTICS_LINE); | ||
| this.logDiagnostic(`DIAGNOSTIC: ` + title); | ||
| this.logDiagnostic(MessageRouter.DIAGNOSTICS_LINE); | ||
| } | ||
|
|
||
| public logDiagnosticFooter(): void { | ||
| this.logDiagnostic(MessageRouter.DIAGNOSTICS_LINE + '\n'); | ||
| } | ||
|
|
||
| public logDiagnostic(message: string): void { | ||
| this.logVerbose(ConsoleMessageId.Diagnostics, message); | ||
| } | ||
|
|
||
| /** | ||
| * Give the calling application a chance to handle the `ExtractorMessage`, and if not, display it on the console. | ||
| */ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "changes": [ | ||
| { | ||
| "packageName": "@microsoft/api-extractor", | ||
| "comment": "Add a \"--diagnostics\" command-line option to help when troubleshooting problems", | ||
| "type": "minor" | ||
| } | ||
| ], | ||
| "packageName": "@microsoft/api-extractor", | ||
| "email": "4673363+octogonz@users.noreply.github.com" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be useful to have some unit tests for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I considered promoting this to a real utility in node-core-library, in which case I definitely would have added tests.
However, the concept of
buildJsonDumpObject()seems lazy and half-baked. A "correct" implementation would useJSON.serialize()for actual data objects, or for other objects we should implement a proper printer/serializer (that knows all the members and how to format them). In my example output, you can see that dumped options aren't very readable (e.g."target": 2).I took the lazy approach because I didn't want to spend too much time on this
--diagnosticsfeature initially. I know that we need some way to investigate API Extractor issues without resorting to a debugger. But this PR is merely a guess about what logging is actually useful.