Static Site
LiteNode can render your app to plain HTML files — no server required to host them. You run a build script once, and the output is a folder of HTML, CSS, and JS files ready to deploy anywhere.
How it works
LiteNode's renderToFile(template, data, outputPath) method does the same thing as res.render() but writes the result to a file instead of sending it to a browser. You call it for each page and the _site/ directory fills up with the static equivalents.
Create the build script
This section provides an example of how to write a build script. The script will vary depending on the application's structure and the features being added.
Create functions/build.js:
import { app, marked } from "./initialize.js"
import { buildMenu } from "./buildMenu.js"
import { cp, mkdir, rm } from "node:fs/promises"
// Parse everything once
const index = app.parseMarkdownFile("index.md")
const pages = await app.parseMarkdownFileS("markdown")
const grouped = await app.groupByMarkdownProperty(
"markdown",
["metadata.category", "metadata.catIndex", "metadata.subcategory", "metadata.subCatIndex", "href"],
"metadata.category",
)
const mainMenu = buildMenu(grouped)
async function build() {
// Clean previous output
await rm("_site", { recursive: true, force: true })
// Create directories
await mkdir("_site/static", { recursive: true })
await mkdir("_site/tutorial", { recursive: true })
// Copy static assets
await cp("static", "_site/static", { recursive: true })
// Build each page
await buildHomepage()
await buildTutorialPages()
await build404()
console.log("✓ Built to _site/")
}
async function buildHomepage() {
const html_content = marked.parse(index.content)
const { title, description } = index.frontmatter
await app.renderToFile(
"layouts/index.html",
{ title, description, html_content, entryRoute: true },
"_site/index.html",
)
}
async function buildTutorialPages() {
for (const page of pages) {
const { href, title, description } = page.frontmatter
await mkdir(`_site/tutorial/${href}`, { recursive: true })
const html_content = marked.parse(page.content)
const html_toc = app.generateTOC(html_content)
await app.renderToFile(
"layouts/index.html",
{ mainMenu, title, description, html_content, html_toc, tutorialRoute: true },
`_site/tutorial/${href}/index.html`,
)
}
}
async function build404() {
await app.renderToFile(
"layouts/index.html",
{ title: "Page Not Found", description: "Page not found.", notFoundRoute: true },
"_site/404.html",
)
}
await build()
Add the build script to package.json
"scripts": {
"build": "node functions/build.js"
}
Run the build:
npm run build
Output structure
_site/
├── index.html
├── 404.html
├── tutorial/
│ ├── installation/
│ │ └── index.html
│ ├── basic-usage/
│ │ └── index.html
│ └── ...
└── static/
└── css/
└── app.css
Each tutorial page gets its own directory so clean URLs work without a server rewriting them.
Preview locally
Install the Live Server extension in VS Code, right-click _site/index.html, and select Open with Live Server. Or use any static file server:
npx serve _site
Deploy
Any static hosting platform can serve the _site/ directory:
- Cloudflare Pages — connect your repo, set build command to
npm run build, output directory to_site - GitHub Pages — push
_site/to thegh-pagesbranch, or configure Pages to serve from it - Netlify — same as Cloudflare: build command
npm run build, publish directory_site - Vercel — set output directory to
_site - Surge —
npx surge _site
The build takes under a second for a project this size, so CI/CD pipelines on any of these platforms will feel instant.
