
🐢 scute
buildy bundly buddies for websites and web apps
npm install --save-dev @e280/scute
@e280/scute contains three lil buddies:
- 🐢 #scute — eponymous zero-config build cli
- 🪄 #ssg — html templating library for static site generation
- 🐙 #octo — watch routine terminal multiplexer cli
👷 project setup
"the golden path."
just an example of how we like to setup our projects
- we setup our typescript apps with ts code in
s/dir, and outputting tox/dir- we install a server like
http-serverfor previewing our app in development- we setup a
tests.test.tstest suite with @e280/science- we add these scripts to package.json
"scripts": { "build": "rm -rf x && tsc && scute -v", "test": "node x/tests.test.js", "watch": "npm run build && octo 'scute -vw' 'tsc -w' 'node --watch x/tests.test.js' 'http-server x'" }- now we can run these commands
npm run build— run your project buildnpm run watch— start a watch routinenpm run test— run our test suite
🐢 scute cli
zero-config build tool and static site generator
scutecommand builds your projectscute -vfor verbose mode so you can see what's upscute -vwfor watch modescute --help``` 🐢 scute {params}readme https://github.com/e280/scute zero-config static site generator - bundles .bundle.js files with esbuild - copies files like .css and .json - builds .html.js template files - executes .exe.js scripts
--watch, -w, flag boolean
watch mode
--in, default string-list s
dirs to read from
--out, default string x
output dir
--bundle, default boolean yes
should we bundle .bundle.js files?
--copy, default string-list **/*.css,**/*.json,**/*.txt
what files should we copy verbatim?
--html, default boolean yes
should we build .html.js templates?
--exe, default boolean yes
should we execute .exe.js scripts?
--debounce, default number 200
milliseconds to wait before watch routine build
--exclude, optional string-list
what files should we ignore?
--verbose, -v, flag boolean
should we log a bunch of crap?
```
- by default we use
s/andx/, instead ofsrc/anddist/. it's our weird e280 tradition. if you wanna be a normie, do this:scute --in="src" --out="dist"
🪄 ssg html templating library
library for static site generation
import {ssg, html} from "@e280/scute"
ssgis a toolkit for templating and paths and stuffhtmlis for writing html, and interleaving it with async jsorbdoes path/url magic and hash-version cache-busting
ssg.page quickstart homepage
make a web page. it makes an <html> document. let's make
index.html.tsexport default ssg.page(import.meta.url, async orb => ({ title: "cool website", js: "main.bundle.min.js", css: "main.css", dark: true, favicon: "/assets/favicon.png", head: html` <meta name="example" value="whatever"/> `, socialCard: { themeColor: "#8FCC8F", title: "scute", description: "buildy bundly buddies", siteName: "https://e280.org/", image: `https://e280.org/assets/e.png`, }, body: html` <h1>incredi website</h1> `, }))did you notice the
orb? we must'nt yet speak of the all-powerful orb..ssg.pageis just sugar which produces an ordinaryTemplateFn
html templating
- html templating fns, with protection against injection attacks
- basic usage
html`<div>hello</div>` - it returns an
Htmlinstanceconst h = html`<div>hello</div>` h.toString() // "<div>hello</div>" - strings are safely sanitized
html`<div>${"<evil/>"}</div>`.toString() // "<div><evil/></div>" html.rawto circumvent sanitization (🚨 allowing injection attacks, lol)html`<div>${html.raw("<evil/>")}</div>`.toString() // "<div><evil/></div>"html.renderproduces a string. it's async and resolves injected promises
the rendering is handled automatically by the scute cliawait html`<div>${Promise.resolve("async magic")}</div>`.render() // "<div>async magic</div>"
ssg.template pages
ssg.templateproduces aTemplateFn.
if it's the default export, and your module has a.html.tsor.html.jsextension, the scute build cli will automatically build the.htmlpage.page.html.tsimport {partial} from "./partial.js" export default ssg.template(import.meta.url, async orb => html` <!doctype html> <html> <head> <title>scute</title> <script type="module" src="${orb.hashurl("main.bundle.min.js")}"></script> </head> <body> <h1>scute is good</h1> ${orb.place(partial)} </body> </html> `)orb.placeto insert one template into another, while maintaining relative pathing..
ssg.template partials
- we can inject one ssg template into another. let's make
partial.tsexport const partial = ssg.template(import.meta.url, async orb => html` <div> <img alt="" src="${orb.url("../images/turtle.avif")}"/> </div> `)- orb.url is relative to our module,
partial.ts
- orb.url is relative to our module,
🔮 the almighty orb
relative paths and urls
every template gets an orb
- the orb's superpower is dealing with paths and urls
- the orb allows you to reference files relative to the current template module,
regardless of how you import html partials from all around your codebase.
this should impress you.
orb pathing fns
orb.url("main.css")— 🧙♂️ important! for clientside browser urls!
this outputs a browser url relative to the page (not partial).
don't get confused here! the input is relative following the magic conventions. the output is page-relative.
eg, you can use these urls as<script>srcand such.orb.path("main.css")— 🧙♂️ important! for serverside filesystem paths!
this outputs a filesystem path relative to the current working directory.
eg, you can use these paths in nodereadFilecalls and such.orb.hashurl("main.css")— 🧙♂️ important! saves you from browser cache problems!
likeorb.url, but it attaches a hash-version query param to the url.
looks likemain.css?v=cdd9738a8eda.
this allows the browser to properly cache that exact version of the content.
anything usingorb.hashurlwill not have stale caching problems in your deployments.
yes, it's reading the target file on disk and producing a sha256 hash of it.
orb's magic pathing conventions
- 🧙♂️ important! all orb functions that expect path strings respect these conventions
"main.css"— relative to the current template module"./main.css"— relative to the current template module (same as above)"/main.css"— relative to the server root (aka, your scute --out dir, maybex/)"$/main.css"— relative to the build process current working directory
more orb fns
orb.place(partial)
prepare a partial template for insertion into this template, preserving relative pathing magic.orb.inject("main.css")
read the contents of that file, and inject it raw without sanitization.
used to insert text directly, like <style>, <script>, json, stuff like that.orb.packageVersion()
returns theversionstring found in yourpackage.json.
orb.io file operations
orb.io.read("main.css")— read a text fileorb.io.write("main.css", "* {}")— write a text fileorb.io.readJson("$/package.json")— read and parse a json fileorb.io.writeJson("$/package.json", {})— write json to a file
ssg.exe executable scripts
- your
.exe.tsmodules will be automatically executed, and they must provide a default exported exe fn like this:export default ssg.exe(import.meta.url, async orb => { await orb.io.write("blog.txt", "lol") }) - this gives you access to an orb, which is useful for resolving paths relative to this module.
- eg, imagine a script like
blog.exe.tswhere you read hundreds of markdown files and emit a webpage for each, or something like that.
🐙 octo cli
tiny watch routine terminal multiplexer
octo 'scute -vw' 'tsc -w'command runs your watch routine- each subcommand gets its own pane
- press
[and]to shimmy between panes, q to quitocto --help``` 🐙 octo ...commandstiny terminal multiplexer for watch routines
...commands,
each command gets its own pane that you can flip between.
for example,
$ octo "scute -vw" "tsc -w"
this will give you two panes,
- press 1 to see the scute output
- press 2 to see the tsc output
- press [ or h or j to shimmy left
- press ] or l or k to shimmy right
- press backspace to clear the pane
- press q or ctrl+c to quit
local npm bin is available,
$ scute -vw # GOOD this works
$ npx scute -vw # BAD npx is unnecessary
```
- here's a typical 4-pane watch routine with octo
octo \ "scute --verbose --watch" \ "tsc -w" \ "node --watch x/tests.test.ts" \ "http-server x"
🧑💻 scute is by e280
reward us with github stars
build with us at https://e280.org/ but only if you're cool