I started by building a static site as a small side project for my brother—but then I wanted partials... and regression tests. I thought partials which could help me inline the CSS and JS tags while breaking the code up into different files for organizational purposes in development. I like to inline the assets to avoid render-blocking latency on simple landing pages which will likely be served over unreliable network conditions.
At first I really didn't think I needed a generator at all, but one thing led to another and I kind built a basic one myself.
It consists of a build.rb
file that looks like this...
prod_build = ARGV[0] == "for_prod" # Read files meta_html = File.open("workspace/meta.partial.html").read style_css = File.open("workspace/style.partial.css").read body_html = File.open("workspace/body.partial.html").read json_data = File.open("workspace/data.json").read scaffold_js = File.open("workspace/scaffold.partial.js").read dynamic_js = File.open("workspace/dynamic.partial.js").read analytics_html = File.open("workspace/analytics.partial.html").read base_html = File.open("workspace/base.html").read test_html = "" unless prod_build test_html = File.open("workspace/test.dev.html").read end # Create built page build_string = base_html .gsub("{{ meta }}", meta_html) .gsub("{{ style }}", style_css) .gsub("{{ html }}", body_html) .gsub("{{ data }}", json_data) .gsub("{{ scaffold_script }}", scaffold_js) .gsub("{{ dynamic_script }}", dynamic_js) .gsub("{{ analytics }}", analytics_html) .gsub("{{ test }}", test_html) # Write to target page if prod_build puts "Production build.... index.html" File.write("index.html", build_string) else puts "Development build.... wip-index.html" File.write("wip-index.html", build_string) end
I could DRY up this code, but I prefer it to be dumb and super explicit at this stage.
As you can see, this is just basic string find and replace. {{
could just as easily have been 💩💩
or [cromulent >>
. It's completely arbitrary, but {{}}
looked fancy.
base.html
looks like this...
<html lang="en"> <head> {{ meta }} <style> {{ style }} </style> </head> <body> {{ html }} <script> // Data var data = {{ data }} // Code {{ scaffold_script }} {{ dynamic_script }} </script> {{ analytics }} {{ test }} </body> </html>
...I even wrote my own dependency-free JavaScript test suite. I'll share more once it's further along.
I probably should have reached for an existing static site generator instead of doing this from scratch, so why did I take this approach?
In all seriousness, I generally like to avoid dependencies when doing projects like this so it's easier to hop in for a quick change in the future without having to install a bunch of old dependencies. Building a whole toolchain myself is sort of silly, but fun!
If you don't want to be like me, you may want to check out this great thread...
Happy coding!
Top comments (28)
I like this approach because, even though you're most likely reinventing the wheel, you often learn new stuff along the road and learning is the most important thing in our industry. Hats off to you.
😊
I agree about keeping dependencies low in small projects! When I hop between multiple projects, I can feel the context switching cost if there are setups involved.
By the way, this looks like a great use-case for…
I haven’t used PHP in years... and this seems very appealing!
What stack do you primarily use? Thanks for your response beforehand.
Blasphemy! Beautiful blasphemy!
BlasPHPemy!
That’s awesome!!! 👏
I’ve been there too, I once was tinkering with markdown parsing when I built this small library (link below) and it turned out that the live demo I made of using it could be seen as a somewhat static site generator from markdown files
I also felt kinda silly initially but now I want to revisit it as it was kinda fun too (and also try building a dependency free one from scratch like yours)
A react hook for parsing markdown with marked and sanitize-html
useMarked() hook
Live Demo
The app located at
/example
demonstrates how it could be used, see the live result at this-fifo.github.io/use-marked-hook/Install
Usage
License
MIT © Filipe Herculano
Neat!
This is mine. I decided to use erb since its not really much different than using handlebars.
Best part is if you don't use any ruby's gems other than standard easy to put on a lambda and use CodeBuild and CodePipeline to automatically deploy changes to S3 Static Website Hosting.
For my use case I have to compile at least a thousand pages and doing it this way is under a 1 minute
I had built a simple static site generator back before CMSs were a thing and SSGs had a name for themselves. I believe in those days everyone working on web development had something similar for organizing site development right ?
I had built one from scratch as well. Mine is mainly used for minimizing page size and optimizing page rendering, hot-reloading is included for development.
The code above is about 3 years old, feel free to run it at your own risk.
Lol I love that we're greeted with a
:shrug:
banner as we open the article.Also, yeah, if you have time to build an interesting side project while building a side project, go for it. If I'm reaching for a static site generator, I like to use Nuxt at the moment—although I'm already partial to Vue and I haven't done anything crazy with it yet.
Cool learning exercise. Have you checked out Svelte? It’s got a similar tooling stack to react and angular but imho much lighter weight and less quirky you can definitely build static sites with it.
Just throwing it out there. Hope you’re doing well Ben.
svelte.dev/
That's how I wound up with my current site. In late 2014 I was using Octopress, which was fine, but I don't really use Ruby professionally and so I thought ideally I'd be using a Node.js solution. On a whim I rolled a very simple proof of concept for a Grunt plugin to convert Markdown and Handlebars templates into HTML, and put together a Yeoman generator to set it up with some other plugins. It went so well that in early 2015 I switched over to it and have been using it since, though lately I have been considering switching to Gatsby.
The advantage is that you only get what you need, no less no more, minimal overhead (both mental and memory/cpu/LOC).
The disadvantage is that, when you find out that you need more, you'll run into limitations, and at that point you'll have to decide - add more features (and risk losing the charm & the simplicity), or migrate to a "real" SSG.
But if you know that your requirements will stay simple then it may be a good choice!