docusaurus-plugin-typedoc-api
Version:
Docusaurus plugin that provides source code API documentation powered by TypeDoc.
348 lines (331 loc) • 15.4 kB
JavaScript
;
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
const fs = require('node:fs');
const path = require('node:path');
const server = require('@docusaurus/plugin-content-docs/server');
const utils = require('@docusaurus/utils');
const data = require('./plugin/data.js');
const sidebar = require('./plugin/sidebar.js');
const version = require('./plugin/version.js');
const _interopDefault = e => e && e.__esModule ? e : {
default: e
};
const fs__default = /*#__PURE__*/_interopDefault(fs);
const path__default = /*#__PURE__*/_interopDefault(path);
/* eslint-disable no-console, sort-keys */
const DEFAULT_OPTIONS = {
banner: '',
breadcrumbs: true,
changelogName: 'CHANGELOG.md',
changelogs: false,
debug: false,
disableVersioning: false,
exclude: [],
gitRefName: 'master',
id: utils.DEFAULT_PLUGIN_ID,
includeCurrentVersion: true,
lastVersion: '',
minimal: false,
onlyIncludeVersions: [],
packageJsonName: 'package.json',
packages: [],
projectRoot: '.',
sortPackages: (a, d) => a.packageName.localeCompare(d.packageName),
sortSidebar: (a, d) => a.localeCompare(d),
readmeName: 'README.md',
readmes: false,
removeScopes: [],
routeBasePath: 'api',
tsconfigName: 'tsconfig.json',
typedocOptions: {},
remarkPlugins: [],
rehypePlugins: [],
versions: {}
};
async function importFile(file) {
const data = await fs__default.default.promises.readFile(file, 'utf8');
if (file.endsWith('.json')) {
return JSON.parse(data);
}
return data;
}
function typedocApiPlugin(context, pluginOptions) {
const options = _objectSpread(_objectSpread({}, DEFAULT_OPTIONS), pluginOptions);
const banner = options.banner,
breadcrumbs = options.breadcrumbs,
changelogs = options.changelogs,
pluginId = options.id,
gitRefName = options.gitRefName,
minimal = options.minimal,
projectRoot = options.projectRoot,
readmes = options.readmes,
removeScopes = options.removeScopes;
const isDefaultPluginId = pluginId === utils.DEFAULT_PLUGIN_ID;
const versionsMetadata = version.readVersionsMetadata(context, options);
const versionsDocsDir = version.getVersionedDocsDirPath(context.siteDir, pluginId);
// Determine entry points from configs
const entryPoints = [];
const packageConfigs = options.packages.map(pkgItem => {
const pkgConfig = typeof pkgItem === 'string' ? {
path: pkgItem
} : pkgItem;
const entries = {};
if (!pkgConfig.entry || typeof pkgConfig.entry === 'string') {
entries.index = {
label: 'Index',
path: pkgConfig.entry ? String(pkgConfig.entry) : 'src/index.ts'
};
} else {
Object.entries(pkgConfig.entry).forEach(([importPath, entryConfig]) => {
entries[importPath] = typeof entryConfig === 'string' ? {
label: 'Index',
path: entryConfig
} : entryConfig;
});
}
Object.values(entries).forEach(entryConfig => {
entryPoints.push(path__default.default.join(pkgConfig.path, entryConfig.path));
});
return {
entryPoints: entries,
packageRoot: path__default.default.normalize(path__default.default.join(projectRoot, pkgConfig.path || '.')),
packagePath: pkgConfig.path || '.',
packageSlug: pkgConfig.slug ?? path__default.default.basename(pkgConfig.path),
// Load later on
packageName: '',
packageVersion: ''
};
});
return {
name: 'docusaurus-plugin-typedoc-api',
extendCli(cli) {
const command = isDefaultPluginId ? 'api:version' : `api:version:${pluginId}`;
const commandDescription = isDefaultPluginId ? 'Tag a new API version' : `Tag a new API version (${pluginId})`;
cli.command(command).arguments('<version>').description(commandDescription).action(async version => {
const outDir = path__default.default.join(versionsDocsDir, `version-${version}`);
const prefix = isDefaultPluginId ? 'api' : pluginId;
console.log(`[${prefix}]:`, 'Generating docs...');
await data.generateJson(projectRoot, entryPoints, path__default.default.join(outDir, 'api-typedoc.json'), options);
console.log(`[${prefix}]:`, 'Persisting packages...');
// Load info from `package.json`s
packageConfigs.forEach(cfg => {
const _data$loadPackageJson = data.loadPackageJsonAndDocs(path__default.default.join(options.projectRoot, cfg.packagePath), options.packageJsonName, options.readmeName, options.changelogName),
packageJson = _data$loadPackageJson.packageJson;
// eslint-disable-next-line no-param-reassign
cfg.packageName = packageJson.name;
// eslint-disable-next-line no-param-reassign
cfg.packageVersion = packageJson.version;
});
await fs__default.default.promises.writeFile(path__default.default.join(outDir, 'api-packages.json'), JSON.stringify(packageConfigs), 'utf8');
console.log(`[${prefix}]:`, `version ${version} created!`);
});
},
async loadContent() {
const versionsMetadataList = await versionsMetadata;
return {
loadedVersions: await Promise.all(versionsMetadataList.map(async metadata => {
let packages = [];
// Current data needs to be generated on demand
if (metadata.versionName === server.CURRENT_VERSION_NAME) {
const outFile = path__default.default.join(context.generatedFilesDir, `api-typedoc-${pluginId}.json`);
await data.generateJson(projectRoot, entryPoints, outFile, options);
packages = data.flattenAndGroupPackages(packageConfigs, await importFile(outFile), metadata.versionPath, options);
// Versioned data is stored in the file system
} else {
const outDir = path__default.default.join(versionsDocsDir, `version-${metadata.versionName}`);
packages = data.flattenAndGroupPackages(await importFile(path__default.default.join(outDir, 'api-packages.json')), await importFile(path__default.default.join(outDir, 'api-typedoc.json')), metadata.versionPath, options, true);
}
packages.sort((a, d) => options.sortPackages(a, d));
// Generate sidebars (this runs before the main sidebar is loaded)
const sidebars = sidebar.extractSidebar(packages, removeScopes, changelogs, options.sortSidebar);
await fs__default.default.promises.writeFile(path__default.default.join(context.generatedFilesDir, `api-sidebar-${pluginId}-${metadata.versionName}.js`), `module.exports = ${JSON.stringify(sidebars, null, 2)};`);
await fs__default.default.promises.writeFile(path__default.default.join(context.generatedFilesDir, `api-sidebar-${pluginId}-${metadata.versionName}.d.ts`), `import type { SidebarConfig } from '@docusaurus/plugin-content-docs';\nexport = Array<SidebarConfig>;`);
return _objectSpread(_objectSpread({}, metadata), {}, {
packages,
sidebars
});
}))
};
},
async contentLoaded({
content,
actions
}) {
if (!content) {
return;
}
const docs = {};
// Create an index of versions for quick lookups.
// This is hacky, but it works, so shrug.
content.loadedVersions.forEach(loadedVersion => {
if (loadedVersion.versionName !== server.CURRENT_VERSION_NAME) {
docs[loadedVersion.versionName] = {
id: loadedVersion.versionPath,
title: loadedVersion.versionLabel,
description: loadedVersion.versionLabel
};
}
});
const rootRoutes = await Promise.all(content.loadedVersions.map(async loadedVersion => {
const version = loadedVersion.versionName;
// Define version metadata for all pages. We need to use the same structure as
// "docs" so that we can utilize the same React components.
// https://github.com/facebook/docusaurus/blob/master/packages/docusaurus-plugin-content-docs/src/index.ts#L337
const versionMetadata = await actions.createData(`version-${version}.json`, JSON.stringify({
badge: loadedVersion.versionBadge,
banner: loadedVersion.versionBanner,
className: loadedVersion.versionClassName,
docs,
docsSidebars: {
api: loadedVersion.sidebars
},
isLast: loadedVersion.isLast,
label: loadedVersion.versionLabel,
noIndex: false,
pluginId,
version: loadedVersion.versionName
}));
const packagesData = await actions.createData(`packages-${version}.json`, JSON.stringify(data.formatPackagesWithoutHostInfo(loadedVersion.packages)));
const optionsData = await actions.createData('options.json', JSON.stringify({
banner,
breadcrumbs,
gitRefName,
minimal,
pluginId,
scopes: removeScopes
}));
function createRoute(info, modules) {
return {
path: info.permalink,
exact: true,
component: path__default.default.join(__dirname, './components/ApiItem.js'),
modules,
sidebar: 'api',
// Map the ID here instead of creating a JSON data file,
// otherwise this will create thousands of files!
id: info.id
};
}
const routes = [];
loadedVersion.packages.forEach(pkg => {
pkg.entryPoints.forEach(entry => {
const children = entry.reflection.children?.filter(child => !child.permalink?.includes('#')) ?? [];
// Map a route for every declaration in the package (the exported APIs)
const subRoutes = children.map(child => createRoute(child));
// Map a top-level package route, otherwise `DocRoot` shows a page not found
subRoutes.push(createRoute(entry.reflection, entry.index && readmes && pkg.readmePath ? {
readme: pkg.readmePath
} : undefined));
if (entry.index && changelogs && pkg.changelogPath) {
subRoutes.push({
path: utils.normalizeUrl([entry.reflection.permalink, 'changelog']),
exact: true,
component: path__default.default.join(__dirname, './components/ApiChangelog.js'),
modules: {
changelog: pkg.changelogPath
},
sidebar: 'api'
});
}
routes.push(...subRoutes);
});
});
const indexPermalink = utils.normalizeUrl([loadedVersion.versionPath]);
if (loadedVersion.packages.length > 1) {
// Only write out the ApiIndex only when we have multiple packages
// otherwise we will have 2 top-level entries in the route entries
routes.push({
path: indexPermalink,
exact: true,
component: path__default.default.join(__dirname, './components/ApiIndex.js'),
modules: {
options: optionsData,
packages: packagesData,
versionMetadata
},
sidebar: 'api'
});
}
// Wrap in the `DocVersionRoot` component:
// https://github.com/facebook/docusaurus/blob/main/packages/docusaurus-plugin-content-docs/src/routes.ts#L192
return {
path: indexPermalink,
exact: false,
component: '@theme/DocVersionRoot',
routes: [{
path: indexPermalink,
exact: false,
component: path__default.default.join(__dirname, './components/ApiPage.js'),
routes,
modules: {
options: optionsData,
packages: packagesData
}
}],
modules: {
version: versionMetadata
},
priority: loadedVersion.routePriority
};
}));
// Wrap in the `DocsRoot` component:
// https://github.com/facebook/docusaurus/blob/main/packages/docusaurus-plugin-content-docs/src/routes.ts#L232
actions.addRoute({
path: utils.normalizeUrl([context.baseUrl, options.routeBasePath ?? 'api']),
exact: false,
component: '@theme/DocsRoot',
routes: rootRoutes
});
},
configureWebpack(config, isServer, utils) {
if (!readmes && !changelogs) {
return {};
}
// Whitelist the folders that this webpack rule applies to, otherwise we collide with the native
// docs/blog plugins. We need to include the specific files only, as in polyrepo mode, the `cfg.packagePath`
// can be project root (where the regular docs are too).
const include = packageConfigs.flatMap(cfg => {
const list = [];
if (readmes) {
list.push(path__default.default.join(options.projectRoot, cfg.packagePath, options.readmeName));
}
if (changelogs) {
list.push(path__default.default.join(options.projectRoot, cfg.packagePath, options.changelogName));
}
return list;
});
return {
module: {
rules: [{
test: /\.mdx?$/,
include,
use: [utils.getJSLoader({
isServer
}), {
loader: require.resolve('@docusaurus/mdx-loader'),
options: {
admonitions: true,
remarkPlugins: options.remarkPlugins,
rehypePlugins: options.rehypePlugins,
siteDir: context.siteDir,
staticDirs: [...context.siteConfig.staticDirectories, path__default.default.join(context.siteDir, 'static')],
// Since this isn't a doc/blog page, we can get
// away with it being a partial!
isMDXPartial: () => true,
markdownConfig: context.siteConfig.markdown
}
}, {
loader: path__default.default.resolve(__dirname, './markdownLoader.js')
}]
}]
}
};
}
};
}
module.exports = typedocApiPlugin;
//# sourceMappingURL=index.js.map