Skip to content

Commit ba101c4

Browse files
author
Sebastian Schürmann
committed
feature(banira): add a new approach at document generation
1 parent 7491b5d commit ba101c4

File tree

7 files changed

+164
-25
lines changed

7 files changed

+164
-25
lines changed
+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import {
2+
Node,
3+
SyntaxKind,
4+
CommentRange,
5+
getLeadingCommentRanges
6+
} from 'typescript';
7+
import { TextRange } from '@microsoft/tsdoc';
8+
9+
export interface IFoundComment {
10+
compilerNode: Node;
11+
textRange: TextRange;
12+
}
13+
14+
function isDeclarationKind(kind: SyntaxKind): boolean {
15+
switch (kind) {
16+
case SyntaxKind.ArrowFunction:
17+
case SyntaxKind.BindingElement:
18+
case SyntaxKind.ClassDeclaration:
19+
case SyntaxKind.ClassExpression:
20+
case SyntaxKind.Constructor:
21+
case SyntaxKind.EnumDeclaration:
22+
case SyntaxKind.EnumMember:
23+
case SyntaxKind.ExportSpecifier:
24+
case SyntaxKind.FunctionDeclaration:
25+
case SyntaxKind.FunctionExpression:
26+
case SyntaxKind.GetAccessor:
27+
case SyntaxKind.ImportEqualsDeclaration:
28+
case SyntaxKind.ImportSpecifier:
29+
case SyntaxKind.InterfaceDeclaration:
30+
case SyntaxKind.JsxAttribute:
31+
case SyntaxKind.MethodDeclaration:
32+
case SyntaxKind.MethodSignature:
33+
case SyntaxKind.ModuleDeclaration:
34+
case SyntaxKind.NamespaceExportDeclaration:
35+
case SyntaxKind.NamespaceImport:
36+
case SyntaxKind.Parameter:
37+
case SyntaxKind.PropertyAssignment:
38+
case SyntaxKind.PropertyDeclaration:
39+
case SyntaxKind.PropertySignature:
40+
case SyntaxKind.SetAccessor:
41+
case SyntaxKind.ShorthandPropertyAssignment:
42+
case SyntaxKind.TypeAliasDeclaration:
43+
case SyntaxKind.TypeParameter:
44+
case SyntaxKind.VariableDeclaration:
45+
case SyntaxKind.JSDocTypedefTag:
46+
case SyntaxKind.JSDocCallbackTag:
47+
case SyntaxKind.JSDocPropertyTag:
48+
return true;
49+
}
50+
return false;
51+
}
52+
53+
function getJSDocCommentRanges(node: Node, text: string): CommentRange[] {
54+
const commentRanges: CommentRange[] = [];
55+
56+
switch (node.kind) {
57+
case SyntaxKind.Parameter:
58+
case SyntaxKind.TypeParameter:
59+
case SyntaxKind.FunctionExpression:
60+
case SyntaxKind.ArrowFunction:
61+
case SyntaxKind.ParenthesizedExpression:
62+
return commentRanges;
63+
}
64+
65+
// Collect comment ranges
66+
const ranges: CommentRange[] | undefined = getLeadingCommentRanges(text, node.getFullStart());
67+
if (ranges) {
68+
commentRanges.push(...ranges);
69+
}
70+
71+
return commentRanges;
72+
}
73+
74+
/**
75+
* Recursively walks through the TypeScript AST and discovers JSDoc comments associated with declaration nodes.
76+
*
77+
* @param node - The TypeScript AST node to analyze
78+
* @param foundComments - Array to collect discovered comments
79+
*/
80+
export function discoverComments(node: Node, foundComments: IFoundComment[]): void {
81+
const buffer: string = node.getSourceFile().getFullText(); // don't use getText() here!
82+
if (isDeclarationKind(node.kind)) {
83+
const comments: CommentRange[] = getJSDocCommentRanges(node, buffer);
84+
85+
if (comments.length > 0) {
86+
for (const comment of comments) {
87+
foundComments.push({
88+
compilerNode: node,
89+
textRange: TextRange.fromStringRange(buffer, comment.pos, comment.end)
90+
});
91+
}
92+
}
93+
}
94+
return node.forEachChild((child) => discoverComments(child, foundComments));
95+
}

packages/banira/src/formatter/doc-page.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DocBlock, DocParamCollection, DocSection, ParserContext } from "@microsoft/tsdoc";
1+
import { DocBlock, DocParamCollection, DocSection, ParserContext, ParserMessage } from "@microsoft/tsdoc";
22

