Skip to content

Commit

Permalink
Merge pull request #34 from RoystonS/parse_jsdoc_tags
Browse files Browse the repository at this point in the history
Add support for JsDoc tags (fix issue #28)
  • Loading branch information
pvasek authored Jun 21, 2017
2 parents 737de76 + 0411de8 commit 7ca59cc
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 13 deletions.
29 changes: 26 additions & 3 deletions src/__tests__/data/transformAST.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ export const exportedVar = 10;
/** unexportedVarFunction comment */
const unexportedVarFunction = (param1: string): number => 0
;
/** exportedVarFunction comment */
/** exportedVarFunction comment
*
* @tag1
* @tag2 partA partB partC
*/
export const exportedVarFunction = (param1: number, param2: string): string => "";

function unexportedFunction(param1: number): string {
Expand All @@ -22,8 +26,21 @@ function exportedFunction(param1: string, param2: number): number {
interface UnexportedInterface {
/** prop1 comment */
prop1: string;

/**
* prop2 comment
* @tag1
* @tag2 partA partB partC
*/
prop2?: string;
}

/**
* Interface comment
*
* @tag1
* @tag2 partA partB partC
*/
export interface ExportedInterface {
/** prop1 comment */
prop1: string;
Expand All @@ -46,7 +63,10 @@ class UnexportedClass extends OurBaseClass<ExportedInterface, {}> {
}
}

/** ExportedClass comment */
/** ExportedClass comment
* @tag1 partA partB
* @tag2
*/
export class ExportedClass {
method1(): string {
return "";
Expand All @@ -73,7 +93,10 @@ export const exportedExternalHoc1 = externalHoc(ExportedClass);
/** exportedExternalHoc2 comment */
export const exportedExternalHoc2 = externalHoc(exportedFunction);

/** exported intersection type */
/** exported intersection type
* @tag1 partA partB
* @tag2
*/
export type ExportedType1 = React.HTMLAttributes<HTMLImageElement> & {
/** the first property */
prop1: "value1" | "value2";
Expand Down
4 changes: 3 additions & 1 deletion src/__tests__/data/transformAST_external.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ export interface ExternalInterfaceBase {
prop1ExternalInterfaceBase: string;
}

/** ExternalInterface comment */
/** ExternalInterface comment
* @tag
*/
export interface ExternalInterface extends ExternalInterfaceBase {
/** prop1 comment */
prop1OnExternalInterface: string;
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/getFileDocumentation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ describe('getFileDocumentation', () => {
assert.isNotNull(r1.propInterface);
const p1 = r1.propInterface;
assert.equal(p1.name, 'Props');
assert.equal(p1.comment, 'Props comment ');
assert.equal(p1.comment, 'Props comment');
assert.equal(p1.members.length, 2);
assert.equal(p1.members[0].name, 'isFlippedX');
assert.equal(p1.members[0].comment, 'whether the image is flipped horizontally');
Expand Down
15 changes: 13 additions & 2 deletions src/__tests__/transformAST.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('transformAST', () => {
assert.equal(r4.name, 'exportedVarFunction');
assert.equal(r4.exported, true);
assert.equal(r4.kind, 'arrowFunction');
assert.equal(r4.comment, 'exportedVarFunction comment');
assert.equal(r4.comment, 'exportedVarFunction comment\n@tag1\n@tag2 partA partB partC');
assert.equal(r4.arrowFunctionType, 'string');
assert.deepEqual(r4.arrowFunctionParams, ['number', 'string']);

Expand Down Expand Up @@ -98,10 +98,18 @@ describe('transformAST', () => {
'isOwn': true,
'comment': 'prop1 comment',
'values': [],
}, {
'name': 'prop2',
'type': 'string',
'isRequired': false,
'isOwn': true,
'comment': 'prop2 comment\n@tag1\n@tag2 partA partB partC',
'values': [],
}]);

const r2 = result[1];
assert.equal(r2.name, 'ExportedInterface');
assert.equal(r2.comment, 'Interface comment\n@tag1\n@tag2 partA partB partC');
assert.equal(r2.exported, true);
assert.deepEqual(r2.properties, [{
'name': 'prop1',
Expand Down Expand Up @@ -147,6 +155,7 @@ describe('transformAST', () => {
const r4 = result[3];
assert.equal(r4.name, 'ExternalInterface');
assert.equal(r4.exported, true);
assert.equal(r4.comment, 'ExternalInterface comment\n@tag');
});

it('should provide data about classes', () => {
Expand All @@ -161,7 +170,7 @@ describe('transformAST', () => {
const r2 = result[2];
assert.equal(r2.name, 'ExportedClass');
assert.equal(r2.exported, true);
assert.equal(r2.comment, 'ExportedClass comment');
assert.equal(r2.comment, 'ExportedClass comment\n@tag1 partA partB\n@tag2');
assert.deepEqual(r2.methods, [{name: 'method1'}, {name: 'method2'}]);

const r4 = result[3];
Expand All @@ -175,6 +184,8 @@ describe('transformAST', () => {
assert.equal(target.types.length, 1);
const t1 = target.types[0];
assert.equal(t1.name, 'ExportedType1');
assert.equal(t1.comment, 'exported intersection type\n@tag1 partA partB\n@tag2');

// because ExportedType1 inherites from built in type and can
// change over time we don't use exact number here
assert.isTrue(t1.properties.length > 200);
Expand Down
5 changes: 5 additions & 0 deletions src/printUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ export function simplePrint(checker: ts.TypeChecker, node: ts.Node, indent = 0)
if (comments.length > 0) {
info.push(prefix + 'comment: \'' + comments.map(i => i.text).join('; ') + '\'');
}
const jsdoctags = s.getJsDocTags();
if (jsdoctags.length > 0) {
info.push(prefix + 'jsdoctags: \'' +
jsdoctags.map(i => `@${i.name} ${i.text}`).join('; ') + '\'');
}
}

if (node.kind === ts.SyntaxKind.FunctionDeclaration) {
Expand Down
38 changes: 32 additions & 6 deletions src/transformAST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,32 @@ function isNodeExported(node: ts.Node): boolean {
return false;
}

/**
* Rebuilds a full JsDoc comment symbol, reconsitituting
* from the parts that TypeScript has broken it into.
*/
function getFullJsDocComment(symbol: ts.Symbol) {
if (!symbol) {
return '';
}

const mainComment = ts.displayPartsToString(symbol.getDocumentationComment());
const tags = symbol.getJsDocTags() || [];

// Transform { name: 'tag', text: 'text1 text2' } into
// '@tag text1 text2'
const tagComments = tags.map(t => {
let result = '@' + t.name;
if (t.text) {
result += ' ' + t.text;
}
return result;
});

const fullComment = mainComment + '\n' + tagComments.join('\n');
return fullComment.trim();
}

function getType(prop: ts.PropertySignature): MemberType {
const unionType = prop.type as ts.UnionTypeNode;
if (unionType && unionType.types) {
Expand Down Expand Up @@ -75,7 +101,7 @@ function getProperties(checker: ts.TypeChecker, type: ts.Type, parent: ts.Node):
type: typeInfo.type,
values: typeInfo.values || [],
isRequired: !prop.questionToken,
comment: ts.displayPartsToString(symbol.getDocumentationComment()).trim(),
comment: getFullJsDocComment(symbol),
};
});
}
Expand Down Expand Up @@ -145,7 +171,7 @@ export function transformAST(sourceFile: ts.SourceFile, checker: ts.TypeChecker)
return {
name: identifier.text,
exported: isNodeExported(i),
comment: symbol ? ts.displayPartsToString(symbol.getDocumentationComment()).trim() : '',
comment: getFullJsDocComment(symbol),
type: varType.symbol ? varType.symbol.getName() : 'unknown',
kind,
arrowFunctionType,
Expand All @@ -163,7 +189,7 @@ export function transformAST(sourceFile: ts.SourceFile, checker: ts.TypeChecker)
const type = checker.getTypeAtLocation(i.name);
return {
name: symbol.name,
comment: ts.displayPartsToString(symbol.getDocumentationComment()).trim(),
comment: getFullJsDocComment(symbol),
exported: isNodeExported(i),
properties: getProperties(checker, type, i),
};
Expand Down Expand Up @@ -196,7 +222,7 @@ export function transformAST(sourceFile: ts.SourceFile, checker: ts.TypeChecker)
name: i.name.getText(),
properties,
exported: isNodeExported(i),
comment: !symbol ? "" : ts.displayPartsToString(symbol.getDocumentationComment())
comment: getFullJsDocComment(symbol)
};
});

Expand Down Expand Up @@ -231,7 +257,7 @@ export function transformAST(sourceFile: ts.SourceFile, checker: ts.TypeChecker)
// in that case we need to include the interface explicitly
interfaces.push({
name: taType.symbol.name,
comment: ts.displayPartsToString(taType.symbol.getDocumentationComment()).trim(),
comment: getFullJsDocComment(taType.symbol),
exported: true, // it has to be exported in order to be used,
properties: getProperties(checker, taType, null),
});
Expand All @@ -251,7 +277,7 @@ export function transformAST(sourceFile: ts.SourceFile, checker: ts.TypeChecker)
name: symbol.name,
exported: isNodeExported(i),
baseType: baseType,
comment: ts.displayPartsToString(symbol.getDocumentationComment()).trim(),
comment: getFullJsDocComment(symbol),
methods: getMethods(checker, type, i),
};
});
Expand Down

0 comments on commit 7ca59cc

Please sign in to comment.