Skip to content

Commit

Permalink
feat(ngdoc): support pipe api doc (#515)
Browse files Browse the repository at this point in the history
  • Loading branch information
Guoxiin committed Nov 27, 2023
1 parent 50f97a9 commit c60e648
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 47 deletions.
5 changes: 5 additions & 0 deletions .docgeni/public/assets/stack-blitz/src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ $dg-directive-color: #d81b60;
$dg-component-color: #348fe4;
$dg-interface-color: #7cb342;
$dg-service-color: #8e24aa;
$dg-pipe-color: #546e7a;

.dg-doc-viewer {
display: block;
Expand Down Expand Up @@ -933,6 +934,10 @@ $dg-service-color: #8e24aa;
&.class {
background-color: rgba($color: $dg-interface-color, $alpha: 0.8);
}

&.pipe {
background-color: rgba($color: $dg-pipe-color, $alpha: 0.8);
}
}
}

Expand Down
43 changes: 36 additions & 7 deletions packages/a-lib/button/button.component.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import {
Component,
OnInit,
HostBinding,
Input,
ContentChild,
Directive,
ElementRef,
Output,
EventEmitter,
Injectable,
ContentChild,
TemplateRef,
Directive
Input,
OnInit,
Output,
Pipe,
PipeTransform,
TemplateRef
} from '@angular/core';

@Directive()
Expand Down Expand Up @@ -120,3 +121,31 @@ export class ButtonService {
*/
close(id: string): void {}
}

/**
* 把文本转换成全大写形式
* @public
* @name uppercase
* @order 10
*/
@Pipe({
name: 'uppercase',
standalone: true
})
export class UpperCasePipe implements PipeTransform {
constructor() {}

/**
* 大写转换
* @public
* @param {string} value 输入值
* @param {string} defaultValue 转不成大写时候的默认值
* @returns {string}
*/
transform(value: string, defaultValue: string): string {
if (typeof value !== 'string') {
return defaultValue;
}
return value.toUpperCase();
}
}
2 changes: 1 addition & 1 deletion packages/core/src/interfaces/api-declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface PropertyDeclaration {
}

export interface ApiDeclaration {
type: 'directive' | 'component' | 'service' | 'interface' | 'class';
type: 'directive' | 'component' | 'service' | 'interface' | 'class' | 'pipe';
name: string;
className: string;
selector: string;
Expand Down
130 changes: 127 additions & 3 deletions packages/ngdoc/src/ng-parser.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { NgDocParser, NgEntryItemDoc, NgMethodDoc } from '../src';
import * as path from 'path';
import { toolkit } from '@docgeni/toolkit';
import { Project, Node } from 'ts-morph';
import * as path from 'path';
import { Project } from 'ts-morph';
import ts from 'typescript';
import { NgDocParser, NgEntryItemDoc, NgMethodDoc } from '../src';
import { createTestNgDocParser } from './testing';

const EXCLUDE_DIRS: string[] = [];
Expand Down Expand Up @@ -966,6 +966,130 @@ export abstract class DialogRef<T = unknown> extends AbstractDialogRef<T> {
});
});
});