33
export class FormatterDocPage {
44
private context: ParserContext;
@@ -11,6 +11,10 @@ export class FormatterDocPage {
1111
return this.context.docComment.customBlocks;
1212
}
1313

14+
get logs(): readonly ParserMessage[] {
15+
return this.context.log.messages;
16+
}
17+
1418
get params(): DocParamCollection {
1519
return this.context.docComment.params
1620
}
@@ -33,8 +37,12 @@ export class FormatterDocPage {
3337
>
3438
</head>
3539
<body>
36-
<h1>${title}</h1>
37-
<${tagName}></${tagName}>
40+
<header>
41+
<h1>${title}</h1>
42+
</header>
43+
<main>
44+
<${tagName}></${tagName}>
45+
</main>
3846
</body>
3947
</html>`
4048
}

packages/banira/src/result-analyzer.ts

+22-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
1-
import { Program, EmitResult, Diagnostic, SourceFile, CompilerOptions, DiagnosticCategory, formatDiagnosticsWithColorAndContext, sys, getPreEmitDiagnostics } from 'typescript';
1+
import {
2+
Program,
3+
EmitResult,
4+
Diagnostic,
5+
SourceFile,
6+
CompilerOptions,
7+
DiagnosticCategory,
8+
formatDiagnosticsWithColorAndContext,
9+
sys,
10+
getPreEmitDiagnostics
11+
} from 'typescript';
212
import { CompilerResult } from './compiler';
13+
import { discoverComments, IFoundComment } from './discover-comments.js';
314

415
/**
516
* Interface representing the result of diagnostic analysis
@@ -71,6 +82,14 @@ export class ResultAnalyzer {
7182
return this.program.getSourceFiles();
7283
}
7384

85+
get comments(): IFoundComment[] {
86+
const foundComments: IFoundComment[] = [];
87+
for (const sourceFile of this.program.getSourceFiles()) {
88+
discoverComments(sourceFile, foundComments);
89+
}
90+
return foundComments;
91+
}
92+
7493
/**
7594
* Gets the paths of all emitted (output) files
7695
*
@@ -105,7 +124,7 @@ export class ResultAnalyzer {
105124
const formatted = formatDiagnosticsWithColorAndContext(diagnostics, {
106125
getCurrentDirectory: () => process.cwd(),
107126
getCanonicalFileName: fileName => fileName,
108-
getNewLine: () => sys.newLine
127+
getNewLine:() => sys.newLine
109128
});
110129

111130
return {
@@ -116,4 +135,4 @@ export class ResultAnalyzer {
116135
formatted
117136
}
118137
}
119-
}
138+
}

packages/banira/test/doc.gen.formatter-page.test.ts

+23-10
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,30 @@ describe('DocGen Formatter', () => {
1414
parsed = await docGen.parseDoc('./test/fixtures/my-circle.ts');
1515
formatter = new FormatterDocPage(parsed);
1616
})
17-
it('should parse a typescript file and return a ParserContext', async () => {
18-
assert.ok(parsed, 'ParserContext should be returned');
19-
});
2017

21-
it('should create a doc page', async () => {
22-
const result = formatter.createDocPage(docGen.tagName, docGen.src, docGen.title);
23-
assert.ok(result, 'Doc page should be created');
24-
});
18+
it('customBlocks', async () => {
19+
assert.equal(formatter.custom.length, 1);
20+
})
21+
22+
it('logs', async () => {
23+
assert.equal(formatter.logs.length, 0);
24+
})
25+
26+
it('params', async () => {
27+
assert.ok(formatter.params);
28+
})
2529

26-
it('contains the demo tag', async () => {
27-
const result = formatter.createDocPage(docGen.tagName, docGen.src, docGen.title);
28-
assert.match(result, /<my-circle><\/my-circle>/);
30+
describe('createDocPage', () => {
31+
let result: string;
32+
before(async () => {
33+
result = formatter.createDocPage(docGen.tagName, docGen.src, docGen.title);
34+
});
35+
36+
it('should create a doc page', async () => {
37+
assert.ok(result, 'Doc page should be created');
38+
});
39+
it('contains the demo tag', async () => {
40+
assert.match(result, /<my-circle><\/my-circle>/);
41+
});
2942
});
3043
});

packages/banira/test/fixtures/my-circle.ts

-8
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
/**
22
* A custom web component that renders a circle using SVG.
33
*
4-
* @example
5-
* ```html
6-
* <my-circle size="100" color="blue"></my-circle>
7-
* ```
8-
*/
9-
/**
10-
* A demo of a custom web component that renders a circle using SVG.
11-
*
124
* @demo
135
* ```html
146
* <my-circle size="100" color="blue"></my-circle>

packages/banira/test/result-analyzer.test.ts

+11
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,17 @@ describe("ResultAnalyzer", () => {
107107
});
108108
});
109109

110+
describe("comments", () => {
111+
it("extracts them", () => {
112+
assert.ok(analyzer.comments);
113+
});
114+
115+
it("gets > 10K comments", () => {
116+
assert.ok(analyzer.comments.length>1000);
117+
});
118+
});
119+
120+
110121
describe("output files", () => {
111122
it("should provide output files as an array", () => {
112123
assert.ok(

packages/component-webaudio/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"build": "tsc",
1111
"build:banira": "banira compile --project ./tsconfig.json --outDir ./dist ./src/wa-knob.ts",
1212
"predemo": "rm -rf ./demo && mkdir ./demo && cp -r ./dist/ ./demo/dist/",
13-
"demo": "banira doc ./src/wa-knob.ts > ./demo/index.html"
13+
"demo": "banira doc ./src/wa-knob.ts > ./demo/index.html",
14+
"server": "npx -y http-server ./demo"
1415
},
1516
"keywords": [
1617
"web-components",

0 commit comments

Comments
 (0)