A metalsmith plugin for layouts
- wraps source files'
contents
field in a layout rendered with a Jstransformer templating engine - alters file extensions from
transform.inputFormats
totransform.outputFormat
- can be used multiple times with different configs per metalsmith pipeline
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.
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:
would render <h1>Article title</h1>Node v16.20
.
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
,compile
orcompileAsync
described in the jstransformer API docs - extname (
string|false|null
): optional. How to transform a file's extensions:''|false|null
to remove the lasttransform.inputFormat
matching 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 whoselayout
is set tofalse
will also be skipped. - directory (
string
): optional. The directory for the layouts (relative tometalsmith.directory()
, notmetalsmith.source()
!). Defaults tolayouts
.
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'
})
)
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.
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 |
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.
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.
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": {}
}
}
]
}
- 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