- JavaScript 42%
- Nunjucks 31.4%
- CSS 20.3%
- HTML 5.2%
- Shell 1.1%
| docs/adr | ||
| examples/1-basic | ||
| picossg.dev | ||
| src | ||
| templates | ||
| test | ||
| .gitignore | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| LICENSE.md | ||
| package-lock.json | ||
| package.json | ||
| prod.sh | ||
| README.md | ||
picossg
The pico static site generator – the simplest one I ever wanted and hopefully "found". See the picossg.dev for more information.
What it does
picossg processes:
- files from the
contentdirectory into theoutputdirectory, processing the files if needed - Markdown files (
.md) using markdown-it - Nunjucks templates (
.njk) using nunjucks - Combined Nunjucks + Markdown files (
.md.njk) - Static assets (copied as-is)
It transforms these files into a static website with a 1:1 mapping from source director+file structure to output.
How to use it
- Make sure to have nodejs installed see here
- open a terminal
- run
npx @pic0/ssg -c content -o output, this builds all files in thecontentdirectory into theoutputdirectory - to serve, open another terminal, run
npx http-server output -p 0(-p 0searches for an available port, staring with 8080), open http://localhost:8080 (or the right port) in your browser and the files incontentare processed and served fromoutput
Getting Started
- create your new site's directory:
mkdir my-site; cd my-site - create the
contentdirectory:mkdir content - put the
package.jsonin the root of the project
{
"name": "my-ssg-site",
"version": "1.0.0",
"scripts": {
"build": "npx @pic0/ssg -c content -o output",
"start": "npx http-server output -p 0",
"build:watch": "npx nodemon --quiet --legacy-watch --watch content --ext '*' --exec \"bash -c 'npm run build'\""
}
}
- put some
*.html.mdor*.html.njkfile into thecontentdirectory, e.g. start withindex.html.md(you can start with just putting "Hello World!" in there, for more see "Create a Site" in the docs) - run
npm run build:watchin one terminal to start building the site - run
npm startin another terminal to start the server - open http://localhost:8080 in your browser to see the site (the port may be different, see the output of the
npm startcommand)
The npm run build script above (the code npx @pic0/ssg -c content -o output) will run the remote picossg
package (which is published on npmjs.org) and look for files in the content directory, process them and put them to the output directory.
The npm run start (or can also be called as npm start) script (the script npx http-server output -p 0)
will run the remote package http-server (also published on npmjs.org) and serve the files from the output directory on an available port (mostly 8080)
on your local computer, which you can then see in your browser at http://localhost:8080.
The npm run build:watch script uses yet another remote package nodemon to watch the content directory for changes
and re-run the build command whenever a file changes, so you don't have to call npm run build every time you change a file.
Examples
- see a basic example in this repo, it contains a simple index.html.njk file, start reading there
- a more complex example is the picossg.dev site, which is built with picossg of course, and contains a lot of examples, see the source code in this repo here
- a bit more complex example is a design implemented for the JSCraftCamp.org site for 2025, find it in the jscc-site-2025 repo – it uses only picossg to build the site
- my blog runs only on picossg the source code is here
Philosophy
The core philosophy of picossg is simplicity and predictability:
- 1:1 file mapping – Each source file maps directly to an output file with the same path (except for extension changes)
- Minimal magic – The only "magic" is:
- Files starting with underscore (
_*) are excluded from the output - Template files (
.njk,.md) are processed, afile.html.md.njkprocesses nunjucks and markdown and outputsfile.html, just by working off the extensions from the last one backward. Allows also forstyle.css.njkto be processed and output asstyle.cssif desired. - All other files are copied as-is
- Files starting with underscore (
- Asset handling – Any assets (CSS, images, etc.) in the source directory are copied to the output, as mentioned in 2. already
- _config.js – An optional
_config.jsfile in the content directory can providepreprocess()andpostprocess()functions, they both receive a map of all files and can be used to preprocess the content before rendering or postprocess the output after rendering.
There are no complex configuration options, no plugins, no middleware – just a simple, predictable build process.
How to develop PicoSSG
PicoSSG is developed in JavaScript, using Node.js. It uses Nunjucks for templating and Markdown-it for Markdown processing. To ensure that PicoSSG really still works as with the last commit, there are tests. The tests are on various levels:
- the most essential ones are those you find in
test/contentandtest/golden-ref, they compare the output of the build with the expected output, these tests are runnpm testandnpm run test:watch(in watch mode), withnpm run testdiffsyou can see the differences between the output and the expected output in case the tests failed - the second layer is testing the preprocessor, which finds all the files to be processed, see the file [test/content/_config_preprocess_assertions.js]
the function
const preprocess()at the bottom, the test names should explain what is tested. These tests are separate because they run during the build step, see thenpm run test:preprocesscommand it just runs the build step with using the config file_config_preprocess_assertions.js, which contains our tests. You can run these in watch mode too, which isnpm run test:preprocess:watch. - the third layer is the smoke tests, which are run with
npm run test:smoke(there is no watch mode). These I introduced when I started testing the better error messages that are given during the build process, and for that smoke is just THE TOOL. Each test runs a build and the test is about comparing the console output of the build with some expected output. See the [test/smoke.yaml] file, which contains the tests, and the [test/njk-errors] directory, which contains the "broken" templates that are used to test the error messages.
Set up
To develop picossg, clone the repository and install the dependencies:
git clone https://codeberg.org/wolframkriesing/picossg.gitcd picossgnpm inpm run dev:installwhich will install tools needed in development, like smoke, which need to be installed once
Does it all work?
You might hate tests, but the least these do here is: tell you if all the things work as expected, on your machine ;) and if it makes sense to even start changing any code. Because if tests fail already the setup might need some love first. (Might also be that the provided setup has a flaw, then report it please.) So I encourage you to run the tests first.
npm run test:ciruns all the tests, just not in watch modenpm run test:watchruns the golden-ref tests in watch mode, and re-runs on any content changetest:preprocess:watchruns the preprocess tests in watch mode, they can NOT run in parallel with thenpm run test:watchtests, since they are using the same_outputdirectory and would overwrite each other, therefore the finalnom run test:cicommand does the "final" test before a releasenpm run test:smokeruns the tests that tests the njk error messages
The npm test runs a simple diff command, it compares the generated _output directory with the expected output in test/golden-ref.
Since the default npm run build:watch runs multiple times when you change the source code, e.g. build.js
there is also build:watch:dev which only watches the code to be run and NOT the content to be generated.
Run it via docker
- you can use
docker-compose run --rm --remove-orphans picossg_node node src/build-cli.js -c content -o _outputto run the build - to build the content from the directory picossg.dev into
_picossg.devdirectory you can usedocker-compose run --rm --remove-orphans picossg_node npm run build:site