Skip to content

Commit dc35a17

Browse files
committed
Implement a declaration import for typedoc output
1 parent a79f8d5 commit dc35a17

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed

src/lib/output/themes/default/DefaultThemeRenderContext.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { hierarchyTemplate } from "./templates/hierarchy.js";
3636
import { reflectionTemplate } from "./templates/reflection.js";
3737
import { typeDeclaration, typeDetails, typeDetailsIfUseful } from "./partials/typeDetails.js";
3838
import { moduleMemberSummary, moduleReflection } from "./partials/moduleReflection.js";
39+
import { declarationImport } from "./partials/declarationImport.js";
3940
import type { Router } from "../../router.js";
4041
import type { JSX, NeverIfInternal } from "#utils";
4142

@@ -159,6 +160,7 @@ export class DefaultThemeRenderContext {
159160
reflectionFlags = bind(reflectionFlags, this);
160161
footer = bind(footer, this);
161162
header = bind(header, this);
163+
declarationImport = bind(declarationImport, this);
162164
hierarchy = bind(hierarchy, this);
163165
index = bind(index, this);
164166
member = bind(member, this);
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { type Reflection, ReflectionKind } from "#models";
2+
import { JSX } from "#utils";
3+
import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext.js";
4+
5+
function getValidIdentifier(name: string) {
6+
const lastSlash = name.lastIndexOf("/");
7+
if (lastSlash) {
8+
return name.substring(lastSlash + 1).replace(/[^a-z0-9]/gi, "_");
9+
}
10+
return name.replace(/[^a-z0-9]/gi, "_");
11+
}
12+
13+
interface ImportInfo {
14+
packageName: string | undefined;
15+
importSpecifier: string;
16+
extra?: string;
17+
}
18+
19+
function getImportInfo(refl: Reflection): ImportInfo | undefined {
20+
if (refl.isDocument()) return;
21+
22+
// Reference is a module, produce a namespace import
23+
if (refl.kindOf(ReflectionKind.Project | ReflectionKind.Module)) {
24+
const packageName = refl.isProject() ? refl.packageName : refl.name;
25+
return {
26+
packageName,
27+
importSpecifier: `* as ${getValidIdentifier(packageName || refl.name)}`,
28+
};
29+
}
30+
31+
// Simple case, directly within a module
32+
if (refl.parent?.kindOf(ReflectionKind.Project | ReflectionKind.Module)) {
33+
return {
34+
packageName: refl.parent.isProject() ? refl.parent.packageName : refl.parent.name,
35+
importSpecifier: `{ ${getValidIdentifier(refl.name)} }`,
36+
};
37+
}
38+
39+
// More obnoxious case, inside a namespace somewhere
40+
const path: Reflection[] = [refl];
41+
let iter = refl;
42+
while (!iter.parent?.kindOf(ReflectionKind.Project | ReflectionKind.Project)) {
43+
iter = iter.parent!;
44+
path.push(iter);
45+
}
46+
47+
return {
48+
packageName: iter.parent.isProject() ? iter.parent.packageName : iter.parent.name,
49+
importSpecifier: `{ ${getValidIdentifier(path[path.length - 1].name)} }`,
50+
extra: "// " + path.reverse().map(r => r.name).join("."),
51+
};
52+
}
53+
54+
export function declarationImport(context: DefaultThemeRenderContext, refl: Reflection) {
55+
const info = getImportInfo(refl);
56+
57+
if (info?.packageName) {
58+
const code = [`import ${info.importSpecifier} from "${info.packageName}"`];
59+
if (info.extra) {
60+
code.push(info.extra);
61+
}
62+
const html = context.markdown([
63+
{ kind: "code", text: "```ts\n" + code.join("\n") + "\n```" },
64+
]);
65+
66+
return <JSX.Raw html={html} />;
67+
}
68+
}

src/lib/output/themes/default/partials/header.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export const header = (context: DefaultThemeRenderContext, props: PageEvent<Refl
4646
{context.reflectionFlags(props.model)}
4747
</h1>
4848
)}
49+
{renderTitle && context.declarationImport(props.model)}
4950
</div>
5051
);
5152
};

0 commit comments

Comments
 (0)