A small, fast CLI for building web assets without a ton of overhead or frameworks. Bodykit can bundle JavaScript, transform and minify CSS, and minify HTML. It is designed to be easy to drop into an existing project and automate common build tasks.
- License: GPLv3
- Package: @reallyspecific/bodykit
- Binary: bodykit
- A recent Node.js v22 or higher
- Yarn for package management (recommended)
Install as a dev dependency in your project:
yarn add -D @reallyspecific/bodykitThis exposes the bodykit binary in your project's node_modules/.bin.
Build everything in one go:
bodykit --build=all --in=./src --out=./distWatch for changes and rebuild automatically:
bodykit --watch --in=./srcTip: Add scripts to your package.json for convenience.
{
"scripts": {
"build": "bodykit --build=all --in=src --out=dist",
"watch": "bodykit --watch --in=src"
}
}Run them with:
yarn build
# or
yarn watchCommon flags:
-
--in=<path>
Input directory to process. Allows glob patterns. Example:--in=./src -
--out=<path>(optional, default is value of--in)
Output directory for built files. Allows glob patterns. Example:--out=./dist -
--build=[all|css|js|fonts|md](optional, default isall) Build supported asset types in one pass. This will recurse the--indirectory and compile specified assets using the same structure in the--outfolder. If no--outis specified, compiled files will be built in the same location as their source. -
--exclude=<pattern>(optional) Exclude files and directories from processing using glob patterns, comma separated in CLI or an array within a JSON config file. Supports multiple patterns when used in configuration. Files matching any exclude pattern will be skipped during compilation. Allows glob
patterns, default is**/node_modules/**. Can be set within compiler config options to apply to specific compiler types. Example:--exclude="temp/**" -
--ignore=<pattern>(optional) Exclude filename patterns from being compiled, comma separated in CLI or an array within a JSON config file. Can be set within compiler config options to apply to specific compiler types. Default is._*,*.map,*.min.*,*~Example:--exclude="temp/**" -
--watch
Start file watching and incremental rebuilds. -
--serve= Startup a local webserver with as the server root. Must be used with watch for now, and page will soft/hard reload when assets are recompiled.
Defaults to
https://localhost:8080, this can be changed using the--hostand--portparameters -
--help
Print the CLI help with the full list of supported flags for your installed version.
Examples:
- Build to a clean directory:
bodykit --build=all --in=src --out=dist- Watch only a specific input folder:
bodykit --watch --in=src/site- Exclude specific files or directories:
bodykit --build=all --in=src --out=dist --exclude="temp/**"Bodykit provides flexible file exclusion capabilities through glob patterns. Files and directories can be excluded from processing using the --exclude parameter or configuration settings.
Use the --exclude flag to exclude files from a single pattern:
# Exclude all files in any temp directory
bodykit --build=all --exclude="**/temp/**" --in=src --out=dist
# Exclude specific file types
bodykit --build=all --exclude="**/*.backup" --in=src --out=dist
# Exclude directories by name
bodykit --build=all --exclude="node_modules/**" --in=src --out=distFor more complex exclusion rules, use the package.json configuration:
{
"config": {
"bodykit": {
"exclude": [
"**/temp/**",
"**/*.backup",
"**/node_modules/**",
"drafts/**"
]
}
}
}You can also configure exclusions for specific asset types:
{
"config": {
"bodykit": {
"exclude": "**/global-exclude/**",
"js": {
"exclude": "assets/js/excluded/**"
},
"css": {
"exclude": [
"**/*.draft.css",
"temp/**"
]
}
}
}
}**/temp/**- Excludes all temp directories and their contents**/*.backup- Excludes all .backup filesdrafts/**- Excludes everything in the drafts directory**/exclude/*- Excludes files directly in any exclude directorytest-*/**- Excludes directories starting with "test-"
- Global exclusions apply to all file processing
- Asset-specific exclusions apply only when processing that asset type
- Patterns are cumulative - files matching any exclusion pattern are skipped
- Built-in exclusions automatically skip:
- Files starting with
.or_ - Files ending with
~ - Files ending with
.min(before extension)
- Files starting with
The exclusion system uses minimatch for pattern matching, supporting standard glob syntax.
Bodykit ships with a pragmatic toolchain, so you can expect:
- JavaScript bundling and optimization (esbuild)
- CSS transformation/minification (Lightning CSS)
- HTML minification
- Optional Markdown-to-HTML workflows if you incorporate Markdown sources
Exact behavior depends on your project layout and flags. Use --help to see the capabilities supported by your installed version.
Bodykit respects your project's Browserslist configuration. Add one to package.json or a .browserslistrc to control CSS/JS transforms:
{
"browserslist": [
"defaults",
"maintained node versions"
]
}Bodykit includes a flexible templating engine that supports two types of template tags: {@} template tags for complex operations and @@ shorttags for simple variable interpolation.
Template tags use curly braces with an @ symbol and provide advanced functionality like content inclusion, asset management, and loops.
{@tagname attribute="value" /}For tags with content:
{@tagname attribute="value"}
content here
{/}Generates HTML tags for CSS and JavaScript assets with automatic versioning.
Required attributes:
type- Asset type:"css"or"js"
Optional attributes:
name- Asset name (looks for compiled assets)path- Direct asset pathversion- Custom version parameteroptional- If true, won't throw error if asset missing
Examples:
{@asset type="css" name="default" /}
<!-- Outputs: <link type="text/css" rel="stylesheet" href="/path/to/default.min.css?v=abc123" id="default-css"> -->
{@asset type="js" path="/scripts/app.js" /}
<!-- Outputs: <script type="text/javascript" src="/scripts/app.js?v=abc123" id="app-js">/* empty */</script> -->
{@asset type="css" name="theme" optional="true" /}
<!-- Won't error if theme.min.css doesn't exist -->Renders content using a specified template or processes the current node's content through Markdown.
Optional attributes:
template- Template to use for rendering
Examples:
{@content template="page/body" /}
<!-- Renders current content using the page/body template -->
{@content /}
<!-- Renders current node's content through Markdown processor -->Includes another template by name.
Required attributes:
name- Template name to include
Example:
{@template name="head-global" /}
<!-- Includes the head-global template -->Iterates over content items with filtering and templating.
Optional attributes:
type- Content type to filter bymax- Maximum number of itemstemplate- Template to use for each item
Example:
{@loop type="blog" max="3" template="blog-excerpt" /}
<!-- Loops through up to 3 blog items, rendering each with blog-excerpt template -->Outputs dynamic content or variables.
Example:
{@echo content="Hello World" /}Template tags provide detailed error messages with file locations:
<!-- This will throw: Missing type for @asset tag at index.html:15 -->
{@asset name="styles" /}
<!-- This will throw: Could not find template: missing-template at index.html:23 -->
{@content template="missing-template" /}Shorttags provide a concise way to insert variables and formatted content using double @ symbols.
@@type:variable:formattype- Variable type (e.g., page, site, meta)variable- Variable nameformat- Optional formatting (e.g., date format)
<!-- Page/node variables -->
@@node:url
<!-- Global variables -->
@@global:url
<!-- Meta variables of currently parsed node -->
@@meta:description
@@meta:keywords
@@meta:title
@@meta:slug
@@meta:timestamp:Y-m-dPage metadata:
<title>@@meta:title - Website Title</title>
<meta name="description" content="@@meta:description">Formatted dates:
<time datetime="@@meta:timestamp:c">{@echo meta="timestamp" format="F j, Y" /}</time>Content variables:
<h1>@@meta:title</h1>
<p>Published on {@echo meta="timestamp" format="F j, Y" /}</p>Templates are automatically collected from your source directory:
src/
├── templates/
│ ├── default.html # Main page template
│ ├── head-global.html # Global head includes
│ └── page/
│ └── body.html # Page body template
└── content/
└── index.md
Templates are referenced by their path relative to the templates/ directory:
templates/head-global.html→{@template name="head-global" /}templates/page/body.html→{@content template="page/body" /}
If a specific template isn't found, the system falls back to templates/default.html.
See test/assets/in for a complete usage example.