Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP (docs driven development) - Preview API #1311

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions website/site/content/docs/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
title: API
position: 140
---

# Previews

The preview pane shows changes to a document in realtime, as they occur, but the output is mostly
unstyled by default. For Netlify CMS to show previews that look like the actual website being
edited, you'll need to provide a template and styling. Three methods are available for this:

* **[registerPreviewTemplateCompiler()](#registerpreviewtemplatecompiler)**
* **[registerPreviewTemplate()](#registerpreviewtemplate)**
* **[registerPreviewStyle()](#registerpreviewstyle)**


## registerPreviewTemplateCompiler()

```js
CMS.registerPreviewTemplateCompiler(
name,
compiler,
)
```

Registers a template compiler using a name of your choice. The compiler is used to transform the
editor document into an HTML preview using a registered template. Template compilers can be written
to parse different kinds of templates, like Handlebars, EJS, golang, anything that can be parsed
with JavaScript.

Learn how to write a template compiler in [Writing a Template Compiler]().


## registerPreviewTemplate()

```js
CMS.registerPreviewTemplate(
name,
template,
[config],
[compiler],
);
```

Registers a preview template. `name` is the string used to apply the template to a collection in the
CMS configuration, `template` can be any type (typically string or function), `config` is an object
for configuring your template, and `compiler` is a string indicating the template compiler to be
used. The template compiler determines the acceptable type for `template` as well as the shape of
`config`.

Learn how to create templates in [Creating Preview Templates]().


## registerPreviewStyle()

```js
CMS.registerPreviewStyle(
file,
);
```

Register a custom stylesheet to use on the preview pane. `file` can either be a URL to a CSS file or
a CSS string.
160 changes: 160 additions & 0 deletions website/site/content/docs/creating-a-template-compiler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
---
title: Creating a Template Compiler
weight: 60
menu:
docs:
parent: guides
---

# Creating a Template Compiler

A template compiler is a function that receives a template and the current entry data from the
editor, and returns an HTML string for use in the editor preview pane. Different compilers support
different templating languages. Netlify CMS comes bundled with a Handlebars template compiler, but
what if you'd rather use [EJS](http://ejs.co/) templates? We'll walk through creation of an EJS
template compiler for this guide.

## Using a library

Template compilers usually don't do much on their own, instead serving as adapters for
template parsing libraries. When creating a compiler to support a templating language, your first stop
should be npm, where you'll search for the best JavaScript compiler available for your templating
language.

A quick search for "ejs" on the [npm website](https://www.npmjs.com/search?q=ejs) reveals the
official JavaScript library for parsing EJS templates, the unsurprisingly named
[ejs](https://www.npmjs.com/package/ejs) package.

Let's add the ejs package to our project:

```bash
npm install ejs
```

Your template compiler will receive four arguments when called:

- `name` {string} - identifier for the template compiler
- `template` {any} - the template itself, which is often a string, but could be anything
- `data` {object} - the current entry data
- `config` {object} - a configuration object that may differ depending on the template compiler

Most template parsing libraries provide a `compile` or `render` function that accepts a string
template and a data object, and returns an HTML string, which is very close to what Netlify CMS
template compilers do. The `ejs` package works this way, providing both a `compile` and a
`render` method. The `render` method is the most straightforward, so we'll create our own `compile`
function using that:

```js
function renderEJS(collectionName, template, data) {
return ejs.render(template, data)
}
```

With just those three lines, we have a function that can be registered as a preview template
compiler!

## Registering the compiler

The last step for our basic EJS compiler is registering it to be used in the CMS:

```js
CMS.registerTemplateCompiler('ejs', renderEJS)
```

If this is the only registered compiler, it will be used automatically for all preview templates.
Otherwise, you can set the compiler for a collection in your CMS config file:

```yaml
collections:
- name: post
label: Post
templateCompiler: ejs
```

## Improving performance

The template compiler will run on every change in the editor, so we want to do as little as possible
when that happens to avoid hurting performance. Most template libraries provide a function to
prepare the template for processing beforehand, so that rendering HTML only requires adding data to
the prepared template.

The `ejs` package has a `compile` method for this purpose - let's run it when our template compiler
loads and cache the result for reuse:

```js
// Our "cache" is just an object. The keys will be collection names, and the
// values will be compiled templates.
const templateCache = {}

function renderEJS(collectionName, template, data) {

// add the compiled template cache if it isn't already there
templateCache[collectionName] = templateCache[collectionName] || ejs.compile(template)

// grab the cached compiled template
const compiledTemplate = templateCache[collectionName]

// return the rendered HTML
return compiledTemplate(data)

}
```

## Transforming templates and data

Sometimes the editor data or the template will need to be tweaked to achieve the desired preview
output. Template compilers should support this by accepting `transformData` and `transformTemplate`
functions in the `config` object.

Let's update our compiler to support template and data transformation functions:

```js
const templateCache = {}

function renderEJS(collectionName, template, data, config) {

const { transformTemplate, transformData } = config

// if no cached template, run the raw template through the transform function
// and the ejs compile function
if (!templateCache[collectionName]) {
const transformedTemplate = transformTemplate ? transformTemplate(template) : template
templateCache[collectionName] = ejs.compile(transformedTemplate)
}

const compiledTemplate = templateCache[collectionName]

// run the data through `transformData`, this happens every time the compiler is called
const transformedData = transformData(data)

return compiledTemplate(transformedData)
}
```

## Example usage

With the EJS compiler we've just created, here's what usage might look like:

```js
import CMS from 'netlify-cms'
import blogTemplate from './src/templates/blog.ejs'

// This import represents the compiler we just created
import ejsCompiler from 'netlify-cms-template-compiler-ejs'

const blogTemplateConfig = {

// Using the `transformTemplate` function to remove scripts from templates
transformTemplate: template => template.replace(/<script ?.*>(.|\s)*<\/script>/g, ''),

// Using the `transformData` function to add placeholder values that a
// static site generator might provide in a production build
transformData: data => { ...data, site: {
tags: ['jamstack', 'static', 'ci'],
}},

}

CMS.registerTemplateCompiler('ejs', ejsCompiler)
CMS.registerPreviewTemplate('blog', blogTemplate, blogTemplateConfig, 'ejs')
```
Loading