Documentation
Starting
Structuring
Styling
Go Static!
Bonus!
Static Sites: Embrace the Essence of the Web!
Static sites are popular for their simplicity, efficiency, and several advantages over traditional dynamic websites. Unlike dynamic sites that generate content on the fly, static sites are pre-rendered HTML, CSS, and JavaScript files that are served directly to users' browsers. This approach offers several benefits:
Speed and Performance: Static sites load faster due to the absence of server-side processing or database queries, resulting in quicker page loads and a smoother user experience.
Security: With no server-side processing or database interactions, static sites are inherently more secure against vulnerabilities like SQL injection or server attacks.
Scalability: They handle high traffic volumes more efficiently because each page is a simple file served by a web server, reducing server load and enabling seamless scaling.
Simplicity and Lower Costs: Building and maintaining static sites is often simpler and less resource-intensive compared to dynamic sites. They require minimal server resources and can be hosted inexpensively on content delivery networks (CDNs).
Reliability: Since static sites are composed of static files, they are less prone to downtime or performance issues caused by server-side problems.
SEO Friendliness: Pre-rendered content makes it easier for search engines to crawl and index pages, potentially improving search engine rankings.
Overall, static sites offer a robust solution for many use cases, providing speed, security, and scalability while simplifying development and maintenance efforts.
Generating
Let's convert our content into a static site using LiteNode's renderToFile method, which makes this process simple and straightforward. While there are many approaches to generating a static site and numerous considerations for error handling, this tutorial focuses on the basics due to the simplicity of our small application. I'll walk you through the fundamental steps without additional complexities or enhancements.
Build Script
Create a file named build.js
in the functions
directory and add the following code:
import { LiteNode } from "litenode"
import { markedParseAndHighlight } from "./markedParseAndHighlight.js"
import { cp, mkdir, rm } from "node:fs/promises"
const app = new LiteNode()
const pages = await app.parseMarkdownFileS("pages")
const titles = await app.extractMarkdownProperties(pages, ["title", "href"])
titles.sort((a, b) => (a.title < b.title ? -1 : 1))
async function build() {
try {
// Remove existing "_site" directory if it exists
await rm("_site", { recursive: true, force: true })
// Create output directories
await mkdir("_site", { recursive: true })
await mkdir("_site/static", { recursive: true })
// Copy static folder to _site/static
await cp("static", "_site/static", { recursive: true })
// Define function to create the main route
async function createMainRoute() {
const parsedIndex = app.parseMarkdownFile("index.md")
const html_content = markedParseAndHighlight.parse(parsedIndex.content)
const { title, description } = parsedIndex.frontmatter
// Create "index.html" in "_site" directory
await app.renderToFile(
"layouts/index.html",
{
title,
description,
html_content,
entryRoute: true,
titles,
},
"_site/index.html"
)
}
// Define function to create each page route
async function createPageRoute() {
for (const page of pages) {
try {
const fileFrontmatter = page.frontmatter
// Added to output each page in a directory matching its href
const fileHREF = fileFrontmatter.href
// Create a folder for each page
await mkdir(`_site/page/${fileHREF}`, { recursive: true })
const htmlContent = markedParseAndHighlight.parse(page.content)
const toc = app.generateTOC(htmlContent)
// Create HTML file out of each page
await app.renderToFile(
"layouts/index.html",
{
description: fileFrontmatter.description,
title: fileFrontmatter.title,
html_content: htmlContent,
titles,
pageRoute: true,
html_toc: toc,
},
`_site/page/${fileHREF}/index.html`
)
} catch (error) {
console.error(`Error processing page: ${page.fileName}`, error)
}
}
}
// Execute the route creation functions
createMainRoute()
createPageRoute()
} catch (error) {
console.error("Build error:", error)
}
}
build()
Build Explanation
Let's break down the build.js
script into smaller sections and explain each part:
Import and Initialize
import { LiteNode } from "litenode"
import { markedParseAndHighlight } from "./markedParseAndHighlight.js"
import { cp, mkdir, rm } from "node:fs/promises"
const app = new LiteNode()
In this section:
- We import the
LiteNode
framework,markedParseAndHighlight
module, and some file system methods from Node.js. - An instance of
LiteNode
is created.
Parse and Sort
const pages = await app.parseMarkdownFileS("pages")
const titles = await app.extractMarkdownProperties(pages, ["title", "href"])
titles.sort((a, b) => (a.title < b.title ? -1 : 1))
In this section:
- We use
parseMarkdownFileS
to parse all markdown files in the 'pages' directory. - The
extractMarkdownProperties
method extracts thetitle
andhref
properties from the parsed pages. - The
titles
array is sorted alphabetically by the title.
Build Function
async function build() {
try {
// Remove existing "_site" directory if it exists
await rm("_site", { recursive: true, force: true })
// Create output directories
await mkdir("_site", { recursive: true })
await mkdir("_site/static", { recursive: true })
// Copy static folder to _site/static
await cp("static", "_site/static", { recursive: true })
In this section:
- The
build
function is defined. - If the
_site
directory exists, it's removed. - New
_site
and_site/static
directories are created. - The
static
folder is copied to_site/static
.
Create Main Route
// Define function to create the main route
async function createMainRoute() {
const parsedIndex = app.parseMarkdownFile("index.md")
const html_content = markedParseAndHighlight.parse(parsedIndex.content)
const { title, description } = parsedIndex.frontmatter
// Create "index.html" in "_site" directory
await app.renderToFile(
"layouts/index.html",
{
title,
description,
html_content,
entryRoute: true,
titles,
},
"_site/index.html"
)
}
In this section:
- The
createMainRoute
function is defined to handle the main route. - The
index.md
file is parsed. - The content is converted to HTML using
markedParseAndHighlight
. - The title and description are extracted from the frontmatter.
- An
index.html
file is created in the_site
directory using the specified layout and data.
Create Page Routes
// Define function to create each page route
async function createPageRoute() {
for (const page of pages) {
try {
const fileFrontmatter = page.frontmatter
// Added to output each page in a directory matching its href
const fileHREF = fileFrontmatter.href
// Create a folder for each page
await mkdir(`_site/page/${fileHREF}`, { recursive: true })
const htmlContent = markedParseAndHighlight.parse(page.content)
const toc = app.generateTOC(htmlContent)
// Create HTML file out of each page
await app.renderToFile(
"layouts/index.html",
{
description: fileFrontmatter.description,
title: fileFrontmatter.title,
html_content: htmlContent,
titles,
pageRoute: true,
html_toc: toc,
},
`_site/page/${fileHREF}/index.html`
)
} catch (error) {
console.error(`Error processing page: ${page.fileName}`, error)
}
}
}
In this section:
- The
createPageRoute
function is defined to handle each individual page route. - For each page:
- We extract the frontmatter.
- A directory is created for each page based on its
href
. - The content is converted to HTML using
markedParseAndHighlight
. - A Table of Contents (TOC) is generated.
- An HTML file is created for each page using the specified layout and data.
Execute Build
// Execute the route creation functions
createMainRoute()
createPageRoute()
} catch (error) {
console.error("Build error:", error)
}
}
build()
In this section:
- The
createMainRoute
andcreatePageRoute
functions are called to create the necessary routes. - Any errors during the build process are caught and logged.
Usage
To automate the build process, you can add a script to your package.json
file. This will allow you to run the build command easily from the terminal.
- Open your
package.json
file. - Add the following script under the "scripts" section:
"scripts": {
"build": "node functions/build.js"
}
- Save the
package.json
file.
Now, you can build your static site by running the following command in your terminal:
npm run build
Running this command executes the build.js
script, generating your static site in the _site
directory.
Summary
- This script automates building a static site, making it simple and efficient.
- It parses markdown files, sorts them, and generates HTML files using templates.
- It handles the main index route and individual page routes, creating necessary directories and copying static assets.
Final Structure
Here is the finalized tree structure of our application after generating a static site using the npm run build
command:
📂 litenode-markdown-app (root directory)
├── 📂 _site (Generated static site directory)
│ ├── 📄 index.html (Static site entry file)
│ ├── 📂 page (Static site's page directory)
│ │ ├── 📂 add-ids-to-headings
│ │ │ └── 📄 index.html (add-ids-to-headings page)
│ │ ├── 📂 basic-usage
│ │ │ └── 📄 index.html (basic-usage page)
│ │ ├── 📂 error-handling
│ │ │ └── 📄 index.html (error-handling page)
│ │ ├── 📂 installation
│ │ │ └── 📄 index.html (installation page)
│ │ └── 📂 middleware
│ │ └── 📄 index.html (middleware page)
│ └── 📂 static (Static site's assets directory)
│ ├── 📂 css (Static site's styles directory)
│ │ ├── 📄 a11y-dark.min.css (Static site's highlighting stylesheet)
│ │ └── 📄 responsive-attributes.css (Static site'sCSS Grid stylesheet)
│ ├── 📂 images (Static site's images directory)
│ └── 📂 js (Static site's scripts directory)
├── 📂 functions (App's functions directory)
│ ├── 📄 build.js (App's build function)
│ └── 📄 markedParseAndHighlight.js (App's parse and highlight function)
├── 📄 index.js (App's entry file)
├── 📄 package-lock.json (App's auto-generated file)
├── 📄 package.json (App's settings file)
├── 📂 routes (App's routes directory)
│ ├── 📄 entryRoute.js (App's entry route)
│ └── 📄 pageRoute.js (App's page route)
├── 📂 static (App's static assets directory)
│ ├── 📂 css (App's styles directory)
│ │ ├── 📄 a11y-dark.min.css (App's highlighting stylesheet)
│ │ └── 📄 responsive-attributes.css (App's CSS Grid stylesheet)
│ ├── 📂 images (App's images directory)
│ └── 📂 js (App's scripts directory)
└── 📂 views (App's views directory)
├── 📂 components (App's components directory)
│ ├── 📄 footer.html (App's reusable footer template)
│ ├── 📄 head.html (App's reusable head template)
│ ├── 📄 header.html (App's reusable header template)
│ └── 📄 main.html (App's reusable main template)
├── 📄 index.md
├── 📂 layouts (App's layouts directory)
│ └── 📄 index.html (App's main layout template)
└── 📂 pages (App's Markdown pages directory)
├── 📄 add-ids-to-headings.md
├── 📄 basic-usage.md
├── 📄 error-handling.md
├── 📄 installation.md
└── 📄 middleware.md
Preview
To preview the content of the _site
directory in a browser from an IDE like VS Code, you can follow these steps:
Install the Live Server Extension in VS Code:
- Open VS Code and go to the Extensions view by clicking on the Extensions icon in the Sidebar or pressing
Ctrl+Shift+X
. - Search for "Live Server" and install it.
- Open VS Code and go to the Extensions view by clicking on the Extensions icon in the Sidebar or pressing
Open the
_site
Directory in VS Code:- Open the
_site
directory in VS Code by navigating toFile > Open Folder...
and selecting the_site
directory.
- Open the
Start Live Server:
- Right-click on the
index.html
file inside the_site
directory and selectOpen with Live Server
. - This will start a local development server and open your default browser to display the contents of the
_site
directory.
- Right-click on the
Alternatively, you can use other tools or IDEs with similar live server functionality.
Publish
You can easily publish the _site
directory content on various platforms that host static sites. Some popular options include:
Azure Static Web Apps:
- Azure Static Web Apps provides a streamlined hosting option for static sites with built-in CI/CD workflows.
Cloudflare Pages:
- Cloudflare Pages offers a fast, secure, and free way to deploy static sites directly from your Git repository.
Deta Space:
- Deta Space is a platform for hosting personal and collaborative apps, including static sites.
GitHub Pages:
- GitHub Pages allows you to host static sites directly from a GitHub repository. Simply push your
_site
directory to thegh-pages
branch of your repository.
- GitHub Pages allows you to host static sites directly from a GitHub repository. Simply push your
GitLab Pages:
- GitLab Pages allows you to host static sites from a GitLab repository. Configure the
.gitlab-ci.yml
file to build and deploy your_site
directory.
- GitLab Pages allows you to host static sites from a GitLab repository. Configure the
Netlify:
- Netlify offers a simple way to deploy static sites. You can connect your repository, specify the build command (
npm run build
), and set the publish directory to_site
.
- Netlify offers a simple way to deploy static sites. You can connect your repository, specify the build command (
Render:
- Render provides a unified cloud platform to deploy static sites quickly and efficiently.
Surge:
- Surge is a simple and straightforward platform for publishing static sites. Install the Surge CLI and deploy your
_site
directory with a single command.
- Surge is a simple and straightforward platform for publishing static sites. Install the Surge CLI and deploy your
Vercel:
- Vercel provides seamless deployment for static sites. Connect your repository, and Vercel will automatically handle the build and deployment process. Set the output directory to
_site
.
- Vercel provides seamless deployment for static sites. Connect your repository, and Vercel will automatically handle the build and deployment process. Set the output directory to
By using these platforms, you can easily publish and share your static site with others.
Bonus
Don't stress about mastering Markdown syntax! To make things easier for you, I've included a handy Markdown Cheat Sheet as a bonus with this tutorial. Happy writing!