Skip to content

Commit

Permalink
Merge pull request #1337 from rbuckton/deeplyLinkedtypes
Browse files Browse the repository at this point in the history
[api-extractor] Deeply linked types
  • Loading branch information
octogonz authored Sep 10, 2019
2 parents 64edc7d + d395618 commit e792300
Show file tree
Hide file tree
Showing 58 changed files with 1,809 additions and 1,565 deletions.
320 changes: 205 additions & 115 deletions apps/api-documenter/src/documenters/YamlDocumenter.ts

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions apps/api-documenter/src/yaml/IYamlApiFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,19 @@ export interface IYamlParameter {
* declared in a separate YAML file.
*/
export interface IYamlReference {
uid?: string;
name?: string;
fullName?: string;
'spec.typeScript'?: IYamlReferenceSpec[];
}

/**
* Part of the IYamlApiFile structure. Represents a text specification for a reference.
*/
export interface IYamlReferenceSpec {
uid?: string;
name?: string;
fullName?: string;
}

/**
Expand Down
24 changes: 24 additions & 0 deletions apps/api-documenter/src/yaml/typescript.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,35 @@
"type": "object",
"additionalProperties": false,
"properties": {
"uid": {
"type": "string"
},
"name": {
"type": "string"
},
"fullName": {
"type": "string"
},
"spec.typeScript": {
"type": "array",
"items": {
"$ref": "#/definitions/spec"
}
}
}
},
"spec": {
"type": "object",
"additionalProperties": false,
"properties": {
"uid": {
"type": "string"
},
"name": {
"type": "string"
},
"fullName": {
"type": "string"
}
}
},
Expand Down
15 changes: 13 additions & 2 deletions apps/api-extractor-model/src/items/ApiDeclaredItem.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.s

import { DeclarationReference } from '@microsoft/tsdoc/lib/beta/DeclarationReference';
import { ApiDocumentedItem, IApiDocumentedItemJson, IApiDocumentedItemOptions } from './ApiDocumentedItem';
import { Excerpt, ExcerptToken, IExcerptTokenRange, IExcerptToken } from '../mixins/Excerpt';
import { DeserializerContext } from '../model/DeserializerContext';
Expand Down Expand Up @@ -47,7 +48,11 @@ export class ApiDeclaredItem extends ApiDocumentedItem {
public constructor(options: IApiDeclaredItemOptions) {
super(options);

this._excerptTokens = options.excerptTokens.map(x => new ExcerptToken(x.kind, x.text));
this._excerptTokens = options.excerptTokens.map(x => {
const canonicalReference: DeclarationReference | undefined = x.canonicalReference === undefined ? undefined :
DeclarationReference.parse(x.canonicalReference);
return new ExcerptToken(x.kind, x.text, canonicalReference);
});
this._excerpt = new Excerpt(this.excerptTokens, { startIndex: 0, endIndex: this.excerptTokens.length });
}

Expand Down Expand Up @@ -99,7 +104,13 @@ export class ApiDeclaredItem extends ApiDocumentedItem {
/** @override */
public serializeInto(jsonObject: Partial<IApiDeclaredItemJson>): void {
super.serializeInto(jsonObject);
jsonObject.excerptTokens = this.excerptTokens.map(x => ({ kind: x.kind, text: x.text }));
jsonObject.excerptTokens = this.excerptTokens.map(x => {
const excerptToken: IExcerptToken = { kind: x.kind, text: x.text };
if (x.canonicalReference !== undefined) {
excerptToken.canonicalReference = x.canonicalReference.toString();
}
return excerptToken;
});
}

/**
Expand Down
10 changes: 9 additions & 1 deletion apps/api-extractor-model/src/mixins/Excerpt.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { DeclarationReference } from '@microsoft/tsdoc/lib/beta/DeclarationReference';
import { Text } from '@microsoft/node-core-library';

/** @public */
Expand All @@ -21,20 +22,23 @@ export interface IExcerptTokenRange {
export interface IExcerptToken {
readonly kind: ExcerptTokenKind;
text: string;
canonicalReference?: string;
}

/** @public */
export class ExcerptToken {
private readonly _kind: ExcerptTokenKind;
private readonly _text: string;
private readonly _canonicalReference: DeclarationReference | undefined;

public constructor(kind: ExcerptTokenKind, text: string) {
public constructor(kind: ExcerptTokenKind, text: string, canonicalReference?: DeclarationReference) {
this._kind = kind;

// Standardize the newlines across operating systems. Even though this may deviate from the actual
// input source file that was parsed, it's useful because the newline gets serialized inside
// a string literal in .api.json, which cannot be automatically normalized by Git.
this._text = Text.convertToLf(text);
this._canonicalReference = canonicalReference;
}

public get kind(): ExcerptTokenKind {
Expand All @@ -44,6 +48,10 @@ export class ExcerptToken {
public get text(): string {
return this._text;
}

public get canonicalReference(): DeclarationReference | undefined {
return this._canonicalReference;
}
}

/**
Expand Down
8 changes: 3 additions & 5 deletions apps/api-extractor/src/analyzer/AstSymbolTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,19 +227,17 @@ export class AstSymbolTable {
* ```
*/
public static getLocalNameForSymbol(symbol: ts.Symbol): string {
const symbolName: string = symbol.name;

// TypeScript binds well-known ECMAScript symbols like "[Symbol.iterator]" as "__@iterator".
// Decode it back into "[Symbol.iterator]".
const wellKnownSymbolName: string | undefined = TypeScriptHelpers.tryDecodeWellKnownSymbolName(symbolName);
const wellKnownSymbolName: string | undefined = TypeScriptHelpers.tryDecodeWellKnownSymbolName(symbol.escapedName);
if (wellKnownSymbolName) {
return wellKnownSymbolName;
}

const isUniqueSymbol: boolean = TypeScriptHelpers.isUniqueSymbolName(symbolName);
const isUniqueSymbol: boolean = TypeScriptHelpers.isUniqueSymbolName(symbol.escapedName);

// We will try to obtain the name from a declaration; otherwise we'll fall back to the symbol name.
let unquotedName: string = symbolName;
let unquotedName: string = symbol.name;

for (const declaration of symbol.declarations || []) {
// Handle cases such as "export default class X { }" where the symbol name is "default"
Expand Down
8 changes: 4 additions & 4 deletions apps/api-extractor/src/analyzer/TypeScriptHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ export class TypeScriptHelpers {
* If `name` is of this form, then `tryGetWellKnownSymbolName()` converts it back into e.g. `[Symbol.iterator]`.
* If the string does not start with `__@` then `undefined` is returned.
*/
public static tryDecodeWellKnownSymbolName(name: string): string | undefined {
const match: RegExpExecArray | null = TypeScriptHelpers._wellKnownSymbolNameRegExp.exec(name);
public static tryDecodeWellKnownSymbolName(name: ts.__String): string | undefined {
const match: RegExpExecArray | null = TypeScriptHelpers._wellKnownSymbolNameRegExp.exec(name as string);
if (match) {
const identifier: string = match[1];
return `[Symbol.${identifier}]`;
Expand All @@ -238,8 +238,8 @@ export class TypeScriptHelpers {
/**
* Returns whether the provided name was generated for a TypeScript `unique symbol`.
*/
public static isUniqueSymbolName(name: string): boolean {
return TypeScriptHelpers._uniqueSymbolNameRegExp.test(name);
public static isUniqueSymbolName(name: ts.__String): boolean {
return TypeScriptHelpers._uniqueSymbolNameRegExp.test(name as string);
}

/**
Expand Down
23 changes: 23 additions & 0 deletions apps/api-extractor/src/generators/ApiModelGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,23 @@ import { Collector } from '../collector/Collector';
import { AstDeclaration } from '../analyzer/AstDeclaration';
import { ExcerptBuilder, IExcerptBuilderNodeToCapture } from './ExcerptBuilder';
import { AstSymbol } from '../analyzer/AstSymbol';
import { DeclarationReferenceGenerator } from './DeclarationReferenceGenerator';

export class ApiModelGenerator {
private readonly _collector: Collector;
private readonly _cachedOverloadIndexesByDeclaration: Map<AstDeclaration, number>;
private readonly _apiModel: ApiModel;
private readonly _referenceGenerator: DeclarationReferenceGenerator;

public constructor(collector: Collector) {
this._collector = collector;
this._cachedOverloadIndexesByDeclaration = new Map<AstDeclaration, number>();
this._apiModel = new ApiModel();
this._referenceGenerator = new DeclarationReferenceGenerator(
collector.packageJsonLookup,
collector.workingPackage.name,
collector.program,
collector.typeChecker);
}

public get apiModel(): ApiModel {
Expand Down Expand Up @@ -195,6 +202,7 @@ export class ApiModelGenerator {
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, callSignature.parameters);

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
nodesToCapture
});
Expand Down Expand Up @@ -225,6 +233,7 @@ export class ApiModelGenerator {
constructorDeclaration.parameters);

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
nodesToCapture
});
Expand Down Expand Up @@ -274,6 +283,7 @@ export class ApiModelGenerator {
}

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
stopBeforeChildKind: ts.SyntaxKind.FirstPunctuation, // FirstPunctuation = "{"
nodesToCapture
Expand Down Expand Up @@ -314,6 +324,7 @@ export class ApiModelGenerator {
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, constructSignature.parameters);

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
nodesToCapture
});
Expand All @@ -337,6 +348,7 @@ export class ApiModelGenerator {

if (apiEnum === undefined) {
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
stopBeforeChildKind: ts.SyntaxKind.FirstPunctuation // FirstPunctuation = "{"
});
Expand Down Expand Up @@ -368,6 +380,7 @@ export class ApiModelGenerator {
nodesToCapture.push({ node: enumMember.initializer, tokenRange: initializerTokenRange });

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
nodesToCapture
});
Expand Down Expand Up @@ -408,6 +421,7 @@ export class ApiModelGenerator {
functionDeclaration.parameters);

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
nodesToCapture
});
Expand Down Expand Up @@ -441,6 +455,7 @@ export class ApiModelGenerator {
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, indexSignature.parameters);

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
nodesToCapture
});
Expand Down Expand Up @@ -483,6 +498,7 @@ export class ApiModelGenerator {
}

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
stopBeforeChildKind: ts.SyntaxKind.FirstPunctuation, // FirstPunctuation = "{"
nodesToCapture
Expand Down Expand Up @@ -525,6 +541,7 @@ export class ApiModelGenerator {
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, methodDeclaration.parameters);

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
nodesToCapture
});
Expand Down Expand Up @@ -564,6 +581,7 @@ export class ApiModelGenerator {
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, methodSignature.parameters);

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
nodesToCapture
});
Expand All @@ -587,6 +605,7 @@ export class ApiModelGenerator {

if (apiNamespace === undefined) {
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
stopBeforeChildKind: ts.SyntaxKind.ModuleBlock // ModuleBlock = the "{ ... }" block
});
Expand Down Expand Up @@ -622,6 +641,7 @@ export class ApiModelGenerator {
nodesToCapture.push({ node: propertyDeclaration.type, tokenRange: propertyTypeTokenRange });

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
nodesToCapture
});
Expand Down Expand Up @@ -654,6 +674,7 @@ export class ApiModelGenerator {
nodesToCapture.push({ node: propertySignature.type, tokenRange: propertyTypeTokenRange });

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
nodesToCapture
});
Expand Down Expand Up @@ -691,6 +712,7 @@ export class ApiModelGenerator {
nodesToCapture.push({ node: typeAliasDeclaration.type, tokenRange: typeTokenRange });

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
nodesToCapture
});
Expand Down Expand Up @@ -723,6 +745,7 @@ export class ApiModelGenerator {
nodesToCapture.push({ node: variableDeclaration.type, tokenRange: variableTypeTokenRange });

const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
referenceGenerator: this._referenceGenerator,
startingNode: astDeclaration.declaration,
nodesToCapture
});
Expand Down
Loading

0 comments on commit e792300

Please sign in to comment.