"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SitePagesManager = void 0;
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const walk_sync_1 = __importDefault(require("walk-sync"));
const Page_1 = require("../Page");
const PageConfig_1 = require("../Page/PageConfig");
const VariableRenderer_1 = require("../variables/VariableRenderer");
const constants_1 = require("./constants");
const fsUtil = __importStar(require("../utils/fsUtil"));
const logger = __importStar(require("../utils/logger"));
const url = {
    join: path_1.default.posix.join,
};
const HIGHLIGHT_ASSETS = {
    dark: 'codeblock-dark.min.css',
    light: 'codeblock-light.min.css',
};
/**
 * Manages the lifecycle and configuration of pages within the site.
 * Handles page creation, collection of addressable pages, and dependency tracking.
 */
class SitePagesManager {
    constructor(rootPath, outputPath, isDevMode) {
        this.rootPath = rootPath;
        this.outputPath = outputPath;
        this.isDevMode = isDevMode;
        // Page template path
        this.pageTemplatePath = path_1.default.join(__dirname, '../Page', constants_1.PAGE_TEMPLATE_NAME);
        this.pageTemplate = VariableRenderer_1.VariableRenderer.compile(fs_extra_1.default.readFileSync(this.pageTemplatePath, 'utf8'));
        this.pages = [];
        this.addressablePages = [];
        this.addressablePagesSource = [];
        this.baseUrlMap = new Set();
    }
    setBaseUrlMap(baseUrlMap) {
        this.baseUrlMap = baseUrlMap;
    }
    /**
     * Create a Page object from the site and page creation config.
     */
    createPage(config) {
        const sourcePath = path_1.default.join(this.rootPath, config.pageSrc);
        const outputExtension = config.fileExtension || '.html';
        const relativePath = fsUtil.ensurePosix(path_1.default.relative(this.rootPath, sourcePath));
        const outputPath = fsUtil.setExtension(relativePath, outputExtension);
        const resultPath = path_1.default.join(this.outputPath, outputPath);
        const baseAssetsPath = path_1.default.posix.join(this.siteConfig.baseUrl || '/', constants_1.TEMPLATE_SITE_ASSET_FOLDER_NAME);
        const highlightAsset = HIGHLIGHT_ASSETS[this.siteConfig.style.codeTheme];
        const pageConfig = new PageConfig_1.PageConfig({
            asset: {
                bootstrap: path_1.default.posix.join(baseAssetsPath, 'css', 'bootstrap.min.css'),
                externalScripts: constants_1._.union(this.siteConfig.externalScripts, config.externalScripts),
                fontAwesome: path_1.default.posix.join(baseAssetsPath, 'fontawesome', 'css', 'all.min.css'),
                glyphicons: path_1.default.posix.join(baseAssetsPath, 'glyphicons', 'css', 'bootstrap-glyphicons.min.css'),
                octicons: path_1.default.posix.join(baseAssetsPath, 'css', 'octicons.css'),
                materialIcons: path_1.default.posix.join(baseAssetsPath, 'material-icons', 'material-icons.css'),
                bootstrapIcons: path_1.default.posix.join(baseAssetsPath, 'bootstrap-icons', 'font', 'bootstrap-icons.css'),
                highlight: path_1.default.posix.join(baseAssetsPath, 'css', highlightAsset),
                markBindCss: path_1.default.posix.join(baseAssetsPath, 'css', 'markbind.min.css'),
                markBindJs: path_1.default.posix.join(baseAssetsPath, 'js', 'markbind.min.js'),
                pageNavCss: path_1.default.posix.join(baseAssetsPath, 'css', 'page-nav.css'),
                siteNavCss: path_1.default.posix.join(baseAssetsPath, 'css', 'site-nav.css'),
                bootstrapUtilityJs: path_1.default.posix.join(baseAssetsPath, 'js', 'bootstrap-utility.min.js'),
                polyfillJs: path_1.default.posix.join(baseAssetsPath, 'js', 'polyfill.min.js'),
                // We use development Vue when MarkBind is served in 'dev' mode so that hydration issues are reported
                vue: this.isDevMode
                    ? 'https://cdn.jsdelivr.net/npm/vue@3.3.11/dist/vue.global.min.js'
                    : path_1.default.posix.join(baseAssetsPath, 'js', 'vue.global.prod.min.js'),
                layoutUserScriptsAndStyles: [],
            },
            baseUrlMap: this.baseUrlMap,
            dev: this.isDevMode,
            faviconUrl: config.faviconUrl,
            frontmatterOverride: config.frontmatter,
            layout: config.layout,
            layoutsAssetPath: path_1.default.posix.join(baseAssetsPath, constants_1.LAYOUT_SITE_FOLDER_NAME),
            pluginManager: this.pluginManager,
            resultPath,
            rootPath: this.rootPath,
            searchable: this.siteConfig.enableSearch && config.searchable,
            siteLinkManager: this.siteLinkManager,
            siteOutputPath: this.outputPath,
            sourcePath,
            src: config.pageSrc,
            title: config.title,
            template: this.pageTemplate,
            variableProcessor: this.variableProcessor,
            addressablePagesSource: this.addressablePagesSource,
            layoutManager: this.layoutManager,
        });
        return new Page_1.Page(pageConfig, this.siteConfig);
    }
    /**
     * Updates the paths to be traversed as addressable pages and returns a list of filepaths to be deleted
     */
    updateAddressablePages() {
        const oldAddressablePagesSources = this.addressablePages.slice().map(page => page.src);
        this.collectAddressablePages();
        const newAddressablePagesSources = this.addressablePages.map(page => page.src);
        return constants_1._.difference(oldAddressablePagesSources, newAddressablePagesSources)
            .map(filePath => fsUtil.setExtension(filePath, '.html'));
    }
    getPageGlobPaths(page, pagesExclude) {
        var _a;
        const pageGlobs = (_a = page.glob) !== null && _a !== void 0 ? _a : [];
        return (0, walk_sync_1.default)(this.rootPath, {
            directories: false,
            globs: Array.isArray(pageGlobs) ? pageGlobs : [pageGlobs],
            ignore: [
                constants_1.CONFIG_FOLDER_NAME,
                constants_1.SITE_FOLDER_NAME,
                ...pagesExclude.concat(page.globExclude || []),
            ],
        });
    }
    /**
     * Collects the paths to be traversed as addressable pages
     */
    collectAddressablePages() {
        const { pages, pagesExclude } = this.siteConfig;
        const pagesFromSrc = constants_1._.flatMap(pages.filter(page => page.src), page => (Array.isArray(page.src)
            ? page.src.map(pageSrc => (Object.assign(Object.assign({}, page), { src: pageSrc })))
            : [page]));
        const set = new Set();
        const duplicatePages = pagesFromSrc
            .filter(page => set.size === set.add(page.src).size)
            .map(page => page.src);
        if (duplicatePages.length > 0) {
            throw new Error(`Duplicate page entries found in site config: ${constants_1._.uniq(duplicatePages).join(', ')}`);
        }
        const pagesFromGlobs = constants_1._.flatMap(pages.filter(page => page.glob), page => this.getPageGlobPaths(page, pagesExclude)
            .map(filePath => ({
            src: filePath,
            searchable: page.searchable,
            layout: page.layout,
            frontmatter: page.frontmatter,
            fileExtension: page.fileExtension,
        })));
        /*
         Add pages collected from globs and merge properties for pages
         Page properties collected from src have priority over page properties from globs,
         while page properties from later entries take priority over earlier ones.
         */
        const filteredPages = {};
        pagesFromGlobs.concat(pagesFromSrc).forEach((page) => {
            const filteredPage = constants_1._.omitBy(page, constants_1._.isUndefined);
            filteredPages[page.src] = page.src in filteredPages
                ? Object.assign(Object.assign({}, filteredPages[page.src]), filteredPage) : filteredPage;
        });
        this.addressablePages = Object.values(filteredPages);
        this.addressablePagesSource.length = 0;
        this.addressablePages.forEach((page) => {
            this.addressablePagesSource.push(fsUtil.removeExtensionPosix(page.src));
        });
    }
    /**
     * Creates new pages and replaces the original pages with the updated version
     */
    updatePages(pagesToUpdate) {
        pagesToUpdate.forEach((pageToUpdate) => {
            this.pages.forEach((page, index) => {
                if (page.pageConfig.src === pageToUpdate.src) {
                    const newPage = this.createNewPage(pageToUpdate, this.getFavIconUrl());
                    newPage.resetState();
                    this.pages[index] = newPage;
                }
            });
        });
    }
    /**
     * Checks if a specified file path is a dependency of a page
     * @param filePath file path to check
     * @returns whether the file path is a dependency of any of the site's pages
     */
    isDependencyOfPage(filePath) {
        return this.pages.some(page => page.isDependency(filePath))
            || fsUtil.ensurePosix(filePath).endsWith(constants_1.USER_VARIABLES_PATH);
    }
    /**
     * Checks if a specified file path satisfies a src or glob in any of the page configurations.
     * @param filePath file path to check
     * @returns whether the file path satisfies any glob
     */
    isFilepathAPage(filePath) {
        const { pages, pagesExclude } = this.siteConfig;
        const relativeFilePath = fsUtil.ensurePosix(path_1.default.relative(this.rootPath, filePath));
        const srcesFromPages = constants_1._.flatMap(pages.filter(page => page.src), page => (Array.isArray(page.src) ? page.src : [page.src]));
        if (srcesFromPages.includes(relativeFilePath)) {
            return true;
        }
        const filePathsFromGlobs = constants_1._.flatMap(pages.filter(page => page.glob), page => this.getPageGlobPaths(page, pagesExclude));
        return filePathsFromGlobs.some(fp => fp === relativeFilePath);
    }
    getFavIconUrl() {
        const { baseUrl, faviconPath } = this.siteConfig;
        if (faviconPath) {
            if (!fs_extra_1.default.existsSync(path_1.default.join(this.rootPath, faviconPath))) {
                logger.warn(`${faviconPath} does not exist`);
            }
            return url.join('/', baseUrl, faviconPath);
        }
        else if (fs_extra_1.default.existsSync(path_1.default.join(this.rootPath, constants_1.FAVICON_DEFAULT_PATH))) {
            return url.join('/', baseUrl, constants_1.FAVICON_DEFAULT_PATH);
        }
        return undefined;
    }
    /**
     * Maps an array of addressable pages to an array of Page object
     */
    mapAddressablePagesToPages(addressablePages, faviconUrl) {
        this.pages = addressablePages.map(page => this.createNewPage(page, faviconUrl));
    }
    /**
     * Creates and returns a new Page with the given page config details and favicon url
     * @param page config
     * @param faviconUrl of the page
     */
    createNewPage(page, faviconUrl) {
        return this.createPage({
            faviconUrl,
            pageSrc: page.src,
            title: page.title,
            layout: page.layout,
            frontmatter: page.frontmatter || {},
            searchable: page.searchable !== 'no' && page.searchable !== false,
            externalScripts: page.externalScripts || [],
            fileExtension: page.fileExtension,
        });
    }
}
exports.SitePagesManager = SitePagesManager;
//# sourceMappingURL=SitePagesManager.js.map