loading...
Cover image for Cleaver - A blazing-fast static site generator using Laravel’s Blade

Cleaver - A blazing-fast static site generator using Laravel’s Blade

aschmelyun profile image Andrew Schmelyun ・6 min read

tl;dr: github.com/aschmelyun/cleaver for more info

I know what you’re probably thinking, “Oh boy, another static site generator”. And you’d be right, but I’m hoping that the one I’ve created is a little different than ones you’ve been exposed to.

Instead of trying to be multiple things for multiple people, or instead of trying to be as friendly as possible to everybody, I've designed Cleaver to be as open to developers as possible.

Under the hood it's a pretty simple application, the core of which is just over 700 lines of code.

The main Cleaver program, where it all starts

Where did it come from?

Born out of scratching my own itch, Cleaver was designed to help me rapidly build data-driven landing pages. The problem that I had with other SSGs on the market is that you're usually stuck between Markdown, or the language that the generator is written in. This wouldn't be a problem for something like a blog, or any content-heavy websites, but I ran into an issue trying to build documentation sites and online spec sheets.

I really wanted to be able to build a site from JSON, just like I would be if I was getting data returned to me from an API. So, I created just that!

Cleaver takes JSON files saved locally, and parses the data in them directly to PHP variables using the keys you've provided. This is passed through to Laravel's Blade templating engine, where everything is then compiled to static HTML files and saved in a directory tree that you set.

So, for example, your JSON that looks like this:

{
    "view": "layouts.default",
    "path": "/posts/a-whole-new-world",
    "title": "A Whole New World",
    "description": "Shining, shimmering, splendid.",
    "items": [
        "Three Forks",
        "Seven Spoons",
        "One Butter Knife"
    ]
}

Gets converted into the following PHP variables:

$view 
"layouts.default"

$path
"/posts/a-whole-new-world"

$title
"A Whole New World"

$description
"Shining, shimmering, splendid."

$items
array("Three Forks", "Seven Spoons", "One Butter Knife")

These are passed through to your Blade template of choice (in this case, located at views/layouts/default.blade.php) in the resources directory. Then you can use them just like you would in your Laravel application:

<h1>{{ $title }}</h1>
<h4>{{ $description }}</h4>
<ul>
    @foreach($items as $item)
        <li>{{ $item }}</li>
    @endforeach
</ul>

After compiling the site through the simple npm run dev script, it'll output the rendered static HTML file at posts/a-whole-new-world/index.html under the /dist directory.

You can see a better example of this on the repository containing the usecleaver.com landing page here.

Getting started

You'll want to make sure that you have the following installed on your machine:

  • PHP
  • Composer
  • Node
  • NPM

The easiest way to get started creating a Cleaver site, is to run the following command:

composer create-project aschmelyun/cleaver your-site-name

This will download the latest version of Cleaver from the repo, install the required packages, and dump everything in a folder called your-site-name. Of course, please change that to reflect what you actually want your site directory to be.

Navigate in your terminal to the newly-created folder, and run the following command to install the required node packages and compile your site for the first time:

npm install && npm run dev

After a few minutes or so, your site should have been successfully built with the demo content included (a lite version of the usecleaver.com/docs page) and added into a /dist folder in the project root directory.

Let's move on to making a new page.

Creating and structuring a page

All of the content used to build your site is located in the resources/content directory. In here you can add JSON and Markdown files, organized and named in any particular way you see fit. Unlike a lot of other static site generators, the structure of your content directory does not affect the structure of the built site.

Inside each content file, there are two keys which have to be set in order for the page to be compiled successfully. Those are view and path. Let's see what each one of those does, and why they're important:

  • view is the Blade template that the page will be compiled against. You can use and include other Blade templates in the specified file, but that is the one it will be built with.
  • path is where this page will end up in your dist directory after Cleaver runs. For instance, if you have a site called myblog.com and want this page to exist at myblog.com/posts/a-cool-post, your path value would be /posts/a-cool-post.

As stated before, Cleaver accepts both JSON files as well as Markdown. Here's an example of each being used for similar pages:

A JSON content file example

A Markdown content file example

The biggest difference between the two is how the data is accessed in your Blade templates.

