Skip to content

Commit

Permalink
Merge pull request microsoft#3427 from mrshllstock/api-readonly-mixin
Browse files Browse the repository at this point in the history
[api-extractor] Add support for showing whether properties are readonly
  • Loading branch information
octogonz authored Jun 7, 2022
2 parents fd89af2 + e0dfbb2 commit 3f61774
Show file tree
Hide file tree
Showing 47 changed files with 946 additions and 128 deletions.
8 changes: 8 additions & 0 deletions apps/api-extractor/src/analyzer/TypeScriptInternals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,12 @@ export class TypeScriptInternals {
}
return resolver;
}

/**
* Returns whether a variable is declared with the const keyword
*/
public static isVarConst(node: ts.VariableDeclaration | ts.VariableDeclarationList): boolean {
// Compiler internal: https://github.com/microsoft/TypeScript/blob/71286e3d49c10e0e99faac360a6bbd40f12db7b6/src/compiler/utilities.ts#L925
return (ts as any).isVarConst(node);
}
}
35 changes: 33 additions & 2 deletions apps/api-extractor/src/generators/ApiModelGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { DeclarationMetadata } from '../collector/DeclarationMetadata';
import { AstNamespaceImport } from '../analyzer/AstNamespaceImport';
import { AstEntity } from '../analyzer/AstEntity';
import { AstModule } from '../analyzer/AstModule';
import { TypeScriptInternals } from '../analyzer/TypeScriptInternals';

export class ApiModelGenerator {
private readonly _collector: Collector;
Expand Down Expand Up @@ -851,6 +852,7 @@ export class ApiModelGenerator {
const isOptional: boolean =
(astDeclaration.astSymbol.followedSymbol.flags & ts.SymbolFlags.Optional) !== 0;
const isProtected: boolean = (astDeclaration.modifierFlags & ts.ModifierFlags.Protected) !== 0;
const isReadonly: boolean = this._determineReadonly(astDeclaration);

apiProperty = new ApiProperty({
name,
Expand All @@ -859,6 +861,7 @@ export class ApiModelGenerator {
isProtected,
isStatic,
isOptional,
isReadonly,
excerptTokens,
propertyTypeTokenRange
});
Expand Down Expand Up @@ -895,14 +898,16 @@ export class ApiModelGenerator {
const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag;
const isOptional: boolean =
(astDeclaration.astSymbol.followedSymbol.flags & ts.SymbolFlags.Optional) !== 0;
const isReadonly: boolean = this._determineReadonly(astDeclaration);

apiPropertySignature = new ApiPropertySignature({
name,
docComment,
releaseTag,
isOptional,
excerptTokens,
propertyTypeTokenRange
propertyTypeTokenRange,
isReadonly
});

parentApiItem.addMember(apiPropertySignature);
Expand Down Expand Up @@ -981,8 +986,16 @@ export class ApiModelGenerator {
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment;
const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag;
const isReadonly: boolean = this._determineReadonly(astDeclaration);

apiVariable = new ApiVariable({ name, docComment, releaseTag, excerptTokens, variableTypeTokenRange });
apiVariable = new ApiVariable({
name,
docComment,
releaseTag,
excerptTokens,
variableTypeTokenRange,
isReadonly
});

parentApiItem.addMember(apiVariable);
}
Expand Down Expand Up @@ -1055,4 +1068,22 @@ export class ApiModelGenerator {
}
return parameters;
}

private _determineReadonly(astDeclaration: AstDeclaration): boolean {
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment;
const declarationMetadata: DeclarationMetadata = this._collector.fetchDeclarationMetadata(astDeclaration);
//Line 1: sees whether the readonly or const modifiers are present
//Line 2: sees if the TSDoc comment for @readonly is present
//Line 3: sees whether a getter is present for a property with no setter
//Line 4: sees if the var declaration has Const keyword
return (
(astDeclaration.modifierFlags & (ts.ModifierFlags.Readonly + ts.ModifierFlags.Const)) !== 0 ||
(docComment !== undefined && docComment.modifierTagSet.hasTagName('@readonly')) ||
(declarationMetadata.ancillaryDeclarations.length === 0 &&
astDeclaration.declaration.kind === ts.SyntaxKind.GetAccessor) ||
(ts.isVariableDeclaration(astDeclaration.declaration) &&
TypeScriptInternals.isVarConst(astDeclaration.declaration))
);
}
}
Loading

0 comments on commit 3f61774

Please sign in to comment.