describe('pipe', () => {
it('should parse pipe methods', () => {
const { ngDocParser } = createTestNgDocParser('button', {
'/dialog/dialog.ts': `
/**
* 把文本转换成全大写形式
* @public
* @name uppercase
* @order 10
*/
@Pipe({
name: 'uppercase',
standalone: true
})
export class UpperCasePipe implements PipeTransform {
constructor() {}
/**
* @public
* @param {string} value 输入值
* @returns {boolean}
*/
transform(value: string): boolean {
return true;
}
}`
});
const docs = ngDocParser.parse('/dialog/*');
expect(docs.length).toBe(1);
expect(docs[0]).toEqual({
type: 'pipe',
name: 'uppercase',
description: '把文本转换成全大写形式',
order: 10,
pure: true,
standalone: true,
methods: [
{
name: 'transform',
parameters: [
{
name: 'value',
description: '输入值',
type: 'string'
}
],
returnValue: {
type: 'boolean',
description: ''
},
description: ''
}
]
});
});

it('should get multiple methods for overload', () => {
const sourceText = `
@Pipe({
name: 'lowercase',
standalone: true
})
export class LowerCasePipe implements PipeTransform {
constructor() {}
/**
* @public
* @description transform 重载方法1
* @param input1 这是一个参数
*/
transform(input1: number): void;
/**
* @description transform 重载方法2
* @param input1
* @param input2
*/
transform(input1: number, input2: number): void;
transform(input1: number, input2?: number): void {}
}`;

const { ngDocParser } = createTestNgDocParser('dialog', {
'/dialog/dialog.ts': sourceText
});
const docs = ngDocParser.parse('/dialog/*');
expect(docs[0].methods).toEqual([
{
name: 'transform',
parameters: [
{
name: 'input1',
description: '这是一个参数',
type: 'number'
}
],
returnValue: {
type: 'void',
description: ''
},
description: 'transform 重载方法1'
},
{
name: 'transform',
parameters: [
{
name: 'input1',
description: '',
type: 'number'
},
{
name: 'input2',
description: '',
type: 'number'
}
],
returnValue: {
type: 'void',
description: ''
},
description: 'transform 重载方法2'
}
] as NgMethodDoc[]);
});
});
});

