Skip to content

Commit

Permalink
Updates README.md & documents transform option
Browse files Browse the repository at this point in the history
  • Loading branch information
webketje committed Jul 3, 2023
1 parent 064cc37 commit 13f7fff
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 46 deletions.
109 changes: 63 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,56 @@
# @metalsmith/in-place

A metalsmith plugin for transforming your source files
A metalsmith plugin for transforming source files' contents. Complements [@metalsmith/layouts](https://github.com/metalsmith/layouts)

[![metalsmith: core plugin][metalsmith-badge]][metalsmith-url]
[![npm: version][npm-badge]][npm-url]
[![ci: build][ci-badge]][ci-url]
[![code coverage][codecov-badge]][codecov-url]
[![license: MIT][license-badge]][license-url]

This plugin allows you to render templating syntax in your source files. It uses file extensions to infer which templating engine to use. So files ending in `.njk` will be processed as nunjucks, `.md` as markdown, etc. You can even chain transformations by appending multiple extensions, which will be processed right-to-left.
## Features

If you want to wrap your source files in a common template, you can use [@metalsmith/layouts](https://github.com/metalsmith/metalsmith/layouts). For usage examples check out our [wiki](https://github.com/metalsmith/metalsmith/in-place/wiki). Feel free to contribute an example if anything is missing, or update the existing ones. For templating engine specific questions try the aforementioned channels, as well as the documentation for [jstransformers](https://github.com/jstransformers) and your templating engine of choice.
- renders source files' `contents` field with any existing or a custom [Jstransformer templating engine](https://github.com/jstransformers/jstransformer)
- alters file extensions from `transform.inputFormats` to `transform.outputFormat`
- can be used multiple times with different configs per metalsmith pipeline

## Installation

NPM:

```bash
npm install @metalsmith/in-place
npm install @metalsmith/in-place jstransformer-handlebars
```

Yarn:

```bash
yarn add @metalsmith/in-place
yarn add @metalsmith/in-place jstransformer-handlebars

```

This plugin works with [jstransformers](https://github.com/jstransformers/jstransformer) 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](https://github.com/jstransformers/jstransformer-marked). To render handlebars you would install [jstransformer-handlebars](https://github.com/jstransformers/jstransformer-handlebars). Other popular templating options include: [Nunjucks](https://github.com/jstransformers/jstransformer-nunjucks), [Twig](https://github.com/jstransformers/jstransformer-twig), [Pug](https://github.com/jstransformers/jstransformer-pug), or [EJS](https://github.com/jstransformers/jstransformer-ejs). See also [this map](https://github.com/jstransformers/inputformat-to-jstransformer/blob/master/dictionary.json) to see which extensions map to which jstransformer.

## Usage

This plugin uses [jstransformers](https://github.com/jstransformers/jstransformer) to transform files. Since there are a lot of jstransformers we don't install them automatically, so you'll also need to install the appropriate jstransformers.
Pass `@metalsmith/in-place` to `metalsmith.use` :

For example, to render markdown you would install [jstransformer-markdown](https://github.com/jstransformers/jstransformer-markdown). To render handlebars you would install [jstransformer-handlebars](https://github.com/jstransformers/jstransformer-handlebars). Other popular templating options are: Nunjucks, Twig, Pug, EJS. See the [jstransformer organisation](https://github.com/jstransformers) for all available jstransformers and [this dictionary](https://github.com/jstransformers/inputformat-to-jstransformer/blob/master/dictionary.json) to see which extensions map to which jstransformer.
```js
import inPlace from '@metalsmith/in-place'

## Options
// render all files matching **/*.{njk,nunjucks} to HTML
metalsmith.use(inPlace({ transform: 'nunjucks' }))

You can pass options to `@metalsmith/in-place` with the [Javascript API](https://github.com/segmentio/metalsmith#api) or [CLI](https://github.com/segmentio/metalsmith#cli). The options are:
// render all files matching **/*.{hbs,handlebars,md,marked,markdown} to HTML
// also works with files containing both!
metalsmith.use(inPlace({ transform: 'handlebars' })).use(inPlace({ transform: 'marked' }))
```

- [pattern](#pattern): optional. Only files that match this pattern will be processed. Accepts a string or an array of strings. The default is `**`.
- [engineOptions](#engineoptions): optional. Use this to pass options to the jstransformer that's rendering your files. The default is `{}`.
## Options

- [transform](#transform): required. Which transformer to use: a string representing node module name or local JS module path (starting with `.`) whose default export is a jstransformer. As a shorthand for existing transformers you can remove the `jstransformer-` prefix: `marked` will be understood as `jstransformer-marked`. Or an actual jstransformer; an object with `name`, `inputFormats`,`outputFormat`, and at least one of the render methods `render`, `renderAsync`, `compile` or `compileAsync` described in the [jstransformer API docs](https://github.com/jstransformers/jstransformer#api)
- [pattern](#pattern): optional. Defaults to `**/*.<transform.inputFormats>`. Useful to further limit the scope of the transform by path or glob (accepts a string or an array of strings).
- [engineOptions](#engineoptions): optional. Pass options to the jstransformer that's rendering the files. The default is `{}`.

### `pattern`

Expand All @@ -47,17 +60,18 @@ Only files that match this pattern will be processed. So this `metalsmith.json`:
{
"source": "src",
"destination": "build",
"plugins": {
"@metalsmith/in-place": {
"pattern": "blog/**/*"
"plugins": [
{
"@metalsmith/in-place": {
"transform": "nunjucks",
"pattern": "blog/**/*.njk"
}
}
}
]
}
```

Would only process files within the `./src/blog` folder, because the pattern is
relative to your source folder. See [Metalsmith#match](https://metalsmith.io/api/#Metalsmith+match)
for further details.
Would only process files within the `./src/blog` folder, because the pattern is relative to your source folder. See [Metalsmith#match](https://metalsmith.io/api/#Metalsmith+match) for further details.

### `engineOptions`

Expand All @@ -68,49 +82,52 @@ Use this to pass options to the jstransformer that's rendering your templates. S
{
"source": "src",
"destination": "build",
"plugins": {
"@metalsmith/in-place": {
"transform": "nunjucks",
"engineOptions": {
"cache": false
"plugins": [
{
"@metalsmith/in-place": {
"transform": "ejs",
"engineOptions": {
"cache": false
}
}
}
}
]
}
```

Would pass `{ "cache": false }` to `jstransformer-nunjucks`.
..would pass `{ "cache": false }` to `jstransformer-ejs`.

If you are using [Pug](https://pugjs.org/api/getting-started.html), make sure to pass `engineOptions: { filename: true }`. This will ensure the filename of each processed file is passed to the render method.
If you use [Pug](https://pugjs.org/api/getting-started.html), 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.

## Errors and debugging
### Usage with @metalsmith/layouts

If you're encountering problems you can use [debug](https://www.npmjs.com/package/debug) to enable verbose logging. To enable `debug` prefix your build command with `DEBUG=@metalsmith/in-place`. So if you normally run `metalsmith` to build, use `DEBUG=@metalsmith/in-place metalsmith` (on windows the syntax is [slightly different](https://www.npmjs.com/package/debug#windows-note)).
`@metalsmith/in-place` should always be used _before_ `@metalsmith/layouts`.
You can easily share `engineOptions` configs between both plugins:

### No files to process
```js
import inPlace from '@metalsmith/in-place'
import layouts from '@metalsmith/layouts'

There are several things that might cause you to get a `no files to process` error:
const engineOptions = {
filters: {
get(context, keypath) {
return keypath.split('.').reduce((obj, prop, index, all) => {
if (obj[prop]) return obj[prop]
return ''
}, context)
}
}
}

- Your [pattern](#pattern) does not match any files
- None of your files pass validation, validation fails for files that:
- Have no extension
- Are not utf-8
- Need a jstransformer that hasn't been installed
metalsmith.use(inPlace({ transform: 'nunjucks', engineOptions })).use(layouts({ pattern: '**/*.html', engineOptions }))
```

### Debug

To enable debug logs, set the `DEBUG` environment variable to `@metalsmith/in-place`:

Linux/Mac:

```bash
export DEBUG=@metalsmith/in-place
```

Windows:
To enable debug logs, set the `DEBUG` environment variable to `@metalsmith/in-place*`:

```bat
set "DEBUG=@metalsmith/in-place"
```js
metalsmith.env('DEBUG', '@metalsmith/in-place*')
```

Alternatively you can set `DEBUG` to `@metalsmith/*` to debug all Metalsmith core plugins.
Expand Down
48 changes: 48 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,53 @@ import path from 'node:path'
import isUtf8 from 'is-utf8'
import jstransformer from 'jstransformer'

/**
* @callback Render
* @param {string} source
* @param {Object} options
* @param {Object} locals
* @returns {string}
*/

/**
* @callback RenderAsync
* @param {string} source
* @param {Object} options
* @param {Object} locals
* @param {Function} callback
* @returns {Promise<string>}
*/

/**
* @callback Compile
* @param {string} source
* @param {Object} options
* @returns {string}
*/

/**
* @callback CompileAsync
* @param {string} source
* @param {Object} options
* @param {Function} callback
* @returns {Promise<string>}
*/

/**
* @typedef {Object} JsTransformer
* @property {string} name
* @property {string[]} inputFormats
* @property {string} outputFormat
* @property {Render} [render]
* @property {RenderAsync} [renderAsync]
* @property {Compile} [compile]
* @property {CompileAsync} [compileAsync]
*/

/**
* @param {string|JsTransformer} namePathOrTransformer
* @returns {Promise<JsTransformer>}
*/
async function getTransformer(namePathOrTransformer) {
let transform = null
const t = namePathOrTransformer
Expand Down Expand Up @@ -126,6 +173,7 @@ function validate({ filename, files, transform }) {

/**
* @typedef {Object} Options
* @property {string|JsTransformer} transform Jstransformer to run
* @property {string} [pattern='**'] (*optional*) Limit the files to process by 1 or more glob patterns. Defaults to `'**'` (all)
* @property {Object} [engineOptions={}] (*optional*) Pass options to the jstransformer templating engine that's rendering your files. The default is `{}`
**/
Expand Down

0 comments on commit 13f7fff

Please sign in to comment.