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 > = {
21
12
// TypeDoc options
22
13
// https://typedoc.org/options/
23
14
githubPages : false ,
24
15
hideGenerator : true ,
25
16
theme : 'tauri-theme' ,
26
- plugin : [ 'typedoc-plugin-mdn-links' , 'typedoc-plugin-markdown' ] ,
17
+ plugin : [ 'typedoc-plugin-mdn-links' ] ,
27
18
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' ] ,
42
39
} ;
43
40
44
41
async function generator ( ) {
@@ -47,8 +44,8 @@ async function generator() {
47
44
entryPoints : [ '../tauri/packages/api/src/index.ts' ] ,
48
45
tsconfig : '../tauri/packages/api/tsconfig.json' ,
49
46
gitRevision : 'dev' ,
50
- publicPath : '/reference/javascript/api/' ,
51
- basePath : '/reference/javascript/api/ ' ,
47
+ out : join ( BASE_OUTPUT_DIR , 'core' ) ,
48
+ name : 'Tauri Core API ' ,
52
49
...typeDocConfigBaseOptions ,
53
50
} ;
54
51
@@ -88,119 +85,182 @@ async function generator() {
88
85
] ;
89
86
90
87
if ( existsSync ( '../plugins-workspace/node_modules' ) ) {
91
- plugins . forEach ( async ( plugin ) => {
88
+ const pluginsPromises = plugins . map ( async ( plugin ) => {
92
89
const pluginJsOptions : Partial < TypeDocOptions > = {
93
90
entryPoints : [ `../plugins-workspace/plugins/${ plugin } /guest-js/index.ts` ] ,
94
91
tsconfig : `../plugins-workspace/plugins/${ plugin } /tsconfig.json` ,
95
92
gitRevision : 'v2' ,
96
- publicPath : `/reference/javascript/` ,
97
- basePath : `/reference/javascript/ ` ,
93
+ out : join ( BASE_OUTPUT_DIR , 'plugins' , plugin ) ,
94
+ name : `@tauri-apps/plugin- ${ plugin } ` ,
98
95
...typeDocConfigBaseOptions ,
99
- // Must go after to override base
100
- entryFileName : `${ plugin } .md` ,
101
96
} ;
102
97
103
- await generateDocs ( pluginJsOptions ) ;
98
+ return generateDocs ( pluginJsOptions ) ;
104
99
} ) ;
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
- } ;
120
100
121
- await generateDocs ( coreJsOptions ) ;
101
+ await Promise . all ( pluginsPromises ) ;
122
102
} else {
123
103
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.'
125
105
) ;
126
106
}
107
+ await generateIndexPage ( ) ;
127
108
}
128
109
129
- // Adapted from https://github.com/HiDeoo/starlight-typedoc
130
110
async function generateDocs ( options : Partial < TypeDocOptions > ) {
131
- const outputDir = `../../src/content/docs ${ options . publicPath } ` ;
111
+ console . log ( `Generating docs for ${ options . name || 'unknown' } ` ) ;
132
112
113
+ const outDir = options . out as string ;
114
+ if ( ! existsSync ( outDir ) ) {
115
+ mkdirSync ( outDir , { recursive : true } ) ;
116
+ }
133
117
const app = await Application . bootstrapWithPlugins ( options ) ;
134
118
app . options . addReader ( new TSConfigReader ( ) ) ;
135
- // @ts -ignore
136
- app . renderer . defineTheme ( 'tauri-theme' , TauriTheme ) ;
137
119
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 ) ;
141
122
142
123
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 } ` ) ;
146
126
}
147
- }
148
127
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 ) ;
168
129
}
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
- }
185
130
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' ) ;
191
133
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>` ;
196
141
} ;
197
- }
198
142
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 ) ;
203
258
}
204
259
}
205
260
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