🐢 @benev/turtle
slow and steady static site generator
- turtle is a one-line command that generates a website
- you write html templates in plain javascript
- templates are just async functions, so you can do anything
- you can kind of imagine it like some kind of static javascript "php"
- turtle also copies other files like css and whatnot
- turtle has proper typescript typings
turtle turtorial
note, turtle doesn't care whether you use typescript or plain javascript, but in the examples here i'll be using the two interchangeably.
run turtle to generate a website
npx @benev/turtle --in="s/demo:x/demo" --out="x/demo" --verbose="true"
![image: turtle example output](https://i.imgur.com/IpAi0rF.png)
ask turtle for help
npx @benev/turtle --help
![image: turtle help page](https://i.imgur.com/Zpf4Iqk.png)
write your first webpage template, like index.html.js
turtle will sniff out your .html.js
files, and render them into html pages.
import {webpage, html} from "@benev/turtle"
export default webpage(async({v}) => html`
<!doctype html>
<html>
<head>
<title>@benev/turtle</title>
<link rel="stylesheet" href="${v("/style.css")}"/>
</head>
<body>
<h1>@benev/turtle</h1>
</body>
</html>
`)
you can write template partials
it can accept a context object
you tell turtle to ignore it with --exclude="**/*.partial.html.js"
page.partial.html.ts
import {webpage, html} from "@benev/turtle"
export default webpage<{x: number}>(async({v}, {x}) => html`
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>@benev/turtle - stamp test</title>
<link rel="stylesheet" href="${v("/style.css")}"/>
</head>
<body>
<h1>@benev/turtle - stamp test</h1>
<p>${x}</p>
</body>
</html>
`)
write your first turtle script, like stamp.turtle.js
turtle also sniffs out .turtle.js
scripts and executes them.
in these, you can do anything you want. your turtle script function is provided some handy stuff like the write_webpage
function.
stamp.turtle.ts
import {turtle_script} from "@benev/turtle"
import page from "./page.partial.html.js"
const values = [1, 2]
export default turtle_script(async({write_webpage}) => {
await Promise.all(values.map(async(x) => {
await write_webpage({
template: page,
context: {x},
destination: `${x}.html`,
})
}))
})
you've gotta get into hash versioning!
- that's what the above example is doing with that
v
function - you use
v
on your urls, and v
will attach that file's hash as a suffix - so
/style.css
becomes /style.css?v=c252882f
- now when you deploy your site, your users won't see old cached css files that break your website -- now the browser cache becomes version aware! 🤯
remember, the templates are just async js functions
- so you can import other modules, read and write files, whatever you want
- thanks to top-level await, you could have a module read yaml files or whatever, and then templates can import that data
be sure to escape globs
- if you provide a glob to a flag like
--exclude="partials/**/*"
-- be sure to use double quotes so that your shell doesn't expand the glob -- the double quotes tells your shell to pass the literal glob to turtle, which will then process the glob properly (if you let the shell expand the glob, it won't work)