/
lib.tsx
174 lines (156 loc) · 5.5 KB
/
lib.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import {
Comment,
CommentDisplayPart,
DeclarationReflection,
Reflection,
ReflectionFlags,
SignatureReflection,
TypeParameterReflection,
} from "../../models";
import { assertNever, JSX } from "../../utils";
import type { DefaultThemeRenderContext } from "./default/DefaultThemeRenderContext";
export function stringify(data: unknown) {
if (typeof data === "bigint") {
return data.toString() + "n";
}
return JSON.stringify(data);
}
/**
* Insert word break tags ``<wbr>`` into the given string.
*
* Breaks the given string at ``_``, ``-`` and capital letters.
*
* @param str The string that should be split.
* @return The original string containing ``<wbr>`` tags where possible.
*/
export function wbr(str: string): (string | JSX.Element)[] {
// TODO surely there is a better way to do this, but I'm tired.
const ret: (string | JSX.Element)[] = [];
const re = /[\s\S]*?(?:[^_-][_-](?=[^_-])|[^A-Z](?=[A-Z][^A-Z]))/g;
let match: RegExpExecArray | null;
let i = 0;
while ((match = re.exec(str))) {
ret.push(match[0], <wbr />);
i += match[0].length;
}
ret.push(str.slice(i));
return ret;
}
export function join<T>(joiner: JSX.Children, list: readonly T[], cb: (x: T) => JSX.Children) {
const result: JSX.Children = [];
for (const item of list) {
if (result.length > 0) {
result.push(joiner);
}
result.push(cb(item));
}
return <>{result}</>;
}
export function renderFlags(flags: ReflectionFlags, comment: Comment | undefined) {
const allFlags = [...flags];
if (comment) {
allFlags.push(...Array.from(comment.modifierTags, (tag) => tag.replace(/@([a-z])/, (x) => x[1].toUpperCase())));
}
return (
<>
{allFlags.map((item) => (
<>
<code class={"tsd-tag ts-flag" + item}>{item}</code>{" "}
</>
))}
</>
);
}
export function classNames(names: Record<string, boolean | null | undefined>, extraCss?: string) {
const css = Object.keys(names)
.filter((key) => names[key])
.concat(extraCss || "")
.join(" ")
.trim()
.replace(/\s+/g, " ");
return css.length ? css : undefined;
}
export function hasTypeParameters(
reflection: Reflection
): reflection is Reflection & { typeParameters: TypeParameterReflection[] } {
return (
(reflection instanceof DeclarationReflection || reflection instanceof SignatureReflection) &&
reflection.typeParameters != null &&
reflection.typeParameters.length > 0
);
}
export function renderTypeParametersSignature(
typeParameters: readonly TypeParameterReflection[] | undefined
): JSX.Element {
return (
<>
{!!typeParameters && typeParameters.length > 0 && (
<>
<span class="tsd-signature-symbol">{"<"}</span>
{join(<span class="tsd-signature-symbol">{", "}</span>, typeParameters, (item) => (
<>
{item.varianceModifier ? `${item.varianceModifier} ` : ""}
<span class="tsd-signature-type" data-tsd-kind={item.kindString}>
{item.name}
</span>
</>
))}
<span class="tsd-signature-symbol">{">"}</span>
</>
)}
</>
);
}
export function camelToTitleCase(text: string) {
return text.substring(0, 1).toUpperCase() + text.substring(1).replace(/[a-z][A-Z]/g, (x) => `${x[0]} ${x[1]}`);
}
export function displayPartsToMarkdown(parts: CommentDisplayPart[], urlTo: DefaultThemeRenderContext["urlTo"]) {
const result: string[] = [];
for (const part of parts) {
switch (part.kind) {
case "text":
case "code":
result.push(part.text);
break;
case "inline-tag":
switch (part.tag) {
case "@label":
case "@inheritdoc": // Shouldn't happen
break; // Not rendered.
case "@link":
case "@linkcode":
case "@linkplain": {
if (part.target) {
const url = typeof part.target === "string" ? part.target : urlTo(part.target);
const wrap = part.tag === "@linkcode" ? "`" : "";
result.push(url ? `[${wrap}${part.text}${wrap}](${url})` : part.text);
} else {
result.push(part.text);
}
break;
}
default:
// Hmm... probably want to be able to render these somehow, so custom inline tags can be given
// special rendering rules. Future capability. For now, just render their text.
result.push(`{${part.tag} ${part.text}}`);
break;
}
break;
default:
assertNever(part);
}
}
return result.join("");
}
/**
* Renders the reflection name with an additional `?` if optional.
*/
export function renderName(refl: Reflection) {
if (!refl.name) {
return <em>{wbr(refl.kindString!)}</em>;
}
if (refl.flags.isOptional) {
return <>{wbr(refl.name)}?</>;
}
return wbr(refl.name);
}