Home

Awesome

@metalsmith/in-place

A metalsmith plugin for transforming source files' contents. Complements @metalsmith/layouts

metalsmith: core plugin npm: version ci: build code coverage license: MIT

Features

Installation

NPM:

npm install @metalsmith/in-place jstransformer-handlebars

Yarn:

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.

Usage

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' }))
}

Options

In most cases, you will only need to specify the transform and engineOptions option.

Extension handling

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.

sourceoutput
src/index.hbsbuild/index.html
src/index.hbs.htmlbuild/index.html
src/index.html.hbsbuild/index.html

The example demonstrates that:

engineOptions

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.

Multiple transforms per file

Suppose a file tags.hbs that lists all the article tags used on your website

---
title: Tags
description: Browse articles by tag
---
<h1>{{ title }}</h1>
<p>{{ description }}</p>
<ul>
{{#each tags}}
  <li><a href="/tags/{{ . }}">{{ . }}</a></li>
{{/each}}
</ul>

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!

---
title: Tags
description: Browse articles by tag
---

# {{ title }}

{{ description }}

{{#each tags}}
- [{{.}}](/tags/{{ . }})
{{/each}}

More markdown here..

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.

Usage with @metalsmith/layouts

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.

Debug

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.

Credits

License

MIT