@metalsmith/layouts
A metalsmith plugin for layouts
Features
- wraps source files'
contentsfield in a layout rendered with a Jstransformer templating engine - alters file extensions from
transform.inputFormatstotransform.outputFormat - can be used multiple times with different configs per metalsmith pipeline
Installation
NPM:
npm install @metalsmith/layouts jstransformer-handlebars
Yarn:
yarn add @metalsmith/layouts jstransformer-handlebars
This plugin works with jstransformers but they should be installed separately. jstransformer-handlebars is just an example, you could use any transformer. To render markdown you could install jstransformer-marked. To render handlebars you would install jstransformer-handlebars. Other popular templating options include: Nunjucks, Twig, Pug, or EJS. See also this map to see which extensions map to which jstransformer.
Usage
Pass @metalsmith/layouts to metalsmith.use :
import layouts from '@metalsmith/layouts'
// shorthand
metalsmith.use(layouts({ transform: 'nunjucks' }))
// same as shorthand
metalsmith.use(
layouts({
directory: 'layouts' // === path.join(metalsmith.directory(), 'layouts')
transform: jsTransformerNunjucks, // resolved
extname: '.html',
pattern: '**/*.{njk,nunjucks}*',
engineOptions: {}
})
)
In the transformed file, you have access to { ...metalsmith.metadata(), ...fileMetadata }, so that the following build
metalsmith
.metadata({ title: 'Default title', nodeVersion: process.version })
.use(layouts({ transform: 'handlebars' }))
for a file:
---
title: Article title
layout: default.hbs
---
with layout:
<h1>{{title}}</h1>Node v{{nodeVersion}}
would render <h1>Article title</h1>Node v16.20.
Options
In most cases, you will only need to specify the transform, default, and engineOptions option.
- transform (
string|JsTransformer): required. Which transformer to use. The full name of the transformer, e.g.jstransformer-handlebars, its shorthandhandlebars, a relative JS module path starting with., e.g../my-transformer.js, whose default export is a jstransformer or an actual jstransformer: an object withname,inputFormats,outputFormat, and at least one of the render methodsrender,renderAsync,compileorcompileAsyncdescribed in the jstransformer API docs - extname (
string|false|null): optional. How to transform a file's extensions:''|false|nullto remove the lasttransform.inputFormatmatching extension,.<ext>to force an extension rename. - engineOptions (
Object<string, any>): optional. Pass options to the jstransformer that's rendering the files. The default is{}. - pattern (
string|string[]): optional. Override default glob pattern matching**/*.<transform.inputFormats>*. Useful to limit the scope of the transform by path or glob to a subfolder, or to include files not matchingtransform.inputFormats. - default (
string): optional. The default layout to apply to files matched withpattern. If none is given, files matched without defined layout will be skipped. Files whoselayoutis set tofalsewill also be skipped. - directory (
string): optional. The directory for the layouts (relative tometalsmith.directory(), notmetalsmith.source()!). Defaults tolayouts.
directory
The directory path is resolved relative to Metalsmith#directory, not Metalsmith#source.
If you prefer having the layouts directory inside the Metalsmith source folder, it is advisable to use Metalsmith#ignore to avoid loading the layouts twice (once via Metalsmith and once via the JSTransformer):
import layouts from '@metalsmith/layouts'
metalsmith.ignore('layouts').use(
layouts({
directory: 'src/layouts'
})
)
engineOptions
Use engineOptions to pass options to the jstransformer that's rendering your templates. For example:
import layouts from '@metalsmith/layouts'
metalsmith.use(
layouts({
engineOptions: {
cache: false
}
})
)
Would pass { "cache": false } to the used jstransformer.
Extension handling
By default layouts will apply smart default extension handling based on transform.inputFormats and transform.outputFormat.
For example, any of the source files below processed through layouts({ transform: 'handlebars' }) will yield index.html.
| source | output |
|---|---|
| src/index.hbs | build/index.html |
| src/index.hbs.html | build/index.html |
| src/index.html.hbs | build/index.html |
Usage with @metalsmith/in-place
In most cases @metalsmith/layouts is intended to be used after @metalsmith/in-place.
You can easily share engineOptions configs between both plugins:
import inPlace from '@metalsmith/in-place'
import layouts from '@metalsmith/layouts'
const engineOptions = {}
metalsmith // index.hbs.hbs
.use(inPlace({ transform: 'handlebars', extname: '', engineOptions })) // -> index.hbs
.use(layouts({ transform: 'handlebars', engineOptions })) // -> index.html
@metalsmith/in-place uses a similar mechanism targeting transform.inputFormats file extensions by default.
The example requires files ending in .hbs.hbs extension, but if you don't like this, you can just have a single .hbs extension, and change the in-place invocation to inPlace({ engineOptions, transform, extname: '.hbs' }) for the same result.
Debug
To enable debug logs, set the DEBUG environment variable to @metalsmith/layouts:
metalsmith.env('DEBUG', '@metalsmith/layouts*')
Alternatively you can set DEBUG to @metalsmith/* to debug all Metalsmith core plugins.
CLI Usage
To use this plugin with the Metalsmith CLI, add @metalsmith/layouts to the plugins key in your metalsmith.json file:
{
"plugins": [
{
"@metalsmith/layouts": {
"default": null,
"directory": "layouts",
"engineOptions": {}
}
}
]
}
Credits
- Ismay Wolff for the current shape of the layouts plugin
- Ian Storm Taylor for creating metalsmith-templates, on which this plugin was based
- Rob Loach for creating metalsmith-jstransformer, which inspired our switch to jstransformers