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
4441async 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
130110async 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 ( / ^ ( h t t p | f t p ) 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>© 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+ } ) ;
0 commit comments