Files
actual/node_modules/@docusaurus/plugin-content-docs/lib/docs.js
Rich In SQL 28d4ee94dd Init
2022-10-16 20:28:33 +01:00

310 lines
14 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.getDocIds = 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 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 constants_1 = require("./constants");
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;
/** Docs with draft front matter are only considered draft in production. */
function isDraftForEnvironment({ env, frontMatter, }) {
return (env === 'production' && frontMatter.draft) ?? false;
}
async function doProcessDocMetadata({ docFile, versionMetadata, context, options, env, }) {
const { source, content, contentPath, filePath } = docFile;
const { siteDir, i18n } = context;
const { frontMatter: unsafeFrontMatter, contentTitle, excerpt, } = (0, utils_1.parseMarkdownString)(content);
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
// The same doc in 2 distinct version could keep the same id,
// we just need to namespace the data by version
const versionIdPrefix = versionMetadata.versionName === constants_1.CURRENT_VERSION_NAME
? undefined
: `version-${versionMetadata.versionName}`;
// 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 unversionedId = [computeDirNameIdPrefix(), baseID]
.filter(Boolean)
.join('/');
// TODO is versioning the id very useful in practice?
// legacy versioned id, requires a breaking change to modify this
const id = [versionIdPrefix, unversionedId].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 = isDraftForEnvironment({ 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 {
unversionedId,
id,
title,
description,
source: (0, utils_1.aliasedSitePath)(filePath, siteDir),
sourceDirName,
slug: docSlug,
permalink,
draft,
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,
};
}
function processDocMetadata(args) {
try {
return doProcessDocMetadata(args);
}
catch (err) {
logger_1.default.error `Can't process doc metadata for doc at path path=${args.docFile.filePath} in version name=${args.versionMetadata.versionName}`;
throw err;
}
}
exports.processDocMetadata = processDocMetadata;
function addDocNavigation(docsBase, sidebarsUtils, sidebarFilePath) {
const docsById = createDocsByIdIndex(docsBase);
sidebarsUtils.checkSidebarsDocIds(docsBase.flatMap(getDocIds), sidebarFilePath);
// Add sidebar/next/previous to the docs
function addNavData(doc) {
const navigation = sidebarsUtils.getDocNavigation(doc.unversionedId, doc.id, doc.frontMatter.displayed_sidebar);
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}.`);
}
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 = docsBase.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 ||
doc.unversionedId === firstDocIdOfFirstSidebar);
}
return docs[0];
}
return getMainDoc().unversionedId;
}
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;
// Return both doc ids
// TODO legacy retro-compatibility due to old versioned sidebars using
// versioned doc ids ("id" should be removed & "versionedId" should be renamed
// to "id")
function getDocIds(doc) {
return [doc.unversionedId, doc.id];
}
exports.getDocIds = getDocIds;
// Docs are indexed by both versioned and unversioned ids at the same time
// TODO legacy retro-compatibility due to old versioned sidebars using
// versioned doc ids ("id" should be removed & "versionedId" should be renamed
// to "id")
function createDocsByIdIndex(docs) {
return Object.fromEntries(docs.flatMap((doc) => [
[doc.unversionedId, doc],
[doc.id, doc],
]));
}
exports.createDocsByIdIndex = createDocsByIdIndex;