UNPKG

litenode

Version:

Lightweight and modular web framework

172 lines (155 loc) 6.57 kB
import { readdir, stat } from "node:fs/promises" import { join, relative } from "node:path" import { SMP } from "../../SMP/smp.js" export class MarkdownHandler { #viewsDir constructor(viewsDir = "views") { this.#viewsDir = viewsDir // Initialize the custom markdown parser and pass viewsDir to SMP this.smp = new SMP(viewsDir) } /** * Parses a single markdown file and extracts its frontmatter and content. * @param {string} filePath - The path to the markdown file. * @returns {Object} - An object containing the parsed frontmatter and content. */ parseMarkdownFile(filePath) { return this.smp.parseFrontmatter(filePath) } /** * Parses all markdown files in a directory. * @param {string} dir - The directory containing markdown files. * @returns {Promise<Array<Object>>} - A promise that resolves to an array of parsed markdown objects. */ async parseMarkdownFileS(dir) { const normalizedDir = dir.startsWith("/") ? dir.slice(1) : dir // Use the custom views directory instead of hardcoded "views" const files = await this.getMarkdownFiles(join(this.#viewsDir, normalizedDir)) return Promise.all( files.map((file) => { // Use the custom views directory instead of hardcoded "views" const relativePath = relative(this.#viewsDir, file) return this.parseMarkdownFile(relativePath) }) ) } /** * Recursively retrieves all markdown files in a directory. * @param {string} dir - The directory to search for markdown files. * @param {Array<string>} fileList - An array to accumulate file paths (used internally for recursion). * @returns {Promise<Array<string>>} - A promise that resolves to an array of markdown file paths. */ async getMarkdownFiles(dir, fileList = []) { const files = await readdir(dir, { withFileTypes: true }) await Promise.all( files.map(async (file) => { const filePath = join(dir, file.name) const stats = await stat(filePath) if (stats.isDirectory()) { await this.getMarkdownFiles(filePath, fileList) } else if (filePath.endsWith(".md")) { fileList.push(filePath) } }) ) return fileList } /** * Extracts specified properties from markdown files. * @param {string|Array} input - A file path, directory path, or array of parsed markdown objects. * @param {Array<string>} properties - An array of property names to extract. * @returns {Promise<Array<Object>>} - A promise that resolves to an array of objects containing the extracted properties. */ async extractMarkdownProperties(input, properties) { let parsedFiles if (Array.isArray(input)) { parsedFiles = input } else if (typeof input === "string") { if (input.endsWith(".md")) { const parsedFile = this.parseMarkdownFile(input) parsedFiles = [parsedFile] } else { parsedFiles = await this.parseMarkdownFileS(input) } } else { throw new Error("Invalid input type for extractMarkdownProperties. Must be an array or a string.") } return parsedFiles.map((obj) => { const { frontmatter } = obj if (!frontmatter) { return properties.reduce((acc, prop) => { acc[prop] = undefined return acc }, {}) } return properties.reduce((acc, prop) => { const value = prop.split(".").reduce((nestedAcc, key) => nestedAcc?.[key], frontmatter) acc[prop] = value return acc }, {}) }) } /** * Groups markdown files by a specified property. * @param {string} dir - The directory containing markdown files. * @param {Array<string>} properties - An array of property names to extract. * @param {string} groupByField - The property to group the files by. * @returns {Promise<Object>} - A promise that resolves to an object where keys are group values and values are arrays of objects. */ async groupByMarkdownProperty(dir, properties, groupByField) { const extractedProperties = await this.extractMarkdownProperties(dir, properties) return extractedProperties.reduce((acc, item) => { const groupValue = item[groupByField] || "Undefined" if (!acc[groupValue]) { acc[groupValue] = [] } acc[groupValue].push(item) return acc }, {}) } /** * Paginates an array of items and returns a paginated result object with previous and next items. * @param {Array<any>} items - The array of items to paginate. * @param {number} current_page - The current page number (default is 1). * @param {number} per_page_items - Number of items per page (default is 10). * @returns {Object} - A paginated result object containing page information, previous and next items, and paginated data. */ #paginator(items, current_page = 1, per_page_items = 10) { let page = current_page, per_page = per_page_items, offset = (page - 1) * per_page, paginatedItems = items.slice(offset, offset + per_page), // Get items for the current page total_pages = Math.ceil(items.length / per_page), // Calculate total number of pages prev_item = offset > 0 ? items[offset - 1] : null, // Previous item or null if on the first page next_item = offset + per_page < items.length ? items[offset + per_page] : null // Next item or null if on the last page return { page: Number(page), // Current page number per_page: per_page, // Number of items per page prev_page: page > 1 ? Number(page) - 1 : null, // Previous page number or null if on the first page next_page: page < total_pages ? Number(page) + 1 : null, // Next page number or null if on the last page prev_item: prev_item, // Previous item in the array next_item: next_item, // Next item in the array total_items: items.length, // Total number of items total_pages: total_pages, // Total number of pages data: paginatedItems, // Items for the current page } } /** * Paginates an array of markdown files or files from a directory. * @param {string|Array} input - A file path, directory path, or array of parsed markdown objects. * @param {number} page - The page number (1-based index). * @param {number} perPage - The number of items per page. * @returns {Promise<Object>} - A promise that resolves to an object containing paginated results and metadata. */ async paginateMarkdownFiles(input, page = 1, perPage = 10) { let parsedFiles if (Array.isArray(input)) { parsedFiles = input } else if (typeof input === "string") { parsedFiles = await this.parseMarkdownFileS(input) } else { throw new Error("Invalid input type for paginateMarkdownFiles. Must be an array or a string.") } return this.#paginator(parsedFiles, page, perPage) } }