A metalsmith plugin for transforming source files' contents. Complements @metalsmith/layouts
- renders source files'
contentsfield with any existing or a custom Jstransformer templating engine - alters file extensions from
transform.inputFormatstotransform.outputFormat - can be used multiple times with different configs per metalsmith pipeline
NPM:
npm install @metalsmith/in-place jstransformer-handlebarsYarn:
yarn add @metalsmith/in-place 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/in-place to metalsmith.use :
import inPlace from '@metalsmith/in-place'
// shorthand
metalsmith.use(inPlace({ transform: 'nunjucks' }))
// same as shorthand
metalsmith.use(
inPlace({
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(inPlace({ transform: 'handlebars' }))for a file:
---
title: Article title
---
<h1>{{ title }}</h1>Node v{{ nodeVersion }}would render <h1>Article title</h1>Node v16.20
Multiple transforms can be used to target different sets of files, or to reprocess the same files multiple times in the order they are metalsmith.use'd:
// this build will apply the marked transform to index.md, the handlebars transform to index.hbs,
// and handlebars first, marked second to both index.hbs.md, index.md.hbs, and html-minifier to all (only in production)
metalsmith
.env('NODE_ENV', process.env.NODE_ENV)
.use(inPlace({ transform: 'handlebars', extname: null }))
.use(inPlace({ transform: 'marked' }))
if (metalsmith.env('NODE_ENV') !== 'development') {
metalsmith.use(inPlace({ transform: 'html-minifier' }))
}In most cases, you will only need to specify the transform 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.
By default in-place will apply smart default extension handling based on transform.inputFormats and transform.outputFormat.
For example, any of the source files below processed through inPlace({ 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 |
The example demonstrates that:
- order of extensions doesn't matter, order of plugin execution does!: you can pick the final extension to match the most suitable editor syntax highlighting
- a single in-place run only alters the rightmost extension matching
transform.inputFormats - you may choose to include or omit the
transform.outputFormatin the source file name (.html in this case).
Pass options to the jstransformer that's rendering your templates via engineOptions. The
metalsmith.json:
{
"source": "src",
"destination": "build",
"plugins": [
{
"@metalsmith/in-place": {
"transform": "ejs",
"engineOptions": {
"cache": false
}
}
}
]
}..would pass { "cache": false } to jstransformer-ejs.
If you use Pug, make sure to pass engineOptions: { filename: true }. This will ensure the filename of each processed file is passed to the render method as expected by this engine.
Suppose a file tags.hbs that lists all the article tags used on your website
To reduce Handlebars noise, you could add metalsmith.use(inPlace({ transform: 'marked' }) to your build and change the filename to tags.hbs.md to generate markdown syntax with Handlebars!
Caution: when using multiple templating transforms per file, make sure there is no conflicting syntax.
For example markdown will transform blocks indented by 4 spaces to <pre> tags, and marked's smartypants can potentially garble the result.
In most cases @metalsmith/in-place is intended to be used before @metalsmith/layouts.
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({ engineOptions })) // -> index.html@metalsmith/layouts 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/in-place*:
metalsmith.env('DEBUG', '@metalsmith/in-place*')Alternatively you can set DEBUG to @metalsmith/* to debug all Metalsmith core plugins.
- Ismay Wolff for improving upon metalsmith-templates & diligently maintaining its successor
- 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