DEV Community

Sivasubramanyam A
Sivasubramanyam A

Posted on • Originally published at

lego, not yet another Static Site Generator

I have been working for over a year on lego, a static site generator written in JavaScript. When I decided to rewrite my site, I had a wide variety of SSGs to choose from. I eventually narrowed down my choices to the following:

  • Gatsby - I really liked how optimised the output was with Gatsby. The only issue I had with Gatsby was the slow build timings.
  • Hugo - I loved Hugo for its speed. However, the templating language was frankly too disappointing.
  • Jekyll - This was fast enough for my site's size and the templating language, Liquid, was pretty similar to Jinja2. However, doing Gatsby like optimisations was challenging.

I had way too much free time on my hands so I did what every bored developer does. Write my own framework. In this case, I decided to write my own static site generator that emulates the good parts of these three frameworks.

I had a few requirements out of this and decided to try building it for a week or so and see how feasible this was.

  • Support Liquid(recently added Nunjucks support).
  • Support Markdown posts, data in YAML(later extended to JS and JSON).
  • Transpile and uglify JS with terser, use PostCSS for CSS(both use browserslist to determine targets).
  • Revision assets like JS, CSS and images.
  • Optimise images.
  • Extract and inline critical styles with critical for all pages.
  • Minify HTML of all pages.
  • Generate a tag-wise listing of posts. For example, site/tags/javascript should list all pages tagged JavaScript.
  • Live Reload during development.

Surprisingly it turned out pretty well. There were still a few rough edges but I quickly ported over my site in the hopes that I could fix them.

This is when the fun began.

lego was taking about 5-6 seconds to start up. While this was not that bad compared to Gatsby, I wanted to squeeze out as much performance out of it as I could. The whole purpose of this project was to learn something and have fun in the process so I decided to profile lego. A few interesting things stood out.

  • Just requiring PostCSS plugins was taking a second or two.
  • Some code paths were running more often than necessary.

After fixing these, it came down to about 2-4 seconds.

This is when I started looking into caching. While coming up with a technique for this was challenging, the actual implementation was rather trivial. After implementing caching, I felt satisfied with this whole endeavour of writing my own SSG. I now had a fast, performant SSG that generated an optimised site.

Right now, lego starts up a development server for my own site in under 600ms. Rebuilds take under a couple hundred milliseconds. Production builds take about 20 seconds!

I wrote a benchmark to test all this. When Liquid templates are used, lego is about 1.5x faster than Jekyll and when Nunjuck templates are used, lego is a cool 8.7x faster than Jekyll.

I then added a few more things like RSS Feed generation and sitemap generation based on my own needs. While lego supports auto-generating responsive images, I think it's a little less robust and I will start paying attention to it in the coming days.

If you are feeling adventurous and want to try this out,

  • Run npm i -g @astronomersiva/lego.

  • Generate a site using lego g my-awesome-site. This will generate a dummy site.

  • Run lego s to start the server.

You can also checkout the code on GitHub.

Have a great weekend folks!

Top comments (6)

astronomersiva profile image
Sivasubramanyam A • Edited

That's interesting. I think lego and eleventy have certain differences in the end goal. Lego tries to be like Gatsby while 11ty leaves all those decisions to the user.

Having said that, I put together a dummy project with a similar structure containing 100 markdown posts. Running lego with all the optimisations and caching disabled results in the following:

[22:12:20] › ▶  Cleaning build directory. Initialized timer...
[22:12:20] › ◼  Cleaning build directory. Timer run for: 38ms
[22:12:20] › ▶  Preparing site Initialized timer...
[22:12:20] › ◼  Preparing site Timer run for: 29ms
[22:12:20] › ▶  Creating posts from markdown Initialized timer...
[22:12:20] › ◼  Creating posts from markdown Timer run for: 97ms
funkysetiz profile image

Really cool job! I like that you kept RSS and other good dev options

astronomersiva profile image
Sivasubramanyam A

Thanks @Setiz!

Yes, most of the work that lego does is to make it easy for developers to work on their sites. There's also build time code highlighting so you don't have to run highlight.js or Prism.js on page load. Just a small CSS file will be sufficient and the code will be highlighted even if the users browse with JS disabled on their browsers.

astronomersiva profile image
Sivasubramanyam A

The bulk of lego's production build timing is spent in three tasks:

  • Copying static assets to the build directory. This might take longer when the project has too many images. My own site(where I got these numbers from) has 394 files in the static directory. This takes like 7-8 seconds. This runs in parallel to the HTML generation so the actual overhead might be about 3-4 seconds.
  • Image optimisation. This will again differ depending on the size and number of images in the project.
  • Inlining Critical Styles. This is the slowest task for my repo. By default, lego does this for every single page in the output. So a browser is spawned and all these pages are visited and analysed. On my site, this takes about 12 seconds.

If I disable image optimisation and inlining critical styles, I am able to hit sub 10 second production builds as well but the output files are still minified with htmlminifier.

I am curious if 11ty does these as well.

sm0ke profile image

Nice, why not another SSG.
Now I'm using 11ty (for simple LP and sites) and Gatsby (for blogs), but I will take a look at your tool.
Happy SSG-ion!

moopet profile image
Ben Sinclair

I think using the name "lego" might be a problem for legal reasons?