LiteNode's logo

LiteNode

Docs GitHub logo
▶ Usage

Documentation

LiteNode Markdown Documentation

🚀 Dive into the world of Markdown-powered applications with LiteNode in Node.js! My comprehensive tutorial covers everything from setup to deployment. Learn the ins and outs of Markdown parsing, master frontmatter handling, and discover dynamic content rendering. Start building your own Markdown-based app today! Check out the tutorial here and embark on your coding adventure! 🎉

Overview

LiteNode provides robust support for parsing and handling Markdown files through its built-in methods. This includes parsing individual Markdown files, parsing multiple Markdown files from a directory, and extracting specific properties from the frontmatter of these files. These features are an enhancement of the parsing of Markdown files in Blog-Doc CMS & SSG. The following documentation details the usage of parseMarkdownFile, parseMarkdownFileS, extractMarkdownProperties, groupByMarkdownProperty, paginateMarkdownFiles and generateTOC methods, along with examples of rendering and serving the parsed content.

Nota Bene:

  1. The usage of Marked in the examples below is to demonstrate how to parse the content of Markdown file(s) and is not related to LiteNode's methods! You can use any other parser that suits your needs to parse the returned content.
  2. Markdown files MUST be located within the views folder or any of its sub-folders!
    Provided examples assume that Markdown files are located either in a pages folder directly under the views directory or directly within the views folder if no specific folder is specified.

Methods

1. parseMarkdownFile

Description: Parses a single Markdown file and extracts its frontmatter and content.

Signature:

parseMarkdownFile(filePath: string): {
  frontmatter: object;
  content: string;
  filePath: string;
  fileDir: string;
  fileName: string;
  fileBaseName: string;
}

Returns:

Example Usage:

import { marked } from "marked"
// or const { marked } = require("marked")

app.get("/markdown-file/:filename", (req, res) => {
    try {
        const currentFile = app.parseMarkdownFile(`pages/${req.params.filename}.md`)
        res.json(currentFile)
    } catch (error) {
        res.status(500).txt(`Error parsing markdown file: ${error.message}`)
    }
})

app.get("/render-markdown/:filename", (req, res) => {
    try {
        const currentFile = app.parseMarkdownFile(`pages/${req.params.filename}.md`)
        res.render("layouts/template.html", {
            frontmatter: currentFile.frontmatter,
            html_content: marked.parse(currentFile.content),
            // Marked needed here to parse the `content` of the Markdown file!
        })
    } catch (error) {
        res.status(500).txt(`Error parsing markdown file: ${error.message}`)
    }
})

app.get("/render-to-file/:filename", async (req, res) => {
    try {
        const currentFile = app.parseMarkdownFile(`pages/${req.params.filename}.md`)
        await app.renderToFile(
            "layouts/template.html",
            {
                frontmatter: currentFile.frontmatter,
                html_content: marked.parse(currentFile.content),
                // Marked needed here to parse the `content` of the Markdown file!
            },
            `output/${req.params.filename}.html`
        )
        res.txt(`File rendered to output/${req.params.filename}.html`)
    } catch (error) {
        res.status(500).txt(`Error rendering markdown file: ${error.message}`)
    }
})

2. parseMarkdownFileS

Description: Parses multiple Markdown files from a specified directory and extracts their frontmatter and content.

Signature:

async parseMarkdownFileS(dir: string): Promise<{
  frontmatter: object;
  content: string;
  filePath: string;
  fileDir: string;
  fileName: string;
  fileBaseName: string;
}[]>

Returns:

Example Usage:

import { marked } from "marked"
// or const { marked } = require("marked")

app.get("/markdown-files", async (req, res) => {
    try {
        const parsedFiles = await app.parseMarkdownFileS("pages")
        res.json(parsedFiles)
    } catch (error) {
        res.status(500).txt(`Error parsing markdown files: ${error.message}`)
    }
})

