"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.linkify = exports.generateBlogPosts = exports.parseBlogFileName = exports.getBlogTags = exports.shouldBeListed = exports.paginateBlogPosts = exports.getSourceToPermalink = exports.truncate = void 0;
const tslib_1 = require("tslib");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const path_1 = tslib_1.__importDefault(require("path"));
const lodash_1 = tslib_1.__importDefault(require("lodash"));
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
const reading_time_1 = tslib_1.__importDefault(require("reading-time"));
const utils_1 = require("@docusaurus/utils");
const frontMatter_1 = require("./frontMatter");
const authors_1 = require("./authors");
function truncate(fileString, truncateMarker) {
    return fileString.split(truncateMarker, 1).shift();
}
exports.truncate = truncate;
function getSourceToPermalink(blogPosts) {
    return Object.fromEntries(blogPosts.map(({ metadata: { source, permalink } }) => [source, permalink]));
}
exports.getSourceToPermalink = getSourceToPermalink;
function paginateBlogPosts({ blogPosts, basePageUrl, blogTitle, blogDescription, postsPerPageOption, }) {
    const totalCount = blogPosts.length;
    const postsPerPage = postsPerPageOption === 'ALL' ? totalCount : postsPerPageOption;
    const numberOfPages = Math.ceil(totalCount / postsPerPage);
    const pages = [];
    function permalink(page) {
        return page > 0
            ? (0, utils_1.normalizeUrl)([basePageUrl, `page/${page + 1}`])
            : basePageUrl;
    }
    for (let page = 0; page < numberOfPages; page += 1) {
        pages.push({
            items: blogPosts
                .slice(page * postsPerPage, (page + 1) * postsPerPage)
                .map((item) => item.id),
            metadata: {
                permalink: permalink(page),
                page: page + 1,
                postsPerPage,
                totalPages: numberOfPages,
                totalCount,
                previousPage: page !== 0 ? permalink(page - 1) : undefined,
                nextPage: page < numberOfPages - 1 ? permalink(page + 1) : undefined,
                blogDescription,
                blogTitle,
            },
        });
    }
    return pages;
}
exports.paginateBlogPosts = paginateBlogPosts;
function shouldBeListed(blogPost) {
    return !blogPost.metadata.unlisted;
}
exports.shouldBeListed = shouldBeListed;
function getBlogTags({ blogPosts, ...params }) {
    const groups = (0, utils_1.groupTaggedItems)(blogPosts, (blogPost) => blogPost.metadata.tags);
    return lodash_1.default.mapValues(groups, ({ tag, items: tagBlogPosts }) => {
        const tagVisibility = (0, utils_1.getTagVisibility)({
            items: tagBlogPosts,
            isUnlisted: (item) => item.metadata.unlisted,
        });
        return {
            label: tag.label,
            items: tagVisibility.listedItems.map((item) => item.id),
            permalink: tag.permalink,
            pages: paginateBlogPosts({
                blogPosts: tagVisibility.listedItems,
                basePageUrl: tag.permalink,
                ...params,
            }),
            unlisted: tagVisibility.unlisted,
        };
    });
}
exports.getBlogTags = getBlogTags;
const DATE_FILENAME_REGEX = /^(?<folder>.*)(?<date>\d{4}[-/]\d{1,2}[-/]\d{1,2})[-/]?(?<text>.*?)(?:\/index)?.mdx?$/;
function parseBlogFileName(blogSourceRelative) {
    const dateFilenameMatch = blogSourceRelative.match(DATE_FILENAME_REGEX);
    if (dateFilenameMatch) {
        const { folder, text, date: dateString } = dateFilenameMatch.groups;
        // Always treat dates as UTC by adding the `Z`
        const date = new Date(`${dateString}Z`);
        const slugDate = dateString.replace(/-/g, '/');
        const slug = `/${slugDate}/${folder}${text}`;
        return { date, text: text, slug };
    }
    const text = blogSourceRelative.replace(/(?:\/index)?\.mdx?$/, '');
    const slug = `/${text}`;
    return { date: undefined, text, slug };
}
exports.parseBlogFileName = parseBlogFileName;
function formatBlogPostDate(locale, date, calendar) {
    try {
        return new Intl.DateTimeFormat(locale, {
            day: 'numeric',
            month: 'long',
            year: 'numeric',
            timeZone: 'UTC',
            calendar,
        }).format(date);
    }
    catch (err) {
        logger_1.default.error `Can't format blog post date "${String(date)}"`;
        throw err;
    }
}
async function parseBlogPostMarkdownFile({ filePath, parseFrontMatter, }) {
    const fileContent = await fs_extra_1.default.readFile(filePath, 'utf-8');
    try {
        const result = await (0, utils_1.parseMarkdownFile)({
            filePath,
            fileContent,
            parseFrontMatter,
            removeContentTitle: true,
        });
        return {
            ...result,
            frontMatter: (0, frontMatter_1.validateBlogPostFrontMatter)(result.frontMatter),
        };
    }
    catch (err) {
        logger_1.default.error `Error while parsing blog post file path=${filePath}.`;
        throw err;
    }
}
const defaultReadingTime = ({ content, options }) => (0, reading_time_1.default)(content, options).minutes;
async function processBlogSourceFile(blogSourceRelative, contentPaths, context, options, authorsMap) {
    const { siteConfig: { baseUrl, markdown: { parseFrontMatter }, }, siteDir, i18n, } = context;
    const { routeBasePath, tagsBasePath: tagsRouteBasePath, truncateMarker, showReadingTime, editUrl, } = options;
    // Lookup in localized folder in priority
    const blogDirPath = await (0, utils_1.getFolderContainingFile)((0, utils_1.getContentPathList)(contentPaths), blogSourceRelative);
    const blogSourceAbsolute = path_1.default.join(blogDirPath, blogSourceRelative);
    const { frontMatter, content, contentTitle, excerpt } = await parseBlogPostMarkdownFile({
        filePath: blogSourceAbsolute,
        parseFrontMatter,
    });
    const aliasedSource = (0, utils_1.aliasedSitePath)(blogSourceAbsolute, siteDir);
    const draft = (0, utils_1.isDraft)({ frontMatter });
    const unlisted = (0, utils_1.isUnlisted)({ frontMatter });
    if (draft) {
        return undefined;
    }
    if (frontMatter.id) {
        logger_1.default.warn `name=${'id'} header option is deprecated in path=${blogSourceRelative} file. Please use name=${'slug'} option instead.`;
    }
    const parsedBlogFileName = parseBlogFileName(blogSourceRelative);
    async function getDate() {
        // Prefer user-defined date.
        if (frontMatter.date) {
            if (typeof frontMatter.date === 'string') {
                // Always treat dates as UTC by adding the `Z`
                return new Date(`${frontMatter.date}Z`);
            }
            // YAML only converts YYYY-MM-DD to dates and leaves others as strings.
            return frontMatter.date;
        }
        else if (parsedBlogFileName.date) {
            return parsedBlogFileName.date;
        }
        try {
            const result = (0, utils_1.getFileCommitDate)(blogSourceAbsolute, {
                age: 'oldest',
                includeAuthor: false,
            });
            return result.date;
        }
        catch (err) {
            logger_1.default.warn(err);
            return (await fs_extra_1.default.stat(blogSourceAbsolute)).birthtime;
        }
    }
    const date = await getDate();
    const formattedDate = formatBlogPostDate(i18n.currentLocale, date, i18n.localeConfigs[i18n.currentLocale].calendar);
    const title = frontMatter.title ?? contentTitle ?? parsedBlogFileName.text;
    const description = frontMatter.description ?? excerpt ?? '';
    const slug = frontMatter.slug ?? parsedBlogFileName.slug;
    const permalink = (0, utils_1.normalizeUrl)([baseUrl, routeBasePath, slug]);
    function getBlogEditUrl() {
        const blogPathRelative = path_1.default.relative(blogDirPath, path_1.default.resolve(blogSourceAbsolute));
        if (typeof editUrl === 'function') {
            return editUrl({
                blogDirPath: (0, utils_1.posixPath)(path_1.default.relative(siteDir, blogDirPath)),
                blogPath: (0, utils_1.posixPath)(blogPathRelative),
                permalink,
                locale: i18n.currentLocale,
            });
        }
        else if (typeof editUrl === 'string') {
            const isLocalized = blogDirPath === contentPaths.contentPathLocalized;
            const fileContentPath = isLocalized && options.editLocalizedFiles
                ? contentPaths.contentPathLocalized
                : contentPaths.contentPath;
            const contentPathEditUrl = (0, utils_1.normalizeUrl)([
                editUrl,
                (0, utils_1.posixPath)(path_1.default.relative(siteDir, fileContentPath)),
            ]);
            return (0, utils_1.getEditUrl)(blogPathRelative, contentPathEditUrl);
        }
        return undefined;
    }
    const tagsBasePath = (0, utils_1.normalizeUrl)([
        baseUrl,
        routeBasePath,
        tagsRouteBasePath,
    ]);
    const authors = (0, authors_1.getBlogPostAuthors)({ authorsMap, frontMatter, baseUrl });
    return {
        id: slug,
        metadata: {
            permalink,
            editUrl: getBlogEditUrl(),
            source: aliasedSource,
            title,
            description,
            date,
            formattedDate,
            tags: (0, utils_1.normalizeFrontMatterTags)(tagsBasePath, frontMatter.tags),
            readingTime: showReadingTime
                ? options.readingTime({
                    content,
                    frontMatter,
                    defaultReadingTime,
                })
                : undefined,
            hasTruncateMarker: truncateMarker.test(content),
            authors,
            frontMatter,
            unlisted,
        },
        content,
    };
}
async function generateBlogPosts(contentPaths, context, options) {
    const { include, exclude } = options;
    if (!(await fs_extra_1.default.pathExists(contentPaths.contentPath))) {
        return [];
    }
    const blogSourceFiles = await (0, utils_1.Globby)(include, {
        cwd: contentPaths.contentPath,
        ignore: exclude,
    });
    const authorsMap = await (0, authors_1.getAuthorsMap)({
        contentPaths,
        authorsMapPath: options.authorsMapPath,
    });
    async function doProcessBlogSourceFile(blogSourceFile) {
        try {
            return await processBlogSourceFile(blogSourceFile, contentPaths, context, options, authorsMap);
        }
        catch (err) {
            throw new Error(`Processing of blog source file path=${blogSourceFile} failed.`, { cause: err });
        }
    }
    const blogPosts = (await Promise.all(blogSourceFiles.map(doProcessBlogSourceFile))).filter(Boolean);
    blogPosts.sort((a, b) => b.metadata.date.getTime() - a.metadata.date.getTime());
    if (options.sortPosts === 'ascending') {
        return blogPosts.reverse();
    }
    return blogPosts;
}
exports.generateBlogPosts = generateBlogPosts;
function linkify({ filePath, contentPaths, fileString, siteDir, sourceToPermalink, onBrokenMarkdownLink, }) {
    const { newContent, brokenMarkdownLinks } = (0, utils_1.replaceMarkdownLinks)({
        siteDir,
        fileString,
        filePath,
        contentPaths,
        sourceToPermalink,
    });
    brokenMarkdownLinks.forEach((l) => onBrokenMarkdownLink(l));
    return newContent;
}
exports.linkify = linkify;
