Skip to content

Commit e67fa69

Browse files
authored
feat: generate API as TypeDoc docs (#1705)
* feat: generate API docs * refactor: simplify output location, updated instructions, allow TypeDoc config options to be passed * chore(deps): update yarn.lock
1 parent f01e8b6 commit e67fa69

File tree

7 files changed

+567
-5
lines changed

7 files changed

+567
-5
lines changed

docs/src/pages/reference/configuration/output.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,28 @@ Default Value: `en`.
386386

387387
Give you the possibility to set the locale for the mock generation. It is used by faker, see the list of available options [here](https://fakerjs.dev/guide/localization.html#available-locales). It should also be strongly typed using `defineConfig`.
388388

389+
### docs
390+
391+
Type: `Boolean | Object`.
392+
393+
Default Value: `false`.
394+
395+
Will generate API docs using [TypeDoc](https://typedoc.org/). by default these docs will be in Markdown format.
396+
397+
TypeDoc can be configured by passing the [options](https://typedoc.org/options/) to the `docs` object or by creating a config file e.g. `typedoc.config.mjs` in your project root (see the [config docs](https://typedoc.org/options/configuration/#options) for a full list of supported file names) or by passing a config filename to the `configPath` option below.
398+
399+
See the TypeDoc [configuration documentation](https://typedoc.org/options/) for more details.
400+
401+
The `docs` option can take some properties to customize the generation if you set it to an object. If you set it to `true`, the default options will be used.
402+
403+
When no output directory destination is specified in `config`, the file will be output to the `docs` directory by default.
404+
405+
#### configPath
406+
407+
Type: `String`.
408+
409+
Use to specify a TypeDoc config filename. This can be useful if your project already has a TypeDoc config for other docs.
410+
389411
### clean
390412

391413
Type: `Boolean | String[]`.

packages/core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
"@types/lodash.uniq": "^4.5.9",
2525
"@types/lodash.uniqby": "^4.7.9",
2626
"@types/lodash.uniqwith": "^4.5.9",
27-
"@types/micromatch": "^4.0.9"
27+
"@types/micromatch": "^4.0.9",
28+
"typedoc": "0.26.11"
2829
},
2930
"dependencies": {
3031
"@apidevtools/swagger-parser": "^10.1.0",

packages/core/src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
} from 'openapi3-ts/oas30';
1313
// @ts-ignore // FIXME when running `yarn test` getting `orval:test: ../core/src/types.ts(12,34): error TS7016: Could not find a declaration file for module 'swagger2openapi'. '/home/maxim/orval/node_modules/swagger2openapi/index.js' implicitly has an 'any' type.`
1414
import type swagger2openapi from 'swagger2openapi';
15+
import { TypeDocOptions } from 'typedoc';
1516

1617
export interface Options {
1718
output?: string | OutputOptions;
@@ -50,6 +51,7 @@ export type NormalizedOutputOptions = {
5051
client: OutputClient | OutputClientFunc;
5152
httpClient: OutputHttpClient;
5253
clean: boolean | string[];
54+
docs: boolean | OutputDocsOptions;
5355
prettier: boolean;
5456
tslint: boolean;
5557
biome: boolean;
@@ -174,6 +176,7 @@ export type OutputOptions = {
174176
client?: OutputClient | OutputClientFunc;
175177
httpClient?: OutputHttpClient;
176178
clean?: boolean | string[];
179+
docs?: boolean | OutputDocsOptions;
177180
prettier?: boolean;
178181
tslint?: boolean;
179182
biome?: boolean;
@@ -239,6 +242,10 @@ export const OutputMode = {
239242

240243
export type OutputMode = (typeof OutputMode)[keyof typeof OutputMode];
241244

245+
export type OutputDocsOptions = {
246+
configPath?: string;
247+
} & Partial<TypeDocOptions>;
248+
242249
// TODO: add support for other mock types (like cypress or playwright)
243250
export const OutputMockType = {
244251
MSW: 'msw',

packages/orval/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@
7575
"lodash.uniq": "^4.5.0",
7676
"openapi3-ts": "4.2.2",
7777
"string-argv": "^0.3.2",
78-
"tsconfck": "^2.0.1"
78+
"tsconfck": "^2.0.1",
79+
"typedoc": "0.26.11",
80+
"typedoc-plugin-markdown": "4.2.10",
81+
"typescript": "^5.6.3"
7982
}
8083
}

packages/orval/src/utils/options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export const normalizeOptions = async (
148148
mode: normalizeOutputMode(outputOptions.mode ?? mode),
149149
mock,
150150
clean: outputOptions.clean ?? clean ?? false,
151+
docs: outputOptions.docs ?? false,
151152
prettier: outputOptions.prettier ?? prettier ?? false,
152153
tslint: outputOptions.tslint ?? tslint ?? false,
153154
biome: outputOptions.biome ?? biome ?? false,

packages/orval/src/write-specs.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
import chalk from 'chalk';
1919
import execa from 'execa';
2020
import fs from 'fs-extra';
21+
import { Application, TypeDocOptions } from 'typedoc';
2122
import uniq from 'lodash.uniq';
2223
import { InfoObject } from 'openapi3-ts/oas30';
2324
import { executeHook } from './utils';
@@ -198,6 +199,45 @@ export const writeSpecs = async (
198199
}
199200
}
200201

202+
if (output.docs) {
203+
try {
204+
let config: Partial<TypeDocOptions> = {};
205+
let configPath: string | null = null;
206+
if (typeof output.docs === 'object') {
207+
({ configPath = null, ...config } = output.docs);
208+
if (configPath) {
209+
config.options = configPath;
210+
}
211+
}
212+
const app = await Application.bootstrapWithPlugins({
213+
entryPoints: paths,
214+
// Set the custom config location if it has been provided.
215+
...config,
216+
plugin: ['typedoc-plugin-markdown'],
217+
});
218+
// Set defaults if the have not been provided by the external config.
219+
if (!app.options.isSet('readme')) {
220+
app.options.setValue('readme', 'none');
221+
}
222+
if (!app.options.isSet('logLevel')) {
223+
app.options.setValue('logLevel', 'None');
224+
}
225+
const project = await app.convert();
226+
if (project) {
227+
await app.generateDocs(project, app.options.getValue('out'));
228+
} else {
229+
throw new Error('TypeDoc not initialised');
230+
}
231+
} catch (e: any) {
232+
const message =
233+
e.exitCode === 1
234+
? e.stdout + e.stderr
235+
: `⚠️ ${projectTitle ? `${projectTitle} - ` : ''}Unable to generate docs`;
236+
237+
log(chalk.yellow(message));
238+
}
239+
}
240+
201241
createSuccessMessage(projectTitle);
202242
};
203243

0 commit comments

Comments
 (0)