"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 (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Template = 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 fsUtil = __importStar(require("../utils/fsUtil"));
const constants_1 = require("./constants");
const SiteConfig_1 = require("./SiteConfig");
const VariableRenderer_1 = require("../variables/VariableRenderer");
const logger = __importStar(require("../utils/logger"));
const { LAYOUT_DEFAULT_NAME, LAYOUT_FOLDER_PATH } = require('../Layout');
const requiredFiles = ['index.md', 'site.json', '_markbind/'];
const PATH_TO_TEMPLATE = '../../template';
const ABOUT_MARKDOWN_FILE = 'about.md';
const ABOUT_MARKDOWN_DEFAULT = '# About\n'
    + 'Welcome to your **About Us** page.\n';
const CONFIG_FOLDER_NAME = '_markbind';
const SITE_FOLDER_NAME = '_site';
const WIKI_SITE_NAV_PATH = '_Sidebar.md';
const WIKI_FOOTER_PATH = '_Footer.md';
class Template {
    constructor(rootPath, templatePath) {
        this.siteConfigPath = constants_1.SITE_CONFIG_NAME;
        this.rootPath = rootPath;
        this.templatePath = path_1.default.join(__dirname, PATH_TO_TEMPLATE, templatePath);
    }
    validateTemplateFromPath() {
        for (let i = 0; i < requiredFiles.length; i += 1) {
            const requiredFile = requiredFiles[i];
            const requiredFilePath = path_1.default.join(this.templatePath, requiredFile);
            if (!fs_extra_1.default.existsSync(requiredFilePath)) {
                return false;
            }
        }
        return true;
    }
    generateSiteWithTemplate() {
        return new Promise((resolve, reject) => {
            fs_extra_1.default.access(this.rootPath)
                .catch(() => fs_extra_1.default.mkdirSync(this.rootPath))
                .then(() => fsUtil.copySyncWithOptions(this.templatePath, this.rootPath, { overwrite: false }))
                .then(resolve)
                .catch(reject);
        });
    }
    /**
     * A method for initializing a markbind site according to the given template.
     * Generate the site.json and an index.md file.
     */
    init() {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this.validateTemplateFromPath()) {
                throw new Error('Template validation failed. Required files does not exist.');
            }
            return new Promise((resolve, reject) => {
                this.generateSiteWithTemplate()
                    .then(resolve)
                    .catch(reject);
            });
        });
    }
    /**
     * Converts an existing GitHub wiki or docs folder to a MarkBind website.
     */
    convert() {
        return __awaiter(this, void 0, void 0, function* () {
            this.siteConfig = yield SiteConfig_1.SiteConfig.readSiteConfig(this.rootPath, this.siteConfigPath);
            this.collectNavigablePages();
            yield this.addIndexPage();
            yield this.addAboutPage();
            this.addDefaultLayoutFiles();
            yield this.addDefaultLayoutToSiteConfig();
        });
    }
    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: [
                CONFIG_FOLDER_NAME,
                SITE_FOLDER_NAME,
                ...pagesExclude.concat(page.globExclude || []),
            ],
        });
    }
    /**
     * Collects the paths to be traversed as navigable pages
     */
    collectNavigablePages() {
        const { pages, pagesExclude } = this.siteConfig;
        const pagesFromGlobs = constants_1._.flatMap(pages.filter(page => page.glob), page => this.getPageGlobPaths(page, pagesExclude)
            .map(filePath => ({
            src: filePath,
            title: page.title,
        })));
        this.navigablePages = pagesFromGlobs;
    }
    /**
     * Copies over README.md or Home.md to default index.md if present.
     */
    addIndexPage() {
        return __awaiter(this, void 0, void 0, function* () {
            const indexPagePath = path_1.default.join(this.rootPath, constants_1.INDEX_MARKDOWN_FILE);
            const fileNames = ['README.md', 'Home.md'];
            const filePath = fileNames.find(fileName => fs_extra_1.default.existsSync(path_1.default.join(this.rootPath, fileName)));
            // if none of the files exist, do nothing
            if (constants_1._.isUndefined(filePath))
                return;
            try {
                yield fs_extra_1.default.copy(path_1.default.join(this.rootPath, filePath), indexPagePath);
            }
            catch (error) {
                throw new Error(`Failed to copy over ${filePath}`);
            }
        });
    }
    /**
     * Adds an about page to site if not present.
     */
    addAboutPage() {
        return __awaiter(this, void 0, void 0, function* () {
            const aboutPath = path_1.default.join(this.rootPath, ABOUT_MARKDOWN_FILE);
            try {
                yield fs_extra_1.default.access(aboutPath);
            }
            catch (error) {
                if (fs_extra_1.default.existsSync(aboutPath)) {
                    return;
                }
                yield fs_extra_1.default.outputFile(aboutPath, ABOUT_MARKDOWN_DEFAULT);
            }
        });
    }
    /**
     * Adds a footer to default layout of site.
     */
    addDefaultLayoutFiles() {
        const wikiFooterPath = path_1.default.join(this.rootPath, WIKI_FOOTER_PATH);
        let footer;
        if (fs_extra_1.default.existsSync(wikiFooterPath)) {
            logger.info(`Copied over the existing ${WIKI_FOOTER_PATH} file to the converted layout`);
            footer = `\n${fs_extra_1.default.readFileSync(wikiFooterPath, 'utf8')}`;
        }
        const wikiSiteNavPath = path_1.default.join(this.rootPath, WIKI_SITE_NAV_PATH);
        let siteNav;
        if (fs_extra_1.default.existsSync(wikiSiteNavPath)) {
            logger.info(`Copied over the existing ${WIKI_SITE_NAV_PATH} file to the converted layout\n`
                + 'Check https://markbind.org/userGuide/tweakingThePageStructure.html#site-navigation-menus\n'
                + 'for information on site navigation menus.');
            siteNav = fs_extra_1.default.readFileSync(wikiSiteNavPath, 'utf8');
        }
        else {
            siteNav = this.buildSiteNav();
        }
        const convertedLayoutTemplate = VariableRenderer_1.VariableRenderer.compile(fs_extra_1.default.readFileSync(path_1.default.join(__dirname, 'siteConvertLayout.njk'), 'utf8'));
        const renderedLayout = convertedLayoutTemplate.render({
            footer,
            siteNav,
        });
        const layoutOutputPath = path_1.default.join(this.rootPath, LAYOUT_FOLDER_PATH, LAYOUT_DEFAULT_NAME);
        fs_extra_1.default.writeFileSync(layoutOutputPath, renderedLayout, 'utf-8');
    }
    /**
     * Builds a site navigation file from the directory structure of the site.
     */
    buildSiteNav() {
        let siteNavContent = '';
        this.navigablePages
            .filter(navigablePage => !navigablePage.src.startsWith('_'))
            .forEach((page) => {
            const navigablePagePath = path_1.default.join(this.rootPath, page.src);
            const relativePagePathWithoutExt = fsUtil.removeExtensionPosix(path_1.default.relative(this.rootPath, navigablePagePath));
            const pageName = constants_1._.startCase(fsUtil.removeExtension(path_1.default.basename(navigablePagePath)));
            const pageUrl = `{{ baseUrl }}/${relativePagePathWithoutExt}.html`;
            siteNavContent += `* [${pageName}](${pageUrl})\n`;
        });
        return siteNavContent.trimEnd();
    }
    /**
     * Applies the default layout to all addressable pages by modifying the site config file.
     */
    addDefaultLayoutToSiteConfig() {
        return __awaiter(this, void 0, void 0, function* () {
            const configPath = path_1.default.join(this.rootPath, constants_1.SITE_CONFIG_NAME);
            const config = yield fs_extra_1.default.readJson(configPath);
            yield Template.writeToSiteConfig(config, configPath);
        });
    }
    /**
     * Helper function for addDefaultLayoutToSiteConfig().
     */
    static writeToSiteConfig(config, configPath) {
        return __awaiter(this, void 0, void 0, function* () {
            const layoutObj = { glob: '**/*.md', layout: LAYOUT_DEFAULT_NAME };
            config.pages.push(layoutObj);
            yield fs_extra_1.default.outputJson(configPath, config);
        });
    }
}
exports.Template = Template;
//# sourceMappingURL=template.js.map