UNPKG

docusaurus-plugin-typedoc-api

Version:

Docusaurus plugin that provides source code API documentation powered by TypeDoc.

348 lines (331 loc) 15.4 kB
'use strict'; 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