app.get("/render-markdown-files", async (req, res) => {
    try {
        const parsedFiles = await app.parseMarkdownFileS("pages")
        const htmlContent = parsedFiles.map((file) => ({
            frontmatter: file.frontmatter,
            html_content: marked.parse(file.content),
            // Marked needed here to parse the `content` of the Markdown file!
        }))
        res.render("layouts/template.html", { files: htmlContent })
    } catch (error) {
        res.status(500).txt(`Error parsing markdown files: ${error.message}`)
    }
})

app.get("/render-to-file-markdown-files", async (req, res) => {
    try {
        const parsedFiles = await app.parseMarkdownFileS("pages")
        const htmlContent = parsedFiles.map((file) => ({
            frontmatter: file.frontmatter,
            html_content: marked.parse(file.content),
            // Marked needed here to parse the `content` of the Markdown file!
        }))
        await app.renderToFile("layouts/template.html", { files: htmlContent }, `output/all_markdown_files.html`)
        res.txt(`Files rendered to output/all_markdown_files.html`)
    } catch (error) {
        res.status(500).txt(`Error rendering markdown files: ${error.message}`)
    }
})

3. extractMarkdownProperties

Description: Extracts specified properties from parsed markdown files. This function can handle input as an array of parsed files, a directory path, or a single markdown file path, and organizes the specified frontmatter properties for further processing or display.

Signature:

extractMarkdownProperties(
  input: string | { frontmatter: object; content: string; filePath: string; fileDir: string; fileName: string }[],
  properties: Array<string>
): Promise<Array<object>>;

Returns:

Example Usage:

import { marked } from "marked"
// or const { marked } = require("marked")

// Extract properties by directly parsing the Markdown files from a specified directory!
const properties = await app.extractMarkdownProperties("pages", ["title", "date"])

// Extract properties from a pre-parsed array of files
const parsedFiles = await app.parseMarkdownFileS("pages")
const properties = await app.extractMarkdownProperties(pages, ["title", "date"])

app.get("/single-file-properties", async (req, res) => {
    try {
        // Extracting properties from a single markdown file
        const propertiesToExtract = ["title", "date", "author"]
        const extractedProperties = await app.extractMarkdownProperties("pages/single-file.md", propertiesToExtract)
        res.json(extractedProperties)
    } catch (error) {
        res.status(500).txt(`Error extracting properties: ${error.message}`)
    }
})

app.get("/extract-properties", async (req, res) => {
    try {
        const parsedFiles = await app.parseMarkdownFileS("pages")
        const propertiesToExtract = ["title", "metadata.category", "metadata.details.length"]
        const extractedData = await app.extractMarkdownProperties(parsedFiles, propertiesToExtract)
        res.json(extractedData)
    } catch (error) {
        res.status(500).txt(`Error extracting properties: ${error.message}`)
    }
})

// The code above and after this example can be simplified like so:
app.get("/extract-properties", async (req, res) => {
    try {
        const extractedData = await app.extractMarkdownProperties("pages", [
            "title",
            "metadata.category",
            "metadata.details.length",
        ])
        res.json(extractedData)
    } catch (error) {
        res.status(500).txt(`Error extracting properties: ${error.message}`)
    }
})

app.get("/render-extracted-properties", async (req, res) => {
    try {
        const parsedFiles = await app.parseMarkdownFileS("pages")
        const propertiesToExtract = ["title", "metadata.category", "metadata.details.length"]
        const extractedData = await app.extractMarkdownProperties(parsedFiles, propertiesToExtract)
        res.render("layouts/template.html", { properties: extractedData })
    } catch (error) {
        res.status(500).txt(`Error extracting properties: ${error.message}`)
    }
})

app.get("/render-to-file-extracted-properties", async (req, res) => {
    try {
        const parsedFiles = await app.parseMarkdownFileS("pages")
        const propertiesToExtract = ["title", "metadata.category", "metadata.details.length"]
        const extractedData = await app.extractMarkdownProperties(parsedFiles, propertiesToExtract)
        await app.renderToFile(
            "layouts/template.html",
            { properties: extractedData },
            `output/extracted_properties.html`
        )
        res.txt(`Properties rendered to output/extracted_properties.html`)
    } catch (error) {
        res.status(500).txt(`Error extracting properties: ${error.message}`)
    }
})

