Search code examples
metalsmith

Metalsmith template of template


Is it possible to use templates that extend each other?

Say, I have these files:

base.html:

<html><head>...<head><body>{{{ contents }}}</body></html>

threeColumns.html:

---
layout: base.html
---
<div class="three-cols-row">
   <div class="first col">
      {{> nav}}
   </div>
   <div class="second col">
       {{{ contents }}}
   </div>
   <div class="third col">
      {{> aside}}
   </div>
</div>

somePost.html:

---
layout: threeColumns.html
---

My awesome blogbost

Desired output:

<html><head>...<head>
    <body>
        <div class="three-cols-row">
           <div class="first col">
              NAV CONTENT
           </div>
            <div class="second col">
               My awesome blogbost
           </div>
           <div class="third col">
              ASIDE CONTENT
           </div>
        </div>
    </body>
</html>

I am using metalsmith-layouts.


Solution

  • It is, but not exactly in the way that you want to.

    Metalsmith-layouts is meant to be a shim for template inheritance for languages that don't natively support it, like handlebars. To make this possible it parses the files that you pass to it, somePost.html in this case, and looks for a layout key in the front-matter. After that it processes the chosen layout, threeColumns.html, by passing somePost.html to threeColumns.html as the variable contents.

    At that point the file is rendered with your chosen engine by consolidate. So the layout key, which is specific to metalsmith-layouts no longer works in threeColumns.html because metalsmith-layouts doesn't process it. At this point the file is being processed by consolidate.

    What you can do is use a language that natively supports template inheritance, like swig. Then you could do:

    build.js

    /**
     * Dependencies
     */
    var filenames = require('metalsmith-filenames');
    var layouts = require('metalsmith-layouts');
    var metalsmith = require('metalsmith');
    
    /**
     * Build
     */
    metalsmith(__dirname)
      .use(filenames()) // Necessary for extends and includes
      .use(layouts('swig'))
    
      .build(function(err){
        if (err) throw err;
      });
    

    src/somePost.swig

    ---
    layout: threeColumns.swig
    ---
    <p>My awesome blogpost</p>
    

    layouts/threeColumns.swig

    {% extends "base.swig" %}
    
    {% block body %}
    <div class="three-cols-row">
       <div class="first col">
          {% include "nav.swig" %}
       </div>
       <div class="second col">
           {{ contents | safe }}
       </div>
       <div class="third col">
          {% include "aside.swig" %}
       </div>
    </div>
    {% endblock %}
    

    layouts/base.swig

    <html>
      <head><head>
      <body>
        {% block body %}{% endblock %}
      </body>
    </html>
    

    layouts/nav.swig

    <nav>Navigation</nav>
    

    layouts/aside.swig

    <aside>Aside content</aside>
    

    If you then run node build.js from the command line, it'll build it into a single page.