function createButtonComponent(body: string = '') {
Expand Down
72 changes: 44 additions & 28 deletions packages/ngdoc/src/ng-parser.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
import { ts } from './typescript';
import { toolkit, debug, fs } from '@docgeni/toolkit';
import { debug, fs, toolkit } from '@docgeni/toolkit';
import { NgParserHost, createNgParserHost } from './ng-parser-host';
import {
NgDirectiveDoc,
NgPropertyDoc,
NgEntryItemDoc,
NgDocItemType,
NgParsedDecorator,
NgMethodDoc,
NgServiceDoc,
ClassLikeDoc
} from './types';
import {
getNgDecorator,
getNgPropertyDecorator,
DocTagResult,
declarationIsPublic,
getDirectiveMeta,
getDocTagsBySignature,
getDocTagsBySymbol,
getHeritageDeclarations,
getNgDecorator,
getNgDocItemType,
getPropertyKind,
getNgPropertyDecorator,
getNgPropertyOptions,
getPipeMeta,
getPropertyKind,
getPropertyValue,
serializeSymbol,
getDocTagsBySymbol,
getDocTagsBySignature,
serializeMethodParameterSymbol,
declarationIsPublic,
hasPrivateTag,
DocTagResult,
getSymbolDeclaration,
getTextByJSDocTagInfo,
hasPrivateTag,
hasPublicTag,
getHeritageClauses,
getSymbolDeclaration,
getHeritageDeclarations,
parseJsDocTagsToDocTagResult
serializeMethodParameterSymbol,
serializeSymbol
} from './parser';
import { createNgParserHost, NgParserHost } from './ng-parser-host';
import {
ClassLikeDoc,
NgDirectiveDoc,
NgDocItemType,
NgEntryItemDoc,
NgMethodDoc,
NgParsedDecorator,
NgPipeDoc,
NgPropertyDoc,
NgServiceDoc
} from './types';
import { ts } from './typescript';

export interface NgDocParserOptions {
tsConfigPath?: string;
Expand Down Expand Up @@ -121,6 +121,7 @@ export class NgDocParser {
docs.push(this.parseServiceDoc(context, symbol, ngDecorator));
break;
case 'pipe':
docs.push(this.parsePipeDoc(context, symbol, ngDecorator));
break;
default:
throw new Error(`${type} is not support.`);
Expand Down Expand Up @@ -157,6 +158,21 @@ export class NgDocParser {
return directiveDoc;
}

private parsePipeDoc(context: ParserSourceFileContext, symbol: ts.Symbol, ngDecorator: NgParsedDecorator) {
const declaration = symbol.valueDeclaration as ts.ClassDeclaration;
const description = serializeSymbol(symbol, context.checker);
const [tags, localeTags] = getDocTagsBySymbol(symbol);
const directiveDoc: NgPipeDoc = {
type: 'pipe',
description: description.description,
order: tags.order ? parseInt(getTextByJSDocTagInfo(tags.order, ''), 10) : Number.MAX_SAFE_INTEGER,
...getPipeMeta(ngDecorator.argumentInfo),
name: getTextByJSDocTagInfo(tags.name, getPipeMeta(ngDecorator.argumentInfo)?.name)
};
directiveDoc.methods = this.parseDeclarationMethods(context, declaration, { explicitPublic: true });
return directiveDoc;
}

private parseDirectiveDoc(
context: ParserSourceFileContext,
type: NgDocItemType,
Expand Down Expand Up @@ -266,7 +282,7 @@ export class NgDocParser {
classDeclaration: ts.ClassDeclaration,
options: { explicitPublic: boolean } = { explicitPublic: false }
) {
const methods: NgPropertyDoc[] = [];
const methods: NgMethodDoc[] = [];
const parsedSymbols = new WeakMap<ts.Symbol, boolean>();
ts.forEachChild(classDeclaration, (node: ts.Node) => {
if (ts.isMethodDeclaration(node) && declarationIsPublic(node)) {
Expand Down
13 changes: 12 additions & 1 deletion packages/ngdoc/src/parser/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ArgumentInfo, NgDirectiveMetadata, NgDocItemType } from '../types';
import { ArgumentInfo, NgDirectiveMetadata, NgDocItemType, NgPipeMetadata } from '../types';
import { ts } from '../typescript';
import { lineFeedPrinter } from './line-feed-printer';

Expand Down Expand Up @@ -56,6 +56,17 @@ export function getDirectiveMeta(args: ArgumentInfo[]): NgDirectiveMetadata {
: undefined;
}

export function getPipeMeta(args: ArgumentInfo[]): NgPipeMetadata {
const firstArg = args[0] as Record<string, string | string[]>;
return firstArg
? ({
name: firstArg.name,
pure: !(firstArg.pure === 'false'),
standalone: firstArg.standalone === 'true'
} as NgPipeMetadata)
: undefined;
}

export function getTypeNodes(typeNodes: ts.NodeArray<ts.TypeNode>) {
if (!typeNodes.some(ts.isLiteralTypeNode)) return null;

Expand Down
3 changes: 2 additions & 1 deletion packages/ngdoc/src/types/entry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { NgDirectiveDoc } from './directive';
import { NgPipeDoc } from './pipe';
import { NgServiceDoc } from './service';

export type NgEntryItemDoc = NgDirectiveDoc | NgServiceDoc;
export type NgEntryItemDoc = NgDirectiveDoc | NgServiceDoc | NgPipeDoc;
11 changes: 6 additions & 5 deletions packages/ngdoc/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export * from './directive';
export * from './decorators';
export * from './class-like';
export * from './declarations';
export * from './enums';
export * from './decorators';
export * from './directive';
export * from './entry';
export * from './module';
export * from './enums';
export * from './expression';
export * from './module';
export * from './pipe';
export * from './service';
export * from './class-like';
16 changes: 16 additions & 0 deletions packages/ngdoc/src/types/pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NgMethodDoc } from './declarations';
import { NgDocItemType } from './enums';

export interface NgPipeDoc extends NgPipeMetadata {
name?: string;
description?: string;
type: NgDocItemType;
order?: number;
methods?: NgMethodDoc[];
}

export interface NgPipeMetadata {
name?: string;
pure?: boolean;
standalone?: boolean;
}

0 comments on commit c60e648

Please sign in to comment.