Files
documentation/node_modules/@docusaurus/plugin-content-docs/lib/docs.js
2024-03-22 03:47:51 +05:30

295 lines
13 KiB
JavaScript

"use strict";
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDocsByIdIndex = exports.toCategoryIndexMatcherParam = exports.isCategoryIndex = exports.getMainDocId = exports.addDocNavigation = exports.processDocMetadata = exports.readVersionDocs = exports.readDocFile = void 0;
const tslib_1 = require("tslib");
const path_1 = tslib_1.__importDefault(require("path"));
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const lodash_1 = tslib_1.__importDefault(require("lodash"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const utils_1 = require("@docusaurus/utils");
const lastUpdate_1 = require("./lastUpdate");
const slug_1 = tslib_1.__importDefault(require("./slug"));
const numberPrefix_1 = require("./numberPrefix");
const frontMatter_1 = require("./frontMatter");
const utils_2 = require("./sidebars/utils");
async function readLastUpdateData(filePath, options, lastUpdateFrontMatter) {
const { showLastUpdateAuthor, showLastUpdateTime } = options;
if (showLastUpdateAuthor || showLastUpdateTime) {
const frontMatterTimestamp = lastUpdateFrontMatter?.date
? new Date(lastUpdateFrontMatter.date).getTime() / 1000
: undefined;
if (lastUpdateFrontMatter?.author && lastUpdateFrontMatter.date) {
return {
lastUpdatedAt: frontMatterTimestamp,
lastUpdatedBy: lastUpdateFrontMatter.author,
};
}
// Use fake data in dev for faster development.
const fileLastUpdateData = process.env.NODE_ENV === 'production'
? await (0, lastUpdate_1.getFileLastUpdate)(filePath)
: {
author: 'Author',
timestamp: 1539502055,
};
const { author, timestamp } = fileLastUpdateData ?? {};
return {
lastUpdatedBy: showLastUpdateAuthor
? lastUpdateFrontMatter?.author ?? author
: undefined,
lastUpdatedAt: showLastUpdateTime
? frontMatterTimestamp ?? timestamp
: undefined,
};
}
return {};
}
async function readDocFile(versionMetadata, source) {
const contentPath = await (0, utils_1.getFolderContainingFile)((0, utils_1.getContentPathList)(versionMetadata), source);
const filePath = path_1.default.join(contentPath, source);
const content = await fs_extra_1.default.readFile(filePath, 'utf-8');
return { source, content, contentPath, filePath };
}
exports.readDocFile = readDocFile;
async function readVersionDocs(versionMetadata, options) {
const sources = await (0, utils_1.Globby)(options.include, {
cwd: versionMetadata.contentPath,
ignore: options.exclude,
});
return Promise.all(sources.map((source) => readDocFile(versionMetadata, source)));
}
exports.readVersionDocs = readVersionDocs;
async function doProcessDocMetadata({ docFile, versionMetadata, context, options, env, }) {
const { source, content, contentPath, filePath } = docFile;
const { siteDir, i18n, siteConfig: { markdown: { parseFrontMatter }, }, } = context;
const { frontMatter: unsafeFrontMatter, contentTitle, excerpt, } = await (0, utils_1.parseMarkdownFile)({
filePath,
fileContent: content,
parseFrontMatter,
});
const frontMatter = (0, frontMatter_1.validateDocFrontMatter)(unsafeFrontMatter);
const { custom_edit_url: customEditURL,
// Strip number prefixes by default
// (01-MyFolder/01-MyDoc.md => MyFolder/MyDoc)
// but allow to disable this behavior with front matter
parse_number_prefixes: parseNumberPrefixes = true, last_update: lastUpdateFrontMatter, } = frontMatter;
const lastUpdate = await readLastUpdateData(filePath, options, lastUpdateFrontMatter);
// E.g. api/plugins/myDoc -> myDoc; myDoc -> myDoc
const sourceFileNameWithoutExtension = path_1.default.basename(source, path_1.default.extname(source));
// E.g. api/plugins/myDoc -> api/plugins; myDoc -> .
const sourceDirName = path_1.default.dirname(source);
const { filename: unprefixedFileName, numberPrefix } = parseNumberPrefixes
? options.numberPrefixParser(sourceFileNameWithoutExtension)
: { filename: sourceFileNameWithoutExtension, numberPrefix: undefined };
const baseID = frontMatter.id ?? unprefixedFileName;
if (baseID.includes('/')) {
throw new Error(`Document id "${baseID}" cannot include slash.`);
}
// For autogenerated sidebars, sidebar position can come from filename number
// prefix or front matter
const sidebarPosition = frontMatter.sidebar_position ?? numberPrefix;
// TODO legacy retrocompatibility
// I think it's bad to affect the front matter id with the dirname?
function computeDirNameIdPrefix() {
if (sourceDirName === '.') {
return undefined;
}
// Eventually remove the number prefixes from intermediate directories
return parseNumberPrefixes
? (0, numberPrefix_1.stripPathNumberPrefixes)(sourceDirName, options.numberPrefixParser)
: sourceDirName;
}
const id = [computeDirNameIdPrefix(), baseID].filter(Boolean).join('/');
const docSlug = (0, slug_1.default)({
baseID,
source,
sourceDirName,
frontMatterSlug: frontMatter.slug,
stripDirNumberPrefixes: parseNumberPrefixes,
numberPrefixParser: options.numberPrefixParser,
});
// Note: the title is used by default for page title, sidebar label,
// pagination buttons... frontMatter.title should be used in priority over
// contentTitle (because it can contain markdown/JSX syntax)
const title = frontMatter.title ?? contentTitle ?? baseID;
const description = frontMatter.description ?? excerpt ?? '';
const permalink = (0, utils_1.normalizeUrl)([versionMetadata.path, docSlug]);
function getDocEditUrl() {
const relativeFilePath = path_1.default.relative(contentPath, filePath);
if (typeof options.editUrl === 'function') {
return options.editUrl({
version: versionMetadata.versionName,
versionDocsDirPath: (0, utils_1.posixPath)(path_1.default.relative(siteDir, versionMetadata.contentPath)),
docPath: (0, utils_1.posixPath)(relativeFilePath),
permalink,
locale: context.i18n.currentLocale,
});
}
else if (typeof options.editUrl === 'string') {
const isLocalized = contentPath === versionMetadata.contentPathLocalized;
const baseVersionEditUrl = isLocalized && options.editLocalizedFiles
? versionMetadata.editUrlLocalized
: versionMetadata.editUrl;
return (0, utils_1.getEditUrl)(relativeFilePath, baseVersionEditUrl);
}
return undefined;
}
const draft = (0, utils_1.isDraft)({ env, frontMatter });
const unlisted = (0, utils_1.isUnlisted)({ env, frontMatter });
const formatDate = (locale, date, calendar) => {
try {
return new Intl.DateTimeFormat(locale, {
day: 'numeric',
month: 'short',
year: 'numeric',
timeZone: 'UTC',
calendar,
}).format(date);
}
catch (err) {
logger_1.default.error `Can't format docs lastUpdatedAt date "${String(date)}"`;
throw err;
}
};
// Assign all of object properties during instantiation (if possible) for
// NodeJS optimization.
// Adding properties to object after instantiation will cause hidden
// class transitions.
return {
id,
title,
description,
source: (0, utils_1.aliasedSitePath)(filePath, siteDir),
sourceDirName,
slug: docSlug,
permalink,
draft,
unlisted,
editUrl: customEditURL !== undefined ? customEditURL : getDocEditUrl(),
tags: (0, utils_1.normalizeFrontMatterTags)(versionMetadata.tagsPath, frontMatter.tags),
version: versionMetadata.versionName,
lastUpdatedBy: lastUpdate.lastUpdatedBy,
lastUpdatedAt: lastUpdate.lastUpdatedAt,
formattedLastUpdatedAt: lastUpdate.lastUpdatedAt
? formatDate(i18n.currentLocale, new Date(lastUpdate.lastUpdatedAt * 1000), i18n.localeConfigs[i18n.currentLocale].calendar)
: undefined,
sidebarPosition,
frontMatter,
};
}
async function processDocMetadata(args) {
try {
return await doProcessDocMetadata(args);
}
catch (err) {
throw new Error(`Can't process doc metadata for doc at path path=${args.docFile.filePath} in version name=${args.versionMetadata.versionName}`, { cause: err });
}
}
exports.processDocMetadata = processDocMetadata;
function getUnlistedIds(docs) {
return new Set(docs.filter((doc) => doc.unlisted).map((doc) => doc.id));
}
function addDocNavigation({ docs, sidebarsUtils, }) {
const docsById = createDocsByIdIndex(docs);
const unlistedIds = getUnlistedIds(docs);
// Add sidebar/next/previous to the docs
function addNavData(doc) {
const navigation = sidebarsUtils.getDocNavigation({
docId: doc.id,
displayedSidebar: doc.frontMatter.displayed_sidebar,
unlistedIds,
});
const toNavigationLinkByDocId = (docId, type) => {
if (!docId) {
return undefined;
}
const navDoc = docsById[docId];
if (!navDoc) {
// This could only happen if user provided the ID through front matter
throw new Error(`Error when loading ${doc.id} in ${doc.sourceDirName}: the pagination_${type} front matter points to a non-existent ID ${docId}.`);
}
// Gracefully handle explicitly providing an unlisted doc ID in production
if (navDoc.unlisted) {
return undefined;
}
return (0, utils_2.toDocNavigationLink)(navDoc);
};
const previous = doc.frontMatter.pagination_prev !== undefined
? toNavigationLinkByDocId(doc.frontMatter.pagination_prev, 'prev')
: (0, utils_2.toNavigationLink)(navigation.previous, docsById);
const next = doc.frontMatter.pagination_next !== undefined
? toNavigationLinkByDocId(doc.frontMatter.pagination_next, 'next')
: (0, utils_2.toNavigationLink)(navigation.next, docsById);
return { ...doc, sidebar: navigation.sidebarName, previous, next };
}
const docsWithNavigation = docs.map(addNavData);
// Sort to ensure consistent output for tests
docsWithNavigation.sort((a, b) => a.id.localeCompare(b.id));
return docsWithNavigation;
}
exports.addDocNavigation = addDocNavigation;
/**
* The "main doc" is the "version entry point"
* We browse this doc by clicking on a version:
* - the "home" doc (at '/docs/')
* - the first doc of the first sidebar
* - a random doc (if no docs are in any sidebar... edge case)
*/
function getMainDocId({ docs, sidebarsUtils, }) {
function getMainDoc() {
const versionHomeDoc = docs.find((doc) => doc.slug === '/');
const firstDocIdOfFirstSidebar = sidebarsUtils.getFirstDocIdOfFirstSidebar();
if (versionHomeDoc) {
return versionHomeDoc;
}
else if (firstDocIdOfFirstSidebar) {
return docs.find((doc) => doc.id === firstDocIdOfFirstSidebar);
}
return docs[0];
}
return getMainDoc().id;
}
exports.getMainDocId = getMainDocId;
// By convention, Docusaurus considers some docs are "indexes":
// - index.md
// - readme.md
// - <folder>/<folder>.md
//
// This function is the default implementation of this convention
//
// Those index docs produce a different behavior
// - Slugs do not end with a weird "/index" suffix
// - Auto-generated sidebar categories link to them as intro
const isCategoryIndex = ({ fileName, directories, }) => {
const eligibleDocIndexNames = [
'index',
'readme',
directories[0]?.toLowerCase(),
];
return eligibleDocIndexNames.includes(fileName.toLowerCase());
};
exports.isCategoryIndex = isCategoryIndex;
/**
* `guides/sidebar/autogenerated.md` ->
* `'autogenerated', '.md', ['sidebar', 'guides']`
*/
function toCategoryIndexMatcherParam({ source, sourceDirName, }) {
// source + sourceDirName are always posix-style
return {
fileName: path_1.default.posix.parse(source).name,
extension: path_1.default.posix.parse(source).ext,
directories: sourceDirName.split(path_1.default.posix.sep).reverse(),
};
}
exports.toCategoryIndexMatcherParam = toCategoryIndexMatcherParam;
// Docs are indexed by their id
function createDocsByIdIndex(docs) {
return lodash_1.default.keyBy(docs, (d) => d.id);
}
exports.createDocsByIdIndex = createDocsByIdIndex;