4. groupByMarkdownProperty

Description: Groups parsed markdown file objects by a specified property in the frontmatter. This function helps in categorizing or organizing markdown files based on a specific frontmatter field.

Signature:

groupByMarkdownProperty(
  dir: string,
  properties: Array<string>,
  groupByField: string
): Promise<{ [key: string]: Array<object> }>;

Returns:

A promise that resolves to an object with keys as group values and values as arrays of grouped objects.

Example Usage:

app.get("/grouped-articles", async (req, res) => {
    try {
        const properties = ["title", "href", "category"]
        const groupedArticles = await app.groupByMarkdownProperty("pages", properties, "category")
        res.json(groupedArticles)
    } catch (error) {
        res.status(500).txt(`Error grouping articles: ${error.message}`)
    }
})

5. paginateMarkdownFiles

Description: Paginates markdown files either from a directory or an array of parsed files. This function helps in displaying markdown files in a paginated format, useful for creating blogs or documentations.

Signature:

paginateMarkdownFiles(
  input: string | { frontmatter: object; content: string; filePath: string; fileDir: string; fileName: string }[],
  page?: number,
  perPage?: number
): Promise<{
  page: number;
  per_page: number;
  prev_page: number | null;
  next_page: number | null;
  total_files: number;
  total_pages: number;
  data: Array<object>;
}>;

Returns:

A promise that resolves to an object containing pagination details and the paginated data.

Example Usage:

app.get("/", async (req, res) => {
    try {
        const page = parseInt(req.query.page, 10) || 1
        const pagination = await app.paginateMarkdownFiles("pages", page, 10)
        res.render("layouts/index.html", {
            paginatedFiles: pagination.data,
            currentPage: pagination.page,
            totalPages: pagination.total_pages,
            prevPage: pagination.prev_page,
            nextPage: pagination.next_page,
        })
    } catch (error) {
        res.status(500).txt(`Error loading articles: ${error.message}`)
    }
})

6. generateTOC

See Add IDs to Headings to better understand the benefits of this method.
This method is used on this site to generate for each page a Content section to navigate between the page's sections.

Description: Generates a Table of Contents (TOC) from an HTML string by extracting headings (h2 to h6) and creating a nested list structure. This function helps in creating a TOC for better navigation within documents.

Signature:

generateTOC(input: string): string;

Returns:

A string representing the HTML for the Table of Contents.

Example Usage:

app.get("/document", async (req, res) => {
    try {
        const parsedFile = app.parseMarkdownFile("document.md")
        const toc = app.generateTOC(parsedFile.content)
        res.render("layouts/document.html", {
            toc,
        })
    } catch (error) {
        res.status(500).txt(`Error generating TOC: ${error.message}`)
    }
})

Add IDs to Headings

This feature imported from Blog-Doc was introduced in v1.4.0 of LiteNode as a method.
Starting v2.0.0, it has been integrated to LiteNode's Simple Markdown Parser (SMP), meaning that whenever parseMarkdownFile, parseMarkdownFileS, extractMarkdownProperties or paginateMarkdownFiles are used, the heading tags (h1 to h6) of the processed file(s) that include special markup indicating an ID (e.g., {# section-id}) are automatically transformed to include the specified ID as an 'id' attribute. The function normalizes and cleans the ID to ensure it is a valid HTML ID attribute.

Example Usage:

  1. Assuming content of example.md in examples directory
## What is LiteNode? {# what is LiteNode?}

LiteNode is a lightweight and modular Node.js web framework designed to provide essential web server functionalities with a clean and intuitive API.

## Who's behind it? { #who's behind it?}