For JSON content files, each key is used as an individual variable. So in the above examples, you'd reference a single $title string and a $paragraphs array of strings.

When using Markdown however, everything below the last three dashes is injected as a single $content variable. If using this, Blade's unescaped echo shortcut {!! $content !!} will render out the proper HTML tags that get compiled from the Markdown file.

Modifying assets

Cleaver uses Sass files for styling, and auto-loads in the Tailwind CSS utility library. You can add your own styles, or override what's currently configured, under the resources/assets/sass directory. By default, there's a single file in there called app.scss that solely imports the Tailwind library, but feel free to import your own Sass files or add in and remove custom classes as you see fit.

Navigating to the resources/assets/js directory, you'll see a single JavaScript file called app.js. Just like with the styles, Cleaver has a very bare-bones approach here. All this file does is import lodash, jQuery, and Vue, giving your site a modern and extensible starting point for front-end functionality. Since we're pulling everything in through Laravel's webpack wrapper Mix, you can use ES6 syntax and import/require statements throughout your JavaScript.

Building and deployment

To compile your assets and build the static site using your content files, all you have to do is run one of the following commands from your project's root:

npm run dev or npm run production

Both will compile all of your CSS/JS and build your site, but running production will take minify and version your asset files. The latter does take a little bit longer, but is recommended if you're going to be deploying your site to a production environment.

Additionally, you can use npm run watch which will start up a local webserver and watch all changes to your assets and content files, re-compiling the entire site every time you save a change.

Once you've built your site, the entire markup tree and assets will be available in a dist directory at the root of your project. You can either upload this file directly to the public root of your webserver and serve it that way, or commit the entire project and adjust your webserver's public folder to point to the dist directory.

Note: If you want to compile your site, but not your assets, you can directly run the php cleaver command from your project root.

That's it!

I hope you enjoyed this (quick?) rundown of Cleaver, the static site generator I ended up building to scratch my own itch. If you end up giving it a try and have any questions, concerns, or feature requests, I would love to hear about them! Feel free to open an issue in GitHub, or reach out directly to me on Twitter.

Posted on Dec 26 '19 by:

aschmelyun profile

Andrew Schmelyun

@aschmelyun

Full-stack PHP developer passionate about Laravel, modern JavaScript, and growing hot peppers.

Discussion

markdown guide
 

Have you considered Gatsby (or even Hugo)? Gatsby consumes data from JSON files (among lots of other sources) and gives you a large frontend ecosystem based on React.

 

Is there some kind of plugin integration with laravel without stuff like react

 

No, Gatsby is just NodeJS and React. Do you need Laravel for some reason? Give Gatsby a try since I think it solves all your needs. Or maybe Hugo if you don't feel confortable with React. Both of them will give a more scalable solution for your JSON data.

I see, i'm not a NODEJS/REACT guy, just run site with pure laravel. Yeah I'm looking for "static site" solutions because my content are pretty static and it's growing.

We are looking at a few million pages. Trying to find way to increase the load speed and static seems to be the way..

If you are trying to generate a static site with millions of pages, you should definitively take a look at Hugo. It's able to generate 1,000 pages per second. Gatsby generates ~10,000 pages per minute.

Definitely have to love the power of the Go language being so close to the metal!

 

Been migrating from Jekyll to GatsbyJS. I can say that they're blazing fast, especially when navigating from page to page. The only problem is they have slow in compiling or generating page (probably because I put too many npm package, haha).

But, Hugo is pretty interesting too when you mention it's able to generate 1000 pages per second. Gonna try if I got spare time.

 

I've tried using Gatsby a few times, and it's definitely great for the same use cases (data-driven sites and online documentation), but I love using Blade so much I wanted a solution to do both!

 

How does it compare to JIGSAW? Consider it adding it in staticgen.com

 

It's similar in that they both use Laravel's Blade templating engine and are built on PHP. Cleaver is definitely much less complex in terms of both options and underlying code.

A big difference though is that Jigsaw doesn't provide an easy way to use a JSON file for content, or work well for heavily data-oriented sites, which is the original reason I started building Cleaver.

Hope this helps, and I'll definitely add it to staticgen.com!