@loopback/tsdocs
Version:
A package to generate api docs using Microsoft api-extractor and api-documenter
147 lines (130 loc) • 4.16 kB
text/typescript
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
// Node module: @loopback/tsdocs
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
import debugFactory from 'debug';
import fs from 'fs-extra';
import path from 'path';
import {
ApiDocsOptions,
DEFAULT_APIDOCS_EXTRACTION_PATH,
DEFAULT_APIDOCS_GENERATION_PATH,
getPackagesWithTsDocs,
getUnscopedPackageName,
LernaPackage,
} from './helper';
const debug = debugFactory('loopback:tsdocs');
/**
* Update markdown files generated by api-documenter to prepend Jekyll metadata
* and generate `apidocs/index.md`.
*
* @param options - Options for api docs
*/
export async function updateApiDocs(options: ApiDocsOptions = {}) {
options = Object.assign(
{
rootDir: process.cwd(),
apiDocsGenerationPath: DEFAULT_APIDOCS_GENERATION_PATH,
apiDocsExtractionPath: DEFAULT_APIDOCS_EXTRACTION_PATH,
generateDefaultPackageDoc: true,
},
options,
);
const packages = await getPackagesWithTsDocs(options.rootDir);
/* istanbul ignore if */
if (!packages.length) return;
const packagesByName: Record<string, LernaPackage> = {};
for (const pkg of packages) {
packagesByName[getUnscopedPackageName(pkg.name)] = pkg;
}
options.lernaPackages = packagesByName;
const found = await addJekyllMetadata(packages[0].rootPath, options);
if (found) {
// await generateIndex(packages, options);
}
}
/**
* Prepend Jekyll metadata to markdown files
*
* @param lernaRootDir - Root directory for the monorepo
* @param options - Options for api docs
*/
async function addJekyllMetadata(
lernaRootDir: string,
options: ApiDocsOptions,
) {
const apiDocsRoot = path.join(lernaRootDir, options.apiDocsGenerationPath!);
const exists = await fs.pathExists(apiDocsRoot);
if (!exists) {
console.error('No API docs found at %s.', apiDocsRoot);
return false;
}
const apiFiles = await fs.readdir(apiDocsRoot);
for (const f of apiFiles) {
/* istanbul ignore if */
if (!f.endsWith('.md')) continue;
const name = f.replace(/\.md$/, '');
const isPackage = f.match(/^[^\.]+.md$/) && f !== 'index.md';
/* istanbul ignore if */
if (!options.silent) {
// Only print the package level name
if (isPackage) {
console.log('Updating *.md files for %s', name);
}
}
const docFile = path.join(apiDocsRoot, f);
let doc = await fs.readFile(docFile, 'utf-8');
const pkgName = name.split('.')[0];
// Calculate the relative uri for the package
let relativeUri = `packages/${pkgName}`;
const pkg = options.lernaPackages?.[pkgName];
if (pkg != null) {
relativeUri = path
.relative(pkg.rootPath, pkg.location)
.split(path.sep) // On Windows, the relative path has `\`
.join('/');
}
const pkgUrl =
f === 'index.md'
? 'https://github.com/loopbackio/loopback-next'
: `https://github.com/loopbackio/loopback-next/tree/master/${relativeUri}`;
if (isPackage && options.generateDefaultPackageDoc) {
const modelFile = path.join(
path.join(
lernaRootDir,
options.apiDocsExtractionPath!,
`models/${name}.api.json`,
),
);
/**
* "kind": "Package",
* "canonicalReference": "@loopback/authentication",
* "docComment": "",
* "name": "@loopback/authentication",
*/
const model = await fs.readJson(modelFile, {encoding: 'utf-8'});
debug('Package %s', name, model);
if (model.kind === 'Package' && !model.docComment) {
const pkgDoc = `[${model.name}](${pkgUrl})`;
doc = doc.replace(
`## ${name} package`,
`## ${name} package\n\n${pkgDoc}`,
);
}
}
doc = `---
lang: en
title: 'API docs: ${name}'
keywords: LoopBack 4.0, LoopBack 4, Node.js, TypeScript, OpenAPI
sidebar: lb4_sidebar
editurl: ${pkgUrl}
permalink: /doc/en/lb4/apidocs.${name}.html
---
${doc}
`;
if (!options.dryRun) {
await fs.writeFile(docFile, doc, 'utf-8');
}
}
return true;
}