From 726cb6b91aee76493d1483a17f9cf6dec1074f18 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Mon, 29 Apr 2019 21:58:53 -0700 Subject: [PATCH 1/2] Fix issue #1249, where the ExcerptBuilder truncated e.g. "interface A extends B<{c: C}> { }" to "interface A extends B<{" because it stopped at the first "{" encountered, even if it was not an immediate child in the AST --- .../src/generators/ApiModelGenerator.ts | 8 ++--- .../src/generators/ExcerptBuilder.ts | 32 ++++++++++++------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/apps/api-extractor/src/generators/ApiModelGenerator.ts b/apps/api-extractor/src/generators/ApiModelGenerator.ts index 28b936ff3e1..998872933a9 100644 --- a/apps/api-extractor/src/generators/ApiModelGenerator.ts +++ b/apps/api-extractor/src/generators/ApiModelGenerator.ts @@ -268,7 +268,7 @@ export class ApiModelGenerator { const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({ startingNode: astDeclaration.declaration, - nodeToStopAt: ts.SyntaxKind.FirstPunctuation, // FirstPunctuation = "{" + stopBeforeChildKind: ts.SyntaxKind.FirstPunctuation, // FirstPunctuation = "{" nodesToCapture }); const docComment: tsdoc.DocComment | undefined = this._collector.fetchMetadata(astDeclaration).tsdocComment; @@ -328,7 +328,7 @@ export class ApiModelGenerator { if (apiEnum === undefined) { const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({ startingNode: astDeclaration.declaration, - nodeToStopAt: ts.SyntaxKind.FirstPunctuation // FirstPunctuation = "{" + stopBeforeChildKind: ts.SyntaxKind.FirstPunctuation // FirstPunctuation = "{" }); const docComment: tsdoc.DocComment | undefined = this._collector.fetchMetadata(astDeclaration).tsdocComment; @@ -467,7 +467,7 @@ export class ApiModelGenerator { const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({ startingNode: astDeclaration.declaration, - nodeToStopAt: ts.SyntaxKind.FirstPunctuation, // FirstPunctuation = "{" + stopBeforeChildKind: ts.SyntaxKind.FirstPunctuation, // FirstPunctuation = "{" nodesToCapture }); @@ -563,7 +563,7 @@ export class ApiModelGenerator { if (apiNamespace === undefined) { const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({ startingNode: astDeclaration.declaration, - nodeToStopAt: ts.SyntaxKind.ModuleBlock // ModuleBlock = the "{ ... }" block + stopBeforeChildKind: ts.SyntaxKind.ModuleBlock // ModuleBlock = the "{ ... }" block }); const docComment: tsdoc.DocComment | undefined = this._collector.fetchMetadata(astDeclaration).tsdocComment; diff --git a/apps/api-extractor/src/generators/ExcerptBuilder.ts b/apps/api-extractor/src/generators/ExcerptBuilder.ts index fff7d22b059..de97f8b6077 100644 --- a/apps/api-extractor/src/generators/ExcerptBuilder.ts +++ b/apps/api-extractor/src/generators/ExcerptBuilder.ts @@ -32,13 +32,18 @@ export interface ISignatureBuilderOptions { /** * The AST node that we will traverse to extract tokens */ - startingNode: ts.Node; + /** - * An AST node to stop at (e.g. the "{" after a class declaration). - * If omitted, then all child nodes for `startingNode` will be processed + * Normally, the excerpt will include all child nodes for `startingNode`; whereas if `childKindToStopBefore` + * is specified, then the node traversal will stop before (i.e. excluding) the first immediate child + * of `startingNode` with the specified syntax kind. + * + * @remarks + * For example, suppose the signature is `interface X: Y { z: string }`. The token `{` has syntax kind + * `ts.SyntaxKind.FirstPunctuation`, so we can specify that to truncate the excerpt to `interface X: Y`. */ - nodeToStopAt?: ts.SyntaxKind; + stopBeforeChildKind?: ts.SyntaxKind; /** * A list of child nodes whose token ranges we want to capture @@ -50,7 +55,9 @@ export interface ISignatureBuilderOptions { * Internal state for ExcerptBuilder */ interface IBuildSpanState { - nodeToStopAt?: ts.SyntaxKind; + startingNode: ts.Node; + stopBeforeChildKind: ts.SyntaxKind | undefined; + tokenRangesByNode: Map; /** @@ -75,7 +82,8 @@ export class ExcerptBuilder { const excerptTokens: IExcerptToken[] = []; ExcerptBuilder._buildSpan(excerptTokens, span, { - nodeToStopAt: options.nodeToStopAt, + startingNode: options.startingNode, + stopBeforeChildKind: options.stopBeforeChildKind, tokenRangesByNode, disableMergingForNextToken: false }); @@ -88,11 +96,6 @@ export class ExcerptBuilder { } private static _buildSpan(excerptTokens: IExcerptToken[], span: Span, state: IBuildSpanState): boolean { - - if (state.nodeToStopAt && span.kind === state.nodeToStopAt) { - return false; - } - if (span.kind === ts.SyntaxKind.JSDocComment) { // Discard any comments return true; @@ -119,6 +122,13 @@ export class ExcerptBuilder { } for (const child of span.children) { + if (span.node === state.startingNode) { + if (state.stopBeforeChildKind && child.kind === state.stopBeforeChildKind) { + // We reached the a child whose kind is stopBeforeChildKind, so stop traversing + return false; + } + } + if (!this._buildSpan(excerptTokens, child, state)) { return false; } From 669f157b959ae6f9f3b1291691e4a6b283ed3423 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Mon, 29 Apr 2019 22:01:09 -0700 Subject: [PATCH 2/2] rush change --- .../octogonz-ae-1249_2019-04-30-05-00.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@microsoft/api-extractor/octogonz-ae-1249_2019-04-30-05-00.json diff --git a/common/changes/@microsoft/api-extractor/octogonz-ae-1249_2019-04-30-05-00.json b/common/changes/@microsoft/api-extractor/octogonz-ae-1249_2019-04-30-05-00.json new file mode 100644 index 00000000000..998e0c9b87e --- /dev/null +++ b/common/changes/@microsoft/api-extractor/octogonz-ae-1249_2019-04-30-05-00.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@microsoft/api-extractor", + "comment": "Fix an issue where API signatures were sometimes truncated in the .api.json file (GitHub #1249)", + "type": "patch" + } + ], + "packageName": "@microsoft/api-extractor", + "email": "4673363+octogonz@users.noreply.github.com" +} \ No newline at end of file