[LebCit](https://github.com/LebCit) is the author of LiteNode

### Why did he make it? { # Why did he make it? }

For the love of JS and Markdown.

## What is LiteNode's purpose? { @Simplicity + Efficiency # What is LiteNode's purpose?}

Simplifying development by typing less to do more!

## Does it have a tagline? {#LiteNode's TagLine}

"What More Could You Ask For?!"
  1. parsing example.md
app.parseMarkdownFile(`examples/example.md`)
  1. would return
<h2 id="what-is-litenode">What is LiteNode?</h2>

LiteNode is a lightweight and modular Node.js web framework designed to provide essential web server functionalities
with a clean and intuitive API.

<h2 id="whos-behind-it">Who's behind it?</h2>

<!-- Link returned as Markdown because content not yet processed by a parser like Marked -->
[LebCit](https://github.com/LebCit) is the author of LiteNode

<h3 id="why-did-he-make-it">Why did he make it?</h3>

For the love of JS and Markdown.

<h2 id="what-is-litenodes-purpose">What is LiteNode's purpose?</h2>

Simplifying development by typing less to do more!

<h2 id="litenodes-tagline">Does it have a tagline?</h2>

"What More Could You Ask For?!"

As you can see, the special markup {# id} has been cleaned, normalized and added to the corresponding heading tag as a its id attribute.

The integrated addIdsToHeadings function is a useful utility for automatically adding IDs to headings in HTML generated from Markdown. This can be particularly helpful for creating navigable documents with anchors, enabling easy linking to specific sections of a document (see generateTOC). The function ensures that the IDs are valid HTML attributes by normalizing and cleaning them. This method enhances the usability of Markdown-rendered content, making it more interactive and user-friendly.


Example Markdown File

---
title: "Introduction to LiteNode"
description: "A comprehensive guide to using LiteNode for your Node.js applications."
metadata:
    category: "Tutorial"
    subcategory: "Markdown"
    details:
        length: 1200
        difficulty: "Beginner"
        audience: ["Developer", "Student"]
tags: ["Node.js", "Markdown", "Tutorial"]
---

# Introduction to LiteNode {#introduction}

LiteNode is a powerful and flexible framework for building Node.js applications. It provides robust support for parsing and rendering Markdown files, making it an ideal choice for creating content-rich applications.

## Features {#features}

-   **Routing**: Easy-to-use routing system.
-   **Middleware**: Support for middleware functions.
-   **Markdown Support**: Parse and render Markdown files with frontmatter.

## Getting Started {#getting-started}

To get started with LiteNode, follow these steps:

1. Install LiteNode via npm.
2. Set up your project structure.
3. Create your routes and middleware.
4. Start building your application!

### Installation {#installation}

You can install LiteNode using npm:

```bash
npm install litenode
```

## Conclusion {#conclusion}

LiteNode offers a wide range of features that make it easy to build and manage your Node.js applications. With its support for Markdown, you can easily create content-driven applications with rich text formatting.

Example Explanation

How This File Can Be Used

  1. Parsing and Rendering:

    • Parsing: Use parseMarkdownFile or parseMarkdownFileS to parse the file and extract its frontmatter and content.
    • Rendering: Render the content using res.render or res.renderToFile with the parsed content.
  2. Extracting Properties:

    • Extract specific properties from the frontmatter using extractMarkdownProperties.
  3. Generate Table Of Contents:

    • Use generateTOC to transform the heading tags (h2 to h6) of the file to create a nested list structure.

Example Usage

import { marked } from "marked"
// or const { marked } = require("marked")

// Parsing a single file
const markdownFile = app.parseMarkdownFile("example.md")
console.log(markdownFile)

// Rendering the file content
app.get("/render-example", (req, res) => {
    try {
        const markdownFile = app.parseMarkdownFile("example.md")
        // Marked needed here to parse the `content` of the Markdown file!
        const parsedFile = marked.parse(markdownFile.content)
        const toc = app.generateTOC(parsedFile)
        res.render("layouts/template.html", {
            frontmatter: markdownFile.frontmatter,
            html_content: parsedFile,
            html_toc: toc,
        })
    } catch (error) {
        res.status(500).txt(`Error parsing markdown file: ${error.message}`)
    }
})

Conclusion

LiteNode's Markdown handling capabilities allow for flexible and powerful parsing, rendering, and serving of Markdown content. Whether you need to parse individual files, multiple files from a directory, extract specific properties from the frontmatter or add IDs to headings, LiteNode provides the tools necessary to efficiently manage and utilize Markdown content in your applications.

Content