@alauda/doom
Version:
Doctor Doom making docs.
155 lines (154 loc) • 6.28 kB
JavaScript
import fs from 'node:fs/promises';
import path from 'node:path';
import { logger, serve } from '@rspress/core';
import { removeLeadingSlash } from '@rspress/shared';
import { Command } from 'commander';
import { cyan, yellow } from 'yoctocolors';
import { autoSidebar, } from '../plugins/index.js';
import { getPdfName } from '../shared/index.js';
import { pathExists, setNodeEnv } from '../utils/index.js';
import { generatePdf, } from './export-pdf-core/index.js';
import { loadConfig } from './load-config.js';
const collectPages = (sidebarItems, base) => {
const pages = new Map();
for (const item of sidebarItems) {
if ('link' in item && item.link) {
const link = removeLeadingSlash(item.link);
pages.set(link, {
key: link,
path: base + link + '.html?print',
title: item.text,
});
}
if ('items' in item) {
for (const page of collectPages(item.items, base)) {
if (!pages.has(page.key)) {
pages.set(page.key, page);
}
}
}
}
return [...pages.values()];
};
export const exportCommand = new Command('export')
.description('Export the documentation as PDF, `apis/**` and `*/apis/**` routes will be ignored automatically')
.argument('[root]', 'Root directory of the documentation')
.option('-H, --host [host]', 'Serve host name')
.option('-P, --port [port]', 'Serve port number', '4173')
.action(async function (root) {
setNodeEnv('production');
const { port, host, ...globalOptions } = this.optsWithGlobals();
let { config, configFilePath } = await loadConfig(root, {
...globalOptions,
export: true,
});
const outDir = config.outDir;
if (!(await pathExists(path.resolve(outDir, 'index.html'), 'file'))) {
logger.error('Please build the documentation first');
process.exit(1);
}
config = await autoSidebar(config, {
ignore: globalOptions.ignore,
export: true,
});
config.builderConfig.server.open = false;
// make sure it won't be overridden by `serve`
const themeConfig = { ...config.themeConfig };
logger.start('Serving...');
await serve({ config, configFilePath, host, port });
const tempDir = path.resolve(outDir, '.doom');
const commonOptions = {
tempDir,
port: port,
host: host || 'localhost',
outDir,
pdfOptions: {
margin: {
top: 40,
right: 40,
bottom: 40,
left: 40,
},
printBackground: true,
headerTemplate: /* HTML */ `<div
style="margin-top: -0.4cm; height: 70%; width: 100%; display: flex; justify-content: center; align-items: center; color: lightgray; border-bottom: solid lightgray 1px; font-size: 10px;"
>
<span class="title"></span>
</div>`,
footerTemplate: /* HTML */ `<div
style="margin-bottom: -0.4cm; height: 70%; width: 100%; display: flex; justify-content: flex-start; align-items: center; color: lightgray; border-top: solid lightgray 1px; font-size: 10px;"
></div>`,
},
printerOptions: {
ignoreHTTPSErrors: true,
initScripts: [
// eslint-disable-next-line @typescript-eslint/require-await
async () => {
localStorage.setItem('rspress-theme-appearance', 'light');
localStorage.setItem('rspress-visited', '1');
},
],
outlineContainerSelector: '.rspress-doc',
},
};
const exportPdf = async (sidebarItems, lang = config.lang, options) => {
const pages = collectPages(sidebarItems, config.base);
const pdfOptions = {
pages,
outFile: getPdfName(lang, config.userBase, config.title),
cleanupTempDir: false,
...commonOptions,
...options,
};
logger.start(`Exporting ${options ? 'custom' : 'all'} ${lang} language documents with ${pages.length} pages into ${yellow(pdfOptions.outFile)}...`);
return generatePdf(pdfOptions);
};
const findScopeFactory = (scope) => function findScope(sidebarItems) {
const found = new Set();
for (const item of sidebarItems) {
if (!('_fileKey' in item) || !item._fileKey) {
continue;
}
if (scope.includes(item._fileKey)) {
found.add(item);
}
if ('items' in item) {
const found_ = findScope(item.items);
if (found_?.size) {
found_.forEach((i) => found.add(i));
}
}
}
return found;
};
const exportItems = config.export || [];
const exportEntries = async (sidebarItems, allOutlines, lang = config.lang) => {
for (const item of exportItems) {
const found = findScopeFactory(item.flattenScope)(sidebarItems);
if (!found?.size) {
logger.warn(`Cannot find matched scope \`${cyan(item.scope.join(', '))}\` for lang ${lang}, skip exporting`);
continue;
}
const foundSidebarItems = [...found];
await exportPdf(foundSidebarItems, lang, {
allOutlines,
outFile: `${item.name || (foundSidebarItems.find((it) => it.link?.endsWith('/index')) ?? foundSidebarItems[0]).text}-${lang}.pdf`,
});
}
};
if (themeConfig.locales?.length) {
for (const { lang, sidebar } of themeConfig.locales) {
const sidebarItems = sidebar[config.lang === lang ? '/' : `/${lang}`];
const { allOutlines } = await exportPdf(sidebarItems, lang);
await exportEntries(sidebarItems, allOutlines, lang);
}
}
else {
const sidebarItems = themeConfig.sidebar['/'];
const { allOutlines } = await exportPdf(sidebarItems);
await exportEntries(sidebarItems, allOutlines);
}
await fs.rm(tempDir, { force: true, recursive: true });
logger.ready('Closing the server');
process.exit(0);
});