"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.isCategoryIndex = void 0;
exports.readDocFile = readDocFile;
exports.readVersionDocs = readVersionDocs;
exports.processDocMetadata = processDocMetadata;
exports.addDocNavigation = addDocNavigation;
exports.getMainDocId = getMainDocId;
exports.toCategoryIndexMatcherParam = toCategoryIndexMatcherParam;
exports.createDocsByIdIndex = createDocsByIdIndex;
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 utils_1 = require("@docusaurus/utils");
const frontMatter_1 = require("./frontMatter");
const slug_1 = tslib_1.__importDefault(require("./slug"));
const numberPrefix_1 = require("./numberPrefix");
const utils_2 = require("./sidebars/utils");
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 };
}
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)));
}
async function doProcessDocMetadata({ docFile, versionMetadata, context, options, env, tagsFile, }) {
    const { source, content, contentPath, filePath } = docFile;
    const { siteDir, siteConfig: { markdown: { parseFrontMatter }, future: { experimental_vcs: vcs }, }, } = 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 (0, utils_1.readLastUpdateData)(filePath, options, lastUpdateFrontMatter, vcs);
    // 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 = typeof versionMetadata.contentPathLocalized !== 'undefined' &&
                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 tags = (0, utils_1.normalizeTags)({
        options,
        source,
        frontMatterTags: frontMatter.tags,
        tagsBaseRoutePath: versionMetadata.tagsPath,
        tagsFile,
    });
    // 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,
        version: versionMetadata.versionName,
        lastUpdatedBy: lastUpdate.lastUpdatedBy,
        lastUpdatedAt: lastUpdate.lastUpdatedAt,
        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 });
    }
}
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;
}
/**
 * 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;
}
// 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(),
    };
}
// Docs are indexed by their id
function createDocsByIdIndex(docs) {
    return lodash_1.default.keyBy(docs, (d) => d.id);
}
