Skip to content

Commit

Permalink
Merge pull request #130 from webdoc-labs/feature/lang-ts-parameter-pr…
Browse files Browse the repository at this point in the history
…operty

Add support for TypeScript parameter properties
  • Loading branch information
ShukantPal committed Jun 6, 2021
2 parents e8c8c23 + 458b877 commit 640ee26
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 10 deletions.
6 changes: 6 additions & 0 deletions example/src/Car.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ export class Car extends PhysicalObject {
*/
static DEFAULT_DRIVE_SHIFT = "manual"

/**
*/
constructor(public model: string = "TEST") {
super();
}

/**
* @member {number}
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/webdoc-parser/.flowconfig
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[ignore]
./lib

[include]
./src

[libs]

Expand Down
20 changes: 18 additions & 2 deletions packages/webdoc-parser/src/symbols-babel/build-symbol-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
isObjectPattern,
isRestElement,
isScope,
isTSParameterProperty,
isVariableDeclaration,
} from "@babel/types";
import {
Expand Down Expand Up @@ -295,7 +296,14 @@ function captureSymbols(nodePath: NodePath, parent: Symbol): ?Symbol {

// leadingComments -> documented
// isScope -> children may be documented even if node is not
if (leadingComments.length || isScope(node) || reportUndocumented) {
// TSParameterProperty -> They don't have doc-comments but the @param tag for the parameter
// covers it so we always include them.
if (
leadingComments.length ||
isScope(node) ||
isTSParameterProperty(node) ||
reportUndocumented
) {
if (!initComment && leadingComments.length && simpleName) {
nodeDocIndex = SymbolUtils.commentIndex(leadingComments);
}
Expand All @@ -304,6 +312,10 @@ function captureSymbols(nodePath: NodePath, parent: Symbol): ?Symbol {

if (!comment) {
nodeSymbol.meta.undocumented = true;

if (isTSParameterProperty(node)) {
nodeSymbol.meta.undocumentedAnchored = true;
}
}

// Does user want to document as a property? Then remove VIRTUAL flag
Expand All @@ -320,7 +332,11 @@ function captureSymbols(nodePath: NodePath, parent: Symbol): ?Symbol {
path: [...parent.path, simpleName],
comment,
members: [],
loc: _createLoc(nodeDoc ? nodeDoc.loc : null),
loc: _createLoc(
nodeDoc ? nodeDoc.loc :
node.loc ? node.loc : // Needed for undocumented symbols
null,
),
}, nodeSymbol);

nodeSymbol = SymbolUtils.addChild(nodeSymbol, parent);
Expand Down
12 changes: 9 additions & 3 deletions packages/webdoc-parser/src/symbols-babel/extract-metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import {
isTSNullKeyword,
isTSNumberKeyword,
isTSObjectKeyword,
isTSParameterProperty,
isTSParenthesizedType,
isTSPropertySignature,
isTSQualifiedName,
Expand Down Expand Up @@ -191,10 +192,13 @@ export function extractParams(
const params: Param[] = [];

for (let i = 0; i < nodeParams.length; i++) {
const paramNode = nodeParams[i];

let paramNode = nodeParams[i];
let param: ?$Shape<Param>;

if (isTSParameterProperty(paramNode)) {
paramNode = paramNode.parameter;
}

// TODO: Infer types
if (isIdentifier(paramNode)) {
param = {
Expand All @@ -208,10 +212,12 @@ export function extractParams(
variadic: true,
};
} else if (isAssignmentPattern(paramNode)) {
const extraRaw = paramNode.right.extra && paramNode.right.extra.raw;

param = {
identifier: paramNode.left.name,
optional: paramNode.optional || false,
default: paramNode.right.raw,
default: paramNode.right.raw || extraRaw,
};
} else if (isObjectExpression(paramNode)) {
// TODO: Find a way to document {x, y, z} parameters
Expand Down
28 changes: 28 additions & 0 deletions packages/webdoc-parser/src/symbols-babel/extract-symbol.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
type VariableDeclarator,
isArrowFunctionExpression,
isAssignmentExpression,
isAssignmentPattern,
isBooleanLiteral,
isCallExpression,
isClassDeclaration,
Expand All @@ -23,6 +24,7 @@ import {
isExpressionStatement,
isFunctionDeclaration,
isFunctionExpression,
isIdentifier,
isInterfaceDeclaration,
isLiteral,
isMemberExpression,
Expand All @@ -39,6 +41,7 @@ import {
isTSEnumMember,
isTSInterfaceDeclaration,
isTSMethodSignature,
isTSParameterProperty,
isTSPropertySignature,
isTSTypeElement,
isThisExpression,
Expand Down Expand Up @@ -125,6 +128,31 @@ export default function extractSymbol(
nodeSymbol.meta.dataType = dataType;
}
}
} else if (isTSParameterProperty(node)) {
// Example:
// constructor(private readonly param: Type) {}

const parameter = node.parameter;

if (isIdentifier(parameter)) {
name = parameter.name;
} else if (isAssignmentPattern(parameter)) {
name = parameter.left.name;
nodeSymbol.meta.defaultValue = parameter.right.raw ||
(parameter.right.extra && parameter.right.extra.raw);
}

nodeSymbol.meta.access = node.accessibility;
nodeSymbol.meta.object = "this";// Assembly-mods will resolve to the parent class symbol
nodeSymbol.meta.readonly = node.readonly;
nodeSymbol.meta.scope = "instance";
nodeSymbol.meta.type = "PropertyDoc";

if (parameter.typeAnnotation) {
nodeSymbol.meta.dataType = extractType(parameter.typeAnnotation);
}

flags |= OBLIGATE_LEAF;
} else if (isClassDeclaration(node) || isClassExpression(node)) {
// Example:
// class ClassName {}
Expand Down
20 changes: 17 additions & 3 deletions packages/webdoc-parser/src/transformer/mod-prune.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,31 @@ import type {Doc, RootDoc} from "@webdoc/types";
import {removeChildDoc} from "@webdoc/model";

export default function prune(doc: Doc, root: RootDoc) {
let anchoredChildren = 0;

if (doc.tags && doc.tags.find((tag) => tag.name === "ignore")) {
removeChildDoc(doc);
return;
} else {
for (let i = 0; i < doc.members.length; i++) {
prune(doc.members[i], root);
const member = doc.members[i];

prune(member, root);

if (member.parserOpts && member.parserOpts.undocumentedAnchored) {
anchoredChildren++;
}
}
}

// Prune undocumented docs with no children too
if (doc.parserOpts && doc.parserOpts.undocumented && doc.members.length === 0) {
// Prune undocumented docs with no children too OR only anchored children.
// (anchored children _can_ be pruned but are not if any ancestor is retained)
if (
doc.parserOpts &&
doc.parserOpts.undocumented &&
!doc.parserOpts.undocumentedAnchored &&
doc.members.length === anchoredChildren
) {
removeChildDoc(doc);
}
if (doc.type !== "PropertyDoc" && doc.type !== "MethodDoc" && doc.type !== "FunctionDoc") {
Expand Down
1 change: 1 addition & 0 deletions packages/webdoc-parser/src/transformer/symbol-to-doc.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export default function symbolToDoc(symbol: Symbol): ?Doc {
options.parserOpts = {
object: symbol.meta.object,
undocumented: symbol.meta.undocumented,
undocumentedAnchored: symbol.meta.undocumentedAnchored,
};

const tags: Tag[] = [];
Expand Down
3 changes: 2 additions & 1 deletion packages/webdoc-parser/src/types/Symbol.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export type SymbolSignature = {
value?: ?string,
type?: ?DocType,
typeParameters?: ?Array<string>,
undocumented?: boolean
undocumented?: boolean,
undocumentedAnchored?: boolean, // Prevent pruning of symbol if parent is retained
}

// The location of a symbol
Expand Down
52 changes: 52 additions & 0 deletions packages/webdoc-parser/test/parse-ts.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,56 @@ describe("@webdoc/parser.parse (Typescript)", function() {
expect(methodDoc.access).to.equal("protected");
expect(methodDoc.returns[0].dataType[0]).to.equal("void");
});

it("should work with property parameters", async function() {
const documentTree = await parse([{
content: `
class RedBlackTree {
/** HERE */
constructor(protected readonly root: Node) {}
}
`,
path: ".ts",
package: createPackageDoc(),
}]);

const rootProperty = findDoc("RedBlackTree.root", documentTree);
const ctor = findDoc("RedBlackTree.constructor", documentTree);

expect(rootProperty).to.not.equal(null);
expect(rootProperty.access).to.equal("protected");
expect(rootProperty.dataType[0]).to.equal("Node");
expect(rootProperty.name).to.equal("root");
expect(rootProperty.readonly).to.equal(true);

expect(ctor).to.not.equal(null);
expect(ctor.params.length).to.equal(1);

const rootParameter = ctor.params[0];

expect(rootParameter.dataType[0]).to.equal("Node");
expect(rootParameter.identifier).to.equal("root");
});

it("should work with property parameter when default values are assigned", async function() {
const documentTree = await parse([{
content: `
class AVLTree {
/** HERE */
constructor(protected root: Node = "null") {}
}
`,
path: ".ts",
package: createPackageDoc(),
}]);

const rootProperty = findDoc("AVLTree.root", documentTree);
const ctor = findDoc("AVLTree.constructor", documentTree);

expect(rootProperty).to.not.equal(null);
expect(ctor).to.not.equal(null);

expect(rootProperty.defaultValue).to.equal("\"null\"");
expect(ctor.params[0].default).to.equal("\"null\"");
});
});
3 changes: 2 additions & 1 deletion packages/webdoc-types/index.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export type SourceFile = {
export type ParserOpts = {
memberof?: string[],
object?: string,
undocumented?: boolean
undocumented?: boolean,
undocumentedAnchored?: boolean
};

export type Param = {
Expand Down

0 comments on commit 640ee26

Please sign in to comment.