@tsdoctor/model 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ApiItems.js +111 -0
- package/CrossLinker.js +81 -0
- package/EntryPoints.js +76 -0
- package/Model.js +90 -0
- package/README.md +55 -52
- package/Render.js +182 -0
- package/Routes.js +110 -0
- package/Signature.js +100 -0
- package/StructuredData.js +18 -0
- package/SyntheticBases.js +83 -0
- package/Tsdoc.js +250 -0
- package/_virtual/_rolldown/runtime.js +18 -0
- package/index.d.ts +477 -66
- package/index.js +11 -6
- package/internal/prose.js +20 -0
- package/internal/text.js +11 -0
- package/package.json +11 -1
- package/cross-linker.js +0 -37
- package/formatter.js +0 -70
- package/model-loader.js +0 -24
- package/render.js +0 -118
- package/tsdoc.js +0 -158
package/Tsdoc.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { ApiDocumentedItem, ApiReleaseTagMixin, ReleaseTag } from "@microsoft/api-extractor-model";
|
|
3
|
+
import { Code, InlineCode, Link, Paragraph, Text } from "@effected/markdown";
|
|
4
|
+
|
|
5
|
+
//#region src/Tsdoc.ts
|
|
6
|
+
var Tsdoc_exports = /* @__PURE__ */ __exportAll({
|
|
7
|
+
deprecation: () => deprecation,
|
|
8
|
+
examples: () => examples,
|
|
9
|
+
hasModifier: () => hasModifier,
|
|
10
|
+
params: () => params,
|
|
11
|
+
plainText: () => plainText,
|
|
12
|
+
releaseTag: () => releaseTag,
|
|
13
|
+
returns: () => returns,
|
|
14
|
+
seeReferences: () => seeReferences,
|
|
15
|
+
summary: () => summary,
|
|
16
|
+
toMarkdown: () => toMarkdown
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Recursively flatten a TSDoc DocNode tree to plain text (code spans →
|
|
20
|
+
* backticks, `{@link}` → display text, code fences dropped).
|
|
21
|
+
*
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
function plainText(node) {
|
|
25
|
+
const nodeAny = node;
|
|
26
|
+
if (node.kind === "PlainText") return nodeAny.text || "";
|
|
27
|
+
if (node.kind === "SoftBreak") return " ";
|
|
28
|
+
if (node.kind === "CodeSpan") return `\`${nodeAny.code || ""}\``;
|
|
29
|
+
if (node.kind === "LinkTag") {
|
|
30
|
+
if (nodeAny.linkText) return plainText(nodeAny.linkText);
|
|
31
|
+
return (nodeAny.codeDestination?.memberReferences?.[0]?.memberIdentifier)?.identifier || "";
|
|
32
|
+
}
|
|
33
|
+
const parts = [];
|
|
34
|
+
if (typeof nodeAny.getChildNodes === "function") for (const child of nodeAny.getChildNodes()) {
|
|
35
|
+
const childText = plainText(child);
|
|
36
|
+
if (childText) parts.push(childText);
|
|
37
|
+
}
|
|
38
|
+
return parts.join("");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Convert a TSDoc DocNode tree to markdown nodes, preserving structure the
|
|
42
|
+
* plain-text flattener drops: code spans become {@link InlineCode}, url
|
|
43
|
+
* `{@link}` tags become {@link Link} nodes, fenced code becomes {@link Code}
|
|
44
|
+
* blocks. Code-destination `{@link}` tags (declaration references) flatten to
|
|
45
|
+
* their display text — resolving them to URLs is the caller's cross-linking
|
|
46
|
+
* concern, not this layer's.
|
|
47
|
+
*
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
function toMarkdown(node) {
|
|
51
|
+
const nodeAny = node;
|
|
52
|
+
switch (node.kind) {
|
|
53
|
+
case "Paragraph": {
|
|
54
|
+
const children = inlineNodes(node);
|
|
55
|
+
return children.length > 0 ? [new Paragraph({ children })] : [];
|
|
56
|
+
}
|
|
57
|
+
case "FencedCode": return [new Code({
|
|
58
|
+
value: String(nodeAny.code ?? "").replace(/\n$/, ""),
|
|
59
|
+
lang: nodeAny.language || "typescript"
|
|
60
|
+
})];
|
|
61
|
+
case "PlainText":
|
|
62
|
+
case "SoftBreak":
|
|
63
|
+
case "CodeSpan":
|
|
64
|
+
case "LinkTag": return inlineNodes(node);
|
|
65
|
+
default: {
|
|
66
|
+
const out = [];
|
|
67
|
+
if (typeof nodeAny.getChildNodes === "function") for (const child of nodeAny.getChildNodes()) out.push(...toMarkdown(child));
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Map a TSDoc inline node (or container of inline nodes) to phrasing content. */
|
|
73
|
+
function inlineNodes(node) {
|
|
74
|
+
const nodeAny = node;
|
|
75
|
+
if (node.kind === "PlainText") {
|
|
76
|
+
const text = nodeAny.text || "";
|
|
77
|
+
return text ? [new Text({ value: text })] : [];
|
|
78
|
+
}
|
|
79
|
+
if (node.kind === "SoftBreak") return [new Text({ value: " " })];
|
|
80
|
+
if (node.kind === "CodeSpan") {
|
|
81
|
+
const code = nodeAny.code || "";
|
|
82
|
+
return code ? [new InlineCode({ value: code })] : [];
|
|
83
|
+
}
|
|
84
|
+
if (node.kind === "LinkTag") {
|
|
85
|
+
const linkText = nodeAny.linkText ?? "";
|
|
86
|
+
if (nodeAny.urlDestination) {
|
|
87
|
+
const url = nodeAny.urlDestination;
|
|
88
|
+
return [new Link({
|
|
89
|
+
url,
|
|
90
|
+
children: [new Text({ value: linkText || url })]
|
|
91
|
+
})];
|
|
92
|
+
}
|
|
93
|
+
const ref = nodeAny.codeDestination?.memberReferences?.[0]?.memberIdentifier;
|
|
94
|
+
const display = linkText || ref?.identifier || "";
|
|
95
|
+
return display ? [new Text({ value: display })] : [];
|
|
96
|
+
}
|
|
97
|
+
const out = [];
|
|
98
|
+
if (typeof nodeAny.getChildNodes === "function") for (const child of nodeAny.getChildNodes()) out.push(...inlineNodes(child));
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The TSDoc summary section as a single cleaned line.
|
|
103
|
+
*
|
|
104
|
+
* @public
|
|
105
|
+
*/
|
|
106
|
+
function summary(item) {
|
|
107
|
+
if (item instanceof ApiDocumentedItem) {
|
|
108
|
+
const tsdoc = item.tsdocComment;
|
|
109
|
+
if (tsdoc?.summarySection) return plainText(tsdoc.summarySection).replace(/\s+/g, " ").trim();
|
|
110
|
+
}
|
|
111
|
+
return "";
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* `@param` blocks merged with parameter types from the declaration excerpt.
|
|
115
|
+
*
|
|
116
|
+
* @public
|
|
117
|
+
*/
|
|
118
|
+
function params(item) {
|
|
119
|
+
const out = [];
|
|
120
|
+
const paramTypes = /* @__PURE__ */ new Map();
|
|
121
|
+
const parameters = item.parameters;
|
|
122
|
+
if (Array.isArray(parameters)) for (const param of parameters) {
|
|
123
|
+
const excerpt = param.parameterTypeExcerpt;
|
|
124
|
+
const name = param.name || "";
|
|
125
|
+
if (excerpt?.text) paramTypes.set(name, String(excerpt.text).trim());
|
|
126
|
+
}
|
|
127
|
+
if (item instanceof ApiDocumentedItem) {
|
|
128
|
+
const tsdoc = item.tsdocComment;
|
|
129
|
+
if (tsdoc?.params) {
|
|
130
|
+
for (const block of tsdoc.params.blocks) {
|
|
131
|
+
const blockAny = block;
|
|
132
|
+
const name = blockAny.parameterName || "";
|
|
133
|
+
const description = plainText(blockAny.content).replace(/\s+/g, " ").trim();
|
|
134
|
+
const type = paramTypes.get(name);
|
|
135
|
+
out.push({
|
|
136
|
+
name,
|
|
137
|
+
...type != null ? { type } : {},
|
|
138
|
+
description
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
for (const [name, type] of paramTypes.entries()) out.push({
|
|
145
|
+
name,
|
|
146
|
+
type,
|
|
147
|
+
description: ""
|
|
148
|
+
});
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The `@returns` block description, if present.
|
|
153
|
+
*
|
|
154
|
+
* @public
|
|
155
|
+
*/
|
|
156
|
+
function returns(item) {
|
|
157
|
+
if (item instanceof ApiDocumentedItem) {
|
|
158
|
+
const tsdoc = item.tsdocComment;
|
|
159
|
+
if (tsdoc?.returnsBlock) {
|
|
160
|
+
const description = plainText(tsdoc.returnsBlock.content).replace(/\s+/g, " ").trim();
|
|
161
|
+
return description.length > 0 ? { description } : null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* All `@example` fenced-code blocks (falls back to plain text).
|
|
168
|
+
*
|
|
169
|
+
* @public
|
|
170
|
+
*/
|
|
171
|
+
function examples(item) {
|
|
172
|
+
const out = [];
|
|
173
|
+
if (!(item instanceof ApiDocumentedItem)) return out;
|
|
174
|
+
const tsdoc = item.tsdocComment;
|
|
175
|
+
for (const block of tsdoc?.customBlocks || []) {
|
|
176
|
+
if (block.blockTag?.tagNameWithUpperCase !== "@EXAMPLE") continue;
|
|
177
|
+
const content = block.content;
|
|
178
|
+
let found = false;
|
|
179
|
+
for (const node of content?.nodes || []) if (node.kind === "FencedCode") {
|
|
180
|
+
out.push({
|
|
181
|
+
language: node.language || "typescript",
|
|
182
|
+
code: node.code || ""
|
|
183
|
+
});
|
|
184
|
+
found = true;
|
|
185
|
+
}
|
|
186
|
+
if (!found) {
|
|
187
|
+
const text = plainText(content).trim();
|
|
188
|
+
if (text) out.push({
|
|
189
|
+
language: "typescript",
|
|
190
|
+
code: text
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* The deprecation-block message, if one is present.
|
|
198
|
+
*
|
|
199
|
+
* @public
|
|
200
|
+
*/
|
|
201
|
+
function deprecation(item) {
|
|
202
|
+
if (item instanceof ApiDocumentedItem) {
|
|
203
|
+
const tsdoc = item.tsdocComment;
|
|
204
|
+
if (tsdoc?.deprecatedBlock) return { message: plainText(tsdoc.deprecatedBlock.content).replace(/\s+/g, " ").trim() };
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* The release tag (Public/Beta/Alpha/Internal), `"Public"` when absent.
|
|
210
|
+
*
|
|
211
|
+
* @public
|
|
212
|
+
*/
|
|
213
|
+
function releaseTag(item) {
|
|
214
|
+
if (ApiReleaseTagMixin.isBaseClassOf(item)) switch (item.releaseTag) {
|
|
215
|
+
case ReleaseTag.Beta: return "Beta";
|
|
216
|
+
case ReleaseTag.Alpha: return "Alpha";
|
|
217
|
+
case ReleaseTag.Internal: return "Internal";
|
|
218
|
+
default: return "Public";
|
|
219
|
+
}
|
|
220
|
+
return "Public";
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* True when the item carries the given TSDoc modifier tag (without the `@`).
|
|
224
|
+
*
|
|
225
|
+
* @public
|
|
226
|
+
*/
|
|
227
|
+
function hasModifier(item, tagName) {
|
|
228
|
+
if (item instanceof ApiDocumentedItem) return ((item.tsdocComment?.modifierTagSet)?.nodes || []).some((t) => t.tagName === `@${tagName}`);
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* `@see` block contents, flattened to prose (whitespace-normalized).
|
|
233
|
+
*
|
|
234
|
+
* @public
|
|
235
|
+
*/
|
|
236
|
+
function seeReferences(item) {
|
|
237
|
+
if (item instanceof ApiDocumentedItem) {
|
|
238
|
+
const tsdoc = item.tsdocComment;
|
|
239
|
+
const references = [];
|
|
240
|
+
for (const seeBlock of tsdoc?.seeBlocks || []) {
|
|
241
|
+
const text = plainText(seeBlock.content);
|
|
242
|
+
if (text.trim()) references.push({ text: text.replace(/\s+/g, " ").trim() });
|
|
243
|
+
}
|
|
244
|
+
return references;
|
|
245
|
+
}
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
//#endregion
|
|
250
|
+
export { Tsdoc_exports, deprecation, examples, hasModifier, params, plainText, releaseTag, returns, seeReferences, summary, toMarkdown };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) {
|
|
6
|
+
__defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
if (!no_symbols) {
|
|
12
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { __exportAll };
|