Skip to content

Commit 688e011

Browse files
committed
initial work to use html typedoc
1 parent 35b5fc1 commit 688e011

File tree

5 files changed

+190
-127
lines changed

5 files changed

+190
-127
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ pnpm-debug.log*
1919

2020
# macOS-specific files
2121
.DS_Store
22+
/public/api-reference
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading

packages/js-api-generator/build.ts

Lines changed: 181 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,41 @@
1-
import {
2-
Application,
3-
DeclarationReflection,
4-
Options,
5-
PageEvent,
6-
Reflection,
7-
SignatureReflection,
8-
TSConfigReader,
9-
type TypeDocOptions,
10-
} from 'typedoc';
11-
import {
12-
MarkdownPageEvent,
13-
MarkdownTheme,
14-
MarkdownThemeContext,
15-
type MarkdownApplication,
16-
type PluginOptions,
17-
} from 'typedoc-plugin-markdown';
18-
import { existsSync } from 'node:fs';
19-
20-
const typeDocConfigBaseOptions: Partial<TypeDocOptions | PluginOptions> = {
1+
import { Application, TSConfigReader, LogLevel, DefaultTheme, type TypeDocOptions } from 'typedoc';
2+
import { existsSync, mkdirSync, writeFileSync, readdirSync } from 'node:fs';
3+
import { resolve, join, dirname } from 'node:path';
4+
import { fileURLToPath } from 'node:url';
5+
6+
const __filename = fileURLToPath(import.meta.url);
7+
const __dirname = dirname(__filename);
8+
9+
const BASE_OUTPUT_DIR = resolve(__dirname, '../../public/api-reference');
10+
11+
const typeDocConfigBaseOptions: Partial<TypeDocOptions> = {
2112
// TypeDoc options
2213
// https://typedoc.org/options/
2314
githubPages: false,
2415
hideGenerator: true,
2516
theme: 'tauri-theme',
26-
plugin: ['typedoc-plugin-mdn-links', 'typedoc-plugin-markdown'],
17+
plugin: ['typedoc-plugin-mdn-links'],
2718
readme: 'none',
28-
logLevel: 'Warn',
29-
parametersFormat: 'table',
30-
// typedoc-plugin-markdown options
31-
// https://github.com/tgreyuk/typedoc-plugin-markdown/blob/next/packages/typedoc-plugin-markdown/docs/usage/options.md
32-
outputFileStrategy: 'modules',
33-
flattenOutputFiles: true,
34-
entryFileName: 'index.md',
35-
hidePageHeader: true,
36-
hidePageTitle: true,
37-
hideBreadcrumbs: true,
38-
useCodeBlocks: true,
39-
propertiesFormat: 'table',
40-
typeDeclarationFormat: 'table',
41-
useHTMLAnchors: true,
19+
logLevel: LogLevel.Warn,
20+
includeVersion: true,
21+
searchInComments: true,
22+
navigationLinks: {
23+
'Tauri Docs': 'https://tauri.app',
24+
GitHub: 'https://github.com/tauri-apps/tauri',
25+
},
26+
visibilityFilters: {
27+
protected: true,
28+
private: false,
29+
inherited: true,
30+
external: false,
31+
},
32+
categorizeByGroup: true,
33+
cleanOutputDir: true,
34+
// disableSources: false,
35+
sourceLinkTemplate:
36+
'https://github.com/tauri-apps/{repository}/blob/{gitRevision}/{path}#L{line}',
37+
sort: ['source-order'],
38+
highlightLanguages: ['typescript', 'javascript', 'json', 'bash', 'shell', 'rust', 'toml'],
4239
};
4340

4441
async function generator() {
@@ -47,8 +44,8 @@ async function generator() {
4744
entryPoints: ['../tauri/packages/api/src/index.ts'],
4845
tsconfig: '../tauri/packages/api/tsconfig.json',
4946
gitRevision: 'dev',
50-
publicPath: '/reference/javascript/api/',
51-
basePath: '/reference/javascript/api/',
47+
out: join(BASE_OUTPUT_DIR, 'core'),
48+
name: 'Tauri Core API',
5249
...typeDocConfigBaseOptions,
5350
};
5451

@@ -88,119 +85,182 @@ async function generator() {
8885
];
8986

9087
if (existsSync('../plugins-workspace/node_modules')) {
91-
plugins.forEach(async (plugin) => {
88+
const pluginsPromises = plugins.map(async (plugin) => {
9289
const pluginJsOptions: Partial<TypeDocOptions> = {
9390
entryPoints: [`../plugins-workspace/plugins/${plugin}/guest-js/index.ts`],
9491
tsconfig: `../plugins-workspace/plugins/${plugin}/tsconfig.json`,
9592
gitRevision: 'v2',
96-
publicPath: `/reference/javascript/`,
97-
basePath: `/reference/javascript/`,
93+
out: join(BASE_OUTPUT_DIR, 'plugins', plugin),
94+
name: `@tauri-apps/plugin-${plugin}`,
9895
...typeDocConfigBaseOptions,
99-
// Must go after to override base
100-
entryFileName: `${plugin}.md`,
10196
};
10297

103-
await generateDocs(pluginJsOptions);
98+
return generateDocs(pluginJsOptions);
10499
});
105-
} else {
106-
console.log(
107-
'Plugins workspace submodule is not initialized, respective API routes will not be rendered.'
108-
);
109-
}
110-
111-
if (existsSync('../tauri/packages/api/node_modules')) {
112-
const coreJsOptions: Partial<TypeDocOptions> = {
113-
entryPoints: ['../tauri/packages/api/src/index.ts'],
114-
tsconfig: '../tauri/packages/api/tsconfig.json',
115-
gitRevision: 'dev',
116-
publicPath: '/reference/javascript/api/',
117-
basePath: '/reference/javascript/api/',
118-
...typeDocConfigBaseOptions,
119-
};
120100

121-
await generateDocs(coreJsOptions);
101+
await Promise.all(pluginsPromises);
122102
} else {
123103
console.log(
124-
'Tauri V2 submodule is not initialized, respective API routes will not be rendered.'
104+
'Plugins workspace submodule is not initialized, respective API routes will not be rendered.'
125105
);
126106
}
107+
await generateIndexPage();
127108
}
128109

129-
// Adapted from https://github.com/HiDeoo/starlight-typedoc
130110
async function generateDocs(options: Partial<TypeDocOptions>) {
131-
const outputDir = `../../src/content/docs${options.publicPath}`;
111+
console.log(`Generating docs for ${options.name || 'unknown'}`);
132112

113+
const outDir = options.out as string;
114+
if (!existsSync(outDir)) {
115+
mkdirSync(outDir, { recursive: true });
116+
}
133117
const app = await Application.bootstrapWithPlugins(options);
134118
app.options.addReader(new TSConfigReader());
135-
// @ts-ignore
136-
app.renderer.defineTheme('tauri-theme', TauriTheme);
137119

138-
app.renderer.on(PageEvent.END, (event: PageEvent<DeclarationReflection>) => {
139-
pageEventEnd(event);
140-
});
120+
options.theme = 'tauri-theme';
121+
app.renderer.defineTheme('tauri-theme', TauriDefaultTheme);
141122

142123
const project = await app.convert();
143-
144-
if (project) {
145-
await app.generateDocs(project, outputDir);
124+
if (!project) {
125+
throw new Error(`Failed to convert project: ${options.name}`);
146126
}
147-
}
148127

149-
// Adds frontmatter to the top of the file
150-
// Adapted from https://github.com/HiDeoo/starlight-typedoc
151-
function pageEventEnd(event: PageEvent<DeclarationReflection>) {
152-
if (!event.contents) {
153-
return;
154-
}
155-
const frontmatter = [
156-
'---',
157-
`title: "${event.model.name}"`,
158-
'editUrl: false',
159-
'sidebar:',
160-
` label: "${event.model.name.replace('@tauri-apps/plugin-', '')}"`,
161-
'tableOfContents:',
162-
' maxHeadingLevel: 5',
163-
'---',
164-
'',
165-
event.contents,
166-
];
167-
event.contents = frontmatter.join('\n');
128+
await app.generateDocs(project, outDir);
168129
}
169-
class TauriThemeRenderContext extends MarkdownThemeContext {
170-
constructor(theme: MarkdownTheme, page: MarkdownPageEvent<Reflection>, options: Options) {
171-
super(theme, page, options);
172-
this.partials = {
173-
...this.partials,
174-
// Formats `@source` to be a single line
175-
sources: (model: DeclarationReflection | SignatureReflection, options: object) => {
176-
if (!model.sources) {
177-
return '';
178-
}
179-
let label = model.sources.length > 1 ? '**Sources**: ' : '**Source**: ';
180-
const sources = model.sources.map((source) => `${source.url}`);
181-
return label + sources.join(', ');
182-
},
183-
};
184-
}
185130

186-
// Adapted from https://github.com/HiDeoo/starlight-typedoc/blob/d95072e218004276942a5132ec8a4e3561425903/packages/starlight-typedoc/src/libs/theme.ts#L28
187-
override getRelativeUrl = (url: string) => {
188-
if (/^(http|ftp)s?:\/\//.test(url)) {
189-
return url;
190-
}
131+
async function generateIndexPage() {
132+
const indexPath = join(BASE_OUTPUT_DIR, 'index.html');
191133

192-
url = decodeURI(
193-
super.getRelativeUrl(url).replaceAll('.md', '/').replaceAll('.', '').toLowerCase()
194-
).replaceAll('\\', '/');
195-
return url;
134+
const cardTemplate = (name: string, path: string) => {
135+
return ` <article>
136+
<hgroup>
137+
<h4>${name}</h4>
138+
<a href="${path}">View</a>
139+
</hgroup>
140+
</article>`;
196141
};
197-
}
198142

199-
// Overrides and extensions based on https://github.com/tgreyuk/typedoc-plugin-markdown/blob/next/packages/typedoc-plugin-markdown/docs/usage/customizing.md
200-
class TauriTheme extends MarkdownTheme {
201-
getRenderContext(page: MarkdownPageEvent<Reflection>): MarkdownThemeContext {
202-
return new TauriThemeRenderContext(this, page, this.application.options);
143+
let pluginsGridHtml;
144+
if (existsSync(join(BASE_OUTPUT_DIR, 'plugins'))) {
145+
const pluginDirs = readdirSync(join(BASE_OUTPUT_DIR, 'plugins'));
146+
147+
pluginsGridHtml = pluginDirs
148+
.map((plugin: string) => cardTemplate(plugin, `./plugins/${plugin}/index.html`))
149+
.join('');
150+
}
151+
152+
// TODO: move this to a file and improve layout
153+
// TODO: copy assets to the output directory
154+
// TODO: improve theme switcher
155+
// TODO: link to docs
156+
// TODO: make docs link to here
157+
const indexContent = `
158+
<!DOCTYPE html>
159+
<html lang="en">
160+
<head>
161+
<meta charset="UTF-8">
162+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
163+
<meta name="color-scheme" content="light dark">
164+
<title>Tauri JS API Reference</title>
165+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.slate.min.css">
166+
<style>
167+
.grid {
168+
--grid-min-value: 16rem;
169+
grid-template-columns: repeat(auto-fit, minmax(var(--grid-min-value), 1fr));
170+
}
171+
.tauri-logo {
172+
height: 2rem;
173+
vertical-align: middle;
174+
margin-right: 0.5rem;
175+
}
176+
.logo-light { display: none; }
177+
.logo-dark { display: none; }
178+
@media (prefers-color-scheme: dark) {
179+
.logo-dark { display: inline; }
180+
.logo-light { display: none; }
181+
}
182+
@media (prefers-color-scheme: light), (prefers-color-scheme: no-preference) {
183+
.logo-light { display: inline; }
184+
.logo-dark { display: none; }
185+
}
186+
[data-theme="dark"] .logo-dark { display: inline; }
187+
[data-theme="dark"] .logo-light { display: none; }
188+
[data-theme="light"] .logo-light { display: inline; }
189+
[data-theme="light"] .logo-dark { display: none; }
190+
</style>
191+
</head>
192+
<body>
193+
<header class="container">
194+
<nav>
195+
<ul>
196+
<li>
197+
<img src="./assets/logo_light.svg" alt="Tauri Logo" class="tauri-logo logo-light" loading="lazy">
198+
<img src="./assets/logo.svg" alt="Tauri Logo" class="tauri-logo logo-dark" loading="lazy">
199+
</li>
200+
</ul>
201+
<ul>
202+
<li>
203+
<select id="theme-switcher" aria-label="Theme">
204+
<option value="light">Light</option>
205+
<option value="dark">Dark</option>
206+
<option value="auto" selected>Auto</option>
207+
</select>
208+
</li>
209+
</ul>
210+
</nav>
211+
</header>
212+
<main class="container">
213+
<hgroup>
214+
<h1>Javascript Reference</h1>
215+
<p>API reference for Tauri core and plugins</p>
216+
</hgroup>
217+
<section>
218+
<div>${cardTemplate('Tauri Core API', './core/index.html')}</div>
219+
<h3>Plugins</h3>
220+
<div class="grid">
221+
${pluginsGridHtml}
222+
</div>
223+
</section>
224+
</main>
225+
<footer class="container">
226+
<small>&copy; 2025 Tauri Apps. All rights reserved.</small>
227+
</footer>
228+
<script>
229+
const themeSwitcher = document.getElementById('theme-switcher');
230+
function setTheme(theme) {
231+
if (theme === 'auto') {
232+
document.documentElement.removeAttribute('data-theme');
233+
localStorage.removeItem('theme');
234+
} else {
235+
document.documentElement.setAttribute('data-theme', theme);
236+
localStorage.setItem('theme', theme);
237+
}
238+
}
239+
themeSwitcher.value = localStorage.getItem('theme') || 'auto';
240+
setTheme(themeSwitcher.value);
241+
themeSwitcher.addEventListener('change', (e) => {
242+
setTheme(e.target.value);
243+
});
244+
</script>
245+
</body>
246+
</html>
247+
`;
248+
249+
const cssDir = join(BASE_OUTPUT_DIR, 'assets');
250+
if (!existsSync(cssDir)) {
251+
mkdirSync(cssDir, { recursive: true });
252+
}
253+
try {
254+
writeFileSync(indexPath, indexContent);
255+
console.log(`Generated index page at ${indexPath}`);
256+
} catch (error) {
257+
console.error('Failed to write index files:', error);
203258
}
204259
}
205260

206-
generator();
261+
class TauriDefaultTheme extends DefaultTheme {}
262+
263+
generator().catch((error) => {
264+
console.error('Failed to generate documentation:', error);
265+
process.exit(1);
266+
});
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"name": "js-api-generator",
3-
"version": "1.0.0",
3+
"version": "2.0.0",
44
"description": "",
5-
"main": "index.js",
5+
"type": "module",
6+
"main": "build.js",
67
"scripts": {
78
"build": "tsm ./build.ts"
89
},
@@ -11,10 +12,9 @@
1112
"license": "MIT",
1213
"private": "true",
1314
"dependencies": {
14-
"github-slugger": "^2.0.0",
1515
"tsm": "^2.3.0",
16-
"typedoc": "0.26.6",
17-
"typedoc-plugin-markdown": "4.2.6",
18-
"typedoc-plugin-mdn-links": "3.2.11"
16+
"typedoc": "^0.28.7",
17+
"typedoc-plugin-mdn-links": "^5.0.5",
18+
"typescript": "5.4.5"
1919
}
2020
}

0 commit comments

Comments
 (0)